From 99af65d5e0e8d81cebde342515a18d7bd641fc05 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 11 Jun 2009 15:21:34 -0700 Subject: dvb-pll: Add Samsung TDTC9251DH0 DVB-T NIM From: Trent Piepho No datasheet, tuner data comes from code in flexcop driver. This tuner is also used on the AVerTV 771 supported by the bttv driver, but that code uses a different tuner configuration, which is surprising. Priority: normal Signed-off-by: Trent Piepho --- linux/drivers/media/dvb/frontends/dvb-pll.c | 15 +++++++++++++++ linux/drivers/media/dvb/frontends/dvb-pll.h | 1 + 2 files changed, 16 insertions(+) diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.c b/linux/drivers/media/dvb/frontends/dvb-pll.c index 8ef5d1eff..6c6506757 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.c +++ b/linux/drivers/media/dvb/frontends/dvb-pll.c @@ -390,6 +390,20 @@ static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = { } }; +/* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */ +static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = { + .name = "Samsung TDTC9251DH0", + .min = 48000000, + .max = 863000000, + .iffreq = 36166667, + .count = 3, + .entries = { + { 157500000, 166667, 0xcc, 0x09 }, + { 443000000, 166667, 0xcc, 0x0a }, + { 863000000, 166667, 0xcc, 0x08 }, + } +}; + /* ----------------------------------------------------------- */ static struct dvb_pll_desc *pll_list[] = { @@ -408,6 +422,7 @@ static struct dvb_pll_desc *pll_list[] = { [DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261, [DVB_PLL_OPERA1] = &dvb_pll_opera1, [DVB_PLL_SAMSUNG_DTOS403IH102A] = &dvb_pll_samsung_dtos403ih102a, + [DVB_PLL_SAMSUNG_TDTC9251DH0] = &dvb_pll_samsung_tdtc9251dh0, }; /* ----------------------------------------------------------- */ diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.h b/linux/drivers/media/dvb/frontends/dvb-pll.h index 05239f579..dcbb28333 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.h +++ b/linux/drivers/media/dvb/frontends/dvb-pll.h @@ -23,6 +23,7 @@ #define DVB_PLL_PHILIPS_SD1878_TDA8261 12 #define DVB_PLL_OPERA1 13 #define DVB_PLL_SAMSUNG_DTOS403IH102A 14 +#define DVB_PLL_SAMSUNG_TDTC9251DH0 15 /** * Attach a dvb-pll to the supplied frontend structure. -- cgit v1.2.3 From 74c4537b6d3fadad69ae9955994e26ce67bdb454 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 11 Jun 2009 15:21:34 -0700 Subject: dvb-pll: Add support for Samsung TBDU18132 DVB-S NIM From: Trent Piepho Tuner parameters taken from flexcop driver. This PLL has a 17 bit divisor while the dvb-pll driver is designed for 15 bit divisors. It's not a problem as 15 bits is enough for the tuner's entire range. But if a larger range was wanted, it could be done by adding additional bands with the extra divisor bits appearing as band switch bits. Priority: normal Signed-off-by: Trent Piepho --- linux/drivers/media/dvb/frontends/dvb-pll.c | 22 ++++++++++++++++++++++ linux/drivers/media/dvb/frontends/dvb-pll.h | 1 + 2 files changed, 23 insertions(+) diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.c b/linux/drivers/media/dvb/frontends/dvb-pll.c index 6c6506757..7abd33389 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.c +++ b/linux/drivers/media/dvb/frontends/dvb-pll.c @@ -404,6 +404,27 @@ static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = { } }; +/* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */ +static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = { + .name = "Samsung TBDU18132", + .min = 950000, + .max = 2150000, /* guesses */ + .iffreq = 0, + .count = 2, + .entries = { + { 1550000, 125, 0x84, 0x82 }, + { 4095937, 125, 0x84, 0x80 }, + } + /* TSA5059 PLL has a 17 bit divisor rather than the 15 bits supported + * by this driver. The two extra bits are 0x60 in the third byte. 15 + * bits is enough for over 4 GHz, which is enough to cover the range + * of this tuner. We could use the additional divisor bits by adding + * more entries, e.g. + { 0x0ffff * 125 + 125/2, 125, 0x84 | 0x20, }, + { 0x17fff * 125 + 125/2, 125, 0x84 | 0x40, }, + { 0x1ffff * 125 + 125/2, 125, 0x84 | 0x60, }, */ +}; + /* ----------------------------------------------------------- */ static struct dvb_pll_desc *pll_list[] = { @@ -423,6 +444,7 @@ static struct dvb_pll_desc *pll_list[] = { [DVB_PLL_OPERA1] = &dvb_pll_opera1, [DVB_PLL_SAMSUNG_DTOS403IH102A] = &dvb_pll_samsung_dtos403ih102a, [DVB_PLL_SAMSUNG_TDTC9251DH0] = &dvb_pll_samsung_tdtc9251dh0, + [DVB_PLL_SAMSUNG_TBDU18132] = &dvb_pll_samsung_tbdu18132, }; /* ----------------------------------------------------------- */ diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.h b/linux/drivers/media/dvb/frontends/dvb-pll.h index dcbb28333..7905f3440 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.h +++ b/linux/drivers/media/dvb/frontends/dvb-pll.h @@ -24,6 +24,7 @@ #define DVB_PLL_OPERA1 13 #define DVB_PLL_SAMSUNG_DTOS403IH102A 14 #define DVB_PLL_SAMSUNG_TDTC9251DH0 15 +#define DVB_PLL_SAMSUNG_TBDU18132 16 /** * Attach a dvb-pll to the supplied frontend structure. -- cgit v1.2.3 From 70cded874faac45e4b8c55850cfaccc4112ffc10 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 11 Jun 2009 15:21:34 -0700 Subject: dvb-pll: Add support for Samsung TBMU24112 DVB-S NIM From: Trent Piepho Tuner parameters determined from code in flexcop driver. That code rounded the divisor down instead of to the nearest value. This was probably not intentional and the dvb-pll version will round to nearest. Priority: normal Signed-off-by: Trent Piepho --- linux/drivers/media/dvb/frontends/dvb-pll.c | 14 ++++++++++++++ linux/drivers/media/dvb/frontends/dvb-pll.h | 1 + 2 files changed, 15 insertions(+) diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.c b/linux/drivers/media/dvb/frontends/dvb-pll.c index 7abd33389..5dd999b8b 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.c +++ b/linux/drivers/media/dvb/frontends/dvb-pll.c @@ -425,6 +425,19 @@ static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = { { 0x1ffff * 125 + 125/2, 125, 0x84 | 0x60, }, */ }; +/* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */ +static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = { + .name = "Samsung TBMU24112", + .min = 950000, + .max = 2150000, /* guesses */ + .iffreq = 0, + .count = 2, + .entries = { + { 1500000, 125, 0x84, 0x18 }, + { 9999999, 125, 0x84, 0x08 }, + } +}; + /* ----------------------------------------------------------- */ static struct dvb_pll_desc *pll_list[] = { @@ -445,6 +458,7 @@ static struct dvb_pll_desc *pll_list[] = { [DVB_PLL_SAMSUNG_DTOS403IH102A] = &dvb_pll_samsung_dtos403ih102a, [DVB_PLL_SAMSUNG_TDTC9251DH0] = &dvb_pll_samsung_tdtc9251dh0, [DVB_PLL_SAMSUNG_TBDU18132] = &dvb_pll_samsung_tbdu18132, + [DVB_PLL_SAMSUNG_TBMU24112] = &dvb_pll_samsung_tbmu24112, }; /* ----------------------------------------------------------- */ diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.h b/linux/drivers/media/dvb/frontends/dvb-pll.h index 7905f3440..c1035812c 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.h +++ b/linux/drivers/media/dvb/frontends/dvb-pll.h @@ -25,6 +25,7 @@ #define DVB_PLL_SAMSUNG_DTOS403IH102A 14 #define DVB_PLL_SAMSUNG_TDTC9251DH0 15 #define DVB_PLL_SAMSUNG_TBDU18132 16 +#define DVB_PLL_SAMSUNG_TBMU24112 17 /** * Attach a dvb-pll to the supplied frontend structure. -- cgit v1.2.3 From ed6182953e804ff70ba2fc087dfe131498f00278 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 11 Jun 2009 15:24:00 -0700 Subject: dvb-pll: Add support for Alps TDEE4 DVB-C NIM From: Trent Piepho No datasheet, data take from code in flexcop driver. That code rounded down the divisor rather than rounding to nearest, which was probably not intentional and the dvb-pll code will round to nearest. Priority: normal Signed-off-by: Trent Piepho --- linux/drivers/media/dvb/frontends/dvb-pll.c | 24 ++++++++++++++++++++++++ linux/drivers/media/dvb/frontends/dvb-pll.h | 1 + 2 files changed, 25 insertions(+) diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.c b/linux/drivers/media/dvb/frontends/dvb-pll.c index 5dd999b8b..d8a7947e0 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.c +++ b/linux/drivers/media/dvb/frontends/dvb-pll.c @@ -438,6 +438,29 @@ static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = { } }; +/* Alps TDEE4 DVB-C NIM, used on Cablestar 2 */ +/* byte 4 : 1 * * AGD R3 R2 R1 R0 + * byte 5 : C1 * RE RTS BS4 BS3 BS2 BS1 + * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95 + * Range(MHz) C1 * RE RTS BS4 BS3 BS2 BS1 Byte 5 + * 47 - 153 0 * 0 0 0 0 0 1 0x01 + * 153 - 430 0 * 0 0 0 0 1 0 0x02 + * 430 - 822 0 * 0 0 1 0 0 0 0x08 + * 822 - 862 1 * 0 0 1 0 0 0 0x88 */ +static struct dvb_pll_desc dvb_pll_alps_tdee4 = { + .name = "ALPS TDEE4", + .min = 47000000, + .max = 862000000, + .iffreq = 36125000, + .count = 4, + .entries = { + { 153000000, 62500, 0x95, 0x01 }, + { 430000000, 62500, 0x95, 0x02 }, + { 822000000, 62500, 0x95, 0x08 }, + { 999999999, 62500, 0x95, 0x88 }, + } +}; + /* ----------------------------------------------------------- */ static struct dvb_pll_desc *pll_list[] = { @@ -451,6 +474,7 @@ static struct dvb_pll_desc *pll_list[] = { [DVB_PLL_TUA6034] = &dvb_pll_tua6034, [DVB_PLL_TDA665X] = &dvb_pll_tda665x, [DVB_PLL_TDED4] = &dvb_pll_tded4, + [DVB_PLL_TDEE4] = &dvb_pll_alps_tdee4, [DVB_PLL_TDHU2] = &dvb_pll_tdhu2, [DVB_PLL_SAMSUNG_TBMV] = &dvb_pll_samsung_tbmv, [DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261, diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.h b/linux/drivers/media/dvb/frontends/dvb-pll.h index c1035812c..086964344 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.h +++ b/linux/drivers/media/dvb/frontends/dvb-pll.h @@ -26,6 +26,7 @@ #define DVB_PLL_SAMSUNG_TDTC9251DH0 15 #define DVB_PLL_SAMSUNG_TBDU18132 16 #define DVB_PLL_SAMSUNG_TBMU24112 17 +#define DVB_PLL_TDEE4 18 /** * Attach a dvb-pll to the supplied frontend structure. -- cgit v1.2.3 From 8b87b848ca8a5e6465888220e57ec3e59104d677 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 11 Jun 2009 15:31:22 -0700 Subject: b2c2: fix frontends compiled into kernel From: Trent Piepho A recent patch didn't take into account that frontends can be compiled into the kernel. Or that frontends compiled as modules can't be used by the b2c2 driver if it is not a module itself. Some frontends require multiple drivers, e.g. a demod driver and a tuner driver. The code for the frontend support was getting added if the demod driver was available. Change this to also require any needed tuner or SEC drivers as well. Priority: normal Signed-off-by: Trent Piepho --- linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c | 67 ++++++++++++++----------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index efb4a6c2b..9a6307a34 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -20,8 +20,14 @@ #include "tuner-simple.h" #include "stv0297.h" + +/* Can we use the specified front-end? Remember that if we are compiled + * into the kernel we can't call code that's in modules. */ +#define FE_SUPPORTED(fe) (defined(CONFIG_DVB_##fe) || \ + (defined(CONFIG_DVB_##fe##_MODULE) && defined(MODULE))) + /* lnb control */ -#if defined(CONFIG_DVB_MT312_MODULE) || defined(CONFIG_DVB_STV0299_MODULE) +#if FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299) static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { struct flexcop_device *fc = fe->dvb->priv; @@ -49,8 +55,7 @@ static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage } #endif -#if defined(CONFIG_DVB_S5H1420_MODULE) || defined(CONFIG_DVB_STV0299_MODULE) \ - || defined(CONFIG_DVB_MT312_MODULE) +#if FE_SUPPORTED(S5H1420) || FE_SUPPORTED(STV0299) || FE_SUPPORTED(MT312) static int flexcop_sleep(struct dvb_frontend* fe) { struct flexcop_device *fc = fe->dvb->priv; @@ -61,7 +66,7 @@ static int flexcop_sleep(struct dvb_frontend* fe) #endif /* SkyStar2 DVB-S rev 2.3 */ -#if defined(CONFIG_DVB_MT312_MODULE) +#if FE_SUPPORTED(MT312) static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) { /* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */ @@ -193,10 +198,12 @@ static int skystar2_rev23_attach(struct flexcop_device *fc, } return 0; } +#else +#define skystar2_rev23_attach NULL #endif /* SkyStar2 DVB-S rev 2.6 */ -#if defined(CONFIG_DVB_STV0299_MODULE) +#if FE_SUPPORTED(STV0299) static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) { @@ -321,10 +328,12 @@ static int skystar2_rev26_attach(struct flexcop_device *fc, } return 0; } +#else +#define skystar2_rev26_attach NULL #endif /* SkyStar2 DVB-S rev 2.7 */ -#if defined(CONFIG_DVB_S5H1420_MODULE) +#if FE_SUPPORTED(S5H1420) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_ITD1000) static struct s5h1420_config skystar2_rev2_7_s5h1420_config = { .demod_address = 0x53, .invert = 1, @@ -385,10 +394,12 @@ fail: fc->fc_i2c_adap[0].no_base_addr = 0; return 0; } +#else +#define skystar2_rev27_attach NULL #endif /* SkyStar2 rev 2.8 */ -#if defined(CONFIG_DVB_CX24123_MODULE) +#if FE_SUPPORTED(CX24123) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_CX24113) static struct cx24123_config skystar2_rev2_8_cx24123_config = { .demod_address = 0x55, .dont_use_pll = 1, @@ -433,10 +444,12 @@ static int skystar2_rev28_attach(struct flexcop_device *fc, * IR-receiver (PIC16F818) - but the card has no input for that ??? */ return 1; } +#else +#define skystar2_rev28_attach NULL #endif /* AirStar DVB-T */ -#if defined(CONFIG_DVB_MT352_MODULE) +#if FE_SUPPORTED(MT352) static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe) { static u8 mt352_clock_config[] = { 0x89, 0x18, 0x2d }; @@ -495,10 +508,12 @@ static int airstar_dvbt_attach(struct flexcop_device *fc, } return 0; } +#else +#define airstar_dvbt_attach NULL #endif /* AirStar ATSC 1st generation */ -#if defined(CONFIG_DVB_BCM3510_MODULE) +#if FE_SUPPORTED(BCM3510) static int flexcop_fe_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char* name) { @@ -517,10 +532,12 @@ static int airstar_atsc1_attach(struct flexcop_device *fc, fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, i2c); return fc->fe != NULL; } +#else +#define airstar_atsc1_attach NULL #endif /* AirStar ATSC 2nd generation */ -#if defined(CONFIG_DVB_NXT200X_MODULE) +#if FE_SUPPORTED(NXT200X) && FE_SUPPORTED(PLL) static struct nxt200x_config samsung_tbmv_config = { .demod_address = 0x0a, }; @@ -535,10 +552,12 @@ static int airstar_atsc2_attach(struct flexcop_device *fc, return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, DVB_PLL_SAMSUNG_TBMV); } +#else +#define airstar_atsc2_attach NULL #endif /* AirStar ATSC 3rd generation */ -#if defined(CONFIG_DVB_LGDT330X_MODULE) +#if FE_SUPPORTED(LGDT330X) static struct lgdt330x_config air2pc_atsc_hd5000_config = { .demod_address = 0x59, .demod_chip = LGDT3303, @@ -556,10 +575,12 @@ static int airstar_atsc3_attach(struct flexcop_device *fc, return !!dvb_attach(simple_tuner_attach, fc->fe, i2c, 0x61, TUNER_LG_TDVS_H06XF); } +#else +#define airstar_atsc3_attach NULL #endif /* CableStar2 DVB-C */ -#if defined(CONFIG_DVB_STV0297_MODULE) +#if FE_SUPPORTED(STV0297) static int alps_tdee4_stv0297_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep) { @@ -698,39 +719,23 @@ static int cablestar2_attach(struct flexcop_device *fc, fc->fe->ops.tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params; return 1; } +#else +#define cablestar2_attach NULL #endif static struct { flexcop_device_type_t type; int (*attach)(struct flexcop_device *, struct i2c_adapter *); } flexcop_frontends[] = { -#if defined(CONFIG_DVB_S5H1420_MODULE) { FC_SKY_REV27, skystar2_rev27_attach }, -#endif -#if defined(CONFIG_DVB_CX24123_MODULE) { FC_SKY_REV28, skystar2_rev28_attach }, -#endif -#if defined(CONFIG_DVB_STV0299_MODULE) { FC_SKY_REV26, skystar2_rev26_attach }, -#endif -#if defined(CONFIG_DVB_MT352_MODULE) { FC_AIR_DVBT, airstar_dvbt_attach }, -#endif -#if defined(CONFIG_DVB_NXT200X_MODULE) { FC_AIR_ATSC2, airstar_atsc2_attach }, -#endif -#if defined(CONFIG_DVB_LGDT330X_MODULE) { FC_AIR_ATSC3, airstar_atsc3_attach }, -#endif -#if defined(CONFIG_DVB_BCM3510_MODULE) { FC_AIR_ATSC1, airstar_atsc1_attach }, -#endif -#if defined(CONFIG_DVB_STV0297_MODULE) { FC_CABLE, cablestar2_attach }, -#endif -#if defined(CONFIG_DVB_MT312_MODULE) { FC_SKY_REV23, skystar2_rev23_attach }, -#endif }; /* try to figure out the frontend */ @@ -738,6 +743,8 @@ int flexcop_frontend_init(struct flexcop_device *fc) { int i; for (i = 0; i < ARRAY_SIZE(flexcop_frontends); i++) { + if (!flexcop_frontends[i].attach) + continue; /* type needs to be set before, because of some workarounds * done based on the probed card type */ fc->dev_type = flexcop_frontends[i].type; -- cgit v1.2.3 From 8ec3691caa62885c1f9f47b05f580ac47b5db528 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 11 Jun 2009 15:31:22 -0700 Subject: b2c2: Use dvb-pll for AirStar DVB-T's tuner From: Trent Piepho The code in samsung_tdtc9251dh0_calc_regs() is equivalent to what dvb-pll's code does. Priority: normal Signed-off-by: Trent Piepho --- linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c | 38 ++++--------------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 9a6307a34..0669a14da 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -449,7 +449,7 @@ static int skystar2_rev28_attach(struct flexcop_device *fc, #endif /* AirStar DVB-T */ -#if FE_SUPPORTED(MT352) +#if FE_SUPPORTED(MT352) && FE_SUPPORTED(PLL) static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe) { static u8 mt352_clock_config[] = { 0x89, 0x18, 0x2d }; @@ -467,32 +467,6 @@ static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe) return 0; } -static int samsung_tdtc9251dh0_calc_regs(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params, u8* pllbuf, int buf_len) -{ - u32 div; - unsigned char bs = 0; - - if (buf_len < 5) - return -EINVAL; - -#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ - div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; - if (params->frequency >= 48000000 && params->frequency <= 154000000) \ - bs = 0x09; - if (params->frequency >= 161000000 && params->frequency <= 439000000) \ - bs = 0x0a; - if (params->frequency >= 447000000 && params->frequency <= 863000000) \ - bs = 0x08; - - pllbuf[0] = 0x61; - pllbuf[1] = div >> 8; - pllbuf[2] = div & 0xff; - pllbuf[3] = 0xcc; - pllbuf[4] = bs; - return 5; -} - static struct mt352_config samsung_tdtc9251dh0_config = { .demod_address = 0x0f, .demod_init = samsung_tdtc9251dh0_demod_init, @@ -502,11 +476,11 @@ static int airstar_dvbt_attach(struct flexcop_device *fc, struct i2c_adapter *i2c) { fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, i2c); - if (fc->fe != NULL) { - fc->fe->ops.tuner_ops.calc_regs = samsung_tdtc9251dh0_calc_regs; - return 1; - } - return 0; + if (!fc->fe) + return 0; + + return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, + DVB_PLL_SAMSUNG_TDTC9251DH0); } #else #define airstar_dvbt_attach NULL -- cgit v1.2.3 From a33131bc8be60a05f9abfa4bc0ce81b30a2af7e8 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 11 Jun 2009 15:31:22 -0700 Subject: b2c2: Use dvb-pll for Skystar2 rev 2.3 and rev 2.6 From: Trent Piepho The code in skystar23_samsung_tbdu18132_tuner_set_params() and samsung_tbmu24112_tuner_set_params() is equivalent to what the dvb-pll code does. There could be an issue because the dvb-pll code will probe to check for the tuner, while the previous code didn't do any checks. Priority: normal Signed-off-by: Trent Piepho --- linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c | 105 +++++++----------------- 1 file changed, 31 insertions(+), 74 deletions(-) diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 0669a14da..928bc4d4d 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -66,7 +66,7 @@ static int flexcop_sleep(struct dvb_frontend* fe) #endif /* SkyStar2 DVB-S rev 2.3 */ -#if FE_SUPPORTED(MT312) +#if FE_SUPPORTED(MT312) && FE_SUPPORTED(PLL) static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) { /* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */ @@ -155,55 +155,34 @@ static struct mt312_config skystar23_samsung_tbdu18132_config = { .demod_address = 0x0e, }; -static int skystar23_samsung_tbdu18132_tuner_set_params(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params) -{ - u8 buf[4]; - u32 div; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, - .len = sizeof(buf) }; - struct flexcop_device *fc = fe->dvb->priv; - div = (params->frequency + (125/2)) / 125; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = (div >> 0) & 0xff; - buf[2] = 0x84 | ((div >> 10) & 0x60); - buf[3] = 0x80; - - if (params->frequency < 1550000) - buf[3] |= 0x02; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&fc->fc_i2c_adap[0].i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - static int skystar2_rev23_attach(struct flexcop_device *fc, struct i2c_adapter *i2c) { + struct dvb_frontend_ops *ops; + fc->fe = dvb_attach(mt312_attach, &skystar23_samsung_tbdu18132_config, i2c); - if (fc->fe != NULL) { - struct dvb_frontend_ops *ops = &fc->fe->ops; - ops->tuner_ops.set_params = - skystar23_samsung_tbdu18132_tuner_set_params; - ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd; - ops->diseqc_send_burst = flexcop_diseqc_send_burst; - ops->set_tone = flexcop_set_tone; - ops->set_voltage = flexcop_set_voltage; - fc->fe_sleep = ops->sleep; - ops->sleep = flexcop_sleep; - return 1; - } - return 0; + if (!fc->fe) + return 0; + + if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c, + DVB_PLL_SAMSUNG_TBDU18132)) + return 0; + + ops = &fc->fe->ops; + ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd; + ops->diseqc_send_burst = flexcop_diseqc_send_burst; + ops->set_tone = flexcop_set_tone; + ops->set_voltage = flexcop_set_voltage; + fc->fe_sleep = ops->sleep; + ops->sleep = flexcop_sleep; + return 1; } #else #define skystar2_rev23_attach NULL #endif /* SkyStar2 DVB-S rev 2.6 */ -#if FE_SUPPORTED(STV0299) +#if FE_SUPPORTED(STV0299) && FE_SUPPORTED(PLL) static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) { @@ -232,31 +211,6 @@ static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe, return 0; } -static int samsung_tbmu24112_tuner_set_params(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params) -{ - u8 buf[4]; - u32 div; - struct i2c_msg msg = { - .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; - struct flexcop_device *fc = fe->dvb->priv; - div = params->frequency / 125; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0x84; /* 0xC4 */ - buf[3] = 0x08; - - if (params->frequency < 1500000) - buf[3] |= 0x10; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&fc->fc_i2c_adap[0].i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - static u8 samsung_tbmu24112_inittab[] = { 0x01, 0x15, 0x02, 0x30, @@ -318,15 +272,18 @@ static int skystar2_rev26_attach(struct flexcop_device *fc, struct i2c_adapter *i2c) { fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, i2c); - if (fc->fe != NULL) { - struct dvb_frontend_ops *ops = &fc->fe->ops; - ops->tuner_ops.set_params = samsung_tbmu24112_tuner_set_params; - ops->set_voltage = flexcop_set_voltage; - fc->fe_sleep = ops->sleep; - ops->sleep = flexcop_sleep; - return 1; - } - return 0; + if (!fc->fe) + return 0; + + if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c, + DVB_PLL_SAMSUNG_TBMU24112)) + return 0; + + fc->fe->ops.set_voltage = flexcop_set_voltage; + fc->fe_sleep = fc->fe->ops.sleep; + fc->fe->ops.sleep = flexcop_sleep; + return 1; + } #else #define skystar2_rev26_attach NULL -- cgit v1.2.3 From d6ff0d4097028addee38d92dff142e1e28259275 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 11 Jun 2009 15:31:22 -0700 Subject: b2c2: Use dvb-pll for Cablestar2 From: Trent Piepho The dvb-pll code should do the same thing that alps_tdee4_stv0297_tuner_set_params() was doing. Except the dvb-pll code will check for tuner presence when attaching, while the old code didn't. This tuner appears to not be behind the stv0297's I2C gate but is instead on a different I2C adapter provided by the flexcop chip. The old code would turn the I2C gate off each time the tuner was used. I've changed it to turn the gate off when the tuner is attached and then disable the gate control function. This should result in the gate staying off, which should be even better. Priority: normal Signed-off-by: Trent Piepho --- linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c | 73 +++++++------------------ 1 file changed, 19 insertions(+), 54 deletions(-) diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 928bc4d4d..f56ffc0e2 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -511,54 +511,7 @@ static int airstar_atsc3_attach(struct flexcop_device *fc, #endif /* CableStar2 DVB-C */ -#if FE_SUPPORTED(STV0297) -static int alps_tdee4_stv0297_tuner_set_params(struct dvb_frontend* fe, - struct dvb_frontend_parameters *fep) -{ - struct flexcop_device *fc = fe->dvb->priv; - u8 buf[4]; - u16 div; - int ret; - -/* 62.5 kHz * 10 */ -#define REF_FREQ 625 -#define FREQ_OFFSET 36125 - - div = ((fep->frequency/1000 + FREQ_OFFSET) * 10) / REF_FREQ; -/* 4 MHz = 4000 KHz */ - - buf[0] = (u8)( div >> 8) & 0x7f; - buf[1] = (u8) div & 0xff; - -/* F(osc) = N * Reference Freq. (62.5 kHz) - * byte 2 : 0 N14 N13 N12 N11 N10 N9 N8 - * byte 3 : N7 N6 N5 N4 N3 N2 N1 N0 - * byte 4 : 1 * * AGD R3 R2 R1 R0 - * byte 5 : C1 * RE RTS BS4 BS3 BS2 BS1 - * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95 */ - buf[2] = 0x95; - -/* Range(MHz) C1 * RE RTS BS4 BS3 BS2 BS1 Byte 5 - * 47 - 153 0 * 0 0 0 0 0 1 0x01 - * 153 - 430 0 * 0 0 0 0 1 0 0x02 - * 430 - 822 0 * 0 0 1 0 0 0 0x08 - * 822 - 862 1 * 0 0 1 0 0 0 0x88 */ - - if (fep->frequency <= 153000000) buf[3] = 0x01; - else if (fep->frequency <= 430000000) buf[3] = 0x02; - else if (fep->frequency <= 822000000) buf[3] = 0x08; - else buf[3] = 0x88; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - deb_tuner("tuner buffer for %d Hz: %x %x %x %x\n", fep->frequency, - buf[0], buf[1], buf[2], buf[3]); - ret = fc->i2c_request(&fc->fc_i2c_adap[2], - FC_WRITE, 0x61, buf[0], &buf[1], 3); - deb_tuner("tuner write returned: %d\n",ret); - return ret; -} - +#if FE_SUPPORTED(STV0297) && FE_SUPPORTED(PLL) static u8 alps_tdee4_stv0297_inittab[] = { 0x80, 0x01, 0x80, 0x00, @@ -642,13 +595,25 @@ static int cablestar2_attach(struct flexcop_device *fc, { fc->fc_i2c_adap[0].no_base_addr = 1; fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c); - if (!fc->fe) { - /* Reset for next frontend to try */ - fc->fc_i2c_adap[0].no_base_addr = 0; - return 0; - } - fc->fe->ops.tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params; + if (!fc->fe) + goto fail; + + /* This tuner doesn't use the stv0297's I2C gate, but instead the + * tuner is connected to a different flexcop I2C adapter. */ + if (fc->fe->ops.i2c_gate_ctrl) + fc->fe->ops.i2c_gate_ctrl(fc->fe, 0); + fc->fe->ops.i2c_gate_ctrl = NULL; + + if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, + fc->fc_i2c_adap[2].i2c_adap, DVB_PLL_TDEE4)) + goto fail; + return 1; + +fail: + /* Reset for next frontend to try */ + fc->fc_i2c_adap[0].no_base_addr = 0; + return 0; } #else #define cablestar2_attach NULL -- cgit v1.2.3 From 8cba5139f0ceaec9221a9eec09bab7c6fdc4b46b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 17 Jun 2009 04:38:10 +0000 Subject: lgs8gxx: add lgs8g75 support From: David Wong lgs8gxx: add lgs8g75 demodulator support Signed-off-by: David T.L. Wong Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/frontends/lgs8gxx.c | 505 +++++++++++++++++------ linux/drivers/media/dvb/frontends/lgs8gxx.h | 11 +- linux/drivers/media/dvb/frontends/lgs8gxx_priv.h | 12 +- 3 files changed, 401 insertions(+), 127 deletions(-) diff --git a/linux/drivers/media/dvb/frontends/lgs8gxx.c b/linux/drivers/media/dvb/frontends/lgs8gxx.c index 1ce30aa53..226536ae0 100644 --- a/linux/drivers/media/dvb/frontends/lgs8gxx.c +++ b/linux/drivers/media/dvb/frontends/lgs8gxx.c @@ -1,9 +1,9 @@ /* - * Support for Legend Silicon DMB-TH demodulator - * LGS8913, LGS8GL5 + * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator + * LGS8913, LGS8GL5, LGS8G75 * experimental support LGS8G42, LGS8G52 * - * Copyright (C) 2007,2008 David T.L. Wong + * Copyright (C) 2007-2009 David T.L. Wong * Copyright (C) 2008 Sirius International (Hong Kong) Limited * Timothy Lee (for initial work on LGS8GL5) * @@ -46,6 +46,42 @@ module_param(fake_signal_str, int, 0644); MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913." "Signal strength calculation is slow.(default:on)."); +static const u8 lgs8g75_initdat[] = { + 0x01, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0xF5, 0xA8, 0xF5, 0xB8, 0xF5, 0x88, 0xF5, + 0x89, 0xF5, 0x87, 0x75, 0xD0, 0x00, 0x11, 0x50, + 0x11, 0x50, 0xF4, 0xF5, 0x80, 0xF5, 0x90, 0xF5, + 0xA0, 0xF5, 0xB0, 0x75, 0x81, 0x30, 0x80, 0x01, + 0x32, 0x90, 0x80, 0x12, 0x74, 0xFF, 0xF0, 0x90, + 0x80, 0x13, 0x74, 0x1F, 0xF0, 0x90, 0x80, 0x23, + 0x74, 0x01, 0xF0, 0x90, 0x80, 0x22, 0xF0, 0x90, + 0x00, 0x48, 0x74, 0x00, 0xF0, 0x90, 0x80, 0x4D, + 0x74, 0x05, 0xF0, 0x90, 0x80, 0x09, 0xE0, 0x60, + 0x21, 0x12, 0x00, 0xDD, 0x14, 0x60, 0x1B, 0x12, + 0x00, 0xDD, 0x14, 0x60, 0x15, 0x12, 0x00, 0xDD, + 0x14, 0x60, 0x0F, 0x12, 0x00, 0xDD, 0x14, 0x60, + 0x09, 0x12, 0x00, 0xDD, 0x14, 0x60, 0x03, 0x12, + 0x00, 0xDD, 0x90, 0x80, 0x42, 0xE0, 0x60, 0x0B, + 0x14, 0x60, 0x0C, 0x14, 0x60, 0x0D, 0x14, 0x60, + 0x0E, 0x01, 0xB3, 0x74, 0x04, 0x01, 0xB9, 0x74, + 0x05, 0x01, 0xB9, 0x74, 0x07, 0x01, 0xB9, 0x74, + 0x0A, 0xC0, 0xE0, 0x74, 0xC8, 0x12, 0x00, 0xE2, + 0xD0, 0xE0, 0x14, 0x70, 0xF4, 0x90, 0x80, 0x09, + 0xE0, 0x70, 0xAE, 0x12, 0x00, 0xF6, 0x12, 0x00, + 0xFE, 0x90, 0x00, 0x48, 0xE0, 0x04, 0xF0, 0x90, + 0x80, 0x4E, 0xF0, 0x01, 0x73, 0x90, 0x80, 0x08, + 0xF0, 0x22, 0xF8, 0x7A, 0x0C, 0x79, 0xFD, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, + 0xF6, 0xDA, 0xF2, 0xD8, 0xEE, 0x22, 0x90, 0x80, + 0x65, 0xE0, 0x54, 0xFD, 0xF0, 0x22, 0x90, 0x80, + 0x65, 0xE0, 0x44, 0xC2, 0xF0, 0x22 +}; + /* LGS8GXX internal helper functions */ static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data) @@ -55,7 +91,7 @@ static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data) struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; msg.addr = priv->config->demod_address; - if (reg >= 0xC0) + if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) msg.addr += 0x02; if (debug >= 2) @@ -84,7 +120,7 @@ static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data) }; dev_addr = priv->config->demod_address; - if (reg >= 0xC0) + if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) dev_addr += 0x02; msg[1].addr = msg[0].addr = dev_addr; @@ -112,19 +148,36 @@ static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv) return 0; } +static int wait_reg_mask(struct lgs8gxx_state *priv, u8 reg, u8 mask, + u8 val, u8 delay, u8 tries) +{ + u8 t; + int i; + + for (i = 0; i < tries; i++) { + lgs8gxx_read_reg(priv, reg, &t); + + if ((t & mask) == val) + return 0; + msleep(delay); + } + + return 1; +} + static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv) { const struct lgs8gxx_config *config = priv->config; u8 if_conf; - if_conf = 0x10; /* AGC output on; */ + if_conf = 0x10; /* AGC output on, RF_AGC output off; */ if_conf |= ((config->ext_adc) ? 0x80 : 0x00) | ((config->if_neg_center) ? 0x04 : 0x00) | ((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */ - ((config->ext_adc && config->adc_signed) ? 0x02 : 0x00) | - ((config->ext_adc && config->if_neg_edge) ? 0x01 : 0x00); + ((config->adc_signed) ? 0x02 : 0x00) | + ((config->if_neg_edge) ? 0x01 : 0x00); if (config->ext_adc && (config->prod == LGS8GXX_PROD_LGS8G52)) { @@ -157,39 +210,82 @@ static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/) } dprintk("AFC_INIT_FREQ = 0x%08X\n", v32); - lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32)); - lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8)); - lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16)); - lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24)); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32)); + lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8)); + lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 16)); + lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 24)); + } else { + lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32)); + lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8)); + lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16)); + lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24)); + } return 0; } +static int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv) +{ + u64 val; + u32 v32 = 0; + u8 reg_addr, t; + int i; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + reg_addr = 0x23; + else + reg_addr = 0x48; + + for (i = 0; i < 4; i++) { + lgs8gxx_read_reg(priv, reg_addr, &t); + v32 <<= 8; + v32 |= t; + reg_addr--; + } + + val = v32; + val *= priv->config->if_clk_freq; + val /= (u64)1 << 32; + dprintk("AFC = %u kHz\n", (u32)val); + return 0; +} + static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv) { u8 t; + u8 prod = priv->config->prod; - if (priv->config->prod == LGS8GXX_PROD_LGS8913) + if (prod == LGS8GXX_PROD_LGS8913) lgs8gxx_write_reg(priv, 0xC6, 0x01); - lgs8gxx_read_reg(priv, 0x7E, &t); - lgs8gxx_write_reg(priv, 0x7E, t | 0x01); - - /* clear FEC self reset */ - lgs8gxx_read_reg(priv, 0xC5, &t); - lgs8gxx_write_reg(priv, 0xC5, t & 0xE0); + if (prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x0C, &t); + t &= (~0x04); + lgs8gxx_write_reg(priv, 0x0C, t | 0x80); + lgs8gxx_write_reg(priv, 0x39, 0x00); + lgs8gxx_write_reg(priv, 0x3D, 0x04); + } else if (prod == LGS8GXX_PROD_LGS8913 || + prod == LGS8GXX_PROD_LGS8GL5 || + prod == LGS8GXX_PROD_LGS8G42 || + prod == LGS8GXX_PROD_LGS8G52 || + prod == LGS8GXX_PROD_LGS8G54) { + lgs8gxx_read_reg(priv, 0x7E, &t); + lgs8gxx_write_reg(priv, 0x7E, t | 0x01); + + /* clear FEC self reset */ + lgs8gxx_read_reg(priv, 0xC5, &t); + lgs8gxx_write_reg(priv, 0xC5, t & 0xE0); + } - if (priv->config->prod == LGS8GXX_PROD_LGS8913) { + if (prod == LGS8GXX_PROD_LGS8913) { /* FEC auto detect */ lgs8gxx_write_reg(priv, 0xC1, 0x03); lgs8gxx_read_reg(priv, 0x7C, &t); t = (t & 0x8C) | 0x03; lgs8gxx_write_reg(priv, 0x7C, t); - } - - if (priv->config->prod == LGS8GXX_PROD_LGS8913) { /* BER test mode */ lgs8gxx_read_reg(priv, 0xC3, &t); t = (t & 0xEF) | 0x10; @@ -207,6 +303,32 @@ static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv) int ret = 0; u8 t; + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + u8 t2; + lgs8gxx_read_reg(priv, 0x0C, &t); + t &= (~0x80); + lgs8gxx_write_reg(priv, 0x0C, t); + + lgs8gxx_read_reg(priv, 0x0C, &t); + lgs8gxx_read_reg(priv, 0x19, &t2); + + if (((t&0x03) == 0x01) && (t2&0x01)) { + lgs8gxx_write_reg(priv, 0x6E, 0x05); + lgs8gxx_write_reg(priv, 0x39, 0x02); + lgs8gxx_write_reg(priv, 0x39, 0x03); + lgs8gxx_write_reg(priv, 0x3D, 0x05); + lgs8gxx_write_reg(priv, 0x3E, 0x28); + lgs8gxx_write_reg(priv, 0x53, 0x80); + } else { + lgs8gxx_write_reg(priv, 0x6E, 0x3F); + lgs8gxx_write_reg(priv, 0x39, 0x00); + lgs8gxx_write_reg(priv, 0x3D, 0x04); + } + + lgs8gxx_soft_reset(priv); + return 0; + } + /* turn off auto-detect; manual settings */ lgs8gxx_write_reg(priv, 0x7E, 0); if (priv->config->prod == LGS8GXX_PROD_LGS8913) @@ -226,11 +348,39 @@ static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked) int ret = 0; u8 t; - ret = lgs8gxx_read_reg(priv, 0x4B, &t); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + ret = lgs8gxx_read_reg(priv, 0x13, &t); + else + ret = lgs8gxx_read_reg(priv, 0x4B, &t); if (ret != 0) return ret; - *locked = ((t & 0xC0) == 0xC0) ? 1 : 0; + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + *locked = ((t & 0x80) == 0x80) ? 1 : 0; + else + *locked = ((t & 0xC0) == 0xC0) ? 1 : 0; + return 0; +} + +/* Wait for Code Acquisition Lock */ +static int lgs8gxx_wait_ca_lock(struct lgs8gxx_state *priv, u8 *locked) +{ + int ret = 0; + u8 reg, mask, val; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + reg = 0x13; + mask = 0x80; + val = 0x80; + } else { + reg = 0x4B; + mask = 0xC0; + val = 0xC0; + } + + ret = wait_reg_mask(priv, reg, mask, val, 50, 40); + *locked = (ret == 0) ? 1 : 0; + return 0; } @@ -238,21 +388,30 @@ static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv, u8 *finished) { int ret = 0; - u8 t; + u8 reg, mask, val; - ret = lgs8gxx_read_reg(priv, 0xA4, &t); - if (ret != 0) - return ret; + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + reg = 0x1f; + mask = 0xC0; + val = 0x80; + } else { + reg = 0xA4; + mask = 0x03; + val = 0x01; + } - *finished = ((t & 0x3) == 0x1) ? 1 : 0; + ret = wait_reg_mask(priv, reg, mask, val, 10, 20); + *finished = (ret == 0) ? 1 : 0; return 0; } -static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 *locked) +static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 cpn, + u8 *locked) { - int err; + int err = 0; u8 ad_fini = 0; + u8 t1, t2; if (gi == GI_945) dprintk("try GI 945\n"); @@ -260,17 +419,29 @@ static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 *locked) dprintk("try GI 595\n"); else if (gi == GI_420) dprintk("try GI 420\n"); - lgs8gxx_write_reg(priv, 0x04, gi); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x0C, &t1); + lgs8gxx_read_reg(priv, 0x18, &t2); + t1 &= ~(GI_MASK); + t1 |= gi; + t2 &= 0xFE; + t2 |= cpn ? 0x01 : 0x00; + lgs8gxx_write_reg(priv, 0x0C, t1); + lgs8gxx_write_reg(priv, 0x18, t2); + } else { + lgs8gxx_write_reg(priv, 0x04, gi); + } lgs8gxx_soft_reset(priv); - msleep(50); + err = lgs8gxx_wait_ca_lock(priv, locked); + if (err || !(*locked)) + return err; err = lgs8gxx_is_autodetect_finished(priv, &ad_fini); if (err != 0) return err; if (ad_fini) { - err = lgs8gxx_is_locked(priv, locked); - if (err != 0) - return err; - } + dprintk("auto detect finished\n"); + } else + *locked = 0; return 0; } @@ -285,13 +456,18 @@ static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv, dprintk("%s\n", __func__); lgs8gxx_set_mode_auto(priv); - /* Guard Interval */ - lgs8gxx_write_reg(priv, 0x03, 00); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_write_reg(priv, 0x67, 0xAA); + lgs8gxx_write_reg(priv, 0x6E, 0x3F); + } else { + /* Guard Interval */ + lgs8gxx_write_reg(priv, 0x03, 00); + } for (i = 0; i < 2; i++) { for (j = 0; j < 2; j++) { tmp_gi = GI_945; - err = lgs8gxx_autolock_gi(priv, GI_945, &locked); + err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked); if (err) goto out; if (locked) @@ -299,14 +475,14 @@ static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv, } for (j = 0; j < 2; j++) { tmp_gi = GI_420; - err = lgs8gxx_autolock_gi(priv, GI_420, &locked); + err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked); if (err) goto out; if (locked) goto locked; } tmp_gi = GI_595; - err = lgs8gxx_autolock_gi(priv, GI_595, &locked); + err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked); if (err) goto out; if (locked) @@ -317,8 +493,13 @@ locked: if ((err == 0) && (locked == 1)) { u8 t; - lgs8gxx_read_reg(priv, 0xA2, &t); - *detected_param = t; + if (priv->config->prod != LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0xA2, &t); + *detected_param = t; + } else { + lgs8gxx_read_reg(priv, 0x1F, &t); + *detected_param = t & 0x3F; + } if (tmp_gi == GI_945) dprintk("GI 945 locked\n"); @@ -349,18 +530,28 @@ static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv) lgs8gxx_write_reg(priv, 0x03, 0x01); #endif dprintk("lgs8gxx_auto_detect failed\n"); - } + } else + dprintk("detected param = 0x%02X\n", detected_param); /* Apply detected parameters */ if (priv->config->prod == LGS8GXX_PROD_LGS8913) { u8 inter_leave_len = detected_param & TIM_MASK ; - inter_leave_len = (inter_leave_len == TIM_LONG) ? 0x60 : 0x40; + /* Fix 8913 time interleaver detection bug */ + inter_leave_len = (inter_leave_len == TIM_MIDDLE) ? 0x60 : 0x40; detected_param &= CF_MASK | SC_MASK | LGS_FEC_MASK; detected_param |= inter_leave_len; } - lgs8gxx_write_reg(priv, 0x7D, detected_param); - if (priv->config->prod == LGS8GXX_PROD_LGS8913) - lgs8gxx_write_reg(priv, 0xC0, detected_param); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + u8 t; + lgs8gxx_read_reg(priv, 0x19, &t); + t &= 0x81; + t |= detected_param << 1; + lgs8gxx_write_reg(priv, 0x19, t); + } else { + lgs8gxx_write_reg(priv, 0x7D, detected_param); + if (priv->config->prod == LGS8GXX_PROD_LGS8913) + lgs8gxx_write_reg(priv, 0xC0, detected_param); + } /* lgs8gxx_soft_reset(priv); */ /* Enter manual mode */ @@ -382,9 +573,10 @@ static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv, u8 serial, u8 clk_pol, u8 clk_gated) { int ret = 0; - u8 t; + u8 t, reg_addr; - ret = lgs8gxx_read_reg(priv, 0xC2, &t); + reg_addr = (priv->config->prod == LGS8GXX_PROD_LGS8G75) ? 0x30 : 0xC2; + ret = lgs8gxx_read_reg(priv, reg_addr, &t); if (ret != 0) return ret; @@ -393,13 +585,29 @@ static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv, t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL; t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN; - ret = lgs8gxx_write_reg(priv, 0xC2, t); + ret = lgs8gxx_write_reg(priv, reg_addr, t); if (ret != 0) return ret; return 0; } +/* A/D input peak-to-peak voltage range */ +static int lgs8g75_set_adc_vpp(struct lgs8gxx_state *priv, + u8 sel) +{ + u8 r26 = 0x73, r27 = 0x90; + + if (priv->config->prod != LGS8GXX_PROD_LGS8G75) + return 0; + + r26 |= (sel & 0x01) << 7; + r27 |= (sel & 0x02) >> 1; + lgs8gxx_write_reg(priv, 0x26, r26); + lgs8gxx_write_reg(priv, 0x27, r27); + + return 0; +} /* LGS8913 demod frontend functions */ @@ -428,6 +636,34 @@ static int lgs8913_init(struct lgs8gxx_state *priv) return 0; } +static int lgs8g75_init_data(struct lgs8gxx_state *priv) +{ + const u8 *p = lgs8g75_initdat; + int i; + + lgs8gxx_write_reg(priv, 0xC6, 0x40); + + lgs8gxx_write_reg(priv, 0x3D, 0x04); + lgs8gxx_write_reg(priv, 0x39, 0x00); + + lgs8gxx_write_reg(priv, 0x3A, 0x00); + lgs8gxx_write_reg(priv, 0x38, 0x00); + lgs8gxx_write_reg(priv, 0x3B, 0x00); + lgs8gxx_write_reg(priv, 0x38, 0x00); + + for (i = 0; i < sizeof(lgs8g75_initdat); i++) { + lgs8gxx_write_reg(priv, 0x38, 0x00); + lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff)); + lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8)); + lgs8gxx_write_reg(priv, 0x3C, *p); + p++; + } + + lgs8gxx_write_reg(priv, 0x38, 0x00); + + return 0; +} + static int lgs8gxx_init(struct dvb_frontend *fe) { struct lgs8gxx_state *priv = @@ -440,6 +676,9 @@ static int lgs8gxx_init(struct dvb_frontend *fe) lgs8gxx_read_reg(priv, 0, &data); dprintk("reg 0 = 0x%02X\n", data); + if (config->prod == LGS8GXX_PROD_LGS8G75) + lgs8g75_set_adc_vpp(priv, config->adc_vpp); + /* Setup MPEG output format */ err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts, config->ts_clk_pol, @@ -450,8 +689,7 @@ static int lgs8gxx_init(struct dvb_frontend *fe) if (config->prod == LGS8GXX_PROD_LGS8913) lgs8913_init(priv); lgs8gxx_set_if_freq(priv, priv->config->if_freq); - if (config->prod != LGS8GXX_PROD_LGS8913) - lgs8gxx_set_ad_mode(priv); + lgs8gxx_set_ad_mode(priv); return 0; } @@ -508,12 +746,6 @@ static int lgs8gxx_set_fe(struct dvb_frontend *fe, static int lgs8gxx_get_fe(struct dvb_frontend *fe, struct dvb_frontend_parameters *fe_params) { - struct lgs8gxx_state *priv = fe->demodulator_priv; - u8 t; -#if 0 - int translated_fec = FEC_1_2; -#endif - dprintk("%s\n", __func__); /* TODO: get real readings from device */ @@ -523,47 +755,10 @@ static int lgs8gxx_get_fe(struct dvb_frontend *fe, /* bandwidth */ fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; - - lgs8gxx_read_reg(priv, 0x7D, &t); -#if 0 - /* FEC. No exact match for DMB-TH, pick approx. value */ - switch (t & LGS_FEC_MASK) { - case LGS_FEC_0_4: /* FEC 0.4 */ - translated_fec = FEC_1_2; - break; - case LGS_FEC_0_6: /* FEC 0.6 */ - translated_fec = FEC_2_3; - break; - case LGS_FEC_0_8: /* FEC 0.8 */ - translated_fec = FEC_5_6; - break; - default: - translated_fec = FEC_1_2; - } - fe_params->u.ofdm.code_rate_HP = translated_fec; - fe_params->u.ofdm.code_rate_LP = translated_fec; -#endif fe_params->u.ofdm.code_rate_HP = FEC_AUTO; fe_params->u.ofdm.code_rate_LP = FEC_AUTO; - /* constellation */ - switch (t & SC_MASK) { - case SC_QAM64: - fe_params->u.ofdm.constellation = QAM_64; - break; - case SC_QAM32: - fe_params->u.ofdm.constellation = QAM_32; - break; - case SC_QAM16: - fe_params->u.ofdm.constellation = QAM_16; - break; - case SC_QAM4: - case SC_QAM4NR: - fe_params->u.ofdm.constellation = QPSK; - break; - default: - fe_params->u.ofdm.constellation = QAM_64; - } + fe_params->u.ofdm.constellation = QAM_AUTO; /* transmission mode */ fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; @@ -592,9 +787,19 @@ static int lgs8gxx_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) { struct lgs8gxx_state *priv = fe->demodulator_priv; s8 ret; - u8 t; + u8 t, locked = 0; dprintk("%s\n", __func__); + *fe_status = 0; + + lgs8gxx_get_afc_phase(priv); + lgs8gxx_is_locked(priv, &locked); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + if (locked) + *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + return 0; + } ret = lgs8gxx_read_reg(priv, 0x4B, &t); if (ret != 0) @@ -698,12 +903,33 @@ static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) return 0; } +static int lgs8g75_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) +{ + u8 t; + s16 v = 0; + + dprintk("%s\n", __func__); + + lgs8gxx_read_reg(priv, 0xB1, &t); + v |= t; + v <<= 8; + lgs8gxx_read_reg(priv, 0xB0, &t); + v |= t; + + *signal = v; + dprintk("%s: signal=0x%02X\n", __func__, *signal); + + return 0; +} + static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal) { struct lgs8gxx_state *priv = fe->demodulator_priv; if (priv->config->prod == LGS8GXX_PROD_LGS8913) return lgs8913_read_signal_strength(priv, signal); + else if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + return lgs8g75_read_signal_strength(priv, signal); else return lgs8gxx_read_signal_agc(priv, signal); } @@ -714,7 +940,10 @@ static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr) u8 t; *snr = 0; - lgs8gxx_read_reg(priv, 0x95, &t); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + lgs8gxx_read_reg(priv, 0x34, &t); + else + lgs8gxx_read_reg(priv, 0x95, &t); dprintk("AVG Noise=0x%02X\n", t); *snr = 256 - t; *snr <<= 8; @@ -730,31 +959,68 @@ static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) return 0; } +static void packet_counter_start(struct lgs8gxx_state *priv) +{ + u8 orig, t; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x30, &orig); + orig &= 0xE7; + t = orig | 0x10; + lgs8gxx_write_reg(priv, 0x30, t); + t = orig | 0x18; + lgs8gxx_write_reg(priv, 0x30, t); + t = orig | 0x10; + lgs8gxx_write_reg(priv, 0x30, t); + } else { + lgs8gxx_write_reg(priv, 0xC6, 0x01); + lgs8gxx_write_reg(priv, 0xC6, 0x41); + lgs8gxx_write_reg(priv, 0xC6, 0x01); + } +} + +static void packet_counter_stop(struct lgs8gxx_state *priv) +{ + u8 t; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x30, &t); + t &= 0xE7; + lgs8gxx_write_reg(priv, 0x30, t); + } else { + lgs8gxx_write_reg(priv, 0xC6, 0x81); + } +} + static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber) { struct lgs8gxx_state *priv = fe->demodulator_priv; - u8 r0, r1, r2, r3; - u32 total_cnt, err_cnt; + u8 reg_err, reg_total, t; + u32 total_cnt = 0, err_cnt = 0; + int i; dprintk("%s\n", __func__); - lgs8gxx_write_reg(priv, 0xc6, 0x01); - lgs8gxx_write_reg(priv, 0xc6, 0x41); - lgs8gxx_write_reg(priv, 0xc6, 0x01); - + packet_counter_start(priv); msleep(200); + packet_counter_stop(priv); + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + reg_total = 0x28; reg_err = 0x2C; + } else { + reg_total = 0xD0; reg_err = 0xD4; + } - lgs8gxx_write_reg(priv, 0xc6, 0x81); - lgs8gxx_read_reg(priv, 0xd0, &r0); - lgs8gxx_read_reg(priv, 0xd1, &r1); - lgs8gxx_read_reg(priv, 0xd2, &r2); - lgs8gxx_read_reg(priv, 0xd3, &r3); - total_cnt = (r3 << 24) | (r2 << 16) | (r1 << 8) | (r0); - lgs8gxx_read_reg(priv, 0xd4, &r0); - lgs8gxx_read_reg(priv, 0xd5, &r1); - lgs8gxx_read_reg(priv, 0xd6, &r2); - lgs8gxx_read_reg(priv, 0xd7, &r3); - err_cnt = (r3 << 24) | (r2 << 16) | (r1 << 8) | (r0); + for (i = 0; i < 4; i++) { + total_cnt <<= 8; + lgs8gxx_read_reg(priv, reg_total+3-i, &t); + total_cnt |= t; + } + for (i = 0; i < 4; i++) { + err_cnt <<= 8; + lgs8gxx_read_reg(priv, reg_err+3-i, &t); + err_cnt |= t; + } dprintk("error=%d total=%d\n", err_cnt, total_cnt); if (total_cnt == 0) @@ -844,6 +1110,9 @@ struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, sizeof(struct dvb_frontend_ops)); priv->frontend.demodulator_priv = priv; + if (config->prod == LGS8GXX_PROD_LGS8G75) + lgs8g75_init_data(priv); + return &priv->frontend; error_out: diff --git a/linux/drivers/media/dvb/frontends/lgs8gxx.h b/linux/drivers/media/dvb/frontends/lgs8gxx.h index 321d366a8..33c3c5e16 100644 --- a/linux/drivers/media/dvb/frontends/lgs8gxx.h +++ b/linux/drivers/media/dvb/frontends/lgs8gxx.h @@ -1,9 +1,9 @@ /* - * Support for Legend Silicon DMB-TH demodulator - * LGS8913, LGS8GL5 + * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator + * LGS8913, LGS8GL5, LGS8G75 * experimental support LGS8G42, LGS8G52 * - * Copyright (C) 2007,2008 David T.L. Wong + * Copyright (C) 2007-2009 David T.L. Wong * Copyright (C) 2008 Sirius International (Hong Kong) Limited * Timothy Lee (for initial work on LGS8GL5) * @@ -34,6 +34,7 @@ #define LGS8GXX_PROD_LGS8G42 3 #define LGS8GXX_PROD_LGS8G52 4 #define LGS8GXX_PROD_LGS8G54 5 +#define LGS8GXX_PROD_LGS8G75 6 struct lgs8gxx_config { @@ -70,6 +71,10 @@ struct lgs8gxx_config { /*IF use Negative center frequency*/ u8 if_neg_center; + /*8G75 internal ADC input range selection*/ + /*0: 0.8Vpp, 1: 1.0Vpp, 2: 1.6Vpp, 3: 2.0Vpp*/ + u8 adc_vpp; + /* slave address and configuration of the tuner */ u8 tuner_address; }; diff --git a/linux/drivers/media/dvb/frontends/lgs8gxx_priv.h b/linux/drivers/media/dvb/frontends/lgs8gxx_priv.h index 9776d3068..8ef376f14 100644 --- a/linux/drivers/media/dvb/frontends/lgs8gxx_priv.h +++ b/linux/drivers/media/dvb/frontends/lgs8gxx_priv.h @@ -1,9 +1,9 @@ /* - * Support for Legend Silicon DMB-TH demodulator - * LGS8913, LGS8GL5 + * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator + * LGS8913, LGS8GL5, LGS8G75 * experimental support LGS8G42, LGS8G52 * - * Copyright (C) 2007,2008 David T.L. Wong + * Copyright (C) 2007-2009 David T.L. Wong * Copyright (C) 2008 Sirius International (Hong Kong) Limited * Timothy Lee (for initial work on LGS8GL5) * @@ -38,7 +38,7 @@ struct lgs8gxx_state { #define SC_QAM64 0x10 /* 64QAM modulation */ #define SC_QAM32 0x0C /* 32QAM modulation */ #define SC_QAM16 0x08 /* 16QAM modulation */ -#define SC_QAM4NR 0x04 /* 4QAM modulation */ +#define SC_QAM4NR 0x04 /* 4QAM-NR modulation */ #define SC_QAM4 0x00 /* 4QAM modulation */ #define LGS_FEC_MASK 0x03 /* FEC Rate Mask */ @@ -47,8 +47,8 @@ struct lgs8gxx_state { #define LGS_FEC_0_8 0x02 /* FEC Rate 0.8 */ #define TIM_MASK 0x20 /* Time Interleave Length Mask */ -#define TIM_LONG 0x00 /* Time Interleave Length = 720 */ -#define TIM_MIDDLE 0x20 /* Time Interleave Length = 240 */ +#define TIM_LONG 0x20 /* Time Interleave Length = 720 */ +#define TIM_MIDDLE 0x00 /* Time Interleave Length = 240 */ #define CF_MASK 0x80 /* Control Frame Mask */ #define CF_EN 0x80 /* Control Frame On */ -- cgit v1.2.3 From 8b9e242409ed16e33bc5b4e7ab3c83724a221366 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 17 Jun 2009 04:38:12 +0000 Subject: cx23885: add card Magic-Pro ProHDTV Extreme 2 From: David Wong cx23885: add card Magic-Pro ProHDTV Extreme 2 PCI-E. Signed-off-by: David T.L. Wong Signed-off-by: Mauro Carvalho Chehab --- linux/Documentation/video4linux/CARDLIST.cx23885 | 1 + linux/drivers/media/video/cx23885/cx23885-cards.c | 12 ++++++++- linux/drivers/media/video/cx23885/cx23885-dvb.c | 33 +++++++++++++++++++++++ linux/drivers/media/video/cx23885/cx23885.h | 1 + 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/linux/Documentation/video4linux/CARDLIST.cx23885 b/linux/Documentation/video4linux/CARDLIST.cx23885 index 450b8f8c3..19feb514f 100644 --- a/linux/Documentation/video4linux/CARDLIST.cx23885 +++ b/linux/Documentation/video4linux/CARDLIST.cx23885 @@ -21,3 +21,4 @@ 20 -> Hauppauge WinTV-HVR1255 [0070:2251] 21 -> Hauppauge WinTV-HVR1210 [0070:2291,0070:2295] 22 -> Mygica X8506 DMB-TH [14f1:8651] + 23 -> Magic-Pro ProHDTV Extreme 2 [14f1:8657] diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c index 35b2abd6a..bf426f041 100644 --- a/linux/drivers/media/video/cx23885/cx23885-cards.c +++ b/linux/drivers/media/video/cx23885/cx23885-cards.c @@ -202,6 +202,10 @@ struct cx23885_board cx23885_boards[] = { .name = "Mygica X8506 DMB-TH", .portb = CX23885_MPEG_DVB, }, + [CX23885_BOARD_MAGICPRO_PROHDTVE2] = { + .name = "Magic-Pro ProHDTV Extreme 2", + .portb = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -325,6 +329,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x14f1, .subdevice = 0x8651, .card = CX23885_BOARD_MYGICA_X8506, + }, { + .subvendor = 0x14f1, + .subdevice = 0x8657, + .card = CX23885_BOARD_MAGICPRO_PROHDTVE2, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -716,8 +724,9 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx23885_gpio_set(dev, GPIO_9); break; case CX23885_BOARD_MYGICA_X8506: + case CX23885_BOARD_MAGICPRO_PROHDTVE2: /* GPIO-1 reset XC5000 */ - /* GPIO-2 reset LGS8GL5 */ + /* GPIO-2 reset LGS8GL5 / LGS8G75 */ cx_set(GP0_IO, 0x00060000); cx_clear(GP0_IO, 0x00000006); mdelay(100); @@ -828,6 +837,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; case CX23885_BOARD_MYGICA_X8506: + case CX23885_BOARD_MAGICPRO_PROHDTVE2: ts1->gen_ctrl_val = 0x5; /* Parallel */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 4bab7c814..85fac41e7 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -488,6 +488,26 @@ static int cx23885_dvb_set_frontend(struct dvb_frontend *fe, port->set_frontend_save(fe, param) : -ENODEV; } +static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = { + .prod = LGS8GXX_PROD_LGS8G75, + .demod_address = 0x19, + .serial_ts = 0, + .ts_clk_pol = 1, + .ts_clk_gated = 1, + .if_clk_freq = 30400, /* 30.4 MHz */ + .if_freq = 6500, /* 6.50 MHz */ + .if_neg_center = 1, + .ext_adc = 0, + .adc_signed = 1, + .adc_vpp = 2, /* 1.6 Vpp */ + .if_neg_edge = 1, +}; + +static struct xc5000_config magicpro_prohdtve2_xc5000_config = { + .i2c_address = 0x61, + .if_khz = 6500, +}; + static int dvb_register(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; @@ -834,6 +854,19 @@ static int dvb_register(struct cx23885_tsport *port) &mygica_x8506_xc5000_config); } break; + case CX23885_BOARD_MAGICPRO_PROHDTVE2: + i2c_bus = &dev->i2c_bus[0]; + i2c_bus2 = &dev->i2c_bus[1]; + fe0->dvb.frontend = dvb_attach(lgs8gxx_attach, + &magicpro_prohdtve2_lgs8g75_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(xc5000_attach, + fe0->dvb.frontend, + &i2c_bus2->i2c_adap, + &magicpro_prohdtve2_xc5000_config); + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h index fe3feb001..2a384e6aa 100644 --- a/linux/drivers/media/video/cx23885/cx23885.h +++ b/linux/drivers/media/video/cx23885/cx23885.h @@ -77,6 +77,7 @@ #define CX23885_BOARD_HAUPPAUGE_HVR1255 20 #define CX23885_BOARD_HAUPPAUGE_HVR1210 21 #define CX23885_BOARD_MYGICA_X8506 22 +#define CX23885_BOARD_MAGICPRO_PROHDTVE2 23 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 -- cgit v1.2.3 From c847e3dd95dce3751a45cac3b01a73b6f8f293c9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 27 Jul 2009 08:24:29 -0300 Subject: mtv9v011: Add a missing chip version to the driver From: Mauro Carvalho Chehab Some mt9v011 webcams report 0x8332 chip version, instead of 0x8243. From the revision history at the mt9v011 datasheet, it seems that the chip version has changed from the first release of the chip. Thanks-to hermann pitton for pointing this to me, on his tests with a Silvercrest webcam. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 3 ++- linux/drivers/media/video/mt9v011.c | 14 +++++++++----- linux/drivers/media/video/mt9v011.h | 3 ++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 8a22caf01..6e4a44331 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1838,7 +1838,8 @@ static int em28xx_hint_sensor(struct em28xx *dev) version = be16_to_cpu(version_be); switch (version) { - case 0x8243: /* mt9v011 640x480 1.3 Mpix sensor */ + case 0x8232: /* mt9v011 640x480 1.3 Mpix sensor */ + case 0x8243: /* mt9v011 rev B 640x480 1.3 Mpix sensor */ dev->model = EM2820_BOARD_SILVERCREST_WEBCAM; sensor_name = "mt9v011"; dev->em28xx_sensor = EM28XX_MT9V011; diff --git a/linux/drivers/media/video/mt9v011.c b/linux/drivers/media/video/mt9v011.c index bf68ea63f..c9706b889 100644 --- a/linux/drivers/media/video/mt9v011.c +++ b/linux/drivers/media/video/mt9v011.c @@ -403,10 +403,13 @@ static int mt9v011_s_register(struct v4l2_subdev *sd, static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { + u16 version; struct i2c_client *client = v4l2_get_subdevdata(sd); + version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011, - MT9V011_VERSION); + version); } static const struct v4l2_subdev_core_ops mt9v011_core_ops = { @@ -459,8 +462,9 @@ static int mt9v011_probe(struct i2c_client *c, /* Check if the sensor is really a MT9V011 */ version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); - if (version != MT9V011_VERSION) { - v4l2_info(sd, "*** unknown micron chip detected (0x%04x.\n", + if ((version != MT9V011_VERSION) && + (version != MT9V011_REV_B_VERSION)) { + v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n", version); kfree(core); return -EINVAL; @@ -471,8 +475,8 @@ static int mt9v011_probe(struct i2c_client *c, core->height = 480; core->xtal = 27000000; /* Hz */ - v4l_info(c, "chip found @ 0x%02x (%s)\n", - c->addr << 1, c->adapter->name); + v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n", + c->addr << 1, c->adapter->name, version); return 0; } diff --git a/linux/drivers/media/video/mt9v011.h b/linux/drivers/media/video/mt9v011.h index 9e443ee30..3350fd608 100644 --- a/linux/drivers/media/video/mt9v011.h +++ b/linux/drivers/media/video/mt9v011.h @@ -30,6 +30,7 @@ #define R35_MT9V011_GLOBAL_GAIN 0x35 #define RF1_MT9V011_CHIP_ENABLE 0xf1 -#define MT9V011_VERSION 0x8243 +#define MT9V011_VERSION 0x8232 +#define MT9V011_REV_B_VERSION 0x8243 #endif -- cgit v1.2.3 From f7ea703f977df67b32fa86271669d8d4a5d3ce7f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 Jun 2009 11:11:57 +0000 Subject: zl10353 and qt1010: fix stack corruption bug From: Jan Nikitenko Fixes stack corruption bug present in dump_regs function of zl10353 and qt1010 drivers: the buffer buf was one byte smaller than required - there are 4 chars for address prefix, 16 * 3 chars for dump of 16 eeprom bytes per line and 1 byte for zero ending the string required, i.e. 53 bytes, but only 52 were provided. The one byte missing in stack based buffer buf can cause stack corruption possibly leading to kernel oops, as discovered originally with af9015 driver (af9015: fix stack corruption bug). Signed-off-by: Jan Nikitenko Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/common/tuners/qt1010.c | 12 +++++------- linux/drivers/media/dvb/frontends/zl10353.c | 12 +++++------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/linux/drivers/media/common/tuners/qt1010.c b/linux/drivers/media/common/tuners/qt1010.c index 4afed44ab..d877c59a7 100644 --- a/linux/drivers/media/common/tuners/qt1010.c +++ b/linux/drivers/media/common/tuners/qt1010.c @@ -65,24 +65,22 @@ static int qt1010_writereg(struct qt1010_priv *priv, u8 reg, u8 val) /* dump all registers */ static void qt1010_dump_regs(struct qt1010_priv *priv) { - char buf[52], buf2[4]; u8 reg, val; for (reg = 0; ; reg++) { if (reg % 16 == 0) { if (reg) - printk("%s\n", buf); - sprintf(buf, "%02x: ", reg); + printk(KERN_CONT "\n"); + printk(KERN_DEBUG "%02x:", reg); } if (qt1010_readreg(priv, reg, &val) == 0) - sprintf(buf2, "%02x ", val); + printk(KERN_CONT " %02x", val); else - strcpy(buf2, "-- "); - strcat(buf, buf2); + printk(KERN_CONT " --"); if (reg == 0x2f) break; } - printk("%s\n", buf); + printk(KERN_CONT "\n"); } static int qt1010_set_params(struct dvb_frontend *fe, diff --git a/linux/drivers/media/dvb/frontends/zl10353.c b/linux/drivers/media/dvb/frontends/zl10353.c index 6b58bf961..a69e9602c 100644 --- a/linux/drivers/media/dvb/frontends/zl10353.c +++ b/linux/drivers/media/dvb/frontends/zl10353.c @@ -102,7 +102,6 @@ static int zl10353_read_register(struct zl10353_state *state, u8 reg) static void zl10353_dump_regs(struct dvb_frontend *fe) { struct zl10353_state *state = fe->demodulator_priv; - char buf[52], buf2[4]; int ret; u8 reg; @@ -110,19 +109,18 @@ static void zl10353_dump_regs(struct dvb_frontend *fe) for (reg = 0; ; reg++) { if (reg % 16 == 0) { if (reg) - printk(KERN_DEBUG "%s\n", buf); - sprintf(buf, "%02x: ", reg); + printk(KERN_CONT "\n"); + printk(KERN_DEBUG "%02x:", reg); } ret = zl10353_read_register(state, reg); if (ret >= 0) - sprintf(buf2, "%02x ", (u8)ret); + printk(KERN_CONT " %02x", (u8)ret); else - strcpy(buf2, "-- "); - strcat(buf, buf2); + printk(KERN_CONT " --"); if (reg == 0xff) break; } - printk(KERN_DEBUG "%s\n", buf); + printk(KERN_CONT "\n"); } #endif -- cgit v1.2.3 From fc9122feb731e5b2e40ba38de622dca900898fb5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Jun 2009 12:13:44 +0200 Subject: tvp514x: Migration to sub-device framework From: Muralidharan Karicheri This patch converts TVP514x driver to sub-device framework from V4L2-int framework. Priority: normal Signed-off-by: Brijesh Jadav Signed-off-by: Hardik Shah Signed-off-by: Vaibhav Hiremath Signed-off-by: Murali Karicheri [hverkuil@xs4all.nl: remove inline from the dump_reg function] Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tvp514x.c | 875 ++++++++++++------------------- linux/drivers/media/video/tvp514x_regs.h | 10 - linux/include/media/tvp514x.h | 4 - 3 files changed, 349 insertions(+), 540 deletions(-) diff --git a/linux/drivers/media/video/tvp514x.c b/linux/drivers/media/video/tvp514x.c index 3750f7fad..c8386e2f7 100644 --- a/linux/drivers/media/video/tvp514x.c +++ b/linux/drivers/media/video/tvp514x.c @@ -31,7 +31,10 @@ #include #include #include -#include + +#include +#include +#include #include #include "tvp514x_regs.h" @@ -49,13 +52,11 @@ static int debug; module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -#define dump_reg(client, reg, val) \ - do { \ - val = tvp514x_read_reg(client, reg); \ - v4l_info(client, "Reg(0x%.2X): 0x%.2X\n", reg, val); \ - } while (0) +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TVP514X linux decoder driver"); +MODULE_LICENSE("GPL"); -/** +/* * enum tvp514x_std - enum for supported standards */ enum tvp514x_std { @@ -64,15 +65,7 @@ enum tvp514x_std { STD_INVALID }; -/** - * enum tvp514x_state - enum for different decoder states - */ -enum tvp514x_state { - STATE_NOT_DETECTED, - STATE_DETECTED -}; - -/** +/* * struct tvp514x_std_info - Structure to store standard informations * @width: Line width in pixels * @height:Number of active lines @@ -87,35 +80,29 @@ struct tvp514x_std_info { }; static struct tvp514x_reg tvp514x_reg_list_default[0x40]; -/** +/* * struct tvp514x_decoder - TVP5146/47 decoder object - * @v4l2_int_device: Slave handle - * @tvp514x_slave: Slave pointer which is used by @v4l2_int_device + * @sd: Subdevice Slave handle * @tvp514x_regs: copy of hw's regs with preset values. * @pdata: Board specific - * @client: I2C client data - * @id: Entry from I2C table * @ver: Chip version - * @state: TVP5146/47 decoder state - detected or not-detected + * @streaming: TVP5146/47 decoder streaming - enabled or disabled. * @pix: Current pixel format * @num_fmts: Number of formats * @fmt_list: Format list * @current_std: Current standard * @num_stds: Number of standards * @std_list: Standards list - * @route: input and output routing at chip level + * @input: Input routing at chip level + * @output: Output routing at chip level */ struct tvp514x_decoder { - struct v4l2_int_device v4l2_int_device; - struct v4l2_int_slave tvp514x_slave; + struct v4l2_subdev sd; struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)]; const struct tvp514x_platform_data *pdata; - struct i2c_client *client; - - struct i2c_device_id *id; int ver; - enum tvp514x_state state; + int streaming; struct v4l2_pix_format pix; int num_fmts; @@ -124,8 +111,11 @@ struct tvp514x_decoder { enum tvp514x_std current_std; int num_stds; struct tvp514x_std_info *std_list; - - struct v4l2_routing route; + /* + * Input and Output Routing parameters + */ + u32 input; + u32 output; }; /* TVP514x default register values */ @@ -191,7 +181,8 @@ static struct tvp514x_reg tvp514x_reg_list_default[] = { {TOK_TERM, 0, 0}, }; -/* List of image formats supported by TVP5146/47 decoder +/* + * List of image formats supported by TVP5146/47 decoder * Currently we are using 8 bit mode only, but can be * extended to 10/20 bit mode. */ @@ -240,35 +231,29 @@ static struct tvp514x_std_info tvp514x_std_list[] = { }, /* Standard: need to add for additional standard */ }; -/* - * Control structure for Auto Gain - * This is temporary data, will get replaced once - * v4l2_ctrl_query_fill supports it. - */ -static const struct v4l2_queryctrl tvp514x_autogain_ctrl = { - .id = V4L2_CID_AUTOGAIN, - .name = "Gain, Automatic", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, -}; + + +static inline struct tvp514x_decoder *to_decoder(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tvp514x_decoder, sd); +} + /* * Read a value from a register in an TVP5146/47 decoder device. * Returns value read if successful, or non-zero (-1) otherwise. */ -static int tvp514x_read_reg(struct i2c_client *client, u8 reg) +static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg) { - int err; - int retry = 0; + int err, retry = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + read_again: err = i2c_smbus_read_byte_data(client, reg); if (err == -1) { if (retry <= I2C_RETRY_COUNT) { - v4l_warn(client, "Read: retry ... %d\n", retry); + v4l2_warn(sd, "Read: retry ... %d\n", retry); retry++; msleep_interruptible(10); goto read_again; @@ -278,20 +263,29 @@ read_again: return err; } +static void dump_reg(struct v4l2_subdev *sd, u8 reg) +{ + u32 val; + + val = tvp514x_read_reg(sd, reg); + v4l2_info(sd, "Reg(0x%.2X): 0x%.2X\n", reg, val); +} + /* * Write a value to a register in an TVP5146/47 decoder device. * Returns zero if successful, or non-zero otherwise. */ -static int tvp514x_write_reg(struct i2c_client *client, u8 reg, u8 val) +static int tvp514x_write_reg(struct v4l2_subdev *sd, u8 reg, u8 val) { - int err; - int retry = 0; + int err, retry = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + write_again: err = i2c_smbus_write_byte_data(client, reg, val); if (err) { if (retry <= I2C_RETRY_COUNT) { - v4l_warn(client, "Write: retry ... %d\n", retry); + v4l2_warn(sd, "Write: retry ... %d\n", retry); retry++; msleep_interruptible(10); goto write_again; @@ -311,7 +305,7 @@ write_again: * reglist - list of registers to be written * Returns zero if successful, or non-zero otherwise. */ -static int tvp514x_write_regs(struct i2c_client *client, +static int tvp514x_write_regs(struct v4l2_subdev *sd, const struct tvp514x_reg reglist[]) { int err; @@ -326,9 +320,9 @@ static int tvp514x_write_regs(struct i2c_client *client, if (next->token == TOK_SKIP) continue; - err = tvp514x_write_reg(client, next->reg, (u8) next->val); + err = tvp514x_write_reg(sd, next->reg, (u8) next->val); if (err) { - v4l_err(client, "Write failed. Err[%d]\n", err); + v4l2_err(sd, "Write failed. Err[%d]\n", err); return err; } } @@ -339,17 +333,15 @@ static int tvp514x_write_regs(struct i2c_client *client, * tvp514x_get_current_std: * Returns the current standard detected by TVP5146/47 */ -static enum tvp514x_std tvp514x_get_current_std(struct tvp514x_decoder - *decoder) +static enum tvp514x_std tvp514x_get_current_std(struct v4l2_subdev *sd) { u8 std, std_status; - std = tvp514x_read_reg(decoder->client, REG_VIDEO_STD); - if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) { + std = tvp514x_read_reg(sd, REG_VIDEO_STD); + if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) /* use the standard status register */ - std_status = tvp514x_read_reg(decoder->client, - REG_VIDEO_STD_STATUS); - } else + std_status = tvp514x_read_reg(sd, REG_VIDEO_STD_STATUS); + else std_status = std; /* use the standard register itself */ switch (std_status & VIDEO_STD_MASK) { @@ -369,70 +361,69 @@ static enum tvp514x_std tvp514x_get_current_std(struct tvp514x_decoder /* * TVP5146/47 register dump function */ -static void tvp514x_reg_dump(struct tvp514x_decoder *decoder) +static void tvp514x_reg_dump(struct v4l2_subdev *sd) { - u8 value; - - dump_reg(decoder->client, REG_INPUT_SEL, value); - dump_reg(decoder->client, REG_AFE_GAIN_CTRL, value); - dump_reg(decoder->client, REG_VIDEO_STD, value); - dump_reg(decoder->client, REG_OPERATION_MODE, value); - dump_reg(decoder->client, REG_COLOR_KILLER, value); - dump_reg(decoder->client, REG_LUMA_CONTROL1, value); - dump_reg(decoder->client, REG_LUMA_CONTROL2, value); - dump_reg(decoder->client, REG_LUMA_CONTROL3, value); - dump_reg(decoder->client, REG_BRIGHTNESS, value); - dump_reg(decoder->client, REG_CONTRAST, value); - dump_reg(decoder->client, REG_SATURATION, value); - dump_reg(decoder->client, REG_HUE, value); - dump_reg(decoder->client, REG_CHROMA_CONTROL1, value); - dump_reg(decoder->client, REG_CHROMA_CONTROL2, value); - dump_reg(decoder->client, REG_COMP_PR_SATURATION, value); - dump_reg(decoder->client, REG_COMP_Y_CONTRAST, value); - dump_reg(decoder->client, REG_COMP_PB_SATURATION, value); - dump_reg(decoder->client, REG_COMP_Y_BRIGHTNESS, value); - dump_reg(decoder->client, REG_AVID_START_PIXEL_LSB, value); - dump_reg(decoder->client, REG_AVID_START_PIXEL_MSB, value); - dump_reg(decoder->client, REG_AVID_STOP_PIXEL_LSB, value); - dump_reg(decoder->client, REG_AVID_STOP_PIXEL_MSB, value); - dump_reg(decoder->client, REG_HSYNC_START_PIXEL_LSB, value); - dump_reg(decoder->client, REG_HSYNC_START_PIXEL_MSB, value); - dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_LSB, value); - dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_MSB, value); - dump_reg(decoder->client, REG_VSYNC_START_LINE_LSB, value); - dump_reg(decoder->client, REG_VSYNC_START_LINE_MSB, value); - dump_reg(decoder->client, REG_VSYNC_STOP_LINE_LSB, value); - dump_reg(decoder->client, REG_VSYNC_STOP_LINE_MSB, value); - dump_reg(decoder->client, REG_VBLK_START_LINE_LSB, value); - dump_reg(decoder->client, REG_VBLK_START_LINE_MSB, value); - dump_reg(decoder->client, REG_VBLK_STOP_LINE_LSB, value); - dump_reg(decoder->client, REG_VBLK_STOP_LINE_MSB, value); - dump_reg(decoder->client, REG_SYNC_CONTROL, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER1, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER2, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER3, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER4, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER5, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER6, value); - dump_reg(decoder->client, REG_CLEAR_LOST_LOCK, value); + dump_reg(sd, REG_INPUT_SEL); + dump_reg(sd, REG_AFE_GAIN_CTRL); + dump_reg(sd, REG_VIDEO_STD); + dump_reg(sd, REG_OPERATION_MODE); + dump_reg(sd, REG_COLOR_KILLER); + dump_reg(sd, REG_LUMA_CONTROL1); + dump_reg(sd, REG_LUMA_CONTROL2); + dump_reg(sd, REG_LUMA_CONTROL3); + dump_reg(sd, REG_BRIGHTNESS); + dump_reg(sd, REG_CONTRAST); + dump_reg(sd, REG_SATURATION); + dump_reg(sd, REG_HUE); + dump_reg(sd, REG_CHROMA_CONTROL1); + dump_reg(sd, REG_CHROMA_CONTROL2); + dump_reg(sd, REG_COMP_PR_SATURATION); + dump_reg(sd, REG_COMP_Y_CONTRAST); + dump_reg(sd, REG_COMP_PB_SATURATION); + dump_reg(sd, REG_COMP_Y_BRIGHTNESS); + dump_reg(sd, REG_AVID_START_PIXEL_LSB); + dump_reg(sd, REG_AVID_START_PIXEL_MSB); + dump_reg(sd, REG_AVID_STOP_PIXEL_LSB); + dump_reg(sd, REG_AVID_STOP_PIXEL_MSB); + dump_reg(sd, REG_HSYNC_START_PIXEL_LSB); + dump_reg(sd, REG_HSYNC_START_PIXEL_MSB); + dump_reg(sd, REG_HSYNC_STOP_PIXEL_LSB); + dump_reg(sd, REG_HSYNC_STOP_PIXEL_MSB); + dump_reg(sd, REG_VSYNC_START_LINE_LSB); + dump_reg(sd, REG_VSYNC_START_LINE_MSB); + dump_reg(sd, REG_VSYNC_STOP_LINE_LSB); + dump_reg(sd, REG_VSYNC_STOP_LINE_MSB); + dump_reg(sd, REG_VBLK_START_LINE_LSB); + dump_reg(sd, REG_VBLK_START_LINE_MSB); + dump_reg(sd, REG_VBLK_STOP_LINE_LSB); + dump_reg(sd, REG_VBLK_STOP_LINE_MSB); + dump_reg(sd, REG_SYNC_CONTROL); + dump_reg(sd, REG_OUTPUT_FORMATTER1); + dump_reg(sd, REG_OUTPUT_FORMATTER2); + dump_reg(sd, REG_OUTPUT_FORMATTER3); + dump_reg(sd, REG_OUTPUT_FORMATTER4); + dump_reg(sd, REG_OUTPUT_FORMATTER5); + dump_reg(sd, REG_OUTPUT_FORMATTER6); + dump_reg(sd, REG_CLEAR_LOST_LOCK); } /* * Configure the TVP5146/47 with the current register settings * Returns zero if successful, or non-zero otherwise. */ -static int tvp514x_configure(struct tvp514x_decoder *decoder) +static int tvp514x_configure(struct v4l2_subdev *sd, + struct tvp514x_decoder *decoder) { int err; /* common register initialization */ err = - tvp514x_write_regs(decoder->client, decoder->tvp514x_regs); + tvp514x_write_regs(sd, decoder->tvp514x_regs); if (err) return err; if (debug) - tvp514x_reg_dump(decoder); + tvp514x_reg_dump(sd); return 0; } @@ -445,15 +436,17 @@ static int tvp514x_configure(struct tvp514x_decoder *decoder) * Returns ENODEV error number if no device is detected, or zero * if a device is detected. */ -static int tvp514x_detect(struct tvp514x_decoder *decoder) +static int tvp514x_detect(struct v4l2_subdev *sd, + struct tvp514x_decoder *decoder) { u8 chip_id_msb, chip_id_lsb, rom_ver; + struct i2c_client *client = v4l2_get_subdevdata(sd); - chip_id_msb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_MSB); - chip_id_lsb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_LSB); - rom_ver = tvp514x_read_reg(decoder->client, REG_ROM_VERSION); + chip_id_msb = tvp514x_read_reg(sd, REG_CHIP_ID_MSB); + chip_id_lsb = tvp514x_read_reg(sd, REG_CHIP_ID_LSB); + rom_ver = tvp514x_read_reg(sd, REG_ROM_VERSION); - v4l_dbg(1, debug, decoder->client, + v4l2_dbg(1, debug, sd, "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n", chip_id_msb, chip_id_lsb, rom_ver); if ((chip_id_msb != TVP514X_CHIP_ID_MSB) @@ -462,19 +455,16 @@ static int tvp514x_detect(struct tvp514x_decoder *decoder) /* We didn't read the values we expected, so this must not be * an TVP5146/47. */ - v4l_err(decoder->client, - "chip id mismatch msb:0x%x lsb:0x%x\n", - chip_id_msb, chip_id_lsb); + v4l2_err(sd, "chip id mismatch msb:0x%x lsb:0x%x\n", + chip_id_msb, chip_id_lsb); return -ENODEV; } decoder->ver = rom_ver; - decoder->state = STATE_DETECTED; - v4l_info(decoder->client, - "%s found at 0x%x (%s)\n", decoder->client->name, - decoder->client->addr << 1, - decoder->client->adapter->name); + v4l2_info(sd, "%s (Version - 0x%.2x) found at 0x%x (%s)\n", + client->name, decoder->ver, + client->addr << 1, client->adapter->name); return 0; } @@ -483,17 +473,17 @@ static int tvp514x_detect(struct tvp514x_decoder *decoder) * TVP5146/47 decoder driver. */ -/** - * ioctl_querystd - V4L2 decoder interface handler for VIDIOC_QUERYSTD ioctl - * @s: pointer to standard V4L2 device structure +/* + * tvp514x_querystd - V4L2 decoder interface handler for VIDIOC_QUERYSTD ioctl + * @sd: pointer to standard V4L2 sub-device structure * @std_id: standard V4L2 std_id ioctl enum * * Returns the current standard detected by TVP5146/47. If no active input is * detected, returns -EINVAL */ -static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id) +static int tvp514x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); enum tvp514x_std current_std; enum tvp514x_input input_sel; u8 sync_lock_status, lock_mask; @@ -502,11 +492,11 @@ static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id) return -EINVAL; /* get the current standard */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) return -EINVAL; - input_sel = decoder->route.input; + input_sel = decoder->input; switch (input_sel) { case INPUT_CVBS_VI1A: @@ -544,42 +534,39 @@ static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id) return -EINVAL; } /* check whether signal is locked */ - sync_lock_status = tvp514x_read_reg(decoder->client, REG_STATUS1); + sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1); if (lock_mask != (sync_lock_status & lock_mask)) return -EINVAL; /* No input detected */ decoder->current_std = current_std; *std_id = decoder->std_list[current_std].standard.id; - v4l_dbg(1, debug, decoder->client, "Current STD: %s", + v4l2_dbg(1, debug, sd, "Current STD: %s", decoder->std_list[current_std].standard.name); return 0; } -/** - * ioctl_s_std - V4L2 decoder interface handler for VIDIOC_S_STD ioctl - * @s: pointer to standard V4L2 device structure +/* + * tvp514x_s_std - V4L2 decoder interface handler for VIDIOC_S_STD ioctl + * @sd: pointer to standard V4L2 sub-device structure * @std_id: standard V4L2 v4l2_std_id ioctl enum * * If std_id is supported, sets the requested standard. Otherwise, returns * -EINVAL */ -static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id) +static int tvp514x_s_std(struct v4l2_subdev *sd, v4l2_std_id std_id) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int err, i; - if (std_id == NULL) - return -EINVAL; - for (i = 0; i < decoder->num_stds; i++) - if (*std_id & decoder->std_list[i].standard.id) + if (std_id & decoder->std_list[i].standard.id) break; if ((i == decoder->num_stds) || (i == STD_INVALID)) return -EINVAL; - err = tvp514x_write_reg(decoder->client, REG_VIDEO_STD, + err = tvp514x_write_reg(sd, REG_VIDEO_STD, decoder->std_list[i].video_std); if (err) return err; @@ -588,24 +575,24 @@ static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id) decoder->tvp514x_regs[REG_VIDEO_STD].val = decoder->std_list[i].video_std; - v4l_dbg(1, debug, decoder->client, "Standard set to: %s", + v4l2_dbg(1, debug, sd, "Standard set to: %s", decoder->std_list[i].standard.name); return 0; } -/** - * ioctl_s_routing - V4L2 decoder interface handler for VIDIOC_S_INPUT ioctl - * @s: pointer to standard V4L2 device structure +/* + * tvp514x_s_routing - V4L2 decoder interface handler for VIDIOC_S_INPUT ioctl + * @sd: pointer to standard V4L2 sub-device structure * @index: number of the input * * If index is valid, selects the requested input. Otherwise, returns -EINVAL if * the input is not supported or there is no active signal present in the * selected input. */ -static int ioctl_s_routing(struct v4l2_int_device *s, - struct v4l2_routing *route) +static int tvp514x_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int err; enum tvp514x_input input_sel; enum tvp514x_output output_sel; @@ -613,20 +600,20 @@ static int ioctl_s_routing(struct v4l2_int_device *s, u8 sync_lock_status, lock_mask; int try_count = LOCK_RETRY_COUNT; - if ((!route) || (route->input >= INPUT_INVALID) || - (route->output >= OUTPUT_INVALID)) + if ((input >= INPUT_INVALID) || + (output >= OUTPUT_INVALID)) return -EINVAL; /* Index out of bound */ - input_sel = route->input; - output_sel = route->output; + input_sel = input; + output_sel = output; - err = tvp514x_write_reg(decoder->client, REG_INPUT_SEL, input_sel); + err = tvp514x_write_reg(sd, REG_INPUT_SEL, input_sel); if (err) return err; - output_sel |= tvp514x_read_reg(decoder->client, + output_sel |= tvp514x_read_reg(sd, REG_OUTPUT_FORMATTER1) & 0x7; - err = tvp514x_write_reg(decoder->client, REG_OUTPUT_FORMATTER1, + err = tvp514x_write_reg(sd, REG_OUTPUT_FORMATTER1, output_sel); if (err) return err; @@ -637,7 +624,7 @@ static int ioctl_s_routing(struct v4l2_int_device *s, /* Clear status */ msleep(LOCK_RETRY_DELAY); err = - tvp514x_write_reg(decoder->client, REG_CLEAR_LOST_LOCK, 0x01); + tvp514x_write_reg(sd, REG_CLEAR_LOST_LOCK, 0x01); if (err) return err; @@ -682,11 +669,11 @@ static int ioctl_s_routing(struct v4l2_int_device *s, msleep(LOCK_RETRY_DELAY); /* get the current standard for future reference */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) continue; - sync_lock_status = tvp514x_read_reg(decoder->client, + sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1); if (lock_mask == (sync_lock_status & lock_mask)) break; /* Input detected */ @@ -696,28 +683,26 @@ static int ioctl_s_routing(struct v4l2_int_device *s, return -EINVAL; decoder->current_std = current_std; - decoder->route.input = route->input; - decoder->route.output = route->output; + decoder->input = input; + decoder->output = output; - v4l_dbg(1, debug, decoder->client, - "Input set to: %d, std : %d", + v4l2_dbg(1, debug, sd, "Input set to: %d, std : %d", input_sel, current_std); return 0; } -/** - * ioctl_queryctrl - V4L2 decoder interface handler for VIDIOC_QUERYCTRL ioctl - * @s: pointer to standard V4L2 device structure +/* + * tvp514x_queryctrl - V4L2 decoder interface handler for VIDIOC_QUERYCTRL ioctl + * @sd: pointer to standard V4L2 sub-device structure * @qctrl: standard V4L2 v4l2_queryctrl structure * * If the requested control is supported, returns the control information. * Otherwise, returns -EINVAL if the control is not supported. */ static int -ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) +tvp514x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl) { - struct tvp514x_decoder *decoder = s->priv; int err = -EINVAL; if (qctrl == NULL) @@ -744,30 +729,27 @@ ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0); break; case V4L2_CID_AUTOGAIN: - /* Autogain is either 0 or 1*/ - memcpy(qctrl, &tvp514x_autogain_ctrl, - sizeof(struct v4l2_queryctrl)); - err = 0; + /* + * Auto Gain supported is - + * 0 - 1 (Default - 1) + */ + err = v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1); break; default: - v4l_err(decoder->client, - "invalid control id %d\n", qctrl->id); + v4l2_err(sd, "invalid control id %d\n", qctrl->id); return err; } - v4l_dbg(1, debug, decoder->client, - "Query Control: %s : Min - %d, Max - %d, Def - %d", - qctrl->name, - qctrl->minimum, - qctrl->maximum, + v4l2_dbg(1, debug, sd, "Query Control:%s: Min - %d, Max - %d, Def - %d", + qctrl->name, qctrl->minimum, qctrl->maximum, qctrl->default_value); return err; } -/** - * ioctl_g_ctrl - V4L2 decoder interface handler for VIDIOC_G_CTRL ioctl - * @s: pointer to standard V4L2 device structure +/* + * tvp514x_g_ctrl - V4L2 decoder interface handler for VIDIOC_G_CTRL ioctl + * @sd: pointer to standard V4L2 sub-device structure * @ctrl: pointer to v4l2_control structure * * If the requested control is supported, returns the control's current @@ -775,9 +757,9 @@ ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) * supported. */ static int -ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) +tvp514x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); if (ctrl == NULL) return -EINVAL; @@ -811,74 +793,70 @@ ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) break; default: - v4l_err(decoder->client, - "invalid control id %d\n", ctrl->id); + v4l2_err(sd, "invalid control id %d\n", ctrl->id); return -EINVAL; } - v4l_dbg(1, debug, decoder->client, - "Get Control: ID - %d - %d", + v4l2_dbg(1, debug, sd, "Get Control: ID - %d - %d", ctrl->id, ctrl->value); return 0; } -/** - * ioctl_s_ctrl - V4L2 decoder interface handler for VIDIOC_S_CTRL ioctl - * @s: pointer to standard V4L2 device structure +/* + * tvp514x_s_ctrl - V4L2 decoder interface handler for VIDIOC_S_CTRL ioctl + * @sd: pointer to standard V4L2 sub-device structure * @ctrl: pointer to v4l2_control structure * * If the requested control is supported, sets the control's current * value in HW. Otherwise, returns -EINVAL if the control is not supported. */ static int -ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) +tvp514x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int err = -EINVAL, value; if (ctrl == NULL) return err; - value = (__s32) ctrl->value; + value = ctrl->value; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(decoder->client, - "invalid brightness setting %d\n", + v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_BRIGHTNESS, + err = tvp514x_write_reg(sd, REG_BRIGHTNESS, value); if (err) return err; + decoder->tvp514x_regs[REG_BRIGHTNESS].val = value; break; case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(decoder->client, - "invalid contrast setting %d\n", + v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_CONTRAST, - value); + err = tvp514x_write_reg(sd, REG_CONTRAST, value); if (err) return err; + decoder->tvp514x_regs[REG_CONTRAST].val = value; break; case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(decoder->client, - "invalid saturation setting %d\n", + v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_SATURATION, - value); + err = tvp514x_write_reg(sd, REG_SATURATION, value); if (err) return err; + decoder->tvp514x_regs[REG_SATURATION].val = value; break; case V4L2_CID_HUE: @@ -889,15 +867,13 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) else if (value == 0) value = 0; else { - v4l_err(decoder->client, - "invalid hue setting %d\n", - ctrl->value); + v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_HUE, - value); + err = tvp514x_write_reg(sd, REG_HUE, value); if (err) return err; + decoder->tvp514x_regs[REG_HUE].val = value; break; case V4L2_CID_AUTOGAIN: @@ -906,41 +882,38 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) else if (value == 0) value = 0x0C; else { - v4l_err(decoder->client, - "invalid auto gain setting %d\n", + v4l2_err(sd, "invalid auto gain setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_AFE_GAIN_CTRL, - value); + err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value); if (err) return err; + decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value; break; default: - v4l_err(decoder->client, - "invalid control id %d\n", ctrl->id); + v4l2_err(sd, "invalid control id %d\n", ctrl->id); return err; } - v4l_dbg(1, debug, decoder->client, - "Set Control: ID - %d - %d", + v4l2_dbg(1, debug, sd, "Set Control: ID - %d - %d", ctrl->id, ctrl->value); return err; } -/** - * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl - * @s: pointer to standard V4L2 device structure +/* + * tvp514x_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl + * @sd: pointer to standard V4L2 sub-device structure * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure * * Implement the VIDIOC_ENUM_FMT ioctl to enumerate supported formats */ static int -ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) +tvp514x_enum_fmt_cap(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmt) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int index; if (fmt == NULL) @@ -956,16 +929,15 @@ ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) memcpy(fmt, &decoder->fmt_list[index], sizeof(struct v4l2_fmtdesc)); - v4l_dbg(1, debug, decoder->client, - "Current FMT: index - %d (%s)", + v4l2_dbg(1, debug, sd, "Current FMT: index - %d (%s)", decoder->fmt_list[index].index, decoder->fmt_list[index].description); return 0; } -/** - * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl - * @s: pointer to standard V4L2 device structure +/* + * tvp514x_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl + * @sd: pointer to standard V4L2 sub-device structure * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure * * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This @@ -973,9 +945,9 @@ ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) * without actually making it take effect. */ static int -ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +tvp514x_try_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int ifmt; struct v4l2_pix_format *pix; enum tvp514x_std current_std; @@ -989,7 +961,7 @@ ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) pix = &f->fmt.pix; /* Calculate height and width based on current standard */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) return -EINVAL; @@ -1012,17 +984,16 @@ ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) pix->colorspace = V4L2_COLORSPACE_SMPTE170M; pix->priv = 0; - v4l_dbg(1, debug, decoder->client, - "Try FMT: pixelformat - %s, bytesperline - %d" + v4l2_dbg(1, debug, sd, "Try FMT: pixelformat - %s, bytesperline - %d" "Width - %d, Height - %d", decoder->fmt_list[ifmt].description, pix->bytesperline, pix->width, pix->height); return 0; } -/** - * ioctl_s_fmt_cap - V4L2 decoder interface handler for VIDIOC_S_FMT ioctl - * @s: pointer to standard V4L2 device structure +/* + * tvp514x_s_fmt_cap - V4L2 decoder interface handler for VIDIOC_S_FMT ioctl + * @sd: pointer to standard V4L2 sub-device structure * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure * * If the requested format is supported, configures the HW to use that @@ -1030,9 +1001,9 @@ ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) * correctly configured. */ static int -ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +tvp514x_s_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); struct v4l2_pix_format *pix; int rval; @@ -1043,7 +1014,7 @@ ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) return -EINVAL; /* only capture is supported */ pix = &f->fmt.pix; - rval = ioctl_try_fmt_cap(s, f); + rval = tvp514x_try_fmt_cap(sd, f); if (rval) return rval; @@ -1052,18 +1023,18 @@ ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) return rval; } -/** - * ioctl_g_fmt_cap - V4L2 decoder interface handler for ioctl_g_fmt_cap - * @s: pointer to standard V4L2 device structure +/* + * tvp514x_g_fmt_cap - V4L2 decoder interface handler for tvp514x_g_fmt_cap + * @sd: pointer to standard V4L2 sub-device structure * @f: pointer to standard V4L2 v4l2_format structure * * Returns the decoder's current pixel format in the v4l2_format * parameter. */ static int -ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +tvp514x_g_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); if (f == NULL) return -EINVAL; @@ -1073,25 +1044,24 @@ ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) f->fmt.pix = decoder->pix; - v4l_dbg(1, debug, decoder->client, - "Current FMT: bytesperline - %d" + v4l2_dbg(1, debug, sd, "Current FMT: bytesperline - %d" "Width - %d, Height - %d", decoder->pix.bytesperline, decoder->pix.width, decoder->pix.height); return 0; } -/** - * ioctl_g_parm - V4L2 decoder interface handler for VIDIOC_G_PARM ioctl - * @s: pointer to standard V4L2 device structure +/* + * tvp514x_g_parm - V4L2 decoder interface handler for VIDIOC_G_PARM ioctl + * @sd: pointer to standard V4L2 sub-device structure * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure * * Returns the decoder's video CAPTURE parameters. */ static int -ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); struct v4l2_captureparm *cparm; enum tvp514x_std current_std; @@ -1105,7 +1075,7 @@ ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* get the current standard */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) return -EINVAL; @@ -1119,18 +1089,18 @@ ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) return 0; } -/** - * ioctl_s_parm - V4L2 decoder interface handler for VIDIOC_S_PARM ioctl - * @s: pointer to standard V4L2 device structure +/* + * tvp514x_s_parm - V4L2 decoder interface handler for VIDIOC_S_PARM ioctl + * @sd: pointer to standard V4L2 sub-device structure * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure * * Configures the decoder to use the input parameters, if possible. If * not possible, returns the appropriate error code. */ static int -ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); struct v4l2_fract *timeperframe; enum tvp514x_std current_std; @@ -1143,7 +1113,7 @@ ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) timeperframe = &a->parm.capture.timeperframe; /* get the current standard */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) return -EINVAL; @@ -1155,112 +1125,59 @@ ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) return 0; } -/** - * ioctl_g_ifparm - V4L2 decoder interface handler for vidioc_int_g_ifparm_num - * @s: pointer to standard V4L2 device structure - * @p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure - * - * Gets slave interface parameters. - * Calculates the required xclk value to support the requested - * clock parameters in p. This value is returned in the p - * parameter. - */ -static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) -{ - struct tvp514x_decoder *decoder = s->priv; - int rval; - - if (p == NULL) - return -EINVAL; - - if (NULL == decoder->pdata->ifparm) - return -EINVAL; - - rval = decoder->pdata->ifparm(p); - if (rval) { - v4l_err(decoder->client, "g_ifparm.Err[%d]\n", rval); - return rval; - } - - p->u.bt656.clock_curr = TVP514X_XCLK_BT656; - - return 0; -} - -/** - * ioctl_g_priv - V4L2 decoder interface handler for vidioc_int_g_priv_num - * @s: pointer to standard V4L2 device structure - * @p: void pointer to hold decoder's private data address - * - * Returns device's (decoder's) private data area address in p parameter - */ -static int ioctl_g_priv(struct v4l2_int_device *s, void *p) -{ - struct tvp514x_decoder *decoder = s->priv; - - if (NULL == decoder->pdata->priv_data_set) - return -EINVAL; - - return decoder->pdata->priv_data_set(p); -} - -/** - * ioctl_s_power - V4L2 decoder interface handler for vidioc_int_s_power_num - * @s: pointer to standard V4L2 device structure - * @on: power state to which device is to be set +/* + * tvp514x_s_stream - V4L2 decoder interface handler for vidioc_int_s_power_num + * @sd: pointer to standard V4L2 sub-device structure + * @enable: streaming enable or disable * - * Sets devices power state to requrested state, if possible. + * Sets streaming to enable or disable, if possible. */ -static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on) +static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable) { - struct tvp514x_decoder *decoder = s->priv; int err = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tvp514x_decoder *decoder = to_decoder(sd); - switch (on) { - case V4L2_POWER_OFF: - /* Power Down Sequence */ - err = - tvp514x_write_reg(decoder->client, REG_OPERATION_MODE, - 0x01); - /* Disable mux for TVP5146/47 decoder data path */ - if (decoder->pdata->power_set) - err |= decoder->pdata->power_set(on); - decoder->state = STATE_NOT_DETECTED; - break; + if (decoder->streaming == enable) + return 0; - case V4L2_POWER_STANDBY: - if (decoder->pdata->power_set) - err = decoder->pdata->power_set(on); + switch (enable) { + case 0: + { + /* Power Down Sequence */ + err = tvp514x_write_reg(sd, REG_OPERATION_MODE, 0x01); + if (err) { + v4l2_err(sd, "Unable to turn off decoder\n"); + return err; + } + decoder->streaming = enable; break; + } + case 1: + { + struct tvp514x_reg *int_seq = (struct tvp514x_reg *) + client->driver->id_table->driver_data; - case V4L2_POWER_ON: - /* Enable mux for TVP5146/47 decoder data path */ - if ((decoder->pdata->power_set) && - (decoder->state == STATE_NOT_DETECTED)) { - int i; - struct tvp514x_init_seq *int_seq = - (struct tvp514x_init_seq *) - decoder->id->driver_data; - - err = decoder->pdata->power_set(on); - - /* Power Up Sequence */ - for (i = 0; i < int_seq->no_regs; i++) { - err |= tvp514x_write_reg(decoder->client, - int_seq->init_reg_seq[i].reg, - int_seq->init_reg_seq[i].val); - } - /* Detect the sensor is not already detected */ - err |= tvp514x_detect(decoder); - if (err) { - v4l_err(decoder->client, - "Unable to detect decoder\n"); - return err; - } + /* Power Up Sequence */ + err = tvp514x_write_regs(sd, int_seq); + if (err) { + v4l2_err(sd, "Unable to turn on decoder\n"); + return err; + } + /* Detect if not already detected */ + err = tvp514x_detect(sd, decoder); + if (err) { + v4l2_err(sd, "Unable to detect decoder\n"); + return err; } - err |= tvp514x_configure(decoder); + err = tvp514x_configure(sd, decoder); + if (err) { + v4l2_err(sd, "Unable to configure decoder\n"); + return err; + } + decoder->streaming = enable; break; - + } default: err = -ENODEV; break; @@ -1269,93 +1186,37 @@ static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on) return err; } -/** - * ioctl_init - V4L2 decoder interface handler for VIDIOC_INT_INIT - * @s: pointer to standard V4L2 device structure - * - * Initialize the decoder device (calls tvp514x_configure()) - */ -static int ioctl_init(struct v4l2_int_device *s) -{ - struct tvp514x_decoder *decoder = s->priv; - - /* Set default standard to auto */ - decoder->tvp514x_regs[REG_VIDEO_STD].val = - VIDEO_STD_AUTO_SWITCH_BIT; - - return tvp514x_configure(decoder); -} - -/** - * ioctl_dev_exit - V4L2 decoder interface handler for vidioc_int_dev_exit_num - * @s: pointer to standard V4L2 device structure - * - * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init. - */ -static int ioctl_dev_exit(struct v4l2_int_device *s) -{ - return 0; -} - -/** - * ioctl_dev_init - V4L2 decoder interface handler for vidioc_int_dev_init_num - * @s: pointer to standard V4L2 device structure - * - * Initialise the device when slave attaches to the master. Returns 0 if - * TVP5146/47 device could be found, otherwise returns appropriate error. - */ -static int ioctl_dev_init(struct v4l2_int_device *s) -{ - struct tvp514x_decoder *decoder = s->priv; - int err; - - err = tvp514x_detect(decoder); - if (err < 0) { - v4l_err(decoder->client, - "Unable to detect decoder\n"); - return err; - } - - v4l_info(decoder->client, - "chip version 0x%.2x detected\n", decoder->ver); +static const struct v4l2_subdev_core_ops tvp514x_core_ops = { + .queryctrl = tvp514x_queryctrl, + .g_ctrl = tvp514x_g_ctrl, + .s_ctrl = tvp514x_s_ctrl, + .s_std = tvp514x_s_std, +}; - return 0; -} +static const struct v4l2_subdev_video_ops tvp514x_video_ops = { + .s_routing = tvp514x_s_routing, + .querystd = tvp514x_querystd, + .enum_fmt = tvp514x_enum_fmt_cap, + .g_fmt = tvp514x_g_fmt_cap, + .try_fmt = tvp514x_try_fmt_cap, + .s_fmt = tvp514x_s_fmt_cap, + .g_parm = tvp514x_g_parm, + .s_parm = tvp514x_s_parm, + .s_stream = tvp514x_s_stream, +}; -static struct v4l2_int_ioctl_desc tvp514x_ioctl_desc[] = { - {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*) ioctl_dev_init}, - {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func*) ioctl_dev_exit}, - {vidioc_int_s_power_num, (v4l2_int_ioctl_func*) ioctl_s_power}, - {vidioc_int_g_priv_num, (v4l2_int_ioctl_func*) ioctl_g_priv}, - {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*) ioctl_g_ifparm}, - {vidioc_int_init_num, (v4l2_int_ioctl_func*) ioctl_init}, - {vidioc_int_enum_fmt_cap_num, - (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, - {vidioc_int_try_fmt_cap_num, - (v4l2_int_ioctl_func *) ioctl_try_fmt_cap}, - {vidioc_int_g_fmt_cap_num, - (v4l2_int_ioctl_func *) ioctl_g_fmt_cap}, - {vidioc_int_s_fmt_cap_num, - (v4l2_int_ioctl_func *) ioctl_s_fmt_cap}, - {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm}, - {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm}, - {vidioc_int_queryctrl_num, - (v4l2_int_ioctl_func *) ioctl_queryctrl}, - {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl}, - {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl}, - {vidioc_int_querystd_num, (v4l2_int_ioctl_func *) ioctl_querystd}, - {vidioc_int_s_std_num, (v4l2_int_ioctl_func *) ioctl_s_std}, - {vidioc_int_s_video_routing_num, - (v4l2_int_ioctl_func *) ioctl_s_routing}, +static const struct v4l2_subdev_ops tvp514x_ops = { + .core = &tvp514x_core_ops, + .video = &tvp514x_video_ops, }; static struct tvp514x_decoder tvp514x_dev = { - .state = STATE_NOT_DETECTED, + .streaming = 0, .fmt_list = tvp514x_fmt_list, .num_fmts = ARRAY_SIZE(tvp514x_fmt_list), - .pix = { /* Default to NTSC 8-bit YUV 422 */ + .pix = {/* Default to NTSC 8-bit YUV 422 */ .width = NTSC_NUM_ACTIVE_PIXELS, .height = NTSC_NUM_ACTIVE_LINES, .pixelformat = V4L2_PIX_FMT_UYVY, @@ -1369,20 +1230,13 @@ static struct tvp514x_decoder tvp514x_dev = { .current_std = STD_NTSC_MJ, .std_list = tvp514x_std_list, .num_stds = ARRAY_SIZE(tvp514x_std_list), - .v4l2_int_device = { - .module = THIS_MODULE, - .name = TVP514X_MODULE_NAME, - .type = v4l2_int_type_slave, - }, - .tvp514x_slave = { - .ioctls = tvp514x_ioctl_desc, - .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc), - }, + }; -/** +/* * tvp514x_probe - decoder driver i2c probe handler * @client: i2c driver client device structure + * @id: i2c driver id table * * Register decoder as an i2c client device and V4L2 * device. @@ -1391,82 +1245,71 @@ static int tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tvp514x_decoder *decoder; - int err; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; + if (!client->dev.platform_data) { + v4l2_err(client, "No platform data!!\n"); + return -ENODEV; + } + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); if (!decoder) return -ENOMEM; - if (!client->dev.platform_data) { - v4l_err(client, "No platform data!!\n"); - err = -ENODEV; - goto out_free; - } - + /* + * Initialize the tvp514x_decoder with default configuration + */ *decoder = tvp514x_dev; - decoder->v4l2_int_device.priv = decoder; - decoder->pdata = client->dev.platform_data; - decoder->v4l2_int_device.u.slave = &decoder->tvp514x_slave; + /* Copy default register configuration */ memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default, sizeof(tvp514x_reg_list_default)); + + /* + * Copy board specific information here + */ + decoder->pdata = client->dev.platform_data; + /* * Fetch platform specific data, and configure the * tvp514x_reg_list[] accordingly. Since this is one * time configuration, no need to preserve. */ decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |= - (decoder->pdata->clk_polarity << 1); + (decoder->pdata->clk_polarity << 1); decoder->tvp514x_regs[REG_SYNC_CONTROL].val |= - ((decoder->pdata->hs_polarity << 2) | - (decoder->pdata->vs_polarity << 3)); - /* - * Save the id data, required for power up sequence - */ - decoder->id = (struct i2c_device_id *)id; - /* Attach to Master */ - strcpy(decoder->v4l2_int_device.u.slave->attach_to, - decoder->pdata->master); - decoder->client = client; - i2c_set_clientdata(client, decoder); + ((decoder->pdata->hs_polarity << 2) | + (decoder->pdata->vs_polarity << 3)); + /* Set default standard to auto */ + decoder->tvp514x_regs[REG_VIDEO_STD].val = + VIDEO_STD_AUTO_SWITCH_BIT; /* Register with V4L2 layer as slave device */ - err = v4l2_int_device_register(&decoder->v4l2_int_device); - if (err) { - i2c_set_clientdata(client, NULL); - v4l_err(client, - "Unable to register to v4l2. Err[%d]\n", err); - goto out_free; - - } else - v4l_info(client, "Registered to v4l2 master %s!!\n", - decoder->pdata->master); + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &tvp514x_ops); + + v4l2_info(sd, "%s decoder driver registered !!\n", sd->name); + return 0; -out_free: - kfree(decoder); - return err; } -/** +/* * tvp514x_remove - decoder driver i2c remove handler * @client: i2c driver client device structure * * Unregister decoder as an i2c client device and V4L2 * device. Complement of tvp514x_probe(). */ -static int __exit tvp514x_remove(struct i2c_client *client) +static int tvp514x_remove(struct i2c_client *client) { - struct tvp514x_decoder *decoder = i2c_get_clientdata(client); - - if (!client->adapter) - return -ENODEV; /* our client isn't attached */ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tvp514x_decoder *decoder = to_decoder(sd); - v4l2_int_device_unregister(&decoder->v4l2_int_device); - i2c_set_clientdata(client, NULL); + v4l2_device_unregister_subdev(sd); kfree(decoder); return 0; } @@ -1485,11 +1328,9 @@ static const struct tvp514x_reg tvp5146_init_reg_seq[] = { {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, {TOK_WRITE, REG_OPERATION_MODE, 0x01}, {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, }; -static const struct tvp514x_init_seq tvp5146_init = { - .no_regs = ARRAY_SIZE(tvp5146_init_reg_seq), - .init_reg_seq = tvp5146_init_reg_seq, -}; + /* * TVP5147 Init/Power on Sequence */ @@ -1512,22 +1353,18 @@ static const struct tvp514x_reg tvp5147_init_reg_seq[] = { {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, {TOK_WRITE, REG_OPERATION_MODE, 0x01}, {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, }; -static const struct tvp514x_init_seq tvp5147_init = { - .no_regs = ARRAY_SIZE(tvp5147_init_reg_seq), - .init_reg_seq = tvp5147_init_reg_seq, -}; + /* * TVP5146M2/TVP5147M1 Init/Power on Sequence */ static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { {TOK_WRITE, REG_OPERATION_MODE, 0x01}, {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, }; -static const struct tvp514x_init_seq tvp514xm_init = { - .no_regs = ARRAY_SIZE(tvp514xm_init_reg_seq), - .init_reg_seq = tvp514xm_init_reg_seq, -}; + /* * I2C Device Table - * @@ -1535,48 +1372,34 @@ static const struct tvp514x_init_seq tvp514xm_init = { * driver_data - Driver data */ static const struct i2c_device_id tvp514x_id[] = { - {"tvp5146", (unsigned long)&tvp5146_init}, - {"tvp5146m2", (unsigned long)&tvp514xm_init}, - {"tvp5147", (unsigned long)&tvp5147_init}, - {"tvp5147m1", (unsigned long)&tvp514xm_init}, + {"tvp5146", (unsigned long)tvp5146_init_reg_seq}, + {"tvp5146m2", (unsigned long)tvp514xm_init_reg_seq}, + {"tvp5147", (unsigned long)tvp5147_init_reg_seq}, + {"tvp5147m1", (unsigned long)tvp514xm_init_reg_seq}, {}, }; MODULE_DEVICE_TABLE(i2c, tvp514x_id); -static struct i2c_driver tvp514x_i2c_driver = { +static struct i2c_driver tvp514x_driver = { .driver = { - .name = TVP514X_MODULE_NAME, - .owner = THIS_MODULE, - }, + .owner = THIS_MODULE, + .name = TVP514X_MODULE_NAME, + }, .probe = tvp514x_probe, - .remove = __exit_p(tvp514x_remove), + .remove = tvp514x_remove, .id_table = tvp514x_id, }; -/** - * tvp514x_init - * - * Module init function - */ static int __init tvp514x_init(void) { - return i2c_add_driver(&tvp514x_i2c_driver); + return i2c_add_driver(&tvp514x_driver); } -/** - * tvp514x_cleanup - * - * Module exit function - */ -static void __exit tvp514x_cleanup(void) +static void __exit tvp514x_exit(void) { - i2c_del_driver(&tvp514x_i2c_driver); + i2c_del_driver(&tvp514x_driver); } module_init(tvp514x_init); -module_exit(tvp514x_cleanup); - -MODULE_AUTHOR("Texas Instruments"); -MODULE_DESCRIPTION("TVP514X linux decoder driver"); -MODULE_LICENSE("GPL"); +module_exit(tvp514x_exit); diff --git a/linux/drivers/media/video/tvp514x_regs.h b/linux/drivers/media/video/tvp514x_regs.h index 351620aee..18f29ad0d 100644 --- a/linux/drivers/media/video/tvp514x_regs.h +++ b/linux/drivers/media/video/tvp514x_regs.h @@ -284,14 +284,4 @@ struct tvp514x_reg { u32 val; }; -/** - * struct tvp514x_init_seq - Structure for TVP5146/47/46M2/47M1 power up - * Sequence. - * @ no_regs - Number of registers to write for power up sequence. - * @ init_reg_seq - Array of registers and respective value to write. - */ -struct tvp514x_init_seq { - unsigned int no_regs; - const struct tvp514x_reg *init_reg_seq; -}; #endif /* ifndef _TVP514X_REGS_H */ diff --git a/linux/include/media/tvp514x.h b/linux/include/media/tvp514x.h index 5e7ee968c..74387e83f 100644 --- a/linux/include/media/tvp514x.h +++ b/linux/include/media/tvp514x.h @@ -104,10 +104,6 @@ enum tvp514x_output { * @ vs_polarity: VSYNC Polarity configuration for current interface. */ struct tvp514x_platform_data { - char *master; - int (*power_set) (enum v4l2_power on); - int (*ifparm) (struct v4l2_ifparm *p); - int (*priv_data_set) (void *); /* Interface control params */ bool clk_polarity; bool hs_polarity; -- cgit v1.2.3 From 60ea01f2166060a29cbc120e0af381ba1fccfaea Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 1 Jul 2009 09:20:43 +0200 Subject: tvp514x: formatting comments as per kernel documentation From: Muralidharan Karicheri Fix documentation style based on comments from Mauro. Reviewed by: Hans Verkuil Priority: normal Signed-off-by: Muralidharan Karicheri Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tvp514x.c | 275 ++++++++++++++++++++---------------- 1 file changed, 157 insertions(+), 118 deletions(-) diff --git a/linux/drivers/media/video/tvp514x.c b/linux/drivers/media/video/tvp514x.c index c8386e2f7..244372627 100644 --- a/linux/drivers/media/video/tvp514x.c +++ b/linux/drivers/media/video/tvp514x.c @@ -56,16 +56,14 @@ MODULE_AUTHOR("Texas Instruments"); MODULE_DESCRIPTION("TVP514X linux decoder driver"); MODULE_LICENSE("GPL"); -/* - * enum tvp514x_std - enum for supported standards - */ +/* enum tvp514x_std - enum for supported standards */ enum tvp514x_std { STD_NTSC_MJ = 0, STD_PAL_BDGHIN, STD_INVALID }; -/* +/** * struct tvp514x_std_info - Structure to store standard informations * @width: Line width in pixels * @height:Number of active lines @@ -80,7 +78,7 @@ struct tvp514x_std_info { }; static struct tvp514x_reg tvp514x_reg_list_default[0x40]; -/* +/** * struct tvp514x_decoder - TVP5146/47 decoder object * @sd: Subdevice Slave handle * @tvp514x_regs: copy of hw's regs with preset values. @@ -111,18 +109,18 @@ struct tvp514x_decoder { enum tvp514x_std current_std; int num_stds; struct tvp514x_std_info *std_list; - /* - * Input and Output Routing parameters - */ + /* Input and Output Routing parameters */ u32 input; u32 output; }; /* TVP514x default register values */ static struct tvp514x_reg tvp514x_reg_list_default[] = { - {TOK_WRITE, REG_INPUT_SEL, 0x05}, /* Composite selected */ + /* Composite selected */ + {TOK_WRITE, REG_INPUT_SEL, 0x05}, {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F}, - {TOK_WRITE, REG_VIDEO_STD, 0x00}, /* Auto mode */ + /* Auto mode */ + {TOK_WRITE, REG_VIDEO_STD, 0x00}, {TOK_WRITE, REG_OPERATION_MODE, 0x00}, {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F}, {TOK_WRITE, REG_COLOR_KILLER, 0x10}, @@ -135,53 +133,73 @@ static struct tvp514x_reg tvp514x_reg_list_default[] = { {TOK_WRITE, REG_HUE, 0x00}, {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00}, {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E}, - {TOK_SKIP, 0x0F, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x0F, 0x00}, {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80}, {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80}, {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80}, - {TOK_SKIP, 0x13, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x13, 0x00}, {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80}, - {TOK_SKIP, 0x15, 0x00}, /* Reserved */ - {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, /* NTSC timing */ + /* Reserved */ + {TOK_SKIP, 0x15, 0x00}, + /* NTSC timing */ + {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00}, {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25}, {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03}, - {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, /* NTSC timing */ + /* NTSC timing */ + {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00}, {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40}, {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00}, - {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, /* NTSC timing */ + /* NTSC timing */ + {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00}, {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07}, {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00}, - {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, /* NTSC timing */ + /* NTSC timing */ + {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00}, {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15}, {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00}, - {TOK_SKIP, 0x26, 0x00}, /* Reserved */ - {TOK_SKIP, 0x27, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x26, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x27, 0x00}, {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC}, - {TOK_SKIP, 0x29, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x29, 0x00}, {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00}, - {TOK_SKIP, 0x2B, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x2B, 0x00}, {TOK_SKIP, REG_SCART_DELAY, 0x00}, {TOK_SKIP, REG_CTI_DELAY, 0x00}, {TOK_SKIP, REG_CTI_CONTROL, 0x00}, - {TOK_SKIP, 0x2F, 0x00}, /* Reserved */ - {TOK_SKIP, 0x30, 0x00}, /* Reserved */ - {TOK_SKIP, 0x31, 0x00}, /* Reserved */ - {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, /* HS, VS active high */ - {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, /* 10-bit BT.656 */ - {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, /* Enable clk & data */ - {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, /* Enable AVID & FLD */ - {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, /* Enable VS & HS */ + /* Reserved */ + {TOK_SKIP, 0x2F, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x30, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x31, 0x00}, + /* HS, VS active high */ + {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, + /* 10-bit BT.656 */ + {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, + /* Enable clk & data */ + {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, + /* Enable AVID & FLD */ + {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, + /* Enable VS & HS */ + {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF}, {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF}, - {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, /* Clear status */ + /* Clear status */ + {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, {TOK_TERM, 0, 0}, }; -/* +/** * List of image formats supported by TVP5146/47 decoder * Currently we are using 8 bit mode only, but can be * extended to 10/20 bit mode. @@ -196,7 +214,7 @@ static const struct v4l2_fmtdesc tvp514x_fmt_list[] = { }, }; -/* +/** * Supported standards - * * Currently supports two standards only, need to add support for rest of the @@ -239,8 +257,11 @@ static inline struct tvp514x_decoder *to_decoder(struct v4l2_subdev *sd) } -/* - * Read a value from a register in an TVP5146/47 decoder device. +/** + * tvp514x_read_reg() - Read a value from a register in an TVP5146/47. + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + * * Returns value read if successful, or non-zero (-1) otherwise. */ static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg) @@ -263,6 +284,11 @@ read_again: return err; } +/** + * dump_reg() - dump the register content of TVP5146/47. + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + */ static void dump_reg(struct v4l2_subdev *sd, u8 reg) { u32 val; @@ -271,7 +297,12 @@ static void dump_reg(struct v4l2_subdev *sd, u8 reg) v4l2_info(sd, "Reg(0x%.2X): 0x%.2X\n", reg, val); } -/* +/** + * tvp514x_write_reg() - Write a value to a register in TVP5146/47 + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + * @val: value to be written to the register + * * Write a value to a register in an TVP5146/47 decoder device. * Returns zero if successful, or non-zero otherwise. */ @@ -295,14 +326,16 @@ write_again: return err; } -/* - * tvp514x_write_regs : Initializes a list of TVP5146/47 registers +/** + * tvp514x_write_regs() : Initializes a list of TVP5146/47 registers + * @sd: ptr to v4l2_subdev struct + * @reglist: list of TVP5146/47 registers and values + * + * Initializes a list of TVP5146/47 registers:- * if token is TOK_TERM, then entire write operation terminates * if token is TOK_DELAY, then a delay of 'val' msec is introduced * if token is TOK_SKIP, then the register write is skipped * if token is TOK_WRITE, then the register write is performed - * - * reglist - list of registers to be written * Returns zero if successful, or non-zero otherwise. */ static int tvp514x_write_regs(struct v4l2_subdev *sd, @@ -329,9 +362,12 @@ static int tvp514x_write_regs(struct v4l2_subdev *sd, return 0; } -/* - * tvp514x_get_current_std: - * Returns the current standard detected by TVP5146/47 +/** + * tvp514x_get_current_std() : Get the current standard detected by TVP5146/47 + * @sd: ptr to v4l2_subdev struct + * + * Get current standard detected by TVP5146/47, STD_INVALID if there is no + * standard detected. */ static enum tvp514x_std tvp514x_get_current_std(struct v4l2_subdev *sd) { @@ -342,7 +378,8 @@ static enum tvp514x_std tvp514x_get_current_std(struct v4l2_subdev *sd) /* use the standard status register */ std_status = tvp514x_read_reg(sd, REG_VIDEO_STD_STATUS); else - std_status = std; /* use the standard register itself */ + /* use the standard register itself */ + std_status = std; switch (std_status & VIDEO_STD_MASK) { case VIDEO_STD_NTSC_MJ_BIT: @@ -358,9 +395,7 @@ static enum tvp514x_std tvp514x_get_current_std(struct v4l2_subdev *sd) return STD_INVALID; } -/* - * TVP5146/47 register dump function - */ +/* TVP5146/47 register dump function */ static void tvp514x_reg_dump(struct v4l2_subdev *sd) { dump_reg(sd, REG_INPUT_SEL); @@ -407,8 +442,11 @@ static void tvp514x_reg_dump(struct v4l2_subdev *sd) dump_reg(sd, REG_CLEAR_LOST_LOCK); } -/* - * Configure the TVP5146/47 with the current register settings +/** + * tvp514x_configure() - Configure the TVP5146/47 registers + * @sd: ptr to v4l2_subdev struct + * @decoder: ptr to tvp514x_decoder structure + * * Returns zero if successful, or non-zero otherwise. */ static int tvp514x_configure(struct v4l2_subdev *sd, @@ -428,8 +466,11 @@ static int tvp514x_configure(struct v4l2_subdev *sd, return 0; } -/* - * Detect if an tvp514x is present, and if so which revision. +/** + * tvp514x_detect() - Detect if an tvp514x is present, and if so which revision. + * @sd: pointer to standard V4L2 sub-device structure + * @decoder: pointer to tvp514x_decoder structure + * * A device is considered to be detected if the chip ID (LSB and MSB) * registers match the expected values. * Any value of the rom version register is accepted. @@ -468,13 +509,8 @@ static int tvp514x_detect(struct v4l2_subdev *sd, return 0; } -/* - * Following are decoder interface functions implemented by - * TVP5146/47 decoder driver. - */ - -/* - * tvp514x_querystd - V4L2 decoder interface handler for VIDIOC_QUERYSTD ioctl +/** + * tvp514x_querystd() - V4L2 decoder interface handler for querystd * @sd: pointer to standard V4L2 sub-device structure * @std_id: standard V4L2 std_id ioctl enum * @@ -546,8 +582,8 @@ static int tvp514x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id) return 0; } -/* - * tvp514x_s_std - V4L2 decoder interface handler for VIDIOC_S_STD ioctl +/** + * tvp514x_s_std() - V4L2 decoder interface handler for s_std * @sd: pointer to standard V4L2 sub-device structure * @std_id: standard V4L2 v4l2_std_id ioctl enum * @@ -580,10 +616,12 @@ static int tvp514x_s_std(struct v4l2_subdev *sd, v4l2_std_id std_id) return 0; } -/* - * tvp514x_s_routing - V4L2 decoder interface handler for VIDIOC_S_INPUT ioctl +/** + * tvp514x_s_routing() - V4L2 decoder interface handler for s_routing * @sd: pointer to standard V4L2 sub-device structure - * @index: number of the input + * @input: input selector for routing the signal + * @output: output selector for routing the signal + * @config: config value. Not used * * If index is valid, selects the requested input. Otherwise, returns -EINVAL if * the input is not supported or there is no active signal present in the @@ -602,7 +640,8 @@ static int tvp514x_s_routing(struct v4l2_subdev *sd, if ((input >= INPUT_INVALID) || (output >= OUTPUT_INVALID)) - return -EINVAL; /* Index out of bound */ + /* Index out of bound */ + return -EINVAL; input_sel = input; output_sel = output; @@ -659,7 +698,7 @@ static int tvp514x_s_routing(struct v4l2_subdev *sd, lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | STATUS_VIRT_SYNC_LOCK_BIT; break; - /*Need to add other interfaces*/ + /* Need to add other interfaces*/ default: return -EINVAL; } @@ -676,7 +715,8 @@ static int tvp514x_s_routing(struct v4l2_subdev *sd, sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1); if (lock_mask == (sync_lock_status & lock_mask)) - break; /* Input detected */ + /* Input detected */ + break; } if ((current_std == STD_INVALID) || (try_count < 0)) @@ -692,8 +732,8 @@ static int tvp514x_s_routing(struct v4l2_subdev *sd, return 0; } -/* - * tvp514x_queryctrl - V4L2 decoder interface handler for VIDIOC_QUERYCTRL ioctl +/** + * tvp514x_queryctrl() - V4L2 decoder interface handler for queryctrl * @sd: pointer to standard V4L2 sub-device structure * @qctrl: standard V4L2 v4l2_queryctrl structure * @@ -710,13 +750,13 @@ tvp514x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl) switch (qctrl->id) { case V4L2_CID_BRIGHTNESS: - /* Brightness supported is (0-255), - */ + /* Brightness supported is (0-255), */ err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128); break; case V4L2_CID_CONTRAST: case V4L2_CID_SATURATION: - /* Saturation and Contrast supported is - + /** + * Saturation and Contrast supported is - * Contrast: 0 - 255 (Default - 128) * Saturation: 0 - 255 (Default - 128) */ @@ -729,7 +769,7 @@ tvp514x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl) err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0); break; case V4L2_CID_AUTOGAIN: - /* + /** * Auto Gain supported is - * 0 - 1 (Default - 1) */ @@ -747,8 +787,8 @@ tvp514x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl) return err; } -/* - * tvp514x_g_ctrl - V4L2 decoder interface handler for VIDIOC_G_CTRL ioctl +/** + * tvp514x_g_ctrl() - V4L2 decoder interface handler for g_ctrl * @sd: pointer to standard V4L2 sub-device structure * @ctrl: pointer to v4l2_control structure * @@ -802,8 +842,8 @@ tvp514x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return 0; } -/* - * tvp514x_s_ctrl - V4L2 decoder interface handler for VIDIOC_S_CTRL ioctl +/** + * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl * @sd: pointer to standard V4L2 sub-device structure * @ctrl: pointer to v4l2_control structure * @@ -903,8 +943,8 @@ tvp514x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return err; } -/* - * tvp514x_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl +/** + * tvp514x_enum_fmt_cap() - V4L2 decoder interface handler for enum_fmt * @sd: pointer to standard V4L2 sub-device structure * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure * @@ -921,10 +961,12 @@ tvp514x_enum_fmt_cap(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmt) index = fmt->index; if ((index >= decoder->num_fmts) || (index < 0)) - return -EINVAL; /* Index out of bound */ + /* Index out of bound */ + return -EINVAL; if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; memcpy(fmt, &decoder->fmt_list[index], sizeof(struct v4l2_fmtdesc)); @@ -935,8 +977,8 @@ tvp514x_enum_fmt_cap(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmt) return 0; } -/* - * tvp514x_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl +/** + * tvp514x_try_fmt_cap() - V4L2 decoder interface handler for try_fmt * @sd: pointer to standard V4L2 sub-device structure * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure * @@ -956,6 +998,7 @@ tvp514x_try_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) return -EINVAL; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + /* only capture is supported */ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; pix = &f->fmt.pix; @@ -975,7 +1018,8 @@ tvp514x_try_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) break; } if (ifmt == decoder->num_fmts) - ifmt = 0; /* None of the format matched, select default */ + /* None of the format matched, select default */ + ifmt = 0; pix->pixelformat = decoder->fmt_list[ifmt].pixelformat; pix->field = V4L2_FIELD_INTERLACED; @@ -991,8 +1035,8 @@ tvp514x_try_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) return 0; } -/* - * tvp514x_s_fmt_cap - V4L2 decoder interface handler for VIDIOC_S_FMT ioctl +/** + * tvp514x_s_fmt_cap() - V4L2 decoder interface handler for s_fmt * @sd: pointer to standard V4L2 sub-device structure * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure * @@ -1011,7 +1055,8 @@ tvp514x_s_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) return -EINVAL; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; pix = &f->fmt.pix; rval = tvp514x_try_fmt_cap(sd, f); @@ -1023,8 +1068,8 @@ tvp514x_s_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) return rval; } -/* - * tvp514x_g_fmt_cap - V4L2 decoder interface handler for tvp514x_g_fmt_cap +/** + * tvp514x_g_fmt_cap() - V4L2 decoder interface handler for tvp514x_g_fmt_cap * @sd: pointer to standard V4L2 sub-device structure * @f: pointer to standard V4L2 v4l2_format structure * @@ -1040,7 +1085,8 @@ tvp514x_g_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) return -EINVAL; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; f->fmt.pix = decoder->pix; @@ -1051,8 +1097,8 @@ tvp514x_g_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) return 0; } -/* - * tvp514x_g_parm - V4L2 decoder interface handler for VIDIOC_G_PARM ioctl +/** + * tvp514x_g_parm() - V4L2 decoder interface handler for g_parm * @sd: pointer to standard V4L2 sub-device structure * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure * @@ -1069,7 +1115,8 @@ tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) return -EINVAL; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; memset(a, 0, sizeof(*a)); a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -1089,8 +1136,8 @@ tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) return 0; } -/* - * tvp514x_s_parm - V4L2 decoder interface handler for VIDIOC_S_PARM ioctl +/** + * tvp514x_s_parm() - V4L2 decoder interface handler for s_parm * @sd: pointer to standard V4L2 sub-device structure * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure * @@ -1108,7 +1155,8 @@ tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) return -EINVAL; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; timeperframe = &a->parm.capture.timeperframe; @@ -1125,8 +1173,8 @@ tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) return 0; } -/* - * tvp514x_s_stream - V4L2 decoder interface handler for vidioc_int_s_power_num +/** + * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream * @sd: pointer to standard V4L2 sub-device structure * @enable: streaming enable or disable * @@ -1216,7 +1264,8 @@ static struct tvp514x_decoder tvp514x_dev = { .fmt_list = tvp514x_fmt_list, .num_fmts = ARRAY_SIZE(tvp514x_fmt_list), - .pix = {/* Default to NTSC 8-bit YUV 422 */ + .pix = { + /* Default to NTSC 8-bit YUV 422 */ .width = NTSC_NUM_ACTIVE_PIXELS, .height = NTSC_NUM_ACTIVE_LINES, .pixelformat = V4L2_PIX_FMT_UYVY, @@ -1233,8 +1282,8 @@ static struct tvp514x_decoder tvp514x_dev = { }; -/* - * tvp514x_probe - decoder driver i2c probe handler +/** + * tvp514x_probe() - decoder driver i2c probe handler * @client: i2c driver client device structure * @id: i2c driver id table * @@ -1260,20 +1309,16 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) if (!decoder) return -ENOMEM; - /* - * Initialize the tvp514x_decoder with default configuration - */ + /* Initialize the tvp514x_decoder with default configuration */ *decoder = tvp514x_dev; /* Copy default register configuration */ memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default, sizeof(tvp514x_reg_list_default)); - /* - * Copy board specific information here - */ + /* Copy board specific information here */ decoder->pdata = client->dev.platform_data; - /* + /** * Fetch platform specific data, and configure the * tvp514x_reg_list[] accordingly. Since this is one * time configuration, no need to preserve. @@ -1297,8 +1342,8 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) } -/* - * tvp514x_remove - decoder driver i2c remove handler +/** + * tvp514x_remove() - decoder driver i2c remove handler * @client: i2c driver client device structure * * Unregister decoder as an i2c client device and V4L2 @@ -1313,9 +1358,7 @@ static int tvp514x_remove(struct i2c_client *client) kfree(decoder); return 0; } -/* - * TVP5146 Init/Power on Sequence - */ +/* TVP5146 Init/Power on Sequence */ static const struct tvp514x_reg tvp5146_init_reg_seq[] = { {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, @@ -1331,9 +1374,7 @@ static const struct tvp514x_reg tvp5146_init_reg_seq[] = { {TOK_TERM, 0, 0}, }; -/* - * TVP5147 Init/Power on Sequence - */ +/* TVP5147 Init/Power on Sequence */ static const struct tvp514x_reg tvp5147_init_reg_seq[] = { {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, @@ -1356,16 +1397,14 @@ static const struct tvp514x_reg tvp5147_init_reg_seq[] = { {TOK_TERM, 0, 0}, }; -/* - * TVP5146M2/TVP5147M1 Init/Power on Sequence - */ +/* TVP5146M2/TVP5147M1 Init/Power on Sequence */ static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { {TOK_WRITE, REG_OPERATION_MODE, 0x01}, {TOK_WRITE, REG_OPERATION_MODE, 0x00}, {TOK_TERM, 0, 0}, }; -/* +/** * I2C Device Table - * * name - Name of the actual device/chip. -- cgit v1.2.3 From 0ec62142b0a383a2c32114b2c8e44713b88eea8b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 6 Jul 2009 20:04:12 +0200 Subject: v4l: vpfe capture bridge driver for DM355 and DM6446 From: Muralidharan Karicheri This the vpfe capture bridge driver for doing video capture on DM355 and DM6446 evms. The ccdc hw modules register with the driver and are used for configuring the CCD Controller for a specific decoder interface. The driver also registers the sub devices required for a specific evm. More than one sub devices can be registered. This allows driver to switch dynamically to capture video from any sub device that is registered. Currently only one sub device (tvp5146) is supported. But in future this driver is expected to do capture from sensor devices such as Micron's MT9T001, MT9T031 and MT9P031 etc. The driver currently supports MMAP based IO. Reviewed by: Laurent Pinchart Reviewed by: Alexey Klimov Priority: normal Signed-off-by: Muralidharan Karicheri Signed-off-by: Hans Verkuil --- linux/drivers/media/video/davinci/vpfe_capture.c | 2124 ++++++++++++++++++++++ linux/include/media/davinci/vpfe_capture.h | 198 ++ linux/include/media/davinci/vpfe_types.h | 51 + 3 files changed, 2373 insertions(+) create mode 100644 linux/drivers/media/video/davinci/vpfe_capture.c create mode 100644 linux/include/media/davinci/vpfe_capture.h create mode 100644 linux/include/media/davinci/vpfe_types.h diff --git a/linux/drivers/media/video/davinci/vpfe_capture.c b/linux/drivers/media/video/davinci/vpfe_capture.c new file mode 100644 index 000000000..402ce43ef --- /dev/null +++ b/linux/drivers/media/video/davinci/vpfe_capture.c @@ -0,0 +1,2124 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Driver name : VPFE Capture driver + * VPFE Capture driver allows applications to capture and stream video + * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as + * TVP5146 or Raw Bayer RGB image data from an image sensor + * such as Microns' MT9T001, MT9T031 etc. + * + * These SoCs have, in common, a Video Processing Subsystem (VPSS) that + * consists of a Video Processing Front End (VPFE) for capturing + * video/raw image data and Video Processing Back End (VPBE) for displaying + * YUV data through an in-built analog encoder or Digital LCD port. This + * driver is for capture through VPFE. A typical EVM using these SoCs have + * following high level configuration. + * + * + * decoder(TVP5146/ YUV/ + * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF) + * data input | | + * V | + * SDRAM | + * V + * Image Processor + * | + * V + * SDRAM + * The data flow happens from a decoder connected to the VPFE over a + * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface + * and to the input of VPFE through an optional MUX (if more inputs are + * to be interfaced on the EVM). The input data is first passed through + * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC + * does very little or no processing on YUV data and does pre-process Raw + * Bayer RGB data through modules such as Defect Pixel Correction (DFC) + * Color Space Conversion (CSC), data gain/offset etc. After this, data + * can be written to SDRAM or can be connected to the image processing + * block such as IPIPE (on DM355 only). + * + * Features supported + * - MMAP IO + * - Capture using TVP5146 over BT.656 + * - support for interfacing decoders using sub device model + * - Work with DM355 or DM6446 CCDC to do Raw Bayer RGB/YUV + * data capture to SDRAM. + * TODO list + * - Support multiple REQBUF after open + * - Support for de-allocating buffers through REQBUF + * - Support for Raw Bayer RGB capture + * - Support for chaining Image Processor + * - Support for static allocation of buffers + * - Support for USERPTR IO + * - Support for STREAMON before QBUF + * - Support for control ioctls + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "ccdc_hw_device.h" + +static int debug; +static u32 numbuffers = 3; +static u32 bufsize = (720 * 576 * 2); + +module_param(numbuffers, uint, S_IRUGO); +module_param(bufsize, uint, S_IRUGO); +module_param(debug, int, 0644); + +MODULE_PARM_DESC(numbuffers, "buffer count (default:3)"); +MODULE_PARM_DESC(bufsize, "buffer size in bytes (default:720 x 576 x 2)"); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); + +/* standard information */ +struct vpfe_standard { + v4l2_std_id std_id; + unsigned int width; + unsigned int height; + struct v4l2_fract pixelaspect; + /* 0 - progressive, 1 - interlaced */ + int frame_format; +}; + +/* ccdc configuration */ +struct ccdc_config { + /* This make sure vpfe is probed and ready to go */ + int vpfe_probed; + /* name of ccdc device */ + char name[32]; + /* for storing mem maps for CCDC */ + int ccdc_addr_size; + void *__iomem ccdc_addr; +}; + +/* data structures */ +static struct vpfe_config_params config_params = { + .min_numbuffers = 3, + .numbuffers = 3, + .min_bufsize = 720 * 480 * 2, + .device_bufsize = 720 * 576 * 2, +}; + +/* ccdc device registered */ +static struct ccdc_hw_device *ccdc_dev; +/* lock for accessing ccdc information */ +static DEFINE_MUTEX(ccdc_lock); +/* ccdc configuration */ +static struct ccdc_config *ccdc_cfg; + +const struct vpfe_standard vpfe_standards[] = { + {V4L2_STD_525_60, 720, 480, {11, 10}, 1}, + {V4L2_STD_625_50, 720, 576, {54, 59}, 1}, +}; + +/* Used when raw Bayer image from ccdc is directly captured to SDRAM */ +static const struct vpfe_pixel_format vpfe_pix_fmts[] = { + { + .fmtdesc = { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Bayer GrRBGb 8bit A-Law compr.", + .pixelformat = V4L2_PIX_FMT_SBGGR8, + }, + .bpp = 1, + }, + { + .fmtdesc = { + .index = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Bayer GrRBGb - 16bit", + .pixelformat = V4L2_PIX_FMT_SBGGR16, + }, + .bpp = 2, + }, + { + .fmtdesc = { + .index = 2, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Bayer GrRBGb 8bit DPCM compr.", + .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8, + }, + .bpp = 1, + }, + { + .fmtdesc = { + .index = 3, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "YCbCr 4:2:2 Interleaved UYVY", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, + .bpp = 2, + }, + { + .fmtdesc = { + .index = 4, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "YCbCr 4:2:2 Interleaved YUYV", + .pixelformat = V4L2_PIX_FMT_YUYV, + }, + .bpp = 2, + }, + { + .fmtdesc = { + .index = 5, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Y/CbCr 4:2:0 - Semi planar", + .pixelformat = V4L2_PIX_FMT_NV12, + }, + .bpp = 1, + }, +}; + +/* + * vpfe_lookup_pix_format() + * lookup an entry in the vpfe pix format table based on pix_format + */ +static const struct vpfe_pixel_format *vpfe_lookup_pix_format(u32 pix_format) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vpfe_pix_fmts); i++) { + if (pix_format == vpfe_pix_fmts[i].fmtdesc.pixelformat) + return &vpfe_pix_fmts[i]; + } + return NULL; +} + +/* + * vpfe_register_ccdc_device. CCDC module calls this to + * register with vpfe capture + */ +int vpfe_register_ccdc_device(struct ccdc_hw_device *dev) +{ + int ret = 0; + printk(KERN_NOTICE "vpfe_register_ccdc_device: %s\n", dev->name); + + BUG_ON(!dev->hw_ops.open); + BUG_ON(!dev->hw_ops.enable); + BUG_ON(!dev->hw_ops.set_hw_if_params); + BUG_ON(!dev->hw_ops.configure); + BUG_ON(!dev->hw_ops.set_buftype); + BUG_ON(!dev->hw_ops.get_buftype); + BUG_ON(!dev->hw_ops.enum_pix); + BUG_ON(!dev->hw_ops.set_frame_format); + BUG_ON(!dev->hw_ops.get_frame_format); + BUG_ON(!dev->hw_ops.get_pixel_format); + BUG_ON(!dev->hw_ops.set_pixel_format); + BUG_ON(!dev->hw_ops.set_params); + BUG_ON(!dev->hw_ops.set_image_window); + BUG_ON(!dev->hw_ops.get_image_window); + BUG_ON(!dev->hw_ops.get_line_length); + BUG_ON(!dev->hw_ops.setfbaddr); + BUG_ON(!dev->hw_ops.getfid); + + mutex_lock(&ccdc_lock); + if (NULL == ccdc_cfg) { + /* + * TODO. Will this ever happen? if so, we need to fix it. + * Proabably we need to add the request to a linked list and + * walk through it during vpfe probe + */ + printk(KERN_ERR "vpfe capture not initialized\n"); + ret = -1; + goto unlock; + } + + if (strcmp(dev->name, ccdc_cfg->name)) { + /* ignore this ccdc */ + ret = -1; + goto unlock; + } + + if (ccdc_dev) { + printk(KERN_ERR "ccdc already registered\n"); + ret = -1; + goto unlock; + } + + ccdc_dev = dev; + dev->hw_ops.set_ccdc_base(ccdc_cfg->ccdc_addr, + ccdc_cfg->ccdc_addr_size); +unlock: + mutex_unlock(&ccdc_lock); + return ret; +} +EXPORT_SYMBOL(vpfe_register_ccdc_device); + +/* + * vpfe_unregister_ccdc_device. CCDC module calls this to + * unregister with vpfe capture + */ +void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev) +{ + if (NULL == dev) { + printk(KERN_ERR "invalid ccdc device ptr\n"); + return; + } + + printk(KERN_NOTICE "vpfe_unregister_ccdc_device, dev->name = %s\n", + dev->name); + + if (strcmp(dev->name, ccdc_cfg->name)) { + /* ignore this ccdc */ + return; + } + + mutex_lock(&ccdc_lock); + ccdc_dev = NULL; + mutex_unlock(&ccdc_lock); + return; +} +EXPORT_SYMBOL(vpfe_unregister_ccdc_device); + +/* + * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings + */ +static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe_dev, + struct v4l2_format *f) +{ + struct v4l2_rect image_win; + enum ccdc_buftype buf_type; + enum ccdc_frmfmt frm_fmt; + + memset(f, 0, sizeof(*f)); + f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + ccdc_dev->hw_ops.get_image_window(&image_win); + f->fmt.pix.width = image_win.width; + f->fmt.pix.height = image_win.height; + f->fmt.pix.bytesperline = ccdc_dev->hw_ops.get_line_length(); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height; + buf_type = ccdc_dev->hw_ops.get_buftype(); + f->fmt.pix.pixelformat = ccdc_dev->hw_ops.get_pixel_format(); + frm_fmt = ccdc_dev->hw_ops.get_frame_format(); + if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + f->fmt.pix.field = V4L2_FIELD_NONE; + else if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED) + f->fmt.pix.field = V4L2_FIELD_SEQ_TB; + else { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf_type\n"); + return -EINVAL; + } + } else { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid frm_fmt\n"); + return -EINVAL; + } + return 0; +} + +/* + * vpfe_config_ccdc_image_format() + * For a pix format, configure ccdc to setup the capture + */ +static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; + int ret = 0; + + if (ccdc_dev->hw_ops.set_pixel_format( + vpfe_dev->fmt.fmt.pix.pixelformat) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "couldn't set pix format in ccdc\n"); + return -EINVAL; + } + /* configure the image window */ + ccdc_dev->hw_ops.set_image_window(&vpfe_dev->crop); + + switch (vpfe_dev->fmt.fmt.pix.field) { + case V4L2_FIELD_INTERLACED: + /* do nothing, since it is default */ + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_INTERLEAVED); + break; + case V4L2_FIELD_NONE: + frm_fmt = CCDC_FRMFMT_PROGRESSIVE; + /* buffer type only applicable for interlaced scan */ + break; + case V4L2_FIELD_SEQ_TB: + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_SEPARATED); + break; + default: + return -EINVAL; + } + + /* set the frame format */ + if (!ret) + ret = ccdc_dev->hw_ops.set_frame_format(frm_fmt); + return ret; +} +/* + * vpfe_config_image_format() + * For a given standard, this functions sets up the default + * pix format & crop values in the vpfe device and ccdc. It first + * starts with defaults based values from the standard table. + * It then checks if sub device support g_fmt and then override the + * values based on that.Sets crop values to match with scan resolution + * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the + * values in ccdc + */ +static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, + const v4l2_std_id *std_id) +{ + struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev; + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { + if (vpfe_standards[i].std_id & *std_id) { + vpfe_dev->std_info.active_pixels = + vpfe_standards[i].width; + vpfe_dev->std_info.active_lines = + vpfe_standards[i].height; + vpfe_dev->std_info.frame_format = + vpfe_standards[i].frame_format; + vpfe_dev->std_index = i; + break; + } + } + + if (i == ARRAY_SIZE(vpfe_standards)) { + v4l2_err(&vpfe_dev->v4l2_dev, "standard not supported\n"); + return -EINVAL; + } + + vpfe_dev->crop.top = 0; + vpfe_dev->crop.left = 0; + vpfe_dev->crop.width = vpfe_dev->std_info.active_pixels; + vpfe_dev->crop.height = vpfe_dev->std_info.active_lines; + vpfe_dev->fmt.fmt.pix.width = vpfe_dev->crop.width; + vpfe_dev->fmt.fmt.pix.height = vpfe_dev->crop.height; + + /* first field and frame format based on standard frame format */ + if (vpfe_dev->std_info.frame_format) { + vpfe_dev->fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + /* assume V4L2_PIX_FMT_UYVY as default */ + vpfe_dev->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + } else { + vpfe_dev->fmt.fmt.pix.field = V4L2_FIELD_NONE; + /* assume V4L2_PIX_FMT_SBGGR8 */ + vpfe_dev->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; + } + + /* if sub device supports g_fmt, override the defaults */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, video, g_fmt, &vpfe_dev->fmt); + + if (ret && ret != -ENOIOCTLCMD) { + v4l2_err(&vpfe_dev->v4l2_dev, + "error in getting g_fmt from sub device\n"); + return ret; + } + + /* Sets the values in CCDC */ + ret = vpfe_config_ccdc_image_format(vpfe_dev); + if (ret) + return ret; + + /* Update the values of sizeimage and bytesperline */ + if (!ret) { + vpfe_dev->fmt.fmt.pix.bytesperline = + ccdc_dev->hw_ops.get_line_length(); + vpfe_dev->fmt.fmt.pix.sizeimage = + vpfe_dev->fmt.fmt.pix.bytesperline * + vpfe_dev->fmt.fmt.pix.height; + } + return ret; +} + +static int vpfe_initialize_device(struct vpfe_device *vpfe_dev) +{ + int ret = 0; + + /* set first input of current subdevice as the current input */ + vpfe_dev->current_input = 0; + + /* set default standard */ + vpfe_dev->std_index = 0; + + /* Configure the default format information */ + ret = vpfe_config_image_format(vpfe_dev, + &vpfe_standards[vpfe_dev->std_index].std_id); + if (ret) + return ret; + + /* now open the ccdc device to initialize it */ + mutex_lock(&ccdc_lock); + if (NULL == ccdc_dev) { + v4l2_err(&vpfe_dev->v4l2_dev, "ccdc device not registered\n"); + ret = -ENODEV; + goto unlock; + } + + if (!try_module_get(ccdc_dev->owner)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Couldn't lock ccdc module\n"); + ret = -ENODEV; + goto unlock; + } + ret = ccdc_dev->hw_ops.open(vpfe_dev->pdev); + if (!ret) + vpfe_dev->initialized = 1; +unlock: + mutex_unlock(&ccdc_lock); + return ret; +} + +/* + * vpfe_open : It creates object of file handle structure and + * stores it in private_data member of filepointer + */ +static int vpfe_open(struct file *file) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n"); + + if (!vpfe_dev->cfg->num_subdevs) { + v4l2_err(&vpfe_dev->v4l2_dev, "No decoder registered\n"); + return -ENODEV; + } + + /* Allocate memory for the file handle object */ + fh = kmalloc(sizeof(struct vpfe_fh), GFP_KERNEL); + if (NULL == fh) { + v4l2_err(&vpfe_dev->v4l2_dev, + "unable to allocate memory for file handle object\n"); + return -ENOMEM; + } + /* store pointer to fh in private_data member of file */ + file->private_data = fh; + fh->vpfe_dev = vpfe_dev; + mutex_lock(&vpfe_dev->lock); + /* If decoder is not initialized. initialize it */ + if (!vpfe_dev->initialized) { + if (vpfe_initialize_device(vpfe_dev)) { + mutex_unlock(&vpfe_dev->lock); + return -ENODEV; + } + } + /* Increment device usrs counter */ + vpfe_dev->usrs++; + /* Set io_allowed member to false */ + fh->io_allowed = 0; + /* Initialize priority of this instance to default priority */ + fh->prio = V4L2_PRIORITY_UNSET; + v4l2_prio_open(&vpfe_dev->prio, &fh->prio); + mutex_unlock(&vpfe_dev->lock); + return 0; +} + +static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe_dev) +{ + unsigned long addr; + + vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, + struct videobuf_buffer, queue); + list_del(&vpfe_dev->next_frm->queue); + vpfe_dev->next_frm->state = VIDEOBUF_ACTIVE; + addr = videobuf_to_dma_contig(vpfe_dev->next_frm); + ccdc_dev->hw_ops.setfbaddr(addr); +} + +static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev) +{ + struct timeval timevalue; + + do_gettimeofday(&timevalue); + vpfe_dev->cur_frm->ts = timevalue; + vpfe_dev->cur_frm->state = VIDEOBUF_DONE; + vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage; + wake_up_interruptible(&vpfe_dev->cur_frm->done); + vpfe_dev->cur_frm = vpfe_dev->next_frm; +} + +/* ISR for VINT0*/ +static irqreturn_t vpfe_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + enum v4l2_field field; + unsigned long addr; + int fid; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nStarting vpfe_isr...\n"); + field = vpfe_dev->fmt.fmt.pix.field; + + /* if streaming not started, don't do anything */ + if (!vpfe_dev->started) + return IRQ_HANDLED; + + /* only for 6446 this will be applicable */ + if (NULL != ccdc_dev->hw_ops.reset) + ccdc_dev->hw_ops.reset(); + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "frame format is progressive...\n"); + if (vpfe_dev->cur_frm != vpfe_dev->next_frm) + vpfe_process_buffer_complete(vpfe_dev); + return IRQ_HANDLED; + } + + /* interlaced or TB capture check which field we are in hardware */ + fid = ccdc_dev->hw_ops.getfid(); + + /* switch the software maintained field id */ + vpfe_dev->field_id ^= 1; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "field id = %x:%x.\n", + fid, vpfe_dev->field_id); + if (fid == vpfe_dev->field_id) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the next frame + * is available, release the current frame and move on + */ + if (vpfe_dev->cur_frm != vpfe_dev->next_frm) + vpfe_process_buffer_complete(vpfe_dev); + /* + * based on whether the two fields are stored + * interleavely or separately in memory, reconfigure + * the CCDC memory address + */ + if (field == V4L2_FIELD_SEQ_TB) { + addr = + videobuf_to_dma_contig(vpfe_dev->cur_frm); + addr += vpfe_dev->field_off; + ccdc_dev->hw_ops.setfbaddr(addr); + } + return IRQ_HANDLED; + } + /* + * if one field is just being captured configure + * the next frame get the next frame from the empty + * queue if no frame is available hold on to the + * current buffer + */ + spin_lock(&vpfe_dev->dma_queue_lock); + if (!list_empty(&vpfe_dev->dma_queue) && + vpfe_dev->cur_frm == vpfe_dev->next_frm) + vpfe_schedule_next_buffer(vpfe_dev); + spin_unlock(&vpfe_dev->dma_queue_lock); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + vpfe_dev->field_id = fid; + } + return IRQ_HANDLED; +} + +/* vdint1_isr - isr handler for VINT1 interrupt */ +static irqreturn_t vdint1_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nInside vdint1_isr...\n"); + + /* if streaming not started, don't do anything */ + if (!vpfe_dev->started) + return IRQ_HANDLED; + + spin_lock(&vpfe_dev->dma_queue_lock); + if ((vpfe_dev->fmt.fmt.pix.field == V4L2_FIELD_NONE) && + !list_empty(&vpfe_dev->dma_queue) && + vpfe_dev->cur_frm == vpfe_dev->next_frm) + vpfe_schedule_next_buffer(vpfe_dev); + spin_unlock(&vpfe_dev->dma_queue_lock); + return IRQ_HANDLED; +} + +static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frame_format; + + frame_format = ccdc_dev->hw_ops.get_frame_format(); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) + free_irq(IRQ_VDINT1, vpfe_dev); +} + +static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frame_format; + + frame_format = ccdc_dev->hw_ops.get_frame_format(); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) { + return request_irq(vpfe_dev->ccdc_irq1, vdint1_isr, + IRQF_DISABLED, "vpfe_capture1", + vpfe_dev); + } + return 0; +} + +/* vpfe_stop_ccdc_capture: stop streaming in ccdc/isif */ +static void vpfe_stop_ccdc_capture(struct vpfe_device *vpfe_dev) +{ + vpfe_dev->started = 0; + ccdc_dev->hw_ops.enable(0); + if (ccdc_dev->hw_ops.enable_out_to_sdram) + ccdc_dev->hw_ops.enable_out_to_sdram(0); +} + +/* + * vpfe_release : This function deletes buffer queue, frees the + * buffers and the vpfe file handle + */ +static int vpfe_release(struct file *file) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n"); + + /* Get the device lock */ + mutex_lock(&vpfe_dev->lock); + /* if this instance is doing IO */ + if (fh->io_allowed) { + if (vpfe_dev->started) { + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, + video, s_stream, 0); + if (ret && (ret != -ENOIOCTLCMD)) + v4l2_err(&vpfe_dev->v4l2_dev, + "stream off failed in subdev\n"); + vpfe_stop_ccdc_capture(vpfe_dev); + vpfe_detach_irq(vpfe_dev); + videobuf_streamoff(&vpfe_dev->buffer_queue); + } + vpfe_dev->io_usrs = 0; + vpfe_dev->numbuffers = config_params.numbuffers; + } + + /* Decrement device usrs counter */ + vpfe_dev->usrs--; + /* Close the priority */ + v4l2_prio_close(&vpfe_dev->prio, &fh->prio); + /* If this is the last file handle */ + if (!vpfe_dev->usrs) { + vpfe_dev->initialized = 0; + if (ccdc_dev->hw_ops.close) + ccdc_dev->hw_ops.close(vpfe_dev->pdev); + module_put(ccdc_dev->owner); + } + mutex_unlock(&vpfe_dev->lock); + file->private_data = NULL; + /* Free memory allocated to file handle object */ + kfree(fh); + return 0; +} + +/* + * vpfe_mmap : It is used to map kernel space buffers + * into user spaces + */ +static int vpfe_mmap(struct file *file, struct vm_area_struct *vma) +{ + /* Get the device object and file handle object */ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n"); + + return videobuf_mmap_mapper(&vpfe_dev->buffer_queue, vma); +} + +/* + * vpfe_poll: It is used for select/poll system call + */ +static unsigned int vpfe_poll(struct file *file, poll_table *wait) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n"); + + if (vpfe_dev->started) + return videobuf_poll_stream(file, + &vpfe_dev->buffer_queue, wait); + return 0; +} + +/* vpfe capture driver file operations */ +static const struct v4l2_file_operations vpfe_fops = { + .owner = THIS_MODULE, + .open = vpfe_open, + .release = vpfe_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpfe_mmap, + .poll = vpfe_poll +}; + +/* + * vpfe_check_format() + * This function adjust the input pixel format as per hardware + * capabilities and update the same in pixfmt. + * Following algorithm used :- + * + * If given pixformat is not in the vpfe list of pix formats or not + * supported by the hardware, current value of pixformat in the device + * is used + * If given field is not supported, then current field is used. If field + * is different from current, then it is matched with that from sub device. + * Minimum height is 2 lines for interlaced or tb field and 1 line for + * progressive. Maximum height is clamped to active active lines of scan + * Minimum width is 32 bytes in memory and width is clamped to active + * pixels of scan. + * bytesperline is a multiple of 32. + */ +static const struct vpfe_pixel_format * + vpfe_check_format(struct vpfe_device *vpfe_dev, + struct v4l2_pix_format *pixfmt) +{ + u32 min_height = 1, min_width = 32, max_width, max_height; + const struct vpfe_pixel_format *vpfe_pix_fmt; + u32 pix; + int temp, found; + + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + if (NULL == vpfe_pix_fmt) { + /* + * use current pixel format in the vpfe device. We + * will find this pix format in the table + */ + pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + } + + /* check if hw supports it */ + temp = 0; + found = 0; + while (ccdc_dev->hw_ops.enum_pix(&pix, temp) >= 0) { + if (vpfe_pix_fmt->fmtdesc.pixelformat == pix) { + found = 1; + break; + } + temp++; + } + + if (!found) { + /* use current pixel format */ + pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; + /* + * Since this is currently used in the vpfe device, we + * will find this pix format in the table + */ + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + } + + /* check what field format is supported */ + if (pixfmt->field == V4L2_FIELD_ANY) { + /* if field is any, use current value as default */ + pixfmt->field = vpfe_dev->fmt.fmt.pix.field; + } + + /* + * if field is not same as current field in the vpfe device + * try matching the field with the sub device field + */ + if (vpfe_dev->fmt.fmt.pix.field != pixfmt->field) { + /* + * If field value is not in the supported fields, use current + * field used in the device as default + */ + switch (pixfmt->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_SEQ_TB: + /* if sub device is supporting progressive, use that */ + if (!vpfe_dev->std_info.frame_format) + pixfmt->field = V4L2_FIELD_NONE; + break; + case V4L2_FIELD_NONE: + if (vpfe_dev->std_info.frame_format) + pixfmt->field = V4L2_FIELD_INTERLACED; + break; + + default: + /* use current field as default */ + pixfmt->field = vpfe_dev->fmt.fmt.pix.field; + break; + } + } + + /* Now adjust image resolutions supported */ + if (pixfmt->field == V4L2_FIELD_INTERLACED || + pixfmt->field == V4L2_FIELD_SEQ_TB) + min_height = 2; + + max_width = vpfe_dev->std_info.active_pixels; + max_height = vpfe_dev->std_info.active_lines; + min_width /= vpfe_pix_fmt->bpp; + + v4l2_info(&vpfe_dev->v4l2_dev, "width = %d, height = %d, bpp = %d\n", + pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp); + + pixfmt->width = clamp((pixfmt->width), min_width, max_width); + pixfmt->height = clamp((pixfmt->height), min_height, max_height); + + /* If interlaced, adjust height to be a multiple of 2 */ + if (pixfmt->field == V4L2_FIELD_INTERLACED) + pixfmt->height &= (~1); + /* + * recalculate bytesperline and sizeimage since width + * and height might have changed + */ + pixfmt->bytesperline = (((pixfmt->width * vpfe_pix_fmt->bpp) + 31) + & ~31); + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + pixfmt->sizeimage = + pixfmt->bytesperline * pixfmt->height + + ((pixfmt->bytesperline * pixfmt->height) >> 1); + else + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + + v4l2_info(&vpfe_dev->v4l2_dev, "adjusted width = %d, height =" + " %d, bpp = %d, bytesperline = %d, sizeimage = %d\n", + pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp, + pixfmt->bytesperline, pixfmt->sizeimage); + return vpfe_pix_fmt; +} + +static int vpfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); + + cap->version = VPFE_CAPTURE_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); + strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); + return 0; +} + +static int vpfe_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt_vid_cap\n"); + /* Fill in the information about format */ + *fmt = vpfe_dev->fmt; + return ret; +} + +static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmt; + int temp_index; + u32 pix; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt_vid_cap\n"); + + if (ccdc_dev->hw_ops.enum_pix(&pix, fmt->index) < 0) + return -EINVAL; + + /* Fill in the information about format */ + pix_fmt = vpfe_lookup_pix_format(pix); + if (NULL != pix_fmt) { + temp_index = fmt->index; + *fmt = pix_fmt->fmtdesc; + fmt->index = temp_index; + return 0; + } + return -EINVAL; +} + +static int vpfe_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmts; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt_vid_cap\n"); + + /* If streaming is started, return error */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + + /* Check for valid frame format */ + pix_fmts = vpfe_check_format(vpfe_dev, &fmt->fmt.pix); + + if (NULL == pix_fmts) + return -EINVAL; + + /* store the pixel format in the device object */ + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + /* First detach any IRQ if currently attached */ + vpfe_detach_irq(vpfe_dev); + vpfe_dev->fmt = *fmt; + /* set image capture parameters in the ccdc */ + ret = vpfe_config_ccdc_image_format(vpfe_dev); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmts; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt_vid_cap\n"); + + pix_fmts = vpfe_check_format(vpfe_dev, &f->fmt.pix); + if (NULL == pix_fmts) + return -EINVAL; + return 0; +} + +/* + * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a + * given app input index + */ +static int vpfe_get_subdev_input_index(struct vpfe_device *vpfe_dev, + int *subdev_index, + int *subdev_input_index, + int app_input_index) +{ + struct vpfe_config *cfg = vpfe_dev->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < cfg->num_subdevs; i++) { + sdinfo = &cfg->sub_devs[i]; + if (app_input_index < (j + sdinfo->num_inputs)) { + *subdev_index = i; + *subdev_input_index = app_input_index - j; + return 0; + } + j += sdinfo->num_inputs; + } + return -EINVAL; +} + +/* + * vpfe_get_app_input - Get app input index for a given subdev input index + * driver stores the input index of the current sub device and translate it + * when application request the current input + */ +static int vpfe_get_app_input_index(struct vpfe_device *vpfe_dev, + int *app_input_index) +{ + struct vpfe_config *cfg = vpfe_dev->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < cfg->num_subdevs; i++) { + sdinfo = &cfg->sub_devs[i]; + if (!strcmp(sdinfo->name, vpfe_dev->current_subdev->name)) { + if (vpfe_dev->current_input >= sdinfo->num_inputs) + return -1; + *app_input_index = j + vpfe_dev->current_input; + return 0; + } + j += sdinfo->num_inputs; + } + return -EINVAL; +} + +static int vpfe_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int subdev, index ; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n"); + + if (vpfe_get_subdev_input_index(vpfe_dev, + &subdev, + &index, + inp->index) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "input information not found" + " for the subdev\n"); + return -EINVAL; + } + sdinfo = &vpfe_dev->cfg->sub_devs[subdev]; + memcpy(inp, &sdinfo->inputs[index], sizeof(struct v4l2_input)); + return 0; +} + +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n"); + + return vpfe_get_app_input_index(vpfe_dev, index); +} + + +static int vpfe_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int subdev_index, inp_index; + struct vpfe_route *route; + u32 input = 0, output = 0; + int ret = -EINVAL; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + /* + * If streaming is started return device busy + * error + */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n"); + ret = -EBUSY; + goto unlock_out; + } + + if (vpfe_get_subdev_input_index(vpfe_dev, + &subdev_index, + &inp_index, + index) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "invalid input index\n"); + goto unlock_out; + } + + sdinfo = &vpfe_dev->cfg->sub_devs[subdev_index]; + route = &sdinfo->routes[inp_index]; + if (route && sdinfo->can_route) { + input = route->input; + output = route->output; + } + + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_routing, input, output, 0); + + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, + "vpfe_doioctl:error in setting input in decoder\n"); + ret = -EINVAL; + goto unlock_out; + } + vpfe_dev->current_subdev = sdinfo; + vpfe_dev->current_input = index; + vpfe_dev->std_index = 0; + + /* set the bus/interface parameter for the sub device in ccdc */ + ret = ccdc_dev->hw_ops.set_hw_if_params(&sdinfo->ccdc_if_params); + if (ret) + goto unlock_out; + + /* set the default image parameters in the device */ + ret = vpfe_config_image_format(vpfe_dev, + &vpfe_standards[vpfe_dev->std_index].std_id); +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n"); + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + sdinfo = vpfe_dev->current_subdev; + if (ret) + return ret; + /* Call querystd function of decoder device */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, querystd, std_id); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); + + /* Call decoder driver function to set the standard */ + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + sdinfo = vpfe_dev->current_subdev; + /* If streaming is started, return device busy error */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n"); + ret = -EBUSY; + goto unlock_out; + } + + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, s_std, *std_id); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); + goto unlock_out; + } + ret = vpfe_config_image_format(vpfe_dev, std_id); + +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n"); + + *std_id = vpfe_standards[vpfe_dev->std_index].std_id; + return 0; +} +/* + * Videobuf operations + */ +static int vpfe_videobuf_setup(struct videobuf_queue *vq, + unsigned int *count, + unsigned int *size) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n"); + *size = config_params.device_bufsize; + + if (*count < config_params.min_numbuffers) + *count = config_params.min_numbuffers; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "count=%d, size=%d\n", *count, *size); + return 0; +} + +static int vpfe_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); + + /* If buffer is not initialized, initialize it */ + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = vpfe_dev->fmt.fmt.pix.width; + vb->height = vpfe_dev->fmt.fmt.pix.height; + vb->size = vpfe_dev->fmt.fmt.pix.sizeimage; + vb->field = field; + } + vb->state = VIDEOBUF_PREPARED; + return 0; +} + +static void vpfe_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and device object */ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue\n"); + + /* add the buffer to the DMA queue */ + spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); + list_add_tail(&vb->queue, &vpfe_dev->dma_queue); + spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); + + /* Change state of the buffer */ + vb->state = VIDEOBUF_QUEUED; +} + +static void vpfe_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_release\n"); + + /* + * We need to flush the buffer from the dma queue since + * they are de-allocated + */ + spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); + INIT_LIST_HEAD(&vpfe_dev->dma_queue); + spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops vpfe_videobuf_qops = { + .buf_setup = vpfe_videobuf_setup, + .buf_prepare = vpfe_videobuf_prepare, + .buf_queue = vpfe_videobuf_queue, + .buf_release = vpfe_videobuf_release, +}; + +/* + * vpfe_reqbufs. currently support REQBUF only once opening + * the device. + */ +static int vpfe_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + if (V4L2_MEMORY_USERPTR == req_buf->memory) { + /* we don't support user ptr IO */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs:" + " USERPTR IO not supported\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + if (vpfe_dev->io_usrs != 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); + ret = -EBUSY; + goto unlock_out; + } + + vpfe_dev->memory = req_buf->memory; + videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue, + &vpfe_videobuf_qops, + NULL, + &vpfe_dev->irqlock, + req_buf->type, + vpfe_dev->fmt.fmt.pix.field, + sizeof(struct videobuf_buffer), + fh); + + fh->io_allowed = 1; + vpfe_dev->io_usrs = 1; + INIT_LIST_HEAD(&vpfe_dev->dma_queue); + ret = videobuf_reqbufs(&vpfe_dev->buffer_queue, req_buf); +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (vpfe_dev->memory != V4L2_MEMORY_MMAP) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n"); + return -EINVAL; + } + /* Call videobuf_querybuf to get information */ + return videobuf_querybuf(&vpfe_dev->buffer_queue, buf); +} + +static int vpfe_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* + * If this file handle is not allowed to do IO, + * return error + */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + return videobuf_qbuf(&vpfe_dev->buffer_queue, p); +} + +static int vpfe_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + return videobuf_dqbuf(&vpfe_dev->buffer_queue, + buf, file->f_flags & O_NONBLOCK); +} + +/* + * vpfe_calculate_offsets : This function calculates buffers offset + * for top and bottom field + */ +static void vpfe_calculate_offsets(struct vpfe_device *vpfe_dev) +{ + struct v4l2_rect image_win; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_calculate_offsets\n"); + + ccdc_dev->hw_ops.get_image_window(&image_win); + vpfe_dev->field_off = image_win.height * image_win.width; +} + +/* vpfe_start_ccdc_capture: start streaming in ccdc/isif */ +static void vpfe_start_ccdc_capture(struct vpfe_device *vpfe_dev) +{ + ccdc_dev->hw_ops.enable(1); + if (ccdc_dev->hw_ops.enable_out_to_sdram) + ccdc_dev->hw_ops.enable_out_to_sdram(1); + vpfe_dev->started = 1; +} + +/* + * vpfe_streamon. Assume the DMA queue is not empty. + * application is expected to call QBUF before calling + * this ioctl. If not, driver returns error + */ +static int vpfe_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + unsigned long addr; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_stream, 1); + + if (ret && (ret != -ENOIOCTLCMD)) { + v4l2_err(&vpfe_dev->v4l2_dev, "stream on failed in subdev\n"); + return -EINVAL; + } + + /* If buffer queue is empty, return error */ + if (list_empty(&vpfe_dev->buffer_queue.stream)) { + v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n"); + return -EIO; + } + + /* Call videobuf_streamon to start streaming * in videobuf */ + ret = videobuf_streamon(&vpfe_dev->buffer_queue); + if (ret) + return ret; + + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + goto streamoff; + /* Get the next frame from the buffer queue */ + vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, + struct videobuf_buffer, queue); + vpfe_dev->cur_frm = vpfe_dev->next_frm; + /* Remove buffer from the buffer queue */ + list_del(&vpfe_dev->cur_frm->queue); + /* Mark state of the current frame to active */ + vpfe_dev->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + vpfe_dev->field_id = 0; + addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); + + /* Calculate field offset */ + vpfe_calculate_offsets(vpfe_dev); + + if (vpfe_attach_irq(vpfe_dev) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in attaching interrupt handle\n"); + ret = -EFAULT; + goto unlock_out; + } + if (ccdc_dev->hw_ops.configure() < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in configuring ccdc\n"); + ret = -EINVAL; + goto unlock_out; + } + ccdc_dev->hw_ops.setfbaddr((unsigned long)(addr)); + vpfe_start_ccdc_capture(vpfe_dev); + mutex_unlock(&vpfe_dev->lock); + return ret; +unlock_out: + mutex_unlock(&vpfe_dev->lock); +streamoff: + ret = videobuf_streamoff(&vpfe_dev->buffer_queue); + return ret; +} + +static int vpfe_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "device started\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + vpfe_stop_ccdc_capture(vpfe_dev); + vpfe_detach_irq(vpfe_dev); + + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_stream, 0); + + if (ret && (ret != -ENOIOCTLCMD)) + v4l2_err(&vpfe_dev->v4l2_dev, "stream off failed in subdev\n"); + ret = videobuf_streamoff(&vpfe_dev->buffer_queue); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *crop) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n"); + + if (vpfe_dev->std_index > ARRAY_SIZE(vpfe_standards)) + return -EINVAL; + + memset(crop, 0, sizeof(struct v4l2_cropcap)); + crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop->bounds.width = crop->defrect.width = + vpfe_standards[vpfe_dev->std_index].width; + crop->bounds.height = crop->defrect.height = + vpfe_standards[vpfe_dev->std_index].height; + crop->pixelaspect = vpfe_standards[vpfe_dev->std_index].pixelaspect; + return 0; +} + +static int vpfe_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_crop\n"); + + crop->c = vpfe_dev->crop; + return 0; +} + +static int vpfe_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_crop\n"); + + if (vpfe_dev->started) { + /* make sure streaming is not started */ + v4l2_err(&vpfe_dev->v4l2_dev, + "Cannot change crop when streaming is ON\n"); + return -EBUSY; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + if (crop->c.top < 0 || crop->c.left < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "doesn't support negative values for top & left\n"); + ret = -EINVAL; + goto unlock_out; + } + + /* adjust the width to 16 pixel boundry */ + crop->c.width = ((crop->c.width + 15) & ~0xf); + + /* make sure parameters are valid */ + if ((crop->c.left + crop->c.width > + vpfe_dev->std_info.active_pixels) || + (crop->c.top + crop->c.height > + vpfe_dev->std_info.active_lines)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_CROP params\n"); + ret = -EINVAL; + goto unlock_out; + } + ccdc_dev->hw_ops.set_image_window(&crop->c); + vpfe_dev->fmt.fmt.pix.width = crop->c.width; + vpfe_dev->fmt.fmt.pix.height = crop->c.height; + vpfe_dev->fmt.fmt.pix.bytesperline = + ccdc_dev->hw_ops.get_line_length(); + vpfe_dev->fmt.fmt.pix.sizeimage = + vpfe_dev->fmt.fmt.pix.bytesperline * + vpfe_dev->fmt.fmt.pix.height; + vpfe_dev->crop = crop->c; +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + + +static long vpfe_param_handler(struct file *file, void *priv, + int cmd, void *param) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_param_handler\n"); + + if (vpfe_dev->started) { + /* only allowed if streaming is not started */ + v4l2_err(&vpfe_dev->v4l2_dev, "device already started\n"); + return -EBUSY; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + switch (cmd) { + case VPFE_CMD_S_CCDC_RAW_PARAMS: + v4l2_warn(&vpfe_dev->v4l2_dev, + "VPFE_CMD_S_CCDC_RAW_PARAMS: experimental ioctl\n"); + ret = ccdc_dev->hw_ops.set_params(param); + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in setting parameters in CCDC\n"); + goto unlock_out; + } + if (vpfe_get_ccdc_image_format(vpfe_dev, &vpfe_dev->fmt) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Invalid image format at CCDC\n"); + goto unlock_out; + } + break; + default: + ret = -EINVAL; + } +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + + +/* vpfe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { + .vidioc_querycap = vpfe_querycap, + .vidioc_g_fmt_vid_cap = vpfe_g_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vpfe_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vpfe_try_fmt_vid_cap, + .vidioc_enum_input = vpfe_enum_input, + .vidioc_g_input = vpfe_g_input, + .vidioc_s_input = vpfe_s_input, + .vidioc_querystd = vpfe_querystd, + .vidioc_s_std = vpfe_s_std, + .vidioc_g_std = vpfe_g_std, + .vidioc_reqbufs = vpfe_reqbufs, + .vidioc_querybuf = vpfe_querybuf, + .vidioc_qbuf = vpfe_qbuf, + .vidioc_dqbuf = vpfe_dqbuf, + .vidioc_streamon = vpfe_streamon, + .vidioc_streamoff = vpfe_streamoff, + .vidioc_cropcap = vpfe_cropcap, + .vidioc_g_crop = vpfe_g_crop, + .vidioc_s_crop = vpfe_s_crop, + .vidioc_default = vpfe_param_handler, +}; + +static struct vpfe_device *vpfe_initialize(void) +{ + struct vpfe_device *vpfe_dev; + + /* Default number of buffers should be 3 */ + if ((numbuffers > 0) && + (numbuffers < config_params.min_numbuffers)) + numbuffers = config_params.min_numbuffers; + + /* + * Set buffer size to min buffers size if invalid buffer size is + * given + */ + if (bufsize < config_params.min_bufsize) + bufsize = config_params.min_bufsize; + + config_params.numbuffers = numbuffers; + + if (numbuffers) + config_params.device_bufsize = bufsize; + + /* Allocate memory for device objects */ + vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL); + + return vpfe_dev; +} + +static void vpfe_disable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + + clk_disable(vpfe_cfg->vpssclk); + clk_put(vpfe_cfg->vpssclk); + clk_disable(vpfe_cfg->slaveclk); + clk_put(vpfe_cfg->slaveclk); + v4l2_info(vpfe_dev->pdev->driver, + "vpfe vpss master & slave clocks disabled\n"); +} + +static int vpfe_enable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int ret = -ENOENT; + + vpfe_cfg->vpssclk = clk_get(vpfe_dev->pdev, "vpss_master"); + if (NULL == vpfe_cfg->vpssclk) { + v4l2_err(vpfe_dev->pdev->driver, "No clock defined for" + "vpss_master\n"); + return ret; + } + + if (clk_enable(vpfe_cfg->vpssclk)) { + v4l2_err(vpfe_dev->pdev->driver, + "vpfe vpss master clock not enabled\n"); + goto out; + } + v4l2_info(vpfe_dev->pdev->driver, + "vpfe vpss master clock enabled\n"); + + vpfe_cfg->slaveclk = clk_get(vpfe_dev->pdev, "vpss_slave"); + if (NULL == vpfe_cfg->slaveclk) { + v4l2_err(vpfe_dev->pdev->driver, + "No clock defined for vpss slave\n"); + goto out; + } + + if (clk_enable(vpfe_cfg->slaveclk)) { + v4l2_err(vpfe_dev->pdev->driver, + "vpfe vpss slave clock not enabled\n"); + goto out; + } + v4l2_info(vpfe_dev->pdev->driver, "vpfe vpss slave clock enabled\n"); + return 0; +out: + if (vpfe_cfg->vpssclk) + clk_put(vpfe_cfg->vpssclk); + if (vpfe_cfg->slaveclk) + clk_put(vpfe_cfg->slaveclk); + + return -1; +} + +/* + * vpfe_probe : This function creates device entries by register + * itself to the V4L2 driver and initializes fields of each + * device objects + */ +static __init int vpfe_probe(struct platform_device *pdev) +{ + struct vpfe_subdev_info *sdinfo; + struct vpfe_config *vpfe_cfg; + struct resource *res1; + struct vpfe_device *vpfe_dev; + struct i2c_adapter *i2c_adap; + struct video_device *vfd; + int ret = -ENOMEM, i, j; + int num_subdevs = 0; + + /* Get the pointer to the device object */ + vpfe_dev = vpfe_initialize(); + + if (!vpfe_dev) { + v4l2_err(pdev->dev.driver, + "Failed to allocate memory for vpfe_dev\n"); + return ret; + } + + vpfe_dev->pdev = &pdev->dev; + + if (NULL == pdev->dev.platform_data) { + v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + vpfe_cfg = pdev->dev.platform_data; + vpfe_dev->cfg = vpfe_cfg; + if (NULL == vpfe_cfg->ccdc || + NULL == vpfe_cfg->card_name || + NULL == vpfe_cfg->sub_devs) { + v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + /* enable vpss clocks */ + ret = vpfe_enable_clock(vpfe_dev); + if (ret) + goto probe_free_dev_mem; + + mutex_lock(&ccdc_lock); + /* Allocate memory for ccdc configuration */ + ccdc_cfg = kmalloc(sizeof(struct ccdc_config), GFP_KERNEL); + if (NULL == ccdc_cfg) { + v4l2_err(pdev->dev.driver, + "Memory allocation failed for ccdc_cfg\n"); + goto probe_disable_clock; + } + + strncpy(ccdc_cfg->name, vpfe_cfg->ccdc, 32); + /* Get VINT0 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT0\n"); + ret = -ENOENT; + goto probe_disable_clock; + } + vpfe_dev->ccdc_irq0 = res1->start; + + /* Get VINT1 irq resource */ + res1 = platform_get_resource(pdev, + IORESOURCE_IRQ, 1); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT1\n"); + ret = -ENOENT; + goto probe_disable_clock; + } + vpfe_dev->ccdc_irq1 = res1->start; + + /* Get address base of CCDC */ + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get register address map\n"); + ret = -ENOENT; + goto probe_disable_clock; + } + + ccdc_cfg->ccdc_addr_size = res1->end - res1->start + 1; + if (!request_mem_region(res1->start, ccdc_cfg->ccdc_addr_size, + pdev->dev.driver->name)) { + v4l2_err(pdev->dev.driver, + "Failed request_mem_region for ccdc base\n"); + ret = -ENXIO; + goto probe_disable_clock; + } + ccdc_cfg->ccdc_addr = ioremap_nocache(res1->start, + ccdc_cfg->ccdc_addr_size); + if (!ccdc_cfg->ccdc_addr) { + v4l2_err(pdev->dev.driver, "Unable to ioremap ccdc addr\n"); + ret = -ENXIO; + goto probe_out_release_mem1; + } + + ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED, + "vpfe_capture0", vpfe_dev); + + if (0 != ret) { + v4l2_err(pdev->dev.driver, "Unable to request interrupt\n"); + goto probe_out_unmap1; + } + + /* Allocate memory for video device */ + vfd = video_device_alloc(); + if (NULL == vfd) { + ret = -ENOMEM; + v4l2_err(pdev->dev.driver, + "Unable to alloc video device\n"); + goto probe_out_release_irq; + } + + /* Initialize field of video device */ + vfd->release = video_device_release; + vfd->fops = &vpfe_fops; + vfd->ioctl_ops = &vpfe_ioctl_ops; + vfd->minor = -1; + vfd->tvnorms = 0; + vfd->current_norm = V4L2_STD_PAL; + vfd->v4l2_dev = &vpfe_dev->v4l2_dev; + snprintf(vfd->name, sizeof(vfd->name), + "%s_V%d.%d.%d", + CAPTURE_DRV_NAME, + (VPFE_CAPTURE_VERSION_CODE >> 16) & 0xff, + (VPFE_CAPTURE_VERSION_CODE >> 8) & 0xff, + (VPFE_CAPTURE_VERSION_CODE) & 0xff); + /* Set video_dev to the video device */ + vpfe_dev->video_dev = vfd; + + ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register v4l2 device.\n"); + goto probe_out_video_release; + } + v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); + spin_lock_init(&vpfe_dev->irqlock); + spin_lock_init(&vpfe_dev->dma_queue_lock); + mutex_init(&vpfe_dev->lock); + + /* Initialize field of the device objects */ + vpfe_dev->numbuffers = config_params.numbuffers; + + /* Initialize prio member of device object */ + v4l2_prio_init(&vpfe_dev->prio); + /* register video device */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "trying to register vpfe device.\n"); + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "video_dev=%x\n", (int)&vpfe_dev->video_dev); + vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = video_register_device(vpfe_dev->video_dev, + VFL_TYPE_GRABBER, -1); + + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register video device.\n"); + goto probe_out_v4l2_unregister; + } + + v4l2_info(&vpfe_dev->v4l2_dev, "video device registered\n"); + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpfe_dev); + /* set driver private data */ + video_set_drvdata(vpfe_dev->video_dev, vpfe_dev); + i2c_adap = i2c_get_adapter(1); + vpfe_cfg = pdev->dev.platform_data; + num_subdevs = vpfe_cfg->num_subdevs; + vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs, + GFP_KERNEL); + if (NULL == vpfe_dev->sd) { + v4l2_err(&vpfe_dev->v4l2_dev, + "unable to allocate memory for subdevice pointers\n"); + ret = -ENOMEM; + goto probe_out_video_unregister; + } + + for (i = 0; i < num_subdevs; i++) { + struct v4l2_input *inps; + + sdinfo = &vpfe_cfg->sub_devs[i]; + + /* Load up the subdevice */ + vpfe_dev->sd[i] = + v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, + i2c_adap, + sdinfo->name, + &sdinfo->board_info, + NULL); + if (vpfe_dev->sd[i]) { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", + sdinfo->name); + vpfe_dev->sd[i]->grp_id = sdinfo->grp_id; + /* update tvnorms from the sub devices */ + for (j = 0; j < sdinfo->num_inputs; j++) { + inps = &sdinfo->inputs[j]; + vfd->tvnorms |= inps->std; + } + } else { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s register fails\n", + sdinfo->name); + goto probe_sd_out; + } + } + + /* set first sub device as current one */ + vpfe_dev->current_subdev = &vpfe_cfg->sub_devs[0]; + + /* We have at least one sub device to work with */ + mutex_unlock(&ccdc_lock); + return 0; + +probe_sd_out: + kfree(vpfe_dev->sd); +probe_out_video_unregister: + video_unregister_device(vpfe_dev->video_dev); +probe_out_v4l2_unregister: + v4l2_device_unregister(&vpfe_dev->v4l2_dev); +probe_out_video_release: + if (vpfe_dev->video_dev->minor == -1) + video_device_release(vpfe_dev->video_dev); +probe_out_release_irq: + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); +probe_out_unmap1: + iounmap(ccdc_cfg->ccdc_addr); +probe_out_release_mem1: + release_mem_region(res1->start, res1->end - res1->start + 1); +probe_disable_clock: + vpfe_disable_clock(vpfe_dev); + mutex_unlock(&ccdc_lock); + kfree(ccdc_cfg); +probe_free_dev_mem: + kfree(vpfe_dev); + return ret; +} + +/* + * vpfe_remove : It un-register device from V4L2 driver + */ +static int vpfe_remove(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); + struct resource *res; + + v4l2_info(pdev->dev.driver, "vpfe_remove\n"); + + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + kfree(vpfe_dev->sd); + v4l2_device_unregister(&vpfe_dev->v4l2_dev); + video_unregister_device(vpfe_dev->video_dev); + mutex_lock(&ccdc_lock); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res->end - res->start + 1); + iounmap(ccdc_cfg->ccdc_addr); + mutex_unlock(&ccdc_lock); + vpfe_disable_clock(vpfe_dev); + kfree(vpfe_dev); + kfree(ccdc_cfg); + return 0; +} + +static int +vpfe_suspend(struct device *dev) +{ + /* add suspend code here later */ + return -1; +} + +static int +vpfe_resume(struct device *dev) +{ + /* add resume code here later */ + return -1; +} + +static struct dev_pm_ops vpfe_dev_pm_ops = { + .suspend = vpfe_suspend, + .resume = vpfe_resume, +}; + +static struct platform_driver vpfe_driver = { + .driver = { + .name = CAPTURE_DRV_NAME, + .owner = THIS_MODULE, + .pm = &vpfe_dev_pm_ops, + }, + .probe = vpfe_probe, + .remove = __devexit_p(vpfe_remove), +}; + +static __init int vpfe_init(void) +{ + printk(KERN_NOTICE "vpfe_init\n"); + /* Register driver to the kernel */ + return platform_driver_register(&vpfe_driver); +} + +/* + * vpfe_cleanup : This function un-registers device driver + */ +static void vpfe_cleanup(void) +{ + platform_driver_unregister(&vpfe_driver); +} + +module_init(vpfe_init); +module_exit(vpfe_cleanup); diff --git a/linux/include/media/davinci/vpfe_capture.h b/linux/include/media/davinci/vpfe_capture.h new file mode 100644 index 000000000..71d8982e1 --- /dev/null +++ b/linux/include/media/davinci/vpfe_capture.h @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _VPFE_CAPTURE_H +#define _VPFE_CAPTURE_H + +#ifdef __KERNEL__ + +/* Header files */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define VPFE_CAPTURE_NUM_DECODERS 5 + +/* Macros */ +#define VPFE_MAJOR_RELEASE 0 +#define VPFE_MINOR_RELEASE 0 +#define VPFE_BUILD 1 +#define VPFE_CAPTURE_VERSION_CODE ((VPFE_MAJOR_RELEASE << 16) | \ + (VPFE_MINOR_RELEASE << 8) | \ + VPFE_BUILD) + +#define CAPTURE_DRV_NAME "vpfe-capture" + +struct vpfe_pixel_format { + struct v4l2_fmtdesc fmtdesc; + /* bytes per pixel */ + int bpp; +}; + +struct vpfe_std_info { + int active_pixels; + int active_lines; + /* current frame format */ + int frame_format; +}; + +struct vpfe_route { + u32 input; + u32 output; +}; + +struct vpfe_subdev_info { + /* Sub device name */ + char name[32]; + /* Sub device group id */ + int grp_id; + /* Number of inputs supported */ + int num_inputs; + /* inputs available at the sub device */ + struct v4l2_input *inputs; + /* Sub dev routing information for each input */ + struct vpfe_route *routes; + /* check if sub dev supports routing */ + int can_route; + /* ccdc bus/interface configuration */ + struct vpfe_hw_if_param ccdc_if_params; + /* i2c subdevice board info */ + struct i2c_board_info board_info; +}; + +struct vpfe_config { + /* Number of sub devices connected to vpfe */ + int num_subdevs; + /* information about each subdev */ + struct vpfe_subdev_info *sub_devs; + /* evm card info */ + char *card_name; + /* ccdc name */ + char *ccdc; + /* vpfe clock */ + struct clk *vpssclk; + struct clk *slaveclk; +}; + +struct vpfe_device { + /* V4l2 specific parameters */ + /* Identifies video device for this channel */ + struct video_device *video_dev; + /* sub devices */ + struct v4l2_subdev **sd; + /* vpfe cfg */ + struct vpfe_config *cfg; + /* V4l2 device */ + struct v4l2_device v4l2_dev; + /* parent device */ + struct device *pdev; + /* Used to keep track of state of the priority */ + struct v4l2_prio_state prio; + /* number of open instances of the channel */ + u32 usrs; + /* Indicates id of the field which is being displayed */ + u32 field_id; + /* flag to indicate whether decoder is initialized */ + u8 initialized; + /* current interface type */ + struct vpfe_hw_if_param vpfe_if_params; + /* ptr to currently selected sub device */ + struct vpfe_subdev_info *current_subdev; + /* current input at the sub device */ + int current_input; + /* Keeps track of the information about the standard */ + struct vpfe_std_info std_info; + /* std index into std table */ + int std_index; + /* CCDC IRQs used when CCDC/ISIF output to SDRAM */ + unsigned int ccdc_irq0; + unsigned int ccdc_irq1; + /* number of buffers in fbuffers */ + u32 numbuffers; + /* List of buffer pointers for storing frames */ + u8 *fbuffers[VIDEO_MAX_FRAME]; + /* Pointer pointing to current v4l2_buffer */ + struct videobuf_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct videobuf_buffer *next_frm; + /* + * This field keeps track of type of buffer exchange mechanism + * user has selected + */ + enum v4l2_memory memory; + /* Used to store pixel format */ + struct v4l2_format fmt; + /* + * used when IMP is chained to store the crop window which + * is different from the image window + */ + struct v4l2_rect crop; + /* Buffer queue used in video-buf */ + struct videobuf_queue buffer_queue; + /* Queue of filled frames */ + struct list_head dma_queue; + /* Used in video-buf */ + spinlock_t irqlock; + /* IRQ lock for DMA queue */ + spinlock_t dma_queue_lock; + /* lock used to access this structure */ + struct mutex lock; + /* number of users performing IO */ + u32 io_usrs; + /* Indicates whether streaming started */ + u8 started; + /* + * offset where second field starts from the starting of the + * buffer for field seperated YCbCr formats + */ + u32 field_off; +}; + +/* File handle structure */ +struct vpfe_fh { + struct vpfe_device *vpfe_dev; + /* Indicates whether this file handle is doing IO */ + u8 io_allowed; + /* Used to keep track priority of this instance */ + enum v4l2_priority prio; +}; + +struct vpfe_config_params { + u8 min_numbuffers; + u8 numbuffers; + u32 min_bufsize; + u32 device_bufsize; +}; + +#endif /* End of __KERNEL__ */ +/** + * VPFE_CMD_S_CCDC_RAW_PARAMS - EXPERIMENTAL IOCTL to set raw capture params + * This can be used to configure modules such as defect pixel correction, + * color space conversion, culling etc. This is an experimental ioctl that + * will change in future kernels. So use this ioctl with care ! + * TODO: This is to be split into multiple ioctls and also explore the + * possibility of extending the v4l2 api to include this + **/ +#define VPFE_CMD_S_CCDC_RAW_PARAMS _IOW('V', BASE_VIDIOC_PRIVATE + 1, \ + void *) +#endif /* _DAVINCI_VPFE_H */ diff --git a/linux/include/media/davinci/vpfe_types.h b/linux/include/media/davinci/vpfe_types.h new file mode 100644 index 000000000..76fb74bad --- /dev/null +++ b/linux/include/media/davinci/vpfe_types.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option)any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _VPFE_TYPES_H +#define _VPFE_TYPES_H + +#ifdef __KERNEL__ + +enum vpfe_pin_pol { + VPFE_PINPOL_POSITIVE, + VPFE_PINPOL_NEGATIVE +}; + +enum vpfe_hw_if_type { + /* BT656 - 8 bit */ + VPFE_BT656, + /* BT1120 - 16 bit */ + VPFE_BT1120, + /* Raw Bayer */ + VPFE_RAW_BAYER, + /* YCbCr - 8 bit with external sync */ + VPFE_YCBCR_SYNC_8, + /* YCbCr - 16 bit with external sync */ + VPFE_YCBCR_SYNC_16, + /* BT656 - 10 bit */ + VPFE_BT656_10BIT +}; + +/* interface description */ +struct vpfe_hw_if_param { + enum vpfe_hw_if_type if_type; + enum vpfe_pin_pol hdpol; + enum vpfe_pin_pol vdpol; +}; + +#endif +#endif -- cgit v1.2.3 From 03807cf31be76b6ca1ac32e44bbed5df8b2adef4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Jun 2009 14:14:10 +0200 Subject: v4l: ccdc hw device header file for vpfe capture From: Muralidharan Karicheri Adds ccdc hw device header for vpfe capture driver Reviewed by: Hans Verkuil Reviewed by: Laurent Pinchart Priority: normal Signed-off-by: Muralidharan Karicheri Signed-off-by: Hans Verkuil --- linux/drivers/media/video/davinci/ccdc_hw_device.h | 110 +++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 linux/drivers/media/video/davinci/ccdc_hw_device.h diff --git a/linux/drivers/media/video/davinci/ccdc_hw_device.h b/linux/drivers/media/video/davinci/ccdc_hw_device.h new file mode 100644 index 000000000..86b9b3518 --- /dev/null +++ b/linux/drivers/media/video/davinci/ccdc_hw_device.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ccdc device API + */ +#ifndef _CCDC_HW_DEVICE_H +#define _CCDC_HW_DEVICE_H + +#ifdef __KERNEL__ +#include +#include +#include +#include + +/* + * ccdc hw operations + */ +struct ccdc_hw_ops { + /* Pointer to initialize function to initialize ccdc device */ + int (*open) (struct device *dev); + /* Pointer to deinitialize function */ + int (*close) (struct device *dev); + /* set ccdc base address */ + void (*set_ccdc_base)(void *base, int size); + /* Pointer to function to enable or disable ccdc */ + void (*enable) (int en); + /* reset sbl. only for 6446 */ + void (*reset) (void); + /* enable output to sdram */ + void (*enable_out_to_sdram) (int en); + /* Pointer to function to set hw parameters */ + int (*set_hw_if_params) (struct vpfe_hw_if_param *param); + /* get interface parameters */ + int (*get_hw_if_params) (struct vpfe_hw_if_param *param); + /* + * Pointer to function to set parameters. Used + * for implementing VPFE_S_CCDC_PARAMS + */ + int (*set_params) (void *params); + /* + * Pointer to function to get parameter. Used + * for implementing VPFE_G_CCDC_PARAMS + */ + int (*get_params) (void *params); + /* Pointer to function to configure ccdc */ + int (*configure) (void); + + /* Pointer to function to set buffer type */ + int (*set_buftype) (enum ccdc_buftype buf_type); + /* Pointer to function to get buffer type */ + enum ccdc_buftype (*get_buftype) (void); + /* Pointer to function to set frame format */ + int (*set_frame_format) (enum ccdc_frmfmt frm_fmt); + /* Pointer to function to get frame format */ + enum ccdc_frmfmt (*get_frame_format) (void); + /* enumerate hw pix formats */ + int (*enum_pix)(u32 *hw_pix, int i); + /* Pointer to function to set buffer type */ + u32 (*get_pixel_format) (void); + /* Pointer to function to get pixel format. */ + int (*set_pixel_format) (u32 pixfmt); + /* Pointer to function to set image window */ + int (*set_image_window) (struct v4l2_rect *win); + /* Pointer to function to set image window */ + void (*get_image_window) (struct v4l2_rect *win); + /* Pointer to function to get line length */ + unsigned int (*get_line_length) (void); + + /* Query CCDC control IDs */ + int (*queryctrl)(struct v4l2_queryctrl *qctrl); + /* Set CCDC control */ + int (*set_control)(struct v4l2_control *ctrl); + /* Get CCDC control */ + int (*get_control)(struct v4l2_control *ctrl); + + /* Pointer to function to set frame buffer address */ + void (*setfbaddr) (unsigned long addr); + /* Pointer to function to get field id */ + int (*getfid) (void); +}; + +struct ccdc_hw_device { + /* ccdc device name */ + char name[32]; + /* module owner */ + struct module *owner; + /* hw ops */ + struct ccdc_hw_ops hw_ops; +}; + +/* Used by CCDC module to register & unregister with vpfe capture driver */ +int vpfe_register_ccdc_device(struct ccdc_hw_device *dev); +void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev); + +#endif +#endif -- cgit v1.2.3 From 91f3825a3a2bd6f368a38e2d00422dcab4d637fa Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 3 Jul 2009 10:23:07 +0200 Subject: v4l: dm355 ccdc module for vpfe capture driver From: Muralidharan Karicheri Adds ccdc hw module for DM355 CCDC. This registers with the bridge driver a set of hw_ops for configuring the CCDC for a specific decoder device connected to vpfe. Reviewed by: Hans Verkuil Reviewed by: Laurent Pinchart Reviewed by: Mauro Carvalho Chehab Priority: normal Signed-off-by: Muralidharan Karicheri Signed-off-by: Hans Verkuil --- linux/drivers/media/video/davinci/dm355_ccdc.c | 978 +++++++++++++++++++++ .../drivers/media/video/davinci/dm355_ccdc_regs.h | 310 +++++++ linux/include/media/davinci/dm355_ccdc.h | 321 +++++++ 3 files changed, 1609 insertions(+) create mode 100644 linux/drivers/media/video/davinci/dm355_ccdc.c create mode 100644 linux/drivers/media/video/davinci/dm355_ccdc_regs.h create mode 100644 linux/include/media/davinci/dm355_ccdc.h diff --git a/linux/drivers/media/video/davinci/dm355_ccdc.c b/linux/drivers/media/video/davinci/dm355_ccdc.c new file mode 100644 index 000000000..4629cabe3 --- /dev/null +++ b/linux/drivers/media/video/davinci/dm355_ccdc.c @@ -0,0 +1,978 @@ +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * CCDC hardware module for DM355 + * ------------------------------ + * + * This module is for configuring DM355 CCD controller of VPFE to capture + * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules + * such as Defect Pixel Correction, Color Space Conversion etc to + * pre-process the Bayer RGB data, before writing it to SDRAM. This + * module also allows application to configure individual + * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL. + * To do so, application include dm355_ccdc.h and vpfe_capture.h header + * files. The setparams() API is called by vpfe_capture driver + * to configure module parameters + * + * TODO: 1) Raw bayer parameter settings and bayer capture + * 2) Split module parameter structure to module specific ioctl structs + * 3) add support for lense shading correction + * 4) investigate if enum used for user space type definition + * to be replaced by #defines or integer + */ +#include +#include +#include +#include +#include +#include "dm355_ccdc_regs.h" +#include "ccdc_hw_device.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CCDC Driver for DM355"); +MODULE_AUTHOR("Texas Instruments"); + +static struct device *dev; + +/* Object for CCDC raw mode */ +static struct ccdc_params_raw ccdc_hw_params_raw = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .gain = { + .r_ye = 256, + .gb_g = 256, + .gr_cy = 256, + .b_mg = 256 + }, + .config_params = { + .datasft = 2, + .data_sz = CCDC_DATA_10BITS, + .mfilt1 = CCDC_NO_MEDIAN_FILTER1, + .mfilt2 = CCDC_NO_MEDIAN_FILTER2, + .alaw = { + .gama_wd = 2, + }, + .blk_clamp = { + .sample_pixel = 1, + .dc_sub = 25 + }, + .col_pat_field0 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED + }, + .col_pat_field1 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED + }, + }, +}; + + +/* Object for CCDC ycbcr mode */ +static struct ccdc_params_ycbcr ccdc_hw_params_ycbcr = { + .win = CCDC_WIN_PAL, + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .bt656_enable = 1, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED +}; + +static enum vpfe_hw_if_type ccdc_if_type; +static void *__iomem ccdc_base_addr; +static int ccdc_addr_size; + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = + {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static u32 ccdc_raw_yuv_pix_formats[] = + {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(ccdc_base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, ccdc_base_addr + offset); +} + +static void ccdc_set_ccdc_base(void *addr, int size) +{ + ccdc_base_addr = addr; + ccdc_addr_size = size; +} + +static void ccdc_enable(int en) +{ + unsigned int temp; + temp = regr(SYNCEN); + temp &= (~CCDC_SYNCEN_VDHDEN_MASK); + temp |= (en & CCDC_SYNCEN_VDHDEN_MASK); + regw(temp, SYNCEN); +} + +static void ccdc_enable_output_to_sdram(int en) +{ + unsigned int temp; + temp = regr(SYNCEN); + temp &= (~(CCDC_SYNCEN_WEN_MASK)); + temp |= ((en << CCDC_SYNCEN_WEN_SHIFT) & CCDC_SYNCEN_WEN_MASK); + regw(temp, SYNCEN); +} + +static void ccdc_config_gain_offset(void) +{ + /* configure gain */ + regw(ccdc_hw_params_raw.gain.r_ye, RYEGAIN); + regw(ccdc_hw_params_raw.gain.gr_cy, GRCYGAIN); + regw(ccdc_hw_params_raw.gain.gb_g, GBGGAIN); + regw(ccdc_hw_params_raw.gain.b_mg, BMGGAIN); + /* configure offset */ + regw(ccdc_hw_params_raw.ccdc_offset, OFFSET); +} + +/* + * ccdc_restore_defaults() + * This function restore power on defaults in the ccdc registers + */ +static int ccdc_restore_defaults(void) +{ + int i; + + dev_dbg(dev, "\nstarting ccdc_restore_defaults..."); + /* set all registers to zero */ + for (i = 0; i <= CCDC_REG_LAST; i += 4) + regw(0, i); + + /* now override the values with power on defaults in registers */ + regw(MODESET_DEFAULT, MODESET); + /* no culling support */ + regw(CULH_DEFAULT, CULH); + regw(CULV_DEFAULT, CULV); + /* Set default Gain and Offset */ + ccdc_hw_params_raw.gain.r_ye = GAIN_DEFAULT; + ccdc_hw_params_raw.gain.gb_g = GAIN_DEFAULT; + ccdc_hw_params_raw.gain.gr_cy = GAIN_DEFAULT; + ccdc_hw_params_raw.gain.b_mg = GAIN_DEFAULT; + ccdc_config_gain_offset(); + regw(OUTCLIP_DEFAULT, OUTCLIP); + regw(LSCCFG2_DEFAULT, LSCCFG2); + /* select ccdc input */ + if (vpss_select_ccdc_source(VPSS_CCDCIN)) { + dev_dbg(dev, "\ncouldn't select ccdc input source"); + return -EFAULT; + } + /* select ccdc clock */ + if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) { + dev_dbg(dev, "\ncouldn't enable ccdc clock"); + return -EFAULT; + } + dev_dbg(dev, "\nEnd of ccdc_restore_defaults..."); + return 0; +} + +static int ccdc_open(struct device *device) +{ + dev = device; + return ccdc_restore_defaults(); +} + +static int ccdc_close(struct device *device) +{ + /* disable clock */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 0); + /* do nothing for now */ + return 0; +} +/* + * ccdc_setwin() + * This function will configure the window size to + * be capture in CCDC reg. + */ +static void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int mid_img = 0; + + dev_dbg(dev, "\nStarting ccdc_setwin..."); + + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + regw(horz_start, SPH); + regw(horz_nr_pixels, NPH); + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* Since first line doesn't have any data */ + vert_start += 1; + /* configure VDINT0 and VDINT1 */ + regw(vert_start, VDINT0); + } else { + /* Since first line doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + regw(vert_start, VDINT0); + regw(mid_img, VDINT1); + } + regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0); + regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1); + regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV); + dev_dbg(dev, "\nEnd of ccdc_setwin..."); +} + +static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) +{ + if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT || + ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) { + dev_dbg(dev, "Invalid value of data shift\n"); + return -EINVAL; + } + + if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 || + ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) { + dev_dbg(dev, "Invalid value of median filter1\n"); + return -EINVAL; + } + + if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 || + ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) { + dev_dbg(dev, "Invalid value of median filter2\n"); + return -EINVAL; + } + + if ((ccdcparam->med_filt_thres < 0) || + (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) { + dev_dbg(dev, "Invalid value of median filter thresold\n"); + return -EINVAL; + } + + if (ccdcparam->data_sz < CCDC_DATA_16BITS || + ccdcparam->data_sz > CCDC_DATA_8BITS) { + dev_dbg(dev, "Invalid value of data size\n"); + return -EINVAL; + } + + if (ccdcparam->alaw.enable) { + if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 || + ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) { + dev_dbg(dev, "Invalid value of ALAW\n"); + return -EINVAL; + } + } + + if (ccdcparam->blk_clamp.b_clamp_enable) { + if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS || + ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) { + dev_dbg(dev, "Invalid value of sample pixel\n"); + return -EINVAL; + } + if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES || + ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) { + dev_dbg(dev, "Invalid value of sample lines\n"); + return -EINVAL; + } + } + return 0; +} + +/* Parameter operations */ +static int ccdc_set_params(void __user *params) +{ + struct ccdc_config_params_raw ccdc_raw_params; + int x; + + /* only raw module parameters can be set through the IOCTL */ + if (ccdc_if_type != VPFE_RAW_BAYER) + return -EINVAL; + + x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); + if (x) { + dev_dbg(dev, "ccdc_set_params: error in copying ccdc" + "params, %d\n", x); + return -EFAULT; + } + + if (!validate_ccdc_param(&ccdc_raw_params)) { + memcpy(&ccdc_hw_params_raw.config_params, + &ccdc_raw_params, + sizeof(ccdc_raw_params)); + return 0; + } + return -EINVAL; +} + +/* This function will configure CCDC for YCbCr video capture */ +static void ccdc_config_ycbcr(void) +{ + struct ccdc_params_ycbcr *params = &ccdc_hw_params_ycbcr; + u32 temp; + + /* first set the CCDC power on defaults values in all registers */ + dev_dbg(dev, "\nStarting ccdc_config_ycbcr..."); + ccdc_restore_defaults(); + + /* configure pixel format & video frame format */ + temp = (((params->pix_fmt & CCDC_INPUT_MODE_MASK) << + CCDC_INPUT_MODE_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << + CCDC_FRM_FMT_SHIFT)); + + /* setup BT.656 sync mode */ + if (params->bt656_enable) { + regw(CCDC_REC656IF_BT656_EN, REC656IF); + /* + * configure the FID, VD, HD pin polarity fld,hd pol positive, + * vd negative, 8-bit pack mode + */ + temp |= CCDC_VD_POL_NEGATIVE; + } else { /* y/c external sync mode */ + temp |= (((params->fid_pol & CCDC_FID_POL_MASK) << + CCDC_FID_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << + CCDC_HD_POL_SHIFT) | + ((params->vd_pol & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT)); + } + + /* pack the data to 8-bit */ + temp |= CCDC_DATA_PACK_ENABLE; + + regw(temp, MODESET); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 2); + + /* configure the order of y cb cr in SD-RAM */ + temp = (params->pix_order << CCDC_Y8POS_SHIFT); + temp |= CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC; + regw(temp, CCDCFG); + + /* + * configure the horizontal line offset. This is done by rounding up + * width to a multiple of 16 pixels and multiply by two to account for + * y:cb:cr 4:2:2 data + */ + regw(((params->win.width * 2 + 31) >> 5), HSIZE); + + /* configure the memory line offset */ + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) { + /* two fields are interleaved in memory */ + regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST); + } + + dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n"); +} + +/* + * ccdc_config_black_clamp() + * configure parameters for Optical Black Clamp + */ +static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) +{ + u32 val; + + if (!bclamp->b_clamp_enable) { + /* configure DCSub */ + regw(bclamp->dc_sub & CCDC_BLK_DC_SUB_MASK, DCSUB); + regw(0x0000, CLAMP); + return; + } + /* Enable the Black clamping, set sample lines and pixels */ + val = (bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) | + ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << + CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE; + regw(val, CLAMP); + + /* If Black clamping is enable then make dcsub 0 */ + val = (bclamp->sample_ln & CCDC_NUM_LINE_CALC_MASK) + << CCDC_NUM_LINE_CALC_SHIFT; + regw(val, DCSUB); +} + +/* + * ccdc_config_black_compense() + * configure parameters for Black Compensation + */ +static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) +{ + u32 val; + + val = (bcomp->b & CCDC_BLK_COMP_MASK) | + ((bcomp->gb & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GB_COMP_SHIFT); + regw(val, BLKCMP1); + + val = ((bcomp->gr & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GR_COMP_SHIFT) | + ((bcomp->r & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_R_COMP_SHIFT); + regw(val, BLKCMP0); +} + +/* + * ccdc_write_dfc_entry() + * write an entry in the dfc table. + */ +int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc) +{ +/* TODO This is to be re-visited and adjusted */ +#define DFC_WRITE_WAIT_COUNT 1000 + u32 val, count = DFC_WRITE_WAIT_COUNT; + + regw(dfc->dft_corr_vert[index], DFCMEM0); + regw(dfc->dft_corr_horz[index], DFCMEM1); + regw(dfc->dft_corr_sub1[index], DFCMEM2); + regw(dfc->dft_corr_sub2[index], DFCMEM3); + regw(dfc->dft_corr_sub3[index], DFCMEM4); + /* set WR bit to write */ + val = regr(DFCMEMCTL) | CCDC_DFCMEMCTL_DFCMWR_MASK; + regw(val, DFCMEMCTL); + + /* + * Assume, it is very short. If we get an error, we need to + * adjust this value + */ + while (regr(DFCMEMCTL) & CCDC_DFCMEMCTL_DFCMWR_MASK) + count--; + /* + * TODO We expect the count to be non-zero to be successful. Adjust + * the count if write requires more time + */ + + if (count) { + dev_err(dev, "defect table write timeout !!!\n"); + return -1; + } + return 0; +} + +/* + * ccdc_config_vdfc() + * configure parameters for Vertical Defect Correction + */ +static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc) +{ + u32 val; + int i; + + /* Configure General Defect Correction. The table used is from IPIPE */ + val = dfc->gen_dft_en & CCDC_DFCCTL_GDFCEN_MASK; + + /* Configure Vertical Defect Correction if needed */ + if (!dfc->ver_dft_en) { + /* Enable only General Defect Correction */ + regw(val, DFCCTL); + return 0; + } + + if (dfc->table_size > CCDC_DFT_TABLE_SIZE) + return -EINVAL; + + val |= CCDC_DFCCTL_VDFC_DISABLE; + val |= (dfc->dft_corr_ctl.vdfcsl & CCDC_DFCCTL_VDFCSL_MASK) << + CCDC_DFCCTL_VDFCSL_SHIFT; + val |= (dfc->dft_corr_ctl.vdfcuda & CCDC_DFCCTL_VDFCUDA_MASK) << + CCDC_DFCCTL_VDFCUDA_SHIFT; + val |= (dfc->dft_corr_ctl.vdflsft & CCDC_DFCCTL_VDFLSFT_MASK) << + CCDC_DFCCTL_VDFLSFT_SHIFT; + regw(val , DFCCTL); + + /* clear address ptr to offset 0 */ + val = CCDC_DFCMEMCTL_DFCMARST_MASK << CCDC_DFCMEMCTL_DFCMARST_SHIFT; + + /* write defect table entries */ + for (i = 0; i < dfc->table_size; i++) { + /* increment address for non zero index */ + if (i != 0) + val = CCDC_DFCMEMCTL_INC_ADDR; + regw(val, DFCMEMCTL); + if (ccdc_write_dfc_entry(i, dfc) < 0) + return -EFAULT; + } + + /* update saturation level and enable dfc */ + regw(dfc->saturation_ctl & CCDC_VDC_DFCVSAT_MASK, DFCVSAT); + val = regr(DFCCTL) | (CCDC_DFCCTL_VDFCEN_MASK << + CCDC_DFCCTL_VDFCEN_SHIFT); + regw(val, DFCCTL); + return 0; +} + +/* + * ccdc_config_csc() + * configure parameters for color space conversion + * Each register CSCM0-7 has two values in S8Q5 format. + */ +static void ccdc_config_csc(struct ccdc_csc *csc) +{ + u32 val1, val2; + int i; + + if (!csc->enable) + return; + + /* Enable the CSC sub-module */ + regw(CCDC_CSC_ENABLE, CSCCTL); + + /* Converting the co-eff as per the format of the register */ + for (i = 0; i < CCDC_CSC_COEFF_TABLE_SIZE; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = (csc->coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) + << CCDC_CSC_COEF_INTEG_SHIFT; + /* + * convert decimal part to binary. Use 2 decimal + * precision, user values range from .00 - 0.99 + */ + val1 |= (((csc->coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK) * + CCDC_CSC_DEC_MAX) / 100); + } else { + + /* CSCM - MSB */ + val2 = (csc->coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) + << CCDC_CSC_COEF_INTEG_SHIFT; + val2 |= (((csc->coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK) * + CCDC_CSC_DEC_MAX) / 100); + val2 <<= CCDC_CSCM_MSB_SHIFT; + val2 |= val1; + regw(val2, (CSCM0 + ((i - 1) << 1))); + } + } +} + +/* + * ccdc_config_color_patterns() + * configure parameters for color patterns + */ +static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0, + struct ccdc_col_pat *pat1) +{ + u32 val; + + val = (pat0->olop | (pat0->olep << 2) | (pat0->elop << 4) | + (pat0->elep << 6) | (pat1->olop << 8) | (pat1->olep << 10) | + (pat1->elop << 12) | (pat1->elep << 14)); + regw(val, COLPTN); +} + +/* This function will configure CCDC for Raw mode image capture */ +static int ccdc_config_raw(void) +{ + struct ccdc_params_raw *params = &ccdc_hw_params_raw; + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int val; + + dev_dbg(dev, "\nStarting ccdc_config_raw..."); + + /* restore power on defaults to register */ + ccdc_restore_defaults(); + + /* CCDCFG register: + * set CCD Not to swap input since input is RAW data + * set FID detection function to Latch at V-Sync + * set WENLOG - ccdc valid area to AND + * set TRGSEL to WENBIT + * set EXTRG to DISABLE + * disable latching function on VSYNC - shadowed registers + */ + regw(CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC | + CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN | + CCDC_CCDCFG_EXTRG_DISABLE | CCDC_LATCH_ON_VSYNC_DISABLE, CCDCFG); + + /* + * Set VDHD direction to input, input type to raw input + * normal data polarity, do not use external WEN + */ + val = (CCDC_VDHDOUT_INPUT | CCDC_RAW_IP_MODE | CCDC_DATAPOL_NORMAL | + CCDC_EXWEN_DISABLE); + + /* + * Configure the vertical sync polarity (MODESET.VDPOL), horizontal + * sync polarity (MODESET.HDPOL), field id polarity (MODESET.FLDPOL), + * frame format(progressive or interlace), & pixel format (Input mode) + */ + val |= (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | + ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | + ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT)); + + /* set pack for alaw compression */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + val |= CCDC_DATA_PACK_ENABLE; + + /* Configure for LPF */ + if (config_params->lpf_enable) + val |= (config_params->lpf_enable & CCDC_LPF_MASK) << + CCDC_LPF_SHIFT; + + /* Configure the data shift */ + val |= (config_params->datasft & CCDC_DATASFT_MASK) << + CCDC_DATASFT_SHIFT; + regw(val , MODESET); + dev_dbg(dev, "\nWriting 0x%x to MODESET...\n", val); + + /* Configure the Median Filter threshold */ + regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT); + + /* Configure GAMMAWD register. defaur 11-2, and Mosaic cfa pattern */ + val = CCDC_GAMMA_BITS_11_2 << CCDC_GAMMAWD_INPUT_SHIFT | + CCDC_CFA_MOSAIC; + + /* Enable and configure aLaw register if needed */ + if (config_params->alaw.enable) { + val |= (CCDC_ALAW_ENABLE | + ((config_params->alaw.gama_wd & + CCDC_ALAW_GAMA_WD_MASK) << + CCDC_GAMMAWD_INPUT_SHIFT)); + } + + /* Configure Median filter1 & filter2 */ + val |= ((config_params->mfilt1 << CCDC_MFILT1_SHIFT) | + (config_params->mfilt2 << CCDC_MFILT2_SHIFT)); + + regw(val, GAMMAWD); + dev_dbg(dev, "\nWriting 0x%x to GAMMAWD...\n", val); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 1); + + /* Optical Clamp Averaging */ + ccdc_config_black_clamp(&config_params->blk_clamp); + + /* Black level compensation */ + ccdc_config_black_compense(&config_params->blk_comp); + + /* Vertical Defect Correction if needed */ + if (ccdc_config_vdfc(&config_params->vertical_dft) < 0) + return -EFAULT; + + /* color space conversion */ + ccdc_config_csc(&config_params->csc); + + /* color pattern */ + ccdc_config_color_patterns(&config_params->col_pat_field0, + &config_params->col_pat_field1); + + /* Configure the Gain & offset control */ + ccdc_config_gain_offset(); + + dev_dbg(dev, "\nWriting %x to COLPTN...\n", val); + + /* Configure DATAOFST register */ + val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) << + CCDC_DATAOFST_H_SHIFT; + val |= (config_params->data_offset.vert_offset & CCDC_DATAOFST_MASK) << + CCDC_DATAOFST_V_SHIFT; + regw(val, DATAOFST); + + /* configuring HSIZE register */ + val = (params->horz_flip_enable & CCDC_HSIZE_FLIP_MASK) << + CCDC_HSIZE_FLIP_SHIFT; + + /* If pack 8 is enable then 1 pixel will take 1 byte */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) { + val |= (((params->win.width) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK; + + /* adjust to multiple of 32 */ + dev_dbg(dev, "\nWriting 0x%x to HSIZE...\n", + (((params->win.width) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK); + } else { + /* else one pixel will take 2 byte */ + val |= (((params->win.width * 2) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK; + + dev_dbg(dev, "\nWriting 0x%x to HSIZE...\n", + (((params->win.width * 2) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK); + } + regw(val, HSIZE); + + /* Configure SDOFST register */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_enable) { + /* For interlace inverse mode */ + regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_INTERLACE_INVERSE); + } else { + /* For interlace non inverse mode */ + regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_INTERLACE_NORMAL); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + if (params->image_invert_enable) { + /* For progessive inverse mode */ + regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_PROGRESSIVE_INVERSE); + } else { + /* For progessive non inverse mode */ + regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_PROGRESSIVE_NORMAL); + } + } + dev_dbg(dev, "\nend of ccdc_config_raw..."); + return 0; +} + +static int ccdc_configure(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_config_raw(); + else + ccdc_config_ycbcr(); + return 0; +} + +static int ccdc_set_buftype(enum ccdc_buftype buf_type) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.buf_type = buf_type; + else + ccdc_hw_params_ycbcr.buf_type = buf_type; + return 0; +} +static enum ccdc_buftype ccdc_get_buftype(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.buf_type; + return ccdc_hw_params_ycbcr.buf_type; +} + +static int ccdc_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + if (ccdc_if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { + *pix = ccdc_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { + *pix = ccdc_raw_yuv_pix_formats[i]; + ret = 0; + } + } + return ret; +} + +static int ccdc_set_pixel_format(u32 pixfmt) +{ + struct ccdc_a_law *alaw = + &ccdc_hw_params_raw.config_params.alaw; + + if (ccdc_if_type == VPFE_RAW_BAYER) { + ccdc_hw_params_raw.pix_fmt = CCDC_PIXFMT_RAW; + if (pixfmt == V4L2_PIX_FMT_SBGGR8) + alaw->enable = 1; + else if (pixfmt != V4L2_PIX_FMT_SBGGR16) + return -EINVAL; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + } + return 0; +} +static u32 ccdc_get_pixel_format(void) +{ + struct ccdc_a_law *alaw = + &ccdc_hw_params_raw.config_params.alaw; + u32 pixfmt; + + if (ccdc_if_type == VPFE_RAW_BAYER) + if (alaw->enable) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (ccdc_hw_params_ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} +static int ccdc_set_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.win = *win; + else + ccdc_hw_params_ycbcr.win = *win; + return 0; +} + +static void ccdc_get_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + *win = ccdc_hw_params_raw.win; + else + *win = ccdc_hw_params_ycbcr.win; +} + +static unsigned int ccdc_get_line_length(void) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int len; + + if (ccdc_if_type == VPFE_RAW_BAYER) { + if ((config_params->alaw.enable) || + (config_params->data_sz == CCDC_DATA_8BITS)) + len = ccdc_hw_params_raw.win.width; + else + len = ccdc_hw_params_raw.win.width * 2; + } else + len = ccdc_hw_params_ycbcr.win.width * 2; + return ALIGN(len, 32); +} + +static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.frm_fmt = frm_fmt; + else + ccdc_hw_params_ycbcr.frm_fmt = frm_fmt; + return 0; +} + +static enum ccdc_frmfmt ccdc_get_frame_format(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.frm_fmt; + else + return ccdc_hw_params_ycbcr.frm_fmt; +} + +static int ccdc_getfid(void) +{ + return (regr(MODESET) >> 15) & 1; +} + +/* misc operations */ +static inline void ccdc_setfbaddr(unsigned long addr) +{ + regw((addr >> 21) & 0x007f, STADRH); + regw((addr >> 5) & 0x0ffff, STADRL); +} + +static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + ccdc_if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_YCBCR_SYNC_16: + case VPFE_YCBCR_SYNC_8: + ccdc_hw_params_ycbcr.vd_pol = params->vdpol; + ccdc_hw_params_ycbcr.hd_pol = params->hdpol; + break; + default: + /* TODO add support for raw bayer here */ + return -EINVAL; + } + return 0; +} + +static struct ccdc_hw_device ccdc_hw_dev = { + .name = "DM355 CCDC", + .owner = THIS_MODULE, + .hw_ops = { + .open = ccdc_open, + .close = ccdc_close, + .set_ccdc_base = ccdc_set_ccdc_base, + .enable = ccdc_enable, + .enable_out_to_sdram = ccdc_enable_output_to_sdram, + .set_hw_if_params = ccdc_set_hw_if_params, + .set_params = ccdc_set_params, + .configure = ccdc_configure, + .set_buftype = ccdc_set_buftype, + .get_buftype = ccdc_get_buftype, + .enum_pix = ccdc_enum_pix, + .set_pixel_format = ccdc_set_pixel_format, + .get_pixel_format = ccdc_get_pixel_format, + .set_frame_format = ccdc_set_frame_format, + .get_frame_format = ccdc_get_frame_format, + .set_image_window = ccdc_set_image_window, + .get_image_window = ccdc_get_image_window, + .get_line_length = ccdc_get_line_length, + .setfbaddr = ccdc_setfbaddr, + .getfid = ccdc_getfid, + }, +}; + +static int dm355_ccdc_init(void) +{ + printk(KERN_NOTICE "dm355_ccdc_init\n"); + if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0) + return -1; + printk(KERN_NOTICE "%s is registered with vpfe.\n", + ccdc_hw_dev.name); + return 0; +} + +static void dm355_ccdc_exit(void) +{ + vpfe_unregister_ccdc_device(&ccdc_hw_dev); +} + +module_init(dm355_ccdc_init); +module_exit(dm355_ccdc_exit); diff --git a/linux/drivers/media/video/davinci/dm355_ccdc_regs.h b/linux/drivers/media/video/davinci/dm355_ccdc_regs.h new file mode 100644 index 000000000..d6d2ef053 --- /dev/null +++ b/linux/drivers/media/video/davinci/dm355_ccdc_regs.h @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _DM355_CCDC_REGS_H +#define _DM355_CCDC_REGS_H + +/**************************************************************************\ +* Register OFFSET Definitions +\**************************************************************************/ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDWIDTH 0x08 +#define VDWIDTH 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define NPH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define NLV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define STADRH 0x3c +#define STADRL 0x40 +#define CLAMP 0x44 +#define DCSUB 0x48 +#define COLPTN 0x4c +#define BLKCMP0 0x50 +#define BLKCMP1 0x54 +#define MEDFILT 0x58 +#define RYEGAIN 0x5c +#define GRCYGAIN 0x60 +#define GBGGAIN 0x64 +#define BMGGAIN 0x68 +#define OFFSET 0x6c +#define OUTCLIP 0x70 +#define VDINT0 0x74 +#define VDINT1 0x78 +#define RSV0 0x7c +#define GAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +#define FMTCFG 0x8c +#define FMTPLEN 0x90 +#define FMTSPH 0x94 +#define FMTLNH 0x98 +#define FMTSLV 0x9c +#define FMTLNV 0xa0 +#define FMTRLEN 0xa4 +#define FMTHCNT 0xa8 +#define FMT_ADDR_PTR_B 0xac +#define FMT_ADDR_PTR(i) (FMT_ADDR_PTR_B + (i * 4)) +#define FMTPGM_VF0 0xcc +#define FMTPGM_VF1 0xd0 +#define FMTPGM_AP0 0xd4 +#define FMTPGM_AP1 0xd8 +#define FMTPGM_AP2 0xdc +#define FMTPGM_AP3 0xe0 +#define FMTPGM_AP4 0xe4 +#define FMTPGM_AP5 0xe8 +#define FMTPGM_AP6 0xec +#define FMTPGM_AP7 0xf0 +#define LSCCFG1 0xf4 +#define LSCCFG2 0xf8 +#define LSCH0 0xfc +#define LSCV0 0x100 +#define LSCKH 0x104 +#define LSCKV 0x108 +#define LSCMEMCTL 0x10c +#define LSCMEMD 0x110 +#define LSCMEMQ 0x114 +#define DFCCTL 0x118 +#define DFCVSAT 0x11c +#define DFCMEMCTL 0x120 +#define DFCMEM0 0x124 +#define DFCMEM1 0x128 +#define DFCMEM2 0x12c +#define DFCMEM3 0x130 +#define DFCMEM4 0x134 +#define CSCCTL 0x138 +#define CSCM0 0x13c +#define CSCM1 0x140 +#define CSCM2 0x144 +#define CSCM3 0x148 +#define CSCM4 0x14c +#define CSCM5 0x150 +#define CSCM6 0x154 +#define CSCM7 0x158 +#define DATAOFST 0x15c +#define CCDC_REG_LAST DATAOFST +/************************************************************** +* Define for various register bit mask and shifts for CCDC +* +**************************************************************/ +#define CCDC_RAW_IP_MODE 0 +#define CCDC_VDHDOUT_INPUT 0 +#define CCDC_YCINSWP_RAW (0 << 4) +#define CCDC_EXWEN_DISABLE 0 +#define CCDC_DATAPOL_NORMAL 0 +#define CCDC_CCDCFG_FIDMD_LATCH_VSYNC 0 +#define CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC (1 << 6) +#define CCDC_CCDCFG_WENLOG_AND 0 +#define CCDC_CCDCFG_TRGSEL_WEN 0 +#define CCDC_CCDCFG_EXTRG_DISABLE 0 +#define CCDC_CFA_MOSAIC 0 +#define CCDC_Y8POS_SHIFT 11 + +#define CCDC_VDC_DFCVSAT_MASK 0x3fff +#define CCDC_DATAOFST_MASK 0x0ff +#define CCDC_DATAOFST_H_SHIFT 0 +#define CCDC_DATAOFST_V_SHIFT 8 +#define CCDC_GAMMAWD_CFA_MASK 1 +#define CCDC_GAMMAWD_CFA_SHIFT 5 +#define CCDC_GAMMAWD_INPUT_SHIFT 2 +#define CCDC_FID_POL_MASK 1 +#define CCDC_FID_POL_SHIFT 4 +#define CCDC_HD_POL_MASK 1 +#define CCDC_HD_POL_SHIFT 3 +#define CCDC_VD_POL_MASK 1 +#define CCDC_VD_POL_SHIFT 2 +#define CCDC_VD_POL_NEGATIVE (1 << 2) +#define CCDC_FRM_FMT_MASK 1 +#define CCDC_FRM_FMT_SHIFT 7 +#define CCDC_DATA_SZ_MASK 7 +#define CCDC_DATA_SZ_SHIFT 8 +#define CCDC_VDHDOUT_MASK 1 +#define CCDC_VDHDOUT_SHIFT 0 +#define CCDC_EXWEN_MASK 1 +#define CCDC_EXWEN_SHIFT 5 +#define CCDC_INPUT_MODE_MASK 3 +#define CCDC_INPUT_MODE_SHIFT 12 +#define CCDC_PIX_FMT_MASK 3 +#define CCDC_PIX_FMT_SHIFT 12 +#define CCDC_DATAPOL_MASK 1 +#define CCDC_DATAPOL_SHIFT 6 +#define CCDC_WEN_ENABLE (1 << 1) +#define CCDC_VDHDEN_ENABLE (1 << 16) +#define CCDC_LPF_ENABLE (1 << 14) +#define CCDC_ALAW_ENABLE 1 +#define CCDC_ALAW_GAMA_WD_MASK 7 +#define CCDC_REC656IF_BT656_EN 3 + +#define CCDC_FMTCFG_FMTMODE_MASK 3 +#define CCDC_FMTCFG_FMTMODE_SHIFT 1 +#define CCDC_FMTCFG_LNUM_MASK 3 +#define CCDC_FMTCFG_LNUM_SHIFT 4 +#define CCDC_FMTCFG_ADDRINC_MASK 7 +#define CCDC_FMTCFG_ADDRINC_SHIFT 8 + +#define CCDC_CCDCFG_FIDMD_SHIFT 6 +#define CCDC_CCDCFG_WENLOG_SHIFT 8 +#define CCDC_CCDCFG_TRGSEL_SHIFT 9 +#define CCDC_CCDCFG_EXTRG_SHIFT 10 +#define CCDC_CCDCFG_MSBINVI_SHIFT 13 + +#define CCDC_HSIZE_FLIP_SHIFT 12 +#define CCDC_HSIZE_FLIP_MASK 1 +#define CCDC_HSIZE_VAL_MASK 0xFFF +#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 +#define CCDC_SDOFST_INTERLACE_INVERSE 0x4B6D +#define CCDC_SDOFST_INTERLACE_NORMAL 0x0B6D +#define CCDC_SDOFST_PROGRESSIVE_INVERSE 0x4000 +#define CCDC_SDOFST_PROGRESSIVE_NORMAL 0 +#define CCDC_START_PX_HOR_MASK 0x7FFF +#define CCDC_NUM_PX_HOR_MASK 0x7FFF +#define CCDC_START_VER_ONE_MASK 0x7FFF +#define CCDC_START_VER_TWO_MASK 0x7FFF +#define CCDC_NUM_LINES_VER 0x7FFF + +#define CCDC_BLK_CLAMP_ENABLE (1 << 15) +#define CCDC_BLK_SGAIN_MASK 0x1F +#define CCDC_BLK_ST_PXL_MASK 0x1FFF +#define CCDC_BLK_SAMPLE_LN_MASK 3 +#define CCDC_BLK_SAMPLE_LN_SHIFT 13 + +#define CCDC_NUM_LINE_CALC_MASK 3 +#define CCDC_NUM_LINE_CALC_SHIFT 14 + +#define CCDC_BLK_DC_SUB_MASK 0x3FFF +#define CCDC_BLK_COMP_MASK 0xFF +#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 +#define CCDC_BLK_COMP_GR_COMP_SHIFT 0 +#define CCDC_BLK_COMP_R_COMP_SHIFT 8 +#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15) +#define CCDC_LATCH_ON_VSYNC_ENABLE (0 << 15) +#define CCDC_FPC_ENABLE (1 << 15) +#define CCDC_FPC_FPC_NUM_MASK 0x7FFF +#define CCDC_DATA_PACK_ENABLE (1 << 11) +#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 +#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 +#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF +#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 +#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF +#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 +#define CCDC_VP_OUT_HORZ_ST_MASK 0xF + +#define CCDC_CSC_COEF_INTEG_MASK 7 +#define CCDC_CSC_COEF_DECIMAL_MASK 0x1f +#define CCDC_CSC_COEF_INTEG_SHIFT 5 +#define CCDC_CSCM_MSB_SHIFT 8 +#define CCDC_CSC_ENABLE 1 +#define CCDC_CSC_DEC_MAX 32 + +#define CCDC_MFILT1_SHIFT 10 +#define CCDC_MFILT2_SHIFT 8 +#define CCDC_MED_FILT_THRESH 0x3FFF +#define CCDC_LPF_MASK 1 +#define CCDC_LPF_SHIFT 14 +#define CCDC_OFFSET_MASK 0x3FF +#define CCDC_DATASFT_MASK 7 +#define CCDC_DATASFT_SHIFT 8 + +#define CCDC_DF_ENABLE 1 + +#define CCDC_FMTPLEN_P0_MASK 0xF +#define CCDC_FMTPLEN_P1_MASK 0xF +#define CCDC_FMTPLEN_P2_MASK 7 +#define CCDC_FMTPLEN_P3_MASK 7 +#define CCDC_FMTPLEN_P0_SHIFT 0 +#define CCDC_FMTPLEN_P1_SHIFT 4 +#define CCDC_FMTPLEN_P2_SHIFT 8 +#define CCDC_FMTPLEN_P3_SHIFT 12 + +#define CCDC_FMTSPH_MASK 0x1FFF +#define CCDC_FMTLNH_MASK 0x1FFF +#define CCDC_FMTSLV_MASK 0x1FFF +#define CCDC_FMTLNV_MASK 0x7FFF +#define CCDC_FMTRLEN_MASK 0x1FFF +#define CCDC_FMTHCNT_MASK 0x1FFF + +#define CCDC_ADP_INIT_MASK 0x1FFF +#define CCDC_ADP_LINE_SHIFT 13 +#define CCDC_ADP_LINE_MASK 3 +#define CCDC_FMTPGN_APTR_MASK 7 + +#define CCDC_DFCCTL_GDFCEN_MASK 1 +#define CCDC_DFCCTL_VDFCEN_MASK 1 +#define CCDC_DFCCTL_VDFC_DISABLE (0 << 4) +#define CCDC_DFCCTL_VDFCEN_SHIFT 4 +#define CCDC_DFCCTL_VDFCSL_MASK 3 +#define CCDC_DFCCTL_VDFCSL_SHIFT 5 +#define CCDC_DFCCTL_VDFCUDA_MASK 1 +#define CCDC_DFCCTL_VDFCUDA_SHIFT 7 +#define CCDC_DFCCTL_VDFLSFT_MASK 3 +#define CCDC_DFCCTL_VDFLSFT_SHIFT 8 +#define CCDC_DFCMEMCTL_DFCMARST_MASK 1 +#define CCDC_DFCMEMCTL_DFCMARST_SHIFT 2 +#define CCDC_DFCMEMCTL_DFCMWR_MASK 1 +#define CCDC_DFCMEMCTL_DFCMWR_SHIFT 0 +#define CCDC_DFCMEMCTL_INC_ADDR (0 << 2) + +#define CCDC_LSCCFG_GFTSF_MASK 7 +#define CCDC_LSCCFG_GFTSF_SHIFT 1 +#define CCDC_LSCCFG_GFTINV_MASK 0xf +#define CCDC_LSCCFG_GFTINV_SHIFT 4 +#define CCDC_LSC_GFTABLE_SEL_MASK 3 +#define CCDC_LSC_GFTABLE_EPEL_SHIFT 8 +#define CCDC_LSC_GFTABLE_OPEL_SHIFT 10 +#define CCDC_LSC_GFTABLE_EPOL_SHIFT 12 +#define CCDC_LSC_GFTABLE_OPOL_SHIFT 14 +#define CCDC_LSC_GFMODE_MASK 3 +#define CCDC_LSC_GFMODE_SHIFT 4 +#define CCDC_LSC_DISABLE 0 +#define CCDC_LSC_ENABLE 1 +#define CCDC_LSC_TABLE1_SLC 0 +#define CCDC_LSC_TABLE2_SLC 1 +#define CCDC_LSC_TABLE3_SLC 2 +#define CCDC_LSC_MEMADDR_RESET (1 << 2) +#define CCDC_LSC_MEMADDR_INCR (0 << 2) +#define CCDC_LSC_FRAC_MASK_T1 0xFF +#define CCDC_LSC_INT_MASK 3 +#define CCDC_LSC_FRAC_MASK 0x3FFF +#define CCDC_LSC_CENTRE_MASK 0x3FFF +#define CCDC_LSC_COEF_MASK 0xff +#define CCDC_LSC_COEFL_SHIFT 0 +#define CCDC_LSC_COEFU_SHIFT 8 +#define CCDC_GAIN_MASK 0x7FF +#define CCDC_SYNCEN_VDHDEN_MASK (1 << 0) +#define CCDC_SYNCEN_WEN_MASK (1 << 1) +#define CCDC_SYNCEN_WEN_SHIFT 1 + +/* Power on Defaults in hardware */ +#define MODESET_DEFAULT 0x200 +#define CULH_DEFAULT 0xFFFF +#define CULV_DEFAULT 0xFF +#define GAIN_DEFAULT 256 +#define OUTCLIP_DEFAULT 0x3FFF +#define LSCCFG2_DEFAULT 0xE + +#endif diff --git a/linux/include/media/davinci/dm355_ccdc.h b/linux/include/media/davinci/dm355_ccdc.h new file mode 100644 index 000000000..df8a7b107 --- /dev/null +++ b/linux/include/media/davinci/dm355_ccdc.h @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _DM355_CCDC_H +#define _DM355_CCDC_H +#include +#include + +/* enum for No of pixel per line to be avg. in Black Clamping */ +enum ccdc_sample_length { + CCDC_SAMPLE_1PIXELS, + CCDC_SAMPLE_2PIXELS, + CCDC_SAMPLE_4PIXELS, + CCDC_SAMPLE_8PIXELS, + CCDC_SAMPLE_16PIXELS +}; + +/* enum for No of lines in Black Clamping */ +enum ccdc_sample_line { + CCDC_SAMPLE_1LINES, + CCDC_SAMPLE_2LINES, + CCDC_SAMPLE_4LINES, + CCDC_SAMPLE_8LINES, + CCDC_SAMPLE_16LINES +}; + +/* enum for Alaw gama width */ +enum ccdc_gamma_width { + CCDC_GAMMA_BITS_13_4, + CCDC_GAMMA_BITS_12_3, + CCDC_GAMMA_BITS_11_2, + CCDC_GAMMA_BITS_10_1, + CCDC_GAMMA_BITS_09_0 +}; + +enum ccdc_colpats { + CCDC_RED, + CCDC_GREEN_RED, + CCDC_GREEN_BLUE, + CCDC_BLUE +}; + +struct ccdc_col_pat { + enum ccdc_colpats olop; + enum ccdc_colpats olep; + enum ccdc_colpats elop; + enum ccdc_colpats elep; +}; + +enum ccdc_datasft { + CCDC_DATA_NO_SHIFT, + CCDC_DATA_SHIFT_1BIT, + CCDC_DATA_SHIFT_2BIT, + CCDC_DATA_SHIFT_3BIT, + CCDC_DATA_SHIFT_4BIT, + CCDC_DATA_SHIFT_5BIT, + CCDC_DATA_SHIFT_6BIT +}; + +enum ccdc_data_size { + CCDC_DATA_16BITS, + CCDC_DATA_15BITS, + CCDC_DATA_14BITS, + CCDC_DATA_13BITS, + CCDC_DATA_12BITS, + CCDC_DATA_11BITS, + CCDC_DATA_10BITS, + CCDC_DATA_8BITS +}; +enum ccdc_mfilt1 { + CCDC_NO_MEDIAN_FILTER1, + CCDC_AVERAGE_FILTER1, + CCDC_MEDIAN_FILTER1 +}; + +enum ccdc_mfilt2 { + CCDC_NO_MEDIAN_FILTER2, + CCDC_AVERAGE_FILTER2, + CCDC_MEDIAN_FILTER2 +}; + +/* structure for ALaw */ +struct ccdc_a_law { + /* Enable/disable A-Law */ + unsigned char enable; + /* Gama Width Input */ + enum ccdc_gamma_width gama_wd; +}; + +/* structure for Black Clamping */ +struct ccdc_black_clamp { + /* only if bClampEnable is TRUE */ + unsigned char b_clamp_enable; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_length sample_pixel; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_line sample_ln; + /* only if bClampEnable is TRUE */ + unsigned short start_pixel; + /* only if bClampEnable is FALSE */ + unsigned short sgain; + unsigned short dc_sub; +}; + +/* structure for Black Level Compensation */ +struct ccdc_black_compensation { + /* Constant value to subtract from Red component */ + unsigned char r; + /* Constant value to subtract from Gr component */ + unsigned char gr; + /* Constant value to subtract from Blue component */ + unsigned char b; + /* Constant value to subtract from Gb component */ + unsigned char gb; +}; + +struct ccdc_float { + int integer; + unsigned int decimal; +}; + +#define CCDC_CSC_COEFF_TABLE_SIZE 16 +/* structure for color space converter */ +struct ccdc_csc { + unsigned char enable; + /* + * S8Q5. Use 2 decimal precision, user values range from -3.00 to 3.99. + * example - to use 1.03, set integer part as 1, and decimal part as 3 + * to use -1.03, set integer part as -1 and decimal part as 3 + */ + struct ccdc_float coeff[CCDC_CSC_COEFF_TABLE_SIZE]; +}; + +/* Structures for Vertical Defect Correction*/ +enum ccdc_vdf_csl { + CCDC_VDF_NORMAL, + CCDC_VDF_HORZ_INTERPOL_SAT, + CCDC_VDF_HORZ_INTERPOL +}; + +enum ccdc_vdf_cuda { + CCDC_VDF_WHOLE_LINE_CORRECT, + CCDC_VDF_UPPER_DISABLE +}; + +enum ccdc_dfc_mwr { + CCDC_DFC_MWR_WRITE_COMPLETE, + CCDC_DFC_WRITE_REG +}; + +enum ccdc_dfc_mrd { + CCDC_DFC_READ_COMPLETE, + CCDC_DFC_READ_REG +}; + +enum ccdc_dfc_ma_rst { + CCDC_DFC_INCR_ADDR, + CCDC_DFC_CLR_ADDR +}; + +enum ccdc_dfc_mclr { + CCDC_DFC_CLEAR_COMPLETE, + CCDC_DFC_CLEAR +}; + +struct ccdc_dft_corr_ctl { + enum ccdc_vdf_csl vdfcsl; + enum ccdc_vdf_cuda vdfcuda; + unsigned int vdflsft; +}; + +struct ccdc_dft_corr_mem_ctl { + enum ccdc_dfc_mwr dfcmwr; + enum ccdc_dfc_mrd dfcmrd; + enum ccdc_dfc_ma_rst dfcmarst; + enum ccdc_dfc_mclr dfcmclr; +}; + +#define CCDC_DFT_TABLE_SIZE 16 +/* + * Main Structure for vertical defect correction. Vertical defect + * correction can correct upto 16 defects if defects less than 16 + * then pad the rest with 0 + */ +struct ccdc_vertical_dft { + unsigned char ver_dft_en; + unsigned char gen_dft_en; + unsigned int saturation_ctl; + struct ccdc_dft_corr_ctl dft_corr_ctl; + struct ccdc_dft_corr_mem_ctl dft_corr_mem_ctl; + int table_size; + unsigned int dft_corr_horz[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_vert[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_sub1[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_sub2[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_sub3[CCDC_DFT_TABLE_SIZE]; +}; + +struct ccdc_data_offset { + unsigned char horz_offset; + unsigned char vert_offset; +}; + +/* + * Structure for CCDC configuration parameters for raw capture mode passed + * by application + */ +struct ccdc_config_params_raw { + /* data shift to be applied before storing */ + enum ccdc_datasft datasft; + /* data size value from 8 to 16 bits */ + enum ccdc_data_size data_sz; + /* median filter for sdram */ + enum ccdc_mfilt1 mfilt1; + enum ccdc_mfilt2 mfilt2; + /* low pass filter enable/disable */ + unsigned char lpf_enable; + /* Threshold of median filter */ + int med_filt_thres; + /* + * horz and vertical data offset. Appliable for defect correction + * and lsc + */ + struct ccdc_data_offset data_offset; + /* Structure for Optional A-Law */ + struct ccdc_a_law alaw; + /* Structure for Optical Black Clamp */ + struct ccdc_black_clamp blk_clamp; + /* Structure for Black Compensation */ + struct ccdc_black_compensation blk_comp; + /* struture for vertical Defect Correction Module Configuration */ + struct ccdc_vertical_dft vertical_dft; + /* structure for color space converter Module Configuration */ + struct ccdc_csc csc; + /* color patters for bayer capture */ + struct ccdc_col_pat col_pat_field0; + struct ccdc_col_pat col_pat_field1; +}; + +#ifdef __KERNEL__ +#include + +#define CCDC_WIN_PAL {0, 0, 720, 576} +#define CCDC_WIN_VGA {0, 0, 640, 480} + +struct ccdc_params_ycbcr { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* enable BT.656 embedded sync mode */ + int bt656_enable; + /* cb:y:cr:y or y:cb:y:cr in memory */ + enum ccdc_pixorder pix_order; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; +}; + +/* Gain applied to Raw Bayer data */ +struct ccdc_gain { + unsigned short r_ye; + unsigned short gr_cy; + unsigned short gb_g; + unsigned short b_mg; +}; + +/* Structure for CCDC configuration parameters for raw capture mode */ +struct ccdc_params_raw { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; + /* Gain values */ + struct ccdc_gain gain; + /* offset */ + unsigned int ccdc_offset; + /* horizontal flip enable */ + unsigned char horz_flip_enable; + /* + * enable to store the image in inverse order in memory + * (bottom to top) + */ + unsigned char image_invert_enable; + /* Configurable part of raw data */ + struct ccdc_config_params_raw config_params; +}; + +#endif +#endif /* DM355_CCDC_H */ -- cgit v1.2.3 From 7a9ed65a4e1e7c25cab4104eae7bea6b400389d2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Jun 2009 14:18:14 +0200 Subject: v4l: dm644x ccdc module for vpfe capture driver From: Muralidharan Karicheri This is the hw module for DM644x CCDC. This registers with the vpfe capture driver and provides a set of hw_ops to configure CCDC for a specific decoder device connected to the VPFE. Reviewed by: Hans Verkuil Reviewed by: Laurent Pinchart Priority: normal Signed-off-by: Muralidharan Karicheri Signed-off-by: Hans Verkuil --- linux/drivers/media/video/davinci/dm644x_ccdc.c | 878 +++++++++++++++++++++ .../drivers/media/video/davinci/dm644x_ccdc_regs.h | 145 ++++ linux/include/media/davinci/dm644x_ccdc.h | 184 +++++ 3 files changed, 1207 insertions(+) create mode 100644 linux/drivers/media/video/davinci/dm644x_ccdc.c create mode 100644 linux/drivers/media/video/davinci/dm644x_ccdc_regs.h create mode 100644 linux/include/media/davinci/dm644x_ccdc.h diff --git a/linux/drivers/media/video/davinci/dm644x_ccdc.c b/linux/drivers/media/video/davinci/dm644x_ccdc.c new file mode 100644 index 000000000..2f19a919f --- /dev/null +++ b/linux/drivers/media/video/davinci/dm644x_ccdc.c @@ -0,0 +1,878 @@ +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * CCDC hardware module for DM6446 + * ------------------------------ + * + * This module is for configuring CCD controller of DM6446 VPFE to capture + * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules + * such as Defect Pixel Correction, Color Space Conversion etc to + * pre-process the Raw Bayer RGB data, before writing it to SDRAM. This + * module also allows application to configure individual + * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL. + * To do so, application includes dm644x_ccdc.h and vpfe_capture.h header + * files. The setparams() API is called by vpfe_capture driver + * to configure module parameters. This file is named DM644x so that other + * variants such DM6443 may be supported using the same module. + * + * TODO: Test Raw bayer parameter settings and bayer capture + * Split module parameter structure to module specific ioctl structs + * investigate if enum used for user space type definition + * to be replaced by #defines or integer + */ +#include +#include +#include +#include +#include +#include "dm644x_ccdc_regs.h" +#include "ccdc_hw_device.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CCDC Driver for DM6446"); +MODULE_AUTHOR("Texas Instruments"); + +static struct device *dev; + +/* Object for CCDC raw mode */ +static struct ccdc_params_raw ccdc_hw_params_raw = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .config_params = { + .data_sz = CCDC_DATA_10BITS, + }, +}; + +/* Object for CCDC ycbcr mode */ +static struct ccdc_params_ycbcr ccdc_hw_params_ycbcr = { + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .win = CCDC_WIN_PAL, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .bt656_enable = 1, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED +}; + +#define CCDC_MAX_RAW_YUV_FORMATS 2 + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = + {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static u32 ccdc_raw_yuv_pix_formats[] = + {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +static void *__iomem ccdc_base_addr; +static int ccdc_addr_size; +static enum vpfe_hw_if_type ccdc_if_type; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(ccdc_base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, ccdc_base_addr + offset); +} + +static void ccdc_set_ccdc_base(void *addr, int size) +{ + ccdc_base_addr = addr; + ccdc_addr_size = size; +} + +static void ccdc_enable(int flag) +{ + regw(flag, CCDC_PCR); +} + +static void ccdc_enable_vport(int flag) +{ + if (flag) + /* enable video port */ + regw(CCDC_ENABLE_VIDEO_PORT, CCDC_FMTCFG); + else + regw(CCDC_DISABLE_VIDEO_PORT, CCDC_FMTCFG); +} + +/* + * ccdc_setwin() + * This function will configure the window size + * to be capture in CCDC reg + */ +void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, + int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int val = 0, mid_img = 0; + + dev_dbg(dev, "\nStarting ccdc_setwin..."); + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = (image_win->width << (ppc - 1)) - 1; + regw((horz_start << CCDC_HORZ_INFO_SPH_SHIFT) | horz_nr_pixels, + CCDC_HORZ_INFO); + + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* Since first line doesn't have any data */ + vert_start += 1; + /* configure VDINT0 */ + val = (vert_start << CCDC_VDINT_VDINT0_SHIFT); + regw(val, CCDC_VDINT); + + } else { + /* Since first line doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* + * configure VDINT0 and VDINT1. VDINT1 will be at half + * of image height + */ + mid_img = vert_start + (image_win->height / 2); + val = (vert_start << CCDC_VDINT_VDINT0_SHIFT) | + (mid_img & CCDC_VDINT_VDINT1_MASK); + regw(val, CCDC_VDINT); + + } + regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start, + CCDC_VERT_START); + regw(vert_nr_lines, CCDC_VERT_LINES); + dev_dbg(dev, "\nEnd of ccdc_setwin..."); +} + +static void ccdc_readregs(void) +{ + unsigned int val = 0; + + val = regr(CCDC_ALAW); + dev_notice(dev, "\nReading 0x%x to ALAW...\n", val); + val = regr(CCDC_CLAMP); + dev_notice(dev, "\nReading 0x%x to CLAMP...\n", val); + val = regr(CCDC_DCSUB); + dev_notice(dev, "\nReading 0x%x to DCSUB...\n", val); + val = regr(CCDC_BLKCMP); + dev_notice(dev, "\nReading 0x%x to BLKCMP...\n", val); + val = regr(CCDC_FPC_ADDR); + dev_notice(dev, "\nReading 0x%x to FPC_ADDR...\n", val); + val = regr(CCDC_FPC); + dev_notice(dev, "\nReading 0x%x to FPC...\n", val); + val = regr(CCDC_FMTCFG); + dev_notice(dev, "\nReading 0x%x to FMTCFG...\n", val); + val = regr(CCDC_COLPTN); + dev_notice(dev, "\nReading 0x%x to COLPTN...\n", val); + val = regr(CCDC_FMT_HORZ); + dev_notice(dev, "\nReading 0x%x to FMT_HORZ...\n", val); + val = regr(CCDC_FMT_VERT); + dev_notice(dev, "\nReading 0x%x to FMT_VERT...\n", val); + val = regr(CCDC_HSIZE_OFF); + dev_notice(dev, "\nReading 0x%x to HSIZE_OFF...\n", val); + val = regr(CCDC_SDOFST); + dev_notice(dev, "\nReading 0x%x to SDOFST...\n", val); + val = regr(CCDC_VP_OUT); + dev_notice(dev, "\nReading 0x%x to VP_OUT...\n", val); + val = regr(CCDC_SYN_MODE); + dev_notice(dev, "\nReading 0x%x to SYN_MODE...\n", val); + val = regr(CCDC_HORZ_INFO); + dev_notice(dev, "\nReading 0x%x to HORZ_INFO...\n", val); + val = regr(CCDC_VERT_START); + dev_notice(dev, "\nReading 0x%x to VERT_START...\n", val); + val = regr(CCDC_VERT_LINES); + dev_notice(dev, "\nReading 0x%x to VERT_LINES...\n", val); +} + +static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) +{ + if (ccdcparam->alaw.enable) { + if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) || + (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) || + (ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) { + dev_dbg(dev, "\nInvalid data line select"); + return -1; + } + } + return 0; +} + +static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int *fpc_virtaddr = NULL; + unsigned int *fpc_physaddr = NULL; + + memcpy(config_params, raw_params, sizeof(*raw_params)); + /* + * allocate memory for fault pixel table and copy the user + * values to the table + */ + if (!config_params->fault_pxl.enable) + return 0; + + fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr; + fpc_virtaddr = (unsigned int *)phys_to_virt( + (unsigned long)fpc_physaddr); + /* + * Allocate memory for FPC table if current + * FPC table buffer is not big enough to + * accomodate FPC Number requested + */ + if (raw_params->fault_pxl.fp_num != config_params->fault_pxl.fp_num) { + if (fpc_physaddr != NULL) { + free_pages((unsigned long)fpc_physaddr, + get_order + (config_params->fault_pxl.fp_num * + FP_NUM_BYTES)); + } + + /* Allocate memory for FPC table */ + fpc_virtaddr = + (unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA, + get_order(raw_params-> + fault_pxl.fp_num * + FP_NUM_BYTES)); + + if (fpc_virtaddr == NULL) { + dev_dbg(dev, + "\nUnable to allocate memory for FPC"); + return -EFAULT; + } + fpc_physaddr = + (unsigned int *)virt_to_phys((void *)fpc_virtaddr); + } + + /* Copy number of fault pixels and FPC table */ + config_params->fault_pxl.fp_num = raw_params->fault_pxl.fp_num; + if (copy_from_user(fpc_virtaddr, + (void __user *)raw_params->fault_pxl.fpc_table_addr, + config_params->fault_pxl.fp_num * FP_NUM_BYTES)) { + dev_dbg(dev, "\n copy_from_user failed"); + return -EFAULT; + } + config_params->fault_pxl.fpc_table_addr = (unsigned int)fpc_physaddr; + return 0; +} + +static int ccdc_close(struct device *dev) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int *fpc_physaddr = NULL, *fpc_virtaddr = NULL; + + fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr; + + if (fpc_physaddr != NULL) { + fpc_virtaddr = (unsigned int *) + phys_to_virt((unsigned long)fpc_physaddr); + free_pages((unsigned long)fpc_virtaddr, + get_order(config_params->fault_pxl.fp_num * + FP_NUM_BYTES)); + } + return 0; +} + +/* + * ccdc_restore_defaults() + * This function will write defaults to all CCDC registers + */ +static void ccdc_restore_defaults(void) +{ + int i; + + /* disable CCDC */ + ccdc_enable(0); + /* set all registers to default value */ + for (i = 4; i <= 0x94; i += 4) + regw(0, i); + regw(CCDC_NO_CULLING, CCDC_CULLING); + regw(CCDC_GAMMA_BITS_11_2, CCDC_ALAW); +} + +static int ccdc_open(struct device *device) +{ + dev = device; + ccdc_restore_defaults(); + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_enable_vport(1); + return 0; +} + +static void ccdc_sbl_reset(void) +{ + vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O); +} + +/* Parameter operations */ +static int ccdc_set_params(void __user *params) +{ + struct ccdc_config_params_raw ccdc_raw_params; + int x; + + if (ccdc_if_type != VPFE_RAW_BAYER) + return -EINVAL; + + x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); + if (x) { + dev_dbg(dev, "ccdc_set_params: error in copying" + "ccdc params, %d\n", x); + return -EFAULT; + } + + if (!validate_ccdc_param(&ccdc_raw_params)) { + if (!ccdc_update_raw_params(&ccdc_raw_params)) + return 0; + } + return -EINVAL; +} + +/* + * ccdc_config_ycbcr() + * This function will configure CCDC for YCbCr video capture + */ +void ccdc_config_ycbcr(void) +{ + struct ccdc_params_ycbcr *params = &ccdc_hw_params_ycbcr; + u32 syn_mode; + + dev_dbg(dev, "\nStarting ccdc_config_ycbcr..."); + /* + * first restore the CCDC registers to default values + * This is important since we assume default values to be set in + * a lot of registers that we didn't touch + */ + ccdc_restore_defaults(); + + /* + * configure pixel format, frame format, configure video frame + * format, enable output to SDRAM, enable internal timing generator + * and 8bit pack mode + */ + syn_mode = (((params->pix_fmt & CCDC_SYN_MODE_INPMOD_MASK) << + CCDC_SYN_MODE_INPMOD_SHIFT) | + ((params->frm_fmt & CCDC_SYN_FLDMODE_MASK) << + CCDC_SYN_FLDMODE_SHIFT) | CCDC_VDHDEN_ENABLE | + CCDC_WEN_ENABLE | CCDC_DATA_PACK_ENABLE); + + /* setup BT.656 sync mode */ + if (params->bt656_enable) { + regw(CCDC_REC656IF_BT656_EN, CCDC_REC656IF); + + /* + * configure the FID, VD, HD pin polarity, + * fld,hd pol positive, vd negative, 8-bit data + */ + syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE | CCDC_SYN_MODE_8BITS; + } else { + /* y/c external sync mode */ + syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) << + CCDC_FID_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << + CCDC_HD_POL_SHIFT) | + ((params->vd_pol & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT)); + } + regw(syn_mode, CCDC_SYN_MODE); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 2); + + /* + * configure the order of y cb cr in SDRAM, and disable latch + * internal register on vsync + */ + regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | + CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); + + /* + * configure the horizontal line offset. This should be a + * on 32 byte bondary. So clear LSB 5 bits + */ + regw(((params->win.width * 2 + 31) & ~0x1f), CCDC_HSIZE_OFF); + + /* configure the memory line offset */ + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) + /* two fields are interleaved in memory */ + regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST); + + ccdc_sbl_reset(); + dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n"); + ccdc_readregs(); +} + +static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) +{ + u32 val; + + if (!bclamp->enable) { + /* configure DCSub */ + val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK; + regw(val, CCDC_DCSUB); + dev_dbg(dev, "\nWriting 0x%x to DCSUB...\n", val); + regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP); + dev_dbg(dev, "\nWriting 0x0000 to CLAMP...\n"); + return; + } + /* + * Configure gain, Start pixel, No of line to be avg, + * No of pixel/line to be avg, & Enable the Black clamping + */ + val = ((bclamp->sgain & CCDC_BLK_SGAIN_MASK) | + ((bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) << + CCDC_BLK_ST_PXL_SHIFT) | + ((bclamp->sample_ln & CCDC_BLK_SAMPLE_LINE_MASK) << + CCDC_BLK_SAMPLE_LINE_SHIFT) | + ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << + CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE); + regw(val, CCDC_CLAMP); + dev_dbg(dev, "\nWriting 0x%x to CLAMP...\n", val); + /* If Black clamping is enable then make dcsub 0 */ + regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB); + dev_dbg(dev, "\nWriting 0x00000000 to DCSUB...\n"); +} + +static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) +{ + u32 val; + + val = ((bcomp->b & CCDC_BLK_COMP_MASK) | + ((bcomp->gb & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GB_COMP_SHIFT) | + ((bcomp->gr & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GR_COMP_SHIFT) | + ((bcomp->r & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_R_COMP_SHIFT)); + regw(val, CCDC_BLKCMP); +} + +static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc) +{ + u32 val; + + /* Initially disable FPC */ + val = CCDC_FPC_DISABLE; + regw(val, CCDC_FPC); + + if (!fpc->enable) + return; + + /* Configure Fault pixel if needed */ + regw(fpc->fpc_table_addr, CCDC_FPC_ADDR); + dev_dbg(dev, "\nWriting 0x%x to FPC_ADDR...\n", + (fpc->fpc_table_addr)); + /* Write the FPC params with FPC disable */ + val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK; + regw(val, CCDC_FPC); + + dev_dbg(dev, "\nWriting 0x%x to FPC...\n", val); + /* read the FPC register */ + val = regr(CCDC_FPC) | CCDC_FPC_ENABLE; + regw(val, CCDC_FPC); + dev_dbg(dev, "\nWriting 0x%x to FPC...\n", val); +} + +/* + * ccdc_config_raw() + * This function will configure CCDC for Raw capture mode + */ +void ccdc_config_raw(void) +{ + struct ccdc_params_raw *params = &ccdc_hw_params_raw; + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int syn_mode = 0; + unsigned int val; + + dev_dbg(dev, "\nStarting ccdc_config_raw..."); + + /* Reset CCDC */ + ccdc_restore_defaults(); + + /* Disable latching function registers on VSYNC */ + regw(CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); + + /* + * Configure the vertical sync polarity(SYN_MODE.VDPOL), + * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity + * (SYN_MODE.FLDPOL), frame format(progressive or interlace), + * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output + * SDRAM, enable internal timing generator + */ + syn_mode = + (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | + ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | + ((config_params->data_sz & CCDC_DATA_SZ_MASK) << + CCDC_DATA_SZ_SHIFT) | + ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT) | + CCDC_WEN_ENABLE | CCDC_VDHDEN_ENABLE); + + /* Enable and configure aLaw register if needed */ + if (config_params->alaw.enable) { + val = ((config_params->alaw.gama_wd & + CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE); + regw(val, CCDC_ALAW); + dev_dbg(dev, "\nWriting 0x%x to ALAW...\n", val); + } + + /* Configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, CCDC_PPC_RAW); + + /* Configure Black Clamp */ + ccdc_config_black_clamp(&config_params->blk_clamp); + + /* Configure Black level compensation */ + ccdc_config_black_compense(&config_params->blk_comp); + + /* Configure Fault Pixel Correction */ + ccdc_config_fpc(&config_params->fault_pxl); + + /* If data size is 8 bit then pack the data */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + syn_mode |= CCDC_DATA_PACK_ENABLE; + +#ifdef CONFIG_DM644X_VIDEO_PORT_ENABLE + /* enable video port */ + val = CCDC_ENABLE_VIDEO_PORT; +#else + /* disable video port */ + val = CCDC_DISABLE_VIDEO_PORT; +#endif + + if (config_params->data_sz == CCDC_DATA_8BITS) + val |= (CCDC_DATA_10BITS & CCDC_FMTCFG_VPIN_MASK) + << CCDC_FMTCFG_VPIN_SHIFT; + else + val |= (config_params->data_sz & CCDC_FMTCFG_VPIN_MASK) + << CCDC_FMTCFG_VPIN_SHIFT; + /* Write value in FMTCFG */ + regw(val, CCDC_FMTCFG); + + dev_dbg(dev, "\nWriting 0x%x to FMTCFG...\n", val); + /* Configure the color pattern according to mt9t001 sensor */ + regw(CCDC_COLPTN_VAL, CCDC_COLPTN); + + dev_dbg(dev, "\nWriting 0xBB11BB11 to COLPTN...\n"); + /* + * Configure Data formatter(Video port) pixel selection + * (FMT_HORZ, FMT_VERT) + */ + val = ((params->win.left & CCDC_FMT_HORZ_FMTSPH_MASK) << + CCDC_FMT_HORZ_FMTSPH_SHIFT) | + (params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK); + regw(val, CCDC_FMT_HORZ); + + dev_dbg(dev, "\nWriting 0x%x to FMT_HORZ...\n", val); + val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK) + << CCDC_FMT_VERT_FMTSLV_SHIFT; + if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + val |= (params->win.height) & CCDC_FMT_VERT_FMTLNV_MASK; + else + val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK; + + dev_dbg(dev, "\nparams->win.height 0x%x ...\n", + params->win.height); + regw(val, CCDC_FMT_VERT); + + dev_dbg(dev, "\nWriting 0x%x to FMT_VERT...\n", val); + + dev_dbg(dev, "\nbelow regw(val, FMT_VERT)..."); + + /* + * Configure Horizontal offset register. If pack 8 is enabled then + * 1 pixel will take 1 byte + */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + regw((params->win.width + CCDC_32BYTE_ALIGN_VAL) & + CCDC_HSIZE_OFF_MASK, CCDC_HSIZE_OFF); + else + /* else one pixel will take 2 byte */ + regw(((params->win.width * CCDC_TWO_BYTES_PER_PIXEL) + + CCDC_32BYTE_ALIGN_VAL) & CCDC_HSIZE_OFF_MASK, + CCDC_HSIZE_OFF); + + /* Set value for SDOFST */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_enable) { + /* For intelace inverse mode */ + regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(dev, "\nWriting 0x4B6D to SDOFST...\n"); + } + + else { + /* For intelace non inverse mode */ + regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(dev, "\nWriting 0x0249 to SDOFST...\n"); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(dev, "\nWriting 0x0000 to SDOFST...\n"); + } + + /* + * Configure video port pixel selection (VPOUT) + * Here -1 is to make the height value less than FMT_VERT.FMTLNV + */ + if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + val = (((params->win.height - 1) & CCDC_VP_OUT_VERT_NUM_MASK)) + << CCDC_VP_OUT_VERT_NUM_SHIFT; + else + val = + ((((params->win.height >> CCDC_INTERLACED_HEIGHT_SHIFT) - + 1) & CCDC_VP_OUT_VERT_NUM_MASK)) << + CCDC_VP_OUT_VERT_NUM_SHIFT; + + val |= ((((params->win.width))) & CCDC_VP_OUT_HORZ_NUM_MASK) + << CCDC_VP_OUT_HORZ_NUM_SHIFT; + val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK; + regw(val, CCDC_VP_OUT); + + dev_dbg(dev, "\nWriting 0x%x to VP_OUT...\n", val); + regw(syn_mode, CCDC_SYN_MODE); + dev_dbg(dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode); + + ccdc_sbl_reset(); + dev_dbg(dev, "\nend of ccdc_config_raw..."); + ccdc_readregs(); +} + +static int ccdc_configure(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_config_raw(); + else + ccdc_config_ycbcr(); + return 0; +} + +static int ccdc_set_buftype(enum ccdc_buftype buf_type) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.buf_type = buf_type; + else + ccdc_hw_params_ycbcr.buf_type = buf_type; + return 0; +} + +static enum ccdc_buftype ccdc_get_buftype(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.buf_type; + return ccdc_hw_params_ycbcr.buf_type; +} + +static int ccdc_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + if (ccdc_if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { + *pix = ccdc_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { + *pix = ccdc_raw_yuv_pix_formats[i]; + ret = 0; + } + } + return ret; +} + +static int ccdc_set_pixel_format(u32 pixfmt) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) { + ccdc_hw_params_raw.pix_fmt = CCDC_PIXFMT_RAW; + if (pixfmt == V4L2_PIX_FMT_SBGGR8) + ccdc_hw_params_raw.config_params.alaw.enable = 1; + else if (pixfmt != V4L2_PIX_FMT_SBGGR16) + return -EINVAL; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + } + return 0; +} + +static u32 ccdc_get_pixel_format(void) +{ + struct ccdc_a_law *alaw = + &ccdc_hw_params_raw.config_params.alaw; + u32 pixfmt; + + if (ccdc_if_type == VPFE_RAW_BAYER) + if (alaw->enable) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (ccdc_hw_params_ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} + +static int ccdc_set_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.win = *win; + else + ccdc_hw_params_ycbcr.win = *win; + return 0; +} + +static void ccdc_get_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + *win = ccdc_hw_params_raw.win; + else + *win = ccdc_hw_params_ycbcr.win; +} + +static unsigned int ccdc_get_line_length(void) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int len; + + if (ccdc_if_type == VPFE_RAW_BAYER) { + if ((config_params->alaw.enable) || + (config_params->data_sz == CCDC_DATA_8BITS)) + len = ccdc_hw_params_raw.win.width; + else + len = ccdc_hw_params_raw.win.width * 2; + } else + len = ccdc_hw_params_ycbcr.win.width * 2; + return ALIGN(len, 32); +} + +static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.frm_fmt = frm_fmt; + else + ccdc_hw_params_ycbcr.frm_fmt = frm_fmt; + return 0; +} + +static enum ccdc_frmfmt ccdc_get_frame_format(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.frm_fmt; + else + return ccdc_hw_params_ycbcr.frm_fmt; +} + +static int ccdc_getfid(void) +{ + return (regr(CCDC_SYN_MODE) >> 15) & 1; +} + +/* misc operations */ +static inline void ccdc_setfbaddr(unsigned long addr) +{ + regw(addr & 0xffffffe0, CCDC_SDR_ADDR); +} + +static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + ccdc_if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_YCBCR_SYNC_16: + case VPFE_YCBCR_SYNC_8: + ccdc_hw_params_ycbcr.vd_pol = params->vdpol; + ccdc_hw_params_ycbcr.hd_pol = params->hdpol; + break; + default: + /* TODO add support for raw bayer here */ + return -EINVAL; + } + return 0; +} + +static struct ccdc_hw_device ccdc_hw_dev = { + .name = "DM6446 CCDC", + .owner = THIS_MODULE, + .hw_ops = { + .open = ccdc_open, + .close = ccdc_close, + .set_ccdc_base = ccdc_set_ccdc_base, + .reset = ccdc_sbl_reset, + .enable = ccdc_enable, + .set_hw_if_params = ccdc_set_hw_if_params, + .set_params = ccdc_set_params, + .configure = ccdc_configure, + .set_buftype = ccdc_set_buftype, + .get_buftype = ccdc_get_buftype, + .enum_pix = ccdc_enum_pix, + .set_pixel_format = ccdc_set_pixel_format, + .get_pixel_format = ccdc_get_pixel_format, + .set_frame_format = ccdc_set_frame_format, + .get_frame_format = ccdc_get_frame_format, + .set_image_window = ccdc_set_image_window, + .get_image_window = ccdc_get_image_window, + .get_line_length = ccdc_get_line_length, + .setfbaddr = ccdc_setfbaddr, + .getfid = ccdc_getfid, + }, +}; + +static int dm644x_ccdc_init(void) +{ + printk(KERN_NOTICE "dm644x_ccdc_init\n"); + if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0) + return -1; + printk(KERN_NOTICE "%s is registered with vpfe.\n", + ccdc_hw_dev.name); + return 0; +} + +static void dm644x_ccdc_exit(void) +{ + vpfe_unregister_ccdc_device(&ccdc_hw_dev); +} + +module_init(dm644x_ccdc_init); +module_exit(dm644x_ccdc_exit); diff --git a/linux/drivers/media/video/davinci/dm644x_ccdc_regs.h b/linux/drivers/media/video/davinci/dm644x_ccdc_regs.h new file mode 100644 index 000000000..6e5d05324 --- /dev/null +++ b/linux/drivers/media/video/davinci/dm644x_ccdc_regs.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _DM644X_CCDC_REGS_H +#define _DM644X_CCDC_REGS_H + +/**************************************************************************\ +* Register OFFSET Definitions +\**************************************************************************/ +#define CCDC_PID 0x0 +#define CCDC_PCR 0x4 +#define CCDC_SYN_MODE 0x8 +#define CCDC_HD_VD_WID 0xc +#define CCDC_PIX_LINES 0x10 +#define CCDC_HORZ_INFO 0x14 +#define CCDC_VERT_START 0x18 +#define CCDC_VERT_LINES 0x1c +#define CCDC_CULLING 0x20 +#define CCDC_HSIZE_OFF 0x24 +#define CCDC_SDOFST 0x28 +#define CCDC_SDR_ADDR 0x2c +#define CCDC_CLAMP 0x30 +#define CCDC_DCSUB 0x34 +#define CCDC_COLPTN 0x38 +#define CCDC_BLKCMP 0x3c +#define CCDC_FPC 0x40 +#define CCDC_FPC_ADDR 0x44 +#define CCDC_VDINT 0x48 +#define CCDC_ALAW 0x4c +#define CCDC_REC656IF 0x50 +#define CCDC_CCDCFG 0x54 +#define CCDC_FMTCFG 0x58 +#define CCDC_FMT_HORZ 0x5c +#define CCDC_FMT_VERT 0x60 +#define CCDC_FMT_ADDR0 0x64 +#define CCDC_FMT_ADDR1 0x68 +#define CCDC_FMT_ADDR2 0x6c +#define CCDC_FMT_ADDR3 0x70 +#define CCDC_FMT_ADDR4 0x74 +#define CCDC_FMT_ADDR5 0x78 +#define CCDC_FMT_ADDR6 0x7c +#define CCDC_FMT_ADDR7 0x80 +#define CCDC_PRGEVEN_0 0x84 +#define CCDC_PRGEVEN_1 0x88 +#define CCDC_PRGODD_0 0x8c +#define CCDC_PRGODD_1 0x90 +#define CCDC_VP_OUT 0x94 + + +/*************************************************************** +* Define for various register bit mask and shifts for CCDC +****************************************************************/ +#define CCDC_FID_POL_MASK 1 +#define CCDC_FID_POL_SHIFT 4 +#define CCDC_HD_POL_MASK 1 +#define CCDC_HD_POL_SHIFT 3 +#define CCDC_VD_POL_MASK 1 +#define CCDC_VD_POL_SHIFT 2 +#define CCDC_HSIZE_OFF_MASK 0xffffffe0 +#define CCDC_32BYTE_ALIGN_VAL 31 +#define CCDC_FRM_FMT_MASK 0x1 +#define CCDC_FRM_FMT_SHIFT 7 +#define CCDC_DATA_SZ_MASK 7 +#define CCDC_DATA_SZ_SHIFT 8 +#define CCDC_PIX_FMT_MASK 3 +#define CCDC_PIX_FMT_SHIFT 12 +#define CCDC_VP2SDR_DISABLE 0xFFFBFFFF +#define CCDC_WEN_ENABLE (1 << 17) +#define CCDC_SDR2RSZ_DISABLE 0xFFF7FFFF +#define CCDC_VDHDEN_ENABLE (1 << 16) +#define CCDC_LPF_ENABLE (1 << 14) +#define CCDC_ALAW_ENABLE (1 << 3) +#define CCDC_ALAW_GAMA_WD_MASK 7 +#define CCDC_BLK_CLAMP_ENABLE (1 << 31) +#define CCDC_BLK_SGAIN_MASK 0x1F +#define CCDC_BLK_ST_PXL_MASK 0x7FFF +#define CCDC_BLK_ST_PXL_SHIFT 10 +#define CCDC_BLK_SAMPLE_LN_MASK 7 +#define CCDC_BLK_SAMPLE_LN_SHIFT 28 +#define CCDC_BLK_SAMPLE_LINE_MASK 7 +#define CCDC_BLK_SAMPLE_LINE_SHIFT 25 +#define CCDC_BLK_DC_SUB_MASK 0x03FFF +#define CCDC_BLK_COMP_MASK 0xFF +#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 +#define CCDC_BLK_COMP_GR_COMP_SHIFT 16 +#define CCDC_BLK_COMP_R_COMP_SHIFT 24 +#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15) +#define CCDC_FPC_ENABLE (1 << 15) +#define CCDC_FPC_DISABLE 0 +#define CCDC_FPC_FPC_NUM_MASK 0x7FFF +#define CCDC_DATA_PACK_ENABLE (1 << 11) +#define CCDC_FMTCFG_VPIN_MASK 7 +#define CCDC_FMTCFG_VPIN_SHIFT 12 +#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 +#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 +#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF +#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 +#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF +#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 +#define CCDC_VP_OUT_HORZ_ST_MASK 0xF +#define CCDC_HORZ_INFO_SPH_SHIFT 16 +#define CCDC_VERT_START_SLV0_SHIFT 16 +#define CCDC_VDINT_VDINT0_SHIFT 16 +#define CCDC_VDINT_VDINT1_MASK 0xFFFF +#define CCDC_PPC_RAW 1 +#define CCDC_DCSUB_DEFAULT_VAL 0 +#define CCDC_CLAMP_DEFAULT_VAL 0 +#define CCDC_ENABLE_VIDEO_PORT 0x8000 +#define CCDC_DISABLE_VIDEO_PORT 0 +#define CCDC_COLPTN_VAL 0xBB11BB11 +#define CCDC_TWO_BYTES_PER_PIXEL 2 +#define CCDC_INTERLACED_IMAGE_INVERT 0x4B6D +#define CCDC_INTERLACED_NO_IMAGE_INVERT 0x0249 +#define CCDC_PROGRESSIVE_IMAGE_INVERT 0x4000 +#define CCDC_PROGRESSIVE_NO_IMAGE_INVERT 0 +#define CCDC_INTERLACED_HEIGHT_SHIFT 1 +#define CCDC_SYN_MODE_INPMOD_SHIFT 12 +#define CCDC_SYN_MODE_INPMOD_MASK 3 +#define CCDC_SYN_MODE_8BITS (7 << 8) +#define CCDC_SYN_FLDMODE_MASK 1 +#define CCDC_SYN_FLDMODE_SHIFT 7 +#define CCDC_REC656IF_BT656_EN 3 +#define CCDC_SYN_MODE_VD_POL_NEGATIVE (1 << 2) +#define CCDC_CCDCFG_Y8POS_SHIFT 11 +#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 +#define CCDC_NO_CULLING 0xffff00ff +#endif diff --git a/linux/include/media/davinci/dm644x_ccdc.h b/linux/include/media/davinci/dm644x_ccdc.h new file mode 100644 index 000000000..3e178eb52 --- /dev/null +++ b/linux/include/media/davinci/dm644x_ccdc.h @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _DM644X_CCDC_H +#define _DM644X_CCDC_H +#include +#include + +/* enum for No of pixel per line to be avg. in Black Clamping*/ +enum ccdc_sample_length { + CCDC_SAMPLE_1PIXELS, + CCDC_SAMPLE_2PIXELS, + CCDC_SAMPLE_4PIXELS, + CCDC_SAMPLE_8PIXELS, + CCDC_SAMPLE_16PIXELS +}; + +/* enum for No of lines in Black Clamping */ +enum ccdc_sample_line { + CCDC_SAMPLE_1LINES, + CCDC_SAMPLE_2LINES, + CCDC_SAMPLE_4LINES, + CCDC_SAMPLE_8LINES, + CCDC_SAMPLE_16LINES +}; + +/* enum for Alaw gama width */ +enum ccdc_gama_width { + CCDC_GAMMA_BITS_15_6, + CCDC_GAMMA_BITS_14_5, + CCDC_GAMMA_BITS_13_4, + CCDC_GAMMA_BITS_12_3, + CCDC_GAMMA_BITS_11_2, + CCDC_GAMMA_BITS_10_1, + CCDC_GAMMA_BITS_09_0 +}; + +enum ccdc_data_size { + CCDC_DATA_16BITS, + CCDC_DATA_15BITS, + CCDC_DATA_14BITS, + CCDC_DATA_13BITS, + CCDC_DATA_12BITS, + CCDC_DATA_11BITS, + CCDC_DATA_10BITS, + CCDC_DATA_8BITS +}; + +/* structure for ALaw */ +struct ccdc_a_law { + /* Enable/disable A-Law */ + unsigned char enable; + /* Gama Width Input */ + enum ccdc_gama_width gama_wd; +}; + +/* structure for Black Clamping */ +struct ccdc_black_clamp { + unsigned char enable; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_length sample_pixel; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_line sample_ln; + /* only if bClampEnable is TRUE */ + unsigned short start_pixel; + /* only if bClampEnable is TRUE */ + unsigned short sgain; + /* only if bClampEnable is FALSE */ + unsigned short dc_sub; +}; + +/* structure for Black Level Compensation */ +struct ccdc_black_compensation { + /* Constant value to subtract from Red component */ + char r; + /* Constant value to subtract from Gr component */ + char gr; + /* Constant value to subtract from Blue component */ + char b; + /* Constant value to subtract from Gb component */ + char gb; +}; + +/* structure for fault pixel correction */ +struct ccdc_fault_pixel { + /* Enable or Disable fault pixel correction */ + unsigned char enable; + /* Number of fault pixel */ + unsigned short fp_num; + /* Address of fault pixel table */ + unsigned int fpc_table_addr; +}; + +/* Structure for CCDC configuration parameters for raw capture mode passed + * by application + */ +struct ccdc_config_params_raw { + /* data size value from 8 to 16 bits */ + enum ccdc_data_size data_sz; + /* Structure for Optional A-Law */ + struct ccdc_a_law alaw; + /* Structure for Optical Black Clamp */ + struct ccdc_black_clamp blk_clamp; + /* Structure for Black Compensation */ + struct ccdc_black_compensation blk_comp; + /* Structure for Fault Pixel Module Configuration */ + struct ccdc_fault_pixel fault_pxl; +}; + + +#ifdef __KERNEL__ +#include +/* Define to enable/disable video port */ +#define FP_NUM_BYTES 4 +/* Define for extra pixel/line and extra lines/frame */ +#define NUM_EXTRAPIXELS 8 +#define NUM_EXTRALINES 8 + +/* settings for commonly used video formats */ +#define CCDC_WIN_PAL {0, 0, 720, 576} +/* ntsc square pixel */ +#define CCDC_WIN_VGA {0, 0, (640 + NUM_EXTRAPIXELS), (480 + NUM_EXTRALINES)} + +/* Structure for CCDC configuration parameters for raw capture mode */ +struct ccdc_params_raw { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; + /* + * enable to store the image in inverse + * order in memory(bottom to top) + */ + unsigned char image_invert_enable; + /* configurable paramaters */ + struct ccdc_config_params_raw config_params; +}; + +struct ccdc_params_ycbcr { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* enable BT.656 embedded sync mode */ + int bt656_enable; + /* cb:y:cr:y or y:cb:y:cr in memory */ + enum ccdc_pixorder pix_order; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; +}; +#endif +#endif /* _DM644X_CCDC_H */ -- cgit v1.2.3 From c1263315bcee61fa0d97043622b2f800bc61a550 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Jun 2009 14:19:17 +0200 Subject: v4l: ccdc types used across ccdc modules for vpfe capture driver From: Muralidharan Karicheri common types used across CCDC modules Reviewed by: Hans Verkuil Reviewed by: Laurent Pinchart Priority: normal Signed-off-by: Muralidharan Karicheri Signed-off-by: Hans Verkuil --- linux/include/media/davinci/ccdc_types.h | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 linux/include/media/davinci/ccdc_types.h diff --git a/linux/include/media/davinci/ccdc_types.h b/linux/include/media/davinci/ccdc_types.h new file mode 100644 index 000000000..5773874bf --- /dev/null +++ b/linux/include/media/davinci/ccdc_types.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + **************************************************************************/ +#ifndef _CCDC_TYPES_H +#define _CCDC_TYPES_H +enum ccdc_pixfmt { + CCDC_PIXFMT_RAW, + CCDC_PIXFMT_YCBCR_16BIT, + CCDC_PIXFMT_YCBCR_8BIT +}; + +enum ccdc_frmfmt { + CCDC_FRMFMT_PROGRESSIVE, + CCDC_FRMFMT_INTERLACED +}; + +/* PIXEL ORDER IN MEMORY from LSB to MSB */ +/* only applicable for 8-bit input mode */ +enum ccdc_pixorder { + CCDC_PIXORDER_YCBYCR, + CCDC_PIXORDER_CBYCRY, +}; + +enum ccdc_buftype { + CCDC_BUFTYPE_FLD_INTERLEAVED, + CCDC_BUFTYPE_FLD_SEPARATED +}; +#endif -- cgit v1.2.3 From 4f2e24628e0f385f1b57e57524fb345cea8a8333 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Jun 2009 14:20:16 +0200 Subject: v4l: common vpss module for video drivers From: Muralidharan Karicheri This is a new module added for vpss library functions that are used for configuring vpss system module. All video drivers will include vpss.h header file and call functions defined in this module to configure vpss system module. Reviewed by: Hans Verkuil Reviewed by: Laurent Pinchart Reviewed by: Alexey Klimov Priority: normal Signed-off-by: Muralidharan Karicheri Signed-off-by: Hans Verkuil --- linux/drivers/media/video/davinci/vpss.c | 301 +++++++++++++++++++++++++++++++ linux/include/media/davinci/vpss.h | 69 +++++++ 2 files changed, 370 insertions(+) create mode 100644 linux/drivers/media/video/davinci/vpss.c create mode 100644 linux/include/media/davinci/vpss.h diff --git a/linux/drivers/media/video/davinci/vpss.c b/linux/drivers/media/video/davinci/vpss.c new file mode 100644 index 000000000..6d709ca8c --- /dev/null +++ b/linux/drivers/media/video/davinci/vpss.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2009 Texas Instruments. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * common vpss driver for all video drivers. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("VPSS Driver"); +MODULE_AUTHOR("Texas Instruments"); + +/* DM644x defines */ +#define DM644X_SBL_PCR_VPSS (4) + +/* vpss BL register offsets */ +#define DM355_VPSSBL_CCDCMUX 0x1c +/* vpss CLK register offsets */ +#define DM355_VPSSCLK_CLKCTRL 0x04 +/* masks and shifts */ +#define VPSS_HSSISEL_SHIFT 4 + +/* + * vpss operations. Depends on platform. Not all functions are available + * on all platforms. The api, first check if a functio is available before + * invoking it. In the probe, the function ptrs are intialized based on + * vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc. + */ +struct vpss_hw_ops { + /* enable clock */ + int (*enable_clock)(enum vpss_clock_sel clock_sel, int en); + /* select input to ccdc */ + void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel); + /* clear wbl overlflow bit */ + int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel); +}; + +/* vpss configuration */ +struct vpss_oper_config { + __iomem void *vpss_bl_regs_base; + __iomem void *vpss_regs_base; + struct resource *r1; + resource_size_t len1; + struct resource *r2; + resource_size_t len2; + char vpss_name[32]; + spinlock_t vpss_lock; + struct vpss_hw_ops hw_ops; +}; + +static struct vpss_oper_config oper_cfg; + +/* register access routines */ +static inline u32 bl_regr(u32 offset) +{ + return __raw_readl(oper_cfg.vpss_bl_regs_base + offset); +} + +static inline void bl_regw(u32 val, u32 offset) +{ + __raw_writel(val, oper_cfg.vpss_bl_regs_base + offset); +} + +static inline u32 vpss_regr(u32 offset) +{ + return __raw_readl(oper_cfg.vpss_regs_base + offset); +} + +static inline void vpss_regw(u32 val, u32 offset) +{ + __raw_writel(val, oper_cfg.vpss_regs_base + offset); +} + +static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) +{ + bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX); +} + +int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) +{ + if (!oper_cfg.hw_ops.select_ccdc_source) + return -1; + + dm355_select_ccdc_source(src_sel); + return 0; +} +EXPORT_SYMBOL(vpss_select_ccdc_source); + +static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) +{ + u32 mask = 1, val; + + if (wbl_sel < VPSS_PCR_AEW_WBL_0 || + wbl_sel > VPSS_PCR_CCDC_WBL_O) + return -1; + + /* writing a 0 clear the overflow */ + mask = ~(mask << wbl_sel); + val = bl_regr(DM644X_SBL_PCR_VPSS) & mask; + bl_regw(val, DM644X_SBL_PCR_VPSS); + return 0; +} + +int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) +{ + if (!oper_cfg.hw_ops.clear_wbl_overflow) + return -1; + + return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel); +} +EXPORT_SYMBOL(vpss_clear_wbl_overflow); + +/* + * dm355_enable_clock - Enable VPSS Clock + * @clock_sel: CLock to be enabled/disabled + * @en: enable/disable flag + * + * This is called to enable or disable a vpss clock + */ +static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en) +{ + unsigned long flags; + u32 utemp, mask = 0x1, shift = 0; + + switch (clock_sel) { + case VPSS_VPBE_CLOCK: + /* nothing since lsb */ + break; + case VPSS_VENC_CLOCK_SEL: + shift = 2; + break; + case VPSS_CFALD_CLOCK: + shift = 3; + break; + case VPSS_H3A_CLOCK: + shift = 4; + break; + case VPSS_IPIPE_CLOCK: + shift = 5; + break; + case VPSS_CCDC_CLOCK: + shift = 6; + break; + default: + printk(KERN_ERR "dm355_enable_clock:" + " Invalid selector: %d\n", clock_sel); + return -1; + } + + spin_lock_irqsave(&oper_cfg.vpss_lock, flags); + utemp = vpss_regr(DM355_VPSSCLK_CLKCTRL); + if (!en) + utemp &= ~(mask << shift); + else + utemp |= (mask << shift); + + vpss_regw(utemp, DM355_VPSSCLK_CLKCTRL); + spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags); + return 0; +} + +int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en) +{ + if (!oper_cfg.hw_ops.enable_clock) + return -1; + + return oper_cfg.hw_ops.enable_clock(clock_sel, en); +} +EXPORT_SYMBOL(vpss_enable_clock); + +static int __init vpss_probe(struct platform_device *pdev) +{ + int status, dm355 = 0; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform data\n"); + return -ENOENT; + } + strcpy(oper_cfg.vpss_name, pdev->dev.platform_data); + + if (!strcmp(oper_cfg.vpss_name, "dm355_vpss")) + dm355 = 1; + else if (strcmp(oper_cfg.vpss_name, "dm644x_vpss")) { + dev_err(&pdev->dev, "vpss driver not supported on" + " this platform\n"); + return -ENODEV; + } + + dev_info(&pdev->dev, "%s vpss probed\n", oper_cfg.vpss_name); + oper_cfg.r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!oper_cfg.r1) + return -ENOENT; + + oper_cfg.len1 = oper_cfg.r1->end - oper_cfg.r1->start + 1; + + oper_cfg.r1 = request_mem_region(oper_cfg.r1->start, oper_cfg.len1, + oper_cfg.r1->name); + if (!oper_cfg.r1) + return -EBUSY; + + oper_cfg.vpss_bl_regs_base = ioremap(oper_cfg.r1->start, oper_cfg.len1); + if (!oper_cfg.vpss_bl_regs_base) { + status = -EBUSY; + goto fail1; + } + + if (dm355) { + oper_cfg.r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!oper_cfg.r2) { + status = -ENOENT; + goto fail2; + } + oper_cfg.len2 = oper_cfg.r2->end - oper_cfg.r2->start + 1; + oper_cfg.r2 = request_mem_region(oper_cfg.r2->start, + oper_cfg.len2, + oper_cfg.r2->name); + if (!oper_cfg.r2) { + status = -EBUSY; + goto fail2; + } + + oper_cfg.vpss_regs_base = ioremap(oper_cfg.r2->start, + oper_cfg.len2); + if (!oper_cfg.vpss_regs_base) { + status = -EBUSY; + goto fail3; + } + } + + if (dm355) { + oper_cfg.hw_ops.enable_clock = dm355_enable_clock; + oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source; + } else + oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow; + + spin_lock_init(&oper_cfg.vpss_lock); + dev_info(&pdev->dev, "%s vpss probe success\n", oper_cfg.vpss_name); + return 0; + +fail3: + release_mem_region(oper_cfg.r2->start, oper_cfg.len2); +fail2: + iounmap(oper_cfg.vpss_bl_regs_base); +fail1: + release_mem_region(oper_cfg.r1->start, oper_cfg.len1); + return status; +} + +static int vpss_remove(struct platform_device *pdev) +{ + iounmap(oper_cfg.vpss_bl_regs_base); + release_mem_region(oper_cfg.r1->start, oper_cfg.len1); + if (!strcmp(oper_cfg.vpss_name, "dm355_vpss")) { + iounmap(oper_cfg.vpss_regs_base); + release_mem_region(oper_cfg.r2->start, oper_cfg.len2); + } + return 0; +} + +static struct platform_driver vpss_driver = { + .driver = { + .name = "vpss", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(vpss_remove), + .probe = vpss_probe, +}; + +static void vpss_exit(void) +{ + platform_driver_unregister(&vpss_driver); +} + +static int __init vpss_init(void) +{ + return platform_driver_register(&vpss_driver); +} +subsys_initcall(vpss_init); +module_exit(vpss_exit); diff --git a/linux/include/media/davinci/vpss.h b/linux/include/media/davinci/vpss.h new file mode 100644 index 000000000..fcdff745f --- /dev/null +++ b/linux/include/media/davinci/vpss.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * vpss - video processing subsystem module header file. + * + * Include this header file if a driver needs to configure vpss system + * module. It exports a set of library functions for video drivers to + * configure vpss system module functions such as clock enable/disable, + * vpss interrupt mux to arm, and other common vpss system module + * functions. + */ +#ifndef _VPSS_H +#define _VPSS_H + +/* selector for ccdc input selection on DM355 */ +enum vpss_ccdc_source_sel { + VPSS_CCDCIN, + VPSS_HSSIIN +}; + +/* Used for enable/diable VPSS Clock */ +enum vpss_clock_sel { + /* DM355/DM365 */ + VPSS_CCDC_CLOCK, + VPSS_IPIPE_CLOCK, + VPSS_H3A_CLOCK, + VPSS_CFALD_CLOCK, + /* + * When using VPSS_VENC_CLOCK_SEL in vpss_enable_clock() api + * following applies:- + * en = 0 selects ENC_CLK + * en = 1 selects ENC_CLK/2 + */ + VPSS_VENC_CLOCK_SEL, + VPSS_VPBE_CLOCK, +}; + +/* select input to ccdc on dm355 */ +int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel); +/* enable/disable a vpss clock, 0 - success, -1 - failure */ +int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en); + +/* wbl reset for dm644x */ +enum vpss_wbl_sel { + VPSS_PCR_AEW_WBL_0 = 16, + VPSS_PCR_AF_WBL_0, + VPSS_PCR_RSZ4_WBL_0, + VPSS_PCR_RSZ3_WBL_0, + VPSS_PCR_RSZ2_WBL_0, + VPSS_PCR_RSZ1_WBL_0, + VPSS_PCR_PREV_WBL_0, + VPSS_PCR_CCDC_WBL_O, +}; +int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel); +#endif -- cgit v1.2.3 From b561142eefcee2657f23d0af7838791f063f10f6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 6 Jul 2009 20:08:31 +0200 Subject: v4l: Makefile and config files for vpfe capture driver From: Muralidharan Karicheri This adds Makefile and Kconfig changes to build vpfe capture driver. Reviewed by: Laurent Pinchart Priority: normal Signed-off-by: Muralidharan Karicheri Signed-off-by: Hans Verkuil --- linux/drivers/media/video/Kconfig | 49 ++++++++++++++++++++++++++++++ linux/drivers/media/video/Makefile | 2 ++ linux/drivers/media/video/davinci/Makefile | 6 ++++ 3 files changed, 57 insertions(+) diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index e54bf1e70..8460013a0 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -527,6 +527,55 @@ config VIDEO_VIVI Say Y here if you want to test video apps or debug V4L devices. In doubt, say N. +config VIDEO_VPSS_SYSTEM + tristate "VPSS System module driver" + depends on ARCH_DAVINCI + help + Support for vpss system module for video driver + default y + +config VIDEO_VPFE_CAPTURE + tristate "VPFE Video Capture Driver" + depends on VIDEO_V4L2 && ARCH_DAVINCI + select VIDEOBUF_DMA_CONTIG + help + Support for DMXXXX VPFE based frame grabber. This is the + common V4L2 module for following DMXXX SoCs from Texas + Instruments:- DM6446 & DM355. + + To compile this driver as a module, choose M here: the + module will be called vpfe-capture. + +config VIDEO_DM6446_CCDC + tristate "DM6446 CCDC HW module" + depends on ARCH_DAVINCI_DM644x && VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + default y + help + Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from slave decoders. + + To compile this driver as a module, choose M here: the + module will be called vpfe. + +config VIDEO_DM355_CCDC + tristate "DM355 CCDC HW module" + depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + default y + help + Enables DM355 CCD hw module. DM355 CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from a slave decoders + + To compile this driver as a module, choose M here: the + module will be called vpfe. + source "drivers/media/video/bt8xx/Kconfig" config VIDEO_PMS diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index d3ec3a2fd..00fb23e64 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -154,6 +154,8 @@ obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o +obj-$(CONFIG_ARCH_DAVINCI) += davinci/ + obj-$(CONFIG_VIDEO_AU0828) += au0828/ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ diff --git a/linux/drivers/media/video/davinci/Makefile b/linux/drivers/media/video/davinci/Makefile index 7fe9bce97..f44cad2f5 100644 --- a/linux/drivers/media/video/davinci/Makefile +++ b/linux/drivers/media/video/davinci/Makefile @@ -7,3 +7,9 @@ obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o #DM646x EVM Display driver obj-$(CONFIG_DISPLAY_DAVINCI_DM646X_EVM) += vpif_display.o + +# Capture: DM6446 and DM355 +obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o +obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o +obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o +obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o -- cgit v1.2.3 From 69db135e84d7caa8eaca84fab752a51a644f6b98 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Jun 2009 14:27:54 +0200 Subject: v4l: davinci drivers should only be compiled for kernels >= 2.6.31. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- v4l/versions.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/v4l/versions.txt b/v4l/versions.txt index 7e8e2fa6e..eb3597f76 100644 --- a/v4l/versions.txt +++ b/v4l/versions.txt @@ -1,6 +1,13 @@ # Use this for stuff for drivers that don't compile [2.6.99] +[2.6.31] +# These rely on arch support that wasn't available until 2.6.31 +VIDEO_VPSS_SYSTEM +VIDEO_VPFE_CAPTURE +VIDEO_DM6446_CCDC +VIDEO_DM355_CCDC + [2.6.29] # Needs defines that are only available from 2.6.29 VIDEO_PXA27x -- cgit v1.2.3 From bce52916562346cea9474564c12897c762d87e46 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 27 Jul 2009 19:05:19 -0300 Subject: af9015: avoid magically sized temporary buffer in eeprom_dump From: Jan Nikitenko Replace printing to magically sized temporary buffer with use of KERN_CONT for continual printing of eeprom registers dump. Since deb_info is defined as dprintk, which is conditionally defined to printk without additional parameters, meaning that deb_info is equivalent to direct printk (without adding KERN_ facility), we can use KERN_DEBUG and KERN_CONT in there, eliminating the need for sprintf into temporary buffer with not easily readable/magical size. Though it's strange, that deb_info definition uses printk without KERN_ facility and callers don't use it either. Signed-off-by: Jan Nikitenko Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-usb/af9015.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c index b6fc8ddf3..a94f4d305 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.c +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -541,24 +541,22 @@ exit: /* dump eeprom */ static int af9015_eeprom_dump(struct dvb_usb_device *d) { - char buf[4+3*16+1], buf2[4]; u8 reg, val; for (reg = 0; ; reg++) { if (reg % 16 == 0) { if (reg) - deb_info("%s\n", buf); - sprintf(buf, "%02x: ", reg); + deb_info(KERN_CONT "\n"); + deb_info(KERN_DEBUG "%02x:", reg); } if (af9015_read_reg_i2c(d, AF9015_I2C_EEPROM, reg, &val) == 0) - sprintf(buf2, "%02x ", val); + deb_info(KERN_CONT " %02x", val); else - strcpy(buf2, "-- "); - strcat(buf, buf2); + deb_info(KERN_CONT " --"); if (reg == 0xff) break; } - deb_info("%s\n", buf); + deb_info(KERN_CONT "\n"); return 0; } -- cgit v1.2.3 From f47aedd6b5ff27a36bee52be6cd41b022375b133 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 19 Jun 2009 19:56:56 +0000 Subject: Stop defining I2C adapter IDs nobody uses From: Jean Delvare There is no point in defining I2C adapter IDs when no code is using them. As this field might go away in the future, stop using it when we don't need to. Signed-off-by: Jean Delvare Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/au0828/au0828-i2c.c | 1 - linux/drivers/media/video/bt8xx/bttv-i2c.c | 4 ++++ linux/drivers/media/video/cafe_ccic.c | 1 - linux/drivers/media/video/cx18/cx18-i2c.c | 2 ++ linux/drivers/media/video/cx231xx/cx231xx-i2c.c | 1 - linux/drivers/media/video/cx23885/cx23885-i2c.c | 2 ++ linux/drivers/media/video/em28xx/em28xx-i2c.c | 2 ++ linux/drivers/media/video/hdpvr/hdpvr-i2c.c | 1 - linux/drivers/media/video/ivtv/ivtv-i2c.c | 4 ++++ linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 2 ++ linux/drivers/media/video/vino.c | 1 - linux/drivers/media/video/w9968cf.c | 1 - linux/drivers/media/video/zoran/zoran_card.c | 1 - 13 files changed, 16 insertions(+), 7 deletions(-) diff --git a/linux/drivers/media/video/au0828/au0828-i2c.c b/linux/drivers/media/video/au0828/au0828-i2c.c index bbe38a7a2..4031d6bd3 100644 --- a/linux/drivers/media/video/au0828/au0828-i2c.c +++ b/linux/drivers/media/video/au0828/au0828-i2c.c @@ -324,7 +324,6 @@ static struct i2c_algorithm au0828_i2c_algo_template = { static struct i2c_adapter au0828_i2c_adap_template = { .name = DRIVER_NAME, .owner = THIS_MODULE, - .id = I2C_HW_B_AU0828, .algo = &au0828_i2c_algo_template, #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .class = I2C_CLASS_TV_ANALOG, diff --git a/linux/drivers/media/video/bt8xx/bttv-i2c.c b/linux/drivers/media/video/bt8xx/bttv-i2c.c index 4febaf881..84c1e7167 100644 --- a/linux/drivers/media/video/bt8xx/bttv-i2c.c +++ b/linux/drivers/media/video/bt8xx/bttv-i2c.c @@ -355,7 +355,9 @@ int __devinit init_bttv_i2c(struct bttv *btv) /* bt878 */ strlcpy(btv->c.i2c_adap.name, "bt878", sizeof(btv->c.i2c_adap.name)); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) btv->c.i2c_adap.id = I2C_HW_B_BT848; /* FIXME */ +#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) btv->c.i2c_adap.algo = (struct i2c_algorithm *)&bttv_algo; #else @@ -369,7 +371,9 @@ int __devinit init_bttv_i2c(struct bttv *btv) strlcpy(btv->c.i2c_adap.name, "bttv", sizeof(btv->c.i2c_adap.name)); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) btv->c.i2c_adap.id = I2C_HW_B_BT848; +#endif memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template, sizeof(bttv_i2c_algo_bit_template)); btv->i2c_algo.udelay = i2c_udelay; diff --git a/linux/drivers/media/video/cafe_ccic.c b/linux/drivers/media/video/cafe_ccic.c index 4b8ecad1b..2c78c35e8 100644 --- a/linux/drivers/media/video/cafe_ccic.c +++ b/linux/drivers/media/video/cafe_ccic.c @@ -491,7 +491,6 @@ static int cafe_smbus_setup(struct cafe_camera *cam) int ret; cafe_smbus_enable_irq(cam); - adap->id = I2C_HW_SMBUS_CAFE; adap->owner = THIS_MODULE; adap->algo = &cafe_smbus_algo; strcpy(adap->name, "cafe_ccic"); diff --git a/linux/drivers/media/video/cx18/cx18-i2c.c b/linux/drivers/media/video/cx18/cx18-i2c.c index 3c76a1e27..cee650c58 100644 --- a/linux/drivers/media/video/cx18/cx18-i2c.c +++ b/linux/drivers/media/video/cx18/cx18-i2c.c @@ -190,7 +190,9 @@ static int cx18_getsda(void *data) /* template for i2c-bit-algo */ static struct i2c_adapter cx18_i2c_adap_template = { .name = "cx18 i2c driver", +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) .id = I2C_HW_B_CX2341X, +#endif .algo = NULL, /* set by i2c-algo-bit */ .algo_data = NULL, /* filled from template */ .owner = THIS_MODULE, diff --git a/linux/drivers/media/video/cx231xx/cx231xx-i2c.c b/linux/drivers/media/video/cx231xx/cx231xx-i2c.c index 4525fab35..f9bfb10bd 100644 --- a/linux/drivers/media/video/cx231xx/cx231xx-i2c.c +++ b/linux/drivers/media/video/cx231xx/cx231xx-i2c.c @@ -435,7 +435,6 @@ static struct i2c_algorithm cx231xx_algo = { static struct i2c_adapter cx231xx_adap_template = { .owner = THIS_MODULE, .name = "cx231xx", - .id = I2C_HW_B_CX231XX, .algo = &cx231xx_algo, }; diff --git a/linux/drivers/media/video/cx23885/cx23885-i2c.c b/linux/drivers/media/video/cx23885/cx23885-i2c.c index 2e3f3d37c..8823c8781 100644 --- a/linux/drivers/media/video/cx23885/cx23885-i2c.c +++ b/linux/drivers/media/video/cx23885/cx23885-i2c.c @@ -287,7 +287,9 @@ static struct i2c_algorithm cx23885_i2c_algo_template = { static struct i2c_adapter cx23885_i2c_adap_template = { .name = "cx23885", .owner = THIS_MODULE, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) .id = I2C_HW_B_CX23885, +#endif .algo = &cx23885_i2c_algo_template, #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .class = I2C_CLASS_TV_ANALOG, diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c index 91cff2af3..5ed24c23a 100644 --- a/linux/drivers/media/video/em28xx/em28xx-i2c.c +++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c @@ -465,7 +465,9 @@ static struct i2c_adapter em28xx_adap_template = { .class = I2C_CLASS_TV_ANALOG, #endif .name = "em28xx", +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) .id = I2C_HW_B_EM28XX, +#endif .algo = &em28xx_algo, }; diff --git a/linux/drivers/media/video/hdpvr/hdpvr-i2c.c b/linux/drivers/media/video/hdpvr/hdpvr-i2c.c index c4b5d1515..296330a0e 100644 --- a/linux/drivers/media/video/hdpvr/hdpvr-i2c.c +++ b/linux/drivers/media/video/hdpvr/hdpvr-i2c.c @@ -127,7 +127,6 @@ int hdpvr_register_i2c_adapter(struct hdpvr_device *dev) sizeof(i2c_adap->name)); i2c_adap->algo = &hdpvr_algo; i2c_adap->class = I2C_CLASS_TV_ANALOG; - i2c_adap->id = I2C_HW_B_HDPVR; i2c_adap->owner = THIS_MODULE; i2c_adap->dev.parent = &dev->udev->dev; diff --git a/linux/drivers/media/video/ivtv/ivtv-i2c.c b/linux/drivers/media/video/ivtv/ivtv-i2c.c index 484a323f1..b5c94d646 100644 --- a/linux/drivers/media/video/ivtv/ivtv-i2c.c +++ b/linux/drivers/media/video/ivtv/ivtv-i2c.c @@ -516,7 +516,9 @@ static struct i2c_algorithm ivtv_algo = { /* template for our-bit banger */ static struct i2c_adapter ivtv_i2c_adap_hw_template = { .name = "ivtv i2c driver", +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) .id = I2C_HW_B_CX2341X, +#endif .algo = &ivtv_algo, .algo_data = NULL, /* filled from template */ .owner = THIS_MODULE, @@ -570,7 +572,9 @@ static int ivtv_getsda_old(void *data) /* template for i2c-bit-algo */ static struct i2c_adapter ivtv_i2c_adap_template = { .name = "ivtv i2c driver", +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) .id = I2C_HW_B_CX2341X, +#endif .algo = NULL, /* set by i2c-algo-bit */ .algo_data = NULL, /* filled from template */ .owner = THIS_MODULE, diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 24644fc96..dbc0551f0 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -618,7 +618,9 @@ static struct i2c_algorithm pvr2_i2c_algo_template = { static struct i2c_adapter pvr2_i2c_adap_template = { .owner = THIS_MODULE, .class = 0, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) .id = I2C_HW_B_BT848, +#endif }; diff --git a/linux/drivers/media/video/vino.c b/linux/drivers/media/video/vino.c index 7c39b43ee..af03ec2d5 100644 --- a/linux/drivers/media/video/vino.c +++ b/linux/drivers/media/video/vino.c @@ -1776,7 +1776,6 @@ static struct i2c_algo_sgi_data i2c_sgi_vino_data = { static struct i2c_adapter vino_i2c_adapter = { .name = "VINO I2C bus", - .id = I2C_HW_SGI_VINO, .algo = &sgi_algo, .algo_data = &i2c_sgi_vino_data, .owner = THIS_MODULE, diff --git a/linux/drivers/media/video/w9968cf.c b/linux/drivers/media/video/w9968cf.c index 86738d186..5324c092a 100644 --- a/linux/drivers/media/video/w9968cf.c +++ b/linux/drivers/media/video/w9968cf.c @@ -1508,7 +1508,6 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam) }; static struct i2c_adapter adap = { - .id = I2C_HW_SMBUS_W9968CF, .owner = THIS_MODULE, #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .class = I2C_CLASS_TV_ANALOG, diff --git a/linux/drivers/media/video/zoran/zoran_card.c b/linux/drivers/media/video/zoran/zoran_card.c index 85f63116d..217fec9e4 100644 --- a/linux/drivers/media/video/zoran/zoran_card.c +++ b/linux/drivers/media/video/zoran/zoran_card.c @@ -733,7 +733,6 @@ zoran_register_i2c (struct zoran *zr) memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template, sizeof(struct i2c_algo_bit_data)); zr->i2c_algo.data = zr; - zr->i2c_adapter.id = I2C_HW_B_ZR36067; strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), sizeof(zr->i2c_adapter.name)); i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev); -- cgit v1.2.3 From 7485857cbea83448e757c7e60c9612d290cd7e02 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 20 Jun 2009 14:50:39 +0200 Subject: v4l: add V4L2_CAP_RDS_OUTPUT and V4L2_CAP_MODULATOR caps From: Hans Verkuil Add capabilities to describe an FM transmitter device. Priority: normal Signed-off-by: Hans Verkuil --- linux/include/linux/videodev2.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux/include/linux/videodev2.h b/linux/include/linux/videodev2.h index 50dd2a51b..b61c3b61c 100644 --- a/linux/include/linux/videodev2.h +++ b/linux/include/linux/videodev2.h @@ -252,10 +252,12 @@ struct v4l2_capability { #define V4L2_CAP_RDS_CAPTURE 0x00000100 /* RDS data capture */ #define V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000200 /* Can do video output overlay */ #define V4L2_CAP_HW_FREQ_SEEK 0x00000400 /* Can do hardware frequency seek */ +#define V4L2_CAP_RDS_OUTPUT 0x00000800 /* Is an RDS encoder */ #define V4L2_CAP_TUNER 0x00010000 /* has a tuner */ #define V4L2_CAP_AUDIO 0x00020000 /* has audio support */ #define V4L2_CAP_RADIO 0x00040000 /* is a radio device */ +#define V4L2_CAP_MODULATOR 0x00080000 /* has a modulator */ #define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */ #define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */ -- cgit v1.2.3 From 0f244c1179f86853703f68e41073118f58dd47e9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 20 Jun 2009 14:52:22 +0200 Subject: v4l2-ctl: support new RDS and modulator caps. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- v4l2-apps/util/v4l2-ctl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp index 6f6b5e2f8..3355f28e6 100644 --- a/v4l2-apps/util/v4l2-ctl.cpp +++ b/v4l2-apps/util/v4l2-ctl.cpp @@ -1019,8 +1019,12 @@ static std::string cap2s(unsigned cap) s += "\t\tSliced VBI Output\n"; if (cap & V4L2_CAP_RDS_CAPTURE) s += "\t\tRDS Capture\n"; + if (cap & V4L2_CAP_RDS_OUTPUT) + s += "\t\tRDS Output\n"; if (cap & V4L2_CAP_TUNER) s += "\t\tTuner\n"; + if (cap & V4L2_CAP_MODULATOR) + s += "\t\tModulator\n"; if (cap & V4L2_CAP_AUDIO) s += "\t\tAudio\n"; if (cap & V4L2_CAP_RADIO) -- cgit v1.2.3 From d82f58cadb63e341c4fba03c3ce32b61af69108d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 23 Jun 2009 08:39:54 +0200 Subject: v4l2-spec: add missing V4L2_PIX_FMT_OV511 documentation. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- v4l2-spec/pixfmt.sgml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/v4l2-spec/pixfmt.sgml b/v4l2-spec/pixfmt.sgml index 36a6934c7..499cdacda 100644 --- a/v4l2-spec/pixfmt.sgml +++ b/v4l2-spec/pixfmt.sgml @@ -724,9 +724,14 @@ kernel sources in the file Documentation/video4linux/cx2341x/README.hm 'M310' Compressed BGGR Bayer format used by the gspca driver. + + V4L2_PIX_FMT_OV511 + 'O511' + OV511 JPEG format used by the gspca driver. + V4L2_PIX_FMT_OV518 - '0518' + 'O518' OV518 JPEG format used by the gspca driver. -- cgit v1.2.3 From 337e432b2e75655ab7d267af800bd4853f8cf3c9 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 19 Jul 2009 17:19:18 -0400 Subject: cx23885-417: fix broken IOCTL handling From: Michael Krufky IOCTLS will never get handled if we dont connect video_ioctl2 to mpeg_fops.ioctl Priority: high Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx23885/cx23885-417.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux/drivers/media/video/cx23885/cx23885-417.c b/linux/drivers/media/video/cx23885/cx23885-417.c index 9974d9a78..9c5233906 100644 --- a/linux/drivers/media/video/cx23885/cx23885-417.c +++ b/linux/drivers/media/video/cx23885/cx23885-417.c @@ -1683,6 +1683,7 @@ static struct v4l2_file_operations mpeg_fops = { .read = mpeg_read, .poll = mpeg_poll, .mmap = mpeg_mmap, + .ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { -- cgit v1.2.3 From ac5128d842099f6536d4acef3243b0881043e519 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 19 Jul 2009 16:55:35 -0400 Subject: cx23885: check pointers before dereferencing in dprintk macro From: Michael Krufky When enabling debug with v4l_debug set to 2 or greater, the driver OOPS's on startup. Checks dev pointer before dereferencing, in order to prevent this OOPS. Priority: high Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx23885/cx23885-417.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/video/cx23885/cx23885-417.c b/linux/drivers/media/video/cx23885/cx23885-417.c index 9c5233906..02909d7a9 100644 --- a/linux/drivers/media/video/cx23885/cx23885-417.c +++ b/linux/drivers/media/video/cx23885/cx23885-417.c @@ -57,7 +57,8 @@ MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages"); #define dprintk(level, fmt, arg...)\ do { if (v4l_debug >= level) \ - printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg);\ + printk(KERN_DEBUG "%s: " fmt, \ + (dev) ? dev->name : "cx23885[?]", ## arg); \ } while (0) static struct cx23885_tvnorm cx23885_tvnorms[] = { -- cgit v1.2.3 From 1024f9d1fd44ef073b47efac545a4d16b4ced8f7 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 25 Jun 2009 22:43:31 -0400 Subject: cx23885: Remove hardcoded gpio bits from the encoder driver From: Steven Toth The encoder driver has hardcoded GPIO bits set for the HVR1800, regardless of whether it's being used by a HVR1800 or not. I've implemented some generic GPIO manipulation routines and I'm calling them only when appropriate. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/video/cx23885/cx23885-417.c | 57 ++++++++++++++--------- linux/drivers/media/video/cx23885/cx23885-cards.c | 9 ++++ linux/drivers/media/video/cx23885/cx23885-core.c | 8 ++++ linux/drivers/media/video/cx23885/cx23885.h | 9 ++++ 4 files changed, 61 insertions(+), 22 deletions(-) diff --git a/linux/drivers/media/video/cx23885/cx23885-417.c b/linux/drivers/media/video/cx23885/cx23885-417.c index 02909d7a9..fe0b3c41d 100644 --- a/linux/drivers/media/video/cx23885/cx23885-417.c +++ b/linux/drivers/media/video/cx23885/cx23885-417.c @@ -629,6 +629,39 @@ int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value) return retval; } +void mc417_gpio_set(struct cx23885_dev *dev, u32 mask) +{ + u32 val; + + /* Set the gpio value */ + mc417_register_read(dev, 0x900C, &val); + val |= (mask & 0x000ffff); + mc417_register_write(dev, 0x900C, val); +} + +void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask) +{ + u32 val; + + /* Clear the gpio value */ + mc417_register_read(dev, 0x900C, &val); + val &= ~(mask & 0x0000ffff); + mc417_register_write(dev, 0x900C, val); +} + +void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput) +{ + u32 val; + + /* Enable GPIO direction bits */ + mc417_register_read(dev, 0x9020, &val); + if (asoutput) + val |= (mask & 0x0000ffff); + else + val &= ~(mask & 0x0000ffff); + + mc417_register_write(dev, 0x9020, val); +} /* ------------------------------------------------------------------ */ /* MPEG encoder API */ @@ -954,25 +987,8 @@ static int cx23885_load_firmware(struct cx23885_dev *dev) retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); - /* Restore GPIO settings, make sure EIO14 is enabled as an output. */ - dprintk(2, "%s: GPIO output EIO 0-15 was = 0x%x\n", - __func__, gpio_output); - /* Power-up seems to have GPIOs AFU. This was causing digital side - * to fail at power-up. Seems GPIOs should be set to 0x10ff0411 at - * power-up. - * gpio_output |= (1<<14); - */ - /* Note: GPIO14 is specific to the HVR1800 here */ - gpio_output = 0x10ff0411 | (1<<14); - retval |= mc417_register_write(dev, 0x9020, gpio_output | (1<<14)); - dprintk(2, "%s: GPIO output EIO 0-15 now = 0x%x\n", - __func__, gpio_output); - - dprintk(1, "%s: GPIO value EIO 0-15 was = 0x%x\n", - __func__, value); - value |= (1<<14); - dprintk(1, "%s: GPIO value EIO 0-15 now = 0x%x\n", - __func__, value); + /* F/W power up disturbs the GPIOs, restore state */ + retval |= mc417_register_write(dev, 0x9020, gpio_output); retval |= mc417_register_write(dev, 0x900C, value); retval |= mc417_register_read(dev, IVTV_REG_VPU, &value); @@ -1792,9 +1808,6 @@ int cx23885_417_register(struct cx23885_dev *dev) return err; } - /* Initialize MC417 registers */ - cx23885_mc417_init(dev); - printk(KERN_INFO "%s: registered device video%d [mpeg]\n", dev->name, dev->v4l_device->num); diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c index bf426f041..623607b55 100644 --- a/linux/drivers/media/video/cx23885/cx23885-cards.c +++ b/linux/drivers/media/video/cx23885/cx23885-cards.c @@ -583,6 +583,15 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* CX23417 GPIO's */ /* EIO15 Zilog Reset */ /* EIO14 S5H1409/CX24227 Reset */ + mc417_gpio_enable(dev, GPIO_15 | GPIO_14, 1); + + /* Put the demod into reset and protect the eeprom */ + mc417_gpio_clear(dev, GPIO_15 | GPIO_14); + mdelay(100); + + /* Bring the demod and blaster out of reset */ + mc417_gpio_set(dev, GPIO_15 | GPIO_14); + mdelay(100); /* Force the TDA8295A into reset and back */ cx_set(GP0_IO, 0x00040004); diff --git a/linux/drivers/media/video/cx23885/cx23885-core.c b/linux/drivers/media/video/cx23885/cx23885-core.c index 6988361c9..351210e5c 100644 --- a/linux/drivers/media/video/cx23885/cx23885-core.c +++ b/linux/drivers/media/video/cx23885/cx23885-core.c @@ -869,6 +869,14 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) dprintk(1, "%s() radio_type = 0x%x radio_addr = 0x%x\n", __func__, dev->radio_type, dev->radio_addr); + /* The cx23417 encoder has GPIO's that need to be initialised + * before DVB, so that demodulators and tuners are out of + * reset before DVB uses them. + */ + if ((cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) || + (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER)) + cx23885_mc417_init(dev); + /* init hardware */ cx23885_reset(dev); diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h index 2a384e6aa..a507af77a 100644 --- a/linux/drivers/media/video/cx23885/cx23885.h +++ b/linux/drivers/media/video/cx23885/cx23885.h @@ -89,6 +89,12 @@ #define GPIO_7 0x00000080 #define GPIO_8 0x00000100 #define GPIO_9 0x00000200 +#define GPIO_10 0x00000400 +#define GPIO_11 0x00000800 +#define GPIO_12 0x00001000 +#define GPIO_13 0x00002000 +#define GPIO_14 0x00004000 +#define GPIO_15 0x00008000 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ @@ -506,6 +512,9 @@ extern void cx23885_417_check_encoder(struct cx23885_dev *dev); extern void cx23885_mc417_init(struct cx23885_dev *dev); extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value); extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value); +extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask); +extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask); +extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput); /* ----------------------------------------------------------- */ -- cgit v1.2.3 From 9565b71c51910214d91b7ffa96bffa3f2665c75c Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 25 Jun 2009 22:50:39 -0400 Subject: cx23885: Convert existing HVR1800 GPIO calls into new format From: Steven Toth ... improves readability and routes the calls through a specific single point. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/video/cx23885/cx23885-cards.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c index 623607b55..edb99215b 100644 --- a/linux/drivers/media/video/cx23885/cx23885-cards.c +++ b/linux/drivers/media/video/cx23885/cx23885-cards.c @@ -594,11 +594,12 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) mdelay(100); /* Force the TDA8295A into reset and back */ - cx_set(GP0_IO, 0x00040004); + cx23885_gpio_enable(dev, GPIO_2, 1); + cx23885_gpio_set(dev, GPIO_2); mdelay(20); - cx_clear(GP0_IO, 0x00000004); + cx23885_gpio_clear(dev, GPIO_2); mdelay(20); - cx_set(GP0_IO, 0x00040004); + cx23885_gpio_set(dev, GPIO_2); mdelay(20); break; case CX23885_BOARD_HAUPPAUGE_HVR1200: -- cgit v1.2.3 From a4c5c6c582583fcb31b975ebacd779ea0c8e5eae Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2009 08:56:09 +0200 Subject: v4l2-common: fix uninitialized variable From: Hans Verkuil Fix compiler warning for uninitialized variable when compiling for pre 2.6.26 kernels. Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/v4l2-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c index ab91734b1..864ee0772 100644 --- a/linux/drivers/media/video/v4l2-common.c +++ b/linux/drivers/media/video/v4l2-common.c @@ -1066,7 +1066,7 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_cfg(struct v4l2_device *v4l2_dev, u8 addr, const unsigned short *probe_addrs) { struct v4l2_subdev *sd = NULL; - struct i2c_client *client; + struct i2c_client *client = NULL; BUG_ON(!v4l2_dev); -- cgit v1.2.3 From e4340839f4ea666cb09caf45d10d30a18a17d2f9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2009 10:39:36 +0200 Subject: em28xx: enable new-style i2c API for kernels >= 2.6.26, not 2.6.22. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/em28xx/em28xx-i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c index 1cdd88361..24eebd4a3 100644 --- a/linux/drivers/media/video/em28xx/em28xx-i2c.c +++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c @@ -461,7 +461,7 @@ static struct i2c_algorithm em28xx_algo = { static struct i2c_adapter em28xx_adap_template = { .owner = THIS_MODULE, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .class = I2C_CLASS_TV_ANALOG, #endif .name = "em28xx", -- cgit v1.2.3 From b59d997727eebbc353fdcf766af87931971f2c55 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Fri, 26 Jun 2009 15:00:59 -0400 Subject: au0828: fix i2c for kernels prior to 2.6.26 From: Michael Krufky Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/video/au0828/au0828-i2c.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux/drivers/media/video/au0828/au0828-i2c.c b/linux/drivers/media/video/au0828/au0828-i2c.c index a589d2ccb..bbe38a7a2 100644 --- a/linux/drivers/media/video/au0828/au0828-i2c.c +++ b/linux/drivers/media/video/au0828/au0828-i2c.c @@ -326,6 +326,9 @@ static struct i2c_adapter au0828_i2c_adap_template = { .owner = THIS_MODULE, .id = I2C_HW_B_AU0828, .algo = &au0828_i2c_algo_template, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) + .class = I2C_CLASS_TV_ANALOG, +#endif }; static struct i2c_client au0828_i2c_client_template = { -- cgit v1.2.3 From 515fc60834c8e3e51c3fb97e362c74a3324d817e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 28 Jun 2009 13:37:50 +0200 Subject: uvcvideo: Restructure the driver to support multiple simultaneous streams. From: Laurent Pinchart As a first step towards multiple streaming interfaces support, reorganize the driver's data structures to cleanly separate video control and video streaming data. Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_driver.c | 174 ++++++------ linux/drivers/media/video/uvc/uvc_isight.c | 7 +- linux/drivers/media/video/uvc/uvc_v4l2.c | 195 +++++++------- linux/drivers/media/video/uvc/uvc_video.c | 415 +++++++++++++++-------------- linux/drivers/media/video/uvc/uvcvideo.h | 86 +++--- 5 files changed, 456 insertions(+), 421 deletions(-) diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index 1d09751a3..70d15a926 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -551,6 +551,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, } mutex_init(&streaming->mutex); + streaming->dev = dev; streaming->intf = usb_get_intf(intf); streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; @@ -751,7 +752,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, streaming->maxpsize = psize; } - list_add_tail(&streaming->list, &dev->streaming); + list_add_tail(&streaming->list, &dev->streams); return 0; error: @@ -1167,13 +1168,75 @@ next_descriptor: */ static void uvc_unregister_video(struct uvc_device *dev) { - if (dev->video.vdev) { - if (dev->video.vdev->minor == -1) - video_device_release(dev->video.vdev); + struct uvc_streaming *streaming; + + list_for_each_entry(streaming, &dev->streams, list) { + if (streaming->vdev == NULL) + continue; + + if (streaming->vdev->minor == -1) + video_device_release(streaming->vdev); else - video_unregister_device(dev->video.vdev); - dev->video.vdev = NULL; + video_unregister_device(streaming->vdev); + streaming->vdev = NULL; + } +} + +static int uvc_register_video(struct uvc_device *dev, + struct uvc_streaming *stream) +{ + struct video_device *vdev; + struct uvc_entity *term; + int ret; + + if (uvc_trace_param & UVC_TRACE_PROBE) { + uvc_printk(KERN_INFO, "Found a valid video chain ("); + list_for_each_entry(term, &dev->video.iterms, chain) { + printk("%d", term->id); + if (term->chain.next != &dev->video.iterms) + printk(","); + } + printk(" -> %d).\n", dev->video.oterm->id); + } + + /* Initialize the streaming interface with default streaming + * parameters. + */ + ret = uvc_video_init(stream); + if (ret < 0) { + uvc_printk(KERN_ERR, "Failed to initialize the device " + "(%d).\n", ret); + return ret; + } + + /* Register the device with V4L. */ + vdev = video_device_alloc(); + if (vdev == NULL) + return -1; + + /* We already hold a reference to dev->udev. The video device will be + * unregistered before the reference is released, so we don't need to + * get another one. + */ + vdev->parent = &dev->intf->dev; + vdev->minor = -1; + vdev->fops = &uvc_fops; + vdev->release = video_device_release; + strlcpy(vdev->name, dev->name, sizeof vdev->name); + + /* Set the driver data before calling video_register_device, otherwise + * uvc_v4l2_open might race us. + */ + stream->vdev = vdev; + video_set_drvdata(vdev, stream); + + if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) { + stream->vdev = NULL; + video_device_release(vdev); + return -1; } + + return 0; } /* @@ -1419,7 +1482,7 @@ static int uvc_scan_chain(struct uvc_video_device *video) } /* - * Register the video devices. + * Scan the device for video chains and register video devices. * * The driver currently supports a single video device per control interface * only. The terminal and units must match the following structure: @@ -1432,15 +1495,14 @@ static int uvc_scan_chain(struct uvc_video_device *video) * Extension Units connected to the main chain as single-unit branches are * also supported. */ -static int uvc_register_video(struct uvc_device *dev) +static int uvc_scan_device(struct uvc_device *dev) { - struct video_device *vdev; struct uvc_entity *term; - int found = 0, ret; + int found = 0; /* Check if the control interface matches the structure we expect. */ list_for_each_entry(term, &dev->entities, list) { - struct uvc_streaming *streaming; + struct uvc_streaming *stream; if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term)) continue; @@ -1454,17 +1516,14 @@ static int uvc_register_video(struct uvc_device *dev) if (uvc_scan_chain(&dev->video) < 0) continue; - list_for_each_entry(streaming, &dev->streaming, list) { - if (streaming->header.bTerminalLink == + list_for_each_entry(stream, &dev->streams, list) { + if (stream->header.bTerminalLink == dev->video.sterm->id) { - dev->video.streaming = streaming; + uvc_register_video(dev, stream); found = 1; break; } } - - if (found) - break; } if (!found) { @@ -1472,55 +1531,6 @@ static int uvc_register_video(struct uvc_device *dev) return -1; } - if (uvc_trace_param & UVC_TRACE_PROBE) { - uvc_printk(KERN_INFO, "Found a valid video chain ("); - list_for_each_entry(term, &dev->video.iterms, chain) { - printk("%d", term->id); - if (term->chain.next != &dev->video.iterms) - printk(","); - } - printk(" -> %d).\n", dev->video.oterm->id); - } - - /* Initialize the video buffers queue. */ - uvc_queue_init(&dev->video.queue, dev->video.streaming->type); - - /* Initialize the streaming interface with default streaming - * parameters. - */ - if ((ret = uvc_video_init(&dev->video)) < 0) { - uvc_printk(KERN_ERR, "Failed to initialize the device " - "(%d).\n", ret); - return ret; - } - - /* Register the device with V4L. */ - vdev = video_device_alloc(); - if (vdev == NULL) - return -1; - - /* We already hold a reference to dev->udev. The video device will be - * unregistered before the reference is released, so we don't need to - * get another one. - */ - vdev->parent = &dev->intf->dev; - vdev->minor = -1; - vdev->fops = &uvc_fops; - vdev->release = video_device_release; - strlcpy(vdev->name, dev->name, sizeof vdev->name); - - /* Set the driver data before calling video_register_device, otherwise - * uvc_v4l2_open might race us. - */ - dev->video.vdev = vdev; - video_set_drvdata(vdev, &dev->video); - - if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) { - dev->video.vdev = NULL; - video_device_release(vdev); - return -1; - } - return 0; } @@ -1559,7 +1569,7 @@ void uvc_delete(struct kref *kref) kfree(entity); } - list_for_each_safe(p, n, &dev->streaming) { + list_for_each_safe(p, n, &dev->streams) { struct uvc_streaming *streaming; streaming = list_entry(p, struct uvc_streaming, list); usb_driver_release_interface(&uvc_driver.driver, @@ -1593,7 +1603,7 @@ static int uvc_probe(struct usb_interface *intf, return -ENOMEM; INIT_LIST_HEAD(&dev->entities); - INIT_LIST_HEAD(&dev->streaming); + INIT_LIST_HEAD(&dev->streams); kref_init(&dev->kref); atomic_set(&dev->users, 0); @@ -1634,8 +1644,8 @@ static int uvc_probe(struct usb_interface *intf, if (uvc_ctrl_init_device(dev) < 0) goto error; - /* Register the video devices. */ - if (uvc_register_video(dev) < 0) + /* Scan the device for video chains and register video devices. */ + if (uvc_scan_device(dev) < 0) goto error; /* Save our data pointer in the interface data. */ @@ -1689,6 +1699,7 @@ static void uvc_disconnect(struct usb_interface *intf) static int uvc_suspend(struct usb_interface *intf, pm_message_t message) { struct uvc_device *dev = usb_get_intfdata(intf); + struct uvc_streaming *stream; uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n", intf->cur_altsetting->desc.bInterfaceNumber); @@ -1698,18 +1709,20 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message) UVC_SC_VIDEOCONTROL) return uvc_status_suspend(dev); - if (dev->video.streaming->intf != intf) { - uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB " - "interface mismatch.\n"); - return -EINVAL; + list_for_each_entry(stream, &dev->streams, list) { + if (stream->intf == intf) + return uvc_video_suspend(stream); } - return uvc_video_suspend(&dev->video); + uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface " + "mismatch.\n"); + return -EINVAL; } static int __uvc_resume(struct usb_interface *intf, int reset) { struct uvc_device *dev = usb_get_intfdata(intf); + struct uvc_streaming *stream; uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", intf->cur_altsetting->desc.bInterfaceNumber); @@ -1726,13 +1739,14 @@ static int __uvc_resume(struct usb_interface *intf, int reset) return uvc_status_resume(dev); } - if (dev->video.streaming->intf != intf) { - uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB " - "interface mismatch.\n"); - return -EINVAL; + list_for_each_entry(stream, &dev->streams, list) { + if (stream->intf == intf) + return uvc_video_resume(stream); } - return uvc_video_resume(&dev->video); + uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface " + "mismatch.\n"); + return -EINVAL; } static int uvc_resume(struct usb_interface *intf) diff --git a/linux/drivers/media/video/uvc/uvc_isight.c b/linux/drivers/media/video/uvc/uvc_isight.c index 436f46268..a9285b570 100644 --- a/linux/drivers/media/video/uvc/uvc_isight.c +++ b/linux/drivers/media/video/uvc/uvc_isight.c @@ -99,7 +99,7 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, return 0; } -void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, +void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, struct uvc_buffer *buf) { int ret, i; @@ -120,7 +120,7 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, * processes the data of the first payload of the new frame. */ do { - ret = isight_decode(&video->queue, buf, + ret = isight_decode(&stream->queue, buf, urb->transfer_buffer + urb->iso_frame_desc[i].offset, urb->iso_frame_desc[i].actual_length); @@ -130,7 +130,8 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, if (buf->state == UVC_BUF_STATE_DONE || buf->state == UVC_BUF_STATE_ERROR) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, + buf); } while (ret == -EAGAIN); } } diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c index 31a9ed4ff..3379e1320 100644 --- a/linux/drivers/media/video/uvc/uvc_v4l2.c +++ b/linux/drivers/media/video/uvc/uvc_v4l2.c @@ -103,7 +103,7 @@ static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval) return interval; } -static int uvc_v4l2_try_format(struct uvc_video_device *video, +static int uvc_v4l2_try_format(struct uvc_streaming *stream, struct v4l2_format *fmt, struct uvc_streaming_control *probe, struct uvc_format **uvc_format, struct uvc_frame **uvc_frame) { @@ -116,7 +116,7 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video, int ret = 0; __u8 *fcc; - if (fmt->type != video->streaming->type) + if (fmt->type != stream->type) return -EINVAL; fcc = (__u8 *)&fmt->fmt.pix.pixelformat; @@ -126,8 +126,8 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video, fmt->fmt.pix.width, fmt->fmt.pix.height); /* Check if the hardware supports the requested format. */ - for (i = 0; i < video->streaming->nformats; ++i) { - format = &video->streaming->format[i]; + for (i = 0; i < stream->nformats; ++i) { + format = &stream->format[i]; if (format->fcc == fmt->fmt.pix.pixelformat) break; } @@ -191,12 +191,13 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video, * developers test their webcams with the Linux driver as well as with * the Windows driver). */ - if (video->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS) + if (stream->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS) probe->dwMaxVideoFrameSize = - video->streaming->ctrl.dwMaxVideoFrameSize; + stream->ctrl.dwMaxVideoFrameSize; /* Probe the device. */ - if ((ret = uvc_probe_video(video, probe)) < 0) + ret = uvc_probe_video(stream, probe); + if (ret < 0) goto done; fmt->fmt.pix.width = frame->wWidth; @@ -216,13 +217,13 @@ done: return ret; } -static int uvc_v4l2_get_format(struct uvc_video_device *video, +static int uvc_v4l2_get_format(struct uvc_streaming *stream, struct v4l2_format *fmt) { - struct uvc_format *format = video->streaming->cur_format; - struct uvc_frame *frame = video->streaming->cur_frame; + struct uvc_format *format = stream->cur_format; + struct uvc_frame *frame = stream->cur_frame; - if (fmt->type != video->streaming->type) + if (fmt->type != stream->type) return -EINVAL; if (format == NULL || frame == NULL) @@ -233,14 +234,14 @@ static int uvc_v4l2_get_format(struct uvc_video_device *video, fmt->fmt.pix.height = frame->wHeight; fmt->fmt.pix.field = V4L2_FIELD_NONE; fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; - fmt->fmt.pix.sizeimage = video->streaming->ctrl.dwMaxVideoFrameSize; + fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize; fmt->fmt.pix.colorspace = format->colorspace; fmt->fmt.pix.priv = 0; return 0; } -static int uvc_v4l2_set_format(struct uvc_video_device *video, +static int uvc_v4l2_set_format(struct uvc_streaming *stream, struct v4l2_format *fmt) { struct uvc_streaming_control probe; @@ -248,39 +249,39 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video, struct uvc_frame *frame; int ret; - if (fmt->type != video->streaming->type) + if (fmt->type != stream->type) return -EINVAL; - if (uvc_queue_allocated(&video->queue)) + if (uvc_queue_allocated(&stream->queue)) return -EBUSY; - ret = uvc_v4l2_try_format(video, fmt, &probe, &format, &frame); + ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame); if (ret < 0) return ret; - memcpy(&video->streaming->ctrl, &probe, sizeof probe); - video->streaming->cur_format = format; - video->streaming->cur_frame = frame; + memcpy(&stream->ctrl, &probe, sizeof probe); + stream->cur_format = format; + stream->cur_frame = frame; return 0; } -static int uvc_v4l2_get_streamparm(struct uvc_video_device *video, +static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream, struct v4l2_streamparm *parm) { uint32_t numerator, denominator; - if (parm->type != video->streaming->type) + if (parm->type != stream->type) return -EINVAL; - numerator = video->streaming->ctrl.dwFrameInterval; + numerator = stream->ctrl.dwFrameInterval; denominator = 10000000; uvc_simplify_fraction(&numerator, &denominator, 8, 333); memset(parm, 0, sizeof *parm); - parm->type = video->streaming->type; + parm->type = stream->type; - if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; parm->parm.capture.capturemode = 0; parm->parm.capture.timeperframe.numerator = numerator; @@ -297,19 +298,19 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video, return 0; } -static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, +static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, struct v4l2_streamparm *parm) { - struct uvc_frame *frame = video->streaming->cur_frame; + struct uvc_frame *frame = stream->cur_frame; struct uvc_streaming_control probe; struct v4l2_fract timeperframe; uint32_t interval; int ret; - if (parm->type != video->streaming->type) + if (parm->type != stream->type) return -EINVAL; - if (uvc_queue_streaming(&video->queue)) + if (uvc_queue_streaming(&stream->queue)) return -EBUSY; if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -317,7 +318,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, else timeperframe = parm->parm.output.timeperframe; - memcpy(&probe, &video->streaming->ctrl, sizeof probe); + memcpy(&probe, &stream->ctrl, sizeof probe); interval = uvc_fraction_to_interval(timeperframe.numerator, timeperframe.denominator); @@ -326,10 +327,11 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, probe.dwFrameInterval = uvc_try_frame_interval(frame, interval); /* Probe the device with the new settings. */ - if ((ret = uvc_probe_video(video, &probe)) < 0) + ret = uvc_probe_video(stream, &probe); + if (ret < 0) return ret; - memcpy(&video->streaming->ctrl, &probe, sizeof probe); + memcpy(&stream->ctrl, &probe, sizeof probe); /* Return the actual frame period. */ timeperframe.numerator = probe.dwFrameInterval; @@ -382,8 +384,8 @@ static int uvc_acquire_privileges(struct uvc_fh *handle) /* Check if the device already has a privileged handle. */ mutex_lock(&uvc_driver.open_mutex); - if (atomic_inc_return(&handle->device->active) != 1) { - atomic_dec(&handle->device->active); + if (atomic_inc_return(&handle->stream->active) != 1) { + atomic_dec(&handle->stream->active); ret = -EBUSY; goto done; } @@ -398,7 +400,7 @@ done: static void uvc_dismiss_privileges(struct uvc_fh *handle) { if (handle->state == UVC_HANDLE_ACTIVE) - atomic_dec(&handle->device->active); + atomic_dec(&handle->stream->active); handle->state = UVC_HANDLE_PASSIVE; } @@ -414,21 +416,21 @@ static int uvc_has_privileges(struct uvc_fh *handle) static int uvc_v4l2_open(struct file *file) { - struct uvc_video_device *video; + struct uvc_streaming *stream; struct uvc_fh *handle; int ret = 0; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); mutex_lock(&uvc_driver.open_mutex); - video = video_drvdata(file); + stream = video_drvdata(file); - if (video->dev->state & UVC_DEV_DISCONNECTED) { + if (stream->dev->state & UVC_DEV_DISCONNECTED) { ret = -ENODEV; goto done; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) - ret = usb_autopm_get_interface(video->dev->intf); + ret = usb_autopm_get_interface(stream->dev->intf); if (ret < 0) goto done; #endif @@ -437,28 +439,30 @@ static int uvc_v4l2_open(struct file *file) handle = kzalloc(sizeof *handle, GFP_KERNEL); if (handle == NULL) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) - usb_autopm_put_interface(video->dev->intf); + usb_autopm_put_interface(stream->dev->intf); #endif ret = -ENOMEM; goto done; } - if (atomic_inc_return(&video->dev->users) == 1) { - if ((ret = uvc_status_start(video->dev)) < 0) { + if (atomic_inc_return(&stream->dev->users) == 1) { + ret = uvc_status_start(stream->dev); + if (ret < 0) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) - usb_autopm_put_interface(video->dev->intf); + usb_autopm_put_interface(stream->dev->intf); #endif - atomic_dec(&video->dev->users); + atomic_dec(&stream->dev->users); kfree(handle); goto done; } } - handle->device = video; + handle->video = &stream->dev->video; + handle->stream = stream; handle->state = UVC_HANDLE_PASSIVE; file->private_data = handle; - kref_get(&video->dev->kref); + kref_get(&stream->dev->kref); done: mutex_unlock(&uvc_driver.open_mutex); @@ -467,20 +471,20 @@ done: static int uvc_v4l2_release(struct file *file) { - struct uvc_video_device *video = video_drvdata(file); struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_streaming *stream = handle->stream; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); /* Only free resources if this is a privileged handle. */ if (uvc_has_privileges(handle)) { - uvc_video_enable(video, 0); + uvc_video_enable(stream, 0); - mutex_lock(&video->queue.mutex); - if (uvc_free_buffers(&video->queue) < 0) + mutex_lock(&stream->queue.mutex); + if (uvc_free_buffers(&stream->queue) < 0) uvc_printk(KERN_ERR, "uvc_v4l2_release: Unable to " "free buffers.\n"); - mutex_unlock(&video->queue.mutex); + mutex_unlock(&stream->queue.mutex); } /* Release the file handle. */ @@ -488,21 +492,22 @@ static int uvc_v4l2_release(struct file *file) kfree(handle); file->private_data = NULL; - if (atomic_dec_return(&video->dev->users) == 0) - uvc_status_stop(video->dev); + if (atomic_dec_return(&stream->dev->users) == 0) + uvc_status_stop(stream->dev); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) - usb_autopm_put_interface(video->dev->intf); + usb_autopm_put_interface(stream->dev->intf); #endif - kref_put(&video->dev->kref, uvc_delete); + kref_put(&stream->dev->kref, uvc_delete); return 0; } static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); - struct uvc_video_device *video = video_get_drvdata(vdev); struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_video_device *video = handle->video; + struct uvc_streaming *stream = handle->stream; long ret = 0; switch (cmd) { @@ -514,10 +519,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(cap, 0, sizeof *cap); strlcpy(cap->driver, "uvcvideo", sizeof cap->driver); strlcpy(cap->card, vdev->name, sizeof cap->card); - usb_make_path(video->dev->udev, + usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info)); cap->version = DRIVER_VERSION_NUMBER; - if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; else @@ -711,15 +716,15 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) enum v4l2_buf_type type = fmt->type; __u32 index = fmt->index; - if (fmt->type != video->streaming->type || - fmt->index >= video->streaming->nformats) + if (fmt->type != stream->type || + fmt->index >= stream->nformats) return -EINVAL; memset(fmt, 0, sizeof(*fmt)); fmt->index = index; fmt->type = type; - format = &video->streaming->format[fmt->index]; + format = &stream->format[fmt->index]; fmt->flags = 0; if (format->flags & UVC_FMT_FLAG_COMPRESSED) fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; @@ -737,17 +742,17 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - return uvc_v4l2_try_format(video, arg, &probe, NULL, NULL); + return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL); } case VIDIOC_S_FMT: if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - return uvc_v4l2_set_format(video, arg); + return uvc_v4l2_set_format(stream, arg); case VIDIOC_G_FMT: - return uvc_v4l2_get_format(video, arg); + return uvc_v4l2_get_format(stream, arg); /* Frame size enumeration */ case VIDIOC_ENUM_FRAMESIZES: @@ -758,10 +763,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) int i; /* Look for the given pixel format */ - for (i = 0; i < video->streaming->nformats; i++) { - if (video->streaming->format[i].fcc == + for (i = 0; i < stream->nformats; i++) { + if (stream->format[i].fcc == fsize->pixel_format) { - format = &video->streaming->format[i]; + format = &stream->format[i]; break; } } @@ -787,10 +792,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) int i; /* Look for the given pixel format and frame size */ - for (i = 0; i < video->streaming->nformats; i++) { - if (video->streaming->format[i].fcc == + for (i = 0; i < stream->nformats; i++) { + if (stream->format[i].fcc == fival->pixel_format) { - format = &video->streaming->format[i]; + format = &stream->format[i]; break; } } @@ -840,21 +845,21 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) /* Get & Set streaming parameters */ case VIDIOC_G_PARM: - return uvc_v4l2_get_streamparm(video, arg); + return uvc_v4l2_get_streamparm(stream, arg); case VIDIOC_S_PARM: if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - return uvc_v4l2_set_streamparm(video, arg); + return uvc_v4l2_set_streamparm(stream, arg); /* Cropping and scaling */ case VIDIOC_CROPCAP: { struct v4l2_cropcap *ccap = arg; - struct uvc_frame *frame = video->streaming->cur_frame; + struct uvc_frame *frame = stream->cur_frame; - if (ccap->type != video->streaming->type) + if (ccap->type != stream->type) return -EINVAL; ccap->bounds.left = 0; @@ -878,16 +883,16 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_requestbuffers *rb = arg; unsigned int bufsize = - video->streaming->ctrl.dwMaxVideoFrameSize; + stream->ctrl.dwMaxVideoFrameSize; - if (rb->type != video->streaming->type || + if (rb->type != stream->type || rb->memory != V4L2_MEMORY_MMAP) return -EINVAL; if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - ret = uvc_alloc_buffers(&video->queue, rb->count, bufsize); + ret = uvc_alloc_buffers(&stream->queue, rb->count, bufsize); if (ret < 0) return ret; @@ -900,39 +905,40 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_buffer *buf = arg; - if (buf->type != video->streaming->type) + if (buf->type != stream->type) return -EINVAL; if (!uvc_has_privileges(handle)) return -EBUSY; - return uvc_query_buffer(&video->queue, buf); + return uvc_query_buffer(&stream->queue, buf); } case VIDIOC_QBUF: if (!uvc_has_privileges(handle)) return -EBUSY; - return uvc_queue_buffer(&video->queue, arg); + return uvc_queue_buffer(&stream->queue, arg); case VIDIOC_DQBUF: if (!uvc_has_privileges(handle)) return -EBUSY; - return uvc_dequeue_buffer(&video->queue, arg, + return uvc_dequeue_buffer(&stream->queue, arg, file->f_flags & O_NONBLOCK); case VIDIOC_STREAMON: { int *type = arg; - if (*type != video->streaming->type) + if (*type != stream->type) return -EINVAL; if (!uvc_has_privileges(handle)) return -EBUSY; - if ((ret = uvc_video_enable(video, 1)) < 0) + ret = uvc_video_enable(stream, 1); + if (ret < 0) return ret; break; } @@ -941,13 +947,13 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { int *type = arg; - if (*type != video->streaming->type) + if (*type != stream->type) return -EINVAL; if (!uvc_has_privileges(handle)) return -EBUSY; - return uvc_video_enable(video, 0); + return uvc_video_enable(stream, 0); } /* Analog video standards make no sense for digital cameras. */ @@ -1078,7 +1084,9 @@ static struct vm_operations_struct uvc_vm_ops = { static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) { - struct uvc_video_device *video = video_drvdata(file); + struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_streaming *stream = handle->stream; + struct uvc_video_queue *queue = &stream->queue; struct uvc_buffer *uninitialized_var(buffer); struct page *page; unsigned long addr, start, size; @@ -1090,15 +1098,15 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) start = vma->vm_start; size = vma->vm_end - vma->vm_start; - mutex_lock(&video->queue.mutex); + mutex_lock(&queue->mutex); - for (i = 0; i < video->queue.count; ++i) { - buffer = &video->queue.buffer[i]; + for (i = 0; i < queue->count; ++i) { + buffer = &queue->buffer[i]; if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) break; } - if (i == video->queue.count || size != video->queue.buf_size) { + if (i == queue->count || size != queue->buf_size) { ret = -EINVAL; goto done; } @@ -1109,7 +1117,7 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) */ vma->vm_flags |= VM_IO; - addr = (unsigned long)video->queue.mem + buffer->buf.m.offset; + addr = (unsigned long)queue->mem + buffer->buf.m.offset; while (size > 0) { page = vmalloc_to_page((void *)addr); if ((ret = vm_insert_page(vma, start, page)) < 0) @@ -1125,17 +1133,18 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) uvc_vm_open(vma); done: - mutex_unlock(&video->queue.mutex); + mutex_unlock(&queue->mutex); return ret; } static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) { - struct uvc_video_device *video = video_drvdata(file); + struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_streaming *stream = handle->stream; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); - return uvc_queue_poll(&video->queue, file, wait); + return uvc_queue_poll(&stream->queue, file, wait); } const struct v4l2_file_operations uvc_fops = { diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c index f4f4e1bb3..38cb7a367 100644 --- a/linux/drivers/media/video/uvc/uvc_video.c +++ b/linux/drivers/media/video/uvc/uvc_video.c @@ -61,7 +61,7 @@ int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, return 0; } -static void uvc_fixup_video_ctrl(struct uvc_video_device *video, +static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl) { struct uvc_format *format; @@ -69,10 +69,10 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video, unsigned int i; if (ctrl->bFormatIndex <= 0 || - ctrl->bFormatIndex > video->streaming->nformats) + ctrl->bFormatIndex > stream->nformats) return; - format = &video->streaming->format[ctrl->bFormatIndex - 1]; + format = &stream->format[ctrl->bFormatIndex - 1]; for (i = 0; i < format->nframes; ++i) { if (format->frame[i].bFrameIndex == ctrl->bFrameIndex) { @@ -86,12 +86,12 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video, if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) || (ctrl->dwMaxVideoFrameSize == 0 && - video->dev->uvc_version < 0x0110)) + stream->dev->uvc_version < 0x0110)) ctrl->dwMaxVideoFrameSize = frame->dwMaxVideoFrameBufferSize; - if (video->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH && - video->streaming->intf->num_altsetting > 1) { + if (stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH && + stream->intf->num_altsetting > 1) { u32 interval; u32 bandwidth; @@ -108,7 +108,7 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video, bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp; bandwidth *= 10000000 / interval + 1; bandwidth /= 1000; - if (video->dev->udev->speed == USB_SPEED_HIGH) + if (stream->dev->udev->speed == USB_SPEED_HIGH) bandwidth /= 8; bandwidth += 12; @@ -116,14 +116,14 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video, } } -static int uvc_get_video_ctrl(struct uvc_video_device *video, +static int uvc_get_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl, int probe, __u8 query) { __u8 *data; __u16 size; int ret; - size = video->dev->uvc_version >= 0x0110 ? 34 : 26; + size = stream->dev->uvc_version >= 0x0110 ? 34 : 26; data = kmalloc(size, GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -131,7 +131,7 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, if ((video->dev->quirks & UVC_QUIRK_PROBE_DEF) && query == UVC_GET_DEF) return -EIO; - ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum, + ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum, probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, size, UVC_CTRL_STREAMING_TIMEOUT); @@ -140,7 +140,7 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, * answer a GET_MIN or GET_MAX request with the wCompQuality * field only. */ - uvc_warn_once(video->dev, UVC_WARN_MINMAX, "UVC non " + uvc_warn_once(stream->dev, UVC_WARN_MINMAX, "UVC non " "compliance - GET_MIN/MAX(PROBE) incorrectly " "supported. Enabling workaround.\n"); memset(ctrl, 0, sizeof ctrl); @@ -152,7 +152,7 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, * video probe control. Warn once and return, the caller will * fall back to GET_CUR. */ - uvc_warn_once(video->dev, UVC_WARN_PROBE_DEF, "UVC non " + uvc_warn_once(stream->dev, UVC_WARN_PROBE_DEF, "UVC non " "compliance - GET_DEF(PROBE) not supported. " "Enabling workaround.\n"); ret = -EIO; @@ -184,7 +184,7 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, ctrl->bMinVersion = data[32]; ctrl->bMaxVersion = data[33]; } else { - ctrl->dwClockFrequency = video->dev->clock_frequency; + ctrl->dwClockFrequency = stream->dev->clock_frequency; ctrl->bmFramingInfo = 0; ctrl->bPreferedVersion = 0; ctrl->bMinVersion = 0; @@ -195,7 +195,7 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, * dwMaxPayloadTransferSize fields. Try to get the value from the * format and frame descriptors. */ - uvc_fixup_video_ctrl(video, ctrl); + uvc_fixup_video_ctrl(stream, ctrl); ret = 0; out: @@ -203,14 +203,14 @@ out: return ret; } -static int uvc_set_video_ctrl(struct uvc_video_device *video, +static int uvc_set_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl, int probe) { __u8 *data; __u16 size; int ret; - size = video->dev->uvc_version >= 0x0110 ? 34 : 26; + size = stream->dev->uvc_version >= 0x0110 ? 34 : 26; data = kzalloc(size, GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -235,8 +235,7 @@ static int uvc_set_video_ctrl(struct uvc_video_device *video, data[33] = ctrl->bMaxVersion; } - ret = __uvc_query_ctrl(video->dev, UVC_SET_CUR, 0, - video->streaming->intfnum, + ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum, probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, size, UVC_CTRL_STREAMING_TIMEOUT); if (ret != size) { @@ -250,7 +249,7 @@ static int uvc_set_video_ctrl(struct uvc_video_device *video, return ret; } -int uvc_probe_video(struct uvc_video_device *video, +int uvc_probe_video(struct uvc_streaming *stream, struct uvc_streaming_control *probe) { struct uvc_streaming_control probe_min, probe_max; @@ -258,7 +257,7 @@ int uvc_probe_video(struct uvc_video_device *video, unsigned int i; int ret; - mutex_lock(&video->streaming->mutex); + mutex_lock(&stream->mutex); /* Perform probing. The device should adjust the requested values * according to its capabilities. However, some devices, namely the @@ -267,15 +266,16 @@ int uvc_probe_video(struct uvc_video_device *video, * that reason, if the needed bandwidth exceeds the maximum available * bandwidth, try to lower the quality. */ - if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0) + ret = uvc_set_video_ctrl(stream, probe, 1); + if (ret < 0) goto done; /* Get the minimum and maximum values for compression settings. */ - if (!(video->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) { - ret = uvc_get_video_ctrl(video, &probe_min, 1, UVC_GET_MIN); + if (!(stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) { + ret = uvc_get_video_ctrl(stream, &probe_min, 1, UVC_GET_MIN); if (ret < 0) goto done; - ret = uvc_get_video_ctrl(video, &probe_max, 1, UVC_GET_MAX); + ret = uvc_get_video_ctrl(stream, &probe_max, 1, UVC_GET_MAX); if (ret < 0) goto done; @@ -283,21 +283,21 @@ int uvc_probe_video(struct uvc_video_device *video, } for (i = 0; i < 2; ++i) { - ret = uvc_set_video_ctrl(video, probe, 1); + ret = uvc_set_video_ctrl(stream, probe, 1); if (ret < 0) goto done; - ret = uvc_get_video_ctrl(video, probe, 1, UVC_GET_CUR); + ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR); if (ret < 0) goto done; - if (video->streaming->intf->num_altsetting == 1) + if (stream->intf->num_altsetting == 1) break; bandwidth = probe->dwMaxPayloadTransferSize; - if (bandwidth <= video->streaming->maxpsize) + if (bandwidth <= stream->maxpsize) break; - if (video->dev->quirks & UVC_QUIRK_PROBE_MINMAX) { + if (stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX) { ret = -ENOSPC; goto done; } @@ -310,14 +310,14 @@ int uvc_probe_video(struct uvc_video_device *video, } done: - mutex_unlock(&video->streaming->mutex); + mutex_unlock(&stream->mutex); return ret; } -int uvc_commit_video(struct uvc_video_device *video, +int uvc_commit_video(struct uvc_streaming *stream, struct uvc_streaming_control *probe) { - return uvc_set_video_ctrl(video, probe, 0); + return uvc_set_video_ctrl(stream, probe, 0); } /* ------------------------------------------------------------------------ @@ -369,7 +369,7 @@ int uvc_commit_video(struct uvc_video_device *video, * to be called with a NULL buf parameter. uvc_video_decode_data and * uvc_video_decode_end will never be called with a NULL buffer. */ -static int uvc_video_decode_start(struct uvc_video_device *video, +static int uvc_video_decode_start(struct uvc_streaming *stream, struct uvc_buffer *buf, const __u8 *data, int len) { __u8 fid; @@ -395,25 +395,25 @@ static int uvc_video_decode_start(struct uvc_video_device *video, * NULL. */ if (buf == NULL) { - video->last_fid = fid; + stream->last_fid = fid; return -ENODATA; } /* Synchronize to the input stream by waiting for the FID bit to be * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE. - * video->last_fid is initialized to -1, so the first isochronous + * stream->last_fid is initialized to -1, so the first isochronous * frame will always be in sync. * - * If the device doesn't toggle the FID bit, invert video->last_fid + * If the device doesn't toggle the FID bit, invert stream->last_fid * when the EOF bit is set to force synchronisation on the next packet. */ if (buf->state != UVC_BUF_STATE_ACTIVE) { - if (fid == video->last_fid) { + if (fid == stream->last_fid) { uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of " "sync).\n"); - if ((video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) && + if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) && (data[1] & UVC_STREAM_EOF)) - video->last_fid ^= UVC_STREAM_FID; + stream->last_fid ^= UVC_STREAM_FID; return -ENODATA; } @@ -428,7 +428,7 @@ static int uvc_video_decode_start(struct uvc_video_device *video, * last payload can be lost anyway). We thus must check if the FID has * been toggled. * - * video->last_fid is initialized to -1, so the first isochronous + * stream->last_fid is initialized to -1, so the first isochronous * frame will never trigger an end of frame detection. * * Empty buffers (bytesused == 0) don't trigger end of frame detection @@ -436,22 +436,22 @@ static int uvc_video_decode_start(struct uvc_video_device *video, * avoids detecting end of frame conditions at FID toggling if the * previous payload had the EOF bit set. */ - if (fid != video->last_fid && buf->buf.bytesused != 0) { + if (fid != stream->last_fid && buf->buf.bytesused != 0) { uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit " "toggled).\n"); buf->state = UVC_BUF_STATE_DONE; return -EAGAIN; } - video->last_fid = fid; + stream->last_fid = fid; return data[0]; } -static void uvc_video_decode_data(struct uvc_video_device *video, +static void uvc_video_decode_data(struct uvc_streaming *stream, struct uvc_buffer *buf, const __u8 *data, int len) { - struct uvc_video_queue *queue = &video->queue; + struct uvc_video_queue *queue = &stream->queue; unsigned int maxlen, nbytes; void *mem; @@ -472,7 +472,7 @@ static void uvc_video_decode_data(struct uvc_video_device *video, } } -static void uvc_video_decode_end(struct uvc_video_device *video, +static void uvc_video_decode_end(struct uvc_streaming *stream, struct uvc_buffer *buf, const __u8 *data, int len) { /* Mark the buffer as done if the EOF marker is set. */ @@ -481,8 +481,8 @@ static void uvc_video_decode_end(struct uvc_video_device *video, if (data[0] == len) uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n"); buf->state = UVC_BUF_STATE_DONE; - if (video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) - video->last_fid ^= UVC_STREAM_FID; + if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) + stream->last_fid ^= UVC_STREAM_FID; } } @@ -497,26 +497,26 @@ static void uvc_video_decode_end(struct uvc_video_device *video, * uvc_video_encode_data is called for every URB and copies the data from the * video buffer to the transfer buffer. */ -static int uvc_video_encode_header(struct uvc_video_device *video, +static int uvc_video_encode_header(struct uvc_streaming *stream, struct uvc_buffer *buf, __u8 *data, int len) { data[0] = 2; /* Header length */ data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF - | (video->last_fid & UVC_STREAM_FID); + | (stream->last_fid & UVC_STREAM_FID); return 2; } -static int uvc_video_encode_data(struct uvc_video_device *video, +static int uvc_video_encode_data(struct uvc_streaming *stream, struct uvc_buffer *buf, __u8 *data, int len) { - struct uvc_video_queue *queue = &video->queue; + struct uvc_video_queue *queue = &stream->queue; unsigned int nbytes; void *mem; /* Copy video data to the URB buffer. */ mem = queue->mem + buf->buf.m.offset + queue->buf_used; nbytes = min((unsigned int)len, buf->buf.bytesused - queue->buf_used); - nbytes = min(video->bulk.max_payload_size - video->bulk.payload_size, + nbytes = min(stream->bulk.max_payload_size - stream->bulk.payload_size, nbytes); memcpy(data, mem, nbytes); @@ -532,8 +532,8 @@ static int uvc_video_encode_data(struct uvc_video_device *video, /* * Completion handler for video URBs. */ -static void uvc_video_decode_isoc(struct urb *urb, - struct uvc_video_device *video, struct uvc_buffer *buf) +static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, + struct uvc_buffer *buf) { u8 *mem; int ret, i; @@ -548,31 +548,32 @@ static void uvc_video_decode_isoc(struct urb *urb, /* Decode the payload header. */ mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset; do { - ret = uvc_video_decode_start(video, buf, mem, + ret = uvc_video_decode_start(stream, buf, mem, urb->iso_frame_desc[i].actual_length); if (ret == -EAGAIN) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, + buf); } while (ret == -EAGAIN); if (ret < 0) continue; /* Decode the payload data. */ - uvc_video_decode_data(video, buf, mem + ret, + uvc_video_decode_data(stream, buf, mem + ret, urb->iso_frame_desc[i].actual_length - ret); /* Process the header again. */ - uvc_video_decode_end(video, buf, mem, + uvc_video_decode_end(stream, buf, mem, urb->iso_frame_desc[i].actual_length); if (buf->state == UVC_BUF_STATE_DONE || buf->state == UVC_BUF_STATE_ERROR) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, buf); } } -static void uvc_video_decode_bulk(struct urb *urb, - struct uvc_video_device *video, struct uvc_buffer *buf) +static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream, + struct uvc_buffer *buf) { u8 *mem; int len, ret; @@ -582,24 +583,25 @@ static void uvc_video_decode_bulk(struct urb *urb, mem = urb->transfer_buffer; len = urb->actual_length; - video->bulk.payload_size += len; + stream->bulk.payload_size += len; /* If the URB is the first of its payload, decode and save the * header. */ - if (video->bulk.header_size == 0 && !video->bulk.skip_payload) { + if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) { do { - ret = uvc_video_decode_start(video, buf, mem, len); + ret = uvc_video_decode_start(stream, buf, mem, len); if (ret == -EAGAIN) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, + buf); } while (ret == -EAGAIN); /* If an error occured skip the rest of the payload. */ if (ret < 0 || buf == NULL) { - video->bulk.skip_payload = 1; + stream->bulk.skip_payload = 1; } else { - memcpy(video->bulk.header, mem, ret); - video->bulk.header_size = ret; + memcpy(stream->bulk.header, mem, ret); + stream->bulk.header_size = ret; mem += ret; len -= ret; @@ -612,33 +614,34 @@ static void uvc_video_decode_bulk(struct urb *urb, */ /* Process video data. */ - if (!video->bulk.skip_payload && buf != NULL) - uvc_video_decode_data(video, buf, mem, len); + if (!stream->bulk.skip_payload && buf != NULL) + uvc_video_decode_data(stream, buf, mem, len); /* Detect the payload end by a URB smaller than the maximum size (or * a payload size equal to the maximum) and process the header again. */ if (urb->actual_length < urb->transfer_buffer_length || - video->bulk.payload_size >= video->bulk.max_payload_size) { - if (!video->bulk.skip_payload && buf != NULL) { - uvc_video_decode_end(video, buf, video->bulk.header, - video->bulk.payload_size); + stream->bulk.payload_size >= stream->bulk.max_payload_size) { + if (!stream->bulk.skip_payload && buf != NULL) { + uvc_video_decode_end(stream, buf, stream->bulk.header, + stream->bulk.payload_size); if (buf->state == UVC_BUF_STATE_DONE || buf->state == UVC_BUF_STATE_ERROR) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, + buf); } - video->bulk.header_size = 0; - video->bulk.skip_payload = 0; - video->bulk.payload_size = 0; + stream->bulk.header_size = 0; + stream->bulk.skip_payload = 0; + stream->bulk.payload_size = 0; } } -static void uvc_video_encode_bulk(struct urb *urb, - struct uvc_video_device *video, struct uvc_buffer *buf) +static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream, + struct uvc_buffer *buf) { u8 *mem = urb->transfer_buffer; - int len = video->urb_size, ret; + int len = stream->urb_size, ret; if (buf == NULL) { urb->transfer_buffer_length = 0; @@ -646,34 +649,34 @@ static void uvc_video_encode_bulk(struct urb *urb, } /* If the URB is the first of its payload, add the header. */ - if (video->bulk.header_size == 0) { - ret = uvc_video_encode_header(video, buf, mem, len); - video->bulk.header_size = ret; - video->bulk.payload_size += ret; + if (stream->bulk.header_size == 0) { + ret = uvc_video_encode_header(stream, buf, mem, len); + stream->bulk.header_size = ret; + stream->bulk.payload_size += ret; mem += ret; len -= ret; } /* Process video data. */ - ret = uvc_video_encode_data(video, buf, mem, len); + ret = uvc_video_encode_data(stream, buf, mem, len); - video->bulk.payload_size += ret; + stream->bulk.payload_size += ret; len -= ret; - if (buf->buf.bytesused == video->queue.buf_used || - video->bulk.payload_size == video->bulk.max_payload_size) { - if (buf->buf.bytesused == video->queue.buf_used) { - video->queue.buf_used = 0; + if (buf->buf.bytesused == stream->queue.buf_used || + stream->bulk.payload_size == stream->bulk.max_payload_size) { + if (buf->buf.bytesused == stream->queue.buf_used) { + stream->queue.buf_used = 0; buf->state = UVC_BUF_STATE_DONE; - uvc_queue_next_buffer(&video->queue, buf); - video->last_fid ^= UVC_STREAM_FID; + uvc_queue_next_buffer(&stream->queue, buf); + stream->last_fid ^= UVC_STREAM_FID; } - video->bulk.header_size = 0; - video->bulk.payload_size = 0; + stream->bulk.header_size = 0; + stream->bulk.payload_size = 0; } - urb->transfer_buffer_length = video->urb_size - len; + urb->transfer_buffer_length = stream->urb_size - len; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) @@ -682,8 +685,8 @@ static void uvc_video_complete(struct urb *urb, struct pt_regs *regs) static void uvc_video_complete(struct urb *urb) #endif { - struct uvc_video_device *video = urb->context; - struct uvc_video_queue *queue = &video->queue; + struct uvc_streaming *stream = urb->context; + struct uvc_video_queue *queue = &stream->queue; struct uvc_buffer *buf = NULL; unsigned long flags; int ret; @@ -697,7 +700,7 @@ static void uvc_video_complete(struct urb *urb) "completion handler.\n", urb->status); case -ENOENT: /* usb_kill_urb() called. */ - if (video->frozen) + if (stream->frozen) return; case -ECONNRESET: /* usb_unlink_urb() called. */ @@ -712,7 +715,7 @@ static void uvc_video_complete(struct urb *urb) queue); spin_unlock_irqrestore(&queue->irqlock, flags); - video->decode(urb, video, buf); + stream->decode(urb, stream, buf); if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n", @@ -723,19 +726,19 @@ static void uvc_video_complete(struct urb *urb) /* * Free transfer buffers. */ -static void uvc_free_urb_buffers(struct uvc_video_device *video) +static void uvc_free_urb_buffers(struct uvc_streaming *stream) { unsigned int i; for (i = 0; i < UVC_URBS; ++i) { - if (video->urb_buffer[i]) { - usb_buffer_free(video->dev->udev, video->urb_size, - video->urb_buffer[i], video->urb_dma[i]); - video->urb_buffer[i] = NULL; + if (stream->urb_buffer[i]) { + usb_buffer_free(stream->dev->udev, stream->urb_size, + stream->urb_buffer[i], stream->urb_dma[i]); + stream->urb_buffer[i] = NULL; } } - video->urb_size = 0; + stream->urb_size = 0; } /* @@ -749,15 +752,15 @@ static void uvc_free_urb_buffers(struct uvc_video_device *video) * * Return the number of allocated packets on success or 0 when out of memory. */ -static int uvc_alloc_urb_buffers(struct uvc_video_device *video, +static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, unsigned int size, unsigned int psize, gfp_t gfp_flags) { unsigned int npackets; unsigned int i; /* Buffers are already allocated, bail out. */ - if (video->urb_size) - return video->urb_size / psize; + if (stream->urb_size) + return stream->urb_size / psize; /* Compute the number of packets. Bulk endpoints might transfer UVC * payloads accross multiple URBs. @@ -769,17 +772,17 @@ static int uvc_alloc_urb_buffers(struct uvc_video_device *video, /* Retry allocations until one succeed. */ for (; npackets > 1; npackets /= 2) { for (i = 0; i < UVC_URBS; ++i) { - video->urb_buffer[i] = usb_buffer_alloc( - video->dev->udev, psize * npackets, - gfp_flags | __GFP_NOWARN, &video->urb_dma[i]); - if (!video->urb_buffer[i]) { - uvc_free_urb_buffers(video); + stream->urb_buffer[i] = usb_buffer_alloc( + stream->dev->udev, psize * npackets, + gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]); + if (!stream->urb_buffer[i]) { + uvc_free_urb_buffers(stream); break; } } if (i == UVC_URBS) { - video->urb_size = psize * npackets; + stream->urb_size = psize * npackets; return npackets; } } @@ -790,29 +793,30 @@ static int uvc_alloc_urb_buffers(struct uvc_video_device *video, /* * Uninitialize isochronous/bulk URBs and free transfer buffers. */ -static void uvc_uninit_video(struct uvc_video_device *video, int free_buffers) +static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers) { struct urb *urb; unsigned int i; for (i = 0; i < UVC_URBS; ++i) { - if ((urb = video->urb[i]) == NULL) + urb = stream->urb[i]; + if (urb == NULL) continue; usb_kill_urb(urb); usb_free_urb(urb); - video->urb[i] = NULL; + stream->urb[i] = NULL; } if (free_buffers) - uvc_free_urb_buffers(video); + uvc_free_urb_buffers(stream); } /* * Initialize isochronous URBs and allocate transfer buffers. The packet size * is given by the endpoint. */ -static int uvc_init_video_isoc(struct uvc_video_device *video, +static int uvc_init_video_isoc(struct uvc_streaming *stream, struct usb_host_endpoint *ep, gfp_t gfp_flags) { struct urb *urb; @@ -822,9 +826,9 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, psize = le16_to_cpu(ep->desc.wMaxPacketSize); psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); - size = video->streaming->ctrl.dwMaxVideoFrameSize; + size = stream->ctrl.dwMaxVideoFrameSize; - npackets = uvc_alloc_urb_buffers(video, size, psize, gfp_flags); + npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags); if (npackets == 0) return -ENOMEM; @@ -833,18 +837,18 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, for (i = 0; i < UVC_URBS; ++i) { urb = usb_alloc_urb(npackets, gfp_flags); if (urb == NULL) { - uvc_uninit_video(video, 1); + uvc_uninit_video(stream, 1); return -ENOMEM; } - urb->dev = video->dev->udev; - urb->context = video; - urb->pipe = usb_rcvisocpipe(video->dev->udev, + urb->dev = stream->dev->udev; + urb->context = stream; + urb->pipe = usb_rcvisocpipe(stream->dev->udev, ep->desc.bEndpointAddress); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; urb->interval = ep->desc.bInterval; - urb->transfer_buffer = video->urb_buffer[i]; - urb->transfer_dma = video->urb_dma[i]; + urb->transfer_buffer = stream->urb_buffer[i]; + urb->transfer_dma = stream->urb_dma[i]; urb->complete = uvc_video_complete; urb->number_of_packets = npackets; urb->transfer_buffer_length = size; @@ -854,7 +858,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, urb->iso_frame_desc[j].length = psize; } - video->urb[i] = urb; + stream->urb[i] = urb; } return 0; @@ -864,7 +868,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, * Initialize bulk URBs and allocate transfer buffers. The packet size is * given by the endpoint. */ -static int uvc_init_video_bulk(struct uvc_video_device *video, +static int uvc_init_video_bulk(struct uvc_streaming *stream, struct usb_host_endpoint *ep, gfp_t gfp_flags) { struct urb *urb; @@ -873,39 +877,39 @@ static int uvc_init_video_bulk(struct uvc_video_device *video, u32 size; psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff; - size = video->streaming->ctrl.dwMaxPayloadTransferSize; - video->bulk.max_payload_size = size; + size = stream->ctrl.dwMaxPayloadTransferSize; + stream->bulk.max_payload_size = size; - npackets = uvc_alloc_urb_buffers(video, size, psize, gfp_flags); + npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags); if (npackets == 0) return -ENOMEM; size = npackets * psize; if (usb_endpoint_dir_in(&ep->desc)) - pipe = usb_rcvbulkpipe(video->dev->udev, + pipe = usb_rcvbulkpipe(stream->dev->udev, ep->desc.bEndpointAddress); else - pipe = usb_sndbulkpipe(video->dev->udev, + pipe = usb_sndbulkpipe(stream->dev->udev, ep->desc.bEndpointAddress); - if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) size = 0; for (i = 0; i < UVC_URBS; ++i) { urb = usb_alloc_urb(0, gfp_flags); if (urb == NULL) { - uvc_uninit_video(video, 1); + uvc_uninit_video(stream, 1); return -ENOMEM; } - usb_fill_bulk_urb(urb, video->dev->udev, pipe, - video->urb_buffer[i], size, uvc_video_complete, - video); + usb_fill_bulk_urb(urb, stream->dev->udev, pipe, + stream->urb_buffer[i], size, uvc_video_complete, + stream); urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; - urb->transfer_dma = video->urb_dma[i]; + urb->transfer_dma = stream->urb_dma[i]; - video->urb[i] = urb; + stream->urb[i] = urb; } return 0; @@ -914,35 +918,35 @@ static int uvc_init_video_bulk(struct uvc_video_device *video, /* * Initialize isochronous/bulk URBs and allocate transfer buffers. */ -static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags) +static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) { - struct usb_interface *intf = video->streaming->intf; + struct usb_interface *intf = stream->intf; struct usb_host_interface *alts; struct usb_host_endpoint *ep = NULL; - int intfnum = video->streaming->intfnum; + int intfnum = stream->intfnum; unsigned int bandwidth, psize, i; int ret; - video->last_fid = -1; - video->bulk.header_size = 0; - video->bulk.skip_payload = 0; - video->bulk.payload_size = 0; + stream->last_fid = -1; + stream->bulk.header_size = 0; + stream->bulk.skip_payload = 0; + stream->bulk.payload_size = 0; if (intf->num_altsetting > 1) { /* Isochronous endpoint, select the alternate setting. */ - bandwidth = video->streaming->ctrl.dwMaxPayloadTransferSize; + bandwidth = stream->ctrl.dwMaxPayloadTransferSize; if (bandwidth == 0) { uvc_printk(KERN_WARNING, "device %s requested null " "bandwidth, defaulting to lowest.\n", - video->vdev->name); + stream->dev->name); bandwidth = 1; } for (i = 0; i < intf->num_altsetting; ++i) { alts = &intf->altsetting[i]; ep = uvc_find_endpoint(alts, - video->streaming->header.bEndpointAddress); + stream->header.bEndpointAddress); if (ep == NULL) continue; @@ -956,18 +960,19 @@ static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags) if (i >= intf->num_altsetting) return -EIO; - if ((ret = usb_set_interface(video->dev->udev, intfnum, i)) < 0) + ret = usb_set_interface(stream->dev->udev, intfnum, i); + if (ret < 0) return ret; - ret = uvc_init_video_isoc(video, ep, gfp_flags); + ret = uvc_init_video_isoc(stream, ep, gfp_flags); } else { /* Bulk endpoint, proceed to URB initialization. */ ep = uvc_find_endpoint(&intf->altsetting[0], - video->streaming->header.bEndpointAddress); + stream->header.bEndpointAddress); if (ep == NULL) return -EIO; - ret = uvc_init_video_bulk(video, ep, gfp_flags); + ret = uvc_init_video_bulk(stream, ep, gfp_flags); } if (ret < 0) @@ -975,10 +980,11 @@ static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags) /* Submit the URBs. */ for (i = 0; i < UVC_URBS; ++i) { - if ((ret = usb_submit_urb(video->urb[i], gfp_flags)) < 0) { + ret = usb_submit_urb(stream->urb[i], gfp_flags); + if (ret < 0) { uvc_printk(KERN_ERR, "Failed to submit URB %u " "(%d).\n", i, ret); - uvc_uninit_video(video, 1); + uvc_uninit_video(stream, 1); return ret; } } @@ -997,14 +1003,14 @@ static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags) * video buffers in any way. We mark the device as frozen to make sure the URB * completion handler won't try to cancel the queue when we kill the URBs. */ -int uvc_video_suspend(struct uvc_video_device *video) +int uvc_video_suspend(struct uvc_streaming *stream) { - if (!uvc_queue_streaming(&video->queue)) + if (!uvc_queue_streaming(&stream->queue)) return 0; - video->frozen = 1; - uvc_uninit_video(video, 0); - usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); + stream->frozen = 1; + uvc_uninit_video(stream, 0); + usb_set_interface(stream->dev->udev, stream->intfnum, 0); return 0; } @@ -1016,22 +1022,24 @@ int uvc_video_suspend(struct uvc_video_device *video) * buffers, making sure userspace applications are notified of the problem * instead of waiting forever. */ -int uvc_video_resume(struct uvc_video_device *video) +int uvc_video_resume(struct uvc_streaming *stream) { int ret; - video->frozen = 0; + stream->frozen = 0; - if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) { - uvc_queue_enable(&video->queue, 0); + ret = uvc_commit_video(stream, &stream->ctrl); + if (ret < 0) { + uvc_queue_enable(&stream->queue, 0); return ret; } - if (!uvc_queue_streaming(&video->queue)) + if (!uvc_queue_streaming(&stream->queue)) return 0; - if ((ret = uvc_init_video(video, GFP_NOIO)) < 0) - uvc_queue_enable(&video->queue, 0); + ret = uvc_init_video(stream, GFP_NOIO); + if (ret < 0) + uvc_queue_enable(&stream->queue, 0); return ret; } @@ -1050,48 +1058,53 @@ int uvc_video_resume(struct uvc_video_device *video) * * This function is called before registering the device with V4L. */ -int uvc_video_init(struct uvc_video_device *video) +int uvc_video_init(struct uvc_streaming *stream) { - struct uvc_streaming_control *probe = &video->streaming->ctrl; + struct uvc_streaming_control *probe = &stream->ctrl; struct uvc_format *format = NULL; struct uvc_frame *frame = NULL; unsigned int i; int ret; - if (video->streaming->nformats == 0) { + if (stream->nformats == 0) { uvc_printk(KERN_INFO, "No supported video formats found.\n"); return -EINVAL; } + atomic_set(&stream->active, 0); + + /* Initialize the video buffers queue. */ + uvc_queue_init(&stream->queue, stream->type); + /* Alternate setting 0 should be the default, yet the XBox Live Vision * Cam (and possibly other devices) crash or otherwise misbehave if * they don't receive a SET_INTERFACE request before any other video * control request. */ - usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); + usb_set_interface(stream->dev->udev, stream->intfnum, 0); /* Set the streaming probe control with default streaming parameters * retrieved from the device. Webcams that don't suport GET_DEF * requests on the probe control will just keep their current streaming * parameters. */ - if (uvc_get_video_ctrl(video, probe, 1, UVC_GET_DEF) == 0) - uvc_set_video_ctrl(video, probe, 1); + if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0) + uvc_set_video_ctrl(stream, probe, 1); /* Initialize the streaming parameters with the probe control current * value. This makes sure SET_CUR requests on the streaming commit * control will always use values retrieved from a successful GET_CUR * request on the probe control, as required by the UVC specification. */ - ret = uvc_get_video_ctrl(video, probe, 1, UVC_GET_CUR); + ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR); if (ret < 0) return ret; /* Check if the default format descriptor exists. Use the first * available format otherwise. */ - for (i = video->streaming->nformats; i > 0; --i) { - format = &video->streaming->format[i-1]; + for (i = stream->nformats; i > 0; --i) { + format = &stream->format[i-1]; if (format->index == probe->bFormatIndex) break; } @@ -1116,21 +1129,20 @@ int uvc_video_init(struct uvc_video_device *video) probe->bFormatIndex = format->index; probe->bFrameIndex = frame->bFrameIndex; - video->streaming->cur_format = format; - video->streaming->cur_frame = frame; - atomic_set(&video->active, 0); + stream->cur_format = format; + stream->cur_frame = frame; /* Select the video decoding function */ - if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) - video->decode = uvc_video_decode_isight; - else if (video->streaming->intf->num_altsetting > 1) - video->decode = uvc_video_decode_isoc; + if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) + stream->decode = uvc_video_decode_isight; + else if (stream->intf->num_altsetting > 1) + stream->decode = uvc_video_decode_isoc; else - video->decode = uvc_video_decode_bulk; + stream->decode = uvc_video_decode_bulk; } else { - if (video->streaming->intf->num_altsetting == 1) - video->decode = uvc_video_encode_bulk; + if (stream->intf->num_altsetting == 1) + stream->decode = uvc_video_encode_bulk; else { uvc_printk(KERN_INFO, "Isochronous endpoints are not " "supported for video output devices.\n"); @@ -1144,31 +1156,32 @@ int uvc_video_init(struct uvc_video_device *video) /* * Enable or disable the video stream. */ -int uvc_video_enable(struct uvc_video_device *video, int enable) +int uvc_video_enable(struct uvc_streaming *stream, int enable) { int ret; if (!enable) { - uvc_uninit_video(video, 1); - usb_set_interface(video->dev->udev, - video->streaming->intfnum, 0); - uvc_queue_enable(&video->queue, 0); + uvc_uninit_video(stream, 1); + usb_set_interface(stream->dev->udev, stream->intfnum, 0); + uvc_queue_enable(&stream->queue, 0); return 0; } - if ((video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) || + if ((stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) || uvc_no_drop_param) - video->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE; + stream->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE; else - video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; + stream->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; - if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) + ret = uvc_queue_enable(&stream->queue, 1); + if (ret < 0) return ret; /* Commit the streaming parameters. */ - if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) + ret = uvc_commit_video(stream, &stream->ctrl); + if (ret < 0) return ret; - return uvc_init_video(video, GFP_KERNEL); + return uvc_init_video(stream, GFP_KERNEL); } diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h index e700bdc20..c385b7bde 100644 --- a/linux/drivers/media/video/uvc/uvcvideo.h +++ b/linux/drivers/media/video/uvc/uvcvideo.h @@ -362,26 +362,6 @@ struct uvc_streaming_header { __u8 bTriggerUsage; }; -struct uvc_streaming { - struct list_head list; - - struct usb_interface *intf; - int intfnum; - __u16 maxpsize; - - struct uvc_streaming_header header; - enum v4l2_buf_type type; - - unsigned int nformats; - struct uvc_format *format; - - struct uvc_streaming_control ctrl; - struct uvc_format *cur_format; - struct uvc_frame *cur_frame; - - struct mutex mutex; -}; - enum uvc_buffer_state { UVC_BUF_STATE_IDLE = 0, UVC_BUF_STATE_QUEUED = 1, @@ -423,26 +403,31 @@ struct uvc_video_queue { struct list_head irqqueue; }; -struct uvc_video_device { +struct uvc_streaming { + struct list_head list; struct uvc_device *dev; struct video_device *vdev; atomic_t active; - unsigned int frozen : 1; - struct list_head iterms; /* Input terminals */ - struct uvc_entity *oterm; /* Output terminal */ - struct uvc_entity *sterm; /* USB streaming terminal */ - struct uvc_entity *processing; - struct uvc_entity *selector; - struct list_head extensions; - struct mutex ctrl_mutex; + struct usb_interface *intf; + int intfnum; + __u16 maxpsize; - struct uvc_video_queue queue; + struct uvc_streaming_header header; + enum v4l2_buf_type type; + + unsigned int nformats; + struct uvc_format *format; - /* Video streaming object, must always be non-NULL. */ - struct uvc_streaming *streaming; + struct uvc_streaming_control ctrl; + struct uvc_format *cur_format; + struct uvc_frame *cur_frame; + + struct mutex mutex; - void (*decode) (struct urb *urb, struct uvc_video_device *video, + unsigned int frozen : 1; + struct uvc_video_queue queue; + void (*decode) (struct urb *urb, struct uvc_streaming *video, struct uvc_buffer *buf); /* Context data used by the bulk completion handler. */ @@ -462,6 +447,18 @@ struct uvc_video_device { __u8 last_fid; }; +struct uvc_video_device { + struct uvc_device *dev; + + struct list_head iterms; /* Input terminals */ + struct uvc_entity *oterm; /* Output terminal */ + struct uvc_entity *sterm; /* USB streaming terminal */ + struct uvc_entity *processing; + struct uvc_entity *selector; + struct list_head extensions; + struct mutex ctrl_mutex; +}; + enum uvc_device_state { UVC_DEV_DISCONNECTED = 1, }; @@ -487,15 +484,15 @@ struct uvc_device { struct uvc_video_device video; + /* Video Streaming interfaces */ + struct list_head streams; + /* Status Interrupt Endpoint */ struct usb_host_endpoint *int_ep; struct urb *int_urb; __u8 *status; struct input_dev *input; char input_phys[64]; - - /* Video Streaming interfaces */ - struct list_head streaming; }; enum uvc_handle_state { @@ -504,7 +501,8 @@ enum uvc_handle_state { }; struct uvc_fh { - struct uvc_video_device *device; + struct uvc_video_device *video; + struct uvc_streaming *stream; enum uvc_handle_state state; }; @@ -601,13 +599,13 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue) extern const struct v4l2_file_operations uvc_fops; /* Video */ -extern int uvc_video_init(struct uvc_video_device *video); -extern int uvc_video_suspend(struct uvc_video_device *video); -extern int uvc_video_resume(struct uvc_video_device *video); -extern int uvc_video_enable(struct uvc_video_device *video, int enable); -extern int uvc_probe_video(struct uvc_video_device *video, +extern int uvc_video_init(struct uvc_streaming *stream); +extern int uvc_video_suspend(struct uvc_streaming *stream); +extern int uvc_video_resume(struct uvc_streaming *stream); +extern int uvc_video_enable(struct uvc_streaming *stream, int enable); +extern int uvc_probe_video(struct uvc_streaming *stream, struct uvc_streaming_control *probe); -extern int uvc_commit_video(struct uvc_video_device *video, +extern int uvc_commit_video(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl); extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, __u8 intfnum, __u8 cs, void *data, __u16 size); @@ -661,7 +659,7 @@ extern struct usb_host_endpoint *uvc_find_endpoint( struct usb_host_interface *alts, __u8 epaddr); /* Quirks support */ -void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, +void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, struct uvc_buffer *buf); #endif /* __KERNEL__ */ -- cgit v1.2.3 From c5bf599bcaa4cba04f76ae9edce2d9766a9602fd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 2 Jul 2009 01:24:47 +0200 Subject: uvcvideo: Multiple streaming interfaces support From: Laurent Pinchart Restructure the UVC descriptors parsing code to handle multiple streaming interfaces. The driver now creates a uvc_video_chain instance for each chain detected in the UVC control interface descriptors, and tries to register one video device per streaming endpoint. Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_ctrl.c | 82 ++--- linux/drivers/media/video/uvc/uvc_driver.c | 466 ++++++++++++++++++----------- linux/drivers/media/video/uvc/uvc_v4l2.c | 80 ++--- linux/drivers/media/video/uvc/uvc_video.c | 2 +- linux/drivers/media/video/uvc/uvcvideo.h | 59 ++-- 5 files changed, 411 insertions(+), 278 deletions(-) diff --git a/linux/drivers/media/video/uvc/uvc_ctrl.c b/linux/drivers/media/video/uvc/uvc_ctrl.c index 81928f838..57b6fe937 100644 --- a/linux/drivers/media/video/uvc/uvc_ctrl.c +++ b/linux/drivers/media/video/uvc/uvc_ctrl.c @@ -731,7 +731,7 @@ static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id, } } -struct uvc_control *uvc_find_control(struct uvc_video_device *video, +struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, __u32 v4l2_id, struct uvc_control_mapping **mapping) { struct uvc_control *ctrl = NULL; @@ -744,17 +744,17 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video, v4l2_id &= V4L2_CTRL_ID_MASK; /* Find the control. */ - __uvc_find_control(video->processing, v4l2_id, mapping, &ctrl, next); + __uvc_find_control(chain->processing, v4l2_id, mapping, &ctrl, next); if (ctrl && !next) return ctrl; - list_for_each_entry(entity, &video->iterms, chain) { + list_for_each_entry(entity, &chain->iterms, chain) { __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); if (ctrl && !next) return ctrl; } - list_for_each_entry(entity, &video->extensions, chain) { + list_for_each_entry(entity, &chain->extensions, chain) { __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); if (ctrl && !next) return ctrl; @@ -767,7 +767,7 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video, return ctrl; } -int uvc_query_v4l2_ctrl(struct uvc_video_device *video, +int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct v4l2_queryctrl *v4l2_ctrl) { struct uvc_control *ctrl; @@ -777,7 +777,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, __u8 *data; int ret; - ctrl = uvc_find_control(video, v4l2_ctrl->id, &mapping); + ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); if (ctrl == NULL) return -EINVAL; @@ -795,9 +795,9 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; if (ctrl->info->flags & UVC_CONTROL_GET_DEF) { - ret = uvc_query_ctrl(video->dev, UVC_GET_DEF, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, data, - ctrl->info->size); + ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + data, ctrl->info->size); if (ret < 0) goto out; v4l2_ctrl->default_value = @@ -833,25 +833,25 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, } if (ctrl->info->flags & UVC_CONTROL_GET_MIN) { - ret = uvc_query_ctrl(video->dev, UVC_GET_MIN, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, data, - ctrl->info->size); + ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + data, ctrl->info->size); if (ret < 0) goto out; v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, data); } if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { - ret = uvc_query_ctrl(video->dev, UVC_GET_MAX, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, data, - ctrl->info->size); + ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + data, ctrl->info->size); if (ret < 0) goto out; v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, data); } if (ctrl->info->flags & UVC_CONTROL_GET_RES) { - ret = uvc_query_ctrl(video->dev, UVC_GET_RES, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, data, - ctrl->info->size); + ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + data, ctrl->info->size); if (ret < 0) goto out; v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, data); @@ -888,9 +888,9 @@ out: * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the * control lock. */ -int uvc_ctrl_begin(struct uvc_video_device *video) +int uvc_ctrl_begin(struct uvc_video_chain *chain) { - return mutex_lock_interruptible(&video->ctrl_mutex) ? -ERESTARTSYS : 0; + return mutex_lock_interruptible(&chain->ctrl_mutex) ? -ERESTARTSYS : 0; } static int uvc_ctrl_commit_entity(struct uvc_device *dev, @@ -940,34 +940,34 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, return 0; } -int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback) +int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) { struct uvc_entity *entity; int ret = 0; /* Find the control. */ - ret = uvc_ctrl_commit_entity(video->dev, video->processing, rollback); + ret = uvc_ctrl_commit_entity(chain->dev, chain->processing, rollback); if (ret < 0) goto done; - list_for_each_entry(entity, &video->iterms, chain) { - ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); + list_for_each_entry(entity, &chain->iterms, chain) { + ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); if (ret < 0) goto done; } - list_for_each_entry(entity, &video->extensions, chain) { - ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); + list_for_each_entry(entity, &chain->extensions, chain) { + ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); if (ret < 0) goto done; } done: - mutex_unlock(&video->ctrl_mutex); + mutex_unlock(&chain->ctrl_mutex); return ret; } -int uvc_ctrl_get(struct uvc_video_device *video, +int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl) { struct uvc_control *ctrl; @@ -976,13 +976,13 @@ int uvc_ctrl_get(struct uvc_video_device *video, unsigned int i; int ret; - ctrl = uvc_find_control(video, xctrl->id, &mapping); + ctrl = uvc_find_control(chain, xctrl->id, &mapping); if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) return -EINVAL; if (!ctrl->loaded) { - ret = uvc_query_ctrl(video->dev, UVC_GET_CUR, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size); if (ret < 0) @@ -1007,7 +1007,7 @@ int uvc_ctrl_get(struct uvc_video_device *video, return 0; } -int uvc_ctrl_set(struct uvc_video_device *video, +int uvc_ctrl_set(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl) { struct uvc_control *ctrl; @@ -1015,7 +1015,7 @@ int uvc_ctrl_set(struct uvc_video_device *video, s32 value = xctrl->value; int ret; - ctrl = uvc_find_control(video, xctrl->id, &mapping); + ctrl = uvc_find_control(chain, xctrl->id, &mapping); if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0) return -EINVAL; @@ -1030,8 +1030,8 @@ int uvc_ctrl_set(struct uvc_video_device *video, memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), 0, ctrl->info->size); } else { - ret = uvc_query_ctrl(video->dev, UVC_GET_CUR, - ctrl->entity->id, video->dev->intfnum, + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, + ctrl->entity->id, chain->dev->intfnum, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size); @@ -1060,7 +1060,7 @@ int uvc_ctrl_set(struct uvc_video_device *video, * Dynamic controls */ -int uvc_xu_ctrl_query(struct uvc_video_device *video, +int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control *xctrl, int set) { struct uvc_entity *entity; @@ -1070,7 +1070,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video, int ret; /* Find the extension unit. */ - list_for_each_entry(entity, &video->extensions, chain) { + list_for_each_entry(entity, &chain->extensions, chain) { if (entity->id == xctrl->unit) break; } @@ -1109,7 +1109,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video, (!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR))) return -EINVAL; - if (mutex_lock_interruptible(&video->ctrl_mutex)) + if (mutex_lock_interruptible(&chain->ctrl_mutex)) return -ERESTARTSYS; memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), @@ -1122,8 +1122,8 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video, goto out; } - ret = uvc_query_ctrl(video->dev, set ? UVC_SET_CUR : UVC_GET_CUR, - xctrl->unit, video->dev->intfnum, xctrl->selector, + ret = uvc_query_ctrl(chain->dev, set ? UVC_SET_CUR : UVC_GET_CUR, + xctrl->unit, chain->dev->intfnum, xctrl->selector, data, xctrl->size); if (ret < 0) goto out; @@ -1139,7 +1139,7 @@ out: uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), xctrl->size); - mutex_unlock(&video->ctrl_mutex); + mutex_unlock(&chain->ctrl_mutex); return ret; } diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index 70d15a926..fa13b6765 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -276,8 +276,20 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, return NULL; } +static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) +{ + struct uvc_streaming *stream; + + list_for_each_entry(stream, &dev->streams, list) { + if (stream->header.bTerminalLink == id) + return stream; + } + + return NULL; +} + /* ------------------------------------------------------------------------ - * Descriptors handling + * Descriptors parsing */ static int uvc_parse_format(struct uvc_device *dev, @@ -1160,101 +1172,36 @@ next_descriptor: } /* ------------------------------------------------------------------------ - * USB probe and disconnect + * UVC device scan */ -/* - * Unregister the video devices. - */ -static void uvc_unregister_video(struct uvc_device *dev) -{ - struct uvc_streaming *streaming; - - list_for_each_entry(streaming, &dev->streams, list) { - if (streaming->vdev == NULL) - continue; - - if (streaming->vdev->minor == -1) - video_device_release(streaming->vdev); - else - video_unregister_device(streaming->vdev); - streaming->vdev = NULL; - } -} - -static int uvc_register_video(struct uvc_device *dev, - struct uvc_streaming *stream) -{ - struct video_device *vdev; - struct uvc_entity *term; - int ret; - - if (uvc_trace_param & UVC_TRACE_PROBE) { - uvc_printk(KERN_INFO, "Found a valid video chain ("); - list_for_each_entry(term, &dev->video.iterms, chain) { - printk("%d", term->id); - if (term->chain.next != &dev->video.iterms) - printk(","); - } - printk(" -> %d).\n", dev->video.oterm->id); - } - - /* Initialize the streaming interface with default streaming - * parameters. - */ - ret = uvc_video_init(stream); - if (ret < 0) { - uvc_printk(KERN_ERR, "Failed to initialize the device " - "(%d).\n", ret); - return ret; - } - - /* Register the device with V4L. */ - vdev = video_device_alloc(); - if (vdev == NULL) - return -1; - - /* We already hold a reference to dev->udev. The video device will be - * unregistered before the reference is released, so we don't need to - * get another one. - */ - vdev->parent = &dev->intf->dev; - vdev->minor = -1; - vdev->fops = &uvc_fops; - vdev->release = video_device_release; - strlcpy(vdev->name, dev->name, sizeof vdev->name); - - /* Set the driver data before calling video_register_device, otherwise - * uvc_v4l2_open might race us. - */ - stream->vdev = vdev; - video_set_drvdata(vdev, stream); - - if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) { - stream->vdev = NULL; - video_device_release(vdev); - return -1; - } - - return 0; -} - /* * Scan the UVC descriptors to locate a chain starting at an Output Terminal * and containing the following units: * - * - one Output Terminal (USB Streaming or Display) + * - one or more Output Terminals (USB Streaming or Display) * - zero or one Processing Unit - * - zero, one or mode single-input Selector Units + * - zero, one or more single-input Selector Units * - zero or one multiple-input Selector Units, provided all inputs are * connected to input terminals * - zero, one or mode single-input Extension Units * - one or more Input Terminals (Camera, External or USB Streaming) * - * A side forward scan is made on each detected entity to check for additional - * extension units. + * The terminal and units must match on of the following structures: + * + * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0) + * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ... + * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n) + * + * +---------+ +---------+ -> OTT_*(0) + * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ... + * +---------+ +---------+ -> OTT_*(n) + * + * The Processing Unit and Extension Units can be in any order. Additional + * Extension Units connected to the main chain as single-unit branches are + * also supported. Single-input Selector Units are ignored. */ -static int uvc_scan_chain_entity(struct uvc_video_device *video, +static int uvc_scan_chain_entity(struct uvc_video_chain *chain, struct uvc_entity *entity) { switch (UVC_ENTITY_TYPE(entity)) { @@ -1268,20 +1215,20 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, return -1; } - list_add_tail(&entity->chain, &video->extensions); + list_add_tail(&entity->chain, &chain->extensions); break; case UVC_VC_PROCESSING_UNIT: if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- PU %d", entity->id); - if (video->processing != NULL) { + if (chain->processing != NULL) { uvc_trace(UVC_TRACE_DESCR, "Found multiple " "Processing Units in chain.\n"); return -1; } - video->processing = entity; + chain->processing = entity; break; case UVC_VC_SELECTOR_UNIT: @@ -1292,13 +1239,13 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, if (entity->selector.bNrInPins == 1) break; - if (video->selector != NULL) { + if (chain->selector != NULL) { uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector " "Units in chain.\n"); return -1; } - video->selector = entity; + chain->selector = entity; break; case UVC_ITT_VENDOR_SPECIFIC: @@ -1307,7 +1254,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT %d\n", entity->id); - list_add_tail(&entity->chain, &video->iterms); + list_add_tail(&entity->chain, &chain->iterms); break; case UVC_TT_STREAMING: @@ -1320,14 +1267,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, return -1; } - if (video->sterm != NULL) { - uvc_trace(UVC_TRACE_DESCR, "Found multiple streaming " - "entities in chain.\n"); - return -1; - } - - list_add_tail(&entity->chain, &video->iterms); - video->sterm = entity; + list_add_tail(&entity->chain, &chain->iterms); break; default: @@ -1339,7 +1279,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, return 0; } -static int uvc_scan_chain_forward(struct uvc_video_device *video, +static int uvc_scan_chain_forward(struct uvc_video_chain *chain, struct uvc_entity *entity, struct uvc_entity *prev) { struct uvc_entity *forward; @@ -1350,28 +1290,51 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video, found = 0; while (1) { - forward = uvc_entity_by_reference(video->dev, entity->id, + forward = uvc_entity_by_reference(chain->dev, entity->id, forward); if (forward == NULL) break; - - if (UVC_ENTITY_TYPE(forward) != UVC_VC_EXTENSION_UNIT || - forward == prev) + if (forward == prev) continue; - if (forward->extension.bNrInPins != 1) { - uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has " - "more than 1 input pin.\n", entity->id); - return -1; - } + switch (UVC_ENTITY_TYPE(forward)) { + case UVC_VC_EXTENSION_UNIT: + if (forward->extension.bNrInPins != 1) { + uvc_trace(UVC_TRACE_DESCR, "Extension unit %d " + "has more than 1 input pin.\n", + entity->id); + return -EINVAL; + } + + list_add_tail(&forward->chain, &chain->extensions); + if (uvc_trace_param & UVC_TRACE_PROBE) { + if (!found) + printk(" (->"); + + printk(" XU %d", forward->id); + found = 1; + } + break; + + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + case UVC_TT_STREAMING: + if (UVC_ENTITY_IS_ITERM(forward)) { + uvc_trace(UVC_TRACE_DESCR, "Unsupported input " + "terminal %u.\n", forward->id); + return -EINVAL; + } - list_add_tail(&forward->chain, &video->extensions); - if (uvc_trace_param & UVC_TRACE_PROBE) { - if (!found) - printk(" (-> XU"); + list_add_tail(&forward->chain, &chain->oterms); + if (uvc_trace_param & UVC_TRACE_PROBE) { + if (!found) + printk(" (->"); - printk(" %d", forward->id); - found = 1; + printk(" OT %d", forward->id); + found = 1; + } + break; } } if (found) @@ -1380,7 +1343,7 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video, return 0; } -static int uvc_scan_chain_backward(struct uvc_video_device *video, +static int uvc_scan_chain_backward(struct uvc_video_chain *chain, struct uvc_entity *entity) { struct uvc_entity *term; @@ -1405,10 +1368,10 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT"); - video->selector = entity; + chain->selector = entity; for (i = 0; i < entity->selector.bNrInPins; ++i) { id = entity->selector.baSourceID[i]; - term = uvc_entity_by_id(video->dev, id); + term = uvc_entity_by_id(chain->dev, id); if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " "input %d isn't connected to an " @@ -1419,8 +1382,8 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" %d", term->id); - list_add_tail(&term->chain, &video->iterms); - uvc_scan_chain_forward(video, term, entity); + list_add_tail(&term->chain, &chain->iterms); + uvc_scan_chain_forward(chain, term, entity); } if (uvc_trace_param & UVC_TRACE_PROBE) @@ -1433,100 +1396,129 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video, return id; } -static int uvc_scan_chain(struct uvc_video_device *video) +static int uvc_scan_chain(struct uvc_video_chain *chain, + struct uvc_entity *oterm) { struct uvc_entity *entity, *prev; int id; - entity = video->oterm; + entity = oterm; + list_add_tail(&entity->chain, &chain->oterms); uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id); - if (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING) - video->sterm = entity; - id = entity->output.bSourceID; while (id != 0) { prev = entity; - entity = uvc_entity_by_id(video->dev, id); + entity = uvc_entity_by_id(chain->dev, id); if (entity == NULL) { uvc_trace(UVC_TRACE_DESCR, "Found reference to " "unknown entity %d.\n", id); - return -1; + return -EINVAL; + } + + if (entity->chain.next || entity->chain.prev) { + uvc_trace(UVC_TRACE_DESCR, "Found reference to " + "entity %d already in chain.\n", id); + return -EINVAL; } /* Process entity */ - if (uvc_scan_chain_entity(video, entity) < 0) - return -1; + if (uvc_scan_chain_entity(chain, entity) < 0) + return -EINVAL; /* Forward scan */ - if (uvc_scan_chain_forward(video, entity, prev) < 0) - return -1; + if (uvc_scan_chain_forward(chain, entity, prev) < 0) + return -EINVAL; /* Stop when a terminal is found. */ - if (!UVC_ENTITY_IS_UNIT(entity)) + if (UVC_ENTITY_IS_TERM(entity)) break; /* Backward scan */ - id = uvc_scan_chain_backward(video, entity); + id = uvc_scan_chain_backward(chain, entity); if (id < 0) return id; } - if (video->sterm == NULL) { - uvc_trace(UVC_TRACE_DESCR, "No streaming entity found in " - "chain.\n"); - return -1; + return 0; +} + +static unsigned int uvc_print_terms(struct list_head *terms, char *buffer) +{ + struct uvc_entity *term; + unsigned int nterms = 0; + char *p = buffer; + + list_for_each_entry(term, terms, chain) { + p += sprintf(p, "%u", term->id); + if (term->chain.next != terms) { + p += sprintf(p, ","); + if (++nterms >= 4) { + p += sprintf(p, "..."); + break; + } + } } - return 0; + return p - buffer; +} + +static const char *uvc_print_chain(struct uvc_video_chain *chain) +{ + static char buffer[43]; + char *p = buffer; + + p += uvc_print_terms(&chain->iterms, p); + p += sprintf(p, " -> "); + uvc_print_terms(&chain->oterms, p); + + return buffer; } /* * Scan the device for video chains and register video devices. * - * The driver currently supports a single video device per control interface - * only. The terminal and units must match the following structure: - * - * ITT_* -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING - * TT_STREAMING -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> OTT_* - * - * The Extension Units, if present, must have a single input pin. The - * Processing Unit and Extension Units can be in any order. Additional - * Extension Units connected to the main chain as single-unit branches are - * also supported. + * Chains are scanned starting at their output terminals and walked backwards. */ static int uvc_scan_device(struct uvc_device *dev) { + struct uvc_video_chain *chain; struct uvc_entity *term; - int found = 0; - /* Check if the control interface matches the structure we expect. */ list_for_each_entry(term, &dev->entities, list) { - struct uvc_streaming *stream; - - if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term)) + if (!UVC_ENTITY_IS_OTERM(term)) continue; - memset(&dev->video, 0, sizeof dev->video); - mutex_init(&dev->video.ctrl_mutex); - INIT_LIST_HEAD(&dev->video.iterms); - INIT_LIST_HEAD(&dev->video.extensions); - dev->video.oterm = term; - dev->video.dev = dev; - if (uvc_scan_chain(&dev->video) < 0) + /* If the terminal is already included in a chain, skip it. + * This can happen for chains that have multiple output + * terminals, where all output terminals beside the first one + * will be inserted in the chain in forward scans. + */ + if (term->chain.next || term->chain.prev) continue; - list_for_each_entry(stream, &dev->streams, list) { - if (stream->header.bTerminalLink == - dev->video.sterm->id) { - uvc_register_video(dev, stream); - found = 1; - break; - } + chain = kzalloc(sizeof(*chain), GFP_KERNEL); + if (chain == NULL) + return -ENOMEM; + + INIT_LIST_HEAD(&chain->iterms); + INIT_LIST_HEAD(&chain->oterms); + INIT_LIST_HEAD(&chain->extensions); + mutex_init(&chain->ctrl_mutex); + chain->dev = dev; + + if (uvc_scan_chain(chain, term) < 0) { + kfree(chain); + continue; } + + uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n", + uvc_print_chain(chain)); + + list_add_tail(&chain->list, &dev->chains); } - if (!found) { + if (list_empty(&dev->chains)) { uvc_printk(KERN_INFO, "No valid video chain found.\n"); return -1; } @@ -1534,6 +1526,133 @@ static int uvc_scan_device(struct uvc_device *dev) return 0; } +/* ------------------------------------------------------------------------ + * Video device registration and unregistration + */ + +/* + * Unregister the video devices. + */ +static void uvc_unregister_video(struct uvc_device *dev) +{ + struct uvc_streaming *stream; + + list_for_each_entry(stream, &dev->streams, list) { + if (stream->vdev == NULL) + continue; + + if (stream->vdev->minor == -1) + video_device_release(stream->vdev); + else + video_unregister_device(stream->vdev); + stream->vdev = NULL; + } +} + +static int uvc_register_video(struct uvc_device *dev, + struct uvc_streaming *stream) +{ + struct video_device *vdev; + int ret; + + /* Initialize the streaming interface with default streaming + * parameters. + */ + ret = uvc_video_init(stream); + if (ret < 0) { + uvc_printk(KERN_ERR, "Failed to initialize the device " + "(%d).\n", ret); + return ret; + } + + /* Register the device with V4L. */ + vdev = video_device_alloc(); + if (vdev == NULL) { + uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n", + ret); + return -ENOMEM; + } + + /* We already hold a reference to dev->udev. The video device will be + * unregistered before the reference is released, so we don't need to + * get another one. + */ + vdev->parent = &dev->intf->dev; + vdev->minor = -1; + vdev->fops = &uvc_fops; + vdev->release = video_device_release; + strlcpy(vdev->name, dev->name, sizeof vdev->name); + + /* Set the driver data before calling video_register_device, otherwise + * uvc_v4l2_open might race us. + */ + stream->vdev = vdev; + video_set_drvdata(vdev, stream); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + uvc_printk(KERN_ERR, "Failed to register video device (%d).\n", + ret); + stream->vdev = NULL; + video_device_release(vdev); + return ret; + } + + return 0; +} + +/* + * Register all video devices in all chains. + */ +static int uvc_register_terms(struct uvc_device *dev, + struct uvc_video_chain *chain, struct list_head *terms) +{ + struct uvc_streaming *stream; + struct uvc_entity *term; + int ret; + + list_for_each_entry(term, terms, chain) { + if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) + continue; + + stream = uvc_stream_by_id(dev, term->id); + if (stream == NULL) { + uvc_printk(KERN_INFO, "No streaming interface found " + "for terminal %u.", term->id); + continue; + } + + stream->chain = chain; + ret = uvc_register_video(dev, stream); + if (ret < 0) + return ret; + } + + return 0; +} + +static int uvc_register_chains(struct uvc_device *dev) +{ + struct uvc_video_chain *chain; + int ret; + + list_for_each_entry(chain, &dev->chains, list) { + ret = uvc_register_terms(dev, chain, &chain->iterms); + if (ret < 0) + return ret; + + ret = uvc_register_terms(dev, chain, &chain->oterms); + if (ret < 0) + return ret; + } + + return 0; +} + +/* ------------------------------------------------------------------------ + * USB probe, disconnect, suspend and resume + */ + /* * Delete the UVC device. * @@ -1555,7 +1674,7 @@ void uvc_delete(struct kref *kref) struct uvc_device *dev = container_of(kref, struct uvc_device, kref); struct list_head *p, *n; - /* Unregister the video device. */ + /* Unregister the video devices. */ uvc_unregister_video(dev); usb_put_intf(dev->intf); usb_put_dev(dev->udev); @@ -1563,6 +1682,12 @@ void uvc_delete(struct kref *kref) uvc_status_cleanup(dev); uvc_ctrl_cleanup_device(dev); + list_for_each_safe(p, n, &dev->chains) { + struct uvc_video_chain *chain; + chain = list_entry(p, struct uvc_video_chain, list); + kfree(chain); + } + list_for_each_safe(p, n, &dev->entities) { struct uvc_entity *entity; entity = list_entry(p, struct uvc_entity, list); @@ -1603,6 +1728,7 @@ static int uvc_probe(struct usb_interface *intf, return -ENOMEM; INIT_LIST_HEAD(&dev->entities); + INIT_LIST_HEAD(&dev->chains); INIT_LIST_HEAD(&dev->streams); kref_init(&dev->kref); atomic_set(&dev->users, 0); @@ -1644,10 +1770,14 @@ static int uvc_probe(struct usb_interface *intf, if (uvc_ctrl_init_device(dev) < 0) goto error; - /* Scan the device for video chains and register video devices. */ + /* Scan the device for video chains. */ if (uvc_scan_device(dev) < 0) goto error; + /* Register video devices. */ + if (uvc_register_chains(dev) < 0) + goto error; + /* Save our data pointer in the interface data. */ usb_set_intfdata(intf, dev); diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c index 3379e1320..b5a16acb6 100644 --- a/linux/drivers/media/video/uvc/uvc_v4l2.c +++ b/linux/drivers/media/video/uvc/uvc_v4l2.c @@ -40,7 +40,7 @@ * table for the controls that can be mapped directly, and handle the others * manually. */ -static int uvc_v4l2_query_menu(struct uvc_video_device *video, +static int uvc_v4l2_query_menu(struct uvc_video_chain *chain, struct v4l2_querymenu *query_menu) { struct uvc_menu_info *menu_info; @@ -49,7 +49,7 @@ static int uvc_v4l2_query_menu(struct uvc_video_device *video, u32 index = query_menu->index; u32 id = query_menu->id; - ctrl = uvc_find_control(video, query_menu->id, &mapping); + ctrl = uvc_find_control(chain, query_menu->id, &mapping); if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) return -EINVAL; @@ -457,7 +457,7 @@ static int uvc_v4l2_open(struct file *file) } } - handle->video = &stream->dev->video; + handle->chain = stream->chain; handle->stream = stream; handle->state = UVC_HANDLE_PASSIVE; file->private_data = handle; @@ -506,7 +506,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); struct uvc_fh *handle = (struct uvc_fh *)file->private_data; - struct uvc_video_device *video = handle->video; + struct uvc_video_chain *chain = handle->chain; struct uvc_streaming *stream = handle->stream; long ret = 0; @@ -533,7 +533,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) /* Get, Set & Query control */ case VIDIOC_QUERYCTRL: - return uvc_query_v4l2_ctrl(video, arg); + return uvc_query_v4l2_ctrl(chain, arg); case VIDIOC_G_CTRL: { @@ -543,12 +543,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&xctrl, 0, sizeof xctrl); xctrl.id = ctrl->id; - ret = uvc_ctrl_begin(video); - if (ret < 0) + ret = uvc_ctrl_begin(chain); + if (ret < 0) return ret; - ret = uvc_ctrl_get(video, &xctrl); - uvc_ctrl_rollback(video); + ret = uvc_ctrl_get(chain, &xctrl); + uvc_ctrl_rollback(chain); if (ret >= 0) ctrl->value = xctrl.value; break; @@ -563,21 +563,21 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) xctrl.id = ctrl->id; xctrl.value = ctrl->value; - ret = uvc_ctrl_begin(video); - if (ret < 0) + uvc_ctrl_begin(chain); + if (ret < 0) return ret; - ret = uvc_ctrl_set(video, &xctrl); + ret = uvc_ctrl_set(chain, &xctrl); if (ret < 0) { - uvc_ctrl_rollback(video); + uvc_ctrl_rollback(chain); return ret; } - ret = uvc_ctrl_commit(video); + ret = uvc_ctrl_commit(chain); break; } case VIDIOC_QUERYMENU: - return uvc_v4l2_query_menu(video, arg); + return uvc_v4l2_query_menu(chain, arg); case VIDIOC_G_EXT_CTRLS: { @@ -585,20 +585,20 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_ext_control *ctrl = ctrls->controls; unsigned int i; - ret = uvc_ctrl_begin(video); - if (ret < 0) + ret = uvc_ctrl_begin(chain); + if (ret < 0) return ret; for (i = 0; i < ctrls->count; ++ctrl, ++i) { - ret = uvc_ctrl_get(video, ctrl); + ret = uvc_ctrl_get(chain, ctrl); if (ret < 0) { - uvc_ctrl_rollback(video); + uvc_ctrl_rollback(chain); ctrls->error_idx = i; return ret; } } ctrls->error_idx = 0; - ret = uvc_ctrl_rollback(video); + ret = uvc_ctrl_rollback(chain); break; } @@ -609,14 +609,14 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_ext_control *ctrl = ctrls->controls; unsigned int i; - ret = uvc_ctrl_begin(video); + ret = uvc_ctrl_begin(chain); if (ret < 0) return ret; for (i = 0; i < ctrls->count; ++ctrl, ++i) { - ret = uvc_ctrl_set(video, ctrl); + ret = uvc_ctrl_set(chain, ctrl); if (ret < 0) { - uvc_ctrl_rollback(video); + uvc_ctrl_rollback(chain); ctrls->error_idx = i; return ret; } @@ -625,31 +625,31 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ctrls->error_idx = 0; if (cmd == VIDIOC_S_EXT_CTRLS) - ret = uvc_ctrl_commit(video); + ret = uvc_ctrl_commit(chain); else - ret = uvc_ctrl_rollback(video); + ret = uvc_ctrl_rollback(chain); break; } /* Get, Set & Enum input */ case VIDIOC_ENUMINPUT: { - const struct uvc_entity *selector = video->selector; + const struct uvc_entity *selector = chain->selector; struct v4l2_input *input = arg; struct uvc_entity *iterm = NULL; u32 index = input->index; int pin = 0; if (selector == NULL || - (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { if (index != 0) return -EINVAL; - iterm = list_first_entry(&video->iterms, + iterm = list_first_entry(&chain->iterms, struct uvc_entity, chain); pin = iterm->id; } else if (pin < selector->selector.bNrInPins) { pin = selector->selector.baSourceID[index]; - list_for_each_entry(iterm, video->iterms.next, chain) { + list_for_each_entry(iterm, chain->iterms.next, chain) { if (iterm->id == pin) break; } @@ -670,14 +670,14 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { u8 input; - if (video->selector == NULL || - (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + if (chain->selector == NULL || + (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { *(int *)arg = 0; break; } - ret = uvc_query_ctrl(video->dev, UVC_GET_CUR, - video->selector->id, video->dev->intfnum, + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, + chain->selector->id, chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL, &input, 1); if (ret < 0) return ret; @@ -693,18 +693,18 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - if (video->selector == NULL || - (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + if (chain->selector == NULL || + (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { if (input != 1) return -EINVAL; break; } - if (input == 0 || input > video->selector->selector.bNrInPins) + if (input == 0 || input > chain->selector->selector.bNrInPins) return -EINVAL; - return uvc_query_ctrl(video->dev, UVC_SET_CUR, - video->selector->id, video->dev->intfnum, + return uvc_query_ctrl(chain->dev, UVC_SET_CUR, + chain->selector->id, chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL, &input, 1); } @@ -1027,10 +1027,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) } case UVCIOC_CTRL_GET: - return uvc_xu_ctrl_query(video, arg, 0); + return uvc_xu_ctrl_query(chain, arg, 0); case UVCIOC_CTRL_SET: - return uvc_xu_ctrl_query(video, arg, 1); + return uvc_xu_ctrl_query(chain, arg, 1); default: if ((ret = v4l_compat_translate_ioctl(file, cmd, arg, diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c index 38cb7a367..792a93a1c 100644 --- a/linux/drivers/media/video/uvc/uvc_video.c +++ b/linux/drivers/media/video/uvc/uvc_video.c @@ -128,7 +128,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, if (data == NULL) return -ENOMEM; - if ((video->dev->quirks & UVC_QUIRK_PROBE_DEF) && query == UVC_GET_DEF) + if ((stream->dev->quirks & UVC_QUIRK_PROBE_DEF) && query == UVC_GET_DEF) return -EIO; ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum, diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h index c385b7bde..30a17f24e 100644 --- a/linux/drivers/media/video/uvc/uvcvideo.h +++ b/linux/drivers/media/video/uvc/uvcvideo.h @@ -81,9 +81,11 @@ struct uvc_xu_control { #define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0) #define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0) #define UVC_ENTITY_IS_ITERM(entity) \ - (((entity)->type & 0x8000) == UVC_TERM_INPUT) + (UVC_ENTITY_IS_TERM(entity) && \ + ((entity)->type & 0x8000) == UVC_TERM_INPUT) #define UVC_ENTITY_IS_OTERM(entity) \ - (((entity)->type & 0x8000) == UVC_TERM_OUTPUT) + (UVC_ENTITY_IS_TERM(entity) && \ + ((entity)->type & 0x8000) == UVC_TERM_OUTPUT) /* ------------------------------------------------------------------------ @@ -403,10 +405,24 @@ struct uvc_video_queue { struct list_head irqqueue; }; +struct uvc_video_chain { + struct uvc_device *dev; + struct list_head list; + + struct list_head iterms; /* Input terminals */ + struct list_head oterms; /* Output terminals */ + struct uvc_entity *processing; /* Processing unit */ + struct uvc_entity *selector; /* Selector unit */ + struct list_head extensions; /* Extension units */ + + struct mutex ctrl_mutex; +}; + struct uvc_streaming { struct list_head list; struct uvc_device *dev; struct video_device *vdev; + struct uvc_video_chain *chain; atomic_t active; struct usb_interface *intf; @@ -447,18 +463,6 @@ struct uvc_streaming { __u8 last_fid; }; -struct uvc_video_device { - struct uvc_device *dev; - - struct list_head iterms; /* Input terminals */ - struct uvc_entity *oterm; /* Output terminal */ - struct uvc_entity *sterm; /* USB streaming terminal */ - struct uvc_entity *processing; - struct uvc_entity *selector; - struct list_head extensions; - struct mutex ctrl_mutex; -}; - enum uvc_device_state { UVC_DEV_DISCONNECTED = 1, }; @@ -481,8 +485,7 @@ struct uvc_device { __u32 clock_frequency; struct list_head entities; - - struct uvc_video_device video; + struct list_head chains; /* Video Streaming interfaces */ struct list_head streams; @@ -501,7 +504,7 @@ enum uvc_handle_state { }; struct uvc_fh { - struct uvc_video_device *video; + struct uvc_video_chain *chain; struct uvc_streaming *stream; enum uvc_handle_state state; }; @@ -619,9 +622,9 @@ extern int uvc_status_suspend(struct uvc_device *dev); extern int uvc_status_resume(struct uvc_device *dev); /* Controls */ -extern struct uvc_control *uvc_find_control(struct uvc_video_device *video, +extern struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, __u32 v4l2_id, struct uvc_control_mapping **mapping); -extern int uvc_query_v4l2_ctrl(struct uvc_video_device *video, +extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct v4l2_queryctrl *v4l2_ctrl); extern int uvc_ctrl_add_info(struct uvc_control_info *info); @@ -631,23 +634,23 @@ extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); extern int uvc_ctrl_resume_device(struct uvc_device *dev); extern void uvc_ctrl_init(void); -extern int uvc_ctrl_begin(struct uvc_video_device *video); -extern int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback); -static inline int uvc_ctrl_commit(struct uvc_video_device *video) +extern int uvc_ctrl_begin(struct uvc_video_chain *chain); +extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback); +static inline int uvc_ctrl_commit(struct uvc_video_chain *chain) { - return __uvc_ctrl_commit(video, 0); + return __uvc_ctrl_commit(chain, 0); } -static inline int uvc_ctrl_rollback(struct uvc_video_device *video) +static inline int uvc_ctrl_rollback(struct uvc_video_chain *chain) { - return __uvc_ctrl_commit(video, 1); + return __uvc_ctrl_commit(chain, 1); } -extern int uvc_ctrl_get(struct uvc_video_device *video, +extern int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); -extern int uvc_ctrl_set(struct uvc_video_device *video, +extern int uvc_ctrl_set(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); -extern int uvc_xu_ctrl_query(struct uvc_video_device *video, +extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control *ctrl, int set); /* Utility functions */ -- cgit v1.2.3 From e18804d1a527fe032a9ca9c4ce0b19bcd1e4b9e5 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 8 Jul 2009 10:21:50 +0200 Subject: gspca - sonixj: Bad sensor init of non ov76xx sensors. From: Jean-Francois Moine The bug was introduced when adding the light frequency control Priority: high Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index a2fb94eb7..915b3a13e 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -1666,6 +1666,8 @@ static void setfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + if (gspca_dev->ctrl_dis & (1 << FREQ_IDX)) + return; if (sd->sensor == SENSOR_OV7660) { switch (sd->freq) { case 0: /* Banding filter disabled */ -- cgit v1.2.3 From c0f8b9a193501684bb2c80845b24d71465941e03 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 8 Jul 2009 11:33:44 +0200 Subject: gspca - sonixj: Remove auto gain/wb/expo for the ov7660 sensor. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 915b3a13e..3d2722704 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -735,13 +735,17 @@ static const u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10}, /* Outformat = rawRGB */ {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */ - {0xd1, 0x21, 0x00, 0x01, 0x74, 0x74, 0x00, 0x10}, + {0xd1, 0x21, 0x00, 0x01, 0x74, 0x92, 0x00, 0x10}, /* GAIN BLUE RED VREF */ {0xd1, 0x21, 0x04, 0x00, 0x7d, 0x62, 0x00, 0x10}, /* COM 1 BAVE GEAVE AECHH */ {0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */ {0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */ +#if 1 + {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xf8, 0x10}, +#else {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10}, +#endif /* AECH CLKRC COM7 COM8 */ {0xc1, 0x21, 0x14, 0x2c, 0x00, 0x02, 0x00, 0x10}, /* COM9 COM10 */ {0xd1, 0x21, 0x17, 0x10, 0x60, 0x02, 0x7b, 0x10}, @@ -1669,16 +1673,23 @@ static void setfreq(struct gspca_dev *gspca_dev) if (gspca_dev->ctrl_dis & (1 << FREQ_IDX)) return; if (sd->sensor == SENSOR_OV7660) { + u8 com8; + +#if 1 + com8 = 0xf8; /* no auto gain/wb/expo */ +#else + com8 = 0xff; +#endif switch (sd->freq) { case 0: /* Banding filter disabled */ - i2c_w1(gspca_dev, 0x13, 0xdf); + i2c_w1(gspca_dev, 0x13, com8 & 0xdf); break; case 1: /* 50 hz */ - i2c_w1(gspca_dev, 0x13, 0xff); + i2c_w1(gspca_dev, 0x13, com8); i2c_w1(gspca_dev, 0x3b, 0x0a); break; case 2: /* 60 hz */ - i2c_w1(gspca_dev, 0x13, 0xff); + i2c_w1(gspca_dev, 0x13, com8); i2c_w1(gspca_dev, 0x3b, 0x02); break; } -- cgit v1.2.3 From 070ce9c8a3bce3b2cea1976e129d0e4731c1538a Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 8 Jul 2009 20:51:35 -0400 Subject: em28xx: make support work for the Pinnacle Hybrid Pro (eb1a:2881) From: Devin Heitmueller Setup the GPIOs properly and enable support for the DVB side of the Pinnacle Hybrid Pro USB stick. Thanks to Andreas Lunderhage for testing patches and providing a remote debug environment. Priority: normal Cc: Andreas Lunderhage Signed-off-by: Devin Heitmueller --- linux/drivers/media/video/em28xx/em28xx-cards.c | 26 ++++++++++++++++++------- linux/drivers/media/video/em28xx/em28xx-dvb.c | 1 + 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index e13f141c7..2fe7a85fc 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -172,6 +172,20 @@ static struct em28xx_reg_seq evga_indtube_digital[] = { { -1, -1, -1, -1}, }; +/* Pinnacle Hybrid Pro eb1a:2881 */ +static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = { + {EM28XX_R08_GPIO, 0x6f, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = { + {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x04, 0xff, 100},/* zl10353 reset */ + {EM2880_R04_GPO, 0x0c, 0xff, 1}, + { -1, -1, -1, -1}, +}; + + /* Callback for the most boards */ static struct em28xx_reg_seq default_tuner_gpio[] = { {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, @@ -1303,29 +1317,26 @@ struct em28xx_board em28xx_boards[] = { }, [EM2881_BOARD_PINNACLE_HYBRID_PRO] = { .name = "Pinnacle Hybrid Pro", - .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, -#if 0 /* FIXME: add an entry at em28xx-dvb */ .has_dvb = 1, - .dvb_gpio = default_digital, -#endif + .dvb_gpio = pinnacle_hybrid_pro_digital, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, - .gpio = default_analog, + .gpio = pinnacle_hybrid_pro_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, - .gpio = default_analog, + .gpio = pinnacle_hybrid_pro_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, - .gpio = default_analog, + .gpio = pinnacle_hybrid_pro_analog, } }, }, [EM2882_BOARD_PINNACLE_HYBRID_PRO] = { @@ -1709,6 +1720,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = { {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, {0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, + {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028}, }; /* I2C devicelist hash table for devices with generic USB IDs */ diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index 86be58641..36c5197ee 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -453,6 +453,7 @@ static int dvb_init(struct em28xx *dev) } break; case EM2880_BOARD_TERRATEC_HYBRID_XS: + case EM2881_BOARD_PINNACLE_HYBRID_PRO: dvb->frontend = dvb_attach(zl10353_attach, &em28xx_terratec_xs_zl10353_xc3028, &dev->i2c_adap); -- cgit v1.2.3 From 0898779f1c75c6821866a08052cd171590af009c Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 8 Jul 2009 21:18:15 -0400 Subject: em28xx: set GPIO properly for Pinnacle Hybrid Pro analog support From: Devin Heitmueller Set the GPIO properly for the analog side of the Pinnacle Hybrid Pro, or else the emp202 doesn't get detected properly. Priority: normal Signed-off-by: Devin Heitmueller --- linux/drivers/media/video/em28xx/em28xx-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 2fe7a85fc..3599d6223 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -174,7 +174,7 @@ static struct em28xx_reg_seq evga_indtube_digital[] = { /* Pinnacle Hybrid Pro eb1a:2881 */ static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = { - {EM28XX_R08_GPIO, 0x6f, ~EM_GPIO_4, 10}, + {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10}, { -1, -1, -1, -1}, }; -- cgit v1.2.3 From e85e9d72ef3cca1749a676ac0e0a99506f2e87c3 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 9 Jul 2009 08:28:46 +0200 Subject: gspca - sunplus: Webcam 052b:1803 added. From: Johannes Goerner Priority: normal Signed-off-by: Johannes Goerner Signed-off-by: Jean-Francois Moine --- linux/Documentation/video4linux/gspca.txt | 1 + linux/drivers/media/video/gspca/sunplus.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index 0ec9acd41..21cafc0bd 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -138,6 +138,7 @@ spca500 04fc:7333 PalmPixDC85 sunplus 04fc:ffff Pure DigitalDakota spca501 0506:00df 3Com HomeConnect Lite sunplus 052b:1513 Megapix V4 +sunplus 052b:1803 MegaImage VI tv8532 0545:808b Veo Stingray tv8532 0545:8333 Veo Stingray sunplus 0546:3155 Polaroid PDC3070 diff --git a/linux/drivers/media/video/gspca/sunplus.c b/linux/drivers/media/video/gspca/sunplus.c index 16bc65dc7..f23594e0b 100644 --- a/linux/drivers/media/video/gspca/sunplus.c +++ b/linux/drivers/media/video/gspca/sunplus.c @@ -52,6 +52,7 @@ struct sd { #define LogitechClickSmart420 2 #define LogitechClickSmart820 3 #define MegapixV4 4 +#define MegaImageVI 5 u8 *jpeg_hdr; }; @@ -875,7 +876,10 @@ static int sd_config(struct gspca_dev *gspca_dev, break; case BRIDGE_SPCA533: cam->cam_mode = custom_mode; - cam->nmodes = sizeof custom_mode / sizeof custom_mode[0]; + if (sd->subtype == MegaImageVI) /* 320x240 only */ + cam->nmodes = ARRAY_SIZE(custom_mode) - 1; + else + cam->nmodes = ARRAY_SIZE(custom_mode); break; case BRIDGE_SPCA504C: cam->cam_mode = vga_mode2; @@ -1017,7 +1021,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA533: */ /* case BRIDGE_SPCA536: */ if (sd->subtype == MegapixV4 || - sd->subtype == LogitechClickSmart820) { + sd->subtype == LogitechClickSmart820 || + sd->subtype == MegaImageVI) { reg_w(gspca_dev, 0xf0, 0, 0, 0); spca504B_WaitCmdStatus(gspca_dev); reg_r(gspca_dev, 0xf0, 4, 0); @@ -1413,6 +1418,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)}, {USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)}, {USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)}, + {USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)}, {USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)}, {USB_DEVICE(0x0546, 0x3191), BS(SPCA504B, 0)}, {USB_DEVICE(0x0546, 0x3273), BS(SPCA504B, 0)}, -- cgit v1.2.3 From 4e59bbd4f49a574bb00ea82a9394afe1f62cd317 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 11 Jul 2009 02:03:43 +0300 Subject: af9013: auto-detect parameters in case of garbage given by app From: Antti Palosaari Request demodulator auto-detect transmission parameters in case of garbage parameters provided by application for compatibility. That's needed at least for MPlayer compatibility currently. Thanks to Jelle de Jong for reporting issue and providing SSH access to Devin for debugging. Thanks to Devin Heitmueller for hard debug work he did to find that bug. Priority: high Signed-off-by: Antti Palosaari Cc: Devin Heitmueller Cc: Jelle de Jong --- linux/drivers/media/dvb/frontends/af9013.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/linux/drivers/media/dvb/frontends/af9013.c b/linux/drivers/media/dvb/frontends/af9013.c index d12d99802..62d3185ee 100644 --- a/linux/drivers/media/dvb/frontends/af9013.c +++ b/linux/drivers/media/dvb/frontends/af9013.c @@ -528,6 +528,10 @@ static int af9013_set_ofdm_params(struct af9013_state *state, u8 i, buf[3] = {0, 0, 0}; *auto_mode = 0; /* set if parameters are requested to auto set */ + /* Try auto-detect transmission parameters in case of AUTO requested or + garbage parameters given by application for compatibility. + MPlayer seems to provide garbage parameters currently. */ + switch (params->transmission_mode) { case TRANSMISSION_MODE_AUTO: *auto_mode = 1; @@ -537,7 +541,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[0] |= (1 << 0); break; default: - return -EINVAL; + deb_info("%s: invalid transmission_mode\n", __func__); + *auto_mode = 1; } switch (params->guard_interval) { @@ -555,7 +560,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[0] |= (3 << 2); break; default: - return -EINVAL; + deb_info("%s: invalid guard_interval\n", __func__); + *auto_mode = 1; } switch (params->hierarchy_information) { @@ -573,7 +579,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[0] |= (3 << 4); break; default: - return -EINVAL; + deb_info("%s: invalid hierarchy_information\n", __func__); + *auto_mode = 1; }; switch (params->constellation) { @@ -588,7 +595,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[1] |= (2 << 6); break; default: - return -EINVAL; + deb_info("%s: invalid constellation\n", __func__); + *auto_mode = 1; } /* Use HP. How and which case we can switch to LP? */ @@ -612,7 +620,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[2] |= (4 << 0); break; default: - return -EINVAL; + deb_info("%s: invalid code_rate_HP\n", __func__); + *auto_mode = 1; } switch (params->code_rate_LP) { @@ -639,7 +648,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, if (params->hierarchy_information == HIERARCHY_AUTO) break; default: - return -EINVAL; + deb_info("%s: invalid code_rate_LP\n", __func__); + *auto_mode = 1; } switch (params->bandwidth) { @@ -652,7 +662,8 @@ static int af9013_set_ofdm_params(struct af9013_state *state, buf[1] |= (2 << 2); break; default: - return -EINVAL; + deb_info("%s: invalid bandwidth\n", __func__); + buf[1] |= (2 << 2); /* cannot auto-detect BW, try 8 MHz */ } /* program */ -- cgit v1.2.3 From 6897951e3af638926c8bf5f660760f4c15fbdee1 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 12 Jul 2009 16:05:02 -0400 Subject: em28xx: fix tuning problem in HVR-900 (R1) From: Devin Heitmueller When the change was introduced in the zl10353 for the i2c gate behavior, this broke the HVR-900 which was not behind a gate. Use a version of the zl10353 config profile that indicates the tuner is not behind such a gate. Without this patch the first tune succeeds, but subsequent tuning attempts will fail. The change also renames the terratec zl10353 profile I wrote to be more generic, since it is shared by the non-terratec device. Thanks to Michael Krufky for providing a HVR-900 and DVB-T environment to test with. Priority: high Cc: Michael Krufky Signed-off-by: Devin Heitmueller --- linux/drivers/media/video/em28xx/em28xx-dvb.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index 86be58641..032eb38d6 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -244,7 +244,7 @@ static struct s5h1409_config em28xx_s5h1409_with_xc3028 = { .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK }; -static struct zl10353_config em28xx_terratec_xs_zl10353_xc3028 = { +static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = { .demod_address = (0x1e >> 1), .no_tuner = 1, .disable_i2c_gate_ctrl = 1, @@ -441,7 +441,6 @@ static int dvb_init(struct em28xx *dev) goto out_free; } break; - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: case EM2880_BOARD_KWORLD_DVB_310U: case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->frontend = dvb_attach(zl10353_attach, @@ -452,9 +451,18 @@ static int dvb_init(struct em28xx *dev) goto out_free; } break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + dvb->frontend = dvb_attach(zl10353_attach, + &em28xx_zl10353_xc3028_no_i2c_gate, + &dev->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; case EM2880_BOARD_TERRATEC_HYBRID_XS: dvb->frontend = dvb_attach(zl10353_attach, - &em28xx_terratec_xs_zl10353_xc3028, + &em28xx_zl10353_xc3028_no_i2c_gate, &dev->i2c_adap); if (dvb->frontend == NULL) { /* This board could have either a zl10353 or a mt352. -- cgit v1.2.3 From 13f1c7cf90c6107ba936578859f469bae7e31a96 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 12 Jul 2009 16:51:12 -0400 Subject: em28xx: make tuning work for Terratec Cinergy T XS USB (mt352 variant) From: Devin Heitmueller The Terratec Cinergy T XS USB can have either a zl10353 or an mt352. Add support for the MT352 variant. Thanks to Jelle de Jong for providing a unit to test/debug with. Priority: normal Cc: Jelle de Jong Signed-off-by: Devin Heitmueller --- linux/drivers/media/video/em28xx/em28xx-dvb.c | 47 +++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index 86be58641..56833da59 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -32,6 +32,8 @@ #include "lgdt330x.h" #include "zl10353.h" #include "s5h1409.h" +#include "mt352.h" +#include "mt352_priv.h" /* FIXME */ MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab "); @@ -259,6 +261,41 @@ static struct drx397xD_config em28xx_drx397xD_with_xc3028 = { }; #endif +static int mt352_terratec_xs_init(struct dvb_frontend *fe) +{ + /* Values extracted from a USB trace of the Terratec Windows driver */ + static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x2c }; + static u8 reset[] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0xa0 }; + static u8 input_freq_cfg[] = { INPUT_FREQ_1, 0x31, 0xb8 }; + static u8 rs_err_cfg[] = { RS_ERR_PER_1, 0x00, 0x4d }; + static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; + static u8 trl_nom_cfg[] = { TRL_NOMINAL_RATE_1, 0x64, 0x00 }; + static u8 tps_given_cfg[] = { TPS_GIVEN_1, 0x40, 0x80, 0x50 }; + static u8 tuner_go[] = { TUNER_GO, 0x5d}; + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(200); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + mt352_write(fe, input_freq_cfg, sizeof(input_freq_cfg)); + mt352_write(fe, rs_err_cfg, sizeof(rs_err_cfg)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + mt352_write(fe, trl_nom_cfg, sizeof(trl_nom_cfg)); + mt352_write(fe, tps_given_cfg, sizeof(tps_given_cfg)); + mt352_write(fe, tuner_go, sizeof(tuner_go)); + return 0; +} + +static struct mt352_config terratec_xs_mt352_cfg = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .if2 = 45600, + .demod_init = mt352_terratec_xs_init, +}; + /* ------------------------------------------------------------------ */ static int attach_xc3028(u8 addr, struct em28xx *dev) @@ -459,13 +496,11 @@ static int dvb_init(struct em28xx *dev) if (dvb->frontend == NULL) { /* This board could have either a zl10353 or a mt352. If the chip id isn't for zl10353, try mt352 */ - - /* FIXME: make support for mt352 work */ - printk(KERN_ERR "version of this board with mt352 not " - "currently supported\n"); - result = -EINVAL; - goto out_free; + dvb->frontend = dvb_attach(mt352_attach, + &terratec_xs_mt352_cfg, + &dev->i2c_adap); } + if (attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; -- cgit v1.2.3 From fa9a1b962b866f12ef1eaa0fd14cc9f828b251d6 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 12 Jul 2009 17:44:19 -0400 Subject: em28xx: fix typo in mt352 init sequence for Terratec Cinergy T XS USB From: Devin Heitmueller Andy walls pointed out that we were passing 0x5d to the TUNER_GO register, instead of 0x01. Set the register properly (note the code did still work with the incorrect value, so this does not address a regression). Thanks to Andy Walls for noticing the issue. Priority: normal Cc: Andy Walls Signed-off-by: Devin Heitmueller --- linux/drivers/media/video/em28xx/em28xx-dvb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index 56833da59..ca881d7c0 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -273,7 +273,7 @@ static int mt352_terratec_xs_init(struct dvb_frontend *fe) static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; static u8 trl_nom_cfg[] = { TRL_NOMINAL_RATE_1, 0x64, 0x00 }; static u8 tps_given_cfg[] = { TPS_GIVEN_1, 0x40, 0x80, 0x50 }; - static u8 tuner_go[] = { TUNER_GO, 0x5d}; + static u8 tuner_go[] = { TUNER_GO, 0x01}; mt352_write(fe, clock_config, sizeof(clock_config)); udelay(200); -- cgit v1.2.3 From 1dc7f8632084630ef1289d688a85629fbc39d059 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jul 2009 01:03:37 -0300 Subject: mt9v011: implement VIDIOC_QUERYCTRL From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/mt9v011.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/linux/drivers/media/video/mt9v011.c b/linux/drivers/media/video/mt9v011.c index 505529e3e..947a23ac0 100644 --- a/linux/drivers/media/video/mt9v011.c +++ b/linux/drivers/media/video/mt9v011.c @@ -225,6 +225,23 @@ static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return -EINVAL; } +static int mt9v011_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + int i; + + v4l2_dbg(1, debug, sd, "queryctrl called\n"); + + for (i = 0; i < ARRAY_SIZE(mt9v011_qctrl); i++) + if (qc->id && qc->id == mt9v011_qctrl[i].id) { + memcpy(qc, &(mt9v011_qctrl[i]), + sizeof(*qc)); + return 0; + } + + return -EINVAL; +} + + static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct mt9v011 *core = to_mt9v011(sd); @@ -348,6 +365,7 @@ static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, } static const struct v4l2_subdev_core_ops mt9v011_core_ops = { + .queryctrl = mt9v011_queryctrl, .g_ctrl = mt9v011_g_ctrl, .s_ctrl = mt9v011_s_ctrl, .reset = mt9v011_reset, -- cgit v1.2.3 From 8c6e58b4adfb075524553ae7b390f8ea7406370d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jul 2009 01:28:21 -0300 Subject: em28xx: call sensor detection code for all webcam entries From: Mauro Carvalho Chehab With the previous approach, autodetection were working only for the two generic entries (em275x and em2820 unknown ones). So, if someone would try to force probing an specific device, the code would not properly run the autodetection code. With the new approach, the sensor autodetection will be run not only for the two generic entries, but also do webcam specific ones. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 5f063e4a1..56aa11a46 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -247,6 +247,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_UNKNOWN] = { .name = "Unknown EM2750/28xx video grabber", .tuner_type = TUNER_ABSENT, + .is_webcam = 1, /* To enable sensor probe */ }, [EM2750_BOARD_DLCW_130] = { /* Beijing Huaqi Information Digital Technology Co., Ltd */ @@ -1790,10 +1791,6 @@ static int em28xx_hint_sensor(struct em28xx *dev) __be16 version_be; u16 version; - if (dev->model != EM2820_BOARD_UNKNOWN && - dev->model != EM2750_BOARD_UNKNOWN) - return 0; - dev->i2c_client.addr = 0xba >> 1; cmd = 0; i2c_master_send(&dev->i2c_client, &cmd, 1); @@ -1848,10 +1845,7 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_info("chip ID is em2750\n"); break; case CHIP_ID_EM2820: - if (dev->board.is_webcam) - em28xx_info("chip is em2710\n"); - else - em28xx_info("chip ID is em2820\n"); + em28xx_info("chip ID is em2710 or em2820\n"); break; case CHIP_ID_EM2840: em28xx_info("chip ID is em2840\n"); @@ -2531,7 +2525,13 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, return errCode; } - em28xx_hint_sensor(dev); + /* + * If the device can be a webcam, seek for a sensor. + * If sensor is not found, then it isn't a webcam. + */ + if (dev->board.is_webcam) + if (em28xx_hint_sensor(dev) < 0) + dev->board.is_webcam = 0; /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); -- cgit v1.2.3 From 50d2769688b47965b7ef2a6b60eba8d80d5dc156 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 13 Jul 2009 20:15:02 -0300 Subject: em28xx: fix webcam scaling From: Mauro Carvalho Chehab While trying to fix an mt9v001 webcam, I noticed that HSCALE/VSCALE do work with em28xx + webcam. The issue is that the scaling setup depends on the number of visible rows/cols of the input image. With mt9v011 (Silvercrest), the resolution is 640x480. So, the scaling is different from a normal TV image (720x480 on NTSC). This were causing a wrong scaling and a previous patch disabled scaling. As each sensor have their different resolution setting, the xres/yres should be adjusted accordingly with the input sensor. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 6 +++--- linux/drivers/media/video/em28xx/em28xx-core.c | 5 +---- linux/drivers/media/video/em28xx/em28xx-video.c | 16 +++------------- linux/drivers/media/video/em28xx/em28xx.h | 15 +++++++++++---- 4 files changed, 18 insertions(+), 24 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 56aa11a46..d5ebfb8be 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -59,8 +59,6 @@ static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); -#define MT9V011_VERSION 0x8243 - /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */ static unsigned long em28xx_devused; @@ -1801,10 +1799,12 @@ static int em28xx_hint_sensor(struct em28xx *dev) version = be16_to_cpu(version_be); switch (version) { - case MT9V011_VERSION: + case 0x8243: /* mt9v011 640x480 1.3 Mpix sensor */ dev->model = EM2820_BOARD_SILVERCREST_WEBCAM; sensor_name = "mt9v011"; dev->em28xx_sensor = EM28XX_MT9V011; + dev->sensor_xres = 640; + dev->sensor_yres = 480; break; default: printk("Unknown Micron Sensor 0x%04x\n", be16_to_cpu(version)); diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 420e343f2..22481e5dd 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -707,10 +707,7 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) u8 mode; /* the em2800 scaler only supports scaling down to 50% */ - if (dev->board.is_webcam) { - /* FIXME: Don't use the scaler yet */ - mode = 0; - } else if (dev->board.is_em2800) { + if (dev->board.is_em2800) { mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00); } else { u8 buf[2]; diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index e447c9f0a..cef676bf8 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -679,8 +679,8 @@ static void get_scale(struct em28xx *dev, unsigned int width, unsigned int height, unsigned int *hscale, unsigned int *vscale) { - unsigned int maxw = norm_maxw(dev); - unsigned int maxh = norm_maxh(dev); + unsigned int maxw = norm_maxw(dev); + unsigned int maxh = norm_maxh(dev); *hscale = (((unsigned long)maxw) << 12) / width - 4096L; if (*hscale >= 0x4000) @@ -748,11 +748,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } - if (dev->board.is_webcam) { - /* FIXME: This is the only supported fmt */ - width = 640; - height = 480; - } else if (dev->board.is_em2800) { + if (dev->board.is_em2800) { /* the em2800 can only scale down to 50% */ height = height > (3 * maxh / 4) ? maxh : maxh / 2; width = width > (3 * maxw / 4) ? maxw : maxw / 2; @@ -789,12 +785,6 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, { struct em28xx_fmt *fmt; - /* FIXME: This is the only supported fmt */ - if (dev->board.is_webcam) { - width = 640; - height = 480; - } - fmt = format_by_fourcc(fourcc); if (!fmt) return -EINVAL; diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 559903b2a..40b228438 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -488,6 +488,7 @@ struct em28xx { struct em28xx_board board; enum em28xx_sensor em28xx_sensor; + int sensor_xres, sensor_yres; unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; @@ -769,17 +770,23 @@ static inline int em28xx_gamma_set(struct em28xx *dev, s32 val) /*FIXME: maxw should be dependent of alt mode */ static inline unsigned int norm_maxw(struct em28xx *dev) { + if (dev->board.is_webcam) + return dev->sensor_xres; + if (dev->board.max_range_640_480) return 640; - else - return 720; + + return 720; } static inline unsigned int norm_maxh(struct em28xx *dev) { + if (dev->board.is_webcam) + return dev->sensor_yres; + if (dev->board.max_range_640_480) return 480; - else - return (dev->norm & V4L2_STD_625_50) ? 576 : 480; + + return (dev->norm & V4L2_STD_625_50) ? 576 : 480; } #endif -- cgit v1.2.3 From 3c16945bd290ac09d7aa8a70c69d8f359eb53a09 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Jul 2009 02:38:18 -0300 Subject: mt9v011: add a function to calculate frames per second rate From: Mauro Carvalho Chehab It is possible to adjust the fps rate by changing some register values. This is function of the connected Xtal at the camera sensor, being a 27 MHz cristal needed, in order to support 640x480 at 30 fps. For now, it will only calculate the values for fps. Later patches may introduce V4L2 ioctls, to allow frequency rate adjustments. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/mt9v011.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/video/mt9v011.c b/linux/drivers/media/video/mt9v011.c index 947a23ac0..43420d311 100644 --- a/linux/drivers/media/video/mt9v011.c +++ b/linux/drivers/media/video/mt9v011.c @@ -9,6 +9,7 @@ #include "compat.h" #include #include +#include #include #include "mt9v011.h" #include @@ -67,6 +68,7 @@ static struct v4l2_queryctrl mt9v011_qctrl[] = { struct mt9v011 { struct v4l2_subdev sd; unsigned width, height; + unsigned xtal; u16 global_gain, red_bal, blue_bal; }; @@ -141,7 +143,7 @@ static const struct i2c_reg_value mt9v011_init_default[] = { { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, { R20_MT9V011_READ_MODE, 0x1000 }, - { R07_MT9V011_OUT_CTRL, 0x000a }, /* chip enable */ + { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ }; static void set_balance(struct v4l2_subdev *sd) @@ -164,6 +166,31 @@ static void set_balance(struct v4l2_subdev *sd) mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); } +static void calc_fps(struct v4l2_subdev *sd) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned height, width, hblank, vblank, speed; + unsigned row_time, t_time; + u64 frames_per_ms; + unsigned tmp; + + height = mt9v011_read(sd, R03_MT9V011_HEIGHT); + width = mt9v011_read(sd, R04_MT9V011_WIDTH); + hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); + vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); + speed = mt9v011_read(sd, R0A_MT9V011_CLK_SPEED); + + row_time = (width + 113 + hblank) * (speed + 2); + t_time = row_time * (height + vblank + 1); + + frames_per_ms = core->xtal * 1000l; + do_div(frames_per_ms, t_time); + tmp = frames_per_ms; + + v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", + tmp / 1000, tmp % 1000, t_time); +} + static void set_res(struct v4l2_subdev *sd) { struct mt9v011 *core = to_mt9v011(sd); @@ -189,6 +216,8 @@ static void set_res(struct v4l2_subdev *sd) mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); + + calc_fps(sd); }; static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) @@ -423,6 +452,7 @@ static int mt9v011_probe(struct i2c_client *c, core->global_gain = 0x0024; core->width = 640; core->height = 480; + core->xtal = 27000000; /* Hz */ v4l_info(c, "chip found @ 0x%02x (%s)\n", c->addr << 1, c->adapter->name); -- cgit v1.2.3 From bd8a28419f128638347a77d46de11461f0c17fd6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Jul 2009 02:39:19 -0300 Subject: mt9v011: Fix vstart From: Mauro Carvalho Chehab vstart calculus were wrong. Fix it. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/mt9v011.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/mt9v011.c b/linux/drivers/media/video/mt9v011.c index 43420d311..46b5a3cf5 100644 --- a/linux/drivers/media/video/mt9v011.c +++ b/linux/drivers/media/video/mt9v011.c @@ -212,7 +212,7 @@ static void set_res(struct v4l2_subdev *sd) mt9v011_write(sd, R04_MT9V011_WIDTH, core->width); mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width); - vstart = 8 + (640 - core->height) / 2; + vstart = 8 + (480 - core->height) / 2; mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); -- cgit v1.2.3 From a8d41efd6cd091346a1276f0c7d818874b10bc36 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Jul 2009 03:14:21 -0300 Subject: mt9v011: implement core->s_config to allow adjusting xtal frequency From: Mauro Carvalho Chehab Since frames per second is a function of cristal frequency, and this is device-specific, add a function that allows adjusting it, via subdev->core->s_config callback. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/mt9v011.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/linux/drivers/media/video/mt9v011.c b/linux/drivers/media/video/mt9v011.c index 46b5a3cf5..bf68ea63f 100644 --- a/linux/drivers/media/video/mt9v011.c +++ b/linux/drivers/media/video/mt9v011.c @@ -350,6 +350,22 @@ static int mt9v011_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) return 0; } +static int mt9v011_s_config(struct v4l2_subdev *sd, int dumb, void *data) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned *xtal = data; + + v4l2_dbg(1, debug, sd, "s_config called\n"); + + if (xtal) { + core->xtal = *xtal; + v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n", + *xtal / 1000000, (*xtal / 1000) % 1000); + } + + return 0; +} + #ifdef CONFIG_VIDEO_ADV_DEBUG static int mt9v011_g_register(struct v4l2_subdev *sd, @@ -398,6 +414,7 @@ static const struct v4l2_subdev_core_ops mt9v011_core_ops = { .g_ctrl = mt9v011_g_ctrl, .s_ctrl = mt9v011_s_ctrl, .reset = mt9v011_reset, + .s_config = mt9v011_s_config, .g_chip_ident = mt9v011_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9v011_g_register, -- cgit v1.2.3 From 0edfef5c337eb0bd9d0146246fae01a308faea4f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Jul 2009 03:18:39 -0300 Subject: em28xx: allow specifying sensor xtal frequency From: Mauro Carvalho Chehab In order to properly estimate fps, mt9v011 sensor driver needs to know what is the used frequency on the sensor cristal. Adds the proper fields and initialization code for specifying the cristal frequency. Also, based on experimentation, it was noticed that the Silvercrest is outputing data at 7 fps. This means that it should be using a 6.3 MHz cristal. This information needs to be double checked later, by opening the device. Anyway, by using this value for xtal, at least now we have the correct fps report. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 11 ++++++++--- linux/drivers/media/video/em28xx/em28xx.h | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index d5ebfb8be..75032271d 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1805,6 +1805,7 @@ static int em28xx_hint_sensor(struct em28xx *dev) dev->em28xx_sensor = EM28XX_MT9V011; dev->sensor_xres = 640; dev->sensor_yres = 480; + dev->sensor_xtal = 6300000; break; default: printk("Unknown Micron Sensor 0x%04x\n", be16_to_cpu(version)); @@ -2366,9 +2367,13 @@ void em28xx_card_setup(struct em28xx *dev) v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, "tvp5150", "tvp5150", tvp5150_addrs); - if (dev->em28xx_sensor == EM28XX_MT9V011) - v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "mt9v011", "mt9v011", mt9v011_addrs); + if (dev->em28xx_sensor == EM28XX_MT9V011) { + struct v4l2_subdev *sd; + + sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "mt9v011", "mt9v011", mt9v011_addrs); + v4l2_subdev_call(sd, core, s_config, 0, &dev->sensor_xtal); + } if (dev->board.adecoder == EM28XX_TVAUDIO) v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 40b228438..26011bf37 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -487,8 +487,10 @@ struct em28xx { struct v4l2_device v4l2_dev; struct em28xx_board board; + /* Webcam specific fields */ enum em28xx_sensor em28xx_sensor; int sensor_xres, sensor_yres; + int sensor_xtal; unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; -- cgit v1.2.3 From f4a61c7eab17e012ce6341745b57b329d9feda98 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Jul 2009 12:33:25 -0300 Subject: em28xx: adjust vinmode/vinctl based on the stream input format From: Mauro Carvalho Chehab Depending on the video input format, vinmode/vinctl needs adjustments. For TV, this is not relevant, since the supported decoders output data at the same format. However, webcam sensors may have different formats, so, this needs to be adjusted based on the device. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 11 +++++++++++ linux/drivers/media/video/em28xx/em28xx-core.c | 17 +++-------------- linux/drivers/media/video/em28xx/em28xx.h | 3 +++ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 75032271d..b40a01e91 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1806,6 +1806,11 @@ static int em28xx_hint_sensor(struct em28xx *dev) dev->sensor_xres = 640; dev->sensor_yres = 480; dev->sensor_xtal = 6300000; + + /* probably means GRGB 16 bit bayer */ + dev->vinmode = 0x0d; + dev->vinctl = 0x00; + break; default: printk("Unknown Micron Sensor 0x%04x\n", be16_to_cpu(version)); @@ -2530,6 +2535,12 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, return errCode; } + /* + * Default format, used for tvp5150 or saa711x output formats + */ + dev->vinmode = 0x10; + dev->vinctl = 0x11; + /* * If the device can be a webcam, seek for a sensor. * If sensor is not found, then it isn't a webcam. diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 22481e5dd..f24af40df 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -648,28 +648,17 @@ int em28xx_capture_start(struct em28xx *dev, int start) int em28xx_set_outfmt(struct em28xx *dev) { int ret; - int vinmode, vinctl, outfmt; - - outfmt = dev->format->reg; - - if (dev->board.is_webcam) { - vinmode = 0x0d; - vinctl = 0x00; - } else { - vinmode = 0x10; - vinctl = 0x11; - } ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT, - outfmt | 0x20, 0xff); + dev->format->reg | 0x20, 0xff); if (ret < 0) return ret; - ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, vinmode); + ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode); if (ret < 0) return ret; - return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctl); + return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, dev->vinctl); } static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 26011bf37..783799324 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -492,6 +492,9 @@ struct em28xx { int sensor_xres, sensor_yres; int sensor_xtal; + /* Vinmode/Vinctl used at the driver */ + int vinmode, vinctl; + unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; unsigned int has_alsa_audio:1; -- cgit v1.2.3 From 9a656496a57cf7f0b434af1b04d02c20e3976cbc Mon Sep 17 00:00:00 2001 From: Andreas Oberritter Date: Wed, 15 Jul 2009 01:28:50 +0200 Subject: Add two new ioctls: DMX_ADD_PID and DMX_REMOVE_PID From: Andreas Oberritter DMX_ADD_PID allows to add multiple PIDs to a transport stream filter previously set up with DMX_SET_PES_FILTER and output=DMX_OUT_TSDEMUX_TAP. DMX_REMOVE_PID is used to drop a PID from a filter. These ioctls are to be used by readers of /dev/dvb/adapterX/demuxY. They may be called at any time, i.e. before or after the first filter on the shared file descriptor was started. They make it possible to record multiple services without the need to de- or re-multiplex TS packets. To accomplish this, dmxdev_filter->feed.ts has been converted to a list of struct dmxdev_feeds, each containing a PID value and a pointer to a struct dmx_ts_feed. Priority: normal Signed-off-by: Andreas Oberritter --- linux/drivers/media/dvb/dvb-core/dmxdev.c | 231 ++++++++++++++++++++++-------- linux/drivers/media/dvb/dvb-core/dmxdev.h | 9 +- linux/include/linux/dvb/dmx.h | 2 + 3 files changed, 184 insertions(+), 58 deletions(-) diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.c b/linux/drivers/media/dvb/dvb-core/dmxdev.c index 6d6121eb5..3750ff48c 100644 --- a/linux/drivers/media/dvb/dvb-core/dmxdev.c +++ b/linux/drivers/media/dvb/dvb-core/dmxdev.c @@ -430,6 +430,8 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, /* stop feed but only mark the specified filter as stopped (state set) */ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter) { + struct dmxdev_feed *feed; + dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); switch (dmxdevfilter->type) { @@ -438,7 +440,8 @@ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter) dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec); break; case DMXDEV_TYPE_PES: - dmxdevfilter->feed.ts->stop_filtering(dmxdevfilter->feed.ts); + list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) + feed->ts->stop_filtering(feed->ts); break; default: return -EINVAL; @@ -449,13 +452,23 @@ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter) /* start feed associated with the specified filter */ static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter) { + struct dmxdev_feed *feed; + int ret; + dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO); switch (filter->type) { case DMXDEV_TYPE_SEC: return filter->feed.sec->start_filtering(filter->feed.sec); case DMXDEV_TYPE_PES: - return filter->feed.ts->start_filtering(filter->feed.ts); + list_for_each_entry(feed, &filter->feed.ts, next) { + ret = feed->ts->start_filtering(feed->ts); + if (ret < 0) { + dvb_dmxdev_feed_stop(filter); + return ret; + } + } + break; default: return -EINVAL; } @@ -487,6 +500,9 @@ static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter) static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter) { + struct dmxdev_feed *feed; + struct dmx_demux *demux; + if (dmxdevfilter->state < DMXDEV_STATE_GO) return 0; @@ -503,13 +519,12 @@ static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter) dmxdevfilter->feed.sec = NULL; break; case DMXDEV_TYPE_PES: - if (!dmxdevfilter->feed.ts) - break; dvb_dmxdev_feed_stop(dmxdevfilter); - dmxdevfilter->dev->demux-> - release_ts_feed(dmxdevfilter->dev->demux, - dmxdevfilter->feed.ts); - dmxdevfilter->feed.ts = NULL; + demux = dmxdevfilter->dev->demux; + list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) { + demux->release_ts_feed(demux, feed->ts); + feed->ts = NULL; + } break; default: if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED) @@ -521,19 +536,88 @@ static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter) return 0; } +static void dvb_dmxdev_delete_pids(struct dmxdev_filter *dmxdevfilter) +{ + struct dmxdev_feed *feed, *tmp; + + /* delete all PIDs */ + list_for_each_entry_safe(feed, tmp, &dmxdevfilter->feed.ts, next) { + list_del(&feed->next); + kfree(feed); + } + + BUG_ON(!list_empty(&dmxdevfilter->feed.ts)); +} + static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter) { if (dmxdevfilter->state < DMXDEV_STATE_SET) return 0; + if (dmxdevfilter->type == DMXDEV_TYPE_PES) + dvb_dmxdev_delete_pids(dmxdevfilter); + dmxdevfilter->type = DMXDEV_TYPE_NONE; dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); return 0; } +static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev, + struct dmxdev_filter *filter, + struct dmxdev_feed *feed) +{ + struct timespec timeout = { 0 }; + struct dmx_pes_filter_params *para = &filter->params.pes; + dmx_output_t otype; + int ret; + int ts_type; + enum dmx_ts_pes ts_pes; + struct dmx_ts_feed *tsfeed; + + feed->ts = NULL; + otype = para->output; + + ts_pes = (enum dmx_ts_pes)para->pes_type; + + if (ts_pes < DMX_PES_OTHER) + ts_type = TS_DECODER; + else + ts_type = 0; + + if (otype == DMX_OUT_TS_TAP) + ts_type |= TS_PACKET; + else if (otype == DMX_OUT_TSDEMUX_TAP) + ts_type |= TS_PACKET | TS_DEMUX; + else if (otype == DMX_OUT_TAP) + ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY; + + ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, &feed->ts, + dvb_dmxdev_ts_callback); + if (ret < 0) + return ret; + + tsfeed = feed->ts; + tsfeed->priv = filter; + + ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout); + if (ret < 0) { + dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed); + return ret; + } + + ret = tsfeed->start_filtering(tsfeed); + if (ret < 0) { + dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed); + return ret; + } + + return 0; +} + static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter) { struct dmxdev *dmxdev = filter->dev; + struct dmxdev_feed *feed; void *mem; int ret, i; @@ -631,56 +715,14 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter) break; } case DMXDEV_TYPE_PES: - { - struct timespec timeout = { 0 }; - struct dmx_pes_filter_params *para = &filter->params.pes; - dmx_output_t otype; - int ts_type; - enum dmx_ts_pes ts_pes; - struct dmx_ts_feed **tsfeed = &filter->feed.ts; - - filter->feed.ts = NULL; - otype = para->output; - - ts_pes = (enum dmx_ts_pes)para->pes_type; - - if (ts_pes < DMX_PES_OTHER) - ts_type = TS_DECODER; - else - ts_type = 0; - - if (otype == DMX_OUT_TS_TAP) - ts_type |= TS_PACKET; - else if (otype == DMX_OUT_TSDEMUX_TAP) - ts_type |= TS_PACKET | TS_DEMUX; - else if (otype == DMX_OUT_TAP) - ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY; - - ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, - tsfeed, - dvb_dmxdev_ts_callback); - if (ret < 0) - return ret; - - (*tsfeed)->priv = filter; - - ret = (*tsfeed)->set(*tsfeed, para->pid, ts_type, ts_pes, - 32768, timeout); - if (ret < 0) { - dmxdev->demux->release_ts_feed(dmxdev->demux, - *tsfeed); - return ret; - } - - ret = filter->feed.ts->start_filtering(filter->feed.ts); - if (ret < 0) { - dmxdev->demux->release_ts_feed(dmxdev->demux, - *tsfeed); - return ret; + list_for_each_entry(feed, &filter->feed.ts, next) { + ret = dvb_dmxdev_start_feed(dmxdev, filter, feed); + if (ret < 0) { + dvb_dmxdev_filter_stop(filter); + return ret; + } } - break; - } default: return -EINVAL; } @@ -718,7 +760,7 @@ static int dvb_demux_open(struct inode *inode, struct file *file) dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192); dmxdevfilter->type = DMXDEV_TYPE_NONE; dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); - dmxdevfilter->feed.ts = NULL; + INIT_LIST_HEAD(&dmxdevfilter->feed.ts); init_timer(&dmxdevfilter->timer); dvbdev->users++; @@ -760,6 +802,55 @@ static inline void invert_mode(dmx_filter_t *filter) filter->mode[i] ^= 0xff; } +static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev, + struct dmxdev_filter *filter, u16 pid) +{ + struct dmxdev_feed *feed; + + if ((filter->type != DMXDEV_TYPE_PES) || + (filter->state < DMXDEV_STATE_SET)) + return -EINVAL; + + /* only TS packet filters may have multiple PIDs */ + if ((filter->params.pes.output != DMX_OUT_TSDEMUX_TAP) && + (!list_empty(&filter->feed.ts))) + return -EINVAL; + + feed = kzalloc(sizeof(struct dmxdev_feed), GFP_KERNEL); + if (feed == NULL) + return -ENOMEM; + + feed->pid = pid; + list_add(&feed->next, &filter->feed.ts); + + if (filter->state >= DMXDEV_STATE_GO) + return dvb_dmxdev_start_feed(dmxdev, filter, feed); + + return 0; +} + +static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev, + struct dmxdev_filter *filter, u16 pid) +{ + struct dmxdev_feed *feed, *tmp; + + if ((filter->type != DMXDEV_TYPE_PES) || + (filter->state < DMXDEV_STATE_SET)) + return -EINVAL; + + list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) { + if ((feed->pid == pid) && (feed->ts != NULL)) { + feed->ts->stop_filtering(feed->ts); + filter->dev->demux->release_ts_feed(filter->dev->demux, + feed->ts); + list_del(&feed->next); + kfree(feed); + } + } + + return 0; +} + static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev, struct dmxdev_filter *dmxdevfilter, struct dmx_sct_filter_params *params) @@ -784,7 +875,10 @@ static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev, struct dmxdev_filter *dmxdevfilter, struct dmx_pes_filter_params *params) { + int ret; + dvb_dmxdev_filter_stop(dmxdevfilter); + dvb_dmxdev_filter_reset(dmxdevfilter); if (params->pes_type > DMX_PES_OTHER || params->pes_type < 0) return -EINVAL; @@ -795,6 +889,11 @@ static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev, dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); + ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, + dmxdevfilter->params.pes.pid); + if (ret < 0) + return ret; + if (params->flags & DMX_IMMEDIATE_START) return dvb_dmxdev_filter_start(dmxdevfilter); @@ -958,6 +1057,24 @@ static int dvb_demux_do_ioctl(struct inode *inode, struct file *file, &((struct dmx_stc *)parg)->base); break; + case DMX_ADD_PID: + if (mutex_lock_interruptible(&dmxdevfilter->mutex)) { + ret = -ERESTARTSYS; + break; + } + ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, *(u16 *)parg); + mutex_unlock(&dmxdevfilter->mutex); + break; + + case DMX_REMOVE_PID: + if (mutex_lock_interruptible(&dmxdevfilter->mutex)) { + ret = -ERESTARTSYS; + break; + } + ret = dvb_dmxdev_remove_pid(dmxdev, dmxdevfilter, *(u16 *)parg); + mutex_unlock(&dmxdevfilter->mutex); + break; + default: ret = -EINVAL; break; diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.h b/linux/drivers/media/dvb/dvb-core/dmxdev.h index 7e8137d0b..be63783ad 100644 --- a/linux/drivers/media/dvb/dvb-core/dmxdev.h +++ b/linux/drivers/media/dvb/dvb-core/dmxdev.h @@ -54,13 +54,20 @@ enum dmxdev_state { DMXDEV_STATE_TIMEDOUT }; +struct dmxdev_feed { + u16 pid; + struct dmx_ts_feed *ts; + struct list_head next; +}; + struct dmxdev_filter { union { struct dmx_section_filter *sec; } filter; union { - struct dmx_ts_feed *ts; + /* list of TS and PES feeds (struct dmxdev_feed) */ + struct list_head ts; struct dmx_section_feed *sec; } feed; diff --git a/linux/include/linux/dvb/dmx.h b/linux/include/linux/dvb/dmx.h index fef943738..f078f3ac8 100644 --- a/linux/include/linux/dvb/dmx.h +++ b/linux/include/linux/dvb/dmx.h @@ -151,5 +151,7 @@ struct dmx_stc { #define DMX_GET_CAPS _IOR('o', 48, dmx_caps_t) #define DMX_SET_SOURCE _IOW('o', 49, dmx_source_t) #define DMX_GET_STC _IOWR('o', 50, struct dmx_stc) +#define DMX_ADD_PID _IOW('o', 51, __u16) +#define DMX_REMOVE_PID _IOW('o', 52, __u16) #endif /*_DVBDMX_H_*/ -- cgit v1.2.3 From 02d22c9b0bfab80d3180aac81f95ca44084fc116 Mon Sep 17 00:00:00 2001 From: Andreas Oberritter Date: Wed, 15 Jul 2009 01:48:37 +0200 Subject: Remove a useless check from dvb_dmx_swfilter_packet() From: Andreas Oberritter Values for 'pid' range from 0 to 0x1fff. Therefore 'feed->pid' can never be equal to both 'pid' and 0x2000. This makes the continue statement have no effect. Priority: normal Signed-off-by: Andreas Oberritter --- linux/drivers/media/dvb/dvb-core/dvb_demux.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/linux/drivers/media/dvb/dvb-core/dvb_demux.c b/linux/drivers/media/dvb/dvb-core/dvb_demux.c index cfe2768d2..eef6d3616 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_demux.c @@ -425,13 +425,9 @@ no_dvb_demux_tscheck: if ((DVR_FEED(feed)) && (dvr_done++)) continue; - if (feed->pid == pid) { + if (feed->pid == pid) dvb_dmx_swfilter_packet_type(feed, buf); - if (DVR_FEED(feed)) - continue; - } - - if (feed->pid == 0x2000) + else if (feed->pid == 0x2000) feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK); } } -- cgit v1.2.3 From 780de7a15fcc2833d415c0ddf001647ac3b24df8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Jul 2009 21:08:22 -0300 Subject: em28xx: add support for mt9m001 webcams From: Mauro Carvalho Chehab Thanks to Wally for bringing the issue and helping with the tests. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 51 +++++++++++++++++++++++++ linux/drivers/media/video/em28xx/em28xx.h | 1 + 2 files changed, 52 insertions(+) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index b40a01e91..6d80e9a38 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -224,6 +224,7 @@ struct em28xx_board em28xx_boards[] = { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, .amux = EM28XX_AMUX_VIDEO, + .gpio = silvercrest_reg_seq, } }, }, [EM2800_BOARD_UNKNOWN] = { @@ -1777,6 +1778,32 @@ static inline void em28xx_set_model(struct em28xx *dev) EM28XX_I2C_FREQ_100_KHZ; } +/* FIXME: Should be replaced by a proper mt9m001 driver */ +static int em28xx_initialize_mt9m001(struct em28xx *dev) +{ + int i; + unsigned char regs[][3] = { + { 0x0d, 0x00, 0x01, }, + { 0x0d, 0x00, 0x00, }, + { 0x04, 0x05, 0x00, }, /* hres = 1280 */ + { 0x03, 0x04, 0x00, }, /* vres = 1024 */ + { 0x20, 0x11, 0x00, }, + { 0x06, 0x00, 0x10, }, + { 0x2b, 0x00, 0x24, }, + { 0x2e, 0x00, 0x24, }, + { 0x35, 0x00, 0x24, }, + { 0x2d, 0x00, 0x20, }, + { 0x2c, 0x00, 0x20, }, + { 0x09, 0x0a, 0xd4, }, + { 0x35, 0x00, 0x57, }, + }; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client, ®s[i][0], 3); + + return 0; +} + /* HINT method: webcam I2C chips * * This method work for webcams with Micron sensors @@ -1811,6 +1838,23 @@ static int em28xx_hint_sensor(struct em28xx *dev) dev->vinmode = 0x0d; dev->vinctl = 0x00; + break; +#if 0 + case 0x8411: + case 0x8421: +#endif + case 0x8431: + dev->model = EM2750_BOARD_UNKNOWN; + sensor_name = "mt9m001"; + dev->em28xx_sensor = EM28XX_MT9M001; + em28xx_initialize_mt9m001(dev); + dev->sensor_xres = 1280; + dev->sensor_yres = 1024; + + /* probably means BGGR 16 bit bayer */ + dev->vinmode = 0x0c; + dev->vinctl = 0x00; + break; default: printk("Unknown Micron Sensor 0x%04x\n", be16_to_cpu(version)); @@ -2380,6 +2424,13 @@ void em28xx_card_setup(struct em28xx *dev) v4l2_subdev_call(sd, core, s_config, 0, &dev->sensor_xtal); } +#if 0 + /* FIXME: use mt9m001 after their conversion to v4l dev/subdev */ + if (dev->em28xx_sensor == EM28XX_MT9M001) + v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "mt9m001", "mt9m001", mt9v011_addrs); +#endif + if (dev->board.adecoder == EM28XX_TVAUDIO) v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "tvaudio", "tvaudio", dev->board.tvaudio_addr); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 783799324..df7fe9af7 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -367,6 +367,7 @@ enum em28xx_decoder { enum em28xx_sensor { EM28XX_NOSENSOR = 0, EM28XX_MT9V011, + EM28XX_MT9M001, }; enum em28xx_adecoder { -- cgit v1.2.3 From d4a045723fae91cc87a8dd7c71f47bf6fc6ae09f Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Tue, 14 Jul 2009 23:35:47 -0400 Subject: em28xx: Make sure the tuner is initialized if generic empia USB id was used From: Devin Heitmueller In cases where the device has a generic Empia USB ID, the call in the precard setup phase did not set the tuner GPIO. As a result, the tuner may not be taken out of reset before attempting initialization in the analog driver. This problem was not seen before with the EVGA inDtube, since that particular board has the analog GPIO setup to include taking the tuner out of reset. Thanks to Andreas Lunderhage for testing patches and providing a remote debug environment for the Pinnacle 320e. Priority: normal Cc: Andreas Lunderhage Signed-off-by: Devin Heitmueller --- linux/drivers/media/video/em28xx/em28xx-cards.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 3599d6223..3be05dcc1 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -2342,6 +2342,7 @@ void em28xx_card_setup(struct em28xx *dev) em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, so make the call now so the analog GPIOs are set properly before probing the i2c bus. */ + em28xx_gpio_set(dev, dev->board.tuner_gpio); em28xx_set_mode(dev, EM28XX_ANALOG_MODE); break; case EM2820_BOARD_SILVERCREST_WEBCAM: -- cgit v1.2.3 From 5d42bf58c3798d11494482f0c4ce11e4264e5fab Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Tue, 14 Jul 2009 23:37:22 -0400 Subject: em28xx: set demod profile for Pinnacle Hybrid Pro 320e From: Devin Heitmueller The Pinnacle Hybrid Pro 320e was missing a demod config for the xc3028, which is required for digital tuning to work properly. Add the missing profile. Thanks to Andreas Lunderhage for testing patches and providing a remote debug environment. Priority: normal Cc: Andreas Lunderhage Signed-off-by: Devin Heitmueller --- linux/drivers/media/video/em28xx/em28xx-cards.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 3be05dcc1..a608f0906 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -2012,6 +2012,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) ctl->demod = XC3028_FE_ZARLINK456; break; case EM2880_BOARD_TERRATEC_HYBRID_XS: + case EM2881_BOARD_PINNACLE_HYBRID_PRO: ctl->demod = XC3028_FE_ZARLINK456; break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: -- cgit v1.2.3 From 340a2011aeb2b8f88e241a9165733518fb97a61c Mon Sep 17 00:00:00 2001 From: Antoine Jacquet Date: Wed, 15 Jul 2009 20:54:55 -0300 Subject: zr364xx: implement V4L2_CAP_STREAMING From: Lamarque Vieira Souza This patch implements V4L2_CAP_STREAMING for the zr364xx driver, by converting the driver to use videobuf. This version is synced with v4l-dvb as of 15/Jul/2009. Tested with Creative PC-CAM 880. It basically: . implements V4L2_CAP_STREAMING using videobuf; . re-implements V4L2_CAP_READWRITE using videobuf; . copies cam->udev->product to the card field of the v4l2_capability struct. That gives more information to the users about the webcam; . moves the brightness setting code from before requesting a frame (in read_frame) to the vidioc_s_ctrl ioctl. This way the brightness code is executed only when the application requests a change in brightness and not before every frame read; . comments part of zr364xx_vidioc_try_fmt_vid_cap that says that Skype + libv4l do not work. This patch fixes zr364xx for applications such as mplayer, Kopete+libv4l and Skype+libv4l can make use of the webcam that comes with zr364xx chip. Signed-off-by: Lamarque V. Souza Signed-off-by: Antoine Jacquet --- linux/drivers/media/video/zr364xx.c | 1165 ++++++++++++++++++++++++++++------- 1 file changed, 938 insertions(+), 227 deletions(-) diff --git a/linux/drivers/media/video/zr364xx.c b/linux/drivers/media/video/zr364xx.c index 8746f067d..7dff47834 100644 --- a/linux/drivers/media/video/zr364xx.c +++ b/linux/drivers/media/video/zr364xx.c @@ -1,5 +1,5 @@ /* - * Zoran 364xx based USB webcam module version 0.72 + * Zoran 364xx based USB webcam module version 0.73 * * Allows you to use your USB webcam with V4L2 applications * This is still in heavy developpement ! @@ -10,6 +10,8 @@ * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers * V4L2 version inspired by meye.c driver * + * Some video buffer code by Lamarque based on s2255drv.c and vivi.c drivers. + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -35,25 +37,34 @@ #include #include #include +#include #include "compat.h" /* Version Information */ -#define DRIVER_VERSION "v0.72" +#define DRIVER_VERSION "v0.73" +#define ZR364_VERSION_CODE KERNEL_VERSION(0, 7, 3) #define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" #define DRIVER_DESC "Zoran 364xx" /* Camera */ -#define FRAMES 2 +#define FRAMES 1 #define MAX_FRAME_SIZE 100000 #define BUFFER_SIZE 0x1000 #define CTRL_TIMEOUT 500 +#define ZR364XX_DEF_BUFS 4 +#define ZR364XX_READ_IDLE 0 +#define ZR364XX_READ_FRAME 1 /* Debug macro */ -#define DBG(x...) if (debug) printk(KERN_INFO KBUILD_MODNAME x) - +#define DBG(fmt, args...) \ + do { \ + if (debug) { \ + printk(KERN_INFO KBUILD_MODNAME " " fmt, ##args); \ + } \ + } while (0) /* Init methods, need to find nicer names for these * the exact names of the chipsets would be the best if someone finds it */ @@ -102,24 +113,93 @@ static struct usb_device_id device_table[] = { MODULE_DEVICE_TABLE(usb, device_table); +struct zr364xx_mode { + u32 color; /* output video color format */ + u32 brightness; /* brightness */ +}; + +/* frame structure */ +struct zr364xx_framei { + unsigned long ulState; /* ulState:ZR364XX_READ_IDLE, + ZR364XX_READ_FRAME */ + void *lpvbits; /* image data */ + unsigned long cur_size; /* current data copied to it */ +}; + +/* image buffer structure */ +struct zr364xx_bufferi { + unsigned long dwFrames; /* number of frames in buffer */ + struct zr364xx_framei frame[FRAMES]; /* array of FRAME structures */ +}; + +struct zr364xx_dmaqueue { + struct list_head active; + struct zr364xx_camera *cam; +}; + +struct zr364xx_pipeinfo { + u32 transfer_size; + u8 *transfer_buffer; + u32 state; + void *stream_urb; + void *cam; /* back pointer to zr364xx_camera struct */ + u32 err_count; + u32 idx; +}; + +struct zr364xx_fmt { + char *name; + u32 fourcc; + int depth; +}; + +/* image formats. */ +static const struct zr364xx_fmt formats[] = { + { + .name = "JPG", + .fourcc = V4L2_PIX_FMT_JPEG, + .depth = 24 + } +}; /* Camera stuff */ struct zr364xx_camera { struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface;/* the interface for this device */ struct video_device *vdev; /* v4l video device */ - u8 *framebuf; int nb; - unsigned char *buffer; + struct zr364xx_bufferi buffer; int skip; - int brightness; int width; int height; int method; struct mutex lock; + struct mutex open_lock; int users; + + spinlock_t slock; + struct zr364xx_dmaqueue vidq; + int resources; + int last_frame; + int cur_frame; + unsigned long frame_count; + int b_acquire; + struct zr364xx_pipeinfo pipe[1]; + + u8 read_endpoint; + + const struct zr364xx_fmt *fmt; + struct videobuf_queue vb_vidq; + enum v4l2_buf_type type; + struct zr364xx_mode mode; }; +/* buffer for one video frame */ +struct zr364xx_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + const struct zr364xx_fmt *fmt; +}; /* function used to send initialisation commands to the camera */ static int send_control_msg(struct usb_device *udev, u8 request, u16 value, @@ -273,139 +353,116 @@ static unsigned char header2[] = { }; static unsigned char header3; +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct zr364xx_camera *cam = vq->priv_data; -/********************/ -/* V4L2 integration */ -/********************/ + *size = cam->width * cam->height * (cam->fmt->depth >> 3); -/* this function reads a full JPEG picture synchronously - * TODO: do it asynchronously... */ -static int read_frame(struct zr364xx_camera *cam, int framenum) -{ - int i, n, temp, head, size, actual_length; - unsigned char *ptr = NULL, *jpeg; - - redo: - /* hardware brightness */ - n = send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); - temp = (0x60 << 8) + 127 - cam->brightness; - n = send_control_msg(cam->udev, 1, temp, 0, NULL, 0); - - /* during the first loop we are going to insert JPEG header */ - head = 0; - /* this is the place in memory where we are going to build - * the JPEG image */ - jpeg = cam->framebuf + framenum * MAX_FRAME_SIZE; - /* read data... */ - do { - n = usb_bulk_msg(cam->udev, - usb_rcvbulkpipe(cam->udev, 0x81), - cam->buffer, BUFFER_SIZE, &actual_length, - CTRL_TIMEOUT); - DBG("buffer : %d %d", cam->buffer[0], cam->buffer[1]); - DBG("bulk : n=%d size=%d", n, actual_length); - if (n < 0) { - dev_err(&cam->udev->dev, "error reading bulk msg\n"); - return 0; - } - if (actual_length < 0 || actual_length > BUFFER_SIZE) { - dev_err(&cam->udev->dev, "wrong number of bytes\n"); - return 0; - } + if (*count == 0) + *count = ZR364XX_DEF_BUFS; - /* swap bytes if camera needs it */ - if (cam->method == METHOD0) { - u16 *buf = (u16*)cam->buffer; - for (i = 0; i < BUFFER_SIZE/2; i++) - swab16s(buf + i); - } + while (*size * (*count) > ZR364XX_DEF_BUFS * 1024 * 1024) + (*count)--; - /* write the JPEG header */ - if (!head) { - DBG("jpeg header"); - ptr = jpeg; - memcpy(ptr, header1, sizeof(header1)); - ptr += sizeof(header1); - header3 = 0; - memcpy(ptr, &header3, 1); - ptr++; - memcpy(ptr, cam->buffer, 64); - ptr += 64; - header3 = 1; - memcpy(ptr, &header3, 1); - ptr++; - memcpy(ptr, cam->buffer + 64, 64); - ptr += 64; - memcpy(ptr, header2, sizeof(header2)); - ptr += sizeof(header2); - memcpy(ptr, cam->buffer + 128, - actual_length - 128); - ptr += actual_length - 128; - head = 1; - DBG("header : %d %d %d %d %d %d %d %d %d", - cam->buffer[0], cam->buffer[1], cam->buffer[2], - cam->buffer[3], cam->buffer[4], cam->buffer[5], - cam->buffer[6], cam->buffer[7], cam->buffer[8]); - } else { - memcpy(ptr, cam->buffer, actual_length); - ptr += actual_length; - } - } - /* ... until there is no more */ - while (actual_length == BUFFER_SIZE); + return 0; +} - /* we skip the 2 first frames which are usually buggy */ - if (cam->skip) { - cam->skip--; - goto redo; - } +static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf) +{ + DBG("%s\n", __func__); - /* go back to find the JPEG EOI marker */ - size = ptr - jpeg; - ptr -= 2; - while (ptr > jpeg) { - if (*ptr == 0xFF && *(ptr + 1) == 0xD9 - && *(ptr + 2) == 0xFF) - break; - ptr--; - } - if (ptr == jpeg) - DBG("No EOI marker"); + if (in_interrupt()) + BUG(); - /* Sometimes there is junk data in the middle of the picture, - * we want to skip this bogus frames */ - while (ptr > jpeg) { - if (*ptr == 0xFF && *(ptr + 1) == 0xFF - && *(ptr + 2) == 0xFF) - break; - ptr--; + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct zr364xx_camera *cam = vq->priv_data; + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + int rc; + + DBG("%s, field=%d, fmt name = %s\n", __func__, field, cam->fmt != NULL ? + cam->fmt->name : ""); + if (cam->fmt == NULL) + return -EINVAL; + + buf->vb.size = cam->width * cam->height * (cam->fmt->depth >> 3); + + if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) { + DBG("invalid buffer prepare\n"); + return -EINVAL; } - if (ptr != jpeg) { - DBG("Bogus frame ? %d", cam->nb); - goto redo; + + buf->fmt = cam->fmt; + buf->vb.width = cam->width; + buf->vb.height = cam->height; + buf->vb.field = field; + + if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; } - DBG("jpeg : %d %d %d %d %d %d %d %d", - jpeg[0], jpeg[1], jpeg[2], jpeg[3], - jpeg[4], jpeg[5], jpeg[6], jpeg[7]); + buf->vb.state = VIDEOBUF_PREPARED; + return 0; +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + struct zr364xx_camera *cam = vq->priv_data; + + DBG("%s\n", __func__); - return size; + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &cam->vidq.active); } +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + + DBG("%s\n", __func__); + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops zr364xx_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/********************/ +/* V4L2 integration */ +/********************/ +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type); -static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t cnt, +static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count, loff_t * ppos) { - unsigned long count = cnt; - struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam; + struct zr364xx_camera *cam = video_drvdata(file); - DBG("zr364xx_read: read %d bytes.", (int) count); - - if (vdev == NULL) - return -ENODEV; - cam = video_get_drvdata(vdev); + DBG("%s\n", __func__); if (!buf) return -EINVAL; @@ -413,21 +470,275 @@ static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t cnt, if (!count) return -EINVAL; - /* NoMan Sux ! */ - count = read_frame(cam, 0); + if (cam->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + zr364xx_vidioc_streamon(file, cam, cam->type) == 0) { + DBG("%s: reading %d bytes at pos %d.\n", __func__, (int) count, + (int) *ppos); + + /* NoMan Sux ! */ + return videobuf_read_one(&cam->vb_vidq, buf, count, ppos, + file->f_flags & O_NONBLOCK); + } + + return 0; +} + +/* video buffer vmalloc implementation based partly on VIVI driver which is + * Copyright (c) 2006 by + * Mauro Carvalho Chehab + * Ted Walther + * John Sokol + * http://v4l.videotechnology.com/ + * + */ +static void zr364xx_fillbuff(struct zr364xx_camera *cam, + struct zr364xx_buffer *buf, + int jpgsize) +{ + int pos = 0; + struct timeval ts; + const char *tmpbuf; + char *vbuf = videobuf_to_vmalloc(&buf->vb); + unsigned long last_frame; + struct zr364xx_framei *frm; + + if (!vbuf) + return; + + last_frame = cam->last_frame; + if (last_frame != -1) { + frm = &cam->buffer.frame[last_frame]; + tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits; + switch (buf->fmt->fourcc) { + case V4L2_PIX_FMT_JPEG: + buf->vb.size = jpgsize; + memcpy(vbuf, tmpbuf, buf->vb.size); + break; + default: + printk(KERN_DEBUG KBUILD_MODNAME ": unknown format?\n"); + } + cam->last_frame = -1; + } else { + printk(KERN_ERR KBUILD_MODNAME ": =======no frame\n"); + return; + } + DBG("%s: Buffer 0x%08lx size= %d\n", __func__, + (unsigned long)vbuf, pos); + /* tell v4l buffer was filled */ + + buf->vb.field_count = cam->frame_count * 2; + do_gettimeofday(&ts); + buf->vb.ts = ts; + buf->vb.state = VIDEOBUF_DONE; +} + +static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) +{ + struct zr364xx_dmaqueue *dma_q = &cam->vidq; + struct zr364xx_buffer *buf; + unsigned long flags = 0; + int rc = 0; + + DBG("wakeup: %p\n", &dma_q); + spin_lock_irqsave(&cam->slock, flags); + + if (list_empty(&dma_q->active)) { + DBG("No active queue to serve\n"); + rc = -1; + goto unlock; + } + buf = list_entry(dma_q->active.next, + struct zr364xx_buffer, vb.queue); + + if (!waitqueue_active(&buf->vb.done)) { + /* no one active */ + rc = -1; + goto unlock; + } + list_del(&buf->vb.queue); + do_gettimeofday(&buf->vb.ts); + DBG("[%p/%d] wakeup\n", buf, buf->vb.i); + zr364xx_fillbuff(cam, buf, jpgsize); + wake_up(&buf->vb.done); + DBG("wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); +unlock: + spin_unlock_irqrestore(&cam->slock, flags); + return 0; +} + +/* this function moves the usb stream read pipe data + * into the system buffers. + * returns 0 on success, EAGAIN if more data to process (call this + * function again). + */ +static int zr364xx_read_video_callback(struct zr364xx_camera *cam, + struct zr364xx_pipeinfo *pipe_info, + struct urb *purb) +{ + unsigned char *pdest; + unsigned char *psrc; + s32 idx = -1; + struct zr364xx_framei *frm; + int i = 0; + unsigned char *ptr = NULL; + + /*DBG("buffer to user\n");*/ + idx = cam->cur_frame; + frm = &cam->buffer.frame[idx]; + + /* swap bytes if camera needs it */ + if (cam->method == METHOD0) { + u16 *buf = (u16 *)pipe_info->transfer_buffer; + for (i = 0; i < purb->actual_length/2; i++) + swab16s(buf + i); + } + + /* search done. now find out if should be acquiring */ + if (!cam->b_acquire) { + /* we found a frame, but this channel is turned off */ + frm->ulState = ZR364XX_READ_IDLE; + return -EINVAL; + } + + if (frm->lpvbits == NULL) { + DBG("%s: frame buffer == NULL.%p %p %d\n", __func__, + frm, cam, idx); + return -ENOMEM; + } - if (copy_to_user(buf, cam->framebuf, count)) - return -EFAULT; + psrc = (u8 *)pipe_info->transfer_buffer; + ptr = pdest = frm->lpvbits; + + if (frm->ulState == ZR364XX_READ_IDLE) { + frm->ulState = ZR364XX_READ_FRAME; + frm->cur_size = 0; + + DBG("jpeg header, "); + memcpy(ptr, header1, sizeof(header1)); + ptr += sizeof(header1); + header3 = 0; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, psrc, 64); + ptr += 64; + header3 = 1; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, psrc + 64, 64); + ptr += 64; + memcpy(ptr, header2, sizeof(header2)); + ptr += sizeof(header2); + memcpy(ptr, psrc + 128, + purb->actual_length - 128); + ptr += purb->actual_length - 128; + DBG("header : %d %d %d %d %d %d %d %d %d\n", + psrc[0], psrc[1], psrc[2], + psrc[3], psrc[4], psrc[5], + psrc[6], psrc[7], psrc[8]); + frm->cur_size = ptr - pdest; + } else { + pdest += frm->cur_size; + memcpy(pdest, psrc, purb->actual_length); + frm->cur_size += purb->actual_length; + } + /*DBG("cur_size %lu urb size %d\n", frm->cur_size, + purb->actual_length);*/ + + if (purb->actual_length < pipe_info->transfer_size) { + DBG("****************Buffer[%d]full*************\n", idx); + cam->last_frame = cam->cur_frame; + cam->cur_frame++; + /* end of system frame ring buffer, start at zero */ + if (cam->cur_frame == cam->buffer.dwFrames) + cam->cur_frame = 0; + + /* frame ready */ + /* go back to find the JPEG EOI marker */ + ptr = pdest = frm->lpvbits; + ptr += frm->cur_size - 2; + while (ptr > pdest) { + if (*ptr == 0xFF && *(ptr + 1) == 0xD9 + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr == pdest) + DBG("No EOI marker\n"); + + /* Sometimes there is junk data in the middle of the picture, + * we want to skip this bogus frames */ + while (ptr > pdest) { + if (*ptr == 0xFF && *(ptr + 1) == 0xFF + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr != pdest) { + DBG("Bogus frame ? %d\n", ++(cam->nb)); + } else if (cam->b_acquire) { + /* we skip the 2 first frames which are usually buggy */ + if (cam->skip) + cam->skip--; + else { + DBG("jpeg(%lu): %d %d %d %d %d %d %d %d\n", + frm->cur_size, + pdest[0], pdest[1], pdest[2], pdest[3], + pdest[4], pdest[5], pdest[6], pdest[7]); + + zr364xx_got_frame(cam, frm->cur_size); + } + } + cam->frame_count++; + frm->ulState = ZR364XX_READ_IDLE; + frm->cur_size = 0; + } + /* done successfully */ + return 0; +} + +static int res_get(struct zr364xx_camera *cam) +{ + /* is it free? */ + mutex_lock(&cam->lock); + if (cam->resources) { + /* no, someone else uses it */ + mutex_unlock(&cam->lock); + return 0; + } + /* it's free, grab it */ + cam->resources = 1; + DBG("res: get\n"); + mutex_unlock(&cam->lock); + return 1; +} - return count; +static inline int res_check(struct zr364xx_camera *cam) +{ + return cam->resources; } +static void res_free(struct zr364xx_camera *cam) +{ + mutex_lock(&cam->lock); + cam->resources = 0; + mutex_unlock(&cam->lock); + DBG("res: put\n"); +} static int zr364xx_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - strcpy(cap->driver, DRIVER_DESC); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + struct zr364xx_camera *cam = video_drvdata(file); + + strlcpy(cap->driver, DRIVER_DESC, sizeof(cap->driver)); + strlcpy(cap->card, cam->udev->product, sizeof(cap->card)); + strlcpy(cap->bus_info, dev_name(&cam->udev->dev), + sizeof(cap->bus_info)); + cap->version = ZR364_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + return 0; } @@ -459,12 +770,11 @@ static int zr364xx_vidioc_s_input(struct file *file, void *priv, static int zr364xx_vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; - if (vdev == NULL) + if (file == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); + cam = video_drvdata(file); switch (c->id) { case V4L2_CID_BRIGHTNESS: @@ -473,7 +783,7 @@ static int zr364xx_vidioc_queryctrl(struct file *file, void *priv, c->minimum = 0; c->maximum = 127; c->step = 1; - c->default_value = cam->brightness; + c->default_value = cam->mode.brightness; c->flags = 0; break; default: @@ -485,36 +795,42 @@ static int zr364xx_vidioc_queryctrl(struct file *file, void *priv, static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *c) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; + int temp; - if (vdev == NULL) + if (file == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); + cam = video_drvdata(file); switch (c->id) { case V4L2_CID_BRIGHTNESS: - cam->brightness = c->value; + cam->mode.brightness = c->value; + /* hardware brightness */ + mutex_lock(&cam->lock); + send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); + temp = (0x60 << 8) + 127 - cam->mode.brightness; + send_control_msg(cam->udev, 1, temp, 0, NULL, 0); + mutex_unlock(&cam->lock); break; default: return -EINVAL; } + return 0; } static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *c) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; - if (vdev == NULL) + if (file == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); + cam = video_drvdata(file); switch (c->id) { case V4L2_CID_BRIGHTNESS: - c->value = cam->brightness; + c->value = cam->mode.brightness; break; default: return -EINVAL; @@ -528,26 +844,36 @@ static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file, if (f->index > 0) return -EINVAL; f->flags = V4L2_FMT_FLAG_COMPRESSED; - strcpy(f->description, "JPEG"); - f->pixelformat = V4L2_PIX_FMT_JPEG; + strcpy(f->description, formats[0].name); + f->pixelformat = formats[0].fourcc; return 0; } +static char *decode_fourcc(__u32 pixelformat, char *buf) +{ + buf[0] = pixelformat & 0xff; + buf[1] = (pixelformat >> 8) & 0xff; + buf[2] = (pixelformat >> 16) & 0xff; + buf[3] = (pixelformat >> 24) & 0xff; + buf[4] = '\0'; + return buf; +} + static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam; + struct zr364xx_camera *cam = video_drvdata(file); + char pixelformat_name[5]; - if (vdev == NULL) + if (cam == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) - return -EINVAL; - if (f->fmt.pix.field != V4L2_FIELD_ANY && - f->fmt.pix.field != V4L2_FIELD_NONE) + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) { + DBG("%s: unsupported pixelformat V4L2_PIX_FMT_%s\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name)); return -EINVAL; + } + f->fmt.pix.field = V4L2_FIELD_NONE; f->fmt.pix.width = cam->width; f->fmt.pix.height = cam->height; @@ -555,20 +881,22 @@ static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = 0; f->fmt.pix.priv = 0; + DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), + f->fmt.pix.field); return 0; } static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; - if (vdev == NULL) + if (file == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); + cam = video_drvdata(file); - f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; + f->fmt.pix.pixelformat = formats[0].fourcc; f->fmt.pix.field = V4L2_FIELD_NONE; f->fmt.pix.width = cam->width; f->fmt.pix.height = cam->height; @@ -579,21 +907,26 @@ static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, return 0; } +/* Lamarque TODO: implement changing resolution on the fly */ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam; + struct zr364xx_camera *cam = video_drvdata(file); + struct videobuf_queue *q = &cam->vb_vidq; + char pixelformat_name[5]; + int ret = zr364xx_vidioc_try_fmt_vid_cap(file, cam, f); - if (vdev == NULL) - return -ENODEV; - cam = video_get_drvdata(vdev); + if (ret < 0) + return ret; + + mutex_lock(&q->vb_lock); + + if (videobuf_queue_is_busy(&cam->vb_vidq)) { + DBG("%s queue busy\n", __func__); + ret = -EBUSY; + goto out; + } - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) - return -EINVAL; - if (f->fmt.pix.field != V4L2_FIELD_ANY && - f->fmt.pix.field != V4L2_FIELD_NONE) - return -EINVAL; f->fmt.pix.field = V4L2_FIELD_NONE; f->fmt.pix.width = cam->width; f->fmt.pix.height = cam->height; @@ -601,19 +934,265 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = 0; f->fmt.pix.priv = 0; - DBG("ok!"); + cam->vb_vidq.field = f->fmt.pix.field; + cam->mode.color = V4L2_PIX_FMT_JPEG; + ret = 0; + +out: + mutex_unlock(&q->vb_lock); + + DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), + f->fmt.pix.field); + return ret; +} + +static int zr364xx_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + rc = videobuf_reqbufs(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_querybuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + rc = videobuf_querybuf(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_qbuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + DBG("%s\n", __func__); + rc = videobuf_qbuf(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_dqbuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + DBG("%s\n", __func__); + rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK); + return rc; +} + +static void read_pipe_completion(struct urb *purb) +{ + struct zr364xx_pipeinfo *pipe_info; + struct zr364xx_camera *cam; + int pipe; + + pipe_info = purb->context; + /*DBG("%s %p, status %d\n", __func__, purb, purb->status);*/ + if (pipe_info == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); + return; + } + + cam = pipe_info->cam; + if (cam == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); + return; + } + + /* if shutting down, do not resubmit, exit immediately */ + if (purb->status == -ESHUTDOWN) { + DBG("%s, err shutdown\n", __func__); + pipe_info->err_count++; + return; + } + + if (pipe_info->state == 0) { + DBG("exiting USB pipe\n"); + return; + } + + if (purb->actual_length < 0 || + purb->actual_length > pipe_info->transfer_size) { + dev_err(&cam->udev->dev, "wrong number of bytes\n"); + return; + } + + if (purb->status == 0) + zr364xx_read_video_callback(cam, pipe_info, purb); + else { + pipe_info->err_count++; + DBG("%s: failed URB %d\n", __func__, purb->status); + } + + pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); + + /* reuse urb */ + usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->transfer_size, + read_pipe_completion, pipe_info); + + if (pipe_info->state != 0) { + purb->status = usb_submit_urb(pipe_info->stream_urb, + GFP_ATOMIC); + + if (purb->status) + dev_err(&cam->udev->dev, + "error submitting urb (error=%i)\n", + purb->status); + } else + DBG("read pipe complete state 0\n"); +} + +static int zr364xx_start_readpipe(struct zr364xx_camera *cam) +{ + int pipe; + int retval; + struct zr364xx_pipeinfo *pipe_info = cam->pipe; + pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); + DBG("%s: start pipe IN x%x\n", __func__, cam->read_endpoint); + + pipe_info->state = 1; + pipe_info->err_count = 0; + pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pipe_info->stream_urb) { + dev_err(&cam->udev->dev, "ReadStream: Unable to alloc URB\n"); + return -ENOMEM; + } + /* transfer buffer allocated in board_init */ + usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->transfer_size, + read_pipe_completion, pipe_info); + + DBG("submitting URB %p\n", pipe_info->stream_urb); + retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); + if (retval) { + printk(KERN_ERR KBUILD_MODNAME ": start read pipe failed\n"); + return retval; + } + + return 0; +} + +static void zr364xx_stop_readpipe(struct zr364xx_camera *cam) +{ + struct zr364xx_pipeinfo *pipe_info; + + if (cam == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": invalid device\n"); + return; + } + DBG("stop read pipe\n"); + pipe_info = cam->pipe; + if (pipe_info) { + if (pipe_info->state != 0) + pipe_info->state = 0; + + if (pipe_info->stream_urb) { + /* cancel urb */ + usb_kill_urb(pipe_info->stream_urb); + usb_free_urb(pipe_info->stream_urb); + pipe_info->stream_urb = NULL; + } + } + DBG("stop read pipe\n"); + return; +} + +/* starts acquisition process */ +static int zr364xx_start_acquire(struct zr364xx_camera *cam) +{ + int j; + + DBG("start acquire\n"); + + cam->last_frame = -1; + cam->cur_frame = 0; + for (j = 0; j < FRAMES; j++) { + cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[j].cur_size = 0; + } + return 0; +} + +static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam) +{ + cam->b_acquire = 0; return 0; } static int zr364xx_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - return 0; + struct zr364xx_camera *cam = video_drvdata(file); + int j; + int res; + + DBG("%s\n", __func__); + + if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev_err(&cam->udev->dev, "invalid fh type0\n"); + return -EINVAL; + } + if (cam->type != type) { + dev_err(&cam->udev->dev, "invalid fh type1\n"); + return -EINVAL; + } + + if (!res_get(cam)) { + dev_err(&cam->udev->dev, "stream busy\n"); + return -EBUSY; + } + + cam->last_frame = -1; + cam->cur_frame = 0; + cam->frame_count = 0; + for (j = 0; j < FRAMES; j++) { + cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[j].cur_size = 0; + } + res = videobuf_streamon(&cam->vb_vidq); + if (res == 0) { + zr364xx_start_acquire(cam); + cam->b_acquire = 1; + } else { + res_free(cam); + } + DBG("%s: %d\n", __func__, res); + return res; } static int zr364xx_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { + int res; + struct zr364xx_camera *cam = video_drvdata(file); + + DBG("%s\n", __func__); + if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev_err(&cam->udev->dev, "invalid fh type0\n"); + return -EINVAL; + } + if (cam->type != type) { + dev_err(&cam->udev->dev, "invalid fh type1\n"); + return -EINVAL; + } + zr364xx_stop_acquire(cam); + res = videobuf_streamoff(&cam->vb_vidq); + if (res < 0) + return res; + res_free(cam); return 0; } @@ -622,28 +1201,19 @@ static int zr364xx_vidioc_streamoff(struct file *file, void *priv, static int zr364xx_open(struct file *file) { struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam = video_get_drvdata(vdev); + struct zr364xx_camera *cam = video_drvdata(file); struct usb_device *udev = cam->udev; int i, err; - DBG("zr364xx_open"); + DBG("%s\n", __func__); - mutex_lock(&cam->lock); + mutex_lock(&cam->open_lock); if (cam->users) { err = -EBUSY; goto out; } - if (!cam->framebuf) { - cam->framebuf = vmalloc_32(MAX_FRAME_SIZE * FRAMES); - if (!cam->framebuf) { - dev_err(&cam->udev->dev, "vmalloc_32 failed!\n"); - err = -ENOMEM; - goto out; - } - } - for (i = 0; init[cam->method][i].size != -1; i++) { err = send_control_msg(udev, 1, init[cam->method][i].value, @@ -659,6 +1229,14 @@ static int zr364xx_open(struct file *file) cam->skip = 2; cam->users++; file->private_data = vdev; + cam->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->fmt = formats; + + videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops, + NULL, &cam->slock, + cam->type, + V4L2_FIELD_NONE, + sizeof(struct zr364xx_buffer), cam); /* Added some delay here, since opening/closing the camera quickly, * like Ekiga does during its startup, can crash the webcam @@ -667,28 +1245,72 @@ static int zr364xx_open(struct file *file) err = 0; out: - mutex_unlock(&cam->lock); + mutex_unlock(&cam->open_lock); + DBG("%s: %d\n", __func__, err); return err; } +static void zr364xx_destroy(struct zr364xx_camera *cam) +{ + unsigned long i; + + if (!cam) { + printk(KERN_ERR KBUILD_MODNAME ", %s: no device\n", __func__); + return; + } + mutex_lock(&cam->open_lock); + if (cam->vdev) + video_unregister_device(cam->vdev); + cam->vdev = NULL; + + /* stops the read pipe if it is running */ + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + + zr364xx_stop_readpipe(cam); + + /* release sys buffers */ + for (i = 0; i < FRAMES; i++) { + if (cam->buffer.frame[i].lpvbits) { + DBG("vfree %p\n", cam->buffer.frame[i].lpvbits); + vfree(cam->buffer.frame[i].lpvbits); + } + cam->buffer.frame[i].lpvbits = NULL; + } + + /* release transfer buffer */ + kfree(cam->pipe->transfer_buffer); + cam->pipe->transfer_buffer = NULL; + + DBG("%s\n", __func__); + mutex_unlock(&cam->open_lock); + kfree(cam); + cam = NULL; +} /* release the camera */ static int zr364xx_release(struct file *file) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; struct usb_device *udev; int i, err; - DBG("zr364xx_release"); + DBG("%s\n", __func__); + cam = video_drvdata(file); - if (vdev == NULL) + if (!cam) return -ENODEV; - cam = video_get_drvdata(vdev); + mutex_lock(&cam->open_lock); udev = cam->udev; - mutex_lock(&cam->lock); + /* turn off stream */ + if (res_check(cam)) { + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + videobuf_streamoff(&cam->vb_vidq); + res_free(cam); + } cam->users--; file->private_data = NULL; @@ -711,40 +1333,43 @@ static int zr364xx_release(struct file *file) err = 0; out: - mutex_unlock(&cam->lock); + mutex_unlock(&cam->open_lock); + return err; } static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma) { - void *pos; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end - vma->vm_start; - struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam; - - DBG("zr364xx_mmap: %ld\n", size); + struct zr364xx_camera *cam = video_drvdata(file); + int ret; - if (vdev == NULL) + if (cam == NULL) { + DBG("%s: cam == NULL\n", __func__); return -ENODEV; - cam = video_get_drvdata(vdev); - - pos = cam->framebuf; - while (size > 0) { - if (vm_insert_page(vma, start, vmalloc_to_page(pos))) - return -EAGAIN; - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; } + DBG("mmap called, vma=0x%08lx\n", (unsigned long)vma); - return 0; + ret = videobuf_mmap_mapper(&cam->vb_vidq, vma); + + DBG("vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); + return ret; } +static unsigned int zr364xx_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct zr364xx_camera *cam = video_drvdata(file); + struct videobuf_queue *q = &cam->vb_vidq; + DBG("%s\n", __func__); + + if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return POLLERR; + + return videobuf_poll_stream(file, q, wait); +} static const struct v4l2_file_operations zr364xx_fops = { .owner = THIS_MODULE, @@ -753,6 +1378,7 @@ static const struct v4l2_file_operations zr364xx_fops = { .read = zr364xx_read, .mmap = zr364xx_mmap, .ioctl = video_ioctl2, + .poll = zr364xx_poll, }; static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { @@ -769,6 +1395,10 @@ static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { .vidioc_queryctrl = zr364xx_vidioc_queryctrl, .vidioc_g_ctrl = zr364xx_vidioc_g_ctrl, .vidioc_s_ctrl = zr364xx_vidioc_s_ctrl, + .vidioc_reqbufs = zr364xx_vidioc_reqbufs, + .vidioc_querybuf = zr364xx_vidioc_querybuf, + .vidioc_qbuf = zr364xx_vidioc_qbuf, + .vidioc_dqbuf = zr364xx_vidioc_dqbuf, }; static struct video_device zr364xx_template = { @@ -784,15 +1414,76 @@ static struct video_device zr364xx_template = { /*******************/ /* USB integration */ /*******************/ +static int zr364xx_board_init(struct zr364xx_camera *cam) +{ + struct zr364xx_pipeinfo *pipe = cam->pipe; + unsigned long i; + + DBG("board init: %p\n", cam); + memset(pipe, 0, sizeof(*pipe)); + pipe->cam = cam; + pipe->transfer_size = BUFFER_SIZE; + + pipe->transfer_buffer = kzalloc(pipe->transfer_size, + GFP_KERNEL); + if (pipe->transfer_buffer == NULL) { + DBG("out of memory!\n"); + return -ENOMEM; + } + + cam->b_acquire = 0; + cam->frame_count = 0; + + /*** start create system buffers ***/ + for (i = 0; i < FRAMES; i++) { + /* always allocate maximum size for system buffers */ + cam->buffer.frame[i].lpvbits = vmalloc(MAX_FRAME_SIZE); + + DBG("valloc %p, idx %lu, pdata %p\n", + &cam->buffer.frame[i], i, + cam->buffer.frame[i].lpvbits); + if (cam->buffer.frame[i].lpvbits == NULL) { + printk(KERN_INFO KBUILD_MODNAME ": out of memory. " + "Using less frames\n"); + break; + } + } + + if (i == 0) { + printk(KERN_INFO KBUILD_MODNAME ": out of memory. Aborting\n"); + kfree(cam->pipe->transfer_buffer); + cam->pipe->transfer_buffer = NULL; + return -ENOMEM; + } else + cam->buffer.dwFrames = i; + + /* make sure internal states are set */ + for (i = 0; i < FRAMES; i++) { + cam->buffer.frame[i].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[i].cur_size = 0; + } + + cam->cur_frame = 0; + cam->last_frame = -1; + /*** end create system buffers ***/ + + /* start read pipe */ + zr364xx_start_readpipe(cam); + DBG(": board initialized\n"); + return 0; +} static int zr364xx_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); struct zr364xx_camera *cam = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; int err; + int i; - DBG("probing..."); + DBG("probing...\n"); dev_info(&intf->dev, DRIVER_DESC " compatible webcam plugged\n"); dev_info(&intf->dev, "model %04x:%04x detected\n", @@ -811,22 +1502,17 @@ static int zr364xx_probe(struct usb_interface *intf, if (cam->vdev == NULL) { dev_err(&udev->dev, "cam->vdev: out of memory !\n"); kfree(cam); + cam = NULL; return -ENOMEM; } memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template)); + cam->vdev->parent = &intf->dev; video_set_drvdata(cam->vdev, cam); if (debug) cam->vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; cam->udev = udev; - if ((cam->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL)) == NULL) { - dev_info(&udev->dev, "cam->buffer: out of memory !\n"); - video_device_release(cam->vdev); - kfree(cam); - return -ENODEV; - } - switch (mode) { case 1: dev_info(&udev->dev, "160x120 mode selected\n"); @@ -853,21 +1539,53 @@ static int zr364xx_probe(struct usb_interface *intf, header2[439] = cam->width / 256; header2[440] = cam->width % 256; + cam->users = 0; cam->nb = 0; - cam->brightness = 64; + cam->mode.brightness = 64; mutex_init(&cam->lock); + mutex_init(&cam->open_lock); + + DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf); + + /* set up the endpoint information */ + iface_desc = intf->cur_altsetting; + DBG("num endpoints %d\n", iface_desc->desc.bNumEndpoints); + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (!cam->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { + /* we found the bulk in endpoint */ + cam->read_endpoint = endpoint->bEndpointAddress; + } + } + if (!cam->read_endpoint) { + dev_err(&intf->dev, "Could not find bulk-in endpoint\n"); + return -ENOMEM; + } + + /* v4l */ + INIT_LIST_HEAD(&cam->vidq.active); + cam->vidq.cam = cam; err = video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1); if (err) { dev_err(&udev->dev, "video_register_device failed\n"); video_device_release(cam->vdev); - kfree(cam->buffer); kfree(cam); + cam = NULL; return err; } usb_set_intfdata(intf, cam); + /* load zr364xx board specific */ + err = zr364xx_board_init(cam); + if (err) { + spin_lock_init(&cam->slock); + return err; + } + + spin_lock_init(&cam->slock); + dev_info(&udev->dev, DRIVER_DESC " controlling video device %d\n", cam->vdev->num); return 0; @@ -877,17 +1595,10 @@ static int zr364xx_probe(struct usb_interface *intf, static void zr364xx_disconnect(struct usb_interface *intf) { struct zr364xx_camera *cam = usb_get_intfdata(intf); + videobuf_mmap_free(&cam->vb_vidq); usb_set_intfdata(intf, NULL); dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n"); - if (cam->vdev) - video_unregister_device(cam->vdev); - cam->vdev = NULL; - kfree(cam->buffer); - cam->buffer = NULL; - vfree(cam->framebuf); - cam->framebuf = NULL; - kfree(cam); - cam = NULL; + zr364xx_destroy(cam); } -- cgit v1.2.3 From 2f2fb3516ed879fd5591730ade5d05328d3afaa5 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 19 Jul 2009 10:29:20 +0200 Subject: gspca - main: Support for vidioc_g_chip_ident and vidioc_g/s_register. From: Brian Johnson Priority: high Signed-off-by: Brian Johnson Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 73 +++++++++++++++++++++++++++++++++ linux/drivers/media/video/gspca/gspca.h | 9 ++++ 2 files changed, 82 insertions(+) diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index fe759178f..eedf9a60c 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -758,6 +758,74 @@ static int gspca_get_mode(struct gspca_dev *gspca_dev, return -EINVAL; } +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + int ret; + struct gspca_dev *gspca_dev = priv; + + if (!gspca_dev->sd_desc->get_chip_ident) + return -EINVAL; + + if (!gspca_dev->sd_desc->get_register) + return -EINVAL; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (gspca_dev->present) + ret = gspca_dev->sd_desc->get_register(gspca_dev, reg); + else + ret = -ENODEV; + mutex_unlock(&gspca_dev->usb_lock); + + return ret; +} + +static int vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + int ret; + struct gspca_dev *gspca_dev = priv; + + if (!gspca_dev->sd_desc->get_chip_ident) + return -EINVAL; + + if (!gspca_dev->sd_desc->set_register) + return -EINVAL; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (gspca_dev->present) + ret = gspca_dev->sd_desc->set_register(gspca_dev, reg); + else + ret = -ENODEV; + mutex_unlock(&gspca_dev->usb_lock); + + return ret; +} +#endif + +static int vidioc_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + int ret; + struct gspca_dev *gspca_dev = priv; + + if (!gspca_dev->sd_desc->get_chip_ident) + return -EINVAL; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (gspca_dev->present) + ret = gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip); + else + ret = -ENODEV; + mutex_unlock(&gspca_dev->usb_lock); + + return ret; +} + static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fmtdesc) { @@ -1914,6 +1982,11 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_s_parm = vidioc_s_parm, .vidioc_s_std = vidioc_s_std, .vidioc_enum_framesizes = vidioc_enum_framesizes, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif + .vidioc_g_chip_ident = vidioc_g_chip_ident, #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, #endif diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index 0508a23b9..70b1fd830 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -69,6 +69,10 @@ typedef void (*cam_v_op) (struct gspca_dev *); typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *); typedef int (*cam_jpg_op) (struct gspca_dev *, struct v4l2_jpegcompression *); +typedef int (*cam_reg_op) (struct gspca_dev *, + struct v4l2_dbg_register *); +typedef int (*cam_ident_op) (struct gspca_dev *, + struct v4l2_dbg_chip_ident *); typedef int (*cam_streamparm_op) (struct gspca_dev *, struct v4l2_streamparm *); typedef int (*cam_qmnu_op) (struct gspca_dev *, @@ -107,6 +111,11 @@ struct sd_desc { cam_qmnu_op querymenu; cam_streamparm_op get_streamparm; cam_streamparm_op set_streamparm; +#ifdef CONFIG_VIDEO_ADV_DEBUG + cam_reg_op set_register; + cam_reg_op get_register; +#endif + cam_ident_op get_chip_ident; }; /* packet types when moving from iso buf to frame buf */ -- cgit v1.2.3 From 624d36913e55b452914dcaa83e0ff84330bf3ec0 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 19 Jul 2009 10:52:58 +0200 Subject: gspca - sn9c20x: New subdriver for sn9c201 and sn9c202 bridges. From: Brian Johnson Priority: high Signed-off-by: Brian Johnson Signed-off-by: Jean-Francois Moine --- linux/Documentation/video4linux/gspca.txt | 32 + linux/drivers/media/video/gspca/Kconfig | 16 + linux/drivers/media/video/gspca/Makefile | 2 + linux/drivers/media/video/gspca/sn9c20x.c | 2433 +++++++++++++++++++++++++++++ linux/include/linux/videodev2.h | 1 + linux/include/media/v4l2-chip-ident.h | 12 + 6 files changed, 2496 insertions(+) create mode 100644 linux/drivers/media/video/gspca/sn9c20x.c diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index 21cafc0bd..37247697d 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -44,7 +44,9 @@ zc3xx 0458:7007 Genius VideoCam V2 zc3xx 0458:700c Genius VideoCam V3 zc3xx 0458:700f Genius VideoCam Web V2 sonixj 0458:7025 Genius Eye 311Q +sn9c20x 0458:7029 Genius Look 320s sonixj 0458:702e Genius Slim 310 NB +sn9c20x 045e:00f4 LifeCam VX-6000 (SN9C20x + OV9650) sonixj 045e:00f5 MicroSoft VX3000 sonixj 045e:00f7 MicroSoft VX1000 ov519 045e:028c Micro$oft xbox cam @@ -285,6 +287,28 @@ sonixj 0c45:613a Microdia Sonix PC Camera sonixj 0c45:613b Surfer SN-206 sonixj 0c45:613c Sonix Pccam168 sonixj 0c45:6143 Sonix Pccam168 +sn9c20x 0c45:6240 PC Camera (SN9C201 + MT9M001) +sn9c20x 0c45:6242 PC Camera (SN9C201 + MT9M111) +sn9c20x 0c45:6248 PC Camera (SN9C201 + OV9655) +sn9c20x 0c45:624e PC Camera (SN9C201 + SOI968) +sn9c20x 0c45:624f PC Camera (SN9C201 + OV9650) +sn9c20x 0c45:6251 PC Camera (SN9C201 + OV9650) +sn9c20x 0c45:6253 PC Camera (SN9C201 + OV9650) +sn9c20x 0c45:6260 PC Camera (SN9C201 + OV7670) +sn9c20x 0c45:6270 PC Camera (SN9C201 + MT9V011/MT9V111/MT9V112) +sn9c20x 0c45:627b PC Camera (SN9C201 + OV7660) +sn9c20x 0c45:627c PC Camera (SN9C201 + HV7131R) +sn9c20x 0c45:627f PC Camera (SN9C201 + OV9650) +sn9c20x 0c45:6280 PC Camera (SN9C202 + MT9M001) +sn9c20x 0c45:6282 PC Camera (SN9C202 + MT9M111) +sn9c20x 0c45:6288 PC Camera (SN9C202 + OV9655) +sn9c20x 0c45:628e PC Camera (SN9C202 + SOI968) +sn9c20x 0c45:628f PC Camera (SN9C202 + OV9650) +sn9c20x 0c45:62a0 PC Camera (SN9C202 + OV7670) +sn9c20x 0c45:62b0 PC Camera (SN9C202 + MT9V011/MT9V111/MT9V112) +sn9c20x 0c45:62b3 PC Camera (SN9C202 + OV9655) +sn9c20x 0c45:62bb PC Camera (SN9C202 + OV7660) +sn9c20x 0c45:62bc PC Camera (SN9C202 + HV7131R) sunplus 0d64:0303 Sunplus FashionCam DXG etoms 102c:6151 Qcam Sangha CIF etoms 102c:6251 Qcam xxxxxx VGA @@ -293,6 +317,7 @@ spca561 10fd:7e50 FlyCam Usb 100 zc3xx 10fd:8050 Typhoon Webshot II USB 300k ov534 1415:2000 Sony HD Eye for PS3 (SLEH 00201) pac207 145f:013a Trust WB-1300N +sn9c20x 145f:013d Trust WB-3600R vc032x 15b8:6001 HP 2.0 Megapixel vc032x 15b8:6002 HP 2.0 Megapixel rz406aa spca501 1776:501c Arowana 300K CMOS Camera @@ -303,4 +328,11 @@ spca500 2899:012c Toptro Industrial spca508 8086:0110 Intel Easy PC Camera spca500 8086:0630 Intel Pocket PC Camera spca506 99fa:8988 Grandtec V.cap +sn9c20x a168:0610 Dino-Lite Digital Microscope (SN9C201 + HV7131R) +sn9c20x a168:0611 Dino-Lite Digital Microscope (SN9C201 + HV7131R) +sn9c20x a168:0613 Dino-Lite Digital Microscope (SN9C201 + HV7131R) +sn9c20x a168:0618 Dino-Lite Digital Microscope (SN9C201 + HV7131R) +sn9c20x a168:0614 Dino-Lite Digital Microscope (SN9C201 + MT9M111) +sn9c20x a168:0615 Dino-Lite Digital Microscope (SN9C201 + MT9M111) +sn9c20x a168:0617 Dino-Lite Digital Microscope (SN9C201 + MT9M111) spca561 abcd:cdee Petcam diff --git a/linux/drivers/media/video/gspca/Kconfig b/linux/drivers/media/video/gspca/Kconfig index 578dc4ffc..34f46f2bc 100644 --- a/linux/drivers/media/video/gspca/Kconfig +++ b/linux/drivers/media/video/gspca/Kconfig @@ -102,6 +102,22 @@ config USB_GSPCA_PAC7311 To compile this driver as a module, choose M here: the module will be called gspca_pac7311. +config USB_GSPCA_SN9C20X + tristate "SN9C20X USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the + sn9c20x chips (SN9C201 and SN9C202). + + To compile this driver as a module, choose M here: the + module will be called gspca_sn9c20x. + +config USB_GSPCA_SN9C20X_EVDEV + bool "Enable evdev support" + depends on USB_GSPCA_SN9C20X + ---help--- + Say Y here in order to enable evdev support for sn9c20x webcam button. + config USB_GSPCA_SONIXB tristate "SONIX Bayer USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/linux/drivers/media/video/gspca/Makefile b/linux/drivers/media/video/gspca/Makefile index 8a6643e8e..f6d3b86e9 100644 --- a/linux/drivers/media/video/gspca/Makefile +++ b/linux/drivers/media/video/gspca/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o +obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o @@ -35,6 +36,7 @@ gspca_ov519-objs := ov519.o gspca_ov534-objs := ov534.o gspca_pac207-objs := pac207.o gspca_pac7311-objs := pac7311.o +gspca_sn9c20x-objs := sn9c20x.o gspca_sonixb-objs := sonixb.o gspca_sonixj-objs := sonixj.o gspca_spca500-objs := spca500.o diff --git a/linux/drivers/media/video/gspca/sn9c20x.c b/linux/drivers/media/video/gspca/sn9c20x.c new file mode 100644 index 000000000..78ab26ceb --- /dev/null +++ b/linux/drivers/media/video/gspca/sn9c20x.c @@ -0,0 +1,2433 @@ +/* + * Sonix sn9c201 sn9c202 library + * Copyright (C) 2008-2009 microdia project + * Copyright (C) 2009 Brian Johnson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "gspca.h" +#include "jpeg.h" + +#include +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV +#include +#include +#include +#include +#endif + +MODULE_AUTHOR("Brian Johnson , " + "microdia project "); +MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver"); +MODULE_LICENSE("GPL"); + +#define MODULE_NAME "sn9c20x" + +#define MODE_RAW 0x10 +#define MODE_JPEG 0x20 +#define MODE_SXGA 0x80 + +#define SENSOR_OV9650 0 +#define SENSOR_OV9655 1 +#define SENSOR_SOI968 2 +#define SENSOR_OV7660 3 +#define SENSOR_OV7670 4 +#define SENSOR_MT9V011 5 +#define SENSOR_MT9V111 6 +#define SENSOR_MT9V112 7 +#define SENSOR_MT9M001 8 +#define SENSOR_MT9M111 9 +#define SENSOR_HV7131R 10 +#define SENSOR_MT9VPRB 20 + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; + +#define MIN_AVG_LUM 80 +#define MAX_AVG_LUM 130 + atomic_t avg_lum; + u8 old_step; + u8 older_step; + u8 exposure_step; + + u8 brightness; + u8 contrast; + u8 saturation; + s16 hue; + u8 gamma; + u8 red; + u8 blue; + + u8 hflip; + u8 vflip; + u8 gain; + u16 exposure; + u8 auto_exposure; + + u8 i2c_addr; + u8 sensor; + u8 hstart; + u8 vstart; + + u8 *jpeg_hdr; + u8 quality; + +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV + struct input_dev *input_dev; + u8 input_gpio; + struct task_struct *input_task; +#endif +}; + +static int sd_setbrightness(struct gspca_dev *gspca_dev, s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setsaturation(struct gspca_dev *gspca_dev, s32 val); +static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val); +static int sd_sethue(struct gspca_dev *gspca_dev, s32 val); +static int sd_gethue(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setgamma(struct gspca_dev *gspca_dev, s32 val); +static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val); +static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val); +static int sd_getbluebalance(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, s32 *val); +static int sd_sethflip(struct gspca_dev *gspca_dev, s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val); +static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val); +static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val); + +static struct ctrl sd_ctrls[] = { + { +#define BRIGHTNESS_IDX 0 + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xff, + .step = 1, +#define BRIGHTNESS_DEFAULT 0x7f + .default_value = BRIGHTNESS_DEFAULT, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { +#define CONTRAST_IDX 1 + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xff, + .step = 1, +#define CONTRAST_DEFAULT 0x7f + .default_value = CONTRAST_DEFAULT, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { +#define SATURATION_IDX 2 + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 0xff, + .step = 1, +#define SATURATION_DEFAULT 0x7f + .default_value = SATURATION_DEFAULT, + }, + .set = sd_setsaturation, + .get = sd_getsaturation, + }, + { +#define HUE_IDX 3 + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -180, + .maximum = 180, + .step = 1, +#define HUE_DEFAULT 0 + .default_value = HUE_DEFAULT, + }, + .set = sd_sethue, + .get = sd_gethue, + }, + { +#define GAMMA_IDX 4 + { + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = 0, + .maximum = 0xff, + .step = 1, +#define GAMMA_DEFAULT 0x10 + .default_value = GAMMA_DEFAULT, + }, + .set = sd_setgamma, + .get = sd_getgamma, + }, + { +#define BLUE_IDX 5 + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0, + .maximum = 0x7f, + .step = 1, +#define BLUE_DEFAULT 0x28 + .default_value = BLUE_DEFAULT, + }, + .set = sd_setbluebalance, + .get = sd_getbluebalance, + }, + { +#define RED_IDX 6 + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0, + .maximum = 0x7f, + .step = 1, +#define RED_DEFAULT 0x28 + .default_value = RED_DEFAULT, + }, + .set = sd_setredbalance, + .get = sd_getredbalance, + }, + { +#define HFLIP_IDX 7 + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal Flip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define HFLIP_DEFAULT 0 + .default_value = HFLIP_DEFAULT, + }, + .set = sd_sethflip, + .get = sd_gethflip, + }, + { +#define VFLIP_IDX 8 + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Flip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEFAULT 0 + .default_value = VFLIP_DEFAULT, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, + { +#define EXPOSURE_IDX 9 + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 0x1780, + .step = 1, +#define EXPOSURE_DEFAULT 0x33 + .default_value = EXPOSURE_DEFAULT, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { +#define GAIN_IDX 10 + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 28, + .step = 1, +#define GAIN_DEFAULT 0x00 + .default_value = GAIN_DEFAULT, + }, + .set = sd_setgain, + .get = sd_getgain, + }, + { +#define AUTOGAIN_IDX 11 + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTO_EXPOSURE_DEFAULT 1 + .default_value = AUTO_EXPOSURE_DEFAULT, + }, + .set = sd_setautoexposure, + .get = sd_getautoexposure, + }, +}; + +static const struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 240, + .sizeimage = 240 * 120, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0 | MODE_JPEG}, + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 | MODE_RAW}, + {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 240, + .sizeimage = 240 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 480, + .sizeimage = 480 * 240 , + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1 | MODE_JPEG}, + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_RAW}, + {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 480, + .sizeimage = 480 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 960, + .sizeimage = 960 * 480, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2 | MODE_JPEG}, + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 | MODE_RAW}, + {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 960, + .sizeimage = 960 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, +}; + +static const struct v4l2_pix_format sxga_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 240, + .sizeimage = 240 * 120, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0 | MODE_JPEG}, + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 | MODE_RAW}, + {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 240, + .sizeimage = 240 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 480, + .sizeimage = 480 * 240 , + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1 | MODE_JPEG}, + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_RAW}, + {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 480, + .sizeimage = 480 * 240 , + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 960, + .sizeimage = 960 * 480, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2 | MODE_JPEG}, + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 | MODE_RAW}, + {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE, + .bytesperline = 960, + .sizeimage = 960 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = (1280 * 1024) + 64, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3 | MODE_RAW | MODE_SXGA}, +}; + +static const int hsv_red_x[] = { + 41, 44, 46, 48, 50, 52, 54, 56, + 58, 60, 62, 64, 66, 68, 70, 72, + 74, 76, 78, 80, 81, 83, 85, 87, + 88, 90, 92, 93, 95, 97, 98, 100, + 101, 102, 104, 105, 107, 108, 109, 110, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 123, 124, 125, 125, + 126, 127, 127, 128, 128, 129, 129, 129, + 130, 130, 130, 130, 131, 131, 131, 131, + 131, 131, 131, 131, 130, 130, 130, 130, + 129, 129, 129, 128, 128, 127, 127, 126, + 125, 125, 124, 123, 122, 122, 121, 120, + 119, 118, 117, 116, 115, 114, 112, 111, + 110, 109, 107, 106, 105, 103, 102, 101, + 99, 98, 96, 94, 93, 91, 90, 88, + 86, 84, 83, 81, 79, 77, 75, 74, + 72, 70, 68, 66, 64, 62, 60, 58, + 56, 54, 52, 49, 47, 45, 43, 41, + 39, 36, 34, 32, 30, 28, 25, 23, + 21, 19, 16, 14, 12, 9, 7, 5, + 3, 0, -1, -3, -6, -8, -10, -12, + -15, -17, -19, -22, -24, -26, -28, -30, + -33, -35, -37, -39, -41, -44, -46, -48, + -50, -52, -54, -56, -58, -60, -62, -64, + -66, -68, -70, -72, -74, -76, -78, -80, + -81, -83, -85, -87, -88, -90, -92, -93, + -95, -97, -98, -100, -101, -102, -104, -105, + -107, -108, -109, -110, -112, -113, -114, -115, + -116, -117, -118, -119, -120, -121, -122, -123, + -123, -124, -125, -125, -126, -127, -127, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -127, -127, -126, -125, -125, -124, -123, + -122, -122, -121, -120, -119, -118, -117, -116, + -115, -114, -112, -111, -110, -109, -107, -106, + -105, -103, -102, -101, -99, -98, -96, -94, + -93, -91, -90, -88, -86, -84, -83, -81, + -79, -77, -75, -74, -72, -70, -68, -66, + -64, -62, -60, -58, -56, -54, -52, -49, + -47, -45, -43, -41, -39, -36, -34, -32, + -30, -28, -25, -23, -21, -19, -16, -14, + -12, -9, -7, -5, -3, 0, 1, 3, + 6, 8, 10, 12, 15, 17, 19, 22, + 24, 26, 28, 30, 33, 35, 37, 39, 41 +}; + +static const int hsv_red_y[] = { + 82, 80, 78, 76, 74, 73, 71, 69, + 67, 65, 63, 61, 58, 56, 54, 52, + 50, 48, 46, 44, 41, 39, 37, 35, + 32, 30, 28, 26, 23, 21, 19, 16, + 14, 12, 10, 7, 5, 3, 0, -1, + -3, -6, -8, -10, -13, -15, -17, -19, + -22, -24, -26, -29, -31, -33, -35, -38, + -40, -42, -44, -46, -48, -51, -53, -55, + -57, -59, -61, -63, -65, -67, -69, -71, + -73, -75, -77, -79, -81, -82, -84, -86, + -88, -89, -91, -93, -94, -96, -98, -99, + -101, -102, -104, -105, -106, -108, -109, -110, + -112, -113, -114, -115, -116, -117, -119, -120, + -120, -121, -122, -123, -124, -125, -126, -126, + -127, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, + -127, -127, -126, -125, -125, -124, -123, -122, + -121, -120, -119, -118, -117, -116, -115, -114, + -113, -111, -110, -109, -107, -106, -105, -103, + -102, -100, -99, -97, -96, -94, -92, -91, + -89, -87, -85, -84, -82, -80, -78, -76, + -74, -73, -71, -69, -67, -65, -63, -61, + -58, -56, -54, -52, -50, -48, -46, -44, + -41, -39, -37, -35, -32, -30, -28, -26, + -23, -21, -19, -16, -14, -12, -10, -7, + -5, -3, 0, 1, 3, 6, 8, 10, + 13, 15, 17, 19, 22, 24, 26, 29, + 31, 33, 35, 38, 40, 42, 44, 46, + 48, 51, 53, 55, 57, 59, 61, 63, + 65, 67, 69, 71, 73, 75, 77, 79, + 81, 82, 84, 86, 88, 89, 91, 93, + 94, 96, 98, 99, 101, 102, 104, 105, + 106, 108, 109, 110, 112, 113, 114, 115, + 116, 117, 119, 120, 120, 121, 122, 123, + 124, 125, 126, 126, 127, 128, 128, 129, + 129, 130, 130, 131, 131, 131, 131, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 131, 131, 131, 130, 130, + 130, 129, 129, 128, 127, 127, 126, 125, + 125, 124, 123, 122, 121, 120, 119, 118, + 117, 116, 115, 114, 113, 111, 110, 109, + 107, 106, 105, 103, 102, 100, 99, 97, + 96, 94, 92, 91, 89, 87, 85, 84, 82 +}; + +static const int hsv_green_x[] = { + -124, -124, -125, -125, -125, -125, -125, -125, + -125, -126, -126, -125, -125, -125, -125, -125, + -125, -124, -124, -124, -123, -123, -122, -122, + -121, -121, -120, -120, -119, -118, -117, -117, + -116, -115, -114, -113, -112, -111, -110, -109, + -108, -107, -105, -104, -103, -102, -100, -99, + -98, -96, -95, -93, -92, -91, -89, -87, + -86, -84, -83, -81, -79, -77, -76, -74, + -72, -70, -69, -67, -65, -63, -61, -59, + -57, -55, -53, -51, -49, -47, -45, -43, + -41, -39, -37, -35, -33, -30, -28, -26, + -24, -22, -20, -18, -15, -13, -11, -9, + -7, -4, -2, 0, 1, 3, 6, 8, + 10, 12, 14, 17, 19, 21, 23, 25, + 27, 29, 32, 34, 36, 38, 40, 42, + 44, 46, 48, 50, 52, 54, 56, 58, + 60, 62, 64, 66, 68, 70, 71, 73, + 75, 77, 78, 80, 82, 83, 85, 87, + 88, 90, 91, 93, 94, 96, 97, 98, + 100, 101, 102, 104, 105, 106, 107, 108, + 109, 111, 112, 113, 113, 114, 115, 116, + 117, 118, 118, 119, 120, 120, 121, 122, + 122, 123, 123, 124, 124, 124, 125, 125, + 125, 125, 125, 125, 125, 126, 126, 125, + 125, 125, 125, 125, 125, 124, 124, 124, + 123, 123, 122, 122, 121, 121, 120, 120, + 119, 118, 117, 117, 116, 115, 114, 113, + 112, 111, 110, 109, 108, 107, 105, 104, + 103, 102, 100, 99, 98, 96, 95, 93, + 92, 91, 89, 87, 86, 84, 83, 81, + 79, 77, 76, 74, 72, 70, 69, 67, + 65, 63, 61, 59, 57, 55, 53, 51, + 49, 47, 45, 43, 41, 39, 37, 35, + 33, 30, 28, 26, 24, 22, 20, 18, + 15, 13, 11, 9, 7, 4, 2, 0, + -1, -3, -6, -8, -10, -12, -14, -17, + -19, -21, -23, -25, -27, -29, -32, -34, + -36, -38, -40, -42, -44, -46, -48, -50, + -52, -54, -56, -58, -60, -62, -64, -66, + -68, -70, -71, -73, -75, -77, -78, -80, + -82, -83, -85, -87, -88, -90, -91, -93, + -94, -96, -97, -98, -100, -101, -102, -104, + -105, -106, -107, -108, -109, -111, -112, -113, + -113, -114, -115, -116, -117, -118, -118, -119, + -120, -120, -121, -122, -122, -123, -123, -124, -124 +}; + +static const int hsv_green_y[] = { + -100, -99, -98, -97, -95, -94, -93, -91, + -90, -89, -87, -86, -84, -83, -81, -80, + -78, -76, -75, -73, -71, -70, -68, -66, + -64, -63, -61, -59, -57, -55, -53, -51, + -49, -48, -46, -44, -42, -40, -38, -36, + -34, -32, -30, -27, -25, -23, -21, -19, + -17, -15, -13, -11, -9, -7, -4, -2, + 0, 1, 3, 5, 7, 9, 11, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, 50, 52, 54, 56, 58, 59, 61, + 63, 65, 67, 68, 70, 72, 74, 75, + 77, 78, 80, 82, 83, 85, 86, 88, + 89, 90, 92, 93, 95, 96, 97, 98, + 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 112, 113, 114, + 115, 115, 116, 116, 117, 117, 118, 118, + 119, 119, 119, 120, 120, 120, 120, 120, + 121, 121, 121, 121, 121, 121, 120, 120, + 120, 120, 120, 119, 119, 119, 118, 118, + 117, 117, 116, 116, 115, 114, 114, 113, + 112, 111, 111, 110, 109, 108, 107, 106, + 105, 104, 103, 102, 100, 99, 98, 97, + 95, 94, 93, 91, 90, 89, 87, 86, + 84, 83, 81, 80, 78, 76, 75, 73, + 71, 70, 68, 66, 64, 63, 61, 59, + 57, 55, 53, 51, 49, 48, 46, 44, + 42, 40, 38, 36, 34, 32, 30, 27, + 25, 23, 21, 19, 17, 15, 13, 11, + 9, 7, 4, 2, 0, -1, -3, -5, + -7, -9, -11, -14, -16, -18, -20, -22, + -24, -26, -28, -30, -32, -34, -36, -38, + -40, -42, -44, -46, -48, -50, -52, -54, + -56, -58, -59, -61, -63, -65, -67, -68, + -70, -72, -74, -75, -77, -78, -80, -82, + -83, -85, -86, -88, -89, -90, -92, -93, + -95, -96, -97, -98, -100, -101, -102, -103, + -104, -105, -106, -107, -108, -109, -110, -111, + -112, -112, -113, -114, -115, -115, -116, -116, + -117, -117, -118, -118, -119, -119, -119, -120, + -120, -120, -120, -120, -121, -121, -121, -121, + -121, -121, -120, -120, -120, -120, -120, -119, + -119, -119, -118, -118, -117, -117, -116, -116, + -115, -114, -114, -113, -112, -111, -111, -110, + -109, -108, -107, -106, -105, -104, -103, -102, -100 +}; + +static const int hsv_blue_x[] = { + 112, 113, 114, 114, 115, 116, 117, 117, + 118, 118, 119, 119, 120, 120, 120, 121, + 121, 121, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 121, 121, + 121, 120, 120, 120, 119, 119, 118, 118, + 117, 116, 116, 115, 114, 113, 113, 112, + 111, 110, 109, 108, 107, 106, 105, 104, + 103, 102, 100, 99, 98, 97, 95, 94, + 93, 91, 90, 88, 87, 85, 84, 82, + 80, 79, 77, 76, 74, 72, 70, 69, + 67, 65, 63, 61, 60, 58, 56, 54, + 52, 50, 48, 46, 44, 42, 40, 38, + 36, 34, 32, 30, 28, 26, 24, 22, + 19, 17, 15, 13, 11, 9, 7, 5, + 2, 0, -1, -3, -5, -7, -9, -12, + -14, -16, -18, -20, -22, -24, -26, -28, + -31, -33, -35, -37, -39, -41, -43, -45, + -47, -49, -51, -53, -54, -56, -58, -60, + -62, -64, -66, -67, -69, -71, -73, -74, + -76, -78, -79, -81, -83, -84, -86, -87, + -89, -90, -92, -93, -94, -96, -97, -98, + -99, -101, -102, -103, -104, -105, -106, -107, + -108, -109, -110, -111, -112, -113, -114, -114, + -115, -116, -117, -117, -118, -118, -119, -119, + -120, -120, -120, -121, -121, -121, -122, -122, + -122, -122, -122, -122, -122, -122, -122, -122, + -122, -122, -121, -121, -121, -120, -120, -120, + -119, -119, -118, -118, -117, -116, -116, -115, + -114, -113, -113, -112, -111, -110, -109, -108, + -107, -106, -105, -104, -103, -102, -100, -99, + -98, -97, -95, -94, -93, -91, -90, -88, + -87, -85, -84, -82, -80, -79, -77, -76, + -74, -72, -70, -69, -67, -65, -63, -61, + -60, -58, -56, -54, -52, -50, -48, -46, + -44, -42, -40, -38, -36, -34, -32, -30, + -28, -26, -24, -22, -19, -17, -15, -13, + -11, -9, -7, -5, -2, 0, 1, 3, + 5, 7, 9, 12, 14, 16, 18, 20, + 22, 24, 26, 28, 31, 33, 35, 37, + 39, 41, 43, 45, 47, 49, 51, 53, + 54, 56, 58, 60, 62, 64, 66, 67, + 69, 71, 73, 74, 76, 78, 79, 81, + 83, 84, 86, 87, 89, 90, 92, 93, + 94, 96, 97, 98, 99, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112 +}; + +static const int hsv_blue_y[] = { + -11, -13, -15, -17, -19, -21, -23, -25, + -27, -29, -31, -33, -35, -37, -39, -41, + -43, -45, -46, -48, -50, -52, -54, -55, + -57, -59, -61, -62, -64, -66, -67, -69, + -71, -72, -74, -75, -77, -78, -80, -81, + -83, -84, -86, -87, -88, -90, -91, -92, + -93, -95, -96, -97, -98, -99, -100, -101, + -102, -103, -104, -105, -106, -106, -107, -108, + -109, -109, -110, -111, -111, -112, -112, -113, + -113, -114, -114, -114, -115, -115, -115, -115, + -116, -116, -116, -116, -116, -116, -116, -116, + -116, -115, -115, -115, -115, -114, -114, -114, + -113, -113, -112, -112, -111, -111, -110, -110, + -109, -108, -108, -107, -106, -105, -104, -103, + -102, -101, -100, -99, -98, -97, -96, -95, + -94, -93, -91, -90, -89, -88, -86, -85, + -84, -82, -81, -79, -78, -76, -75, -73, + -71, -70, -68, -67, -65, -63, -62, -60, + -58, -56, -55, -53, -51, -49, -47, -45, + -44, -42, -40, -38, -36, -34, -32, -30, + -28, -26, -24, -22, -20, -18, -16, -14, + -12, -10, -8, -6, -4, -2, 0, 1, + 3, 5, 7, 9, 11, 13, 15, 17, + 19, 21, 23, 25, 27, 29, 31, 33, + 35, 37, 39, 41, 43, 45, 46, 48, + 50, 52, 54, 55, 57, 59, 61, 62, + 64, 66, 67, 69, 71, 72, 74, 75, + 77, 78, 80, 81, 83, 84, 86, 87, + 88, 90, 91, 92, 93, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, + 106, 106, 107, 108, 109, 109, 110, 111, + 111, 112, 112, 113, 113, 114, 114, 114, + 115, 115, 115, 115, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 115, 115, 115, + 115, 114, 114, 114, 113, 113, 112, 112, + 111, 111, 110, 110, 109, 108, 108, 107, + 106, 105, 104, 103, 102, 101, 100, 99, + 98, 97, 96, 95, 94, 93, 91, 90, + 89, 88, 86, 85, 84, 82, 81, 79, + 78, 76, 75, 73, 71, 70, 68, 67, + 65, 63, 62, 60, 58, 56, 55, 53, + 51, 49, 47, 45, 44, 42, 40, 38, + 36, 34, 32, 30, 28, 26, 24, 22, + 20, 18, 16, 14, 12, 10, 8, 6, + 4, 2, 0, -1, -3, -5, -7, -9, -11 +}; + +static u16 i2c_ident[] = { + V4L2_IDENT_OV9650, + V4L2_IDENT_OV9655, + V4L2_IDENT_SOI968, + V4L2_IDENT_OV7660, + V4L2_IDENT_OV7670, + V4L2_IDENT_MT9V011, + V4L2_IDENT_MT9V111, + V4L2_IDENT_MT9V112, + V4L2_IDENT_MT9M001C12ST, + V4L2_IDENT_MT9M111, + V4L2_IDENT_HV7131R, +}; + +static u16 bridge_init[][2] = { + {0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c}, + {0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40}, + {0x1068, 0x30}, {0x1069, 0x20}, {0x106a, 0x10}, + {0x106b, 0x08}, {0x1188, 0x87}, {0x11a1, 0x00}, + {0x11a2, 0x00}, {0x11a3, 0x6a}, {0x11a4, 0x50}, + {0x11ab, 0x00}, {0x11ac, 0x00}, {0x11ad, 0x50}, + {0x11ae, 0x3c}, {0x118a, 0x04}, {0x0395, 0x04}, + {0x11b8, 0x3a}, {0x118b, 0x0e}, {0x10f7, 0x05}, + {0x10f8, 0x14}, {0x10fa, 0xff}, {0x10f9, 0x00}, + {0x11ba, 0x0a}, {0x11a5, 0x2d}, {0x11a6, 0x2d}, + {0x11a7, 0x3a}, {0x11a8, 0x05}, {0x11a9, 0x04}, + {0x11aa, 0x3f}, {0x11af, 0x28}, {0x11b0, 0xd8}, + {0x11b1, 0x14}, {0x11b2, 0xec}, {0x11b3, 0x32}, + {0x11b4, 0xdd}, {0x11b5, 0x32}, {0x11b6, 0xdd}, + {0x10e0, 0x2c}, {0x11bc, 0x40}, {0x11bd, 0x01}, + {0x11be, 0xf0}, {0x11bf, 0x00}, {0x118c, 0x1f}, + {0x118d, 0x1f}, {0x118e, 0x1f}, {0x118f, 0x1f}, + {0x1180, 0x01}, {0x1181, 0x00}, {0x1182, 0x01}, + {0x1183, 0x00}, {0x1184, 0x50}, {0x1185, 0x80} +}; + +/* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */ +static u8 ov_gain[] = { + 0x00 /* 1x */, 0x04 /* 1.25x */, 0x08 /* 1.5x */, 0x0c /* 1.75x */, + 0x10 /* 2x */, 0x12 /* 2.25x */, 0x14 /* 2.5x */, 0x16 /* 2.75x */, + 0x18 /* 3x */, 0x1a /* 3.25x */, 0x1c /* 3.5x */, 0x1e /* 3.75x */, + 0x30 /* 4x */, 0x31 /* 4.25x */, 0x32 /* 4.5x */, 0x33 /* 4.75x */, + 0x34 /* 5x */, 0x35 /* 5.25x */, 0x36 /* 5.5x */, 0x37 /* 5.75x */, + 0x38 /* 6x */, 0x39 /* 6.25x */, 0x3a /* 6.5x */, 0x3b /* 6.75x */, + 0x3c /* 7x */, 0x3d /* 7.25x */, 0x3e /* 7.5x */, 0x3f /* 7.75x */, + 0x70 /* 8x */ +}; + +/* Gain = (bit[8] + 1) * (bit[7] + 1) * (bit[6:0] * 0.03125) */ +static u16 micron1_gain[] = { + /* 1x 1.25x 1.5x 1.75x */ + 0x0020, 0x0028, 0x0030, 0x0038, + /* 2x 2.25x 2.5x 2.75x */ + 0x00a0, 0x00a4, 0x00a8, 0x00ac, + /* 3x 3.25x 3.5x 3.75x */ + 0x00b0, 0x00b4, 0x00b8, 0x00bc, + /* 4x 4.25x 4.5x 4.75x */ + 0x00c0, 0x00c4, 0x00c8, 0x00cc, + /* 5x 5.25x 5.5x 5.75x */ + 0x00d0, 0x00d4, 0x00d8, 0x00dc, + /* 6x 6.25x 6.5x 6.75x */ + 0x00e0, 0x00e4, 0x00e8, 0x00ec, + /* 7x 7.25x 7.5x 7.75x */ + 0x00f0, 0x00f4, 0x00f8, 0x00fc, + /* 8x */ + 0x01c0 +}; + +/* mt9m001 sensor uses a different gain formula then other micron sensors */ +/* Gain = (bit[6] + 1) * (bit[5-0] * 0.125) */ +static u16 micron2_gain[] = { + /* 1x 1.25x 1.5x 1.75x */ + 0x0008, 0x000a, 0x000c, 0x000e, + /* 2x 2.25x 2.5x 2.75x */ + 0x0010, 0x0012, 0x0014, 0x0016, + /* 3x 3.25x 3.5x 3.75x */ + 0x0018, 0x001a, 0x001c, 0x001e, + /* 4x 4.25x 4.5x 4.75x */ + 0x0020, 0x0051, 0x0052, 0x0053, + /* 5x 5.25x 5.5x 5.75x */ + 0x0054, 0x0055, 0x0056, 0x0057, + /* 6x 6.25x 6.5x 6.75x */ + 0x0058, 0x0059, 0x005a, 0x005b, + /* 7x 7.25x 7.5x 7.75x */ + 0x005c, 0x005d, 0x005e, 0x005f, + /* 8x */ + 0x0060 +}; + +/* Gain = .5 + bit[7:0] / 16 */ +static u8 hv7131r_gain[] = { + 0x08 /* 1x */, 0x0c /* 1.25x */, 0x10 /* 1.5x */, 0x14 /* 1.75x */, + 0x18 /* 2x */, 0x1c /* 2.25x */, 0x20 /* 2.5x */, 0x24 /* 2.75x */, + 0x28 /* 3x */, 0x2c /* 3.25x */, 0x30 /* 3.5x */, 0x34 /* 3.75x */, + 0x38 /* 4x */, 0x3c /* 4.25x */, 0x40 /* 4.5x */, 0x44 /* 4.75x */, + 0x48 /* 5x */, 0x4c /* 5.25x */, 0x50 /* 5.5x */, 0x54 /* 5.75x */, + 0x58 /* 6x */, 0x5c /* 6.25x */, 0x60 /* 6.5x */, 0x64 /* 6.75x */, + 0x68 /* 7x */, 0x6c /* 7.25x */, 0x70 /* 7.5x */, 0x74 /* 7.75x */, + 0x78 /* 8x */ +}; + +static u8 soi968_init[][2] = { + {0x12, 0x80}, {0x0c, 0x00}, {0x0f, 0x1f}, + {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00}, + {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c}, + {0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff}, + {0x3e, 0x00}, {0x3f, 0x00}, {0x3b, 0x20}, + {0x3a, 0x96}, {0x3d, 0x0a}, {0x14, 0x8e}, + {0x13, 0x8a}, {0x12, 0x40}, {0x17, 0x13}, + {0x18, 0x63}, {0x19, 0x01}, {0x1a, 0x79}, + {0x32, 0x24}, {0x03, 0x00}, {0x11, 0x40}, + {0x2a, 0x10}, {0x2b, 0xe0}, {0x10, 0x32}, + {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80}, +}; + +static u8 ov7660_init[][2] = { + {0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3}, + {0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40}, + {0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a}, + {0x37, 0x0f}, {0x38, 0x02}, {0x39, 0x43}, + {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0xf6}, + {0x2e, 0x0b}, {0x01, 0x78}, {0x02, 0x50}, +}; + +static u8 ov7670_init[][2] = { + {0x12, 0x80}, {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01}, + {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00}, + {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0}, + {0xa2, 0x02}, {0x13, 0xe0}, {0x00, 0x00}, {0x10, 0x00}, + {0x0d, 0x40}, {0x14, 0x28}, {0xa5, 0x05}, {0xab, 0x07}, + {0x24, 0x95}, {0x25, 0x33}, {0x26, 0xe3}, {0x9f, 0x75}, + {0xa0, 0x65}, {0xa1, 0x0b}, {0xa6, 0xd8}, {0xa7, 0xd8}, + {0xa8, 0xf0}, {0xa9, 0x90}, {0xaa, 0x94}, {0x13, 0xe5}, + {0x0e, 0x61}, {0x0f, 0x4b}, {0x16, 0x02}, {0x1e, 0x27}, + {0x21, 0x02}, {0x22, 0x91}, {0x29, 0x07}, {0x33, 0x0b}, + {0x35, 0x0b}, {0x37, 0x1d}, {0x38, 0x71}, {0x39, 0x2a}, + {0x3c, 0x78}, {0x4d, 0x40}, {0x4e, 0x20}, {0x69, 0x00}, + {0x74, 0x19}, {0x8d, 0x4f}, {0x8e, 0x00}, {0x8f, 0x00}, + {0x90, 0x00}, {0x91, 0x00}, {0x96, 0x00}, {0x9a, 0x80}, + {0xb0, 0x84}, {0xb1, 0x0c}, {0xb2, 0x0e}, {0xb3, 0x82}, + {0xb8, 0x0a}, {0x43, 0x0a}, {0x44, 0xf0}, {0x45, 0x20}, + {0x46, 0x7d}, {0x47, 0x29}, {0x48, 0x4a}, {0x59, 0x8c}, + {0x5a, 0xa5}, {0x5b, 0xde}, {0x5c, 0x96}, {0x5d, 0x66}, + {0x5e, 0x10}, {0x6c, 0x0a}, {0x6d, 0x55}, {0x6e, 0x11}, + {0x6f, 0x9e}, {0x6a, 0x40}, {0x01, 0x40}, {0x02, 0x40}, + {0x13, 0xe7}, {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x02}, + {0x52, 0x1d}, {0x53, 0x56}, {0x54, 0x73}, {0x55, 0x0a}, + {0x56, 0x55}, {0x57, 0x80}, {0x58, 0x9e}, {0x41, 0x08}, + {0x3f, 0x02}, {0x75, 0x03}, {0x76, 0x63}, {0x4c, 0x04}, + {0x77, 0x06}, {0x3d, 0x02}, {0x4b, 0x09}, {0xc9, 0x30}, + {0x41, 0x08}, {0x56, 0x48}, {0x34, 0x11}, {0xa4, 0x88}, + {0x96, 0x00}, {0x97, 0x30}, {0x98, 0x20}, {0x99, 0x30}, + {0x9a, 0x84}, {0x9b, 0x29}, {0x9c, 0x03}, {0x9d, 0x99}, + {0x9e, 0x7f}, {0x78, 0x04}, {0x79, 0x01}, {0xc8, 0xf0}, + {0x79, 0x0f}, {0xc8, 0x00}, {0x79, 0x10}, {0xc8, 0x7e}, + {0x79, 0x0a}, {0xc8, 0x80}, {0x79, 0x0b}, {0xc8, 0x01}, + {0x79, 0x0c}, {0xc8, 0x0f}, {0x79, 0x0d}, {0xc8, 0x20}, + {0x79, 0x09}, {0xc8, 0x80}, {0x79, 0x02}, {0xc8, 0xc0}, + {0x79, 0x03}, {0xc8, 0x40}, {0x79, 0x05}, {0xc8, 0x30}, + {0x79, 0x26}, {0x62, 0x20}, {0x63, 0x00}, {0x64, 0x06}, + {0x65, 0x00}, {0x66, 0x05}, {0x94, 0x05}, {0x95, 0x0a}, + {0x17, 0x13}, {0x18, 0x01}, {0x19, 0x02}, {0x1a, 0x7a}, + {0x46, 0x59}, {0x47, 0x30}, {0x58, 0x9a}, {0x59, 0x84}, + {0x5a, 0x91}, {0x5b, 0x57}, {0x5c, 0x75}, {0x5d, 0x6d}, + {0x5e, 0x13}, {0x64, 0x07}, {0x94, 0x07}, {0x95, 0x0d}, + {0xa6, 0xdf}, {0xa7, 0xdf}, {0x48, 0x4d}, {0x51, 0x00}, + {0x6b, 0x0a}, {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, + {0x92, 0x00}, {0x93, 0x00}, {0x55, 0x0a}, {0x56, 0x60}, + {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, + {0x53, 0x56}, {0x54, 0x73}, {0x58, 0x9a}, {0x4f, 0x6e}, + {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, {0x53, 0x56}, + {0x54, 0x73}, {0x58, 0x9a}, {0x3f, 0x01}, {0x7b, 0x03}, + {0x7c, 0x09}, {0x7d, 0x16}, {0x7e, 0x38}, {0x7f, 0x47}, + {0x80, 0x53}, {0x81, 0x5e}, {0x82, 0x6a}, {0x83, 0x74}, + {0x84, 0x80}, {0x85, 0x8c}, {0x86, 0x9b}, {0x87, 0xb2}, + {0x88, 0xcc}, {0x89, 0xe5}, {0x7a, 0x24}, {0x3b, 0x00}, + {0x9f, 0x76}, {0xa0, 0x65}, {0x13, 0xe2}, {0x6b, 0x0a}, + {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, {0x92, 0x00}, + {0x93, 0x00}, +}; + +static u8 ov9650_init[][2] = { + {0x12, 0x80}, {0x00, 0x00}, {0x01, 0x78}, + {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03}, + {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00}, + {0x09, 0x01}, {0x0c, 0x00}, {0x0d, 0x00}, + {0x0e, 0xa0}, {0x0f, 0x52}, {0x10, 0x7c}, + {0x11, 0x80}, {0x12, 0x45}, {0x13, 0xc2}, + {0x14, 0x2e}, {0x15, 0x00}, {0x16, 0x07}, + {0x17, 0x24}, {0x18, 0xc5}, {0x19, 0x00}, + {0x1a, 0x3c}, {0x1b, 0x00}, {0x1e, 0x04}, + {0x1f, 0x00}, {0x24, 0x78}, {0x25, 0x68}, + {0x26, 0xd4}, {0x27, 0x80}, {0x28, 0x80}, + {0x29, 0x30}, {0x2a, 0x00}, {0x2b, 0x00}, + {0x2c, 0x80}, {0x2d, 0x00}, {0x2e, 0x00}, + {0x2f, 0x00}, {0x30, 0x08}, {0x31, 0x30}, + {0x32, 0x84}, {0x33, 0xe2}, {0x34, 0xbf}, + {0x35, 0x81}, {0x36, 0xf9}, {0x37, 0x00}, + {0x38, 0x93}, {0x39, 0x50}, {0x3a, 0x01}, + {0x3b, 0x01}, {0x3c, 0x73}, {0x3d, 0x19}, + {0x3e, 0x0b}, {0x3f, 0x80}, {0x40, 0xc1}, + {0x41, 0x00}, {0x42, 0x08}, {0x67, 0x80}, + {0x68, 0x80}, {0x69, 0x40}, {0x6a, 0x00}, + {0x6b, 0x0a}, {0x8b, 0x06}, {0x8c, 0x20}, + {0x8d, 0x00}, {0x8e, 0x00}, {0x8f, 0xdf}, + {0x92, 0x00}, {0x93, 0x00}, {0x94, 0x88}, + {0x95, 0x88}, {0x96, 0x04}, {0xa1, 0x00}, + {0xa5, 0x80}, {0xa8, 0x80}, {0xa9, 0xb8}, + {0xaa, 0x92}, {0xab, 0x0a}, +}; + +static u8 ov9655_init[][2] = { + {0x12, 0x80}, {0x12, 0x01}, {0x0d, 0x00}, {0x0e, 0x61}, + {0x11, 0x80}, {0x13, 0xba}, {0x14, 0x2e}, {0x16, 0x24}, + {0x1e, 0x04}, {0x1e, 0x04}, {0x1e, 0x04}, {0x27, 0x08}, + {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x32, 0xbf}, + {0x34, 0x3d}, {0x35, 0x00}, {0x36, 0xf8}, {0x38, 0x12}, + {0x39, 0x57}, {0x3a, 0x00}, {0x3b, 0xcc}, {0x3c, 0x0c}, + {0x3d, 0x19}, {0x3e, 0x0c}, {0x3f, 0x01}, {0x41, 0x40}, + {0x42, 0x80}, {0x45, 0x46}, {0x46, 0x62}, {0x47, 0x2a}, + {0x48, 0x3c}, {0x4a, 0xf0}, {0x4b, 0xdc}, {0x4c, 0xdc}, + {0x4d, 0xdc}, {0x4e, 0xdc}, {0x69, 0x02}, {0x6c, 0x04}, + {0x6f, 0x9e}, {0x70, 0x05}, {0x71, 0x78}, {0x77, 0x02}, + {0x8a, 0x23}, {0x8c, 0x0d}, {0x90, 0x7e}, {0x91, 0x7c}, + {0x9f, 0x6e}, {0xa0, 0x6e}, {0xa5, 0x68}, {0xa6, 0x60}, + {0xa8, 0xc1}, {0xa9, 0xfa}, {0xaa, 0x92}, {0xab, 0x04}, + {0xac, 0x80}, {0xad, 0x80}, {0xae, 0x80}, {0xaf, 0x80}, + {0xb2, 0xf2}, {0xb3, 0x20}, {0xb5, 0x00}, {0xb6, 0xaf}, + {0xbb, 0xae}, {0xbc, 0x44}, {0xbd, 0x44}, {0xbe, 0x3b}, + {0xbf, 0x3a}, {0xc0, 0xe2}, {0xc1, 0xc8}, {0xc2, 0x01}, + {0xc4, 0x00}, {0xc6, 0x85}, {0xc7, 0x81}, {0xc9, 0xe0}, + {0xca, 0xe8}, {0xcc, 0xd8}, {0xcd, 0x93}, {0x12, 0x61}, + {0x36, 0xfa}, {0x8c, 0x8d}, {0xc0, 0xaa}, {0x69, 0x0a}, + {0x03, 0x12}, {0x17, 0x14}, {0x18, 0x00}, {0x19, 0x01}, + {0x1a, 0x3d}, {0x32, 0xbf}, {0x11, 0x80}, {0x2a, 0x10}, + {0x2b, 0x0a}, {0x92, 0x00}, {0x93, 0x00}, {0x1e, 0x04}, + {0x1e, 0x04}, {0x10, 0x7c}, {0x04, 0x03}, {0xa1, 0x00}, + {0x2d, 0x00}, {0x2e, 0x00}, {0x00, 0x00}, {0x01, 0x80}, + {0x02, 0x80}, {0x12, 0x61}, {0x36, 0xfa}, {0x8c, 0x8d}, + {0xc0, 0xaa}, {0x69, 0x0a}, {0x03, 0x12}, {0x17, 0x14}, + {0x18, 0x00}, {0x19, 0x01}, {0x1a, 0x3d}, {0x32, 0xbf}, + {0x11, 0x80}, {0x2a, 0x10}, {0x2b, 0x0a}, {0x92, 0x00}, + {0x93, 0x00}, {0x04, 0x01}, {0x10, 0x1f}, {0xa1, 0x00}, + {0x00, 0x0a}, {0xa1, 0x00}, {0x10, 0x5d}, {0x04, 0x03}, + {0x00, 0x01}, {0xa1, 0x00}, {0x10, 0x7c}, {0x04, 0x03}, + {0x00, 0x03}, {0x00, 0x0a}, {0x00, 0x10}, {0x00, 0x13}, +}; + +static u16 mt9v112_init[][2] = { + {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020}, + {0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b}, + {0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001}, + {0x05, 0x0000}, {0x06, 0x340c}, {0x3b, 0x042a}, + {0x3c, 0x0400}, {0xf0, 0x0002}, {0x2e, 0x0c58}, + {0x5b, 0x0001}, {0xc8, 0x9f0b}, {0xf0, 0x0001}, + {0x9b, 0x5300}, {0xf0, 0x0000}, {0x2b, 0x0020}, + {0x2c, 0x002a}, {0x2d, 0x0032}, {0x2e, 0x0020}, + {0x09, 0x01dc}, {0x01, 0x000c}, {0x02, 0x0020}, + {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c}, + {0x05, 0x0098}, {0x20, 0x0703}, {0x09, 0x01f2}, + {0x2b, 0x00a0}, {0x2c, 0x00a0}, {0x2d, 0x00a0}, + {0x2e, 0x00a0}, {0x01, 0x000c}, {0x02, 0x0020}, + {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c}, + {0x05, 0x0098}, {0x09, 0x01c1}, {0x2b, 0x00ae}, + {0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae}, +}; + +static u16 mt9v111_init[][2] = { + {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000}, + {0x01, 0x0001}, {0x02, 0x0016}, {0x03, 0x01e1}, + {0x04, 0x0281}, {0x05, 0x0004}, {0x07, 0x3002}, + {0x21, 0x0000}, {0x25, 0x4024}, {0x26, 0xff03}, + {0x27, 0xff10}, {0x2b, 0x7828}, {0x2c, 0xb43c}, + {0x2d, 0xf0a0}, {0x2e, 0x0c64}, {0x2f, 0x0064}, + {0x67, 0x4010}, {0x06, 0x301e}, {0x08, 0x0480}, + {0x01, 0x0004}, {0x02, 0x0016}, {0x03, 0x01e6}, + {0x04, 0x0286}, {0x05, 0x0004}, {0x06, 0x0000}, + {0x07, 0x3002}, {0x08, 0x0008}, {0x0c, 0x0000}, + {0x0d, 0x0000}, {0x0e, 0x0000}, {0x0f, 0x0000}, + {0x10, 0x0000}, {0x11, 0x0000}, {0x12, 0x00b0}, + {0x13, 0x007c}, {0x14, 0x0000}, {0x15, 0x0000}, + {0x16, 0x0000}, {0x17, 0x0000}, {0x18, 0x0000}, + {0x19, 0x0000}, {0x1a, 0x0000}, {0x1b, 0x0000}, + {0x1c, 0x0000}, {0x1d, 0x0000}, {0x30, 0x0000}, + {0x30, 0x0005}, {0x31, 0x0000}, {0x02, 0x0016}, + {0x03, 0x01e1}, {0x04, 0x0281}, {0x05, 0x0004}, + {0x06, 0x0000}, {0x07, 0x3002}, {0x06, 0x002d}, + {0x05, 0x0004}, {0x09, 0x0064}, {0x2b, 0x00a0}, + {0x2c, 0x00a0}, {0x2d, 0x00a0}, {0x2e, 0x00a0}, + {0x02, 0x0016}, {0x03, 0x01e1}, {0x04, 0x0281}, + {0x05, 0x0004}, {0x06, 0x002d}, {0x07, 0x3002}, + {0x0e, 0x0008}, {0x06, 0x002d}, {0x05, 0x0004}, +}; + +static u16 mt9v011_init[][2] = { + {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000}, + {0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1}, + {0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006}, + {0x0d, 0x0002}, {0x0a, 0x0000}, {0x0b, 0x0000}, + {0x0c, 0x0000}, {0x0d, 0x0000}, {0x0e, 0x0000}, + {0x0f, 0x0000}, {0x10, 0x0000}, {0x11, 0x0000}, + {0x12, 0x0000}, {0x13, 0x0000}, {0x14, 0x0000}, + {0x15, 0x0000}, {0x16, 0x0000}, {0x17, 0x0000}, + {0x18, 0x0000}, {0x19, 0x0000}, {0x1a, 0x0000}, + {0x1b, 0x0000}, {0x1c, 0x0000}, {0x1d, 0x0000}, + {0x32, 0x0000}, {0x20, 0x1101}, {0x21, 0x0000}, + {0x22, 0x0000}, {0x23, 0x0000}, {0x24, 0x0000}, + {0x25, 0x0000}, {0x26, 0x0000}, {0x27, 0x0024}, + {0x2f, 0xf7b0}, {0x30, 0x0005}, {0x31, 0x0000}, + {0x32, 0x0000}, {0x33, 0x0000}, {0x34, 0x0100}, + {0x3d, 0x068f}, {0x40, 0x01e0}, {0x41, 0x00d1}, + {0x44, 0x0082}, {0x5a, 0x0000}, {0x5b, 0x0000}, + {0x5c, 0x0000}, {0x5d, 0x0000}, {0x5e, 0x0000}, + {0x5f, 0xa31d}, {0x62, 0x0611}, {0x0a, 0x0000}, + {0x06, 0x0029}, {0x05, 0x0009}, {0x20, 0x1101}, + {0x20, 0x1101}, {0x09, 0x0064}, {0x07, 0x0003}, + {0x2b, 0x0033}, {0x2c, 0x00a0}, {0x2d, 0x00a0}, + {0x2e, 0x0033}, {0x07, 0x0002}, {0x06, 0x0000}, + {0x06, 0x0029}, {0x05, 0x0009}, +}; + +static u16 mt9m001_init[][2] = { + {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x000e}, + {0x02, 0x0014}, {0x03, 0x03c1}, {0x04, 0x0501}, + {0x05, 0x0083}, {0x06, 0x0006}, {0x0d, 0x0002}, + {0x0a, 0x0000}, {0x0c, 0x0000}, {0x11, 0x0000}, + {0x1e, 0x8000}, {0x5f, 0x8904}, {0x60, 0x0000}, + {0x61, 0x0000}, {0x62, 0x0498}, {0x63, 0x0000}, + {0x64, 0x0000}, {0x20, 0x111d}, {0x06, 0x00f2}, + {0x05, 0x0013}, {0x09, 0x10f2}, {0x07, 0x0003}, + {0x2b, 0x002a}, {0x2d, 0x002a}, {0x2c, 0x002a}, + {0x2e, 0x0029}, {0x07, 0x0002}, +}; + +static u16 mt9m111_init[][2] = { + {0xf0, 0x0000}, {0x0d, 0x0008}, {0x0d, 0x0009}, + {0x0d, 0x0008}, {0xf0, 0x0001}, {0x3a, 0x4300}, + {0x9b, 0x4300}, {0xa1, 0x0280}, {0xa4, 0x0200}, + {0x06, 0x308e}, {0xf0, 0x0000}, +}; + +static u8 hv7131r_init[][2] = { + {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08}, + {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0}, + {0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08}, + {0x01, 0x08}, {0x01, 0x08}, {0x25, 0x07}, + {0x26, 0xc3}, {0x27, 0x50}, {0x30, 0x62}, + {0x31, 0x10}, {0x32, 0x06}, {0x33, 0x10}, + {0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00}, + {0x23, 0x09}, {0x01, 0x08}, +}; + +int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) +{ + struct usb_device *dev = gspca_dev->dev; + int result; + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + reg, + 0x00, + gspca_dev->usb_buf, + length, + 500); + if (unlikely(result < 0 || result != length)) { + err("Read register failed 0x%02X", reg); + return -EIO; + } + return 0; +} + +int reg_w(struct gspca_dev *gspca_dev, u16 reg, const u8 *buffer, int length) +{ + struct usb_device *dev = gspca_dev->dev; + int result; + memcpy(gspca_dev->usb_buf, buffer, length); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + reg, + 0x00, + gspca_dev->usb_buf, + length, + 500); + if (unlikely(result < 0 || result != length)) { + err("Write register failed index 0x%02X", reg); + return -EIO; + } + return 0; +} + +int reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value) +{ + u8 data[1] = {value}; + return reg_w(gspca_dev, reg, data, 1); +} + +int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) +{ + int i; + reg_w(gspca_dev, 0x10c0, buffer, 8); + for (i = 0; i < 5; i++) { + reg_r(gspca_dev, 0x10c0, 1); + if (gspca_dev->usb_buf[0] & 0x04) { + if (gspca_dev->usb_buf[0] & 0x08) + return -1; + return 0; + } + msleep(1); + } + return -1; +} + +int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + u8 row[8]; + + /* + * from the point of view of the bridge, the length + * includes the address + */ + row[0] = 0x81 | (2 << 4); + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = val; + row[4] = 0x00; + row[5] = 0x00; + row[6] = 0x00; + row[7] = 0x10; + + return i2c_w(gspca_dev, row); +} + +int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 row[8]; + + /* + * from the point of view of the bridge, the length + * includes the address + */ + row[0] = 0x81 | (3 << 4); + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = (val >> 8) & 0xff; + row[4] = val & 0xff; + row[5] = 0x00; + row[6] = 0x00; + row[7] = 0x10; + + return i2c_w(gspca_dev, row); +} + +int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 row[8]; + + row[0] = 0x81 | 0x10; + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = 0; + row[4] = 0; + row[5] = 0; + row[6] = 0; + row[7] = 0x10; + reg_w(gspca_dev, 0x10c0, row, 8); + msleep(1); + row[0] = 0x81 | (2 << 4) | 0x02; + row[2] = 0; + reg_w(gspca_dev, 0x10c0, row, 8); + msleep(1); + reg_r(gspca_dev, 0x10c2, 5); + *val = gspca_dev->usb_buf[3]; + return 0; +} + +int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 row[8]; + + row[0] = 0x81 | 0x10; + row[1] = sd->i2c_addr; + row[2] = reg; + row[3] = 0; + row[4] = 0; + row[5] = 0; + row[6] = 0; + row[7] = 0x10; + reg_w(gspca_dev, 0x10c0, row, 8); + msleep(1); + row[0] = 0x81 | (3 << 4) | 0x02; + row[2] = 0; + reg_w(gspca_dev, 0x10c0, row, 8); + msleep(1); + reg_r(gspca_dev, 0x10c2, 5); + *val = (gspca_dev->usb_buf[2] << 8) | gspca_dev->usb_buf[3]; + return 0; +} + +static int ov9650_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(ov9650_init); i++) { + if (i2c_w1(gspca_dev, ov9650_init[i][0], + ov9650_init[i][1]) < 0) { + err("OV9650 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 1; + sd->vstart = 7; + return 0; +} + +static int ov9655_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(ov9655_init); i++) { + if (i2c_w1(gspca_dev, ov9655_init[i][0], + ov9655_init[i][1]) < 0) { + err("OV9655 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 0; + sd->vstart = 7; + return 0; +} + +static int soi968_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(soi968_init); i++) { + if (i2c_w1(gspca_dev, soi968_init[i][0], + soi968_init[i][1]) < 0) { + err("SOI968 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 60; + sd->vstart = 11; + return 0; +} + +static int ov7660_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(ov7660_init); i++) { + if (i2c_w1(gspca_dev, ov7660_init[i][0], + ov7660_init[i][1]) < 0) { + err("OV7660 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 1; + sd->vstart = 1; + return 0; +} + +static int ov7670_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(ov7670_init); i++) { + if (i2c_w1(gspca_dev, ov7670_init[i][0], + ov7670_init[i][1]) < 0) { + err("OV7670 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 0; + sd->vstart = 1; + return 0; +} + +static int mt9v_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + u16 value; + int ret; + + sd->i2c_addr = 0x5d; + ret = i2c_r2(gspca_dev, 0xff, &value); + if ((ret == 0) && (value == 0x8243)) { + for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) { + if (i2c_w2(gspca_dev, mt9v011_init[i][0], + mt9v011_init[i][1]) < 0) { + err("MT9V011 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 2; + sd->vstart = 2; + sd->sensor = SENSOR_MT9V011; + info("MT9V011 sensor detected"); + return 0; + } + + sd->i2c_addr = 0x5c; + i2c_w2(gspca_dev, 0x01, 0x0004); + ret = i2c_r2(gspca_dev, 0xff, &value); + if ((ret == 0) && (value == 0x823a)) { + for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) { + if (i2c_w2(gspca_dev, mt9v111_init[i][0], + mt9v111_init[i][1]) < 0) { + err("MT9V111 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 2; + sd->vstart = 2; + sd->sensor = SENSOR_MT9V111; + info("MT9V111 sensor detected"); + return 0; + } + + sd->i2c_addr = 0x5d; + ret = i2c_w2(gspca_dev, 0xf0, 0x0000); + if (ret < 0) { + sd->i2c_addr = 0x48; + i2c_w2(gspca_dev, 0xf0, 0x0000); + } + ret = i2c_r2(gspca_dev, 0x00, &value); + if ((ret == 0) && (value == 0x1229)) { + for (i = 0; i < ARRAY_SIZE(mt9v112_init); i++) { + if (i2c_w2(gspca_dev, mt9v112_init[i][0], + mt9v112_init[i][1]) < 0) { + err("MT9V112 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 6; + sd->vstart = 2; + sd->sensor = SENSOR_MT9V112; + info("MT9V112 sensor detected"); + return 0; + } + + return -ENODEV; +} + +static int mt9m111_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) { + if (i2c_w2(gspca_dev, mt9m111_init[i][0], + mt9m111_init[i][1]) < 0) { + err("MT9M111 sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 0; + sd->vstart = 2; + return 0; +} + +static int mt9m001_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) { + if (i2c_w2(gspca_dev, mt9m001_init[i][0], + mt9m001_init[i][1]) < 0) { + err("MT9M001 sensor initialization failed"); + return -ENODEV; + } + } + /* disable hflip and vflip */ + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + sd->hstart = 2; + sd->vstart = 2; + return 0; +} + +static int hv7131r_init_sensor(struct gspca_dev *gspca_dev) +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + for (i = 0; i < ARRAY_SIZE(hv7131r_init); i++) { + if (i2c_w1(gspca_dev, hv7131r_init[i][0], + hv7131r_init[i][1]) < 0) { + err("HV7131R Sensor initialization failed"); + return -ENODEV; + } + } + sd->hstart = 0; + sd->vstart = 1; + return 0; +} + +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV +static int input_kthread(void *data) +{ + struct gspca_dev *gspca_dev = (struct gspca_dev *)data; + struct sd *sd = (struct sd *) gspca_dev; + + DECLARE_WAIT_QUEUE_HEAD(wait); + set_freezable(); + for (;;) { + if (kthread_should_stop()) + break; + + if (reg_r(gspca_dev, 0x1005, 1) < 0) + continue; + + input_report_key(sd->input_dev, + KEY_CAMERA, + gspca_dev->usb_buf[0] & sd->input_gpio); + input_sync(sd->input_dev); + + wait_event_freezable_timeout(wait, + kthread_should_stop(), + msecs_to_jiffies(100)); + } + return 0; +} + + +static int sn9c20x_input_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + if (sd->input_gpio == 0) + return 0; + + sd->input_dev = input_allocate_device(); + if (!sd->input_dev) + return -ENOMEM; + + sd->input_dev->name = "SN9C20X Webcam"; + + sd->input_dev->phys = kasprintf(GFP_KERNEL, "usb-%s-%s", + gspca_dev->dev->bus->bus_name, + gspca_dev->dev->devpath); + + if (!sd->input_dev->phys) + return -ENOMEM; + + usb_to_input_id(gspca_dev->dev, &sd->input_dev->id); + sd->input_dev->dev.parent = &gspca_dev->dev->dev; + + set_bit(EV_KEY, sd->input_dev->evbit); + set_bit(KEY_CAMERA, sd->input_dev->keybit); + + if (input_register_device(sd->input_dev)) + return -EINVAL; + + sd->input_task = kthread_run(input_kthread, gspca_dev, "sn9c20x/%d", + gspca_dev->vdev.minor); + + if (IS_ERR(sd->input_task)) + return -EINVAL; + + return 0; +} + +static void sn9c20x_input_cleanup(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + if (sd->input_task != NULL && !IS_ERR(sd->input_task)) + kthread_stop(sd->input_task); + + if (sd->input_dev != NULL) { + input_unregister_device(sd->input_dev); + kfree(sd->input_dev->phys); + input_free_device(sd->input_dev); + sd->input_dev = NULL; + } +} +#endif + +static int set_cmatrix(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 hue_coord, hue_index = 180 + sd->hue; + u8 cmatrix[21]; + memset(cmatrix, 0, 21); + + cmatrix[2] = (sd->contrast * 0x25 / 0x100) + 0x26; + cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25; + cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25; + cmatrix[18] = sd->brightness - 0x80; + + hue_coord = (hsv_red_x[hue_index] * sd->saturation) >> 8; + cmatrix[6] = (unsigned char)(hue_coord & 0xff); + cmatrix[7] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_red_y[hue_index] * sd->saturation) >> 8; + cmatrix[8] = (unsigned char)(hue_coord & 0xff); + cmatrix[9] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_green_x[hue_index] * sd->saturation) >> 8; + cmatrix[10] = (unsigned char)(hue_coord & 0xff); + cmatrix[11] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_green_y[hue_index] * sd->saturation) >> 8; + cmatrix[12] = (unsigned char)(hue_coord & 0xff); + cmatrix[13] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_blue_x[hue_index] * sd->saturation) >> 8; + cmatrix[14] = (unsigned char)(hue_coord & 0xff); + cmatrix[15] = (unsigned char)((hue_coord >> 8) & 0x0f); + + hue_coord = (hsv_blue_y[hue_index] * sd->saturation) >> 8; + cmatrix[16] = (unsigned char)(hue_coord & 0xff); + cmatrix[17] = (unsigned char)((hue_coord >> 8) & 0x0f); + + return reg_w(gspca_dev, 0x10e1, cmatrix, 21); +} + +static int set_gamma(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 gamma[17]; + u8 gval = sd->gamma * 0xb8 / 0x100; + + + gamma[0] = 0x0a; + gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8); + gamma[2] = 0x25 + (gval * (0xee - 0x25) / 0xb8); + gamma[3] = 0x37 + (gval * (0xfa - 0x37) / 0xb8); + gamma[4] = 0x45 + (gval * (0xfc - 0x45) / 0xb8); + gamma[5] = 0x55 + (gval * (0xfb - 0x55) / 0xb8); + gamma[6] = 0x65 + (gval * (0xfc - 0x65) / 0xb8); + gamma[7] = 0x74 + (gval * (0xfd - 0x74) / 0xb8); + gamma[8] = 0x83 + (gval * (0xfe - 0x83) / 0xb8); + gamma[9] = 0x92 + (gval * (0xfc - 0x92) / 0xb8); + gamma[10] = 0xa1 + (gval * (0xfc - 0xa1) / 0xb8); + gamma[11] = 0xb0 + (gval * (0xfc - 0xb0) / 0xb8); + gamma[12] = 0xbf + (gval * (0xfb - 0xbf) / 0xb8); + gamma[13] = 0xce + (gval * (0xfb - 0xce) / 0xb8); + gamma[14] = 0xdf + (gval * (0xfd - 0xdf) / 0xb8); + gamma[15] = 0xea + (gval * (0xf9 - 0xea) / 0xb8); + gamma[16] = 0xf5; + + return reg_w(gspca_dev, 0x1190, gamma, 17); +} + +static int set_redblue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + reg_w1(gspca_dev, 0x118c, sd->red); + reg_w1(gspca_dev, 0x118f, sd->blue); + return 0; +} + +static int set_hvflip(struct gspca_dev *gspca_dev) +{ + u8 value, tslb; + u16 value2; + struct sd *sd = (struct sd *) gspca_dev; + switch (sd->sensor) { + case SENSOR_OV9650: + i2c_r1(gspca_dev, 0x1e, &value); + value &= ~0x30; + tslb = 0x01; + if (sd->hflip) + value |= 0x20; + if (sd->vflip) { + value |= 0x10; + tslb = 0x49; + } + i2c_w1(gspca_dev, 0x1e, value); + i2c_w1(gspca_dev, 0x3a, tslb); + break; + case SENSOR_MT9V111: + case SENSOR_MT9V011: + i2c_r2(gspca_dev, 0x20, &value2); + value2 &= ~0xc0a0; + if (sd->hflip) + value2 |= 0x8080; + if (sd->vflip) + value2 |= 0x4020; + i2c_w2(gspca_dev, 0x20, value2); + break; + case SENSOR_MT9M111: + case SENSOR_MT9V112: + i2c_r2(gspca_dev, 0x20, &value2); + value2 &= ~0x0003; + if (sd->hflip) + value2 |= 0x0002; + if (sd->vflip) + value2 |= 0x0001; + i2c_w2(gspca_dev, 0x20, value2); + break; + case SENSOR_HV7131R: + i2c_r1(gspca_dev, 0x01, &value); + value &= ~0x03; + if (sd->vflip) + value |= 0x01; + if (sd->hflip) + value |= 0x02; + i2c_w1(gspca_dev, 0x01, value); + break; + } + return 0; +} + +static int set_exposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 exp[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e}; + switch (sd->sensor) { + case SENSOR_OV7660: + case SENSOR_OV7670: + case SENSOR_SOI968: + case SENSOR_OV9655: + case SENSOR_OV9650: + exp[0] |= (3 << 4); + exp[2] = 0x2d; + exp[3] = sd->exposure & 0xff; + exp[4] = sd->exposure >> 8; + break; + case SENSOR_MT9M001: + case SENSOR_MT9M111: + case SENSOR_MT9V112: + case SENSOR_MT9V111: + case SENSOR_MT9V011: + exp[0] |= (3 << 4); + exp[2] = 0x09; + exp[3] = sd->exposure >> 8; + exp[4] = sd->exposure & 0xff; + break; + case SENSOR_HV7131R: + exp[0] |= (4 << 4); + exp[2] = 0x25; + exp[3] = ((sd->exposure * 0xffffff) / 0xffff) >> 16; + exp[4] = ((sd->exposure * 0xffffff) / 0xffff) >> 8; + exp[5] = ((sd->exposure * 0xffffff) / 0xffff) & 0xff; + break; + } + i2c_w(gspca_dev, exp); + return 0; +} + +static int set_gain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 gain[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d}; + switch (sd->sensor) { + case SENSOR_OV7660: + case SENSOR_OV7670: + case SENSOR_SOI968: + case SENSOR_OV9655: + case SENSOR_OV9650: + gain[0] |= (2 << 4); + gain[3] = ov_gain[sd->gain]; + break; + case SENSOR_MT9V011: + case SENSOR_MT9V111: + gain[0] |= (3 << 4); + gain[2] = 0x35; + gain[3] = micron1_gain[sd->gain] >> 8; + gain[4] = micron1_gain[sd->gain] & 0xff; + break; + case SENSOR_MT9V112: + case SENSOR_MT9M111: + gain[0] |= (3 << 4); + gain[2] = 0x2f; + gain[3] = micron1_gain[sd->gain] >> 8; + gain[4] = micron1_gain[sd->gain] & 0xff; + break; + case SENSOR_MT9M001: + gain[0] |= (3 << 4); + gain[2] = 0x2f; + gain[3] = micron2_gain[sd->gain] >> 8; + gain[4] = micron2_gain[sd->gain] & 0xff; + break; + case SENSOR_HV7131R: + gain[0] |= (2 << 4); + gain[2] = 0x30; + gain[3] = hv7131r_gain[sd->gain]; + break; + } + i2c_w(gspca_dev, gain); + return 0; +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + return set_cmatrix(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->brightness; + return 0; +} + + +static int sd_setcontrast(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + return set_cmatrix(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->contrast; + return 0; +} + +static int sd_setsaturation(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->saturation = val; + if (gspca_dev->streaming) + return set_cmatrix(gspca_dev); + return 0; +} + +static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->saturation; + return 0; +} + +static int sd_sethue(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = val; + if (gspca_dev->streaming) + return set_cmatrix(gspca_dev); + return 0; +} + +static int sd_gethue(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->hue; + return 0; +} + +static int sd_setgamma(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gamma = val; + if (gspca_dev->streaming) + return set_gamma(gspca_dev); + return 0; +} + +static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->gamma; + return 0; +} + +static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->red = val; + if (gspca_dev->streaming) + return set_redblue(gspca_dev); + return 0; +} + +static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->red; + return 0; +} + +static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->blue = val; + if (gspca_dev->streaming) + return set_redblue(gspca_dev); + return 0; +} + +static int sd_getbluebalance(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->blue; + return 0; +} + +static int sd_sethflip(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) + return set_hvflip(gspca_dev); + return 0; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->hflip; + return 0; +} + +static int sd_setvflip(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + if (gspca_dev->streaming) + return set_hvflip(gspca_dev); + return 0; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->vflip; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + return set_exposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->exposure; + return 0; +} + +static int sd_setgain(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + return set_gain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->gain; + return 0; +} + +static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + sd->auto_exposure = val; + return 0; +} + +static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->auto_exposure; + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int sd_dbg_g_register(struct gspca_dev *gspca_dev, + struct v4l2_dbg_register *reg) +{ + struct sd *sd = (struct sd *) gspca_dev; + switch (reg->match.type) { + case V4L2_CHIP_MATCH_HOST: + if (reg->match.addr != 0) + return -EINVAL; + if (reg->reg < 0x1000 || reg->reg > 0x11ff) + return -EINVAL; + if (reg_r(gspca_dev, reg->reg, 1) < 0) + return -EINVAL; + reg->val = gspca_dev->usb_buf[0]; + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + if (reg->match.addr != sd->i2c_addr) + return -EINVAL; + if (sd->sensor >= SENSOR_MT9V011 && + sd->sensor <= SENSOR_MT9M111) { + if (i2c_r2(gspca_dev, reg->reg, (u16 *)®->val) < 0) + return -EINVAL; + } else { + if (i2c_r1(gspca_dev, reg->reg, (u8 *)®->val) < 0) + return -EINVAL; + } + return 0; + } + return -EINVAL; +} + +static int sd_dbg_s_register(struct gspca_dev *gspca_dev, + struct v4l2_dbg_register *reg) +{ + struct sd *sd = (struct sd *) gspca_dev; + switch (reg->match.type) { + case V4L2_CHIP_MATCH_HOST: + if (reg->match.addr != 0) + return -EINVAL; + if (reg->reg < 0x1000 || reg->reg > 0x11ff) + return -EINVAL; + if (reg_w1(gspca_dev, reg->reg, reg->val) < 0) + return -EINVAL; + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + if (reg->match.addr != sd->i2c_addr) + return -EINVAL; + if (sd->sensor >= SENSOR_MT9V011 && + sd->sensor <= SENSOR_MT9M111) { + if (i2c_w2(gspca_dev, reg->reg, reg->val) < 0) + return -EINVAL; + } else { + if (i2c_w1(gspca_dev, reg->reg, reg->val) < 0) + return -EINVAL; + } + return 0; + } + return -EINVAL; +} +#endif + +static int sd_chip_ident(struct gspca_dev *gspca_dev, + struct v4l2_dbg_chip_ident *chip) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (chip->match.type) { + case V4L2_CHIP_MATCH_HOST: + if (chip->match.addr != 0) + return -EINVAL; + chip->revision = 0; + chip->ident = V4L2_IDENT_SN9C20X; + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + if (chip->match.addr != sd->i2c_addr) + return -EINVAL; + chip->revision = 0; + chip->ident = i2c_ident[sd->sensor]; + return 0; + } + return -EINVAL; +} + +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + + sd->sensor = (id->driver_info >> 8) & 0xff; + sd->i2c_addr = id->driver_info & 0xff; + + switch (sd->sensor) { + case SENSOR_OV9650: + cam->cam_mode = sxga_mode; + cam->nmodes = ARRAY_SIZE(sxga_mode); + break; + default: + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + } + + sd->old_step = 0; + sd->older_step = 0; + sd->exposure_step = 16; + + sd->brightness = BRIGHTNESS_DEFAULT; + sd->contrast = CONTRAST_DEFAULT; + sd->saturation = SATURATION_DEFAULT; + sd->hue = HUE_DEFAULT; + sd->gamma = GAMMA_DEFAULT; + sd->red = RED_DEFAULT; + sd->blue = BLUE_DEFAULT; + + sd->hflip = HFLIP_DEFAULT; + sd->vflip = VFLIP_DEFAULT; + sd->exposure = EXPOSURE_DEFAULT; + sd->gain = GAIN_DEFAULT; + sd->auto_exposure = AUTO_EXPOSURE_DEFAULT; + + sd->quality = 95; + +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV + sd->input_gpio = (id->driver_info >> 16) & 0xff; + if (sn9c20x_input_init(gspca_dev) < 0) + return -ENODEV; +#endif + return 0; +} + +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + u8 value; + u8 i2c_init[9] = + {0x80, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}; + + for (i = 0; i < ARRAY_SIZE(bridge_init); i++) { + value = bridge_init[i][1]; + if (reg_w(gspca_dev, bridge_init[i][0], &value, 1) < 0) { + err("Device initialization failed"); + return -ENODEV; + } + } + + if (reg_w(gspca_dev, 0x10c0, i2c_init, 9) < 0) { + err("Device initialization failed"); + return -ENODEV; + } + + switch (sd->sensor) { + case SENSOR_OV9650: + if (ov9650_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("OV9650 sensor detected"); + break; + case SENSOR_OV9655: + if (ov9655_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("OV9655 sensor detected"); + break; + case SENSOR_SOI968: + if (soi968_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("SOI968 sensor detected"); + break; + case SENSOR_OV7660: + if (ov7660_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("OV7660 sensor detected"); + break; + case SENSOR_OV7670: + if (ov7670_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("OV7670 sensor detected"); + break; + case SENSOR_MT9VPRB: + if (mt9v_init_sensor(gspca_dev) < 0) + return -ENODEV; + break; + case SENSOR_MT9M111: + if (mt9m111_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("MT9M111 sensor detected"); + break; + case SENSOR_MT9M001: + if (mt9m001_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("MT9M001 sensor detected"); + break; + case SENSOR_HV7131R: + if (hv7131r_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("HV7131R sensor detected"); + break; + default: + info("Unsupported Sensor"); + return -ENODEV; + } + + return 0; +} + +static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 value; + switch (sd->sensor) { + case SENSOR_OV9650: + if (mode & MODE_SXGA) { + i2c_w1(gspca_dev, 0x17, 0x1b); + i2c_w1(gspca_dev, 0x18, 0xbc); + i2c_w1(gspca_dev, 0x19, 0x01); + i2c_w1(gspca_dev, 0x1a, 0x82); + i2c_r1(gspca_dev, 0x12, &value); + i2c_w1(gspca_dev, 0x12, value & 0x07); + } else { + i2c_w1(gspca_dev, 0x17, 0x24); + i2c_w1(gspca_dev, 0x18, 0xc5); + i2c_w1(gspca_dev, 0x19, 0x00); + i2c_w1(gspca_dev, 0x1a, 0x3c); + i2c_r1(gspca_dev, 0x12, &value); + i2c_w1(gspca_dev, 0x12, (value & 0x7) | 0x40); + } + break; + } +} + +#define HW_WIN(mode, hstart, vstart) \ +((const u8 []){hstart & 0xff, hstart >> 8, \ +vstart & 0xff, vstart >> 8, \ +(mode & MODE_SXGA ? 1280 >> 4 : 640 >> 4), \ +(mode & MODE_SXGA ? 1024 >> 3 : 480 >> 3)}) + +#define CLR_WIN(width, height) \ +((const u8 [])\ +{0, width >> 2, 0, height >> 1,\ +((width >> 10) & 0x01) | ((height >> 8) & 0x6)}) + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + int width = gspca_dev->width; + int height = gspca_dev->height; + u8 fmt, scale = 0; + + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (sd->jpeg_hdr == NULL) + return -ENOMEM; + + jpeg_define(sd->jpeg_hdr, height, width, + 0x21); + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + + if (mode & MODE_RAW) + fmt = 0x2d; + else if (mode & MODE_JPEG) + fmt = 0x2c; + else + fmt = 0x2f; + + switch (mode & 0x0f) { + case 3: + scale = 0xc0; + info("Set 1280x1024"); + break; + case 2: + scale = 0x80; + info("Set 640x480"); + break; + case 1: + scale = 0x90; + info("Set 320x240"); + break; + case 0: + scale = 0xa0; + info("Set 160x120"); + break; + } + + configure_sensor_output(gspca_dev, mode); + reg_w(gspca_dev, 0x1100, sd->jpeg_hdr + JPEG_QT0_OFFSET, 64); + reg_w(gspca_dev, 0x1140, sd->jpeg_hdr + JPEG_QT1_OFFSET, 64); + reg_w(gspca_dev, 0x10fb, CLR_WIN(width, height), 5); + reg_w(gspca_dev, 0x1180, HW_WIN(mode, sd->hstart, sd->vstart), 6); + reg_w1(gspca_dev, 0x1189, scale); + reg_w1(gspca_dev, 0x10e0, fmt); + + set_cmatrix(gspca_dev); + set_gamma(gspca_dev); + set_redblue(gspca_dev); + set_gain(gspca_dev); + set_exposure(gspca_dev); + set_hvflip(gspca_dev); + + reg_r(gspca_dev, 0x1061, 1); + reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] | 0x02); + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_r(gspca_dev, 0x1061, 1); + reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + kfree(sd->jpeg_hdr); +} + +static void do_autoexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum, new_exp; + + if (!sd->auto_exposure) + return; + + avg_lum = atomic_read(&sd->avg_lum); + + /* + * some hardcoded values are present + * like those for maximal/minimal exposure + * and exposure steps + */ + if (avg_lum < MIN_AVG_LUM) { + if (sd->exposure > 0x1770) + return; + + new_exp = sd->exposure + sd->exposure_step; + if (new_exp > 0x1770) + new_exp = 0x1770; + if (new_exp < 0x10) + new_exp = 0x10; + sd->exposure = new_exp; + set_exposure(gspca_dev); + + sd->older_step = sd->old_step; + sd->old_step = 1; + + if (sd->old_step ^ sd->older_step) + sd->exposure_step /= 2; + else + sd->exposure_step += 2; + } + if (avg_lum > MAX_AVG_LUM) { + if (sd->exposure < 0x10) + return; + new_exp = sd->exposure - sd->exposure_step; + if (new_exp > 0x1700) + new_exp = 0x1770; + if (new_exp < 0x10) + new_exp = 0x10; + sd->exposure = new_exp; + set_exposure(gspca_dev); + sd->older_step = sd->old_step; + sd->old_step = 0; + + if (sd->old_step ^ sd->older_step) + sd->exposure_step /= 2; + else + sd->exposure_step += 2; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum; + static unsigned char frame_header[] = + {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; + if (len == 64 && memcmp(data, frame_header, 6) == 0) { + avg_lum = ((data[35] >> 2) & 3) | + (data[20] << 2) | + (data[19] << 10); + avg_lum += ((data[35] >> 4) & 3) | + (data[22] << 2) | + (data[21] << 10); + avg_lum += ((data[35] >> 6) & 3) | + (data[24] << 2) | + (data[23] << 10); + avg_lum += (data[36] & 3) | + (data[26] << 2) | + (data[25] << 10); + avg_lum += ((data[36] >> 2) & 3) | + (data[28] << 2) | + (data[27] << 10); + avg_lum += ((data[36] >> 4) & 3) | + (data[30] << 2) | + (data[29] << 10); + avg_lum += ((data[36] >> 6) & 3) | + (data[32] << 2) | + (data[31] << 10); + avg_lum += ((data[44] >> 4) & 3) | + (data[34] << 2) | + (data[33] << 10); + avg_lum >>= 9; + atomic_set(&sd->avg_lum, avg_lum); + gspca_frame_add(gspca_dev, LAST_PACKET, + frame, data, len); + return; + } + if (gspca_dev->last_packet_type == LAST_PACKET) { + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv + & MODE_JPEG) { + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, len); + } else { + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + } + } else { + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + } +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = do_autoexposure, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .set_register = sd_dbg_s_register, + .get_register = sd_dbg_g_register, +#endif + .get_chip_ident = sd_chip_ident, +}; + +#define SN9C20X(sensor, i2c_addr, button_mask) \ + .driver_info = (button_mask << 16) \ + | (SENSOR_ ## sensor << 8) \ + | (i2c_addr) + +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0c45, 0x6240), SN9C20X(MT9M001, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6242), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6248), SN9C20X(OV9655, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30, 0x10)}, + {USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6251), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6253), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6260), SN9C20X(OV7670, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x6270), SN9C20X(MT9VPRB, 0x00, 0)}, + {USB_DEVICE(0x0c45, 0x627b), SN9C20X(OV7660, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x627c), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0x0c45, 0x627f), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x6280), SN9C20X(MT9M001, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6282), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x6288), SN9C20X(OV9655, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x628e), SN9C20X(SOI968, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)}, + {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, 0)}, + {USB_DEVICE(0x0c45, 0x62bc), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x145f, 0x013d), SN9C20X(OV7660, 0x21, 0)}, + {USB_DEVICE(0x0458, 0x7029), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0610), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0611), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0613), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0618), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0xa168, 0x0614), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0xa168, 0x0615), SN9C20X(MT9M111, 0x5d, 0)}, + {USB_DEVICE(0xa168, 0x0617), SN9C20X(MT9M111, 0x5d, 0)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static void sd_disconnect(struct usb_interface *intf) +{ +#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + + sn9c20x_input_cleanup(gspca_dev); +#endif + + gspca_disconnect(intf); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = sd_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + info("registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + info("deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/linux/include/linux/videodev2.h b/linux/include/linux/videodev2.h index b5c4f7295..50dd2a51b 100644 --- a/linux/include/linux/videodev2.h +++ b/linux/include/linux/videodev2.h @@ -344,6 +344,7 @@ struct v4l2_pix_format { /* Vendor-specific formats */ #define V4L2_PIX_FMT_WNVA v4l2_fourcc('W', 'N', 'V', 'A') /* Winnov hw compress */ #define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S', '9', '1', '0') /* SN9C10x compression */ +#define V4L2_PIX_FMT_SN9C20X_I420 v4l2_fourcc('S', '9', '2', '0') /* SN9C20x YUV 4:2:0 */ #define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P', 'W', 'C', '1') /* pwc older webcam */ #define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P', 'W', 'C', '2') /* pwc newer webcam */ #define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E', '6', '2', '5') /* ET61X251 compression */ diff --git a/linux/include/media/v4l2-chip-ident.h b/linux/include/media/v4l2-chip-ident.h index 11a4a2d3e..94e908c0d 100644 --- a/linux/include/media/v4l2-chip-ident.h +++ b/linux/include/media/v4l2-chip-ident.h @@ -60,6 +60,10 @@ enum { V4L2_IDENT_OV7670 = 250, V4L2_IDENT_OV7720 = 251, V4L2_IDENT_OV7725 = 252, + V4L2_IDENT_OV7660 = 253, + V4L2_IDENT_OV9650 = 254, + V4L2_IDENT_OV9655 = 255, + V4L2_IDENT_SOI968 = 256, /* module saa7146: reserved range 300-309 */ V4L2_IDENT_SAA7146 = 300, @@ -161,6 +165,9 @@ enum { /* module tw9910: just ident 9910 */ V4L2_IDENT_TW9910 = 9910, + /* module sn9c20x: just ident 10000 */ + V4L2_IDENT_SN9C20X = 10000, + /* module msp3400: reserved range 34000-34999 and 44000-44999 */ V4L2_IDENT_MSPX4XX = 34000, /* generic MSPX4XX identifier, only use internally (tveeprom.c). */ @@ -237,6 +244,11 @@ enum { V4L2_IDENT_MT9V022IX7ATC = 45010, /* No way to detect "normal" I77ATx */ V4L2_IDENT_MT9V022IX7ATM = 45015, /* and "lead free" IA7ATx chips */ V4L2_IDENT_MT9T031 = 45020, + V4L2_IDENT_MT9V111 = 45031, + V4L2_IDENT_MT9V112 = 45032, + + /* HV7131R CMOS sensor: just ident 46000 */ + V4L2_IDENT_HV7131R = 46000, /* module cs53132a: just ident 53132 */ V4L2_IDENT_CS53l32A = 53132, -- cgit v1.2.3 From 13f81b30dcdaede4d7b3fc982a6d63c494aff666 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 19 Jul 2009 12:09:32 +0200 Subject: gspca - jpeg subdrivers: Check the result of kmalloc(jpeg header). From: Julia Lawall Priority: high Signed-off-by: Julia Lawall Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/conex.c | 2 ++ linux/drivers/media/video/gspca/mars.c | 2 ++ linux/drivers/media/video/gspca/sonixj.c | 2 ++ linux/drivers/media/video/gspca/spca500.c | 2 ++ linux/drivers/media/video/gspca/stk014.c | 2 ++ linux/drivers/media/video/gspca/sunplus.c | 2 ++ linux/drivers/media/video/gspca/zc3xx.c | 2 ++ 7 files changed, 14 insertions(+) diff --git a/linux/drivers/media/video/gspca/conex.c b/linux/drivers/media/video/gspca/conex.c index 219cfa6fb..8d48ea174 100644 --- a/linux/drivers/media/video/gspca/conex.c +++ b/linux/drivers/media/video/gspca/conex.c @@ -846,6 +846,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/linux/drivers/media/video/gspca/mars.c b/linux/drivers/media/video/gspca/mars.c index 789fd178a..68dd1ea0f 100644 --- a/linux/drivers/media/video/gspca/mars.c +++ b/linux/drivers/media/video/gspca/mars.c @@ -201,6 +201,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 3d2722704..5568401e8 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -1780,6 +1780,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/linux/drivers/media/video/gspca/spca500.c b/linux/drivers/media/video/gspca/spca500.c index 9717da6af..1fbad8d8f 100644 --- a/linux/drivers/media/video/gspca/spca500.c +++ b/linux/drivers/media/video/gspca/spca500.c @@ -687,6 +687,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/linux/drivers/media/video/gspca/stk014.c b/linux/drivers/media/video/gspca/stk014.c index f25be20cf..476289648 100644 --- a/linux/drivers/media/video/gspca/stk014.c +++ b/linux/drivers/media/video/gspca/stk014.c @@ -333,6 +333,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/linux/drivers/media/video/gspca/sunplus.c b/linux/drivers/media/video/gspca/sunplus.c index f23594e0b..270b86f24 100644 --- a/linux/drivers/media/video/gspca/sunplus.c +++ b/linux/drivers/media/video/gspca/sunplus.c @@ -1008,6 +1008,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c index 2855ebf90..2a9480138 100644 --- a/linux/drivers/media/video/gspca/zc3xx.c +++ b/linux/drivers/media/video/gspca/zc3xx.c @@ -7272,6 +7272,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); -- cgit v1.2.3 From b510105ff1dd5026a33548ef518f1dc40f97d313 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 19 Jul 2009 09:10:06 -0300 Subject: em28xx-video: rename ac97 audio controls to better document it From: Mauro Carvalho Chehab As em28xx chip has nothing to do with volume/mute controls, rename those controls to properly indicate that they control the companion AC97 chip that it is inside the boards with this chip. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index cef676bf8..635f5b6d2 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -142,7 +142,7 @@ static struct em28xx_fmt format[] = { /* supported controls */ /* Common to all boards */ -static struct v4l2_queryctrl em28xx_qctrl[] = { +static struct v4l2_queryctrl ac97_qctrl[] = { { .id = V4L2_CID_AUDIO_VOLUME, .type = V4L2_CTRL_TYPE_INTEGER, @@ -1027,9 +1027,9 @@ static int vidioc_queryctrl(struct file *file, void *priv, qc->id = id; if (!dev->board.has_msp34xx) { - for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { - if (qc->id && qc->id == em28xx_qctrl[i].id) { - memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc)); + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { + if (qc->id && qc->id == ac97_qctrl[i].id) { + memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); return 0; } } @@ -1091,10 +1091,10 @@ static int vidioc_s_ctrl(struct file *file, void *priv, v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); else { rc = 1; - for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { - if (ctrl->id == em28xx_qctrl[i].id) { - if (ctrl->value < em28xx_qctrl[i].minimum || - ctrl->value > em28xx_qctrl[i].maximum) { + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { + if (ctrl->id == ac97_qctrl[i].id) { + if (ctrl->value < ac97_qctrl[i].minimum || + ctrl->value > ac97_qctrl[i].maximum) { rc = -ERANGE; break; } @@ -1668,9 +1668,9 @@ static int radio_queryctrl(struct file *file, void *priv, qc->id >= V4L2_CID_LASTP1) return -EINVAL; - for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { - if (qc->id && qc->id == em28xx_qctrl[i].id) { - memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc)); + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { + if (qc->id && qc->id == ac97_qctrl[i].id) { + memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); return 0; } } -- cgit v1.2.3 From 2344fe31e157aac9a1b78033e60f064e6131de04 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 19 Jul 2009 10:45:49 -0300 Subject: em28xx-video: better implement ac97 control ioctls From: Mauro Carvalho Chehab In the past, some devices with saa711x had their parameters controlled directly inside em28xx driver, instead of using their proper module for it. Due to that, the ac97 controls were mixed with saa711x ones. Older patches removed all saa711x controls, but we still need to control ac97 devices on em28xx, since we don't have a separate v4l2 device for it. The proper way to address is to create a separate ac97 v4l2 device. While we don't have it, we should clean up the code to allow having a better view of what is part of em28xx core code and what's due to ac97 control inside it. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 117 ++++++++++++++---------- 1 file changed, 68 insertions(+), 49 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 635f5b6d2..1237e325d 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -151,7 +151,7 @@ static struct v4l2_queryctrl ac97_qctrl[] = { .maximum = 0x1f, .step = 0x1, .default_value = 0x1f, - .flags = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_AUDIO_MUTE, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -622,10 +622,29 @@ static void res_free(struct em28xx_fh *fh) } /* - * em28xx_get_ctrl() - * return the current saturation, brightness or contrast, mute state + * ac97_queryctrl() + * return the ac97 supported controls + */ +static int ac97_queryctrl(struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { + if (qc->id && qc->id == ac97_qctrl[i].id) { + memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); + return 0; + } + } + + /* Control is not ac97 related */ + return 1; +} + +/* + * ac97_get_ctrl() + * return the current values for ac97 mute and volume */ -static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) +static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) { switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -635,29 +654,41 @@ static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) ctrl->value = dev->volume; return 0; default: - return -EINVAL; + /* Control is not ac97 related */ + return 1; } } /* - * em28xx_set_ctrl() - * mute or set new saturation, brightness or contrast + * ac97_set_ctrl() + * set values for ac97 mute and volume */ -static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) +static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) { + int i; + + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) + if (ctrl->id == ac97_qctrl[i].id) + goto handle; + + /* Announce that hasn't handle it */ + return 1; + +handle: + if (ctrl->value < ac97_qctrl[i].minimum || + ctrl->value > ac97_qctrl[i].maximum) + return -ERANGE; + switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - if (ctrl->value != dev->mute) { - dev->mute = ctrl->value; - return em28xx_audio_analog_set(dev); - } - return 0; + dev->mute = ctrl->value; + break; case V4L2_CID_AUDIO_VOLUME: dev->volume = ctrl->value; - return em28xx_audio_analog_set(dev); - default: - return -EINVAL; + break; } + + return em28xx_audio_analog_set(dev); } static int check_dev(struct em28xx *dev) @@ -1015,7 +1046,6 @@ static int vidioc_queryctrl(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; int id = qc->id; - int i; int rc; rc = check_dev(dev); @@ -1026,15 +1056,14 @@ static int vidioc_queryctrl(struct file *file, void *priv, qc->id = id; - if (!dev->board.has_msp34xx) { - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { - if (qc->id && qc->id == ac97_qctrl[i].id) { - memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); - return 0; - } - } + /* enumberate AC97 controls */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + rc = ac97_queryctrl(qc); + if (!rc) + return 0; } + /* enumberate V4L2 device controls */ mutex_lock(&dev->lock); v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); mutex_unlock(&dev->lock); @@ -1059,14 +1088,16 @@ static int vidioc_g_ctrl(struct file *file, void *priv, mutex_lock(&dev->lock); - if (dev->board.has_msp34xx) + /* Set an AC97 control */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) + rc = ac97_get_ctrl(dev, ctrl); + else + rc = 1; + + /* It were not an AC97 control. Sends it to the v4l2 dev interface */ + if (rc == 1) { v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); - else { - rc = em28xx_get_ctrl(dev, ctrl); - if (rc < 0) { - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); - rc = 0; - } + rc = 0; } mutex_unlock(&dev->lock); @@ -1078,7 +1109,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - u8 i; int rc; rc = check_dev(dev); @@ -1087,27 +1117,16 @@ static int vidioc_s_ctrl(struct file *file, void *priv, mutex_lock(&dev->lock); - if (dev->board.has_msp34xx) - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); - else { + /* Set an AC97 control */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) + rc = ac97_set_ctrl(dev, ctrl); + else rc = 1; - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { - if (ctrl->id == ac97_qctrl[i].id) { - if (ctrl->value < ac97_qctrl[i].minimum || - ctrl->value > ac97_qctrl[i].maximum) { - rc = -ERANGE; - break; - } - - rc = em28xx_set_ctrl(dev, ctrl); - break; - } - } - } - /* Control not found - try to send it to the attached devices */ + /* It were not an AC97 control. Sends it to the v4l2 dev interface */ if (rc == 1) { v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); + /* FIXME: should be returning a meaninful value */ rc = 0; } -- cgit v1.2.3 From 680b58a1db752ccb021764aae84f0a40b1825e7e Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Mon, 20 Jul 2009 00:37:09 +0400 Subject: Add output clock configuration for stv6110 tuner. From: Igor M. Liplianin Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/frontends/stv6110.c | 10 ++++++++++ linux/drivers/media/dvb/frontends/stv6110.h | 1 + 2 files changed, 11 insertions(+) diff --git a/linux/drivers/media/dvb/frontends/stv6110.c b/linux/drivers/media/dvb/frontends/stv6110.c index 70efac869..5aabb782e 100644 --- a/linux/drivers/media/dvb/frontends/stv6110.c +++ b/linux/drivers/media/dvb/frontends/stv6110.c @@ -36,6 +36,7 @@ struct stv6110_priv { struct i2c_adapter *i2c; u32 mclk; + u8 clk_div; u8 regs[8]; }; @@ -221,6 +222,10 @@ static int stv6110_init(struct dvb_frontend *fe) priv->regs[RSTV6110_CTRL1] |= ((((priv->mclk / 1000000) - 16) & 0x1f) << 3); + /* divisor value for the output clock */ + priv->regs[RSTV6110_CTRL2] &= ~0xc0; + priv->regs[RSTV6110_CTRL2] |= (priv->clk_div << 6); + stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL1], RSTV6110_CTRL1, 8); msleep(1); stv6110_set_bandwidth(fe, 72000000); @@ -418,6 +423,10 @@ struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, }; int ret; + /* divisor value for the output clock */ + reg0[2] &= ~0xc0; + reg0[2] |= (config->clk_div << 6); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -436,6 +445,7 @@ struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, priv->i2c_address = config->i2c_address; priv->i2c = i2c; priv->mclk = config->mclk; + priv->clk_div = config->clk_div; memcpy(&priv->regs, ®0[1], 8); diff --git a/linux/drivers/media/dvb/frontends/stv6110.h b/linux/drivers/media/dvb/frontends/stv6110.h index 1c0314d6a..2574e1fba 100644 --- a/linux/drivers/media/dvb/frontends/stv6110.h +++ b/linux/drivers/media/dvb/frontends/stv6110.h @@ -42,6 +42,7 @@ struct stv6110_config { u8 i2c_address; u32 mclk; int iq_wiring; + u8 clk_div; /* divisor value for the output clock */ }; #if defined(CONFIG_DVB_STV6110) || (defined(CONFIG_DVB_STV6110_MODULE) \ -- cgit v1.2.3 From f3641acdca07f8402060ee7ff77cb0bc72634413 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Mon, 20 Jul 2009 00:57:29 +0400 Subject: stv6110 tuner: remove unused iq_wiring configuration parameter. From: Igor M. Liplianin Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/frontends/stv6110.h | 1 - linux/drivers/media/video/cx23885/cx23885-dvb.c | 2 -- 2 files changed, 3 deletions(-) diff --git a/linux/drivers/media/dvb/frontends/stv6110.h b/linux/drivers/media/dvb/frontends/stv6110.h index 2574e1fba..9db240241 100644 --- a/linux/drivers/media/dvb/frontends/stv6110.h +++ b/linux/drivers/media/dvb/frontends/stv6110.h @@ -41,7 +41,6 @@ struct stv6110_config { u8 i2c_address; u32 mclk; - int iq_wiring; u8 clk_div; /* divisor value for the output clock */ }; diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 85fac41e7..805272239 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -410,13 +410,11 @@ static struct stv0900_config netup_stv0900_config = { static struct stv6110_config netup_stv6110_tunerconfig_a = { .i2c_address = 0x60, .mclk = 27000000, - .iq_wiring = 0, }; static struct stv6110_config netup_stv6110_tunerconfig_b = { .i2c_address = 0x63, .mclk = 27000000, - .iq_wiring = 1, }; static int tbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) -- cgit v1.2.3 From 20d8713a89d5f49511495e944294806642ec6a13 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 19 Jul 2009 18:03:23 -0300 Subject: sn9c20x: backport to work since 2.6.18 From: Mauro Carvalho Chehab Make sn9c20x driver to work with RHEL5 kernel. Yet, the event interface needs a little more work. Since I'm lazy, I've backported just the webcam driver. While here, reorder includes to the order used on other drivers. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/gspca/sn9c20x.c | 14 ++++++++++---- v4l/versions.txt | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/linux/drivers/media/video/gspca/sn9c20x.c b/linux/drivers/media/video/gspca/sn9c20x.c index 78ab26ceb..a516866a2 100644 --- a/linux/drivers/media/video/gspca/sn9c20x.c +++ b/linux/drivers/media/video/gspca/sn9c20x.c @@ -18,17 +18,21 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "gspca.h" -#include "jpeg.h" - -#include #ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) #include +#endif #include #include #endif +#include "gspca.h" +#include "jpeg.h" +#include "compat.h" + +#include + MODULE_AUTHOR("Brian Johnson , " "microdia project "); MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver"); @@ -2409,8 +2413,10 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) .reset_resume = gspca_resume, #endif +#endif }; /* -- module insert / remove -- */ diff --git a/v4l/versions.txt b/v4l/versions.txt index eb3597f76..792419f99 100644 --- a/v4l/versions.txt +++ b/v4l/versions.txt @@ -29,6 +29,8 @@ SOC_CAMERA_MT9M001 [2.6.23] # Needs field intf_assoc in struct usb_host_config VIDEO_CX231XX +# Some freezer routines +USB_GSPCA_SN9C20X_EVDEV [2.6.22] #This driver requires I2C probe/remove fields -- cgit v1.2.3 From 20de14406ca4448efc8d965f5bfe0ab0def25b3e Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Mon, 20 Jul 2009 01:06:00 +0400 Subject: Change clocking configuration and frequency for NetUP card. From: Abylay Ospan Signed-off-by: Abylay Ospan --- linux/drivers/media/video/cx23885/cx23885-dvb.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 805272239..7f8692f97 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -397,7 +397,7 @@ static struct stv0900_reg stv0900_ts_regs[] = { static struct stv0900_config netup_stv0900_config = { .demod_address = 0x68, - .xtal = 27000000, + .xtal = 8000000, .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ .diseqc_mode = 2,/* 2/3 PWM */ .ts_config_regs = stv0900_ts_regs, @@ -409,12 +409,14 @@ static struct stv0900_config netup_stv0900_config = { static struct stv6110_config netup_stv6110_tunerconfig_a = { .i2c_address = 0x60, - .mclk = 27000000, + .mclk = 16000000, + .clk_div = 1, }; static struct stv6110_config netup_stv6110_tunerconfig_b = { .i2c_address = 0x63, - .mclk = 27000000, + .mclk = 16000000, + .clk_div = 1, }; static int tbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) -- cgit v1.2.3 From ab5fd1dec1bf106e22d4bc18f3119830270f20aa Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Mon, 20 Jul 2009 01:15:45 +0400 Subject: Bug fix: stv0900 i2c repeater configuration must be set to manual. From: Abylay Ospan In automatic mode every stop event on SDA line ends repetition. However, in NetUP Dual card on the same i2c bus we have several devices. If someone using both adapters to lock simultaneously or working with CAM interface during lock procedure, it lead to end repetition prematurely quite often. Set stv0900 i2c repeater to manual mode prevents such situation. Signed-off-by: Abylay Ospan --- linux/drivers/media/dvb/frontends/stv0900_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/linux/drivers/media/dvb/frontends/stv0900_core.c b/linux/drivers/media/dvb/frontends/stv0900_core.c index 84bf35edb..5dbcf215f 100644 --- a/linux/drivers/media/dvb/frontends/stv0900_core.c +++ b/linux/drivers/media/dvb/frontends/stv0900_core.c @@ -234,8 +234,8 @@ enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *i_params) #endif stv0900_write_reg(i_params, R0900_P1_TNRCFG, 0x6c); stv0900_write_reg(i_params, R0900_P2_TNRCFG, 0x6f); - stv0900_write_reg(i_params, R0900_P1_I2CRPT, 0x24); - stv0900_write_reg(i_params, R0900_P2_I2CRPT, 0x24); + stv0900_write_reg(i_params, R0900_P1_I2CRPT, 0x20); + stv0900_write_reg(i_params, R0900_P2_I2CRPT, 0x20); stv0900_write_reg(i_params, R0900_NCOARSE, 0x13); msleep(3); stv0900_write_reg(i_params, R0900_I2CCFG, 0x08); @@ -374,8 +374,8 @@ static int stv0900_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) u32 fi2c; dmd_reg(fi2c, F0900_P1_I2CT_ON, F0900_P2_I2CT_ON); - if (enable) - stv0900_write_bits(i_params, fi2c, 1); + + stv0900_write_bits(i_params, fi2c, enable); return 0; } -- cgit v1.2.3 From 1f04aaf2cbc9e510002eaabb94b8a85d21b12640 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Mon, 20 Jul 2009 01:21:38 +0400 Subject: stv6110 tuner: Read registers through one time i2c_transfer calling. From: Igor M. Liplianin Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/frontends/stv6110.c | 38 +++++++++++------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/linux/drivers/media/dvb/frontends/stv6110.c b/linux/drivers/media/dvb/frontends/stv6110.c index 5aabb782e..dcf1b21ea 100644 --- a/linux/drivers/media/dvb/frontends/stv6110.c +++ b/linux/drivers/media/dvb/frontends/stv6110.c @@ -101,35 +101,25 @@ static int stv6110_read_regs(struct dvb_frontend *fe, u8 regs[], struct stv6110_priv *priv = fe->tuner_priv; int rc; u8 reg[] = { start }; - struct i2c_msg msg_wr = { - .addr = priv->i2c_address, - .flags = 0, - .buf = reg, - .len = 1, + struct i2c_msg msg[] = { + { + .addr = priv->i2c_address, + .flags = 0, + .buf = reg, + .len = 1, + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .buf = regs, + .len = len, + }, }; - struct i2c_msg msg_rd = { - .addr = priv->i2c_address, - .flags = I2C_M_RD, - .buf = regs, - .len = len, - }; - /* write subaddr */ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - rc = i2c_transfer(priv->i2c, &msg_wr, 1); - if (rc != 1) - dprintk("%s: i2c error\n", __func__); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - /* read registers */ - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - rc = i2c_transfer(priv->i2c, &msg_rd, 1); - if (rc != 1) + rc = i2c_transfer(priv->i2c, msg, 2); + if (rc != 2) dprintk("%s: i2c error\n", __func__); if (fe->ops.i2c_gate_ctrl) -- cgit v1.2.3 From 391fa9b1286e89a5b82f26c2efc3df18d72428e5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 19 Jul 2009 23:39:56 +0200 Subject: uvcvideo: Add PROBE_DEF quirk and enable it for the MT6227 device From: Laurent Pinchart At least one MT6227 model crashes when receiving a GET_DEF request on the video probe control. As the various models can't be told apart based on the descriptors, add a PROBE_DEF quirk to avoid sending the GET_DEF request and enable the quirk for all models. Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_driver.c | 3 ++- linux/drivers/media/video/uvc/uvc_video.c | 3 +++ linux/drivers/media/video/uvc/uvcvideo.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index f7b76884d..1c09cd809 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -1868,7 +1868,8 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_PROBE_DEF }, /* Syntek (HP Spartan) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c index c833e984e..f4f4e1bb3 100644 --- a/linux/drivers/media/video/uvc/uvc_video.c +++ b/linux/drivers/media/video/uvc/uvc_video.c @@ -128,6 +128,9 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, if (data == NULL) return -ENOMEM; + if ((video->dev->quirks & UVC_QUIRK_PROBE_DEF) && query == UVC_GET_DEF) + return -EIO; + ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum, probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, size, UVC_CTRL_STREAMING_TIMEOUT); diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h index 4887650c9..e700bdc20 100644 --- a/linux/drivers/media/video/uvc/uvcvideo.h +++ b/linux/drivers/media/video/uvc/uvcvideo.h @@ -157,6 +157,7 @@ struct uvc_xu_control { #define UVC_QUIRK_STREAM_NO_FID 0x00000010 #define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 #define UVC_QUIRK_FIX_BANDWIDTH 0x00000080 +#define UVC_QUIRK_PROBE_DEF 0x00000100 /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 -- cgit v1.2.3 From f32c3e119058285fb6ceadc44d235717e45c7d5c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 20 Jul 2009 00:16:05 +0200 Subject: uvcvideo: Don't apply the FIX_BANDWIDTH quirk to all ViMicro devices From: Laurent Pinchart Commit 50144aeeb702ea105697ae5249f059ea3990b838 broke the Samsung NC10 netbook webcam. Instead of applying the FIX_BANDWIDTH quirk to all ViMicro devices, list the devices explicitly. Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_driver.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index 1c09cd809..1d09751a3 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -1851,11 +1851,29 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* ViMicro */ + /* ViMicro Vega */ { .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x0ac8, - .idProduct = 0x0000, + .idProduct = 0x332d, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, + /* ViMicro - Minoru3D */ + { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0ac8, + .idProduct = 0x3410, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, + /* ViMicro Venus - Minoru3D */ + { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0ac8, + .idProduct = 0x3420, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, -- cgit v1.2.3 From 41e2b46981cc531fe61b04a83a5e2bc4171c9257 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2009 13:14:17 +0200 Subject: bttv: fix regression: tvaudio must be loaded before tuner From: Hans Verkuil Both tvaudio and the tuner share i2c address 0x42. The tvaudio module can check whether it really is a tda9840, but the tuner can't. So the tvaudio module must be loaded before the tuner module. This was also the case for 2.6.29, but the order was swapped in 2.6.30. Thanks to Krzysztof Grygiencz for reporting and testing this. Priority: high Signed-off-by: Hans Verkuil --- linux/drivers/media/video/bt8xx/bttv-cards.c | 92 ++++++++++++++------------- linux/drivers/media/video/bt8xx/bttv-driver.c | 1 + linux/drivers/media/video/bt8xx/bttv.h | 1 + 3 files changed, 50 insertions(+), 44 deletions(-) diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c index 38b238f0f..fdca5f67c 100644 --- a/linux/drivers/media/video/bt8xx/bttv-cards.c +++ b/linux/drivers/media/video/bt8xx/bttv-cards.c @@ -3362,8 +3362,6 @@ void __devinit bttv_init_card1(struct bttv *btv) /* initialization part two -- after registering i2c bus */ void __devinit bttv_init_card2(struct bttv *btv) { - int addr=ADDR_UNSET; - btv->tuner_type = UNSET; if (BTTV_BOARD_UNKNOWN == btv->c.type) { @@ -3508,9 +3506,6 @@ void __devinit bttv_init_card2(struct bttv *btv) btv->pll.pll_current = -1; /* tuner configuration (from card list / autodetect / insmod option) */ - if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr) - addr = bttv_tvcards[btv->c.type].tuner_addr; - if (UNSET != bttv_tvcards[btv->c.type].tuner_type) if (UNSET == btv->tuner_type) btv->tuner_type = bttv_tvcards[btv->c.type].tuner_type; @@ -3534,40 +3529,6 @@ void __devinit bttv_init_card2(struct bttv *btv) if (UNSET == btv->tuner_type) btv->tuner_type = TUNER_ABSENT; - if (btv->tuner_type != TUNER_ABSENT) { - struct tuner_setup tun_setup; - - /* Load tuner module before issuing tuner config call! */ - if (bttv_tvcards[btv->c.type].has_radio) - v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_RADIO)); - v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); - v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD)); - - tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; - tun_setup.type = btv->tuner_type; - tun_setup.addr = addr; - - if (bttv_tvcards[btv->c.type].has_radio) - tun_setup.mode_mask |= T_RADIO; - - bttv_call_all(btv, tuner, s_type_addr, &tun_setup); - } - - if (btv->tda9887_conf) { - struct v4l2_priv_tun_config tda9887_cfg; - - tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &btv->tda9887_conf; - - bttv_call_all(btv, tuner, s_config, &tda9887_cfg); - } - btv->dig = bttv_tvcards[btv->c.type].has_dig_in ? bttv_tvcards[btv->c.type].video_inputs - 1 : UNSET; btv->svhs = bttv_tvcards[btv->c.type].svhs == NO_SVHS ? @@ -3578,15 +3539,15 @@ void __devinit bttv_init_card2(struct bttv *btv) btv->has_remote = remote[btv->c.nr]; if (bttv_tvcards[btv->c.type].has_radio) - btv->has_radio=1; + btv->has_radio = 1; if (bttv_tvcards[btv->c.type].has_remote) - btv->has_remote=1; + btv->has_remote = 1; if (!bttv_tvcards[btv->c.type].no_gpioirq) - btv->gpioirq=1; + btv->gpioirq = 1; if (bttv_tvcards[btv->c.type].volume_gpio) - btv->volume_gpio=bttv_tvcards[btv->c.type].volume_gpio; + btv->volume_gpio = bttv_tvcards[btv->c.type].volume_gpio; if (bttv_tvcards[btv->c.type].audio_mode_gpio) - btv->audio_mode_gpio=bttv_tvcards[btv->c.type].audio_mode_gpio; + btv->audio_mode_gpio = bttv_tvcards[btv->c.type].audio_mode_gpio; if (btv->tuner_type == TUNER_ABSENT) return; /* no tuner or related drivers to load */ @@ -3704,6 +3665,49 @@ no_audio: } +/* initialize the tuner */ +void __devinit bttv_init_tuner(struct bttv *btv) +{ + int addr = ADDR_UNSET; + + if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr) + addr = bttv_tvcards[btv->c.type].tuner_addr; + + if (btv->tuner_type != TUNER_ABSENT) { + struct tuner_setup tun_setup; + + /* Load tuner module before issuing tuner config call! */ + if (bttv_tvcards[btv->c.type].has_radio) + v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_RADIO)); + v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD)); + + tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; + tun_setup.type = btv->tuner_type; + tun_setup.addr = addr; + + if (bttv_tvcards[btv->c.type].has_radio) + tun_setup.mode_mask |= T_RADIO; + + bttv_call_all(btv, tuner, s_type_addr, &tun_setup); + } + + if (btv->tda9887_conf) { + struct v4l2_priv_tun_config tda9887_cfg; + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &btv->tda9887_conf; + + bttv_call_all(btv, tuner, s_config, &tda9887_cfg); + } +} + /* ----------------------------------------------------------------------- */ static void modtec_eeprom(struct bttv *btv) diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index 9c25efe06..18085ba61 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -4457,6 +4457,7 @@ static int __devinit bttv_probe(struct pci_dev *dev, /* some card-specific stuff (needs working i2c) */ bttv_init_card2(btv); + bttv_init_tuner(btv); init_irqreg(btv); /* register video4linux + input */ diff --git a/linux/drivers/media/video/bt8xx/bttv.h b/linux/drivers/media/video/bt8xx/bttv.h index 6b7b62bd4..a0cb26b4a 100644 --- a/linux/drivers/media/video/bt8xx/bttv.h +++ b/linux/drivers/media/video/bt8xx/bttv.h @@ -284,6 +284,7 @@ extern struct tvcard bttv_tvcards[]; extern void bttv_idcard(struct bttv *btv); extern void bttv_init_card1(struct bttv *btv); extern void bttv_init_card2(struct bttv *btv); +extern void bttv_init_tuner(struct bttv *btv); /* card-specific funtions */ extern void tea5757_set_freq(struct bttv *btv, unsigned short freq); -- cgit v1.2.3 From bcadb4347f42dfeb5b2f3eddc7af77c069b302ba Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2009 13:56:20 +0200 Subject: v4l2-spec: add documentation for missing V4L2_PIX_FMT_SN9C20X_I420 pixfmt. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- v4l2-spec/pixfmt.sgml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/v4l2-spec/pixfmt.sgml b/v4l2-spec/pixfmt.sgml index 1be70eea0..9e6bebeb4 100644 --- a/v4l2-spec/pixfmt.sgml +++ b/v4l2-spec/pixfmt.sgml @@ -765,6 +765,11 @@ kernel sources in the file Documentation/video4linux/cx2341x/README.hm 'S910' Compressed format of the SN9C102 driver. + + V4L2_PIX_FMT_SN9C20X_I420 + 'S920' + YUV 4:2:0 format of the gspca sn9c20x driver. + V4L2_PIX_FMT_WNVA 'WNVA' -- cgit v1.2.3 From db54df2055130294d02d4b75e3e9b0a391bb7806 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2009 14:17:55 +0200 Subject: v4l2-spec: update for new modulator and rds encoder caps. From: Hans Verkuil Document the radio modulator and RDS encoder API. Priority: normal Signed-off-by: Hans Verkuil --- v4l2-spec/common.sgml | 2 +- v4l2-spec/compat.sgml | 3 +++ v4l2-spec/dev-radio.sgml | 14 ++++++++------ v4l2-spec/dev-rds.sgml | 20 ++++++++++++++------ v4l2-spec/v4l2.sgml | 3 ++- v4l2-spec/vidioc-g-modulator.sgml | 5 +++++ v4l2-spec/vidioc-querycap.sgml | 14 +++++++++++--- 7 files changed, 44 insertions(+), 17 deletions(-) diff --git a/v4l2-spec/common.sgml b/v4l2-spec/common.sgml index ff595960b..75c7141e3 100644 --- a/v4l2-spec/common.sgml +++ b/v4l2-spec/common.sgml @@ -491,7 +491,7 @@ the &VIDIOC-G-MODULATOR; and &VIDIOC-S-MODULATOR; ioctl. Note that VIDIOC_S_MODULATOR does not switch the current modulator, when there is more than one at all. The modulator is solely determined by the current video output. Drivers must support both -ioctls and set the V4L2_CAP_TUNER (sic) flag in +ioctls and set the V4L2_CAP_MODULATOR flag in the &v4l2-capability; returned by the &VIDIOC-QUERYCAP; ioctl when the device has one or more modulators. diff --git a/v4l2-spec/compat.sgml b/v4l2-spec/compat.sgml index c1c725f35..bcc372c18 100644 --- a/v4l2-spec/compat.sgml +++ b/v4l2-spec/compat.sgml @@ -2300,6 +2300,9 @@ was renamed to v4l2_chip_ident_oldFinalized the RDS capture API. See for more information. + + Added new capabilities for modulators and RDS encoders. + diff --git a/v4l2-spec/dev-radio.sgml b/v4l2-spec/dev-radio.sgml index ae8367219..4d521d2a8 100644 --- a/v4l2-spec/dev-radio.sgml +++ b/v4l2-spec/dev-radio.sgml @@ -1,7 +1,7 @@ Radio Interface This interface is intended for AM and FM (analog) radio -receivers. +receivers and transmitters. Conventionally V4L2 radio devices are accessed through character device special files named /dev/radio @@ -14,7 +14,8 @@ numbers 64 to 127. Devices supporting the radio interface set the V4L2_CAP_RADIO and -V4L2_CAP_TUNER flag in the +V4L2_CAP_TUNER or +V4L2_CAP_MODULATOR flag in the capabilities field of &v4l2-capability; returned by the &VIDIOC-QUERYCAP; ioctl. Other combinations of capability flags are reserved for future extensions. @@ -25,7 +26,7 @@ capability flags are reserved for future extensions. Radio devices can support controls, and must support the tuner ioctls. +linkend="tuner">tuner or modulator ioctls. They do not support the video input or output, audio input or output, video standard, cropping and scaling, compression and @@ -38,11 +39,12 @@ methods are reserved for future extensions. Radio devices may have a couple audio controls (as discussed in ) such as a volume control, possibly custom -controls. Further all radio devices have one tuner (these are +controls. Further all radio devices have one tuner or modulator (these are discussed in ) with index number zero to select the radio frequency and to determine if a monaural or FM stereo -program is received. Drivers switch automatically between AM and FM -depending on the selected frequency. The &VIDIOC-G-TUNER; ioctl +program is received/emitted. Drivers switch automatically between AM and FM +depending on the selected frequency. The &VIDIOC-G-TUNER; or +&VIDIOC-G-MODULATOR; ioctl reports the supported frequency range. diff --git a/v4l2-spec/dev-rds.sgml b/v4l2-spec/dev-rds.sgml index 8f357d9f0..9d59d0cb0 100644 --- a/v4l2-spec/dev-rds.sgml +++ b/v4l2-spec/dev-rds.sgml @@ -24,16 +24,24 @@ be needed, then please contact the linux-media mailing list: &v4l-ml;. Devices supporting the RDS capturing API set the V4L2_CAP_RDS_CAPTURE flag in the capabilities field of &v4l2-capability; -returned by the &VIDIOC-QUERYCAP; ioctl. - - Any tuner that supports RDS will set the +returned by the &VIDIOC-QUERYCAP; ioctl. +Any tuner that supports RDS will set the V4L2_TUNER_CAP_RDS flag in the capability -field of &v4l2-tuner;. - - Whether an RDS signal is present can be detected by looking at +field of &v4l2-tuner;. +Whether an RDS signal is present can be detected by looking at the rxsubchans field of &v4l2-tuner;: the V4L2_TUNER_SUB_RDS will be set if RDS data was detected. + Devices supporting the RDS output API +set the V4L2_CAP_RDS_OUTPUT flag in +the capabilities field of &v4l2-capability; +returned by the &VIDIOC-QUERYCAP; ioctl. +Any modulator that supports RDS will set the +V4L2_TUNER_CAP_RDS flag in the capability +field of &v4l2-modulator;. +In order to enable the RDS transmission one must set the V4L2_TUNER_SUB_RDS +bit in the txsubchans field of &v4l2-modulator;. +
diff --git a/v4l2-spec/v4l2.sgml b/v4l2-spec/v4l2.sgml index 815e96ab5..bae7eb441 100644 --- a/v4l2-spec/v4l2.sgml +++ b/v4l2-spec/v4l2.sgml @@ -134,7 +134,8 @@ applications. --> 0.26 2009-06-15 hv - Finalized the RDS capture API. + Finalized the RDS capture API. Added modulator and RDS encoder +capabilities. diff --git a/v4l2-spec/vidioc-g-modulator.sgml b/v4l2-spec/vidioc-g-modulator.sgml index a3ea5ef8a..945147f90 100644 --- a/v4l2-spec/vidioc-g-modulator.sgml +++ b/v4l2-spec/vidioc-g-modulator.sgml @@ -212,6 +212,11 @@ does not permit SAP the VIDIOC_S_MODULATOR ioctl shall return an &EINVAL; and driver shall fall back to mono or stereo mode. + + V4L2_TUNER_SUB_RDS + 0x0010 + Enable the RDS encoder for a radio FM transmitter. + diff --git a/v4l2-spec/vidioc-querycap.sgml b/v4l2-spec/vidioc-querycap.sgml index 4a741f5e9..fa12ae738 100644 --- a/v4l2-spec/vidioc-querycap.sgml +++ b/v4l2-spec/vidioc-querycap.sgml @@ -208,9 +208,9 @@ hardware frequency seeking. V4L2_CAP_TUNER 0x00010000 - The device has some sort of tuner or modulator to -receive or emit RF-modulated video signals. For more information about -tuner and modulator programming see + The device has some sort of tuner to +receive RF-modulated video signals. For more information about +tuner programming see . @@ -227,6 +227,14 @@ interface. For more information on audio inputs and outputs see 0x00040000 This is a radio receiver. + + V4L2_CAP_MODULATOR + 0x00080000 + The device has some sort of modulator to +emit RF-modulated video/audio signals. For more information about +modulator programming see +. + V4L2_CAP_READWRITE 0x01000000 -- cgit v1.2.3 From 4aa3bac2d8bb302d797091628a4873c5c2d2bc05 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2009 14:22:20 +0200 Subject: v4l2grab: fix build rule. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- v4l2-apps/test/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v4l2-apps/test/Makefile b/v4l2-apps/test/Makefile index 71eaa77cf..13eebf377 100644 --- a/v4l2-apps/test/Makefile +++ b/v4l2-apps/test/Makefile @@ -34,6 +34,6 @@ pixfmt-test: pixfmt-test.o $(CC) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ -lX11 v4l2grab: v4l2grab.o - $(CC) $(LDFLAGS) $^ -o $@ ../libv4l/libv4l2/libv4l2.so + $(CC) $(LDFLAGS) $^ -o $@ -L ../libv4l/libv4l2 -L ../libv4l/libv4lconvert -lv4l2 -lv4lconvert include ../Make.rules -- cgit v1.2.3 From e466ec6a572b81252cdc4a681c8ca4949b929dcd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2009 14:59:21 +0200 Subject: mt312: fix build for kernels < 2.6.24 From: Hans Verkuil Add compat.h include to get the KERN_CONT define needed for older kernels. Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/dvb/frontends/mt312.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux/drivers/media/dvb/frontends/mt312.c b/linux/drivers/media/dvb/frontends/mt312.c index 071328d7b..f348f8bea 100644 --- a/linux/drivers/media/dvb/frontends/mt312.c +++ b/linux/drivers/media/dvb/frontends/mt312.c @@ -35,6 +35,7 @@ #include "dvb_frontend.h" #include "mt312_priv.h" #include "mt312.h" +#include "compat.h" struct mt312_state { -- cgit v1.2.3 From 0b52ea5cb80b6745b684d77d006cfa78053dd6b0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2009 15:04:02 +0200 Subject: versions.txt: freezer routines appeared in 2.6.24, not 2.6.23 From: Hans Verkuil Minor fix to get sn9c20x.c to compile on 2.6.23. Priority: normal Signed-off-by: Hans Verkuil --- v4l/versions.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/v4l/versions.txt b/v4l/versions.txt index 792419f99..01b553d6c 100644 --- a/v4l/versions.txt +++ b/v4l/versions.txt @@ -26,11 +26,13 @@ SOC_CAMERA SOC_CAMERA_MT9V022 SOC_CAMERA_MT9M001 +[2.6.24] +# Some freezer routines +USB_GSPCA_SN9C20X_EVDEV + [2.6.23] # Needs field intf_assoc in struct usb_host_config VIDEO_CX231XX -# Some freezer routines -USB_GSPCA_SN9C20X_EVDEV [2.6.22] #This driver requires I2C probe/remove fields -- cgit v1.2.3 From 952c49f54e270f24e84923c228b98348e052317c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2009 16:20:36 +0200 Subject: v4l2-ctl: fix broken camera control support. From: Hans Verkuil v4l2-ctl didn't properly support the camera controls. Priority: normal Signed-off-by: Hans Verkuil --- v4l2-apps/util/v4l2-ctl.cpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp index 6f6b5e2f8..5410e5d03 100644 --- a/v4l2-apps/util/v4l2-ctl.cpp +++ b/v4l2-apps/util/v4l2-ctl.cpp @@ -147,6 +147,7 @@ static unsigned capabilities; typedef std::vector ctrl_list; static ctrl_list user_ctrls; static ctrl_list mpeg_ctrls; +static ctrl_list camera_ctrls; typedef std::map ctrl_strmap; static ctrl_strmap ctrl_str2id; @@ -2187,6 +2188,8 @@ set_vid_fmt_error: ctrl.value = strtol(iter->second.c_str(), NULL, 0); if (V4L2_CTRL_ID2CLASS(ctrl.id) == V4L2_CTRL_CLASS_MPEG) mpeg_ctrls.push_back(ctrl); + else if (V4L2_CTRL_ID2CLASS(ctrl.id) == V4L2_CTRL_CLASS_CAMERA) + camera_ctrls.push_back(ctrl); else user_ctrls.push_back(ctrl); } @@ -2217,6 +2220,22 @@ set_vid_fmt_error: } } } + if (camera_ctrls.size()) { + ctrls.ctrl_class = V4L2_CTRL_CLASS_CAMERA; + ctrls.count = camera_ctrls.size(); + ctrls.controls = &camera_ctrls[0]; + if (doioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls, "VIDIOC_S_EXT_CTRLS")) { + if (ctrls.error_idx >= ctrls.count) { + fprintf(stderr, "Error setting CAMERA controls: %s\n", + strerror(errno)); + } + else { + fprintf(stderr, "%s: %s\n", + ctrl_id2str[camera_ctrls[ctrls.error_idx].id].c_str(), + strerror(errno)); + } + } + } } /* Get options */ @@ -2432,6 +2451,7 @@ set_vid_fmt_error: struct v4l2_ext_controls ctrls = { 0 }; mpeg_ctrls.clear(); + camera_ctrls.clear(); user_ctrls.clear(); for (ctrl_get_list::iterator iter = get_ctrls.begin(); iter != get_ctrls.end(); ++iter) { @@ -2440,6 +2460,8 @@ set_vid_fmt_error: ctrl.id = ctrl_str2id[*iter]; if (V4L2_CTRL_ID2CLASS(ctrl.id) == V4L2_CTRL_CLASS_MPEG) mpeg_ctrls.push_back(ctrl); + else if (V4L2_CTRL_ID2CLASS(ctrl.id) == V4L2_CTRL_CLASS_CAMERA) + camera_ctrls.push_back(ctrl); else user_ctrls.push_back(ctrl); } @@ -2458,6 +2480,17 @@ set_vid_fmt_error: for (unsigned i = 0; i < mpeg_ctrls.size(); i++) { struct v4l2_ext_control ctrl = mpeg_ctrls[i]; + printf("%s: %d\n", ctrl_id2str[ctrl.id].c_str(), ctrl.value); + } + } + if (camera_ctrls.size()) { + ctrls.ctrl_class = V4L2_CTRL_CLASS_CAMERA; + ctrls.count = camera_ctrls.size(); + ctrls.controls = &camera_ctrls[0]; + doioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls, "VIDIOC_G_EXT_CTRLS"); + for (unsigned i = 0; i < camera_ctrls.size(); i++) { + struct v4l2_ext_control ctrl = camera_ctrls[i]; + printf("%s: %d\n", ctrl_id2str[ctrl.id].c_str(), ctrl.value); } } -- cgit v1.2.3 From 49baec9371209e4c77da0321f577b9a33be0fe48 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 20 Jul 2009 11:20:58 -0400 Subject: cx23885-417: fix manipulation of tvnorms From: Michael Krufky Currently, the VIDIOC_S_STD ioctl just returns -EINVAL regardless of the norm passed. This patch sets cx23885_mpeg_template.tvnorms and cx23885_mpeg_template.current_norm so that the VIDIOC_S_STD will work. Thanks to Joseph Yasi for pointing this out, even though this particular fix was already pushed into a development repository, merge priority of this changeset has been escalated as a result of Joseph posting this identical patch. Priority: high Signed-off-by: Michael Krufky Signed-off-by: Joseph A. Yasi Reviewed-by: Steven Toth --- linux/drivers/media/video/cx23885/cx23885-417.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux/drivers/media/video/cx23885/cx23885-417.c b/linux/drivers/media/video/cx23885/cx23885-417.c index fe0b3c41d..0751fa4dc 100644 --- a/linux/drivers/media/video/cx23885/cx23885-417.c +++ b/linux/drivers/media/video/cx23885/cx23885-417.c @@ -1737,6 +1737,8 @@ static struct video_device cx23885_mpeg_template = { .fops = &mpeg_fops, .ioctl_ops = &mpeg_ioctl_ops, .minor = -1, + .tvnorms = CX23885_NORMS, + .current_norm = V4L2_STD_NTSC_M, }; void cx23885_417_unregister(struct cx23885_dev *dev) -- cgit v1.2.3 From 59ac780bc36a1d932e6290e0ea492085f2ec35cd Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Mon, 20 Jul 2009 19:27:27 +0400 Subject: Add CAM presence checkout during CiMax init for NetUP card. From: Igor M. Liplianin CAM presence checkout is needed after power on PC. Signed-off-by: Igor M. Liplianin --- linux/drivers/media/video/cx23885/cimax2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux/drivers/media/video/cx23885/cimax2.c b/linux/drivers/media/video/cx23885/cimax2.c index fed6d9f9f..d717b9416 100644 --- a/linux/drivers/media/video/cx23885/cimax2.c +++ b/linux/drivers/media/video/cx23885/cimax2.c @@ -455,6 +455,7 @@ int netup_ci_init(struct cx23885_tsport *port) #else INIT_WORK(&state->work, netup_read_ci_status); #endif + schedule_work(&state->work); ci_dbg_print("%s: CI initialized!\n", __func__); -- cgit v1.2.3 From 7991d8e7d32620e4b6f0fd92a78e4a090b4a09ee Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Mon, 20 Jul 2009 14:37:25 -0400 Subject: cx23885: Add support for ATSC/QAM on Hauppauge HVR-1850 From: Steven Toth cx23885: Add support for ATSC/QAM on Hauppauge HVR-1850 Priority: normal Signed-off-by: Steven Toth --- linux/Documentation/video4linux/CARDLIST.cx23885 | 1 + linux/drivers/media/video/cx23885/cx23885-cards.c | 49 ++++++++++++++++++++++- linux/drivers/media/video/cx23885/cx23885-dvb.c | 11 +++++ linux/drivers/media/video/cx23885/cx23885.h | 1 + 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/linux/Documentation/video4linux/CARDLIST.cx23885 b/linux/Documentation/video4linux/CARDLIST.cx23885 index 19feb514f..525edb37c 100644 --- a/linux/Documentation/video4linux/CARDLIST.cx23885 +++ b/linux/Documentation/video4linux/CARDLIST.cx23885 @@ -22,3 +22,4 @@ 21 -> Hauppauge WinTV-HVR1210 [0070:2291,0070:2295] 22 -> Mygica X8506 DMB-TH [14f1:8651] 23 -> Magic-Pro ProHDTV Extreme 2 [14f1:8657] + 24 -> Hauppauge WinTV-HVR1850 [0070:8541] diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c index edb99215b..2ffc83e60 100644 --- a/linux/drivers/media/video/cx23885/cx23885-cards.c +++ b/linux/drivers/media/video/cx23885/cx23885-cards.c @@ -206,6 +206,11 @@ struct cx23885_board cx23885_boards[] = { .name = "Magic-Pro ProHDTV Extreme 2", .portb = CX23885_MPEG_DVB, }, + [CX23885_BOARD_HAUPPAUGE_HVR1850] = { + .name = "Hauppauge WinTV-HVR1850", + .portb = CX23885_MPEG_ENCODER, + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -333,6 +338,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x14f1, .subdevice = 0x8657, .card = CX23885_BOARD_MAGICPRO_PROHDTVE2, + }, { + .subvendor = 0x0070, + .subdevice = 0x8541, + .card = CX23885_BOARD_HAUPPAUGE_HVR1850, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -492,8 +501,13 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) /* WinTV-HVR1700 (PCIe, OEM, No IR, full height) * DVB-T and MPEG2 HW Encoder */ break; + case 85021: + /* WinTV-HVR1850 (PCIe, OEM, RCA in, IR, FM, + Dual channel ATSC and MPEG2 HW Encoder */ + break; default: - printk(KERN_WARNING "%s: warning: unknown hauppauge model #%d\n", + printk(KERN_WARNING "%s: warning: " + "unknown hauppauge model #%d\n", dev->name, tv.model); break; } @@ -743,6 +757,36 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00060006); mdelay(100); break; + case CX23885_BOARD_HAUPPAUGE_HVR1850: + /* GPIO-0 656_CLK */ + /* GPIO-1 656_D0 */ + /* GPIO-2 Wake# */ + /* GPIO-3-10 cx23417 data0-7 */ + /* GPIO-11-14 cx23417 addr0-3 */ + /* GPIO-15-18 cx23417 READY, CS, RD, WR */ + /* GPIO-19 IR_RX */ + /* GPIO-20 C_IR_TX */ + /* GPIO-21 I2S DAT */ + /* GPIO-22 I2S WCLK */ + /* GPIO-23 I2S BCLK */ + /* ALT GPIO: EXP GPIO LATCH */ + + /* CX23417 GPIO's */ + /* GPIO-14 S5H1411/CX24228 Reset */ + /* GPIO-13 EEPROM write protect */ + mc417_gpio_enable(dev, GPIO_14 | GPIO_13, 1); + + /* Put the demod into reset and protect the eeprom */ + mc417_gpio_clear(dev, GPIO_14 | GPIO_13); + mdelay(100); + + /* Bring the demod out of reset */ + mc417_gpio_set(dev, GPIO_14); + mdelay(100); + + /* CX24228 GPIO */ + /* Connected to IF / Mux */ + break; } } @@ -759,6 +803,7 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: + case CX23885_BOARD_HAUPPAUGE_HVR1850: /* FIXME: Implement me */ break; case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: @@ -798,6 +843,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: + case CX23885_BOARD_HAUPPAUGE_HVR1850: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -865,6 +911,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: + case CX23885_BOARD_HAUPPAUGE_HVR1850: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 85fac41e7..6edfbbc81 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -867,6 +867,17 @@ static int dvb_register(struct cx23885_tsport *port) &magicpro_prohdtve2_xc5000_config); } break; + case CX23885_BOARD_HAUPPAUGE_HVR1850: + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(s5h1411_attach, + &hcw_s5h1411_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_bus[0].i2c_adap, + &hauppauge_tda18271_config); + break; + default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h index a507af77a..f71277845 100644 --- a/linux/drivers/media/video/cx23885/cx23885.h +++ b/linux/drivers/media/video/cx23885/cx23885.h @@ -78,6 +78,7 @@ #define CX23885_BOARD_HAUPPAUGE_HVR1210 21 #define CX23885_BOARD_MYGICA_X8506 22 #define CX23885_BOARD_MAGICPRO_PROHDTVE2 23 +#define CX23885_BOARD_HAUPPAUGE_HVR1850 24 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 -- cgit v1.2.3 From 988cd25daffa554b51e3f0d5858cb1727f8a9c09 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Mon, 20 Jul 2009 14:40:31 -0400 Subject: cx23885: Modify hardware revision detection for newer silicon From: Steven Toth cx23885: Modify hardware revision detection for newer silicon Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/video/cx23885/cx23885-core.c | 22 +++++++++++++++++++--- linux/drivers/media/video/cx23885/cx23885.h | 1 + 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/linux/drivers/media/video/cx23885/cx23885-core.c b/linux/drivers/media/video/cx23885/cx23885-core.c index 351210e5c..4854d54f1 100644 --- a/linux/drivers/media/video/cx23885/cx23885-core.c +++ b/linux/drivers/media/video/cx23885/cx23885-core.c @@ -714,12 +714,26 @@ static void cx23885_dev_checkrevision(struct cx23885_dev *dev) dev->hwrevision = 0xa1; break; case 0x02: - /* CX23885-13Z */ + /* CX23885-13Z/14Z */ dev->hwrevision = 0xb0; break; case 0x03: - /* CX23888-22Z */ - dev->hwrevision = 0xc0; + if (dev->pci->device == 0x8880) { + /* CX23888-21Z/22Z */ + dev->hwrevision = 0xc0; + } else { + /* CX23885-14Z */ + dev->hwrevision = 0xa4; + } + break; + case 0x04: + if (dev->pci->device == 0x8880) { + /* CX23888-31Z */ + dev->hwrevision = 0xd0; + } else { + /* CX23885-15Z, CX23888-31Z */ + dev->hwrevision = 0xa5; + } break; case 0x0e: /* CX23887-15Z */ @@ -757,6 +771,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) /* Configure the internal memory */ if (dev->pci->device == 0x8880) { + /* Could be 887 or 888, assume a default */ dev->bridge = CX23885_BRIDGE_887; /* Apply a sensible clock frequency for the PCIe bridge */ dev->clk_freq = 25000000; @@ -1259,6 +1274,7 @@ static int cx23885_start_dma(struct cx23885_tsport *port, switch (dev->bridge) { case CX23885_BRIDGE_885: case CX23885_BRIDGE_887: + case CX23885_BRIDGE_888: /* enable irqs */ dprintk(1, "%s() enabling TS int's and DMA\n", __func__); cx_set(port->reg_ts_int_msk, port->ts_int_msk_val); diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h index f71277845..6ddabc263 100644 --- a/linux/drivers/media/video/cx23885/cx23885.h +++ b/linux/drivers/media/video/cx23885/cx23885.h @@ -340,6 +340,7 @@ struct cx23885_dev { CX23885_BRIDGE_UNDEFINED = 0, CX23885_BRIDGE_885 = 885, CX23885_BRIDGE_887 = 887, + CX23885_BRIDGE_888 = 888, } bridge; /* Analog video */ -- cgit v1.2.3 From f48a1698a65eb9b345a625027ac3b8895ab1917a Mon Sep 17 00:00:00 2001 From: Antoine Jacquet Date: Mon, 20 Jul 2009 20:46:42 -0300 Subject: Implement changing resolution on the fly for zr364xx driver From: Lamarque Vieira Souza This patch implements changing resolution in zr364xx_vidioc_s_fmt_vid_cap for zr364xx driver. This version is synced with v4l-dvb as of 20/Jul/2009. Tested with Creative PC-CAM 880. OBS: I had to increase MAX_FRAME_SIZE to prevent a hard crash in my notebook (caps lock blinking) when testing with mplayer, which automatically sets resolution to the maximum (640x480). Maybe we should add code to auto-detect frame size to prevent this kind of crash in the future. Signed-off-by: Lamarque V. Souza Signed-off-by: Antoine Jacquet --- linux/drivers/media/video/zr364xx.c | 65 +++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/linux/drivers/media/video/zr364xx.c b/linux/drivers/media/video/zr364xx.c index 084f97f6a..29ba9f932 100644 --- a/linux/drivers/media/video/zr364xx.c +++ b/linux/drivers/media/video/zr364xx.c @@ -44,14 +44,14 @@ /* Version Information */ #define DRIVER_VERSION "v0.73" -#define ZR364_VERSION_CODE KERNEL_VERSION(0, 7, 3) +#define ZR364XX_VERSION_CODE KERNEL_VERSION(0, 7, 3) #define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" #define DRIVER_DESC "Zoran 364xx" /* Camera */ #define FRAMES 1 -#define MAX_FRAME_SIZE 100000 +#define MAX_FRAME_SIZE 200000 #define BUFFER_SIZE 0x1000 #define CTRL_TIMEOUT 500 @@ -735,7 +735,7 @@ static int zr364xx_vidioc_querycap(struct file *file, void *priv, strlcpy(cap->card, cam->udev->product, sizeof(cap->card)); strlcpy(cap->bus_info, dev_name(&cam->udev->dev), sizeof(cap->bus_info)); - cap->version = ZR364_VERSION_CODE; + cap->version = ZR364XX_VERSION_CODE; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; @@ -875,9 +875,14 @@ static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } + if (!(f->fmt.pix.width == 160 && f->fmt.pix.height == 120) && + !(f->fmt.pix.width == 640 && f->fmt.pix.height == 480)) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + } + f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.width = cam->width; - f->fmt.pix.height = cam->height; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = 0; @@ -908,7 +913,6 @@ static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, return 0; } -/* Lamarque TODO: implement changing resolution on the fly */ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { @@ -916,6 +920,7 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct videobuf_queue *q = &cam->vb_vidq; char pixelformat_name[5]; int ret = zr364xx_vidioc_try_fmt_vid_cap(file, cam, f); + int i; if (ret < 0) return ret; @@ -928,15 +933,55 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, goto out; } - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.width = cam->width; - f->fmt.pix.height = cam->height; + if (res_check(cam)) { + DBG("%s can't change format after started\n", __func__); + ret = -EBUSY; + goto out; + } + + cam->width = f->fmt.pix.width; + cam->height = f->fmt.pix.height; + dev_info(&cam->udev->dev, "%s: %dx%d mode selected\n", __func__, + cam->width, cam->height); f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = 0; f->fmt.pix.priv = 0; cam->vb_vidq.field = f->fmt.pix.field; cam->mode.color = V4L2_PIX_FMT_JPEG; + + if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120) + mode = 1; + else if (f->fmt.pix.width == 640 && f->fmt.pix.height == 480) + mode = 2; + else + mode = 0; + + m0d1[0] = mode; + m1[2].value = 0xf000 + mode; + m2[1].value = 0xf000 + mode; + header2[437] = cam->height / 256; + header2[438] = cam->height % 256; + header2[439] = cam->width / 256; + header2[440] = cam->width % 256; + + for (i = 0; init[cam->method][i].size != -1; i++) { + ret = + send_control_msg(cam->udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (ret < 0) { + dev_err(&cam->udev->dev, + "error during resolution change sequence: %d\n", i); + goto out; + } + } + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + cam->skip = 2; ret = 0; out: @@ -1124,6 +1169,7 @@ static int zr364xx_start_acquire(struct zr364xx_camera *cam) cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; cam->buffer.frame[j].cur_size = 0; } + cam->b_acquire = 1; return 0; } @@ -1166,7 +1212,6 @@ static int zr364xx_vidioc_streamon(struct file *file, void *priv, res = videobuf_streamon(&cam->vb_vidq); if (res == 0) { zr364xx_start_acquire(cam); - cam->b_acquire = 1; } else { res_free(cam); } -- cgit v1.2.3 From e788c8488b451e3c429a140a054712e15e195134 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 21 Jul 2009 08:33:16 +0200 Subject: flexcop: fix compile error From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 301bf1688..850a6c606 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -605,7 +605,7 @@ static int cablestar2_attach(struct flexcop_device *fc, fc->fe->ops.i2c_gate_ctrl = NULL; if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, - fc->fc_i2c_adap[2].i2c_adap, DVB_PLL_TDEE4)) + &fc->fc_i2c_adap[2].i2c_adap, DVB_PLL_TDEE4)) goto fail; return 1; -- cgit v1.2.3 From 1b5f53fa337a5c09750864277d21a723ac28c389 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 21 Jul 2009 04:23:22 -0300 Subject: zr364xx: Need to include linux/version.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/media/video/zr364xx.c: In function ‘zr364xx_vidioc_querycap’: drivers/media/video/zr364xx.c:736: error: implicit declaration of function ‘KERNEL_VERSION’ kernel-sync: Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/zr364xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux/drivers/media/video/zr364xx.c b/linux/drivers/media/video/zr364xx.c index 7dff47834..084f97f6a 100644 --- a/linux/drivers/media/video/zr364xx.c +++ b/linux/drivers/media/video/zr364xx.c @@ -29,6 +29,7 @@ #include +#include #include #include #include -- cgit v1.2.3 From d614ff9f8b6297350f6d1690599733960e186b77 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 21 Jul 2009 09:17:24 -0300 Subject: backport commit 405f55712dfe464b3240d7816cc4fe4174831be2 kernel-sync: Author: Alexey Dobriyan Date: Sat Jul 11 22:08:37 2009 +0400 headers: smp_lock.h redux * Remove smp_lock.h from files which don't need it (including some headers!) * Add smp_lock.h to files which do need it * Make smp_lock.h include conditional in hardirq.h It's needed only for one kernel_locked() usage which is under CONFIG_PREEMPT This will make hardirq.h inclusion cheaper for every PREEMPT=n config (which includes allmodconfig/allyesconfig, BTW) Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/bt8xx/dst_ca.c | 1 + linux/drivers/media/dvb/dvb-core/dvbdev.h | 1 - linux/drivers/media/dvb/ttpci/av7110.c | 1 - linux/drivers/media/radio/radio-mr800.c | 1 + linux/drivers/media/radio/radio-si470x.c | 1 + linux/drivers/media/video/bt8xx/bttv-driver.c | 1 + linux/drivers/media/video/cx23885/cx23885-417.c | 1 + linux/drivers/media/video/cx23885/cx23885-video.c | 1 + linux/drivers/media/video/cx88/cx88-blackbird.c | 1 + linux/drivers/media/video/cx88/cx88-video.c | 1 + linux/drivers/media/video/dabusb.c | 1 + linux/drivers/media/video/pwc/pwc-if.c | 1 + linux/drivers/media/video/pwc/pwc.h | 1 - linux/drivers/media/video/s2255drv.c | 1 + linux/drivers/media/video/saa5246a.c | 1 - linux/drivers/media/video/saa5249.c | 1 - linux/drivers/media/video/saa7134/saa7134-empress.c | 1 + linux/drivers/media/video/se401.c | 1 + linux/drivers/media/video/stk-webcam.c | 1 + linux/drivers/media/video/stradis.c | 1 + linux/drivers/media/video/stv680.c | 1 + linux/drivers/media/video/usbvideo/vicam.c | 1 + linux/drivers/media/video/usbvision/usbvision-video.c | 1 + linux/drivers/media/video/v4l2-dev.c | 1 - linux/drivers/media/video/zoran/zoran_driver.c | 1 + 25 files changed, 19 insertions(+), 6 deletions(-) diff --git a/linux/drivers/media/dvb/bt8xx/dst_ca.c b/linux/drivers/media/dvb/bt8xx/dst_ca.c index bdc22a467..cf35a916c 100644 --- a/linux/drivers/media/dvb/bt8xx/dst_ca.c +++ b/linux/drivers/media/dvb/bt8xx/dst_ca.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include "dvbdev.h" diff --git a/linux/drivers/media/dvb/dvb-core/dvbdev.h b/linux/drivers/media/dvb/dvb-core/dvbdev.h index 79927305e..487919bea 100644 --- a/linux/drivers/media/dvb/dvb-core/dvbdev.h +++ b/linux/drivers/media/dvb/dvb-core/dvbdev.h @@ -27,7 +27,6 @@ #include #include #include -#include #define DVB_MAJOR 212 diff --git a/linux/drivers/media/dvb/ttpci/av7110.c b/linux/drivers/media/dvb/ttpci/av7110.c index 1b961db62..23f45a971 100644 --- a/linux/drivers/media/dvb/ttpci/av7110.c +++ b/linux/drivers/media/dvb/ttpci/av7110.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include diff --git a/linux/drivers/media/radio/radio-mr800.c b/linux/drivers/media/radio/radio-mr800.c index 5ff444cee..2e8d50be8 100644 --- a/linux/drivers/media/radio/radio-mr800.c +++ b/linux/drivers/media/radio/radio-mr800.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include diff --git a/linux/drivers/media/radio/radio-si470x.c b/linux/drivers/media/radio/radio-si470x.c index 3efd397d1..5b4cab3d0 100644 --- a/linux/drivers/media/radio/radio-si470x.c +++ b/linux/drivers/media/radio/radio-si470x.c @@ -131,6 +131,7 @@ #include #include #include +#include #include #include #include diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index 18085ba61..63b0043e2 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include "bttvp.h" diff --git a/linux/drivers/media/video/cx23885/cx23885-417.c b/linux/drivers/media/video/cx23885/cx23885-417.c index fe0b3c41d..d55246072 100644 --- a/linux/drivers/media/video/cx23885/cx23885-417.c +++ b/linux/drivers/media/video/cx23885/cx23885-417.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/linux/drivers/media/video/cx23885/cx23885-video.c b/linux/drivers/media/video/cx23885/cx23885-video.c index 2248e0216..fc7af991e 100644 --- a/linux/drivers/media/video/cx23885/cx23885-video.c +++ b/linux/drivers/media/video/cx23885/cx23885-video.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include "compat.h" diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index b62eadc71..36212fad7 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/linux/drivers/media/video/cx88/cx88-video.c b/linux/drivers/media/video/cx88/cx88-video.c index a55053767..d4bd9ba68 100644 --- a/linux/drivers/media/video/cx88/cx88-video.c +++ b/linux/drivers/media/video/cx88/cx88-video.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/linux/drivers/media/video/dabusb.c b/linux/drivers/media/video/dabusb.c index 9e9cba431..cb0a41c45 100644 --- a/linux/drivers/media/video/dabusb.c +++ b/linux/drivers/media/video/dabusb.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/linux/drivers/media/video/pwc/pwc-if.c b/linux/drivers/media/video/pwc/pwc-if.c index 4087612ee..f695c6624 100644 --- a/linux/drivers/media/video/pwc/pwc-if.c +++ b/linux/drivers/media/video/pwc/pwc-if.c @@ -62,6 +62,7 @@ #include #include #include +#include #ifdef CONFIG_USB_PWC_INPUT_EVDEV #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) #include diff --git a/linux/drivers/media/video/pwc/pwc.h b/linux/drivers/media/video/pwc/pwc.h index 7a766eb35..12c869d71 100644 --- a/linux/drivers/media/video/pwc/pwc.h +++ b/linux/drivers/media/video/pwc/pwc.h @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include diff --git a/linux/drivers/media/video/s2255drv.c b/linux/drivers/media/video/s2255drv.c index 203f6e3ff..4478f26fe 100644 --- a/linux/drivers/media/video/s2255drv.c +++ b/linux/drivers/media/video/s2255drv.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include diff --git a/linux/drivers/media/video/saa5246a.c b/linux/drivers/media/video/saa5246a.c index 1b19a72cc..4593ace1c 100644 --- a/linux/drivers/media/video/saa5246a.c +++ b/linux/drivers/media/video/saa5246a.c @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include diff --git a/linux/drivers/media/video/saa5249.c b/linux/drivers/media/video/saa5249.c index 2d9d7d7fd..b7cf17361 100644 --- a/linux/drivers/media/video/saa5249.c +++ b/linux/drivers/media/video/saa5249.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include diff --git a/linux/drivers/media/video/saa7134/saa7134-empress.c b/linux/drivers/media/video/saa7134/saa7134-empress.c index 96334a20b..181d96e83 100644 --- a/linux/drivers/media/video/saa7134/saa7134-empress.c +++ b/linux/drivers/media/video/saa7134/saa7134-empress.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "saa7134-reg.h" diff --git a/linux/drivers/media/video/se401.c b/linux/drivers/media/video/se401.c index 1274a5966..de9e25035 100644 --- a/linux/drivers/media/video/se401.c +++ b/linux/drivers/media/video/se401.c @@ -31,6 +31,7 @@ static const char version[] = "0.24"; #include #include #include +#include #include #include #include "se401.h" diff --git a/linux/drivers/media/video/stk-webcam.c b/linux/drivers/media/video/stk-webcam.c index 886caf736..849867ab2 100644 --- a/linux/drivers/media/video/stk-webcam.c +++ b/linux/drivers/media/video/stk-webcam.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include diff --git a/linux/drivers/media/video/stradis.c b/linux/drivers/media/video/stradis.c index 33001b426..9e28eb2d8 100644 --- a/linux/drivers/media/video/stradis.c +++ b/linux/drivers/media/video/stradis.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/linux/drivers/media/video/stv680.c b/linux/drivers/media/video/stv680.c index a2d7ec732..eafe68502 100644 --- a/linux/drivers/media/video/stv680.c +++ b/linux/drivers/media/video/stv680.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include #include diff --git a/linux/drivers/media/video/usbvideo/vicam.c b/linux/drivers/media/video/usbvideo/vicam.c index 3b332ef52..b8d9e81b4 100644 --- a/linux/drivers/media/video/usbvideo/vicam.c +++ b/linux/drivers/media/video/usbvideo/vicam.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) #include diff --git a/linux/drivers/media/video/usbvision/usbvision-video.c b/linux/drivers/media/video/usbvision/usbvision-video.c index 90b58914f..90d9b5c0e 100644 --- a/linux/drivers/media/video/usbvision/usbvision-video.c +++ b/linux/drivers/media/video/usbvision/usbvision-video.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include diff --git a/linux/drivers/media/video/v4l2-dev.c b/linux/drivers/media/video/v4l2-dev.c index f7e128729..9969abb28 100644 --- a/linux/drivers/media/video/v4l2-dev.c +++ b/linux/drivers/media/video/v4l2-dev.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include diff --git a/linux/drivers/media/video/zoran/zoran_driver.c b/linux/drivers/media/video/zoran/zoran_driver.c index 8c17d47b3..450a00c98 100644 --- a/linux/drivers/media/video/zoran/zoran_driver.c +++ b/linux/drivers/media/video/zoran/zoran_driver.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 9eeecdeb0316860051424e4fa4fe315c10fcc0ad Mon Sep 17 00:00:00 2001 From: Antoine Jacquet Date: Wed, 22 Jul 2009 16:54:51 -0300 Subject: zr364xx: error message when buffer is too small and code cleanup From: Lamarque Vieira Souza . added code to print an error message when buffer is too small to hold frame data, that is better than just a hard crash. Tested using MAX_FRAME_SIZE = 50000, lots of error messages appeared in /var/log/messages but no crash. . removed line "f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;" in zr364xx_vidioc_try_fmt_vid_cap, it should not be there. . changes to improve performance (or at least not hurt it): removed some unneeded debug messages; added macro FULL_DEBUG to enable debug messages in performance critical places, this macro is disabled by default; removed "if (frm->lpvbits == NULL)..." in zr364xx_read_video_callback because after analisying the source code I concluded it will always results to false, so it is not needed. . some small code reorganization. Signed-off-by: Lamarque V. Souza Signed-off-by: Antoine Jacquet --- linux/drivers/media/video/zr364xx.c | 63 +++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/linux/drivers/media/video/zr364xx.c b/linux/drivers/media/video/zr364xx.c index 29ba9f932..c298ce625 100644 --- a/linux/drivers/media/video/zr364xx.c +++ b/linux/drivers/media/video/zr364xx.c @@ -67,6 +67,13 @@ } \ } while (0) +/*#define FULL_DEBUG 1*/ +#ifdef FULL_DEBUG +#define _DBG DBG +#else +#define _DBG(fmt, args...) +#endif + /* Init methods, need to find nicer names for these * the exact names of the chipsets would be the best if someone finds it */ #define METHOD0 0 @@ -376,7 +383,7 @@ static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf) { - DBG("%s\n", __func__); + _DBG("%s\n", __func__); if (in_interrupt()) BUG(); @@ -429,7 +436,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) vb); struct zr364xx_camera *cam = vq->priv_data; - DBG("%s\n", __func__); + _DBG("%s\n", __func__); buf->vb.state = VIDEOBUF_QUEUED; list_add_tail(&buf->vb.queue, &cam->vidq.active); @@ -441,7 +448,7 @@ static void buffer_release(struct videobuf_queue *vq, struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, vb); - DBG("%s\n", __func__); + _DBG("%s\n", __func__); free_buffer(vq, buf); } @@ -463,7 +470,7 @@ static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count, { struct zr364xx_camera *cam = video_drvdata(file); - DBG("%s\n", __func__); + _DBG("%s\n", __func__); if (!buf) return -EINVAL; @@ -583,7 +590,7 @@ static int zr364xx_read_video_callback(struct zr364xx_camera *cam, int i = 0; unsigned char *ptr = NULL; - /*DBG("buffer to user\n");*/ + _DBG("buffer to user\n"); idx = cam->cur_frame; frm = &cam->buffer.frame[idx]; @@ -601,12 +608,6 @@ static int zr364xx_read_video_callback(struct zr364xx_camera *cam, return -EINVAL; } - if (frm->lpvbits == NULL) { - DBG("%s: frame buffer == NULL.%p %p %d\n", __func__, - frm, cam, idx); - return -ENOMEM; - } - psrc = (u8 *)pipe_info->transfer_buffer; ptr = pdest = frm->lpvbits; @@ -614,7 +615,7 @@ static int zr364xx_read_video_callback(struct zr364xx_camera *cam, frm->ulState = ZR364XX_READ_FRAME; frm->cur_size = 0; - DBG("jpeg header, "); + _DBG("jpeg header, "); memcpy(ptr, header1, sizeof(header1)); ptr += sizeof(header1); header3 = 0; @@ -632,21 +633,28 @@ static int zr364xx_read_video_callback(struct zr364xx_camera *cam, memcpy(ptr, psrc + 128, purb->actual_length - 128); ptr += purb->actual_length - 128; - DBG("header : %d %d %d %d %d %d %d %d %d\n", + _DBG("header : %d %d %d %d %d %d %d %d %d\n", psrc[0], psrc[1], psrc[2], psrc[3], psrc[4], psrc[5], psrc[6], psrc[7], psrc[8]); frm->cur_size = ptr - pdest; } else { - pdest += frm->cur_size; - memcpy(pdest, psrc, purb->actual_length); - frm->cur_size += purb->actual_length; + if (frm->cur_size + purb->actual_length > MAX_FRAME_SIZE) { + dev_info(&cam->udev->dev, + "%s: buffer (%d bytes) too small to hold " + "frame data. Discarding frame data.\n", + __func__, MAX_FRAME_SIZE); + } else { + pdest += frm->cur_size; + memcpy(pdest, psrc, purb->actual_length); + frm->cur_size += purb->actual_length; + } } - /*DBG("cur_size %lu urb size %d\n", frm->cur_size, + /*_DBG("cur_size %lu urb size %d\n", frm->cur_size, purb->actual_length);*/ if (purb->actual_length < pipe_info->transfer_size) { - DBG("****************Buffer[%d]full*************\n", idx); + _DBG("****************Buffer[%d]full*************\n", idx); cam->last_frame = cam->cur_frame; cam->cur_frame++; /* end of system frame ring buffer, start at zero */ @@ -681,7 +689,7 @@ static int zr364xx_read_video_callback(struct zr364xx_camera *cam, if (cam->skip) cam->skip--; else { - DBG("jpeg(%lu): %d %d %d %d %d %d %d %d\n", + _DBG("jpeg(%lu): %d %d %d %d %d %d %d %d\n", frm->cur_size, pdest[0], pdest[1], pdest[2], pdest[3], pdest[4], pdest[5], pdest[6], pdest[7]); @@ -708,7 +716,7 @@ static int res_get(struct zr364xx_camera *cam) } /* it's free, grab it */ cam->resources = 1; - DBG("res: get\n"); + _DBG("res: get\n"); mutex_unlock(&cam->lock); return 1; } @@ -723,7 +731,7 @@ static void res_free(struct zr364xx_camera *cam) mutex_lock(&cam->lock); cam->resources = 0; mutex_unlock(&cam->lock); - DBG("res: put\n"); + _DBG("res: put\n"); } static int zr364xx_vidioc_querycap(struct file *file, void *priv, @@ -882,7 +890,6 @@ static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, } f->fmt.pix.field = V4L2_FIELD_NONE; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = 0; @@ -1018,7 +1025,7 @@ static int zr364xx_vidioc_qbuf(struct file *file, { int rc; struct zr364xx_camera *cam = video_drvdata(file); - DBG("%s\n", __func__); + _DBG("%s\n", __func__); rc = videobuf_qbuf(&cam->vb_vidq, p); return rc; } @@ -1029,7 +1036,7 @@ static int zr364xx_vidioc_dqbuf(struct file *file, { int rc; struct zr364xx_camera *cam = video_drvdata(file); - DBG("%s\n", __func__); + _DBG("%s\n", __func__); rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK); return rc; } @@ -1041,7 +1048,7 @@ static void read_pipe_completion(struct urb *purb) int pipe; pipe_info = purb->context; - /*DBG("%s %p, status %d\n", __func__, purb, purb->status);*/ + _DBG("%s %p, status %d\n", __func__, purb, purb->status); if (pipe_info == NULL) { printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); return; @@ -1152,7 +1159,6 @@ static void zr364xx_stop_readpipe(struct zr364xx_camera *cam) pipe_info->stream_urb = NULL; } } - DBG("stop read pipe\n"); return; } @@ -1215,7 +1221,6 @@ static int zr364xx_vidioc_streamon(struct file *file, void *priv, } else { res_free(cam); } - DBG("%s: %d\n", __func__, res); return res; } @@ -1327,8 +1332,6 @@ static void zr364xx_destroy(struct zr364xx_camera *cam) /* release transfer buffer */ kfree(cam->pipe->transfer_buffer); cam->pipe->transfer_buffer = NULL; - - DBG("%s\n", __func__); mutex_unlock(&cam->open_lock); kfree(cam); cam = NULL; @@ -1409,7 +1412,7 @@ static unsigned int zr364xx_poll(struct file *file, { struct zr364xx_camera *cam = video_drvdata(file); struct videobuf_queue *q = &cam->vb_vidq; - DBG("%s\n", __func__); + _DBG("%s\n", __func__); if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return POLLERR; -- cgit v1.2.3 From 72e561371a01666dd61a04af3cc6c0eac757f542 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 22 Jul 2009 22:20:50 +0200 Subject: pxa_camera: Fix Oops in pxa_camera_probe. From: Antonio Ospite mclk_get_divisor uses pcdev->soc_host.dev, make sure it is initialized. Priority: high Signed-off-by: Antonio Ospite Signed-off-by: Guennadi Liakhovetski --- drivers/media/video/pxa_camera.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) --- linux/drivers/media/video/pxa_camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index 8964d60cd..3f1b63e4e 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -1591,6 +1591,7 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev) pcdev->mclk = 20000000; } + pcdev->soc_host.dev = &pdev->dev; pcdev->mclk_divisor = mclk_get_divisor(pcdev); INIT_LIST_HEAD(&pcdev->capture); @@ -1656,7 +1657,6 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev) pcdev->soc_host.drv_name = PXA_CAM_DRV_NAME; pcdev->soc_host.ops = &pxa_soc_camera_host_ops; pcdev->soc_host.priv = pcdev; - pcdev->soc_host.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; err = soc_camera_host_register(&pcdev->soc_host); -- cgit v1.2.3 From 0c962c2acd61e781501914a4b96fd47a707bccc5 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Wed, 22 Jul 2009 23:30:25 +0300 Subject: Create card parameters array in SDMC DM1105 driver. From: Igor M. Liplianin Create card parameters array in SDMC DM1105 driver. It is useful for cards with the same pci id, but different tuners, lnb power control circuits, etc. Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/dm1105/dm1105.c | 148 ++++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 15 deletions(-) diff --git a/linux/drivers/media/dvb/dm1105/dm1105.c b/linux/drivers/media/dvb/dm1105/dm1105.c index 17edeb66b..f937151b3 100644 --- a/linux/drivers/media/dvb/dm1105/dm1105.c +++ b/linux/drivers/media/dvb/dm1105/dm1105.c @@ -44,6 +44,14 @@ #include "cx24116.h" #include "z0194a.h" +#define UNSET (-1U) + +#define DM1105_BOARD_NOAUTO UNSET +#define DM1105_BOARD_UNKNOWN 0 +#define DM1105_BOARD_DVBWORLD_2002 1 +#define DM1105_BOARD_DVBWORLD_2004 2 +#define DM1105_BOARD_AXESS_DM05 3 + /* ----------------------------------------------- */ /* * PCI ID's @@ -153,20 +161,105 @@ /* GPIO's for LNB power control */ #define DM1105_LNB_MASK 0x00000000 +#define DM1105_LNB_OFF 0x00020000 #define DM1105_LNB_13V 0x00010100 #define DM1105_LNB_18V 0x00000100 /* GPIO's for LNB power control for Axess DM05 */ #define DM05_LNB_MASK 0x00000000 +#define DM05_LNB_OFF 0x00020000/* actually 13v */ #define DM05_LNB_13V 0x00020000 #define DM05_LNB_18V 0x00030000 +static unsigned int card[] = {[0 ... 3] = UNSET }; +module_param_array(card, int, NULL, 0444); +MODULE_PARM_DESC(card, "card type"); + static int ir_debug; module_param(ir_debug, int, 0644); MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); +static unsigned int dm1105_devcount; + DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +struct dm1105_board { + char *name; +}; + +struct dm1105_subid { + u16 subvendor; + u16 subdevice; + u32 card; +}; + +static const struct dm1105_board dm1105_boards[] = { + [DM1105_BOARD_UNKNOWN] = { + .name = "UNKNOWN/GENERIC", + }, + [DM1105_BOARD_DVBWORLD_2002] = { + .name = "DVBWorld PCI 2002", + }, + [DM1105_BOARD_DVBWORLD_2004] = { + .name = "DVBWorld PCI 2004", + }, + [DM1105_BOARD_AXESS_DM05] = { + .name = "Axess/EasyTv DM05", + }, +}; + +static const struct dm1105_subid dm1105_subids[] = { + { + .subvendor = 0x0000, + .subdevice = 0x2002, + .card = DM1105_BOARD_DVBWORLD_2002, + }, { + .subvendor = 0x0001, + .subdevice = 0x2002, + .card = DM1105_BOARD_DVBWORLD_2002, + }, { + .subvendor = 0x0000, + .subdevice = 0x2004, + .card = DM1105_BOARD_DVBWORLD_2004, + }, { + .subvendor = 0x0001, + .subdevice = 0x2004, + .card = DM1105_BOARD_DVBWORLD_2004, + }, { + .subvendor = 0x195d, + .subdevice = 0x1105, + .card = DM1105_BOARD_AXESS_DM05, + }, +}; + +static void dm1105_card_list(struct pci_dev *pci) +{ + int i; + + if (0 == pci->subsystem_vendor && + 0 == pci->subsystem_device) { + printk(KERN_ERR + "dm1105: Your board has no valid PCI Subsystem ID\n" + "dm1105: and thus can't be autodetected\n" + "dm1105: Please pass card= insmod option to\n" + "dm1105: workaround that. Redirect complaints to\n" + "dm1105: the vendor of the TV card. Best regards,\n" + "dm1105: -- tux\n"); + } else { + printk(KERN_ERR + "dm1105: Your board isn't known (yet) to the driver.\n" + "dm1105: You can try to pick one of the existing\n" + "dm1105: card configs via card= insmod option.\n" + "dm1105: Updating to the latest version might help\n" + "dm1105: as well.\n"); + } + printk(KERN_ERR "Here is a list of valid choices for the card= " + "insmod option:\n"); + for (i = 0; i < ARRAY_SIZE(dm1105_boards); i++) + printk(KERN_ERR "dm1105: card=%d -> %s\n", + i, dm1105_boards[i].name); +} + /* infrared remote control */ struct infrared { struct input_dev *input_dev; @@ -193,6 +286,8 @@ struct dm1105dvb { struct dvb_frontend *fe; struct dvb_net dvbnet; unsigned int full_ts_users; + unsigned int boardnr; + int nr; /* i2c */ struct i2c_adapter i2c_adap; @@ -211,7 +306,6 @@ struct dm1105dvb { unsigned int PacketErrorCount; unsigned int dmarst; spinlock_t lock; - }; #define dm_io_mem(reg) ((unsigned long)(&dm1105dvb->io_mem[reg])) @@ -329,16 +423,20 @@ static inline struct dm1105dvb *frontend_to_dm1105dvb(struct dvb_frontend *fe) static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { struct dm1105dvb *dm1105dvb = frontend_to_dm1105dvb(fe); - u32 lnb_mask, lnb_13v, lnb_18v; + u32 lnb_mask, lnb_13v, lnb_18v, lnb_off; - switch (dm1105dvb->pdev->subsystem_device) { - case PCI_DEVICE_ID_DM05: + switch (dm1105dvb->boardnr) { + case DM1105_BOARD_AXESS_DM05: lnb_mask = DM05_LNB_MASK; + lnb_off = DM05_LNB_OFF; lnb_13v = DM05_LNB_13V; lnb_18v = DM05_LNB_18V; break; + case DM1105_BOARD_DVBWORLD_2002: + case DM1105_BOARD_DVBWORLD_2004: default: lnb_mask = DM1105_LNB_MASK; + lnb_off = DM1105_LNB_OFF; lnb_13v = DM1105_LNB_13V; lnb_18v = DM1105_LNB_18V; } @@ -346,8 +444,10 @@ static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volta outl(lnb_mask, dm_io_mem(DM1105_GPIOCTR)); if (voltage == SEC_VOLTAGE_18) outl(lnb_18v , dm_io_mem(DM1105_GPIOVAL)); - else + else if (voltage == SEC_VOLTAGE_13) outl(lnb_13v, dm_io_mem(DM1105_GPIOVAL)); + else + outl(lnb_off, dm_io_mem(DM1105_GPIOVAL)); return 0; } @@ -621,8 +721,8 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) { int ret; - switch (dm1105dvb->pdev->subsystem_device) { - case PCI_DEVICE_ID_DW2004: + switch (dm1105dvb->boardnr) { + case DM1105_BOARD_DVBWORLD_2004: dm1105dvb->fe = dvb_attach( cx24116_attach, &serit_sp2633_config, &dm1105dvb->i2c_adap); @@ -630,6 +730,8 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) dm1105dvb->fe->ops.set_voltage = dm1105dvb_set_voltage; break; + case DM1105_BOARD_DVBWORLD_2002: + case DM1105_BOARD_AXESS_DM05: default: dm1105dvb->fe = dvb_attach( stv0299_attach, &sharp_z0194a_config, @@ -708,11 +810,31 @@ static int __devinit dm1105_probe(struct pci_dev *pdev, struct dvb_demux *dvbdemux; struct dmx_demux *dmx; int ret = -ENOMEM; + int i; dm1105dvb = kzalloc(sizeof(struct dm1105dvb), GFP_KERNEL); if (!dm1105dvb) return -ENOMEM; + /* board config */ + dm1105dvb->nr = dm1105_devcount; + dm1105dvb->boardnr = UNSET; + if (card[dm1105dvb->nr] < ARRAY_SIZE(dm1105_boards)) + dm1105dvb->boardnr = card[dm1105dvb->nr]; + for (i = 0; UNSET == dm1105dvb->boardnr && + i < ARRAY_SIZE(dm1105_subids); i++) + if (pdev->subsystem_vendor == + dm1105_subids[i].subvendor && + pdev->subsystem_device == + dm1105_subids[i].subdevice) + dm1105dvb->boardnr = dm1105_subids[i].card; + + if (UNSET == dm1105dvb->boardnr) { + dm1105dvb->boardnr = DM1105_BOARD_UNKNOWN; + dm1105_card_list(pdev); + } + + dm1105_devcount++; dm1105dvb->pdev = pdev; dm1105dvb->buffer_size = 5 * DM1105_DMA_BYTES; dm1105dvb->PacketErrorCount = 0; @@ -889,6 +1011,7 @@ static void __devexit dm1105_remove(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); + dm1105_devcount--; kfree(dm1105dvb); } @@ -897,17 +1020,12 @@ static struct pci_device_id dm1105_id_table[] __devinitdata = { .vendor = PCI_VENDOR_ID_TRIGEM, .device = PCI_DEVICE_ID_DM1105, .subvendor = PCI_ANY_ID, - .subdevice = PCI_DEVICE_ID_DW2002, - }, { - .vendor = PCI_VENDOR_ID_TRIGEM, - .device = PCI_DEVICE_ID_DM1105, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_DEVICE_ID_DW2004, + .subdevice = PCI_ANY_ID, }, { .vendor = PCI_VENDOR_ID_AXESS, .device = PCI_DEVICE_ID_DM05, - .subvendor = PCI_VENDOR_ID_AXESS, - .subdevice = PCI_DEVICE_ID_DM05, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, }, { /* empty */ }, -- cgit v1.2.3 From f28ced8b3b15803ed5c0d99c29aaa51c4153e842 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 22 Jul 2009 20:02:44 -0400 Subject: tuner-simple: Add an entry for the Partsnic PTI-5NF05 NTSC tuner From: Andy Walls Priority: normal Signed-off-by: Andy Walls --- linux/Documentation/video4linux/CARDLIST.tuner | 1 + linux/drivers/media/common/tuners/tuner-types.c | 25 +++++++++++++++++++++++++ linux/include/media/tuner.h | 1 + 3 files changed, 27 insertions(+) diff --git a/linux/Documentation/video4linux/CARDLIST.tuner b/linux/Documentation/video4linux/CARDLIST.tuner index be6784407..ba9fa679e 100644 --- a/linux/Documentation/video4linux/CARDLIST.tuner +++ b/linux/Documentation/video4linux/CARDLIST.tuner @@ -78,3 +78,4 @@ tuner=77 - TCL tuner MF02GIP-5N-E tuner=78 - Philips FMD1216MEX MK3 Hybrid Tuner tuner=79 - Philips PAL/SECAM multi (FM1216 MK5) tuner=80 - Philips FQ1216LME MK3 PAL/SECAM w/active loopthrough +tuner=81 - Partsnic (Daewoo) PTI-5NF05 diff --git a/linux/drivers/media/common/tuners/tuner-types.c b/linux/drivers/media/common/tuners/tuner-types.c index e57d7cb2f..c4bbb460a 100644 --- a/linux/drivers/media/common/tuners/tuner-types.c +++ b/linux/drivers/media/common/tuners/tuner-types.c @@ -1302,6 +1302,25 @@ static struct tuner_params tuner_fq1216lme_mk3_params[] = { }, }; +/* ----- TUNER_PARTSNIC_PTI_5NF05 - Partsnic (Daewoo) PTI-5NF05 NTSC ----- */ + +static struct tuner_range tuner_partsnic_pti_5nf05_ranges[] = { + /* The datasheet specified channel ranges and the bandswitch byte */ + /* The control byte value of 0x8e is just a guess */ + { 16 * 133.25 /*MHz*/, 0x8e, 0x01, }, /* Channels 2 - B */ + { 16 * 367.25 /*MHz*/, 0x8e, 0x02, }, /* Channels C - W+11 */ + { 16 * 999.99 , 0x8e, 0x08, }, /* Channels W+12 - 69 */ +}; + +static struct tuner_params tuner_partsnic_pti_5nf05_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_partsnic_pti_5nf05_ranges, + .count = ARRAY_SIZE(tuner_partsnic_pti_5nf05_ranges), + .cb_first_if_lower_freq = 1, /* not specified but safe to do */ + }, +}; + /* --------------------------------------------------------------------- */ struct tunertype tuners[] = { @@ -1754,6 +1773,12 @@ struct tunertype tuners[] = { .params = tuner_fq1216lme_mk3_params, .count = ARRAY_SIZE(tuner_fq1216lme_mk3_params), }, + + [TUNER_PARTSNIC_PTI_5NF05] = { + .name = "Partsnic (Daewoo) PTI-5NF05", + .params = tuner_partsnic_pti_5nf05_params, + .count = ARRAY_SIZE(tuner_partsnic_pti_5nf05_params), + }, }; EXPORT_SYMBOL(tuners); diff --git a/linux/include/media/tuner.h b/linux/include/media/tuner.h index cbf97f45f..c146f2f53 100644 --- a/linux/include/media/tuner.h +++ b/linux/include/media/tuner.h @@ -126,6 +126,7 @@ #define TUNER_PHILIPS_FMD1216MEX_MK3 78 #define TUNER_PHILIPS_FM1216MK5 79 #define TUNER_PHILIPS_FQ1216LME_MK3 80 /* Active loopthrough, no FM */ +#define TUNER_PARTSNIC_PTI_5NF05 81 /* tv card specific */ #define TDA9887_PRESENT (1<<0) -- cgit v1.2.3 From 97c94bc15d7a898a7061e5655947214479655a20 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 22 Jul 2009 20:15:58 -0400 Subject: ivtv: Fix errors in AVerTV M113 card definitions and add a new M113 card From: Andy Walls Priority: normal Tested-by: Ravi A. Signed-off-by: Andy Walls --- linux/drivers/media/video/ivtv/ivtv-cards.c | 33 +++++++++++++++++------------ 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/linux/drivers/media/video/ivtv/ivtv-cards.c b/linux/drivers/media/video/ivtv/ivtv-cards.c index 40c29032f..4873b6ca5 100644 --- a/linux/drivers/media/video/ivtv/ivtv-cards.c +++ b/linux/drivers/media/video/ivtv/ivtv-cards.c @@ -977,26 +977,27 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = { /* ------------------------------------------------------------------------- */ -/* AVerMedia PVR-150 Plus (M113) card */ +/* AVerMedia PVR-150 Plus / AVerTV M113 cards with a Daewoo/Partsnic Tuner */ static const struct ivtv_card_pci_info ivtv_pci_aver_pvr150[] = { - { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc035 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc034 }, /* NTSC */ + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc035 }, /* NTSC FM */ { 0, 0, 0 } }; static const struct ivtv_card ivtv_card_aver_pvr150 = { .type = IVTV_CARD_AVER_PVR150PLUS, - .name = "AVerMedia PVR-150 Plus", + .name = "AVerMedia PVR-150 Plus / AVerTV M113 Partsnic (Daewoo) Tuner", .v4l2_capabilities = IVTV_CAP_ENCODER, .hw_video = IVTV_HW_CX25840, .hw_audio = IVTV_HW_CX25840, .hw_audio_ctrl = IVTV_HW_CX25840, .hw_muxer = IVTV_HW_GPIO, - .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | + IVTV_HW_WM8739 | IVTV_HW_GPIO, .video_inputs = { { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, - { IVTV_CARD_INPUT_SVIDEO1, 1, - CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 }, { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, }, .audio_inputs = { @@ -1004,13 +1005,18 @@ static const struct ivtv_card ivtv_card_aver_pvr150 = { { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, }, .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, - .gpio_init = { .direction = 0x0800, .initial_value = 0 }, - .gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 }, + /* The 74HC4052 Dual 4:1 multiplexer is controlled by 2 GPIO lines */ + .gpio_init = { .direction = 0xc000, .initial_value = 0 }, + .gpio_audio_input = { .mask = 0xc000, + .tuner = 0x0000, + .linein = 0x4000, + .radio = 0x8000 }, .tuners = { - /* This card has a Partsnic PTI-5NF05 tuner */ - { .std = V4L2_STD_MN, .tuner = TUNER_TCL_2002N }, + /* Subsystem ID's 0xc03[45] have a Partsnic PTI-5NF05 tuner */ + { .std = V4L2_STD_MN, .tuner = TUNER_PARTSNIC_PTI_5NF05 }, }, .pci_list = ivtv_pci_aver_pvr150, + /* Subsystem ID 0xc035 has a TEA5767(?) FM tuner, 0xc034 does not */ .i2c = &ivtv_i2c_radio, }; @@ -1025,7 +1031,7 @@ static const struct ivtv_card_pci_info ivtv_pci_aver_ultra1500mce[] = { static const struct ivtv_card ivtv_card_aver_ultra1500mce = { .type = IVTV_CARD_AVER_ULTRA1500MCE, - .name = "AVerMedia UltraTV 1500 MCE / AVerTV M113", + .name = "AVerMedia UltraTV 1500 MCE / AVerTV M113 Philips Tuner", .v4l2_capabilities = IVTV_CAP_ENCODER, .hw_video = IVTV_HW_CX25840, .hw_audio = IVTV_HW_CX25840, @@ -1035,8 +1041,7 @@ static const struct ivtv_card ivtv_card_aver_ultra1500mce = { IVTV_HW_WM8739 | IVTV_HW_GPIO, .video_inputs = { { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, - { IVTV_CARD_INPUT_SVIDEO1, 1, - CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 }, { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, }, .audio_inputs = { @@ -1044,7 +1049,7 @@ static const struct ivtv_card ivtv_card_aver_ultra1500mce = { { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, }, .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, - /* The 74HC4502 Dual 4:1 multiplexer is controlled by 2 GPIO lines */ + /* The 74HC4052 Dual 4:1 multiplexer is controlled by 2 GPIO lines */ .gpio_init = { .direction = 0xc000, .initial_value = 0 }, .gpio_audio_input = { .mask = 0xc000, .tuner = 0x0000, -- cgit v1.2.3 From f1fdde7178e4aee3df0e79c6c181d16e75776343 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 22 Jul 2009 20:24:53 -0400 Subject: ivtv: Fix improper GPIO audio mux input switch on video standard change From: Andy Walls Remove the subdev_s_std() method from the GPIO subdev. It appears it was used in the past to effect the switch back from radio mode to tuner mode, but it had a side effect that is improper and against the V4L2 spec, when you were not in radio mode. Whenever the video stanadrd was changed, the GPIO audio mux was set back to the tuner audio input, even if you were set to a Line In input. Thanks to Ravi A. for doing the troubleshooting to point me right to the problem. Reported-by: Ravi A Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/ivtv/ivtv-gpio.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/linux/drivers/media/video/ivtv/ivtv-gpio.c b/linux/drivers/media/video/ivtv/ivtv-gpio.c index 85ac70722..aede061ca 100644 --- a/linux/drivers/media/video/ivtv/ivtv-gpio.c +++ b/linux/drivers/media/video/ivtv/ivtv-gpio.c @@ -236,18 +236,6 @@ static int subdev_s_radio(struct v4l2_subdev *sd) return 0; } -static int subdev_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct ivtv *itv = sd_to_ivtv(sd); - u16 mask, data; - - mask = itv->card->gpio_audio_input.mask; - data = itv->card->gpio_audio_input.tuner; - if (mask) - write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); - return 0; -} - static int subdev_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { @@ -344,7 +332,6 @@ static const struct v4l2_subdev_core_ops subdev_core_ops = { .g_ctrl = subdev_g_ctrl, .s_ctrl = subdev_s_ctrl, .queryctrl = subdev_queryctrl, - .s_std = subdev_s_std, }; static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = { -- cgit v1.2.3 From d4d9fba26b4460171132eb9f8dad28fc4ae8fca4 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 23 Jul 2009 10:55:43 +0200 Subject: gspca - sn9c20x: Misc fixes. From: Brian Johnson * use i2c_w instead of reg_w * return error on failure * read the correct number of bytes Priority: normal Signed-off-by: Brian Johnson Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sn9c20x.c | 38 ++++++++++++++++--------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/linux/drivers/media/video/gspca/sn9c20x.c b/linux/drivers/media/video/gspca/sn9c20x.c index a516866a2..fc47f978e 100644 --- a/linux/drivers/media/video/gspca/sn9c20x.c +++ b/linux/drivers/media/video/gspca/sn9c20x.c @@ -1099,12 +1099,12 @@ int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) reg_r(gspca_dev, 0x10c0, 1); if (gspca_dev->usb_buf[0] & 0x04) { if (gspca_dev->usb_buf[0] & 0x08) - return -1; + return -EIO; return 0; } msleep(1); } - return -1; + return -EIO; } int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) @@ -1155,7 +1155,7 @@ int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; - row[0] = 0x81 | 0x10; + row[0] = 0x81 | (1 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = 0; @@ -1163,14 +1163,15 @@ int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) row[5] = 0; row[6] = 0; row[7] = 0x10; - reg_w(gspca_dev, 0x10c0, row, 8); - msleep(1); - row[0] = 0x81 | (2 << 4) | 0x02; + if (i2c_w(gspca_dev, row) < 0) + return -EIO; + row[0] = 0x81 | (1 << 4) | 0x02; row[2] = 0; - reg_w(gspca_dev, 0x10c0, row, 8); - msleep(1); - reg_r(gspca_dev, 0x10c2, 5); - *val = gspca_dev->usb_buf[3]; + if (i2c_w(gspca_dev, row) < 0) + return -EIO; + if (reg_r(gspca_dev, 0x10c2, 5) < 0) + return -EIO; + *val = gspca_dev->usb_buf[4]; return 0; } @@ -1179,7 +1180,7 @@ int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; - row[0] = 0x81 | 0x10; + row[0] = 0x81 | (1 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = 0; @@ -1187,14 +1188,15 @@ int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) row[5] = 0; row[6] = 0; row[7] = 0x10; - reg_w(gspca_dev, 0x10c0, row, 8); - msleep(1); - row[0] = 0x81 | (3 << 4) | 0x02; + if (i2c_w(gspca_dev, row) < 0) + return -EIO; + row[0] = 0x81 | (2 << 4) | 0x02; row[2] = 0; - reg_w(gspca_dev, 0x10c0, row, 8); - msleep(1); - reg_r(gspca_dev, 0x10c2, 5); - *val = (gspca_dev->usb_buf[2] << 8) | gspca_dev->usb_buf[3]; + if (i2c_w(gspca_dev, row) < 0) + return -EIO; + if (reg_r(gspca_dev, 0x10c2, 5) < 0) + return -EIO; + *val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; return 0; } -- cgit v1.2.3 From e568b608a89f187c88cbbff8149047e16754aec7 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 23 Jul 2009 11:18:54 -0400 Subject: cx25840: Bugfix for no DVB-T on the Hauppauge HVR-1700 From: Steven Toth After the i2c subdev changes the ordering of initialization changed, causing a total loss of previous GPIO settings and a loss of DTV. The generic firmware loading routine has now changed to preserve GPIO values if the device is cx23885 based (safety) and I've moved the GPIO configuration from probe() into the cx23885 init func which is a little clearer and fixes the bug. Priority: normal Tested-By: Sohail Syyed Reviewed-by: Michael Krufky Signed-off-by: Steven Toth --- linux/drivers/media/video/cx25840/cx25840-core.c | 15 +++++++++------ linux/drivers/media/video/cx25840/cx25840-firmware.c | 13 ++++++++++++- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/linux/drivers/media/video/cx25840/cx25840-core.c b/linux/drivers/media/video/cx25840/cx25840-core.c index 2929bf3b7..557a3eaa1 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.c +++ b/linux/drivers/media/video/cx25840/cx25840-core.c @@ -341,6 +341,15 @@ static void cx23885_initialize(struct i2c_client *client) /* Select AFE clock pad output source */ cx25840_write(client, 0x144, 0x05); + /* Drive GPIO2 direction and values for HVR1700 + * where an onboard mux selects the output of demodulator + * vs the 417. Failure to set this results in no DTV. + * It's safe to set this across all Hauppauge boards + * currently, regardless of the board type. + */ + cx25840_write(client, 0x160, 0x1d); + cx25840_write(client, 0x164, 0x00); + /* Do the firmware load in a work handler to prevent. Otherwise the kernel is blocked waiting for the bit-banging i2c interface to finish uploading the @@ -1610,12 +1619,6 @@ static int cx25840_probe(struct i2c_client *client, state->id = id; state->rev = device_id; - if (state->is_cx23885) { - /* Drive GPIO2 direction and values */ - cx25840_write(client, 0x160, 0x1d); - cx25840_write(client, 0x164, 0x00); - } - return 0; } diff --git a/linux/drivers/media/video/cx25840/cx25840-firmware.c b/linux/drivers/media/video/cx25840/cx25840-firmware.c index f01456ec4..bdc630f0f 100644 --- a/linux/drivers/media/video/cx25840/cx25840-firmware.c +++ b/linux/drivers/media/video/cx25840/cx25840-firmware.c @@ -99,9 +99,14 @@ int cx25840_loadfw(struct i2c_client *client) const u8 *ptr; int size, retval; int MAX_BUF_SIZE = FWSEND; + u32 gpio_oe = 0, gpio_da = 0; - if (state->is_cx23885) + if (state->is_cx23885) { firmware = FWFILE_CX23885; + /* Preserve the GPIO OE and output bits */ + gpio_oe = cx25840_read(client, 0x160); + gpio_da = cx25840_read(client, 0x164); + } else if (state->is_cx231xx) firmware = FWFILE_CX231XX; @@ -143,5 +148,11 @@ int cx25840_loadfw(struct i2c_client *client) size = fw->size; release_firmware(fw); + if (state->is_cx23885) { + /* Restore GPIO configuration after f/w load */ + cx25840_write(client, 0x160, gpio_oe); + cx25840_write(client, 0x164, gpio_da); + } + return check_fw_load(client, size); } -- cgit v1.2.3 From f1f1d841250e56e65222123f52c87e33f58d2e87 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 23 Jul 2009 20:46:57 -0400 Subject: ivtv: Read buffer overflow From: Roel Kluin This mistakenly tests against sizeof(freqs) instead of the array size. Due to the mask the only illegal value possible was 3. Priority: high Signed-off-by: Roel Kluin Signed-off-by: Andy Walls --- linux/drivers/media/video/ivtv/ivtv-controls.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/video/ivtv/ivtv-controls.c b/linux/drivers/media/video/ivtv/ivtv-controls.c index a3b77ed3f..4a9c8ce0e 100644 --- a/linux/drivers/media/video/ivtv/ivtv-controls.c +++ b/linux/drivers/media/video/ivtv/ivtv-controls.c @@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include "ivtv-driver.h" #include "ivtv-cards.h" @@ -281,7 +282,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) idx = p.audio_properties & 0x03; /* The audio clock of the digitizer must match the codec sample rate otherwise you get some very strange effects. */ - if (idx < sizeof(freqs)) + if (idx < ARRAY_SIZE(freqs)) ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]); return err; } -- cgit v1.2.3 From 54a9310128f01c9c97607d99f52ced91e050e834 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 23 Jul 2009 20:51:29 -0400 Subject: cx18: Read buffer overflow From: Andy Walls This mistakenly tested against sizeof(freqs) instead of the array size. Due to the mask the only illegal value possible was 3. Reported-by: Roel Kluin Priority: high Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-controls.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/video/cx18/cx18-controls.c b/linux/drivers/media/video/cx18/cx18-controls.c index 5136df198..93f0dae01 100644 --- a/linux/drivers/media/video/cx18/cx18-controls.c +++ b/linux/drivers/media/video/cx18/cx18-controls.c @@ -20,6 +20,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ +#include #include "cx18-driver.h" #include "cx18-cards.h" @@ -317,7 +318,7 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) idx = p.audio_properties & 0x03; /* The audio clock of the digitizer must match the codec sample rate otherwise you get some very strange effects. */ - if (idx < sizeof(freqs)) + if (idx < ARRAY_SIZE(freqs)) cx18_call_all(cx, audio, s_clock_freq, freqs[idx]); return err; } -- cgit v1.2.3 From 54165c372c7e8c065545a4916acef9b54786f8c0 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Sun, 26 Jul 2009 10:06:20 -0400 Subject: cx88: HVR1300 ensure switching from Encoder to DVB-T and back is reliable From: Sohail Syyed Current tip is broken and does not switch back to DVB-T correctly Priority: high Signed-off-by: Sohail Syyed Signed-off-by: Steven Toth --- linux/drivers/media/video/cx88/cx88-mpeg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/linux/drivers/media/video/cx88/cx88-mpeg.c b/linux/drivers/media/video/cx88/cx88-mpeg.c index ba083d249..e1bcd556b 100644 --- a/linux/drivers/media/video/cx88/cx88-mpeg.c +++ b/linux/drivers/media/video/cx88/cx88-mpeg.c @@ -129,6 +129,10 @@ static int cx8802_start_dma(struct cx8802_dev *dev, udelay(100); break; case CX88_BOARD_HAUPPAUGE_HVR1300: + /* Enable MPEG parallel IO and video signal pins */ + cx_write(MO_PINMUX_IO, 0x88); + cx_write(TS_SOP_STAT, 0); + cx_write(TS_VALERR_CNTRL, 0); break; case CX88_BOARD_PINNACLE_PCTV_HD_800i: /* Enable MPEG parallel IO and video signal pins */ -- cgit v1.2.3 From edb37f2afad7a2b06f7df8873a4b4c991924a280 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 27 Jul 2009 10:43:55 +0200 Subject: gspca - vc032x: Fix mi1310_soc preview and LED. From: AceLan Kao The patches gspca - vc032x: Add resolution 1280x1024 for sensor mi1310_soc. gspca - vc032x: Webcam 041e:405b added and mi1310_soc updated. will make the preview function not work, so I disable the 1280x1024 resolution and revert back to the origin mi1310_soc settings. And, using USB snoop tool on Windows to get the correct i2c commands to turn on/off the LED flash. Priority: normal Signed-off-by: AceLan Kao Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/vc032x.c | 397 ++++++++++++++++--------------- 1 file changed, 205 insertions(+), 192 deletions(-) diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index d8d2c5dc7..85cb790fc 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -446,208 +446,92 @@ static const __u8 mi0360_initQVGA_JPG[][4] = { static const __u8 mi1310_socinitVGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, - {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x03, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x04, 0x0d, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb8, 0x01, 0x7d, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x26, 0x80, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb8, 0x00, 0x13, 0xcc}, - {0xbc, 0x00, 0x71, 0xcc}, - {0xb8, 0x81, 0x01, 0xcc}, - {0xb8, 0x2c, 0x5a, 0xcc}, - {0xb8, 0x2d, 0xff, 0xcc}, - {0xb8, 0x2e, 0xee, 0xcc}, - {0xb8, 0x2f, 0xfb, 0xcc}, - {0xb8, 0x30, 0x52, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf1, 0xcc}, - {0xb8, 0x33, 0xff, 0xcc}, - {0xb8, 0x34, 0x54, 0xcc}, - {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, - {0xb8, 0x37, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x0d, 0x00, 0x09, 0xbb}, - {0x0d, 0x00, 0x08, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x06, 0x00, 0x14, 0xbb}, - {0x3a, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0x9b, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, + {0x05, 0x00, 0x07, 0xbb}, + {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, + {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, + {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, + {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, + {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, + {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2e, 0x0c, 0x55, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, + {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x2b, 0x00, 0x28, 0xbb}, - {0x2c, 0x00, 0x30, 0xbb}, - {0x2d, 0x00, 0x30, 0xbb}, - {0x2e, 0x00, 0x28, 0xbb}, - {0x41, 0x00, 0xd7, 0xbb}, - {0x09, 0x02, 0x3a, 0xbb}, - {0x0c, 0x00, 0x00, 0xbb}, - {0x20, 0x00, 0x00, 0xbb}, - {0x05, 0x00, 0x8c, 0xbb}, - {0x06, 0x00, 0x32, 0xbb}, - {0x07, 0x00, 0xc6, 0xbb}, - {0x08, 0x00, 0x19, 0xbb}, - {0x24, 0x80, 0x6f, 0xbb}, - {0xc8, 0x00, 0x0f, 0xbb}, - {0x20, 0x00, 0x0f, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, + {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, + {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, + {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, + {0x64, 0x7b, 0x5b, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc0, 0xbb}, + {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, + {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, {0xb6, 0x03, 0x02, 0xcc}, {0xb6, 0x02, 0x80, 0xcc}, {0xb6, 0x05, 0x01, 0xcc}, {0xb6, 0x04, 0xe0, 0xcc}, - {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x25, 0xcc}, {0xb6, 0x18, 0x02, 0xcc}, {0xb6, 0x17, 0x58, 0xcc}, {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, {0xb6, 0x23, 0x0b, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x10, 0xcc}, - {0xb9, 0x12, 0x00, 0xcc}, - {0xb9, 0x13, 0x0a, 0xcc}, - {0xb9, 0x14, 0x0a, 0xcc}, - {0xb9, 0x15, 0x0a, 0xcc}, - {0xb9, 0x16, 0x0a, 0xcc}, - {0xb9, 0x18, 0x00, 0xcc}, - {0xb9, 0x19, 0x0f, 0xcc}, - {0xb9, 0x1a, 0x0f, 0xcc}, - {0xb9, 0x1b, 0x0f, 0xcc}, - {0xb9, 0x1c, 0x0f, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, - {0x03, 0x03, 0xc0, 0xbb}, - {0x06, 0x00, 0x10, 0xbb}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb8, 0x0c, 0x20, 0xcc}, - {0xb8, 0x0d, 0x70, 0xcc}, - {0xb6, 0x13, 0x13, 0xcc}, - {0x2f, 0x00, 0xC0, 0xbb}, - {0xb8, 0xa0, 0x12, 0xcc}, - {}, -}; -static const __u8 mi1310_socinitQVGA_JPG[][4] = { - {0xb0, 0x03, 0x19, 0xcc}, - {0xb0, 0x04, 0x02, 0xcc}, - {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x03, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x35, 0xdd, 0xcc}, - {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x04, 0x0d, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb8, 0x01, 0x7d, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x26, 0x80, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb8, 0x00, 0x13, 0xcc}, - {0xbc, 0x00, 0xd1, 0xcc}, - {0xb8, 0x81, 0x01, 0xcc}, - {0xb8, 0x2c, 0x5a, 0xcc}, - {0xb8, 0x2d, 0xff, 0xcc}, - {0xb8, 0x2e, 0xee, 0xcc}, - {0xb8, 0x2f, 0xfb, 0xcc}, - {0xb8, 0x30, 0x52, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf1, 0xcc}, - {0xb8, 0x33, 0xff, 0xcc}, - {0xb8, 0x34, 0x54, 0xcc}, - {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, - {0xb8, 0x37, 0x00, 0xcc}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x0d, 0x00, 0x09, 0xbb}, - {0x0d, 0x00, 0x08, 0xbb}, - {0xf0, 0x00, 0x01, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x06, 0x00, 0x14, 0xbb}, - {0x3a, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0x9b, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x2b, 0x00, 0x28, 0xbb}, - {0x2c, 0x00, 0x30, 0xbb}, - {0x2d, 0x00, 0x30, 0xbb}, - {0x2e, 0x00, 0x28, 0xbb}, - {0x41, 0x00, 0xd7, 0xbb}, - {0x09, 0x02, 0x3a, 0xbb}, - {0x0c, 0x00, 0x00, 0xbb}, - {0x20, 0x00, 0x00, 0xbb}, - {0x05, 0x00, 0x8c, 0xbb}, - {0x06, 0x00, 0x32, 0xbb}, - {0x07, 0x00, 0xc6, 0xbb}, - {0x08, 0x00, 0x19, 0xbb}, - {0x24, 0x80, 0x6f, 0xbb}, - {0xc8, 0x00, 0x0f, 0xbb}, - {0x20, 0x00, 0x0f, 0xbb}, - {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x01, 0xcc}, - {0xb6, 0x02, 0x40, 0xcc}, - {0xb6, 0x05, 0x00, 0xcc}, - {0xb6, 0x04, 0xf0, 0xcc}, - {0xb6, 0x12, 0x78, 0xcc}, - {0xb6, 0x18, 0x00, 0xcc}, - {0xb6, 0x17, 0x96, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, - {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, - {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x10, 0xcc}, - {0xb9, 0x12, 0x00, 0xcc}, - {0xb9, 0x13, 0x0a, 0xcc}, - {0xb9, 0x14, 0x0a, 0xcc}, - {0xb9, 0x15, 0x0a, 0xcc}, - {0xb9, 0x16, 0x0a, 0xcc}, - {0xb9, 0x18, 0x00, 0xcc}, - {0xb9, 0x19, 0x0f, 0xcc}, - {0xb9, 0x1a, 0x0f, 0xcc}, - {0xb9, 0x1b, 0x0f, 0xcc}, - {0xb9, 0x1c, 0x0f, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, {0xbc, 0x04, 0x18, 0xcc}, @@ -658,15 +542,130 @@ static const __u8 mi1310_socinitQVGA_JPG[][4] = { {0xbc, 0x0a, 0x10, 0xcc}, {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, + {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, + {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, + {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, + {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, + {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, + {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, + {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, + {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, + {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, + {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, + {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, + {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, + {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, + {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, + {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, + {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, + {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xf4, 0x8e, 0xbb}, + {0x00, 0x00, 0x50, 0xdd}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x24, 0x50, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x34, 0x0c, 0x50, 0xbb}, {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, + {}, +}; +static const __u8 mi1310_socinitQVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, {0xbc, 0x00, 0xf0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x2e, 0x0c, 0x55, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, {0x64, 0x7b, 0x5b, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc0, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x25, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0xf4, 0x8e, 0xbb}, + {0x00, 0x00, 0x50, 0xdd}, {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x24, 0x50, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x34, 0x0c, 0x50, 0xbb}, + {0xb3, 0x01, 0x41, 0xcc}, {0xf0, 0x00, 0x00, 0xbb}, {0x03, 0x03, 0xc0, 0xbb}, - {0x06, 0x00, 0x10, 0xbb}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb8, 0x0c, 0x20, 0xcc}, - {0xb8, 0x0d, 0x70, 0xcc}, - {0xb6, 0x13, 0x13, 0xcc}, - {0x2f, 0x00, 0xC0, 0xbb}, - {0xb8, 0xa0, 0x12, 0xcc}, {}, }; static const u8 mi1310_soc_InitSXGA_JPG[][4] = { @@ -2982,7 +2981,8 @@ static int sd_config(struct gspca_dev *gspca_dev, break; case SENSOR_MI1310_SOC: cam->cam_mode = vc0323_mode; - cam->nmodes = ARRAY_SIZE(vc0323_mode); + /* TODO: 1280x1024 resolution setting is incorrect, so don't export it now*/ + cam->nmodes = ARRAY_SIZE(vc0323_mode) - 1; break; case SENSOR_MI1320_SOC: cam->cam_mode = bi_mode; @@ -3217,15 +3217,20 @@ static int sd_start(struct gspca_dev *gspca_dev) put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c); /* set the led on 0x0892 0x0896 */ - if (sd->sensor != SENSOR_PO1200) { - reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); + if (sd->sensor == SENSOR_PO1200) { + setsharpness(gspca_dev); + sethvflip(gspca_dev); + reg_w(gspca_dev->dev, 0x89, 0x0400, 0x1415); + } else if (sd->sensor == SENSOR_MI1310_SOC) { + reg_w(gspca_dev->dev, 0x89, 0x058c, 0x0000); msleep(100); sethvflip(gspca_dev); setlightfreq(gspca_dev); } else { - setsharpness(gspca_dev); + reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); + msleep(100); sethvflip(gspca_dev); - reg_w(gspca_dev->dev, 0x89, 0x0400, 0x1415); + setlightfreq(gspca_dev); } } return 0; @@ -3234,8 +3239,12 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; + struct sd *sd = (struct sd *) gspca_dev; - reg_w(dev, 0x89, 0xffff, 0xffff); + if( sd->sensor == SENSOR_MI1310_SOC) + reg_w(dev, 0x89, 0x058c, 0x00ff); + else + reg_w(dev, 0x89, 0xffff, 0xffff); reg_w(dev, 0xa0, 0x01, 0xb301); reg_w(dev, 0xa0, 0x09, 0xb003); } @@ -3244,10 +3253,14 @@ static void sd_stopN(struct gspca_dev *gspca_dev) static void sd_stop0(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; + struct sd *sd = (struct sd *) gspca_dev; if (!gspca_dev->present) return; - reg_w(dev, 0x89, 0xffff, 0xffff); + if( sd->sensor == SENSOR_MI1310_SOC) + reg_w(dev, 0x89, 0x058c, 0x00ff); + else + reg_w(dev, 0x89, 0xffff, 0xffff); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, -- cgit v1.2.3 From 5ff3ce2a04c7a20cc6190c1dd5dd200cb551bbea Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 27 Jul 2009 10:52:27 +0200 Subject: gspca - vc032x: Add the 1280x960 resolution for sensor mi1310_soc. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/vc032x.c | 245 +++++++++++++++++++++---------- 1 file changed, 165 insertions(+), 80 deletions(-) diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index 85cb790fc..6fe1c77d6 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -153,9 +153,9 @@ static const struct v4l2_pix_format vc0323_mode[] = { .sizeimage = 640 * 480 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, - {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi13x0_soc only */ + {1280, 960, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi1310_soc only */ .bytesperline = 1280, - .sizeimage = 1280 * 1024 * 1 / 4 + 590, + .sizeimage = 1280 * 960 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 2}, }; @@ -671,108 +671,194 @@ static const __u8 mi1310_socinitQVGA_JPG[][4] = { static const u8 mi1310_soc_InitSXGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, - {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x0d, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x04, 0xcc}, - {0xb3, 0x23, 0x00, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc}, {0xb3, 0x17, 0xff, 0xcc}, - {0xb8, 0x01, 0x7d, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x26, 0x80, 0xcc}, - {0xb8, 0x06, 0x00, 0xcc}, - {0xb8, 0x07, 0x05, 0xcc}, - {0xb8, 0x08, 0x00, 0xcc}, - {0xb8, 0x09, 0x04, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb8, 0x00, 0x11, 0xcc}, - {0xbc, 0x00, 0x71, 0xcc}, - {0xb8, 0x81, 0x01, 0xcc}, - {0xb8, 0x2c, 0x5a, 0xcc}, - {0xb8, 0x2d, 0xff, 0xcc}, - {0xb8, 0x2e, 0xee, 0xcc}, - {0xb8, 0x2f, 0xfb, 0xcc}, - {0xb8, 0x30, 0x52, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf1, 0xcc}, - {0xb8, 0x33, 0xff, 0xcc}, - {0xb8, 0x34, 0x54, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0x70, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x0d, 0x00, 0x09, 0xbb}, - {0x0d, 0x00, 0x08, 0xbb}, + {0x20, 0x03, 0x03, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x06, 0x00, 0x14, 0xbb}, - {0x3a, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0x9b, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, + {0x05, 0x00, 0x07, 0xbb}, + {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, + {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, + {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, + {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, + {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, + {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2e, 0x0c, 0x60, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, + {0x37, 0x01, 0x40, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x2b, 0x00, 0x28, 0xbb}, - {0x2c, 0x00, 0x30, 0xbb}, - {0x2d, 0x00, 0x30, 0xbb}, - {0x2e, 0x00, 0x28, 0xbb}, - {0x41, 0x00, 0xd7, 0xbb}, - {0x09, 0x02, 0x3a, 0xbb}, - {0x0c, 0x00, 0x00, 0xbb}, - {0x20, 0x00, 0x00, 0xbb}, - {0x05, 0x00, 0x8c, 0xbb}, - {0x06, 0x00, 0x32, 0xbb}, - {0x07, 0x00, 0xc6, 0xbb}, - {0x08, 0x00, 0x19, 0xbb}, - {0x24, 0x80, 0x6f, 0xbb}, - {0xc8, 0x00, 0x0f, 0xbb}, - {0x20, 0x00, 0x03, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, + {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, + {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, + {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, + {0x64, 0x7b, 0x5b, 0xbb}, {0xb6, 0x00, 0x00, 0xcc}, {0xb6, 0x03, 0x05, 0xcc}, {0xb6, 0x02, 0x00, 0xcc}, - {0xb6, 0x05, 0x04, 0xcc}, - {0xb6, 0x04, 0x00, 0xcc}, + {0xb6, 0x05, 0x03, 0xcc}, + {0xb6, 0x04, 0xc0, 0xcc}, {0xb6, 0x12, 0xf8, 0xcc}, - {0xb6, 0x18, 0x0a, 0xcc}, - {0xb6, 0x17, 0x00, 0xcc}, + {0xb6, 0x13, 0x29, 0xcc}, + {0xb6, 0x18, 0x09, 0xcc}, + {0xb6, 0x17, 0x60, 0xcc}, {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, {0xb6, 0x23, 0x0b, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x10, 0xcc}, - {0xb9, 0x12, 0x00, 0xcc}, - {0xb9, 0x13, 0x14, 0xcc}, - {0xb9, 0x14, 0x14, 0xcc}, - {0xb9, 0x15, 0x14, 0xcc}, - {0xb9, 0x16, 0x14, 0xcc}, - {0xb9, 0x18, 0x00, 0xcc}, - {0xb9, 0x19, 0x1e, 0xcc}, - {0xb9, 0x1a, 0x1e, 0xcc}, - {0xb9, 0x1b, 0x1e, 0xcc}, - {0xb9, 0x1c, 0x1e, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb8, 0x0c, 0x20, 0xcc}, - {0xb8, 0x0d, 0x70, 0xcc}, - {0xb6, 0x13, 0x13, 0xcc}, - {0x2f, 0x00, 0xC0, 0xbb}, - {0xb8, 0xa0, 0x12, 0xcc}, + {0x00, 0x00, 0x80, 0xdd}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0x22, 0xa0, 0x78, 0xbb}, + {0x23, 0xa0, 0x78, 0xbb}, + {0x24, 0x7f, 0x00, 0xbb}, + {0x28, 0xea, 0x02, 0xbb}, + {0x29, 0x86, 0x7a, 0xbb}, + {0x5e, 0x52, 0x4c, 0xbb}, + {0x5f, 0x20, 0x24, 0xbb}, + {0x60, 0x00, 0x02, 0xbb}, + {0x02, 0x00, 0xee, 0xbb}, + {0x03, 0x39, 0x23, 0xbb}, + {0x04, 0x07, 0x24, 0xbb}, + {0x09, 0x00, 0xc0, 0xbb}, + {0x0a, 0x00, 0x79, 0xbb}, + {0x0b, 0x00, 0x04, 0xbb}, + {0x0c, 0x00, 0x5c, 0xbb}, + {0x0d, 0x00, 0xd9, 0xbb}, + {0x0e, 0x00, 0x53, 0xbb}, + {0x0f, 0x00, 0x21, 0xbb}, + {0x10, 0x00, 0xa4, 0xbb}, + {0x11, 0x00, 0xe5, 0xbb}, + {0x15, 0x00, 0x00, 0xbb}, + {0x16, 0x00, 0x00, 0xbb}, + {0x17, 0x00, 0x00, 0xbb}, + {0x18, 0x00, 0x00, 0xbb}, + {0x19, 0x00, 0x00, 0xbb}, + {0x1a, 0x00, 0x00, 0xbb}, + {0x1b, 0x00, 0x00, 0xbb}, + {0x1c, 0x00, 0x00, 0xbb}, + {0x1d, 0x00, 0x00, 0xbb}, + {0x1e, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x06, 0xf0, 0x8e, 0xbb}, + {0x00, 0x00, 0x80, 0xdd}, + {0x06, 0x70, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x5e, 0x6a, 0x53, 0xbb}, + {0x5f, 0x40, 0x2c, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x58, 0x00, 0x00, 0xbb}, + {0x53, 0x09, 0x03, 0xbb}, + {0x54, 0x31, 0x18, 0xbb}, + {0x55, 0x8b, 0x5f, 0xbb}, + {0x56, 0xc0, 0xa9, 0xbb}, + {0x57, 0xe0, 0xd2, 0xbb}, + {0xe1, 0x00, 0x00, 0xbb}, + {0xdc, 0x09, 0x03, 0xbb}, + {0xdd, 0x31, 0x18, 0xbb}, + {0xde, 0x8b, 0x5f, 0xbb}, + {0xdf, 0xc0, 0xa9, 0xbb}, + {0xe0, 0xe0, 0xd2, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xf0, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x24, 0x50, 0x20, 0xbb}, + {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, + {0xbc, 0x10, 0xc0, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x34, 0x0c, 0x50, 0xbb}, + {0xbc, 0x11, 0x03, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, + {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, + {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, + {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, + {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, + {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, + {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, + {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, + {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, + {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, + {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, + {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, + {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, + {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, + {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, + {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, + {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, + {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, {} }; @@ -2981,8 +3067,7 @@ static int sd_config(struct gspca_dev *gspca_dev, break; case SENSOR_MI1310_SOC: cam->cam_mode = vc0323_mode; - /* TODO: 1280x1024 resolution setting is incorrect, so don't export it now*/ - cam->nmodes = ARRAY_SIZE(vc0323_mode) - 1; + cam->nmodes = ARRAY_SIZE(vc0323_mode); break; case SENSOR_MI1320_SOC: cam->cam_mode = bi_mode; -- cgit v1.2.3 From 21625be118a5ac52bdd1fe150079ab19e931c02c Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 27 Jul 2009 11:00:03 +0200 Subject: gspca - vc032x: H and V flip controls added for mi13x0_soc sensors. From: Jean-Francois Moine Also, H/V flip default values adjusted according to the webcam IDs. Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/vc032x.c | 111 ++++++++++++++++++------------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index 6fe1c77d6..97b9d677c 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -52,7 +52,10 @@ struct sd { #define SENSOR_OV7670 6 #define SENSOR_PO1200 7 #define SENSOR_PO3130NC 8 - u8 ninput; /* != 0 when 2 sensors - SamsungQ1 */ + u8 flags; +#define FL_SAMSUNG 0x01 /* SamsungQ1 (2 sensors) */ +#define FL_HFLIP 0x02 /* mirrored by default */ +#define FL_VFLIP 0x04 /* vertical flipped by default */ }; /* V4L2 controls supported by the driver */ @@ -66,7 +69,7 @@ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { -/* next 2 controls work with ov7660 and ov7670 only */ +/* next 2 controls work with some sensors only */ #define HFLIP_IDX 0 { { @@ -474,7 +477,7 @@ static const __u8 mi1310_socinitVGA_JPG[][4] = { {0x5b, 0x00, 0x01, 0xbb}, {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x20, 0x03, 0x02, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ {0xf0, 0x00, 0x01, 0xbb}, {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb}, @@ -611,7 +614,8 @@ static const __u8 mi1310_socinitQVGA_JPG[][4] = { {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x02, 0xbb}, {0xc8, 0x9f, 0x0b, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x20, 0x03, 0x02, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ + {0xf0, 0x00, 0x01, 0xbb}, {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb}, {0x35, 0xff, 0x00, 0xbb}, {0xdc, 0x07, 0x02, 0xbb}, {0xdd, 0x3c, 0x18, 0xbb}, {0xde, 0x92, 0x6d, 0xbb}, @@ -698,7 +702,11 @@ static const u8 mi1310_soc_InitSXGA_JPG[][4] = { {0xc8, 0x9f, 0x0b, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x20, 0x03, 0x03, 0xbb}, +#if 1 + {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ +#else + {0x20, 0x03, 0x03, 0xbb}, /* h/v flip */ +#endif {0xf0, 0x00, 0x01, 0xbb}, {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb}, @@ -1055,7 +1063,7 @@ static const u8 mi1320_soc_InitVGA[][4] = { {0x07, 0x00, 0xe0, 0xbb}, {0x08, 0x00, 0x0b, 0xbb}, {0x21, 0x00, 0x0c, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc}, {0xbf, 0xcc, 0x04, 0xcc}, @@ -1065,7 +1073,7 @@ static const u8 mi1320_soc_InitVGA[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1159,7 +1167,7 @@ static const u8 mi1320_soc_InitVGA_JPG[][4] = { {0x07, 0x00, 0xe0, 0xbb}, {0x08, 0x00, 0x0b, 0xbb}, {0x21, 0x00, 0x0c, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0xb6, 0x00, 0x00, 0xcc}, {0xb6, 0x03, 0x02, 0xcc}, {0xb6, 0x02, 0x80, 0xcc}, @@ -1181,7 +1189,7 @@ static const u8 mi1320_soc_InitVGA_JPG[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1275,7 +1283,7 @@ static const u8 mi1320_soc_InitQVGA[][4] = { {0x07, 0x00, 0xe0, 0xbb}, {0x08, 0x00, 0x0b, 0xbb}, {0x21, 0x00, 0x0c, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc}, {0xbf, 0xcc, 0x04, 0xcc}, @@ -1295,7 +1303,7 @@ static const u8 mi1320_soc_InitQVGA[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1389,7 +1397,7 @@ static const u8 mi1320_soc_InitQVGA_JPG[][4] = { {0x07, 0x00, 0xe0, 0xbb}, {0x08, 0x00, 0x0b, 0xbb}, {0x21, 0x00, 0x0c, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0xb6, 0x00, 0x00, 0xcc}, {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc}, @@ -1421,7 +1429,7 @@ static const u8 mi1320_soc_InitQVGA_JPG[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1513,7 +1521,7 @@ static const u8 mi1320_soc_InitSXGA_JPG[][4] = { {0x00, 0x00, 0x20, 0xdd}, {0xf0, 0x00, 0x00, 0xbb}, {0x00, 0x00, 0x30, 0xdd}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x00, 0x00, 0x20, 0xdd}, {0xb6, 0x00, 0x00, 0xcc}, {0xb6, 0x03, 0x05, 0xcc}, @@ -1537,7 +1545,7 @@ static const u8 mi1320_soc_InitSXGA_JPG[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1595,7 +1603,7 @@ static const u8 mi1320_soc_InitSXGA_JPG[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb}, {0x08, 0x00, 0x27, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1651,7 +1659,7 @@ static const u8 mi1320_soc_InitSXGA[][4] = { {0x00, 0x00, 0x20, 0xdd}, {0xf0, 0x00, 0x00, 0xbb}, {0x00, 0x00, 0x30, 0xdd}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x00, 0x00, 0x20, 0xdd}, {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc}, @@ -1662,7 +1670,7 @@ static const u8 mi1320_soc_InitSXGA[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1720,7 +1728,7 @@ static const u8 mi1320_soc_InitSXGA[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb}, {0x08, 0x00, 0x27, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -2877,7 +2885,7 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) const struct sensor_info *ptsensor_info; /*fixme: should also check the other sensor (back mi1320_soc, front mc501cb)*/ - if (sd->ninput != 0) { + if (sd->flags & FL_SAMSUNG) { reg_w(dev, 0xa0, 0x01, 0xb301); #if 1 reg_w(dev, 0x89, 0xf0ff, 0xffff); /* select the back sensor */ @@ -3016,7 +3024,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; sd->bridge = id->driver_info >> 8; - sd->ninput = id->driver_info & 0xff; + sd->flags = id->driver_info & 0xff; #if 0 vc0321_reset(gspca_dev); #endif @@ -3072,8 +3080,6 @@ static int sd_config(struct gspca_dev *gspca_dev, case SENSOR_MI1320_SOC: cam->cam_mode = bi_mode; cam->nmodes = ARRAY_SIZE(bi_mode); - cam->input_flags = V4L2_IN_ST_VFLIP | - V4L2_IN_ST_HFLIP; break; default: cam->cam_mode = vc0323_mode; @@ -3085,14 +3091,14 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->hflip = HFLIP_DEF; sd->vflip = VFLIP_DEF; - if (sd->sensor == SENSOR_OV7670) { - sd->hflip = 1; - sd->vflip = 1; - } + if (sd->sensor == SENSOR_OV7670) + sd->flags |= FL_HFLIP | FL_VFLIP; sd->lightfreq = FREQ_DEF; if (sd->sensor != SENSOR_OV7670) gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX); switch (sd->sensor) { + case SENSOR_MI1310_SOC: + case SENSOR_MI1320_SOC: case SENSOR_OV7660: case SENSOR_OV7670: case SENSOR_PO1200: @@ -3121,33 +3127,44 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -/* for OV7660 and OV7670 only */ +/* some sensors only */ static void sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 data; - + u8 data[2], hflip, vflip; + + hflip = sd->hflip; + if (sd->flags & FL_HFLIP) + hflip != hflip; + vflip = sd->vflip; + if (sd->flags & FL_VFLIP) + vflip != vflip; switch (sd->sensor) { - case SENSOR_OV7660: - data = 1; + case SENSOR_MI1310_SOC: + case SENSOR_MI1320_SOC: + data[0] = data[1] = 0; /* select page 0 */ + i2c_write(gspca_dev, 0xf0, data, 2); + data[0] = sd->sensor == SENSOR_MI1310_SOC ? 0x03 : 0x01; + data[1] = 0x02 * hflip + | 0x01 * vflip; + i2c_write(gspca_dev, 0x20, data, 2); break; + case SENSOR_OV7660: case SENSOR_OV7670: - data = 7; + data[0] = sd->sensor == SENSOR_OV7660 ? 0x01 : 0x07; + data[0] |= OV7660_MVFP_MIRROR * hflip + | OV7660_MVFP_VFLIP * vflip; + i2c_write(gspca_dev, OV7660_REG_MVFP, data, 1); break; case SENSOR_PO1200: - data = 0; - i2c_write(gspca_dev, 0x03, &data, 1); - data = 0x80 * sd->hflip - | 0x40 * sd->vflip + data[0] = 0; + i2c_write(gspca_dev, 0x03, data, 1); + data[0] = 0x80 * hflip + | 0x40 * vflip | 0x06; - i2c_write(gspca_dev, 0x1e, &data, 1); - return; - default: - return; + i2c_write(gspca_dev, 0x1e, data, 1); + break; } - data |= OV7660_MVFP_MIRROR * sd->hflip - | OV7660_MVFP_VFLIP * sd->vflip; - i2c_write(gspca_dev, OV7660_REG_MVFP, &data, 1); } static void setlightfreq(struct gspca_dev *gspca_dev) @@ -3194,7 +3211,7 @@ static int sd_start(struct gspca_dev *gspca_dev) }; /*fixme: back sensor only*/ - if (sd->ninput != 0) { + if (sd->flags & FL_SAMSUNG) { reg_w(gspca_dev->dev, 0x89, 0xf0ff, 0xffff); reg_w(gspca_dev->dev, 0xa9, 0x8348, 0x000e); reg_w(gspca_dev->dev, 0xa9, 0x0000, 0x001a); @@ -3488,7 +3505,7 @@ static const struct sd_desc sd_desc = { .driver_info = (BRIDGE_ ## bridge << 8) \ | (flags) static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x405b), BF(VC0323, 0)}, + {USB_DEVICE(0x041e, 0x405b), BF(VC0323, FL_VFLIP)}, {USB_DEVICE(0x046d, 0x0892), BF(VC0321, 0)}, {USB_DEVICE(0x046d, 0x0896), BF(VC0321, 0)}, {USB_DEVICE(0x046d, 0x0897), BF(VC0321, 0)}, @@ -3497,7 +3514,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0ac8, 0x0328), BF(VC0321, 0)}, {USB_DEVICE(0x0ac8, 0xc001), BF(VC0321, 0)}, {USB_DEVICE(0x0ac8, 0xc002), BF(VC0321, 0)}, - {USB_DEVICE(0x0ac8, 0xc301), BF(VC0323, 1)}, + {USB_DEVICE(0x0ac8, 0xc301), BF(VC0323, FL_SAMSUNG)}, {USB_DEVICE(0x15b8, 0x6001), BF(VC0323, 0)}, {USB_DEVICE(0x15b8, 0x6002), BF(VC0323, 0)}, {USB_DEVICE(0x17ef, 0x4802), BF(VC0323, 0)}, -- cgit v1.2.3 From c5009fa8355db0bbf3316c237eda11f593344e75 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 27 Jul 2009 11:28:37 +0200 Subject: gspca - vc032x: Cleanup source. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/vc032x.c | 122 +++++++++++++++---------------- 1 file changed, 57 insertions(+), 65 deletions(-) diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index 97b9d677c..44dea7949 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -32,14 +32,14 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - __u8 hflip; - __u8 vflip; - __u8 lightfreq; - __u8 sharpness; + u8 hflip; + u8 vflip; + u8 lightfreq; + u8 sharpness; u8 image_offset; - char bridge; + u8 bridge; #define BRIDGE_VC0321 0 #define BRIDGE_VC0323 1 u8 sensor; @@ -213,11 +213,11 @@ static const struct v4l2_pix_format svga_mode[] = { #define OV7660_MVFP_MIRROR 0x20 #define OV7660_MVFP_VFLIP 0x10 -static const __u8 mi0360_matrix[9] = { +static const u8 mi0360_matrix[9] = { 0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50 }; -static const __u8 mi0360_initVGA_JPG[][4] = { +static const u8 mi0360_initVGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -326,7 +326,7 @@ static const __u8 mi0360_initVGA_JPG[][4] = { {0xb3, 0x5c, 0x01, 0xcc}, {} }; -static const __u8 mi0360_initQVGA_JPG[][4] = { +static const u8 mi0360_initQVGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -446,7 +446,7 @@ static const __u8 mi0360_initQVGA_JPG[][4] = { {} }; -static const __u8 mi1310_socinitVGA_JPG[][4] = { +static const u8 mi1310_socinitVGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, {0xb3, 0x00, 0x64, 0xcc}, @@ -598,7 +598,7 @@ static const __u8 mi1310_socinitVGA_JPG[][4] = { {0x03, 0x03, 0xc0, 0xbb}, {}, }; -static const __u8 mi1310_socinitQVGA_JPG[][4] = { +static const u8 mi1310_socinitQVGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x00, 0xcc}, @@ -870,14 +870,14 @@ static const u8 mi1310_soc_InitSXGA_JPG[][4] = { {} }; -static const __u8 mi1320_gamma[17] = { +static const u8 mi1320_gamma[17] = { 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff }; -static const __u8 mi1320_matrix[9] = { +static const u8 mi1320_matrix[9] = { 0x54, 0xda, 0x06, 0xf1, 0x50, 0xf4, 0xf7, 0xea, 0x52 }; -static const __u8 mi1320_initVGA_data[][4] = { +static const u8 mi1320_initVGA_data[][4] = { {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, @@ -956,7 +956,7 @@ static const __u8 mi1320_initVGA_data[][4] = { {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, {} }; -static const __u8 mi1320_initQVGA_data[][4] = { +static const u8 mi1320_initQVGA_data[][4] = { {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, @@ -1747,15 +1747,15 @@ static const u8 mi1320_soc_InitSXGA[][4] = { {0x64, 0x5e, 0x1c, 0xbb}, {} }; -static const __u8 po3130_gamma[17] = { +static const u8 po3130_gamma[17] = { 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff }; -static const __u8 po3130_matrix[9] = { +static const u8 po3130_matrix[9] = { 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 }; -static const __u8 po3130_initVGA_data[][4] = { +static const u8 po3130_initVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -1838,7 +1838,7 @@ static const __u8 po3130_initVGA_data[][4] = { {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, {} }; -static const __u8 po3130_rundata[][4] = { +static const u8 po3130_rundata[][4] = { {0x00, 0x47, 0x45, 0xaa}, {0x00, 0x48, 0x9b, 0xaa}, {0x00, 0x49, 0x3a, 0xaa}, {0x00, 0x4a, 0x01, 0xaa}, {0x00, 0x44, 0x40, 0xaa}, @@ -1853,7 +1853,7 @@ static const __u8 po3130_rundata[][4] = { {} }; -static const __u8 po3130_initQVGA_data[][4] = { +static const u8 po3130_initQVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x09, 0xcc}, {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -1939,16 +1939,16 @@ static const __u8 po3130_initQVGA_data[][4] = { {} }; -static const __u8 hv7131r_gamma[17] = { +static const u8 hv7131r_gamma[17] = { /* 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, * 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff */ 0x04, 0x1a, 0x36, 0x55, 0x6f, 0x87, 0x9d, 0xb0, 0xc1, 0xcf, 0xda, 0xe4, 0xec, 0xf3, 0xf8, 0xfd, 0xff }; -static const __u8 hv7131r_matrix[9] = { +static const u8 hv7131r_matrix[9] = { 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 }; -static const __u8 hv7131r_initVGA_data[][4] = { +static const u8 hv7131r_initVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -1991,7 +1991,7 @@ static const __u8 hv7131r_initVGA_data[][4] = { {} }; -static const __u8 hv7131r_initQVGA_data[][4] = { +static const u8 hv7131r_initQVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -2046,14 +2046,14 @@ static const __u8 hv7131r_initQVGA_data[][4] = { {} }; -static const __u8 ov7660_gamma[17] = { +static const u8 ov7660_gamma[17] = { 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff }; -static const __u8 ov7660_matrix[9] = { +static const u8 ov7660_matrix[9] = { 0x5a, 0xf0, 0xf6, 0xf3, 0x57, 0xf6, 0xf3, 0xef, 0x62 }; -static const __u8 ov7660_initVGA_data[][4] = { +static const u8 ov7660_initVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, @@ -2111,7 +2111,7 @@ static const __u8 ov7660_initVGA_data[][4] = { {0x00, 0x29, 0x3c, 0xaa}, {0xb3, 0x01, 0x45, 0xcc}, {} }; -static const __u8 ov7660_initQVGA_data[][4] = { +static const u8 ov7660_initQVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, @@ -2180,26 +2180,26 @@ static const __u8 ov7660_initQVGA_data[][4] = { {} }; -static const __u8 ov7660_50HZ[][4] = { +static const u8 ov7660_50HZ[][4] = { {0x00, 0x3b, 0x08, 0xaa}, {0x00, 0x9d, 0x40, 0xaa}, {0x00, 0x13, 0xa7, 0xaa}, {} }; -static const __u8 ov7660_60HZ[][4] = { +static const u8 ov7660_60HZ[][4] = { {0x00, 0x3b, 0x00, 0xaa}, {0x00, 0x9e, 0x40, 0xaa}, {0x00, 0x13, 0xa7, 0xaa}, {} }; -static const __u8 ov7660_NoFliker[][4] = { +static const u8 ov7660_NoFliker[][4] = { {0x00, 0x13, 0x87, 0xaa}, {} }; -static const __u8 ov7670_initVGA_JPG[][4] = { +static const u8 ov7670_initVGA_JPG[][4] = { {0xb3, 0x01, 0x05, 0xcc}, {0x00, 0x00, 0x30, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, @@ -2329,7 +2329,7 @@ static const __u8 ov7670_initVGA_JPG[][4] = { {}, }; -static const __u8 ov7670_initQVGA_JPG[][4] = { +static const u8 ov7670_initQVGA_JPG[][4] = { {0xb3, 0x01, 0x05, 0xcc}, {0x00, 0x00, 0x30, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, @@ -2464,7 +2464,7 @@ static const __u8 ov7670_initQVGA_JPG[][4] = { }; /* PO1200 - values from usbvm326.inf and ms-win trace */ -static const __u8 po1200_gamma[17] = { +static const u8 po1200_gamma[17] = { #if 1 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff @@ -2474,10 +2474,10 @@ static const __u8 po1200_gamma[17] = { 0xc1, 0xd0, 0xdd, 0xe8, 0xf2, 0xf9, 0xff, 0xff #endif }; -static const __u8 po1200_matrix[9] = { +static const u8 po1200_matrix[9] = { 0x60, 0xf9, 0xe5, 0xe7, 0x50, 0x05, 0xf3, 0xe6, 0x5e }; -static const __u8 po1200_initVGA_data[][4] = { +static const u8 po1200_initVGA_data[][4] = { {0xb0, 0x03, 0x19, 0xcc}, /* reset? */ #if 0 {0x00, 0x00, 0x64, 0xdd}, @@ -2812,9 +2812,9 @@ static const struct sensor_info sensor_info_data[] = { /* read 'len' bytes in gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, - __u16 req, - __u16 index, - __u16 len) + u16 req, + u16 index, + u16 len) { usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), @@ -2826,9 +2826,9 @@ static void reg_r(struct gspca_dev *gspca_dev, } static void reg_w(struct usb_device *dev, - __u16 req, - __u16 value, - __u16 index) + u16 req, + u16 value, + u16 index) { usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -2953,17 +2953,17 @@ static void i2c_write(struct gspca_dev *gspca_dev, } static void put_tab_to_reg(struct gspca_dev *gspca_dev, - const __u8 *tab, __u8 tabsize, __u16 addr) + const u8 *tab, u8 tabsize, u16 addr) { int j; - __u16 ad = addr; + u16 ad = addr; for (j = 0; j < tabsize; j++) reg_w(gspca_dev->dev, 0xa0, tab[j], ad++); } static void usb_exchange(struct gspca_dev *gspca_dev, - const __u8 data[][4]) + const u8 data[][4]) { struct usb_device *dev = gspca_dev->dev; int i = 0; @@ -3170,7 +3170,7 @@ static void sethvflip(struct gspca_dev *gspca_dev) static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - static const __u8 (*ov7660_freq_tb[3])[4] = + static const u8 (*ov7660_freq_tb[3])[4] = {ov7660_NoFliker, ov7660_50HZ, ov7660_60HZ}; if (sd->sensor != SENSOR_OV7660) @@ -3182,7 +3182,7 @@ static void setlightfreq(struct gspca_dev *gspca_dev) static void setsharpness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 data; + u8 data; if (sd->sensor != SENSOR_PO1200) return; @@ -3195,9 +3195,9 @@ static void setsharpness(struct gspca_dev *gspca_dev) static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - const __u8 (*init)[4]; - const __u8 *GammaT = NULL; - const __u8 *MatrixT = NULL; + const u8 (*init)[4]; + const u8 *GammaT = NULL; + const u8 *MatrixT = NULL; int mode; static const u8 (*mi1320_soc_init[])[4] = { mi1320_soc_InitSXGA, @@ -3343,7 +3343,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) struct usb_device *dev = gspca_dev->dev; struct sd *sd = (struct sd *) gspca_dev; - if( sd->sensor == SENSOR_MI1310_SOC) + if (sd->sensor == SENSOR_MI1310_SOC) reg_w(dev, 0x89, 0x058c, 0x00ff); else reg_w(dev, 0x89, 0xffff, 0xffff); @@ -3359,7 +3359,8 @@ static void sd_stop0(struct gspca_dev *gspca_dev) if (!gspca_dev->present) return; - if( sd->sensor == SENSOR_MI1310_SOC) +/*fixme: is this useful?*/ + if (sd->sensor == SENSOR_MI1310_SOC) reg_w(dev, 0x89, 0x058c, 0x00ff); else reg_w(dev, 0x89, 0xffff, 0xffff); @@ -3367,7 +3368,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso pkt length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -3467,21 +3468,12 @@ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu) { + static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"}; + switch (menu->id) { case V4L2_CID_POWER_LINE_FREQUENCY: - switch (menu->index) { - case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ - strcpy((char *) menu->name, "NoFliker"); - return 0; - case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ - strcpy((char *) menu->name, "50 Hz"); - return 0; - default: -/* case 2: * V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ - strcpy((char *) menu->name, "60 Hz"); - return 0; - } - break; + strcpy((char *) menu->name, freq_nm[menu->index]); + return 0; } return -EINVAL; } -- cgit v1.2.3 From 09967917f95eee6651fb685695f91598840e114b Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 27 Jul 2009 16:35:59 -0400 Subject: au0828: fix typo: dvb uses bulk xfer, dont say isoc in debug From: Michael Krufky The au0828-dvb driver uses bulk usb transfers for digital transport, but the debug reports, "iso xfer already running!\n". Fix this to report bulk instead of isoc. Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/video/au0828/au0828-dvb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/au0828/au0828-dvb.c b/linux/drivers/media/video/au0828/au0828-dvb.c index 104f15cc3..93c665067 100644 --- a/linux/drivers/media/video/au0828/au0828-dvb.c +++ b/linux/drivers/media/video/au0828/au0828-dvb.c @@ -156,7 +156,7 @@ static int start_urb_transfer(struct au0828_dev *dev) dprintk(2, "%s()\n", __func__); if (dev->urb_streaming) { - dprintk(2, "%s: iso xfer already running!\n", __func__); + dprintk(2, "%s: bulk xfer already running!\n", __func__); return 0; } -- cgit v1.2.3 From f2405beb3c4b581cfb5754e7891dec8a3129b299 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 28 Jul 2009 08:39:10 +0200 Subject: gspca - sonixj: Webcam 0c45:6148 added. From: Denis Loginov Priority: normal Signed-off-by: Denis Loginov Signed-off-by: Jean-Francois Moine --- linux/Documentation/video4linux/gspca.txt | 1 + linux/drivers/media/video/gspca/sonixj.c | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index 37247697d..b43702470 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -287,6 +287,7 @@ sonixj 0c45:613a Microdia Sonix PC Camera sonixj 0c45:613b Surfer SN-206 sonixj 0c45:613c Sonix Pccam168 sonixj 0c45:6143 Sonix Pccam168 +sonixj 0c45:6148 Digitus DA-70811/ZSMC USB PC Camera ZS211/Microdia sn9c20x 0c45:6240 PC Camera (SN9C201 + MT9M001) sn9c20x 0c45:6242 PC Camera (SN9C201 + MT9M111) sn9c20x 0c45:6248 PC Camera (SN9C201 + OV9655) diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 5568401e8..98fb39c99 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -2384,7 +2384,8 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)}, #endif {USB_DEVICE(0x0c45, 0x6100), BSI(SN9C120, MI0360, 0x5d)}, /*sn9c128*/ -/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6801, 0x??)}, */ +/* {USB_DEVICE(0x0c45, 0x6102), BSI(SN9C120, PO2030N, ??)}, */ +/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6802, 0x21)}, */ {USB_DEVICE(0x0c45, 0x610a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c128*/ {USB_DEVICE(0x0c45, 0x610b), BSI(SN9C120, OV7660, 0x21)}, /*sn9c128*/ {USB_DEVICE(0x0c45, 0x610c), BSI(SN9C120, HV7131R, 0x11)}, /*sn9c128*/ @@ -2400,6 +2401,7 @@ static const __devinitdata struct usb_device_id device_table[] = { #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)}, #endif +/* {USB_DEVICE(0x0c45, 0x6132), BSI(SN9C120, OV7670, 0x21)}, */ {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)}, {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE @@ -2407,7 +2409,9 @@ static const __devinitdata struct usb_device_id device_table[] = { #endif {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x21)}, - {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, +/* {USB_DEVICE(0x0c45, 0x6142), BSI(SN9C120, PO2030N, ??)}, *sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, /*sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6148), BSI(SN9C120, OM6802, 0x21)}, /*sn9c120b*/ {} }; MODULE_DEVICE_TABLE(usb, device_table); -- cgit v1.2.3 From bf8e2366acf0eddbefdabe74b14e0b3634fcd669 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 28 Jul 2009 09:28:45 +0200 Subject: gspca - tv8532: Bad ISOC packet scan. From: Hans de Goede Priority: normal Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/tv8532.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/gspca/tv8532.c b/linux/drivers/media/video/gspca/tv8532.c index 79b78f234..29c765304 100644 --- a/linux/drivers/media/video/gspca/tv8532.c +++ b/linux/drivers/media/video/gspca/tv8532.c @@ -432,7 +432,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, packet_type0, frame, data + 2, gspca_dev->width); gspca_frame_add(gspca_dev, packet_type1, - frame, data + gspca_dev->width + 6, gspca_dev->width); + frame, data + gspca_dev->width + 5, gspca_dev->width); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) -- cgit v1.2.3 From f36217342b748f16badc1abb3aaeb433ee088020 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 28 Jul 2009 09:38:25 +0200 Subject: gspca - main: Memorize the current alt before setting it. From: Jean-Francois Moine This prevents a loop if a permanent error occurs in usb_set_interface(). Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index eedf9a60c..2e5197073 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -498,6 +498,7 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) } PDEBUG(D_STREAM, "use alt %d ep 0x%02x", i, ep->desc.bEndpointAddress); + gspca_dev->alt = i; /* memorize the current alt setting */ if (gspca_dev->nbalt > 1) { ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i); if (ret < 0) { @@ -505,7 +506,6 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) return NULL; } } - gspca_dev->alt = i; /* memorize the current alt setting */ return ep; } -- cgit v1.2.3 From 79035e11d5ad4166c51bf7e7c334000494e0a859 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 29 Jul 2009 01:39:12 -0300 Subject: em28xx: fix support for Plextor ConvertX PX-TV100U From: Mauro Carvalho Chehab This device uses msp34xx and uses 2.048 MHz frequency for I2S communication. Thanks to Angelo Cano for pointing the issues with this device and proposing an approach for fixing the issue. Priority: normal Tested-by: Angelo Cano Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 6e4a44331..795fd16a5 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -639,22 +639,27 @@ struct em28xx_board em28xx_boards[] = { }, [EM2861_BOARD_PLEXTOR_PX_TV100U] = { .name = "Plextor ConvertX PX-TV100U", - .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_TNF_5335MF, + .xclk = EM28XX_XCLK_I2S_MSB_TIMING | + EM28XX_XCLK_FREQUENCY_12MHZ, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_TVP5150, + .has_msp34xx = 1, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, + .gpio = pinnacle_hybrid_pro_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = pinnacle_hybrid_pro_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = pinnacle_hybrid_pro_analog, } }, }, @@ -1948,9 +1953,8 @@ void em28xx_pre_card_setup(struct em28xx *dev) /* request some modules */ switch (dev->model) { case EM2861_BOARD_PLEXTOR_PX_TV100U: - /* FIXME guess */ - /* Turn on analog audio output */ - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); + /* Sets the msp34xx I2S speed */ + dev->i2s_speed = 2048000; break; case EM2861_BOARD_KWORLD_PVRTV_300U: case EM2880_BOARD_KWORLD_DVB_305U: -- cgit v1.2.3 From b6e13ad1e52efcb4693b9260cb0cff0c6e85537f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 29 Jul 2009 01:42:02 -0300 Subject: em28xx: fix audio VIDIOC_S_CTRL adjustments on devices without ac97 From: Mauro Carvalho Chehab Even devices without ac97 needs to call analog audio setup function, to properly set xclk and mute/unmute. Thanks to Angelo Cano for reporting and testing it. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 1237e325d..8cb8674c9 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1123,9 +1123,26 @@ static int vidioc_s_ctrl(struct file *file, void *priv, else rc = 1; - /* It were not an AC97 control. Sends it to the v4l2 dev interface */ + /* It isn't an AC97 control. Sends it to the v4l2 dev interface */ if (rc == 1) { v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); + + /* + * In the case of non-AC97 volume controls, we still need + * to do some setups at em28xx, in order to mute/unmute + * and to adjust audio volume. However, the value ranges + * should be checked by the corresponding V4L subdriver. + */ + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + dev->mute = ctrl->value; + return em28xx_audio_analog_set(dev); + break; + case V4L2_CID_AUDIO_VOLUME: + dev->volume = ctrl->value; + return em28xx_audio_analog_set(dev); + } + /* FIXME: should be returning a meaninful value */ rc = 0; } -- cgit v1.2.3 From ba0a4fd1311b247e1b3d5954c7412e72240542a1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 30 Jul 2009 19:59:13 -0300 Subject: em28xx: fix mutex inbalance From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 8cb8674c9..fcb99fb71 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1136,15 +1136,12 @@ static int vidioc_s_ctrl(struct file *file, void *priv, switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: dev->mute = ctrl->value; - return em28xx_audio_analog_set(dev); + rc = em28xx_audio_analog_set(dev); break; case V4L2_CID_AUDIO_VOLUME: dev->volume = ctrl->value; - return em28xx_audio_analog_set(dev); + rc = em28xx_audio_analog_set(dev); } - - /* FIXME: should be returning a meaninful value */ - rc = 0; } mutex_unlock(&dev->lock); -- cgit v1.2.3 From 51e081d10118c1d432f7aedc4cadd3776d56e700 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Jul 2009 11:39:02 -0300 Subject: ir-kbd-i2c: fix spaces From: Douglas Schilling Landgraf Fixed checkpath warnings Priority: normal Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/ir-kbd-i2c.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/linux/drivers/media/video/ir-kbd-i2c.c b/linux/drivers/media/video/ir-kbd-i2c.c index cdf369129..fffe8239d 100644 --- a/linux/drivers/media/video/ir-kbd-i2c.c +++ b/linux/drivers/media/video/ir-kbd-i2c.c @@ -354,19 +354,19 @@ static struct i2c_driver driver = { .driver = { .name = "ir-kbd-i2c", }, - .id = I2C_DRIVERID_INFRARED, - .attach_adapter = ir_probe, - .detach_client = ir_detach, + .id = I2C_DRIVERID_INFRARED, + .attach_adapter = ir_probe, + .detach_client = ir_detach, }; - + static struct i2c_client client = { - .name = "unset", - .driver = &driver + .name = "unset", + .driver = &driver }; static int ir_attach(struct i2c_adapter *adap, int addr, - unsigned short flags, int kind) + unsigned short flags, int kind) #else static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) #endif @@ -492,7 +492,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Sets name */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) - snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (%s)", name); + snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (%s)", name); #else snprintf(ir->name, sizeof(ir->name), "i2c IR (%s)", name); #endif -- cgit v1.2.3 From 8239798d50a90e8d80bafdeade1aa38e77c537e1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Jul 2009 11:41:35 -0300 Subject: ir-kbd-i2c: Remove superfulous inlines From: Jean Delvare Functions which are referenced by their address can't be inlined by definition. Priority: normal Signed-off-by: Jean Delvare Signed-off-by: Andy Walls Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/ir-kbd-i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/drivers/media/video/ir-kbd-i2c.c b/linux/drivers/media/video/ir-kbd-i2c.c index fffe8239d..c61b22d25 100644 --- a/linux/drivers/media/video/ir-kbd-i2c.c +++ b/linux/drivers/media/video/ir-kbd-i2c.c @@ -127,12 +127,12 @@ static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, return 1; } -static inline int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { return get_key_haup_common (ir, ir_key, ir_raw, 3, 0); } -static inline int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { return get_key_haup_common (ir, ir_key, ir_raw, 6, 3); } -- cgit v1.2.3 From 7059988b9d5badb3606286718c371665ff4a70ae Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Jul 2009 11:44:05 -0300 Subject: ir-kbd-i2c: Allow use of ir-kdb-i2c internal get_key funcs and set ir_type From: Andy Walls This patch augments the init data passed by bridge drivers to ir-kbd-i2c, so that the ir_type can be set explicitly, and so ir-kbd-i2c internal get_key functions can be reused without requiring symbols from ir-kbd-i2c in the bridge driver. Priority: normal Signed-off-by: Andy Walls Reviewed-by: Jean Delvare Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/ir-kbd-i2c.c | 31 ++++++++++++++++++++++++++++++- linux/include/media/ir-kbd-i2c.h | 17 +++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/video/ir-kbd-i2c.c b/linux/drivers/media/video/ir-kbd-i2c.c index c61b22d25..ae928be28 100644 --- a/linux/drivers/media/video/ir-kbd-i2c.c +++ b/linux/drivers/media/video/ir-kbd-i2c.c @@ -478,7 +478,36 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir_codes = init_data->ir_codes; name = init_data->name; - ir->get_key = init_data->get_key; + if (init_data->type) + ir_type = init_data->type; + + switch (init_data->internal_get_key_func) { + case IR_KBD_GET_KEY_CUSTOM: + /* The bridge driver provided us its own function */ + ir->get_key = init_data->get_key; + break; + case IR_KBD_GET_KEY_PIXELVIEW: + ir->get_key = get_key_pixelview; + break; + case IR_KBD_GET_KEY_PV951: + ir->get_key = get_key_pv951; + break; + case IR_KBD_GET_KEY_HAUP: + ir->get_key = get_key_haup; + break; + case IR_KBD_GET_KEY_KNC1: + ir->get_key = get_key_knc1; + break; + case IR_KBD_GET_KEY_FUSIONHDTV: + ir->get_key = get_key_fusionhdtv; + break; + case IR_KBD_GET_KEY_HAUP_XVR: + ir->get_key = get_key_haup_xvr; + break; + case IR_KBD_GET_KEY_AVERMEDIA_CARDBUS: + ir->get_key = get_key_avermedia_cardbus; + break; + } } /* Make sure we are all setup before going on */ diff --git a/linux/include/media/ir-kbd-i2c.h b/linux/include/media/ir-kbd-i2c.h index 93a6c933b..2a8da5027 100644 --- a/linux/include/media/ir-kbd-i2c.h +++ b/linux/include/media/ir-kbd-i2c.h @@ -24,10 +24,27 @@ struct IR_i2c { int (*get_key)(struct IR_i2c*, u32*, u32*); }; +enum ir_kbd_get_key_fn { + IR_KBD_GET_KEY_CUSTOM = 0, + IR_KBD_GET_KEY_PIXELVIEW, + IR_KBD_GET_KEY_PV951, + IR_KBD_GET_KEY_HAUP, + IR_KBD_GET_KEY_KNC1, + IR_KBD_GET_KEY_FUSIONHDTV, + IR_KBD_GET_KEY_HAUP_XVR, + IR_KBD_GET_KEY_AVERMEDIA_CARDBUS, +}; + /* Can be passed when instantiating an ir_video i2c device */ struct IR_i2c_init_data { IR_KEYTAB_TYPE *ir_codes; const char *name; + int type; /* IR_TYPE_RC5, IR_TYPE_PD, etc */ + /* + * Specify either a function pointer or a value indicating one of + * ir_kbd_i2c's internal get_key functions + */ int (*get_key)(struct IR_i2c*, u32*, u32*); + enum ir_kbd_get_key_fn internal_get_key_func; }; #endif -- cgit v1.2.3 From 0be3a0fdedcfa1995a6b1398ccea67554e7edad6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Jul 2009 11:48:18 -0300 Subject: cx18: Add i2c initialization for Z8F0811/Hauppage IR transceivers From: Andy Walls This patch add support to the cx18 driver for setting up the Z8F0811/Hauppauge IR Tx/Rx chip with the i2c binding model in newer kernels. Priority: normal Signed-off-by: Andy Walls Reviewed-by: Jean Delvare Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cx18/cx18-cards.c | 6 ++- linux/drivers/media/video/cx18/cx18-cards.h | 18 +++++---- linux/drivers/media/video/cx18/cx18-i2c.c | 63 +++++++++++++++++++++++++---- 3 files changed, 70 insertions(+), 17 deletions(-) diff --git a/linux/drivers/media/video/cx18/cx18-cards.c b/linux/drivers/media/video/cx18/cx18-cards.c index c0abf3476..d34cba6a7 100644 --- a/linux/drivers/media/video/cx18/cx18-cards.c +++ b/linux/drivers/media/video/cx18/cx18-cards.c @@ -56,7 +56,8 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { .hw_audio_ctrl = CX18_HW_418_AV, .hw_muxer = CX18_HW_CS5345, .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | - CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, + CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL | + CX18_HW_Z8F0811_IR_HAUP, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, @@ -102,7 +103,8 @@ static const struct cx18_card cx18_card_hvr1600_samsung = { .hw_audio_ctrl = CX18_HW_418_AV, .hw_muxer = CX18_HW_CS5345, .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | - CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, + CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL | + CX18_HW_Z8F0811_IR_HAUP, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, diff --git a/linux/drivers/media/video/cx18/cx18-cards.h b/linux/drivers/media/video/cx18/cx18-cards.h index 3c552b6b7..444e3c7c5 100644 --- a/linux/drivers/media/video/cx18/cx18-cards.h +++ b/linux/drivers/media/video/cx18/cx18-cards.h @@ -22,13 +22,17 @@ */ /* hardware flags */ -#define CX18_HW_TUNER (1 << 0) -#define CX18_HW_TVEEPROM (1 << 1) -#define CX18_HW_CS5345 (1 << 2) -#define CX18_HW_DVB (1 << 3) -#define CX18_HW_418_AV (1 << 4) -#define CX18_HW_GPIO_MUX (1 << 5) -#define CX18_HW_GPIO_RESET_CTRL (1 << 6) +#define CX18_HW_TUNER (1 << 0) +#define CX18_HW_TVEEPROM (1 << 1) +#define CX18_HW_CS5345 (1 << 2) +#define CX18_HW_DVB (1 << 3) +#define CX18_HW_418_AV (1 << 4) +#define CX18_HW_GPIO_MUX (1 << 5) +#define CX18_HW_GPIO_RESET_CTRL (1 << 6) +#define CX18_HW_Z8F0811_IR_TX_HAUP (1 << 7) +#define CX18_HW_Z8F0811_IR_RX_HAUP (1 << 8) +#define CX18_HW_Z8F0811_IR_HAUP (CX18_HW_Z8F0811_IR_RX_HAUP | \ + CX18_HW_Z8F0811_IR_TX_HAUP) /* video inputs */ #define CX18_CARD_INPUT_VID_TUNER 1 diff --git a/linux/drivers/media/video/cx18/cx18-i2c.c b/linux/drivers/media/video/cx18/cx18-i2c.c index cee650c58..c551fcd47 100644 --- a/linux/drivers/media/video/cx18/cx18-i2c.c +++ b/linux/drivers/media/video/cx18/cx18-i2c.c @@ -28,6 +28,9 @@ #include "cx18-gpio.h" #include "cx18-i2c.h" #include "cx18-irq.h" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) +#include +#endif #define CX18_REG_I2C_1_WR 0xf15000 #define CX18_REG_I2C_1_RD 0xf15008 @@ -40,16 +43,20 @@ #define GETSDL_BIT 0x0008 #define CX18_CS5345_I2C_ADDR 0x4c +#define CX18_Z8F0811_IR_TX_I2C_ADDR 0x70 +#define CX18_Z8F0811_IR_RX_I2C_ADDR 0x71 /* This array should match the CX18_HW_ defines */ static const u8 hw_addrs[] = { - 0, /* CX18_HW_TUNER */ - 0, /* CX18_HW_TVEEPROM */ - CX18_CS5345_I2C_ADDR, /* CX18_HW_CS5345 */ - 0, /* CX18_HW_DVB */ - 0, /* CX18_HW_418_AV */ - 0, /* CX18_HW_GPIO_MUX */ - 0, /* CX18_HW_GPIO_RESET_CTRL */ + 0, /* CX18_HW_TUNER */ + 0, /* CX18_HW_TVEEPROM */ + CX18_CS5345_I2C_ADDR, /* CX18_HW_CS5345 */ + 0, /* CX18_HW_DVB */ + 0, /* CX18_HW_418_AV */ + 0, /* CX18_HW_GPIO_MUX */ + 0, /* CX18_HW_GPIO_RESET_CTRL */ + CX18_Z8F0811_IR_TX_I2C_ADDR, /* CX18_HW_Z8F0811_IR_TX_HAUP */ + CX18_Z8F0811_IR_RX_I2C_ADDR, /* CX18_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the CX18_HW_ defines */ @@ -62,6 +69,8 @@ static const u8 hw_bus[] = { 0, /* CX18_HW_418_AV */ 0, /* CX18_HW_GPIO_MUX */ 0, /* CX18_HW_GPIO_RESET_CTRL */ + 0, /* CX18_HW_Z8F0811_IR_TX_HAUP */ + 0, /* CX18_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the CX18_HW_ defines */ @@ -73,6 +82,8 @@ static const char * const hw_modules[] = { NULL, /* CX18_HW_418_AV */ NULL, /* CX18_HW_GPIO_MUX */ NULL, /* CX18_HW_GPIO_RESET_CTRL */ + NULL, /* CX18_HW_Z8F0811_IR_TX_HAUP */ + NULL, /* CX18_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the CX18_HW_ defines */ @@ -84,8 +95,41 @@ static const char * const hw_devicenames[] = { "cx23418_AV", "gpio_mux", "gpio_reset_ctrl", + "ir_tx_z8f0811_haup", + "ir_rx_z8f0811_haup", }; +static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type, + u8 addr) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + struct i2c_board_info info; + struct IR_i2c_init_data ir_init_data; + unsigned short addr_list[2] = { addr, I2C_CLIENT_END }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, type, I2C_NAME_SIZE); + + /* Our default information for ir-kbd-i2c.c to use */ + switch (hw) { + case CX18_HW_Z8F0811_IR_RX_HAUP: + memset(&ir_init_data, 0, sizeof(struct IR_i2c_init_data)); + ir_init_data.ir_codes = ir_codes_hauppauge_new; + ir_init_data.internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; + ir_init_data.type = IR_TYPE_RC5; + ir_init_data.name = "CX23418 Z8F0811 Hauppauge"; + info.platform_data = &ir_init_data; + break; + default: + break; + } + + return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0; +#else + return -1; +#endif +} + int cx18_i2c_register(struct cx18 *cx, unsigned idx) { struct v4l2_subdev *sd; @@ -115,11 +159,14 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx) return sd != NULL ? 0 : -1; } + if (hw & CX18_HW_Z8F0811_IR_HAUP) + return cx18_i2c_new_ir(adap, hw, type, hw_addrs[idx]); + /* Is it not an I2C device or one we do not wish to register? */ if (!hw_addrs[idx]) return -1; - /* It's an I2C device other than an analog tuner */ + /* It's an I2C device other than an analog tuner or IR chip */ sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, adap, mod, type, hw_addrs[idx]); if (sd != NULL) sd->grp_id = hw; -- cgit v1.2.3 From e0cc8a30c11596d8e20080c03a1ee6d79b5b353a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Jul 2009 11:50:14 -0300 Subject: ir-kbd-i2c: Add support for Z8F0811/Hauppage IR transceivers From: Andy Walls This patch adds support for Zilog Z8F0811 IR transceiver chips on CX2341[68] based boards to ir-kbd-i2c for both the old i2c binding model and the new i2c binding model. Priority: normal Signed-off-by: Andy Walls Reviewed-by: Jean Delvare Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/ir-kbd-i2c.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/linux/drivers/media/video/ir-kbd-i2c.c b/linux/drivers/media/video/ir-kbd-i2c.c index ae928be28..a1e8fb561 100644 --- a/linux/drivers/media/video/ir-kbd-i2c.c +++ b/linux/drivers/media/video/ir-kbd-i2c.c @@ -442,9 +442,11 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) case 0x47: case 0x71: case 0x2d: - if (adap->id == I2C_HW_B_CX2388x) { + if (adap->id == I2C_HW_B_CX2388x || + adap->id == I2C_HW_B_CX2341X) { /* Handled by cx88-input */ - name = "CX2388x remote"; + name = adap->id == I2C_HW_B_CX2341X ? "CX2341x remote" + : "CX2388x remote"; ir_type = IR_TYPE_RC5; ir->get_key = get_key_haup_xvr; if (hauppauge == 1) { @@ -726,7 +728,8 @@ static int ir_probe(struct i2c_adapter *adap) static const struct i2c_device_id ir_kbd_id[] = { /* Generic entry for any IR receiver */ { "ir_video", 0 }, - /* IR device specific entries could be added here */ + /* IR device specific entries should be added here */ + { "ir_rx_z8f0811_haup", 0 }, { } }; -- cgit v1.2.3 From 3defb5a7493d187675c33898730f3ee456530a76 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Jul 2009 11:52:10 -0300 Subject: stv680: kfree called before usb_kill_urb From: Oliver Neukum The irq handler will touch memory. Even in the error case some URBs may complete. Thus no memory must be kfreed before all URBs are killed. Priority: normal Signed-off-by: Oliver Neukum Acked-by: Greg Kroah-Hartman Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/stv680.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/linux/drivers/media/video/stv680.c b/linux/drivers/media/video/stv680.c index eafe68502..600c19a78 100644 --- a/linux/drivers/media/video/stv680.c +++ b/linux/drivers/media/video/stv680.c @@ -739,10 +739,6 @@ static int stv680_start_stream (struct usb_stv *stv680) return 0; nomem_err: - for (i = 0; i < STV680_NUMSCRATCH; i++) { - kfree(stv680->scratch[i].data); - stv680->scratch[i].data = NULL; - } for (i = 0; i < STV680_NUMSBUF; i++) { usb_kill_urb(stv680->urb[i]); usb_free_urb(stv680->urb[i]); @@ -750,6 +746,11 @@ static int stv680_start_stream (struct usb_stv *stv680) kfree(stv680->sbuf[i].data); stv680->sbuf[i].data = NULL; } + /* used in irq, free only as all URBs are dead */ + for (i = 0; i < STV680_NUMSCRATCH; i++) { + kfree(stv680->scratch[i].data); + stv680->scratch[i].data = NULL; + } return -ENOMEM; } -- cgit v1.2.3 From c89fddecd67b48db7a9ab00bb31b46f1b0cdbbb5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Jul 2009 11:53:20 -0300 Subject: v4l doc: fix cqcam source code path From: Mhayk Whandson Fixed the c-qcam source code path in the linux kernel tree. Priority: normal Signed-off-by: Mhayk Whandson Signed-off-by: Randy Dunlap Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/Documentation/video4linux/CQcam.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/Documentation/video4linux/CQcam.txt b/linux/Documentation/video4linux/CQcam.txt index 04986efb7..d230878e4 100644 --- a/linux/Documentation/video4linux/CQcam.txt +++ b/linux/Documentation/video4linux/CQcam.txt @@ -18,8 +18,8 @@ Table of Contents 1.0 Introduction - The file ../drivers/char/c-qcam.c is a device driver for the -Logitech (nee Connectix) parallel port interface color CCD camera. + The file ../../drivers/media/video/c-qcam.c is a device driver for +the Logitech (nee Connectix) parallel port interface color CCD camera. This is a fairly inexpensive device for capturing images. Logitech does not currently provide information for developers, but many people have engineered several solutions for non-Microsoft use of the Color -- cgit v1.2.3 From e93775b260028520447ff7c8bf8f0978bed9d2f8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Jul 2009 11:54:52 -0300 Subject: af9015: Fix for crash in dvb-usb-af9015 From: Nils Kassube Moving BOOT fixes problem. Priority: normal Signed-off-by: Nils Kassube Acked-by: Antti Palosaari Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-usb/af9015.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c index a94f4d305..7798aabaa 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.c +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -81,7 +81,6 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) switch (req->cmd) { case GET_CONFIG: - case BOOT: case READ_MEMORY: case RECONNECT_USB: case GET_IR_CODE: @@ -100,6 +99,7 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) case WRITE_VIRTUAL_MEMORY: case COPY_FIRMWARE: case DOWNLOAD_FIRMWARE: + case BOOT: break; default: err("unknown command:%d", req->cmd); -- cgit v1.2.3 From b790d1d65f747cf5694a44470f45539c42859b0e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Jul 2009 11:56:33 -0300 Subject: saa7134: fix lock imbalance From: Jiri Slaby There seems to be one superfluos unlock in a poll function. Remove it. Priority: normal Signed-off-by: Jiri Slaby Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/saa7134/saa7134-video.c | 1 - 1 file changed, 1 deletion(-) diff --git a/linux/drivers/media/video/saa7134/saa7134-video.c b/linux/drivers/media/video/saa7134/saa7134-video.c index 5be072c57..04d7e8d66 100644 --- a/linux/drivers/media/video/saa7134/saa7134-video.c +++ b/linux/drivers/media/video/saa7134/saa7134-video.c @@ -1444,7 +1444,6 @@ video_poll(struct file *file, struct poll_table_struct *wait) fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf); fh->cap.read_off = 0; } - mutex_unlock(&fh->cap.vb_lock); buf = fh->cap.read_buf; } -- cgit v1.2.3 From 63f6769bcad76518a85506b5f19de1a48707d6fb Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 29 Jul 2009 10:01:54 +0200 Subject: gspca - main: Remove vidioc_s_std(). From: Marton Nemeth The vidioc_s_std() is not necessary when vdev->tvnorms == 0. Priority: normal Signed-off-by: Marton Nemeth Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 2e5197073..670091e42 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -1504,12 +1504,6 @@ static int vidioc_s_parm(struct file *filp, void *priv, return 0; } -static int vidioc_s_std(struct file *filp, void *priv, - v4l2_std_id *parm) -{ - return 0; -} - #ifdef CONFIG_VIDEO_V4L1_COMPAT static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) @@ -1980,7 +1974,6 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_s_jpegcomp = vidioc_s_jpegcomp, .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, - .vidioc_s_std = vidioc_s_std, .vidioc_enum_framesizes = vidioc_enum_framesizes, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, -- cgit v1.2.3 From e523ddb42e0588210df880e0e32f0de801803e94 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 30 Jul 2009 20:00:44 -0300 Subject: hdpvr: fix lock imbalances From: Jiri Slaby There are many lock imbalances in this driver. Fix all found. Priority: normal Signed-off-by: Jiri Slaby Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/hdpvr/hdpvr-core.c | 12 ++++++------ linux/drivers/media/video/hdpvr/hdpvr-video.c | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/linux/drivers/media/video/hdpvr/hdpvr-core.c b/linux/drivers/media/video/hdpvr/hdpvr-core.c index 188bd5aea..1c9bc94c9 100644 --- a/linux/drivers/media/video/hdpvr/hdpvr-core.c +++ b/linux/drivers/media/video/hdpvr/hdpvr-core.c @@ -126,7 +126,7 @@ static int device_authorization(struct hdpvr_device *dev) char *print_buf = kzalloc(5*buf_size+1, GFP_KERNEL); if (!print_buf) { v4l2_err(&dev->v4l2_dev, "Out of memory\n"); - goto error; + return retval; } #endif @@ -140,7 +140,7 @@ static int device_authorization(struct hdpvr_device *dev) if (ret != 46) { v4l2_err(&dev->v4l2_dev, "unexpected answer of status request, len %d\n", ret); - goto error; + goto unlock; } #ifdef HDPVR_DEBUG else { @@ -163,7 +163,7 @@ static int device_authorization(struct hdpvr_device *dev) v4l2_err(&dev->v4l2_dev, "unknown firmware version 0x%x\n", dev->usbc_buf[1]); ret = -EINVAL; - goto error; + goto unlock; } response = dev->usbc_buf+38; @@ -188,10 +188,10 @@ static int device_authorization(struct hdpvr_device *dev) 10000); v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "magic request returned %d\n", ret); - mutex_unlock(&dev->usbc_mutex); retval = ret != 8; -error: +unlock: + mutex_unlock(&dev->usbc_mutex); return retval; } @@ -350,6 +350,7 @@ static int hdpvr_probe(struct usb_interface *interface, mutex_lock(&dev->io_mutex); if (hdpvr_alloc_buffers(dev, NUM_BUFFERS)) { + mutex_unlock(&dev->io_mutex); v4l2_err(&dev->v4l2_dev, "allocating transfer buffers failed\n"); goto error; @@ -381,7 +382,6 @@ static int hdpvr_probe(struct usb_interface *interface, error: if (dev) { - mutex_unlock(&dev->io_mutex); /* this frees allocated memory */ hdpvr_delete(dev); } diff --git a/linux/drivers/media/video/hdpvr/hdpvr-video.c b/linux/drivers/media/video/hdpvr/hdpvr-video.c index ccd47f57f..5937de23c 100644 --- a/linux/drivers/media/video/hdpvr/hdpvr-video.c +++ b/linux/drivers/media/video/hdpvr/hdpvr-video.c @@ -375,6 +375,7 @@ static int hdpvr_open(struct file *file) * in resumption */ mutex_lock(&dev->io_mutex); dev->open_count++; + mutex_unlock(&dev->io_mutex); fh->dev = dev; @@ -383,7 +384,6 @@ static int hdpvr_open(struct file *file) retval = 0; err: - mutex_unlock(&dev->io_mutex); return retval; } @@ -519,8 +519,10 @@ static unsigned int hdpvr_poll(struct file *filp, poll_table *wait) mutex_lock(&dev->io_mutex); - if (video_is_unregistered(dev->video_dev)) + if (video_is_unregistered(dev->video_dev)) { + mutex_unlock(&dev->io_mutex); return -EIO; + } if (dev->status == STATUS_IDLE) { if (hdpvr_start_streaming(dev)) { -- cgit v1.2.3 From be7e7366b6e627b24db77147c07f9a3deb5f3933 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 1 Aug 2009 23:14:24 +0200 Subject: uvcvideo: Avoid flooding the kernel log with "unknown event type" messages From: Laurent Pinchart The iSight sends non-UVC status events through the interrupt endpoint. Those invalid events are reported to the kernel log, resulting in a log flood. Only log the events when the UVC_TRACE_STATUS flag is set. Priority: high Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_status.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/drivers/media/video/uvc/uvc_status.c b/linux/drivers/media/video/uvc/uvc_status.c index b05df63b4..48614d39c 100644 --- a/linux/drivers/media/video/uvc/uvc_status.c +++ b/linux/drivers/media/video/uvc/uvc_status.c @@ -157,8 +157,8 @@ static void uvc_status_complete(struct urb *urb) break; default: - uvc_printk(KERN_INFO, "unknown event type %u.\n", - dev->status[0]); + uvc_trace(UVC_TRACE_STATUS, "Unknown status event " + "type %u.\n", dev->status[0]); break; } } -- cgit v1.2.3 From 0315e4babbc90dd9a89e82968fd924cae514d993 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 2 Aug 2009 11:56:47 +0200 Subject: gspca - vc032x: Bad h/v flip controls when inverted by default. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/vc032x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index 44dea7949..6f9aab89c 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -3135,10 +3135,10 @@ static void sethvflip(struct gspca_dev *gspca_dev) hflip = sd->hflip; if (sd->flags & FL_HFLIP) - hflip != hflip; + hflip = !hflip; vflip = sd->vflip; if (sd->flags & FL_VFLIP) - vflip != vflip; + vflip = !vflip; switch (sd->sensor) { case SENSOR_MI1310_SOC: case SENSOR_MI1320_SOC: -- cgit v1.2.3 From 920d68bc15da48dcaf2144b777f7dcae138599ca Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 2 Aug 2009 11:37:34 -0400 Subject: sms1xxx: fix build warning: unused variable 'board' From: Michael Krufky Remove the following build warning: sms-cards.c: In function 'sms_board_event': sms-cards.c:120: warning: unused variable 'board' Thanks to Hans Verkuil for pointing this out. The problem code has been #if 0'd for now, this will likely be used again in the future, once the event interface is complete. Priority: high Signed-off-by: Michael Krufky Cc: Hans Verkuil --- linux/drivers/media/dvb/siano/sms-cards.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux/drivers/media/dvb/siano/sms-cards.c b/linux/drivers/media/dvb/siano/sms-cards.c index 76bd3cb82..62c1bf604 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.c +++ b/linux/drivers/media/dvb/siano/sms-cards.c @@ -116,8 +116,10 @@ static inline void sms_gpio_assign_11xx_default_led_config( int sms_board_event(struct smscore_device_t *coredev, enum SMS_BOARD_EVENTS gevent) { +#if 0 int board_id = smscore_get_board_id(coredev); struct sms_board *board = sms_get_board(board_id); +#endif struct smscore_gpio_config MyGpioConfig; sms_gpio_assign_11xx_default_led_config(&MyGpioConfig); -- cgit v1.2.3 From 2e966abec56337af8c6314850d990720869b8786 Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Sun, 2 Aug 2009 20:44:37 +0200 Subject: dvb-usb: fix tuning with Cinergy T2 From: Eberhard Mattes cinergyInitialize param.flags. Priority: normal Signed-off-by: Eberhard Mattes Signed-off-by: Thierry MERLE --- linux/drivers/media/dvb/dvb-usb/cinergyT2-fe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux/drivers/media/dvb/dvb-usb/cinergyT2-fe.c b/linux/drivers/media/dvb/dvb-usb/cinergyT2-fe.c index 649f25cca..9cd51ac12 100644 --- a/linux/drivers/media/dvb/dvb-usb/cinergyT2-fe.c +++ b/linux/drivers/media/dvb/dvb-usb/cinergyT2-fe.c @@ -275,6 +275,7 @@ static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe, param.tps = cpu_to_le16(compute_tps(fep)); param.freq = cpu_to_le32(fep->frequency / 1000); param.bandwidth = 8 - fep->u.ofdm.bandwidth - BANDWIDTH_8_MHZ; + param.flags = 0; err = dvb_usb_generic_rw(state->d, (char *)¶m, sizeof(param), -- cgit v1.2.3 From deec85326356815e5bdd8c0442d5c758b12fe7eb Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 3 Aug 2009 15:51:33 -0400 Subject: saa7134: Use correct product name for Hauppauge WinTV-HVR1150 ATSC/QAM-Hybrid From: Michael Krufky Priority: high Signed-off-by: Michael Krufky --- linux/Documentation/video4linux/CARDLIST.saa7134 | 2 +- linux/drivers/media/video/saa7134/saa7134-cards.c | 20 ++++++++++---------- linux/drivers/media/video/saa7134/saa7134-dvb.c | 2 +- linux/drivers/media/video/saa7134/saa7134.h | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/linux/Documentation/video4linux/CARDLIST.saa7134 b/linux/Documentation/video4linux/CARDLIST.saa7134 index 15562427e..a82b767f9 100644 --- a/linux/Documentation/video4linux/CARDLIST.saa7134 +++ b/linux/Documentation/video4linux/CARDLIST.saa7134 @@ -153,7 +153,7 @@ 152 -> Asus Tiger Rev:1.00 [1043:4857] 153 -> Kworld Plus TV Analog Lite PCI [17de:7128] 154 -> Avermedia AVerTV GO 007 FM Plus [1461:f31d] -155 -> Hauppauge WinTV-HVR1120 ATSC/QAM-Hybrid [0070:6706,0070:6708] +155 -> Hauppauge WinTV-HVR1150 ATSC/QAM-Hybrid [0070:6706,0070:6708] 156 -> Hauppauge WinTV-HVR1110r3 DVB-T/Hybrid [0070:6707,0070:6709,0070:670a] 157 -> Avermedia AVerTV Studio 507UA [1461:a11b] 158 -> AVerMedia Cardbus TV/Radio (E501R) [1461:b7e9] diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index e2d98ff5c..fcdb20676 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -3370,8 +3370,8 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200100, }, }, - [SAA7134_BOARD_HAUPPAUGE_HVR1120] = { - .name = "Hauppauge WinTV-HVR1120 ATSC/QAM-Hybrid", + [SAA7134_BOARD_HAUPPAUGE_HVR1150] = { + .name = "Hauppauge WinTV-HVR1150 ATSC/QAM-Hybrid", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, @@ -5901,7 +5901,7 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x6706, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -5913,7 +5913,7 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x6708, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -6458,7 +6458,7 @@ static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, switch (command) { case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */ switch (dev->board) { - case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1150: case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg); break; @@ -6479,7 +6479,7 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev, int ret; switch (dev->board) { - case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1150: case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: /* tda8290 + tda18271 */ ret = saa7134_tda8290_18271_callback(dev, command, arg); @@ -6526,7 +6526,7 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) switch (tv.model) { case 67019: /* WinTV-HVR1110 (Retail, IR Blaster, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67109: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ - case 67201: /* WinTV-HVR1120 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ + case 67201: /* WinTV-HVR1150 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67301: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ case 67209: /* WinTV-HVR1110 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67559: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ @@ -6534,7 +6534,7 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) case 67579: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM) */ case 67589: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ case 67599: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ - case 67651: /* WinTV-HVR1120 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ + case 67651: /* WinTV-HVR1150 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ break; default: @@ -6728,7 +6728,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00); break; - case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1150: case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: /* GPIO 26 high for digital, low for analog */ saa7134_set_gpio(dev, 26, 0); @@ -6994,7 +6994,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->name, saa7134_boards[dev->board].name); } break; - case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1150: case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: hauppauge_eeprom(dev, dev->eedata+0x80); break; diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 360fbdf19..377645a17 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -1147,7 +1147,7 @@ static int dvb_init(struct saa7134_dev *dev) &tda827x_cfg_1) < 0) goto dettach_frontend; break; - case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1150: fe0->dvb.frontend = dvb_attach(lgdt3305_attach, &hcw_lgdt3305_config, &dev->i2c_adap); diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index be5abf059..a6e8c7f92 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -279,7 +279,7 @@ struct saa7134_format { #define SAA7134_BOARD_ASUSTeK_TIGER 152 #define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153 #define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154 -#define SAA7134_BOARD_HAUPPAUGE_HVR1120 155 +#define SAA7134_BOARD_HAUPPAUGE_HVR1150 155 #define SAA7134_BOARD_HAUPPAUGE_HVR1110R3 156 #define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157 #define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158 -- cgit v1.2.3 From 219fe38bd79dab42db83cacc1f5444d0e27fa8ea Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 3 Aug 2009 15:51:33 -0400 Subject: saa7134: Use correct product name for Hauppauge WinTV-HVR1120 DVB-T/Hybrid From: Michael Krufky Priority: high Signed-off-by: Michael Krufky --- linux/Documentation/video4linux/CARDLIST.saa7134 | 2 +- linux/drivers/media/video/saa7134/saa7134-cards.c | 18 +++++++++--------- linux/drivers/media/video/saa7134/saa7134-dvb.c | 2 +- linux/drivers/media/video/saa7134/saa7134.h | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/linux/Documentation/video4linux/CARDLIST.saa7134 b/linux/Documentation/video4linux/CARDLIST.saa7134 index a82b767f9..c913e5614 100644 --- a/linux/Documentation/video4linux/CARDLIST.saa7134 +++ b/linux/Documentation/video4linux/CARDLIST.saa7134 @@ -154,7 +154,7 @@ 153 -> Kworld Plus TV Analog Lite PCI [17de:7128] 154 -> Avermedia AVerTV GO 007 FM Plus [1461:f31d] 155 -> Hauppauge WinTV-HVR1150 ATSC/QAM-Hybrid [0070:6706,0070:6708] -156 -> Hauppauge WinTV-HVR1110r3 DVB-T/Hybrid [0070:6707,0070:6709,0070:670a] +156 -> Hauppauge WinTV-HVR1120 DVB-T/Hybrid [0070:6707,0070:6709,0070:670a] 157 -> Avermedia AVerTV Studio 507UA [1461:a11b] 158 -> AVerMedia Cardbus TV/Radio (E501R) [1461:b7e9] 159 -> Beholder BeholdTV 505 RDS [0000:505B] diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index fcdb20676..c18b41ee6 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -3402,8 +3402,8 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0800100, /* GPIO 23 HI for FM */ }, }, - [SAA7134_BOARD_HAUPPAUGE_HVR1110R3] = { - .name = "Hauppauge WinTV-HVR1110r3 DVB-T/Hybrid", + [SAA7134_BOARD_HAUPPAUGE_HVR1120] = { + .name = "Hauppauge WinTV-HVR1120 DVB-T/Hybrid", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, @@ -5907,7 +5907,7 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x6707, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -5919,13 +5919,13 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x6709, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x670a, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -6459,7 +6459,7 @@ static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */ switch (dev->board) { case SAA7134_BOARD_HAUPPAUGE_HVR1150: - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg); break; default: @@ -6480,7 +6480,7 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev, switch (dev->board) { case SAA7134_BOARD_HAUPPAUGE_HVR1150: - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: /* tda8290 + tda18271 */ ret = saa7134_tda8290_18271_callback(dev, command, arg); break; @@ -6729,7 +6729,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00); break; case SAA7134_BOARD_HAUPPAUGE_HVR1150: - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: /* GPIO 26 high for digital, low for analog */ saa7134_set_gpio(dev, 26, 0); msleep(1); @@ -6995,7 +6995,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) } break; case SAA7134_BOARD_HAUPPAUGE_HVR1150: - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: hauppauge_eeprom(dev, dev->eedata+0x80); break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 377645a17..a5263c391 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -1119,7 +1119,7 @@ static int dvb_init(struct saa7134_dev *dev) &tda827x_cfg_2) < 0) goto dettach_frontend; break; - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: fe0->dvb.frontend = dvb_attach(tda10048_attach, &hcw_tda10048_config, &dev->i2c_adap); diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index a6e8c7f92..6b0742ad7 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -280,7 +280,7 @@ struct saa7134_format { #define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153 #define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154 #define SAA7134_BOARD_HAUPPAUGE_HVR1150 155 -#define SAA7134_BOARD_HAUPPAUGE_HVR1110R3 156 +#define SAA7134_BOARD_HAUPPAUGE_HVR1120 156 #define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157 #define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158 #define SAA7134_BOARD_BEHOLD_505RDS 159 -- cgit v1.2.3 From f0b81434ee6e958e7ffc72aa28d580c3d08cb31c Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Mon, 3 Aug 2009 21:40:16 -0400 Subject: cx88: fix regression in tuning for Geniatech X8000 MT From: Devin Heitmueller The introduction of the zl10353 i2c gate control broke support for the Geniatech board (which is not behind an i2 gate). Add the needed parameter. Priority: high Signed-off-by: Devin Heitmueller --- linux/drivers/media/video/cx88/cx88-dvb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 36f35f0b5..9c88b6c0f 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -502,6 +502,7 @@ static struct zl10353_config cx88_pinnacle_hybrid_pctv = { static struct zl10353_config cx88_geniatech_x8000_mt = { .demod_address = (0x1e >> 1), .no_tuner = 1, + .disable_i2c_gate_ctrl = 1, #if 0 .input_frequency = 0xe609, .parallel_ts = 1, -- cgit v1.2.3 From aa7df40721e36bcf9d4f1a60b9e58e69c08aac48 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Mon, 3 Aug 2009 21:52:59 -0400 Subject: cx88: Disable xc3028 power management for Geniatech x8000 From: Devin Heitmueller A user discovered that the Geniatech x8000 encountered a regression when the xc3028 power management was introduced. The xc3028 never recovers after setting the powerdown register, which is probably because the xc3028 reset GPIO is not properly configured. Since I do not have access to the hardware and thus cannot determine the correct GPIO configuration, just disable xc3028 power management on this board, which fixes the regression. Thanks to user "ritec" for reporting the issue and testing the fix. Priority: high Cc: rictec Signed-off-by: Devin Heitmueller --- linux/drivers/media/common/tuners/tuner-xc2028.c | 4 ++-- linux/drivers/media/common/tuners/tuner-xc2028.h | 1 + linux/drivers/media/video/cx88/cx88-cards.c | 8 ++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/linux/drivers/media/common/tuners/tuner-xc2028.c b/linux/drivers/media/common/tuners/tuner-xc2028.c index 3de5d6685..3cf380e6b 100644 --- a/linux/drivers/media/common/tuners/tuner-xc2028.c +++ b/linux/drivers/media/common/tuners/tuner-xc2028.c @@ -1123,8 +1123,8 @@ static int xc2028_sleep(struct dvb_frontend *fe) struct xc2028_data *priv = fe->tuner_priv; int rc = 0; - /* Avoid firmware reload on slow devices */ - if (no_poweroff) + /* Avoid firmware reload on slow devices or if PM disabled */ + if (no_poweroff || priv->ctrl.disable_power_mgmt) return 0; tuner_dbg("Putting xc2028/3028 into poweroff mode.\n"); diff --git a/linux/drivers/media/common/tuners/tuner-xc2028.h b/linux/drivers/media/common/tuners/tuner-xc2028.h index 19de7928a..a90c35d50 100644 --- a/linux/drivers/media/common/tuners/tuner-xc2028.h +++ b/linux/drivers/media/common/tuners/tuner-xc2028.h @@ -38,6 +38,7 @@ struct xc2028_ctrl { unsigned int input1:1; unsigned int vhfbw7:1; unsigned int uhfbw8:1; + unsigned int disable_power_mgmt:1; unsigned int demod; enum firmware_type type:2; }; diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index bf0f26c03..67ccc59a7 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -3036,6 +3036,14 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: ctl->demod = XC3028_FE_OREN538; break; + case CX88_BOARD_GENIATECH_X8000_MT: + /* FIXME: For this board, the xc3028 never recovers after being + powered down (the reset GPIO probably is not set properly). + We don't have access to the hardware so we cannot determine + which GPIO is used for xc3028, so just disable power xc3028 + power management for now */ + ctl->disable_power_mgmt = 1; + break; case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: case CX88_BOARD_PROLINK_PV_8000GT: -- cgit v1.2.3 From 6355bbe94603dee693331f5ae62c8c1a109a8007 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Mon, 3 Aug 2009 22:56:51 -0400 Subject: em28xx: add support for Terratec Cinergy Hybrid T USB XS remote control From: Devin Heitmueller Add support for the remote control that comes with the Cinergy Hybrid T USB XS Thanks to Jelle de Jong for providing sample hardware to test with. Priority: normal Signed-off-by: Devin Heitmueller Cc: Jelle de Jong --- linux/drivers/media/common/ir-keymaps.c | 55 +++++++++++++++++++++++++ linux/drivers/media/video/em28xx/em28xx-cards.c | 2 + linux/include/media/ir-common.h | 1 + 3 files changed, 58 insertions(+) diff --git a/linux/drivers/media/common/ir-keymaps.c b/linux/drivers/media/common/ir-keymaps.c index 56fa64b4c..67c67430d 100644 --- a/linux/drivers/media/common/ir-keymaps.c +++ b/linux/drivers/media/common/ir-keymaps.c @@ -2801,6 +2801,61 @@ IR_KEYTAB_TYPE ir_codes_dm1105_nec[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec); + +/* Terratec Cinergy Hybrid T USB XS + Devin Heitmueller + */ +IR_KEYTAB_TYPE ir_codes_terratec_cinergy_xs[IR_KEYTAB_SIZE] = { + [0x41] = KEY_HOME, + [0x01] = KEY_POWER, + [0x42] = KEY_MENU, + [0x02] = KEY_1, + [0x03] = KEY_2, + [0x04] = KEY_3, + [0x43] = KEY_SUBTITLE, + [0x05] = KEY_4, + [0x06] = KEY_5, + [0x07] = KEY_6, + [0x44] = KEY_TEXT, + [0x08] = KEY_7, + [0x09] = KEY_8, + [0x0a] = KEY_9, + [0x45] = KEY_DELETE, + [0x0b] = KEY_TUNER, + [0x0c] = KEY_0, + [0x0d] = KEY_MODE, + [0x46] = KEY_TV, + [0x47] = KEY_DVD, + [0x49] = KEY_VIDEO, + [0x4b] = KEY_AUX, + [0x10] = KEY_UP, + [0x11] = KEY_LEFT, + [0x12] = KEY_OK, + [0x13] = KEY_RIGHT, + [0x14] = KEY_DOWN, + [0x0f] = KEY_EPG, + [0x16] = KEY_INFO, + [0x4d] = KEY_BACKSPACE, + [0x1c] = KEY_VOLUMEUP, + [0x4c] = KEY_PLAY, + [0x1b] = KEY_CHANNELUP, + [0x1e] = KEY_VOLUMEDOWN, + [0x1d] = KEY_MUTE, + [0x1f] = KEY_CHANNELDOWN, + [0x17] = KEY_RED, + [0x18] = KEY_GREEN, + [0x19] = KEY_YELLOW, + [0x1a] = KEY_BLUE, + [0x58] = KEY_RECORD, + [0x48] = KEY_STOP, + [0x40] = KEY_PAUSE, + [0x54] = KEY_LAST, + [0x4e] = KEY_REWIND, + [0x4f] = KEY_FASTFORWARD, + [0x5c] = KEY_NEXT, +}; +EXPORT_SYMBOL_GPL(ir_codes_terratec_cinergy_xs); + /* EVGA inDtube Devin Heitmueller */ diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 795fd16a5..daa710b36 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -907,6 +907,8 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_TVP5150, .has_dvb = 1, .dvb_gpio = default_digital, + .ir_codes = ir_codes_terratec_cinergy_xs, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, diff --git a/linux/include/media/ir-common.h b/linux/include/media/ir-common.h index 9dcb632f6..922bad37d 100644 --- a/linux/include/media/ir-common.h +++ b/linux/include/media/ir-common.h @@ -163,6 +163,7 @@ extern IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_kaiomy[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_dm1105_nec[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_evga_indtube[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_terratec_cinergy_xs[IR_KEYTAB_SIZE]; #endif -- cgit v1.2.3 From 33a9a00f41338f5ab7589a2611da979c87b884ce Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 4 Aug 2009 19:52:37 -0300 Subject: em28xx: fix V4L2 API compliance: don't expose audio inputs for devices without it From: Mauro Carvalho Chehab V4L2 API (chapter 1.5) states that: Drivers must implement all input ioctls when the device has one or more inputs, all output ioctls when the device has one or more outputs. When the device has any audio inputs or outputs the driver must set the V4L2_CAP_AUDIO flag in the struct v4l2_capability returned by the VIDIOC_QUERYCAP ioctl. So, devices without audio input should return -EINVAL. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index fcb99fb71..258cd38ce 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -976,6 +976,9 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; + if (!dev->audio_mode.has_audio) + return -EINVAL; + switch (a->index) { case EM28XX_AMUX_VIDEO: strcpy(a->name, "Television"); @@ -1022,6 +1025,9 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) return -EINVAL; #else + if (!dev->audio_mode.has_audio) + return -EINVAL; + if (a->index >= MAX_EM28XX_INPUT) return -EINVAL; if (0 == INPUT(a->index)->type) @@ -1444,9 +1450,11 @@ static int vidioc_querycap(struct file *file, void *priv, #endif V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (dev->audio_mode.has_audio) + cap->capabilities |= V4L2_CAP_AUDIO; + if (dev->tuner_type != TUNER_ABSENT) cap->capabilities |= V4L2_CAP_TUNER; -- cgit v1.2.3 From 3d106fc1f1a62d4ad61ba0ab78a9171508a97f16 Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Sun, 9 Aug 2009 19:22:20 +0200 Subject: radio-si470x: separate common and usb code This patch is a preceding work to add the i2c interface of si470x. The si470x directory includes a common file and usb specific file and header file. The part unrelated with usb interface and i2c interface exists in radio-si470x-common.c file, and The usb specific part exists in radio-si470x-usb.c file. Signed-off-by: Joonyoung Shim Small changes, due to new include "linux/smp_lock.h". Acked-by: Tobias Lorenz --- linux/drivers/media/radio/Kconfig | 26 +- linux/drivers/media/radio/Makefile | 2 +- linux/drivers/media/radio/radio-si470x.c | 1870 -------------------- linux/drivers/media/radio/si470x/Kconfig | 24 + linux/drivers/media/radio/si470x/Makefile | 7 + .../media/radio/si470x/radio-si470x-common.c | 794 +++++++++ .../drivers/media/radio/si470x/radio-si470x-usb.c | 958 ++++++++++ linux/drivers/media/radio/si470x/radio-si470x.h | 202 +++ 8 files changed, 1989 insertions(+), 1892 deletions(-) delete mode 100644 linux/drivers/media/radio/radio-si470x.c create mode 100644 linux/drivers/media/radio/si470x/Kconfig create mode 100644 linux/drivers/media/radio/si470x/Makefile create mode 100644 linux/drivers/media/radio/si470x/radio-si470x-common.c create mode 100644 linux/drivers/media/radio/si470x/radio-si470x-usb.c create mode 100644 linux/drivers/media/radio/si470x/radio-si470x.h --- linux/drivers/media/radio/Kconfig | 26 +- linux/drivers/media/radio/Makefile | 2 +- linux/drivers/media/radio/radio-si470x.c | 1870 -------------------- linux/drivers/media/radio/si470x/Kconfig | 24 + linux/drivers/media/radio/si470x/Makefile | 7 + .../media/radio/si470x/radio-si470x-common.c | 794 +++++++++ .../drivers/media/radio/si470x/radio-si470x-usb.c | 958 ++++++++++ linux/drivers/media/radio/si470x/radio-si470x.h | 202 +++ 8 files changed, 1990 insertions(+), 1893 deletions(-) delete mode 100644 linux/drivers/media/radio/radio-si470x.c create mode 100644 linux/drivers/media/radio/si470x/Kconfig create mode 100644 linux/drivers/media/radio/si470x/Makefile create mode 100644 linux/drivers/media/radio/si470x/radio-si470x-common.c create mode 100644 linux/drivers/media/radio/si470x/radio-si470x-usb.c create mode 100644 linux/drivers/media/radio/si470x/radio-si470x.h diff --git a/linux/drivers/media/radio/Kconfig b/linux/drivers/media/radio/Kconfig index 3315cac87..41bbec6df 100644 --- a/linux/drivers/media/radio/Kconfig +++ b/linux/drivers/media/radio/Kconfig @@ -351,29 +351,11 @@ config USB_DSBR To compile this driver as a module, choose M here: the module will be called dsbr100. -config USB_SI470X - tristate "Silicon Labs Si470x FM Radio Receiver support" - depends on USB && VIDEO_V4L2 - ---help--- - This is a driver for USB devices with the Silicon Labs SI470x - chip. Currently these devices are known to work: - - 10c4:818a: Silicon Labs USB FM Radio Reference Design - - 06e1:a155: ADS/Tech FM Radio Receiver (formerly Instant FM Music) - - 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700 (FM700) - - Sound is provided by the ALSA USB Audio/MIDI driver. Therefore - if you don't want to use the device solely for RDS receiving, - it is recommended to also select SND_USB_AUDIO. - - Please have a look at the documentation, especially on how - to redirect the audio stream from the radio to your sound device: - Documentation/video4linux/si470x.txt - - Say Y here if you want to connect this type of radio to your - computer's USB port. +config RADIO_SI470X + bool "Silicon Labs Si470x FM Radio Receiver support" + depends on VIDEO_V4L2 - To compile this driver as a module, choose M here: the - module will be called radio-si470x. +source "drivers/media/radio/si470x/Kconfig" config USB_MR800 tristate "AverMedia MR 800 USB FM radio support" diff --git a/linux/drivers/media/radio/Makefile b/linux/drivers/media/radio/Makefile index 0f2b35b3e..18111e1fe 100644 --- a/linux/drivers/media/radio/Makefile +++ b/linux/drivers/media/radio/Makefile @@ -17,7 +17,7 @@ obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o obj-$(CONFIG_RADIO_TRUST) += radio-trust.o obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o obj-$(CONFIG_USB_DSBR) += dsbr100.o -obj-$(CONFIG_USB_SI470X) += radio-si470x.o +obj-$(CONFIG_RADIO_SI470X) += si470x/ obj-$(CONFIG_USB_MR800) += radio-mr800.o obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o diff --git a/linux/drivers/media/radio/radio-si470x.c b/linux/drivers/media/radio/radio-si470x.c deleted file mode 100644 index 5b4cab3d0..000000000 --- a/linux/drivers/media/radio/radio-si470x.c +++ /dev/null @@ -1,1870 +0,0 @@ -/* - * drivers/media/radio/radio-si470x.c - * - * Driver for USB radios for the Silicon Labs Si470x FM Radio Receivers: - * - Silicon Labs USB FM Radio Reference Design - * - ADS/Tech FM Radio Receiver (formerly Instant FM Music) (RDX-155-EF) - * - KWorld USB FM Radio SnapMusic Mobile 700 (FM700) - * - Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) - * - * Copyright (c) 2009 Tobias Lorenz - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -/* - * History: - * 2008-01-12 Tobias Lorenz - * Version 1.0.0 - * - First working version - * 2008-01-13 Tobias Lorenz - * Version 1.0.1 - * - Improved error handling, every function now returns errno - * - Improved multi user access (start/mute/stop) - * - Channel doesn't get lost anymore after start/mute/stop - * - RDS support added (polling mode via interrupt EP 1) - * - marked default module parameters with *value* - * - switched from bit structs to bit masks - * - header file cleaned and integrated - * 2008-01-14 Tobias Lorenz - * Version 1.0.2 - * - hex values are now lower case - * - commented USB ID for ADS/Tech moved on todo list - * - blacklisted si470x in hid-quirks.c - * - rds buffer handling functions integrated into *_work, *_read - * - rds_command in si470x_poll exchanged against simple retval - * - check for firmware version 15 - * - code order and prototypes still remain the same - * - spacing and bottom of band codes remain the same - * 2008-01-16 Tobias Lorenz - * Version 1.0.3 - * - code reordered to avoid function prototypes - * - switch/case defaults are now more user-friendly - * - unified comment style - * - applied all checkpatch.pl v1.12 suggestions - * except the warning about the too long lines with bit comments - * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl) - * 2008-01-22 Tobias Lorenz - * Version 1.0.4 - * - avoid poss. locking when doing copy_to_user which may sleep - * - RDS is automatically activated on read now - * - code cleaned of unnecessary rds_commands - * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified - * (thanks to Guillaume RAMOUSSE) - * 2008-01-27 Tobias Lorenz - * Version 1.0.5 - * - number of seek_retries changed to tune_timeout - * - fixed problem with incomplete tune operations by own buffers - * - optimization of variables and printf types - * - improved error logging - * 2008-01-31 Tobias Lorenz - * Oliver Neukum - * Version 1.0.6 - * - fixed coverity checker warnings in *_usb_driver_disconnect - * - probe()/open() race by correct ordering in probe() - * - DMA coherency rules by separate allocation of all buffers - * - use of endianness macros - * - abuse of spinlock, replaced by mutex - * - racy handling of timer in disconnect, - * replaced by delayed_work - * - racy interruptible_sleep_on(), - * replaced with wait_event_interruptible() - * - handle signals in read() - * 2008-02-08 Tobias Lorenz - * Oliver Neukum - * Version 1.0.7 - * - usb autosuspend support - * - unplugging fixed - * 2008-05-07 Tobias Lorenz - * Version 1.0.8 - * - hardware frequency seek support - * - afc indication - * - more safety checks, let si470x_get_freq return errno - * - vidioc behavior corrected according to v4l2 spec - * 2008-10-20 Alexey Klimov - * - add support for KWorld USB FM Radio FM700 - * - blacklisted KWorld radio in hid-core.c and hid-ids.h - * 2008-12-03 Mark Lord - * - add support for DealExtreme USB Radio - * 2009-01-31 Bob Ross - * - correction of stereo detection/setting - * - correction of signal strength indicator scaling - * 2009-01-31 Rick Bronson - * Tobias Lorenz - * - add LED status output - * - get HW/SW version from scratchpad - * 2009-06-16 Edouard Lafargue - * Version 1.0.10 - * - add support for interrupt mode for RDS endpoint, - * instead of polling. - * Improves RDS reception significantly - * - * ToDo: - * - add firmware download/update support - */ - - -/* driver definitions */ -#define DRIVER_AUTHOR "Tobias Lorenz " -#define DRIVER_NAME "radio-si470x" -#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 10) -#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" -#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" -#define DRIVER_VERSION "1.0.10" - - -/* kernel includes */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "compat.h" -#include -#include -#include -#include -#include -#include - - -/* USB Device ID List */ -static struct usb_device_id si470x_usb_driver_id_table[] = { - /* Silicon Labs USB FM Radio Reference Design */ - { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) }, - /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */ - { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) }, - /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */ - { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) }, - /* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */ - { USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) }, - /* Terminating entry */ - { } -}; -MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table); - - - -/************************************************************************** - * Module Parameters - **************************************************************************/ - -/* Radio Nr */ -static int radio_nr = -1; -module_param(radio_nr, int, 0444); -MODULE_PARM_DESC(radio_nr, "Radio Nr"); - -/* Spacing (kHz) */ -/* 0: 200 kHz (USA, Australia) */ -/* 1: 100 kHz (Europe, Japan) */ -/* 2: 50 kHz */ -static unsigned short space = 2; -module_param(space, ushort, 0444); -MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*"); - -/* Bottom of Band (MHz) */ -/* 0: 87.5 - 108 MHz (USA, Europe)*/ -/* 1: 76 - 108 MHz (Japan wide band) */ -/* 2: 76 - 90 MHz (Japan) */ -static unsigned short band = 1; -module_param(band, ushort, 0444); -MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz"); - -/* De-emphasis */ -/* 0: 75 us (USA) */ -/* 1: 50 us (Europe, Australia, Japan) */ -static unsigned short de = 1; -module_param(de, ushort, 0444); -MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*"); - -/* USB timeout */ -static unsigned int usb_timeout = 500; -module_param(usb_timeout, uint, 0644); -MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*"); - -/* Tune timeout */ -static unsigned int tune_timeout = 3000; -module_param(tune_timeout, uint, 0644); -MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*"); - -/* Seek timeout */ -static unsigned int seek_timeout = 5000; -module_param(seek_timeout, uint, 0644); -MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*"); - -/* RDS buffer blocks */ -static unsigned int rds_buf = 100; -module_param(rds_buf, uint, 0444); -MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*"); - -/* RDS maximum block errors */ -static unsigned short max_rds_errors = 1; -/* 0 means 0 errors requiring correction */ -/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */ -/* 2 means 3-5 errors requiring correction */ -/* 3 means 6+ errors or errors in checkword, correction not possible */ -module_param(max_rds_errors, ushort, 0644); -MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); - - -/************************************************************************** - * Register Definitions - **************************************************************************/ -#define RADIO_REGISTER_SIZE 2 /* 16 register bit width */ -#define RADIO_REGISTER_NUM 16 /* DEVICEID ... RDSD */ -#define RDS_REGISTER_NUM 6 /* STATUSRSSI ... RDSD */ - -#define DEVICEID 0 /* Device ID */ -#define DEVICEID_PN 0xf000 /* bits 15..12: Part Number */ -#define DEVICEID_MFGID 0x0fff /* bits 11..00: Manufacturer ID */ - -#define CHIPID 1 /* Chip ID */ -#define CHIPID_REV 0xfc00 /* bits 15..10: Chip Version */ -#define CHIPID_DEV 0x0200 /* bits 09..09: Device */ -#define CHIPID_FIRMWARE 0x01ff /* bits 08..00: Firmware Version */ - -#define POWERCFG 2 /* Power Configuration */ -#define POWERCFG_DSMUTE 0x8000 /* bits 15..15: Softmute Disable */ -#define POWERCFG_DMUTE 0x4000 /* bits 14..14: Mute Disable */ -#define POWERCFG_MONO 0x2000 /* bits 13..13: Mono Select */ -#define POWERCFG_RDSM 0x0800 /* bits 11..11: RDS Mode (Si4701 only) */ -#define POWERCFG_SKMODE 0x0400 /* bits 10..10: Seek Mode */ -#define POWERCFG_SEEKUP 0x0200 /* bits 09..09: Seek Direction */ -#define POWERCFG_SEEK 0x0100 /* bits 08..08: Seek */ -#define POWERCFG_DISABLE 0x0040 /* bits 06..06: Powerup Disable */ -#define POWERCFG_ENABLE 0x0001 /* bits 00..00: Powerup Enable */ - -#define CHANNEL 3 /* Channel */ -#define CHANNEL_TUNE 0x8000 /* bits 15..15: Tune */ -#define CHANNEL_CHAN 0x03ff /* bits 09..00: Channel Select */ - -#define SYSCONFIG1 4 /* System Configuration 1 */ -#define SYSCONFIG1_RDSIEN 0x8000 /* bits 15..15: RDS Interrupt Enable (Si4701 only) */ -#define SYSCONFIG1_STCIEN 0x4000 /* bits 14..14: Seek/Tune Complete Interrupt Enable */ -#define SYSCONFIG1_RDS 0x1000 /* bits 12..12: RDS Enable (Si4701 only) */ -#define SYSCONFIG1_DE 0x0800 /* bits 11..11: De-emphasis (0=75us 1=50us) */ -#define SYSCONFIG1_AGCD 0x0400 /* bits 10..10: AGC Disable */ -#define SYSCONFIG1_BLNDADJ 0x00c0 /* bits 07..06: Stereo/Mono Blend Level Adjustment */ -#define SYSCONFIG1_GPIO3 0x0030 /* bits 05..04: General Purpose I/O 3 */ -#define SYSCONFIG1_GPIO2 0x000c /* bits 03..02: General Purpose I/O 2 */ -#define SYSCONFIG1_GPIO1 0x0003 /* bits 01..00: General Purpose I/O 1 */ - -#define SYSCONFIG2 5 /* System Configuration 2 */ -#define SYSCONFIG2_SEEKTH 0xff00 /* bits 15..08: RSSI Seek Threshold */ -#define SYSCONFIG2_BAND 0x0080 /* bits 07..06: Band Select */ -#define SYSCONFIG2_SPACE 0x0030 /* bits 05..04: Channel Spacing */ -#define SYSCONFIG2_VOLUME 0x000f /* bits 03..00: Volume */ - -#define SYSCONFIG3 6 /* System Configuration 3 */ -#define SYSCONFIG3_SMUTER 0xc000 /* bits 15..14: Softmute Attack/Recover Rate */ -#define SYSCONFIG3_SMUTEA 0x3000 /* bits 13..12: Softmute Attenuation */ -#define SYSCONFIG3_SKSNR 0x00f0 /* bits 07..04: Seek SNR Threshold */ -#define SYSCONFIG3_SKCNT 0x000f /* bits 03..00: Seek FM Impulse Detection Threshold */ - -#define TEST1 7 /* Test 1 */ -#define TEST1_AHIZEN 0x4000 /* bits 14..14: Audio High-Z Enable */ - -#define TEST2 8 /* Test 2 */ -/* TEST2 only contains reserved bits */ - -#define BOOTCONFIG 9 /* Boot Configuration */ -/* BOOTCONFIG only contains reserved bits */ - -#define STATUSRSSI 10 /* Status RSSI */ -#define STATUSRSSI_RDSR 0x8000 /* bits 15..15: RDS Ready (Si4701 only) */ -#define STATUSRSSI_STC 0x4000 /* bits 14..14: Seek/Tune Complete */ -#define STATUSRSSI_SF 0x2000 /* bits 13..13: Seek Fail/Band Limit */ -#define STATUSRSSI_AFCRL 0x1000 /* bits 12..12: AFC Rail */ -#define STATUSRSSI_RDSS 0x0800 /* bits 11..11: RDS Synchronized (Si4701 only) */ -#define STATUSRSSI_BLERA 0x0600 /* bits 10..09: RDS Block A Errors (Si4701 only) */ -#define STATUSRSSI_ST 0x0100 /* bits 08..08: Stereo Indicator */ -#define STATUSRSSI_RSSI 0x00ff /* bits 07..00: RSSI (Received Signal Strength Indicator) */ - -#define READCHAN 11 /* Read Channel */ -#define READCHAN_BLERB 0xc000 /* bits 15..14: RDS Block D Errors (Si4701 only) */ -#define READCHAN_BLERC 0x3000 /* bits 13..12: RDS Block C Errors (Si4701 only) */ -#define READCHAN_BLERD 0x0c00 /* bits 11..10: RDS Block B Errors (Si4701 only) */ -#define READCHAN_READCHAN 0x03ff /* bits 09..00: Read Channel */ - -#define RDSA 12 /* RDSA */ -#define RDSA_RDSA 0xffff /* bits 15..00: RDS Block A Data (Si4701 only) */ - -#define RDSB 13 /* RDSB */ -#define RDSB_RDSB 0xffff /* bits 15..00: RDS Block B Data (Si4701 only) */ - -#define RDSC 14 /* RDSC */ -#define RDSC_RDSC 0xffff /* bits 15..00: RDS Block C Data (Si4701 only) */ - -#define RDSD 15 /* RDSD */ -#define RDSD_RDSD 0xffff /* bits 15..00: RDS Block D Data (Si4701 only) */ - - - -/************************************************************************** - * USB HID Reports - **************************************************************************/ - -/* Reports 1-16 give direct read/write access to the 16 Si470x registers */ -/* with the (REPORT_ID - 1) corresponding to the register address across USB */ -/* endpoint 0 using GET_REPORT and SET_REPORT */ -#define REGISTER_REPORT_SIZE (RADIO_REGISTER_SIZE + 1) -#define REGISTER_REPORT(reg) ((reg) + 1) - -/* Report 17 gives direct read/write access to the entire Si470x register */ -/* map across endpoint 0 using GET_REPORT and SET_REPORT */ -#define ENTIRE_REPORT_SIZE (RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1) -#define ENTIRE_REPORT 17 - -/* Report 18 is used to send the lowest 6 Si470x registers up the HID */ -/* interrupt endpoint 1 to Windows every 20 milliseconds for status */ -#define RDS_REPORT_SIZE (RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1) -#define RDS_REPORT 18 - -/* Report 19: LED state */ -#define LED_REPORT_SIZE 3 -#define LED_REPORT 19 - -/* Report 19: stream */ -#define STREAM_REPORT_SIZE 3 -#define STREAM_REPORT 19 - -/* Report 20: scratch */ -#define SCRATCH_PAGE_SIZE 63 -#define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1) -#define SCRATCH_REPORT 20 - -/* Reports 19-22: flash upgrade of the C8051F321 */ -#define WRITE_REPORT_SIZE 4 -#define WRITE_REPORT 19 -#define FLASH_REPORT_SIZE 64 -#define FLASH_REPORT 20 -#define CRC_REPORT_SIZE 3 -#define CRC_REPORT 21 -#define RESPONSE_REPORT_SIZE 2 -#define RESPONSE_REPORT 22 - -/* Report 23: currently unused, but can accept 60 byte reports on the HID */ -/* interrupt out endpoint 2 every 1 millisecond */ -#define UNUSED_REPORT 23 - - - -/************************************************************************** - * Software/Hardware Versions - **************************************************************************/ -#define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6 -#define RADIO_SW_VERSION 7 -#define RADIO_SW_VERSION_CURRENT 15 -#define RADIO_HW_VERSION 1 - -#define SCRATCH_PAGE_SW_VERSION 1 -#define SCRATCH_PAGE_HW_VERSION 2 - - - -/************************************************************************** - * LED State Definitions - **************************************************************************/ -#define LED_COMMAND 0x35 - -#define NO_CHANGE_LED 0x00 -#define ALL_COLOR_LED 0x01 /* streaming state */ -#define BLINK_GREEN_LED 0x02 /* connect state */ -#define BLINK_RED_LED 0x04 -#define BLINK_ORANGE_LED 0x10 /* disconnect state */ -#define SOLID_GREEN_LED 0x20 /* tuning/seeking state */ -#define SOLID_RED_LED 0x40 /* bootload state */ -#define SOLID_ORANGE_LED 0x80 - - - -/************************************************************************** - * Stream State Definitions - **************************************************************************/ -#define STREAM_COMMAND 0x36 -#define STREAM_VIDPID 0x00 -#define STREAM_AUDIO 0xff - - - -/************************************************************************** - * Bootloader / Flash Commands - **************************************************************************/ - -/* unique id sent to bootloader and required to put into a bootload state */ -#define UNIQUE_BL_ID 0x34 - -/* mask for the flash data */ -#define FLASH_DATA_MASK 0x55 - -/* bootloader commands */ -#define GET_SW_VERSION_COMMAND 0x00 -#define SET_PAGE_COMMAND 0x01 -#define ERASE_PAGE_COMMAND 0x02 -#define WRITE_PAGE_COMMAND 0x03 -#define CRC_ON_PAGE_COMMAND 0x04 -#define READ_FLASH_BYTE_COMMAND 0x05 -#define RESET_DEVICE_COMMAND 0x06 -#define GET_HW_VERSION_COMMAND 0x07 -#define BLANK 0xff - -/* bootloader command responses */ -#define COMMAND_OK 0x01 -#define COMMAND_FAILED 0x02 -#define COMMAND_PENDING 0x03 - - - -/************************************************************************** - * General Driver Definitions - **************************************************************************/ - -/* - * si470x_device - private data - */ -struct si470x_device { - /* reference to USB and video device */ - struct usb_device *usbdev; - struct usb_interface *intf; - struct video_device *videodev; - - /* Interrupt endpoint handling */ - char *int_in_buffer; - struct usb_endpoint_descriptor *int_in_endpoint; - struct urb *int_in_urb; - int int_in_running; - - /* driver management */ - unsigned int users; - unsigned char disconnected; - struct mutex disconnect_lock; - - /* Silabs internal registers (0..15) */ - unsigned short registers[RADIO_REGISTER_NUM]; - - /* RDS receive buffer */ - wait_queue_head_t read_queue; - struct mutex lock; /* buffer locking */ - unsigned char *buffer; /* size is always multiple of three */ - unsigned int buf_size; - unsigned int rd_index; - unsigned int wr_index; - - /* scratch page */ - unsigned char software_version; - unsigned char hardware_version; -}; - - -/* - * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW, - * 62.5 kHz otherwise. - * The tuner is able to have a channel spacing of 50, 100 or 200 kHz. - * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW - * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000 - */ -#define FREQ_MUL (1000000 / 62.5) - - - -/************************************************************************** - * General Driver Functions - REGISTER_REPORTs - **************************************************************************/ - -/* - * si470x_get_report - receive a HID report - */ -static int si470x_get_report(struct si470x_device *radio, void *buf, int size) -{ - unsigned char *report = (unsigned char *) buf; - int retval; - - retval = usb_control_msg(radio->usbdev, - usb_rcvctrlpipe(radio->usbdev, 0), - HID_REQ_GET_REPORT, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - report[0], 2, - buf, size, usb_timeout); - - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": si470x_get_report: usb_control_msg returned %d\n", - retval); - return retval; -} - - -/* - * si470x_set_report - send a HID report - */ -static int si470x_set_report(struct si470x_device *radio, void *buf, int size) -{ - unsigned char *report = (unsigned char *) buf; - int retval; - - retval = usb_control_msg(radio->usbdev, - usb_sndctrlpipe(radio->usbdev, 0), - HID_REQ_SET_REPORT, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - report[0], 2, - buf, size, usb_timeout); - - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": si470x_set_report: usb_control_msg returned %d\n", - retval); - return retval; -} - - -/* - * si470x_get_register - read register - */ -static int si470x_get_register(struct si470x_device *radio, int regnr) -{ - unsigned char buf[REGISTER_REPORT_SIZE]; - int retval; - - buf[0] = REGISTER_REPORT(regnr); - - retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); - - if (retval >= 0) - radio->registers[regnr] = get_unaligned_be16(&buf[1]); - - return (retval < 0) ? -EINVAL : 0; -} - - -/* - * si470x_set_register - write register - */ -static int si470x_set_register(struct si470x_device *radio, int regnr) -{ - unsigned char buf[REGISTER_REPORT_SIZE]; - int retval; - - buf[0] = REGISTER_REPORT(regnr); - put_unaligned_be16(radio->registers[regnr], &buf[1]); - - retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); - - return (retval < 0) ? -EINVAL : 0; -} - - -/* - * si470x_set_chan - set the channel - */ -static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) -{ - int retval; - unsigned long timeout; - bool timed_out = 0; - - /* start tuning */ - radio->registers[CHANNEL] &= ~CHANNEL_CHAN; - radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; - retval = si470x_set_register(radio, CHANNEL); - if (retval < 0) - goto done; - - /* wait till tune operation has completed */ - timeout = jiffies + msecs_to_jiffies(tune_timeout); - do { - retval = si470x_get_register(radio, STATUSRSSI); - if (retval < 0) - goto stop; - timed_out = time_after(jiffies, timeout); - } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && - (!timed_out)); - if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) - printk(KERN_WARNING DRIVER_NAME ": tune does not complete\n"); - if (timed_out) - printk(KERN_WARNING DRIVER_NAME - ": tune timed out after %u ms\n", tune_timeout); - -stop: - /* stop tuning */ - radio->registers[CHANNEL] &= ~CHANNEL_TUNE; - retval = si470x_set_register(radio, CHANNEL); - -done: - return retval; -} - - -/* - * si470x_get_freq - get the frequency - */ -static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) -{ - unsigned int spacing, band_bottom; - unsigned short chan; - int retval; - - /* Spacing (kHz) */ - switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) { - /* 0: 200 kHz (USA, Australia) */ - case 0: - spacing = 0.200 * FREQ_MUL; break; - /* 1: 100 kHz (Europe, Japan) */ - case 1: - spacing = 0.100 * FREQ_MUL; break; - /* 2: 50 kHz */ - default: - spacing = 0.050 * FREQ_MUL; break; - }; - - /* Bottom of Band (MHz) */ - switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { - /* 0: 87.5 - 108 MHz (USA, Europe) */ - case 0: - band_bottom = 87.5 * FREQ_MUL; break; - /* 1: 76 - 108 MHz (Japan wide band) */ - default: - band_bottom = 76 * FREQ_MUL; break; - /* 2: 76 - 90 MHz (Japan) */ - case 2: - band_bottom = 76 * FREQ_MUL; break; - }; - - /* read channel */ - retval = si470x_get_register(radio, READCHAN); - chan = radio->registers[READCHAN] & READCHAN_READCHAN; - - /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */ - *freq = chan * spacing + band_bottom; - - return retval; -} - - -/* - * si470x_set_freq - set the frequency - */ -static int si470x_set_freq(struct si470x_device *radio, unsigned int freq) -{ - unsigned int spacing, band_bottom; - unsigned short chan; - - /* Spacing (kHz) */ - switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) { - /* 0: 200 kHz (USA, Australia) */ - case 0: - spacing = 0.200 * FREQ_MUL; break; - /* 1: 100 kHz (Europe, Japan) */ - case 1: - spacing = 0.100 * FREQ_MUL; break; - /* 2: 50 kHz */ - default: - spacing = 0.050 * FREQ_MUL; break; - }; - - /* Bottom of Band (MHz) */ - switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { - /* 0: 87.5 - 108 MHz (USA, Europe) */ - case 0: - band_bottom = 87.5 * FREQ_MUL; break; - /* 1: 76 - 108 MHz (Japan wide band) */ - default: - band_bottom = 76 * FREQ_MUL; break; - /* 2: 76 - 90 MHz (Japan) */ - case 2: - band_bottom = 76 * FREQ_MUL; break; - }; - - /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */ - chan = (freq - band_bottom) / spacing; - - return si470x_set_chan(radio, chan); -} - - -/* - * si470x_set_seek - set seek - */ -static int si470x_set_seek(struct si470x_device *radio, - unsigned int wrap_around, unsigned int seek_upward) -{ - int retval = 0; - unsigned long timeout; - bool timed_out = 0; - - /* start seeking */ - radio->registers[POWERCFG] |= POWERCFG_SEEK; - if (wrap_around == 1) - radio->registers[POWERCFG] &= ~POWERCFG_SKMODE; - else - radio->registers[POWERCFG] |= POWERCFG_SKMODE; - if (seek_upward == 1) - radio->registers[POWERCFG] |= POWERCFG_SEEKUP; - else - radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; - retval = si470x_set_register(radio, POWERCFG); - if (retval < 0) - goto done; - - /* wait till seek operation has completed */ - timeout = jiffies + msecs_to_jiffies(seek_timeout); - do { - retval = si470x_get_register(radio, STATUSRSSI); - if (retval < 0) - goto stop; - timed_out = time_after(jiffies, timeout); - } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && - (!timed_out)); - if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) - printk(KERN_WARNING DRIVER_NAME ": seek does not complete\n"); - if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) - printk(KERN_WARNING DRIVER_NAME - ": seek failed / band limit reached\n"); - if (timed_out) - printk(KERN_WARNING DRIVER_NAME - ": seek timed out after %u ms\n", seek_timeout); - -stop: - /* stop seeking */ - radio->registers[POWERCFG] &= ~POWERCFG_SEEK; - retval = si470x_set_register(radio, POWERCFG); - -done: - /* try again, if timed out */ - if ((retval == 0) && timed_out) - retval = -EAGAIN; - - return retval; -} - - -/* - * si470x_start - switch on radio - */ -static int si470x_start(struct si470x_device *radio) -{ - int retval; - - /* powercfg */ - radio->registers[POWERCFG] = - POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM; - retval = si470x_set_register(radio, POWERCFG); - if (retval < 0) - goto done; - - /* sysconfig 1 */ - radio->registers[SYSCONFIG1] = SYSCONFIG1_DE; - retval = si470x_set_register(radio, SYSCONFIG1); - if (retval < 0) - goto done; - - /* sysconfig 2 */ - radio->registers[SYSCONFIG2] = - (0x3f << 8) | /* SEEKTH */ - ((band << 6) & SYSCONFIG2_BAND) | /* BAND */ - ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */ - 15; /* VOLUME (max) */ - retval = si470x_set_register(radio, SYSCONFIG2); - if (retval < 0) - goto done; - - /* reset last channel */ - retval = si470x_set_chan(radio, - radio->registers[CHANNEL] & CHANNEL_CHAN); - -done: - return retval; -} - - -/* - * si470x_stop - switch off radio - */ -static int si470x_stop(struct si470x_device *radio) -{ - int retval; - - /* sysconfig 1 */ - radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; - retval = si470x_set_register(radio, SYSCONFIG1); - if (retval < 0) - goto done; - - /* powercfg */ - radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; - /* POWERCFG_ENABLE has to automatically go low */ - radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE; - retval = si470x_set_register(radio, POWERCFG); - -done: - return retval; -} - - -/* - * si470x_rds_on - switch on rds reception - */ -static int si470x_rds_on(struct si470x_device *radio) -{ - int retval; - - /* sysconfig 1 */ - mutex_lock(&radio->lock); - radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS; - retval = si470x_set_register(radio, SYSCONFIG1); - if (retval < 0) - radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; - mutex_unlock(&radio->lock); - - return retval; -} - - - -/************************************************************************** - * General Driver Functions - ENTIRE_REPORT - **************************************************************************/ - -/* - * si470x_get_all_registers - read entire registers - */ -static int si470x_get_all_registers(struct si470x_device *radio) -{ - unsigned char buf[ENTIRE_REPORT_SIZE]; - int retval; - unsigned char regnr; - - buf[0] = ENTIRE_REPORT; - - retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); - - if (retval >= 0) - for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) - radio->registers[regnr] = get_unaligned_be16( - &buf[regnr * RADIO_REGISTER_SIZE + 1]); - - return (retval < 0) ? -EINVAL : 0; -} - - - -/************************************************************************** - * General Driver Functions - LED_REPORT - **************************************************************************/ - -/* - * si470x_set_led_state - sets the led state - */ -static int si470x_set_led_state(struct si470x_device *radio, - unsigned char led_state) -{ - unsigned char buf[LED_REPORT_SIZE]; - int retval; - - buf[0] = LED_REPORT; - buf[1] = LED_COMMAND; - buf[2] = led_state; - - retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); - - return (retval < 0) ? -EINVAL : 0; -} - - - -/************************************************************************** - * General Driver Functions - SCRATCH_REPORT - **************************************************************************/ - -/* - * si470x_get_scratch_versions - gets the scratch page and version infos - */ -static int si470x_get_scratch_page_versions(struct si470x_device *radio) -{ - unsigned char buf[SCRATCH_REPORT_SIZE]; - int retval; - - buf[0] = SCRATCH_REPORT; - - retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); - - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME ": si470x_get_scratch: " - "si470x_get_report returned %d\n", retval); - else { - radio->software_version = buf[1]; - radio->hardware_version = buf[2]; - } - - return (retval < 0) ? -EINVAL : 0; -} - - - -/************************************************************************** - * RDS Driver Functions - **************************************************************************/ - -/* - * si470x_int_in_callback - rds callback and processing function - * - * TODO: do we need to use mutex locks in some sections? - */ -static void si470x_int_in_callback(struct urb *urb) -{ - struct si470x_device *radio = urb->context; - unsigned char buf[RDS_REPORT_SIZE]; - int retval; - unsigned char regnr; - unsigned char blocknum; - unsigned short bler; /* rds block errors */ - unsigned short rds; - unsigned char tmpbuf[3]; - - if (urb->status) { - if (urb->status == -ENOENT || - urb->status == -ECONNRESET || - urb->status == -ESHUTDOWN) { - return; - } else { - printk(KERN_WARNING DRIVER_NAME - ": non-zero urb status (%d)\n", urb->status); - goto resubmit; /* Maybe we can recover. */ - } - } - - /* safety checks */ - if (radio->disconnected) - return; - if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) - goto resubmit; - - if (urb->actual_length > 0) { - /* Update RDS registers with URB data */ - buf[0] = RDS_REPORT; - for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) - radio->registers[STATUSRSSI + regnr] = - get_unaligned_be16(&radio->int_in_buffer[ - regnr * RADIO_REGISTER_SIZE + 1]); - /* get rds blocks */ - if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) { - /* No RDS group ready, better luck next time */ - goto resubmit; - } - if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) { - /* RDS decoder not synchronized */ - goto resubmit; - } - for (blocknum = 0; blocknum < 4; blocknum++) { - switch (blocknum) { - default: - bler = (radio->registers[STATUSRSSI] & - STATUSRSSI_BLERA) >> 9; - rds = radio->registers[RDSA]; - break; - case 1: - bler = (radio->registers[READCHAN] & - READCHAN_BLERB) >> 14; - rds = radio->registers[RDSB]; - break; - case 2: - bler = (radio->registers[READCHAN] & - READCHAN_BLERC) >> 12; - rds = radio->registers[RDSC]; - break; - case 3: - bler = (radio->registers[READCHAN] & - READCHAN_BLERD) >> 10; - rds = radio->registers[RDSD]; - break; - }; - - /* Fill the V4L2 RDS buffer */ - put_unaligned_le16(rds, &tmpbuf); - tmpbuf[2] = blocknum; /* offset name */ - tmpbuf[2] |= blocknum << 3; /* received offset */ - if (bler > max_rds_errors) - tmpbuf[2] |= 0x80; /* uncorrectable errors */ - else if (bler > 0) - tmpbuf[2] |= 0x40; /* corrected error(s) */ - - /* copy RDS block to internal buffer */ - memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3); - radio->wr_index += 3; - - /* wrap write pointer */ - if (radio->wr_index >= radio->buf_size) - radio->wr_index = 0; - - /* check for overflow */ - if (radio->wr_index == radio->rd_index) { - /* increment and wrap read pointer */ - radio->rd_index += 3; - if (radio->rd_index >= radio->buf_size) - radio->rd_index = 0; - } - } - if (radio->wr_index != radio->rd_index) - wake_up_interruptible(&radio->read_queue); - } - -resubmit: - /* Resubmit if we're still running. */ - if (radio->int_in_running && radio->usbdev) { - retval = usb_submit_urb(radio->int_in_urb, GFP_ATOMIC); - if (retval) { - printk(KERN_WARNING DRIVER_NAME - ": resubmitting urb failed (%d)", retval); - radio->int_in_running = 0; - } - } -} - - - -/************************************************************************** - * File Operations Interface - **************************************************************************/ - -/* - * si470x_fops_read - read RDS data - */ -static ssize_t si470x_fops_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - unsigned int block_count = 0; - - /* switch on rds reception */ - if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) { - si470x_rds_on(radio); - } - - /* block if no new data available */ - while (radio->wr_index == radio->rd_index) { - if (file->f_flags & O_NONBLOCK) { - retval = -EWOULDBLOCK; - goto done; - } - if (wait_event_interruptible(radio->read_queue, - radio->wr_index != radio->rd_index) < 0) { - retval = -EINTR; - goto done; - } - } - - /* calculate block count from byte count */ - count /= 3; - - /* copy RDS block out of internal buffer and to user buffer */ - mutex_lock(&radio->lock); - while (block_count < count) { - if (radio->rd_index == radio->wr_index) - break; - - /* always transfer rds complete blocks */ - if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3)) - /* retval = -EFAULT; */ - break; - - /* increment and wrap read pointer */ - radio->rd_index += 3; - if (radio->rd_index >= radio->buf_size) - radio->rd_index = 0; - - /* increment counters */ - block_count++; - buf += 3; - retval += 3; - } - mutex_unlock(&radio->lock); - -done: - return retval; -} - - -/* - * si470x_fops_poll - poll RDS data - */ -static unsigned int si470x_fops_poll(struct file *file, - struct poll_table_struct *pts) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* switch on rds reception */ - if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) { - si470x_rds_on(radio); - } - - poll_wait(file, &radio->read_queue, pts); - - if (radio->rd_index != radio->wr_index) - retval = POLLIN | POLLRDNORM; - - return retval; -} - - -/* - * si470x_fops_open - file open - */ -static int si470x_fops_open(struct file *file) -{ - struct si470x_device *radio = video_drvdata(file); - int retval; - - lock_kernel(); - radio->users++; - - retval = usb_autopm_get_interface(radio->intf); - if (retval < 0) { - radio->users--; - retval = -EIO; - goto done; - } - - if (radio->users == 1) { - /* start radio */ - retval = si470x_start(radio); - if (retval < 0) { - usb_autopm_put_interface(radio->intf); - goto done; - } - - /* initialize interrupt urb */ - usb_fill_int_urb(radio->int_in_urb, radio->usbdev, - usb_rcvintpipe(radio->usbdev, - radio->int_in_endpoint->bEndpointAddress), - radio->int_in_buffer, - le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize), - si470x_int_in_callback, - radio, - radio->int_in_endpoint->bInterval); - - radio->int_in_running = 1; - mb(); - - retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL); - if (retval) { - printk(KERN_INFO DRIVER_NAME - ": submitting int urb failed (%d)\n", retval); - radio->int_in_running = 0; - usb_autopm_put_interface(radio->intf); - } - } - -done: - unlock_kernel(); - return retval; -} - - -/* - * si470x_fops_release - file release - */ -static int si470x_fops_release(struct file *file) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety check */ - if (!radio) { - retval = -ENODEV; - goto done; - } - - mutex_lock(&radio->disconnect_lock); - radio->users--; - if (radio->users == 0) { - /* shutdown interrupt handler */ - if (radio->int_in_running) { - radio->int_in_running = 0; - if (radio->int_in_urb) - usb_kill_urb(radio->int_in_urb); - } - - if (radio->disconnected) { - video_unregister_device(radio->videodev); - kfree(radio->int_in_buffer); - kfree(radio->buffer); - kfree(radio); - goto unlock; - } - - /* cancel read processes */ - wake_up_interruptible(&radio->read_queue); - - /* stop radio */ - retval = si470x_stop(radio); - usb_autopm_put_interface(radio->intf); - } -unlock: - mutex_unlock(&radio->disconnect_lock); -done: - return retval; -} - - -/* - * si470x_fops - file operations interface - */ -static const struct v4l2_file_operations si470x_fops = { - .owner = THIS_MODULE, - .read = si470x_fops_read, - .poll = si470x_fops_poll, - .ioctl = video_ioctl2, - .open = si470x_fops_open, - .release = si470x_fops_release, -}; - - - -/************************************************************************** - * Video4Linux Interface - **************************************************************************/ - -/* - * si470x_vidioc_querycap - query device capabilities - */ -static int si470x_vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *capability) -{ - struct si470x_device *radio = video_drvdata(file); - - strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); - strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); - usb_make_path(radio->usbdev, capability->bus_info, sizeof(capability->bus_info)); - capability->version = DRIVER_KERNEL_VERSION; - capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | - V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; - - return 0; -} - - -/* - * si470x_vidioc_queryctrl - enumerate control items - */ -static int si470x_vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - int retval = -EINVAL; - - /* abort if qc->id is below V4L2_CID_BASE */ - if (qc->id < V4L2_CID_BASE) - goto done; - - /* search video control */ - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 15, 1, 15); - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - } - - /* disable unsupported base controls */ - /* to satisfy kradio and such apps */ - if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) { - qc->flags = V4L2_CTRL_FLAG_DISABLED; - retval = 0; - } - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": query controls failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_g_ctrl - get the value of a control - */ -static int si470x_vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety checks */ - if (radio->disconnected) { - retval = -EIO; - goto done; - } - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = radio->registers[SYSCONFIG2] & - SYSCONFIG2_VOLUME; - break; - case V4L2_CID_AUDIO_MUTE: - ctrl->value = ((radio->registers[POWERCFG] & - POWERCFG_DMUTE) == 0) ? 1 : 0; - break; - default: - retval = -EINVAL; - } - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": get control failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_s_ctrl - set the value of a control - */ -static int si470x_vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety checks */ - if (radio->disconnected) { - retval = -EIO; - goto done; - } - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME; - radio->registers[SYSCONFIG2] |= ctrl->value; - retval = si470x_set_register(radio, SYSCONFIG2); - break; - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value == 1) - radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; - else - radio->registers[POWERCFG] |= POWERCFG_DMUTE; - retval = si470x_set_register(radio, POWERCFG); - break; - default: - retval = -EINVAL; - } - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": set control failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_g_audio - get audio attributes - */ -static int si470x_vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *audio) -{ - /* driver constants */ - audio->index = 0; - strcpy(audio->name, "Radio"); - audio->capability = V4L2_AUDCAP_STEREO; - audio->mode = 0; - - return 0; -} - - -/* - * si470x_vidioc_g_tuner - get tuner attributes - */ -static int si470x_vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *tuner) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety checks */ - if (radio->disconnected) { - retval = -EIO; - goto done; - } - if (tuner->index != 0) { - retval = -EINVAL; - goto done; - } - - retval = si470x_get_register(radio, STATUSRSSI); - if (retval < 0) - goto done; - - /* driver constants */ - strcpy(tuner->name, "FM"); - tuner->type = V4L2_TUNER_RADIO; - tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_RDS; - - /* range limits */ - switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { - /* 0: 87.5 - 108 MHz (USA, Europe, default) */ - default: - tuner->rangelow = 87.5 * FREQ_MUL; - tuner->rangehigh = 108 * FREQ_MUL; - break; - /* 1: 76 - 108 MHz (Japan wide band) */ - case 1 : - tuner->rangelow = 76 * FREQ_MUL; - tuner->rangehigh = 108 * FREQ_MUL; - break; - /* 2: 76 - 90 MHz (Japan) */ - case 2 : - tuner->rangelow = 76 * FREQ_MUL; - tuner->rangehigh = 90 * FREQ_MUL; - break; - }; - - /* stereo indicator == stereo (instead of mono) */ - if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) - tuner->rxsubchans = V4L2_TUNER_SUB_MONO; - else - tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - /* If there is a reliable method of detecting an RDS channel, - then this code should check for that before setting this - RDS subchannel. */ - tuner->rxsubchans |= V4L2_TUNER_SUB_RDS; - - /* mono/stereo selector */ - if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0) - tuner->audmode = V4L2_TUNER_MODE_STEREO; - else - tuner->audmode = V4L2_TUNER_MODE_MONO; - - /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ - /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */ - tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); - /* the ideal factor is 0xffff/75 = 873,8 */ - tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); - - /* automatic frequency control: -1: freq to low, 1 freq to high */ - /* AFCRL does only indicate that freq. differs, not if too low/high */ - tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0; - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": get tuner failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_s_tuner - set tuner attributes - */ -static int si470x_vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *tuner) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = -EINVAL; - - /* safety checks */ - if (radio->disconnected) { - retval = -EIO; - goto done; - } - if (tuner->index != 0) - goto done; - - /* mono/stereo selector */ - switch (tuner->audmode) { - case V4L2_TUNER_MODE_MONO: - radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */ - break; - case V4L2_TUNER_MODE_STEREO: - radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */ - break; - default: - goto done; - } - - retval = si470x_set_register(radio, POWERCFG); - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": set tuner failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_g_frequency - get tuner or modulator radio frequency - */ -static int si470x_vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *freq) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety checks */ - if (radio->disconnected) { - retval = -EIO; - goto done; - } - if (freq->tuner != 0) { - retval = -EINVAL; - goto done; - } - - freq->type = V4L2_TUNER_RADIO; - retval = si470x_get_freq(radio, &freq->frequency); - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": get frequency failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_s_frequency - set tuner or modulator radio frequency - */ -static int si470x_vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *freq) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety checks */ - if (radio->disconnected) { - retval = -EIO; - goto done; - } - if (freq->tuner != 0) { - retval = -EINVAL; - goto done; - } - - retval = si470x_set_freq(radio, freq->frequency); - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": set frequency failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek - */ -static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, - struct v4l2_hw_freq_seek *seek) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety checks */ - if (radio->disconnected) { - retval = -EIO; - goto done; - } - if (seek->tuner != 0) { - retval = -EINVAL; - goto done; - } - - retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); - -done: - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": set hardware frequency seek failed with %d\n", - retval); - return retval; -} - - -/* - * si470x_ioctl_ops - video device ioctl operations - */ -static const struct v4l2_ioctl_ops si470x_ioctl_ops = { - .vidioc_querycap = si470x_vidioc_querycap, - .vidioc_queryctrl = si470x_vidioc_queryctrl, - .vidioc_g_ctrl = si470x_vidioc_g_ctrl, - .vidioc_s_ctrl = si470x_vidioc_s_ctrl, - .vidioc_g_audio = si470x_vidioc_g_audio, - .vidioc_g_tuner = si470x_vidioc_g_tuner, - .vidioc_s_tuner = si470x_vidioc_s_tuner, - .vidioc_g_frequency = si470x_vidioc_g_frequency, - .vidioc_s_frequency = si470x_vidioc_s_frequency, - .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek, -}; - - -/* - * si470x_viddev_template - video device interface - */ -static struct video_device si470x_viddev_template = { - .fops = &si470x_fops, - .name = DRIVER_NAME, - .release = video_device_release, - .ioctl_ops = &si470x_ioctl_ops, -}; - - - -/************************************************************************** - * USB Interface - **************************************************************************/ - -/* - * si470x_usb_driver_probe - probe for the device - */ -static int si470x_usb_driver_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct si470x_device *radio; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - int i, int_end_size, retval = 0; - - /* private data allocation and initialization */ - radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); - if (!radio) { - retval = -ENOMEM; - goto err_initial; - } - radio->users = 0; - radio->disconnected = 0; - radio->usbdev = interface_to_usbdev(intf); - radio->intf = intf; - mutex_init(&radio->disconnect_lock); - mutex_init(&radio->lock); - - iface_desc = intf->cur_altsetting; - - /* Set up interrupt endpoint information. */ - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == - USB_DIR_IN) && ((endpoint->bmAttributes & - USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) - radio->int_in_endpoint = endpoint; - } - if (!radio->int_in_endpoint) { - printk(KERN_INFO DRIVER_NAME - ": could not find interrupt in endpoint\n"); - retval = -EIO; - goto err_radio; - } - - int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize); - - radio->int_in_buffer = kmalloc(int_end_size, GFP_KERNEL); - if (!radio->int_in_buffer) { - printk(KERN_INFO DRIVER_NAME - "could not allocate int_in_buffer"); - retval = -ENOMEM; - goto err_radio; - } - - radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!radio->int_in_urb) { - printk(KERN_INFO DRIVER_NAME "could not allocate int_in_urb"); - retval = -ENOMEM; - goto err_intbuffer; - } - - /* video device allocation and initialization */ - radio->videodev = video_device_alloc(); - if (!radio->videodev) { - retval = -ENOMEM; - goto err_intbuffer; - } - memcpy(radio->videodev, &si470x_viddev_template, - sizeof(si470x_viddev_template)); - video_set_drvdata(radio->videodev, radio); - - /* show some infos about the specific si470x device */ - if (si470x_get_all_registers(radio) < 0) { - retval = -EIO; - goto err_video; - } - printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", - radio->registers[DEVICEID], radio->registers[CHIPID]); - - /* get software and hardware versions */ - if (si470x_get_scratch_page_versions(radio) < 0) { - retval = -EIO; - goto err_video; - } - printk(KERN_INFO DRIVER_NAME - ": software version %d, hardware version %d\n", - radio->software_version, radio->hardware_version); - - /* check if device and firmware is current */ - if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) - < RADIO_SW_VERSION_CURRENT) { - printk(KERN_WARNING DRIVER_NAME - ": This driver is known to work with " - "firmware version %hu,\n", RADIO_SW_VERSION_CURRENT); - printk(KERN_WARNING DRIVER_NAME - ": but the device has firmware version %hu.\n", - radio->registers[CHIPID] & CHIPID_FIRMWARE); - printk(KERN_WARNING DRIVER_NAME - ": If you have some trouble using this driver,\n"); - printk(KERN_WARNING DRIVER_NAME - ": please report to V4L ML at " - "linux-media@vger.kernel.org\n"); - } - - /* set initial frequency */ - si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ - - /* set led to connect state */ - si470x_set_led_state(radio, BLINK_GREEN_LED); - - /* rds buffer allocation */ - radio->buf_size = rds_buf * 3; - radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); - if (!radio->buffer) { - retval = -EIO; - goto err_video; - } - - /* rds buffer configuration */ - radio->wr_index = 0; - radio->rd_index = 0; - init_waitqueue_head(&radio->read_queue); - - /* register video device */ - retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr); - if (retval) { - printk(KERN_WARNING DRIVER_NAME - ": Could not register video device\n"); - goto err_all; - } - usb_set_intfdata(intf, radio); - - return 0; -err_all: - kfree(radio->buffer); -err_video: - video_device_release(radio->videodev); -err_intbuffer: - kfree(radio->int_in_buffer); -err_radio: - kfree(radio); -err_initial: - return retval; -} - - -/* - * si470x_usb_driver_suspend - suspend the device - */ -static int si470x_usb_driver_suspend(struct usb_interface *intf, - pm_message_t message) -{ - printk(KERN_INFO DRIVER_NAME ": suspending now...\n"); - - return 0; -} - - -/* - * si470x_usb_driver_resume - resume the device - */ -static int si470x_usb_driver_resume(struct usb_interface *intf) -{ - printk(KERN_INFO DRIVER_NAME ": resuming now...\n"); - - return 0; -} - - -/* - * si470x_usb_driver_disconnect - disconnect the device - */ -static void si470x_usb_driver_disconnect(struct usb_interface *intf) -{ - struct si470x_device *radio = usb_get_intfdata(intf); - - mutex_lock(&radio->disconnect_lock); - radio->disconnected = 1; - usb_set_intfdata(intf, NULL); - if (radio->users == 0) { - /* set led to disconnect state */ - si470x_set_led_state(radio, BLINK_ORANGE_LED); - - /* Free data structures. */ - usb_free_urb(radio->int_in_urb); - - kfree(radio->int_in_buffer); - video_unregister_device(radio->videodev); - kfree(radio->buffer); - kfree(radio); - } - mutex_unlock(&radio->disconnect_lock); -} - - -/* - * si470x_usb_driver - usb driver interface - */ -static struct usb_driver si470x_usb_driver = { - .name = DRIVER_NAME, - .probe = si470x_usb_driver_probe, - .disconnect = si470x_usb_driver_disconnect, - .suspend = si470x_usb_driver_suspend, - .resume = si470x_usb_driver_resume, - .id_table = si470x_usb_driver_id_table, - .supports_autosuspend = 1, -}; - - - -/************************************************************************** - * Module Interface - **************************************************************************/ - -/* - * si470x_module_init - module init - */ -static int __init si470x_module_init(void) -{ - printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n"); - return usb_register(&si470x_usb_driver); -} - - -/* - * si470x_module_exit - module exit - */ -static void __exit si470x_module_exit(void) -{ - usb_deregister(&si470x_usb_driver); -} - - -module_init(si470x_module_init); -module_exit(si470x_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_VERSION(DRIVER_VERSION); diff --git a/linux/drivers/media/radio/si470x/Kconfig b/linux/drivers/media/radio/si470x/Kconfig new file mode 100644 index 000000000..20d05c04f --- /dev/null +++ b/linux/drivers/media/radio/si470x/Kconfig @@ -0,0 +1,24 @@ +config USB_SI470X + tristate "Silicon Labs Si470x FM Radio Receiver support with USB" + depends on USB && RADIO_SI470X + ---help--- + This is a driver for USB devices with the Silicon Labs SI470x + chip. Currently these devices are known to work: + - 10c4:818a: Silicon Labs USB FM Radio Reference Design + - 06e1:a155: ADS/Tech FM Radio Receiver (formerly Instant FM Music) + - 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700 (FM700) + - 10c5:819a: Sanei Electric FM USB Radio (aka DealExtreme.com PCear) + + Sound is provided by the ALSA USB Audio/MIDI driver. Therefore + if you don't want to use the device solely for RDS receiving, + it is recommended to also select SND_USB_AUDIO. + + Please have a look at the documentation, especially on how + to redirect the audio stream from the radio to your sound device: + Documentation/video4linux/si470x.txt + + Say Y here if you want to connect this type of radio to your + computer's USB port. + + To compile this driver as a module, choose M here: the + module will be called radio-usb-si470x. diff --git a/linux/drivers/media/radio/si470x/Makefile b/linux/drivers/media/radio/si470x/Makefile new file mode 100644 index 000000000..3cb777fe3 --- /dev/null +++ b/linux/drivers/media/radio/si470x/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for radios with Silicon Labs Si470x FM Radio Receivers +# + +radio-usb-si470x-objs := radio-si470x-usb.o radio-si470x-common.o + +obj-$(CONFIG_USB_SI470X) += radio-usb-si470x.o diff --git a/linux/drivers/media/radio/si470x/radio-si470x-common.c b/linux/drivers/media/radio/si470x/radio-si470x-common.c new file mode 100644 index 000000000..b64559d56 --- /dev/null +++ b/linux/drivers/media/radio/si470x/radio-si470x-common.c @@ -0,0 +1,794 @@ +/* + * drivers/media/radio/si470x/radio-si470x-common.c + * + * Driver for radios with Silicon Labs Si470x FM Radio Receivers + * + * Copyright (c) 2009 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * History: + * 2008-01-12 Tobias Lorenz + * Version 1.0.0 + * - First working version + * 2008-01-13 Tobias Lorenz + * Version 1.0.1 + * - Improved error handling, every function now returns errno + * - Improved multi user access (start/mute/stop) + * - Channel doesn't get lost anymore after start/mute/stop + * - RDS support added (polling mode via interrupt EP 1) + * - marked default module parameters with *value* + * - switched from bit structs to bit masks + * - header file cleaned and integrated + * 2008-01-14 Tobias Lorenz + * Version 1.0.2 + * - hex values are now lower case + * - commented USB ID for ADS/Tech moved on todo list + * - blacklisted si470x in hid-quirks.c + * - rds buffer handling functions integrated into *_work, *_read + * - rds_command in si470x_poll exchanged against simple retval + * - check for firmware version 15 + * - code order and prototypes still remain the same + * - spacing and bottom of band codes remain the same + * 2008-01-16 Tobias Lorenz + * Version 1.0.3 + * - code reordered to avoid function prototypes + * - switch/case defaults are now more user-friendly + * - unified comment style + * - applied all checkpatch.pl v1.12 suggestions + * except the warning about the too long lines with bit comments + * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl) + * 2008-01-22 Tobias Lorenz + * Version 1.0.4 + * - avoid poss. locking when doing copy_to_user which may sleep + * - RDS is automatically activated on read now + * - code cleaned of unnecessary rds_commands + * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified + * (thanks to Guillaume RAMOUSSE) + * 2008-01-27 Tobias Lorenz + * Version 1.0.5 + * - number of seek_retries changed to tune_timeout + * - fixed problem with incomplete tune operations by own buffers + * - optimization of variables and printf types + * - improved error logging + * 2008-01-31 Tobias Lorenz + * Oliver Neukum + * Version 1.0.6 + * - fixed coverity checker warnings in *_usb_driver_disconnect + * - probe()/open() race by correct ordering in probe() + * - DMA coherency rules by separate allocation of all buffers + * - use of endianness macros + * - abuse of spinlock, replaced by mutex + * - racy handling of timer in disconnect, + * replaced by delayed_work + * - racy interruptible_sleep_on(), + * replaced with wait_event_interruptible() + * - handle signals in read() + * 2008-02-08 Tobias Lorenz + * Oliver Neukum + * Version 1.0.7 + * - usb autosuspend support + * - unplugging fixed + * 2008-05-07 Tobias Lorenz + * Version 1.0.8 + * - hardware frequency seek support + * - afc indication + * - more safety checks, let si470x_get_freq return errno + * - vidioc behavior corrected according to v4l2 spec + * 2008-10-20 Alexey Klimov + * - add support for KWorld USB FM Radio FM700 + * - blacklisted KWorld radio in hid-core.c and hid-ids.h + * 2008-12-03 Mark Lord + * - add support for DealExtreme USB Radio + * 2009-01-31 Bob Ross + * - correction of stereo detection/setting + * - correction of signal strength indicator scaling + * 2009-01-31 Rick Bronson + * Tobias Lorenz + * - add LED status output + * - get HW/SW version from scratchpad + * 2009-06-16 Edouard Lafargue + * Version 1.0.10 + * - add support for interrupt mode for RDS endpoint, + * instead of polling. + * Improves RDS reception significantly + */ + + +/* kernel includes */ +#include "radio-si470x.h" + + + +/************************************************************************** + * Module Parameters + **************************************************************************/ + +/* Spacing (kHz) */ +/* 0: 200 kHz (USA, Australia) */ +/* 1: 100 kHz (Europe, Japan) */ +/* 2: 50 kHz */ +static unsigned short space = 2; +module_param(space, ushort, 0444); +MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*"); + +/* Bottom of Band (MHz) */ +/* 0: 87.5 - 108 MHz (USA, Europe)*/ +/* 1: 76 - 108 MHz (Japan wide band) */ +/* 2: 76 - 90 MHz (Japan) */ +static unsigned short band = 1; +module_param(band, ushort, 0444); +MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz"); + +/* De-emphasis */ +/* 0: 75 us (USA) */ +/* 1: 50 us (Europe, Australia, Japan) */ +static unsigned short de = 1; +module_param(de, ushort, 0444); +MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*"); + +/* Tune timeout */ +static unsigned int tune_timeout = 3000; +module_param(tune_timeout, uint, 0644); +MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*"); + +/* Seek timeout */ +static unsigned int seek_timeout = 5000; +module_param(seek_timeout, uint, 0644); +MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*"); + + + +/************************************************************************** + * Generic Functions + **************************************************************************/ + +/* + * si470x_set_chan - set the channel + */ +static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) +{ + int retval; + unsigned long timeout; + bool timed_out = 0; + + /* start tuning */ + radio->registers[CHANNEL] &= ~CHANNEL_CHAN; + radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; + retval = si470x_set_register(radio, CHANNEL); + if (retval < 0) + goto done; + + /* wait till tune operation has completed */ + timeout = jiffies + msecs_to_jiffies(tune_timeout); + do { + retval = si470x_get_register(radio, STATUSRSSI); + if (retval < 0) + goto stop; + timed_out = time_after(jiffies, timeout); + } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && + (!timed_out)); + if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) + printk(KERN_WARNING DRIVER_NAME ": tune does not complete\n"); + if (timed_out) + printk(KERN_WARNING DRIVER_NAME + ": tune timed out after %u ms\n", tune_timeout); + +stop: + /* stop tuning */ + radio->registers[CHANNEL] &= ~CHANNEL_TUNE; + retval = si470x_set_register(radio, CHANNEL); + +done: + return retval; +} + + +/* + * si470x_get_freq - get the frequency + */ +static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) +{ + unsigned int spacing, band_bottom; + unsigned short chan; + int retval; + + /* Spacing (kHz) */ + switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) { + /* 0: 200 kHz (USA, Australia) */ + case 0: + spacing = 0.200 * FREQ_MUL; break; + /* 1: 100 kHz (Europe, Japan) */ + case 1: + spacing = 0.100 * FREQ_MUL; break; + /* 2: 50 kHz */ + default: + spacing = 0.050 * FREQ_MUL; break; + }; + + /* Bottom of Band (MHz) */ + switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { + /* 0: 87.5 - 108 MHz (USA, Europe) */ + case 0: + band_bottom = 87.5 * FREQ_MUL; break; + /* 1: 76 - 108 MHz (Japan wide band) */ + default: + band_bottom = 76 * FREQ_MUL; break; + /* 2: 76 - 90 MHz (Japan) */ + case 2: + band_bottom = 76 * FREQ_MUL; break; + }; + + /* read channel */ + retval = si470x_get_register(radio, READCHAN); + chan = radio->registers[READCHAN] & READCHAN_READCHAN; + + /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */ + *freq = chan * spacing + band_bottom; + + return retval; +} + + +/* + * si470x_set_freq - set the frequency + */ +int si470x_set_freq(struct si470x_device *radio, unsigned int freq) +{ + unsigned int spacing, band_bottom; + unsigned short chan; + + /* Spacing (kHz) */ + switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) { + /* 0: 200 kHz (USA, Australia) */ + case 0: + spacing = 0.200 * FREQ_MUL; break; + /* 1: 100 kHz (Europe, Japan) */ + case 1: + spacing = 0.100 * FREQ_MUL; break; + /* 2: 50 kHz */ + default: + spacing = 0.050 * FREQ_MUL; break; + }; + + /* Bottom of Band (MHz) */ + switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { + /* 0: 87.5 - 108 MHz (USA, Europe) */ + case 0: + band_bottom = 87.5 * FREQ_MUL; break; + /* 1: 76 - 108 MHz (Japan wide band) */ + default: + band_bottom = 76 * FREQ_MUL; break; + /* 2: 76 - 90 MHz (Japan) */ + case 2: + band_bottom = 76 * FREQ_MUL; break; + }; + + /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */ + chan = (freq - band_bottom) / spacing; + + return si470x_set_chan(radio, chan); +} + + +/* + * si470x_set_seek - set seek + */ +static int si470x_set_seek(struct si470x_device *radio, + unsigned int wrap_around, unsigned int seek_upward) +{ + int retval = 0; + unsigned long timeout; + bool timed_out = 0; + + /* start seeking */ + radio->registers[POWERCFG] |= POWERCFG_SEEK; + if (wrap_around == 1) + radio->registers[POWERCFG] &= ~POWERCFG_SKMODE; + else + radio->registers[POWERCFG] |= POWERCFG_SKMODE; + if (seek_upward == 1) + radio->registers[POWERCFG] |= POWERCFG_SEEKUP; + else + radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; + retval = si470x_set_register(radio, POWERCFG); + if (retval < 0) + goto done; + + /* wait till seek operation has completed */ + timeout = jiffies + msecs_to_jiffies(seek_timeout); + do { + retval = si470x_get_register(radio, STATUSRSSI); + if (retval < 0) + goto stop; + timed_out = time_after(jiffies, timeout); + } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && + (!timed_out)); + if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) + printk(KERN_WARNING DRIVER_NAME ": seek does not complete\n"); + if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) + printk(KERN_WARNING DRIVER_NAME + ": seek failed / band limit reached\n"); + if (timed_out) + printk(KERN_WARNING DRIVER_NAME + ": seek timed out after %u ms\n", seek_timeout); + +stop: + /* stop seeking */ + radio->registers[POWERCFG] &= ~POWERCFG_SEEK; + retval = si470x_set_register(radio, POWERCFG); + +done: + /* try again, if timed out */ + if ((retval == 0) && timed_out) + retval = -EAGAIN; + + return retval; +} + + +/* + * si470x_start - switch on radio + */ +int si470x_start(struct si470x_device *radio) +{ + int retval; + + /* powercfg */ + radio->registers[POWERCFG] = + POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM; + retval = si470x_set_register(radio, POWERCFG); + if (retval < 0) + goto done; + + /* sysconfig 1 */ + radio->registers[SYSCONFIG1] = SYSCONFIG1_DE; + retval = si470x_set_register(radio, SYSCONFIG1); + if (retval < 0) + goto done; + + /* sysconfig 2 */ + radio->registers[SYSCONFIG2] = + (0x3f << 8) | /* SEEKTH */ + ((band << 6) & SYSCONFIG2_BAND) | /* BAND */ + ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */ + 15; /* VOLUME (max) */ + retval = si470x_set_register(radio, SYSCONFIG2); + if (retval < 0) + goto done; + + /* reset last channel */ + retval = si470x_set_chan(radio, + radio->registers[CHANNEL] & CHANNEL_CHAN); + +done: + return retval; +} + + +/* + * si470x_stop - switch off radio + */ +int si470x_stop(struct si470x_device *radio) +{ + int retval; + + /* sysconfig 1 */ + radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; + retval = si470x_set_register(radio, SYSCONFIG1); + if (retval < 0) + goto done; + + /* powercfg */ + radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; + /* POWERCFG_ENABLE has to automatically go low */ + radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE; + retval = si470x_set_register(radio, POWERCFG); + +done: + return retval; +} + + +/* + * si470x_rds_on - switch on rds reception + */ +int si470x_rds_on(struct si470x_device *radio) +{ + int retval; + + /* sysconfig 1 */ + mutex_lock(&radio->lock); + radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS; + retval = si470x_set_register(radio, SYSCONFIG1); + if (retval < 0) + radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; + mutex_unlock(&radio->lock); + + return retval; +} + + + +/************************************************************************** + * Video4Linux Interface + **************************************************************************/ + +/* + * si470x_vidioc_queryctrl - enumerate control items + */ +static int si470x_vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int retval = -EINVAL; + + /* abort if qc->id is below V4L2_CID_BASE */ + if (qc->id < V4L2_CID_BASE) + goto done; + + /* search video control */ + switch (qc->id) { + case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 15, 1, 15); + case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); + } + + /* disable unsupported base controls */ + /* to satisfy kradio and such apps */ + if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) { + qc->flags = V4L2_CTRL_FLAG_DISABLED; + retval = 0; + } + +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": query controls failed with %d\n", retval); + return retval; +} + + +/* + * si470x_vidioc_g_ctrl - get the value of a control + */ +static int si470x_vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = radio->registers[SYSCONFIG2] & + SYSCONFIG2_VOLUME; + break; + case V4L2_CID_AUDIO_MUTE: + ctrl->value = ((radio->registers[POWERCFG] & + POWERCFG_DMUTE) == 0) ? 1 : 0; + break; + default: + retval = -EINVAL; + } + +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": get control failed with %d\n", retval); + return retval; +} + + +/* + * si470x_vidioc_s_ctrl - set the value of a control + */ +static int si470x_vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME; + radio->registers[SYSCONFIG2] |= ctrl->value; + retval = si470x_set_register(radio, SYSCONFIG2); + break; + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value == 1) + radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; + else + radio->registers[POWERCFG] |= POWERCFG_DMUTE; + retval = si470x_set_register(radio, POWERCFG); + break; + default: + retval = -EINVAL; + } + +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": set control failed with %d\n", retval); + return retval; +} + + +/* + * si470x_vidioc_g_audio - get audio attributes + */ +static int si470x_vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *audio) +{ + /* driver constants */ + audio->index = 0; + strcpy(audio->name, "Radio"); + audio->capability = V4L2_AUDCAP_STEREO; + audio->mode = 0; + + return 0; +} + + +/* + * si470x_vidioc_g_tuner - get tuner attributes + */ +static int si470x_vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *tuner) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if (tuner->index != 0) { + retval = -EINVAL; + goto done; + } + + retval = si470x_get_register(radio, STATUSRSSI); + if (retval < 0) + goto done; + + /* driver constants */ + strcpy(tuner->name, "FM"); + tuner->type = V4L2_TUNER_RADIO; + tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_RDS; + + /* range limits */ + switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { + /* 0: 87.5 - 108 MHz (USA, Europe, default) */ + default: + tuner->rangelow = 87.5 * FREQ_MUL; + tuner->rangehigh = 108 * FREQ_MUL; + break; + /* 1: 76 - 108 MHz (Japan wide band) */ + case 1: + tuner->rangelow = 76 * FREQ_MUL; + tuner->rangehigh = 108 * FREQ_MUL; + break; + /* 2: 76 - 90 MHz (Japan) */ + case 2: + tuner->rangelow = 76 * FREQ_MUL; + tuner->rangehigh = 90 * FREQ_MUL; + break; + }; + + /* stereo indicator == stereo (instead of mono) */ + if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) + tuner->rxsubchans = V4L2_TUNER_SUB_MONO; + else + tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + /* If there is a reliable method of detecting an RDS channel, + then this code should check for that before setting this + RDS subchannel. */ + tuner->rxsubchans |= V4L2_TUNER_SUB_RDS; + + /* mono/stereo selector */ + if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0) + tuner->audmode = V4L2_TUNER_MODE_STEREO; + else + tuner->audmode = V4L2_TUNER_MODE_MONO; + + /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ + /* measured in units of db쨉V in 1 db increments (max at ~75 db쨉V) */ + tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); + /* the ideal factor is 0xffff/75 = 873,8 */ + tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); + + /* automatic frequency control: -1: freq to low, 1 freq to high */ + /* AFCRL does only indicate that freq. differs, not if too low/high */ + tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0; + +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": get tuner failed with %d\n", retval); + return retval; +} + + +/* + * si470x_vidioc_s_tuner - set tuner attributes + */ +static int si470x_vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *tuner) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = -EINVAL; + + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if (tuner->index != 0) + goto done; + + /* mono/stereo selector */ + switch (tuner->audmode) { + case V4L2_TUNER_MODE_MONO: + radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */ + break; + case V4L2_TUNER_MODE_STEREO: + radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */ + break; + default: + goto done; + } + + retval = si470x_set_register(radio, POWERCFG); + +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": set tuner failed with %d\n", retval); + return retval; +} + + +/* + * si470x_vidioc_g_frequency - get tuner or modulator radio frequency + */ +static int si470x_vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if (freq->tuner != 0) { + retval = -EINVAL; + goto done; + } + + freq->type = V4L2_TUNER_RADIO; + retval = si470x_get_freq(radio, &freq->frequency); + +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": get frequency failed with %d\n", retval); + return retval; +} + + +/* + * si470x_vidioc_s_frequency - set tuner or modulator radio frequency + */ +static int si470x_vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if (freq->tuner != 0) { + retval = -EINVAL; + goto done; + } + + retval = si470x_set_freq(radio, freq->frequency); + +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": set frequency failed with %d\n", retval); + return retval; +} + + +/* + * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek + */ +static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, + struct v4l2_hw_freq_seek *seek) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if (seek->tuner != 0) { + retval = -EINVAL; + goto done; + } + + retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); + +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": set hardware frequency seek failed with %d\n", + retval); + return retval; +} + + +/* + * si470x_ioctl_ops - video device ioctl operations + */ +static const struct v4l2_ioctl_ops si470x_ioctl_ops = { + .vidioc_querycap = si470x_vidioc_querycap, + .vidioc_queryctrl = si470x_vidioc_queryctrl, + .vidioc_g_ctrl = si470x_vidioc_g_ctrl, + .vidioc_s_ctrl = si470x_vidioc_s_ctrl, + .vidioc_g_audio = si470x_vidioc_g_audio, + .vidioc_g_tuner = si470x_vidioc_g_tuner, + .vidioc_s_tuner = si470x_vidioc_s_tuner, + .vidioc_g_frequency = si470x_vidioc_g_frequency, + .vidioc_s_frequency = si470x_vidioc_s_frequency, + .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek, +}; + + +/* + * si470x_viddev_template - video device interface + */ +struct video_device si470x_viddev_template = { + .fops = &si470x_fops, + .name = DRIVER_NAME, + .release = video_device_release, + .ioctl_ops = &si470x_ioctl_ops, +}; diff --git a/linux/drivers/media/radio/si470x/radio-si470x-usb.c b/linux/drivers/media/radio/si470x/radio-si470x-usb.c new file mode 100644 index 000000000..f3d805f70 --- /dev/null +++ b/linux/drivers/media/radio/si470x/radio-si470x-usb.c @@ -0,0 +1,958 @@ +/* + * drivers/media/radio/si470x/radio-si470x-usb.c + * + * USB driver for radios with Silicon Labs Si470x FM Radio Receivers + * + * Copyright (c) 2009 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * ToDo: + * - add firmware download/update support + */ + + +/* driver definitions */ +#define DRIVER_AUTHOR "Tobias Lorenz " +#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 10) +#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" +#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" +#define DRIVER_VERSION "1.0.10" + +/* kernel includes */ +#include +#include + +#include "radio-si470x.h" + + +/* USB Device ID List */ +static struct usb_device_id si470x_usb_driver_id_table[] = { + /* Silicon Labs USB FM Radio Reference Design */ + { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) }, + /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) }, + /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) }, + /* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) }, + /* Terminating entry */ + { } +}; +MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table); + + + +/************************************************************************** + * Module Parameters + **************************************************************************/ + +/* Radio Nr */ +static int radio_nr = -1; +module_param(radio_nr, int, 0444); +MODULE_PARM_DESC(radio_nr, "Radio Nr"); + +/* USB timeout */ +static unsigned int usb_timeout = 500; +module_param(usb_timeout, uint, 0644); +MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*"); + +/* RDS buffer blocks */ +static unsigned int rds_buf = 100; +module_param(rds_buf, uint, 0444); +MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*"); + +/* RDS maximum block errors */ +static unsigned short max_rds_errors = 1; +/* 0 means 0 errors requiring correction */ +/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */ +/* 2 means 3-5 errors requiring correction */ +/* 3 means 6+ errors or errors in checkword, correction not possible */ +module_param(max_rds_errors, ushort, 0644); +MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); + + + +/************************************************************************** + * USB HID Reports + **************************************************************************/ + +/* Reports 1-16 give direct read/write access to the 16 Si470x registers */ +/* with the (REPORT_ID - 1) corresponding to the register address across USB */ +/* endpoint 0 using GET_REPORT and SET_REPORT */ +#define REGISTER_REPORT_SIZE (RADIO_REGISTER_SIZE + 1) +#define REGISTER_REPORT(reg) ((reg) + 1) + +/* Report 17 gives direct read/write access to the entire Si470x register */ +/* map across endpoint 0 using GET_REPORT and SET_REPORT */ +#define ENTIRE_REPORT_SIZE (RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1) +#define ENTIRE_REPORT 17 + +/* Report 18 is used to send the lowest 6 Si470x registers up the HID */ +/* interrupt endpoint 1 to Windows every 20 milliseconds for status */ +#define RDS_REPORT_SIZE (RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1) +#define RDS_REPORT 18 + +/* Report 19: LED state */ +#define LED_REPORT_SIZE 3 +#define LED_REPORT 19 + +/* Report 19: stream */ +#define STREAM_REPORT_SIZE 3 +#define STREAM_REPORT 19 + +/* Report 20: scratch */ +#define SCRATCH_PAGE_SIZE 63 +#define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1) +#define SCRATCH_REPORT 20 + +/* Reports 19-22: flash upgrade of the C8051F321 */ +#define WRITE_REPORT_SIZE 4 +#define WRITE_REPORT 19 +#define FLASH_REPORT_SIZE 64 +#define FLASH_REPORT 20 +#define CRC_REPORT_SIZE 3 +#define CRC_REPORT 21 +#define RESPONSE_REPORT_SIZE 2 +#define RESPONSE_REPORT 22 + +/* Report 23: currently unused, but can accept 60 byte reports on the HID */ +/* interrupt out endpoint 2 every 1 millisecond */ +#define UNUSED_REPORT 23 + + + +/************************************************************************** + * Software/Hardware Versions + **************************************************************************/ +#define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6 +#define RADIO_SW_VERSION 7 +#define RADIO_SW_VERSION_CURRENT 15 +#define RADIO_HW_VERSION 1 + +#define SCRATCH_PAGE_SW_VERSION 1 +#define SCRATCH_PAGE_HW_VERSION 2 + + + +/************************************************************************** + * LED State Definitions + **************************************************************************/ +#define LED_COMMAND 0x35 + +#define NO_CHANGE_LED 0x00 +#define ALL_COLOR_LED 0x01 /* streaming state */ +#define BLINK_GREEN_LED 0x02 /* connect state */ +#define BLINK_RED_LED 0x04 +#define BLINK_ORANGE_LED 0x10 /* disconnect state */ +#define SOLID_GREEN_LED 0x20 /* tuning/seeking state */ +#define SOLID_RED_LED 0x40 /* bootload state */ +#define SOLID_ORANGE_LED 0x80 + + + +/************************************************************************** + * Stream State Definitions + **************************************************************************/ +#define STREAM_COMMAND 0x36 +#define STREAM_VIDPID 0x00 +#define STREAM_AUDIO 0xff + + + +/************************************************************************** + * Bootloader / Flash Commands + **************************************************************************/ + +/* unique id sent to bootloader and required to put into a bootload state */ +#define UNIQUE_BL_ID 0x34 + +/* mask for the flash data */ +#define FLASH_DATA_MASK 0x55 + +/* bootloader commands */ +#define GET_SW_VERSION_COMMAND 0x00 +#define SET_PAGE_COMMAND 0x01 +#define ERASE_PAGE_COMMAND 0x02 +#define WRITE_PAGE_COMMAND 0x03 +#define CRC_ON_PAGE_COMMAND 0x04 +#define READ_FLASH_BYTE_COMMAND 0x05 +#define RESET_DEVICE_COMMAND 0x06 +#define GET_HW_VERSION_COMMAND 0x07 +#define BLANK 0xff + +/* bootloader command responses */ +#define COMMAND_OK 0x01 +#define COMMAND_FAILED 0x02 +#define COMMAND_PENDING 0x03 + + + +/************************************************************************** + * General Driver Functions - REGISTER_REPORTs + **************************************************************************/ + +/* + * si470x_get_report - receive a HID report + */ +static int si470x_get_report(struct si470x_device *radio, void *buf, int size) +{ + unsigned char *report = (unsigned char *) buf; + int retval; + + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + HID_REQ_GET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + report[0], 2, + buf, size, usb_timeout); + + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": si470x_get_report: usb_control_msg returned %d\n", + retval); + return retval; +} + + +/* + * si470x_set_report - send a HID report + */ +static int si470x_set_report(struct si470x_device *radio, void *buf, int size) +{ + unsigned char *report = (unsigned char *) buf; + int retval; + + retval = usb_control_msg(radio->usbdev, + usb_sndctrlpipe(radio->usbdev, 0), + HID_REQ_SET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + report[0], 2, + buf, size, usb_timeout); + + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": si470x_set_report: usb_control_msg returned %d\n", + retval); + return retval; +} + + +/* + * si470x_get_register - read register + */ +int si470x_get_register(struct si470x_device *radio, int regnr) +{ + unsigned char buf[REGISTER_REPORT_SIZE]; + int retval; + + buf[0] = REGISTER_REPORT(regnr); + + retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); + + if (retval >= 0) + radio->registers[regnr] = get_unaligned_be16(&buf[1]); + + return (retval < 0) ? -EINVAL : 0; +} + + +/* + * si470x_set_register - write register + */ +int si470x_set_register(struct si470x_device *radio, int regnr) +{ + unsigned char buf[REGISTER_REPORT_SIZE]; + int retval; + + buf[0] = REGISTER_REPORT(regnr); + put_unaligned_be16(radio->registers[regnr], &buf[1]); + + retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); + + return (retval < 0) ? -EINVAL : 0; +} + + + +/************************************************************************** + * General Driver Functions - ENTIRE_REPORT + **************************************************************************/ + +/* + * si470x_get_all_registers - read entire registers + */ +static int si470x_get_all_registers(struct si470x_device *radio) +{ + unsigned char buf[ENTIRE_REPORT_SIZE]; + int retval; + unsigned char regnr; + + buf[0] = ENTIRE_REPORT; + + retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); + + if (retval >= 0) + for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) + radio->registers[regnr] = get_unaligned_be16( + &buf[regnr * RADIO_REGISTER_SIZE + 1]); + + return (retval < 0) ? -EINVAL : 0; +} + + + +/************************************************************************** + * General Driver Functions - LED_REPORT + **************************************************************************/ + +/* + * si470x_set_led_state - sets the led state + */ +static int si470x_set_led_state(struct si470x_device *radio, + unsigned char led_state) +{ + unsigned char buf[LED_REPORT_SIZE]; + int retval; + + buf[0] = LED_REPORT; + buf[1] = LED_COMMAND; + buf[2] = led_state; + + retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); + + return (retval < 0) ? -EINVAL : 0; +} + + + +/************************************************************************** + * General Driver Functions - SCRATCH_REPORT + **************************************************************************/ + +/* + * si470x_get_scratch_versions - gets the scratch page and version infos + */ +static int si470x_get_scratch_page_versions(struct si470x_device *radio) +{ + unsigned char buf[SCRATCH_REPORT_SIZE]; + int retval; + + buf[0] = SCRATCH_REPORT; + + retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); + + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME ": si470x_get_scratch: " + "si470x_get_report returned %d\n", retval); + else { + radio->software_version = buf[1]; + radio->hardware_version = buf[2]; + } + + return (retval < 0) ? -EINVAL : 0; +} + + + +/************************************************************************** + * RDS Driver Functions + **************************************************************************/ + +/* + * si470x_int_in_callback - rds callback and processing function + * + * TODO: do we need to use mutex locks in some sections? + */ +static void si470x_int_in_callback(struct urb *urb) +{ + struct si470x_device *radio = urb->context; + unsigned char buf[RDS_REPORT_SIZE]; + int retval; + unsigned char regnr; + unsigned char blocknum; + unsigned short bler; /* rds block errors */ + unsigned short rds; + unsigned char tmpbuf[3]; + + if (urb->status) { + if (urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN) { + return; + } else { + printk(KERN_WARNING DRIVER_NAME + ": non-zero urb status (%d)\n", urb->status); + goto resubmit; /* Maybe we can recover. */ + } + } + + /* safety checks */ + if (radio->disconnected) + return; + if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) + goto resubmit; + + if (urb->actual_length > 0) { + /* Update RDS registers with URB data */ + buf[0] = RDS_REPORT; + for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) + radio->registers[STATUSRSSI + regnr] = + get_unaligned_be16(&radio->int_in_buffer[ + regnr * RADIO_REGISTER_SIZE + 1]); + /* get rds blocks */ + if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) { + /* No RDS group ready, better luck next time */ + goto resubmit; + } + if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) { + /* RDS decoder not synchronized */ + goto resubmit; + } + for (blocknum = 0; blocknum < 4; blocknum++) { + switch (blocknum) { + default: + bler = (radio->registers[STATUSRSSI] & + STATUSRSSI_BLERA) >> 9; + rds = radio->registers[RDSA]; + break; + case 1: + bler = (radio->registers[READCHAN] & + READCHAN_BLERB) >> 14; + rds = radio->registers[RDSB]; + break; + case 2: + bler = (radio->registers[READCHAN] & + READCHAN_BLERC) >> 12; + rds = radio->registers[RDSC]; + break; + case 3: + bler = (radio->registers[READCHAN] & + READCHAN_BLERD) >> 10; + rds = radio->registers[RDSD]; + break; + }; + + /* Fill the V4L2 RDS buffer */ + put_unaligned_le16(rds, &tmpbuf); + tmpbuf[2] = blocknum; /* offset name */ + tmpbuf[2] |= blocknum << 3; /* received offset */ + if (bler > max_rds_errors) + tmpbuf[2] |= 0x80; /* uncorrectable errors */ + else if (bler > 0) + tmpbuf[2] |= 0x40; /* corrected error(s) */ + + /* copy RDS block to internal buffer */ + memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3); + radio->wr_index += 3; + + /* wrap write pointer */ + if (radio->wr_index >= radio->buf_size) + radio->wr_index = 0; + + /* check for overflow */ + if (radio->wr_index == radio->rd_index) { + /* increment and wrap read pointer */ + radio->rd_index += 3; + if (radio->rd_index >= radio->buf_size) + radio->rd_index = 0; + } + } + if (radio->wr_index != radio->rd_index) + wake_up_interruptible(&radio->read_queue); + } + +resubmit: + /* Resubmit if we're still running. */ + if (radio->int_in_running && radio->usbdev) { + retval = usb_submit_urb(radio->int_in_urb, GFP_ATOMIC); + if (retval) { + printk(KERN_WARNING DRIVER_NAME + ": resubmitting urb failed (%d)", retval); + radio->int_in_running = 0; + } + } +} + + + +/************************************************************************** + * File Operations Interface + **************************************************************************/ + +/* + * si470x_fops_read - read RDS data + */ +static ssize_t si470x_fops_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + unsigned int block_count = 0; + + /* switch on rds reception */ + if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) + si470x_rds_on(radio); + + /* block if no new data available */ + while (radio->wr_index == radio->rd_index) { + if (file->f_flags & O_NONBLOCK) { + retval = -EWOULDBLOCK; + goto done; + } + if (wait_event_interruptible(radio->read_queue, + radio->wr_index != radio->rd_index) < 0) { + retval = -EINTR; + goto done; + } + } + + /* calculate block count from byte count */ + count /= 3; + + /* copy RDS block out of internal buffer and to user buffer */ + mutex_lock(&radio->lock); + while (block_count < count) { + if (radio->rd_index == radio->wr_index) + break; + + /* always transfer rds complete blocks */ + if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3)) + /* retval = -EFAULT; */ + break; + + /* increment and wrap read pointer */ + radio->rd_index += 3; + if (radio->rd_index >= radio->buf_size) + radio->rd_index = 0; + + /* increment counters */ + block_count++; + buf += 3; + retval += 3; + } + mutex_unlock(&radio->lock); + +done: + return retval; +} + + +/* + * si470x_fops_poll - poll RDS data + */ +static unsigned int si470x_fops_poll(struct file *file, + struct poll_table_struct *pts) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* switch on rds reception */ + if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) + si470x_rds_on(radio); + + poll_wait(file, &radio->read_queue, pts); + + if (radio->rd_index != radio->wr_index) + retval = POLLIN | POLLRDNORM; + + return retval; +} + + +/* + * si470x_fops_open - file open + */ +static int si470x_fops_open(struct file *file) +{ + struct si470x_device *radio = video_drvdata(file); + int retval; + + lock_kernel(); + radio->users++; + + retval = usb_autopm_get_interface(radio->intf); + if (retval < 0) { + radio->users--; + retval = -EIO; + goto done; + } + + if (radio->users == 1) { + /* start radio */ + retval = si470x_start(radio); + if (retval < 0) { + usb_autopm_put_interface(radio->intf); + goto done; + } + + /* initialize interrupt urb */ + usb_fill_int_urb(radio->int_in_urb, radio->usbdev, + usb_rcvintpipe(radio->usbdev, + radio->int_in_endpoint->bEndpointAddress), + radio->int_in_buffer, + le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize), + si470x_int_in_callback, + radio, + radio->int_in_endpoint->bInterval); + + radio->int_in_running = 1; + mb(); + + retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL); + if (retval) { + printk(KERN_INFO DRIVER_NAME + ": submitting int urb failed (%d)\n", retval); + radio->int_in_running = 0; + usb_autopm_put_interface(radio->intf); + } + } + +done: + unlock_kernel(); + return retval; +} + + +/* + * si470x_fops_release - file release + */ +static int si470x_fops_release(struct file *file) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety check */ + if (!radio) { + retval = -ENODEV; + goto done; + } + + mutex_lock(&radio->disconnect_lock); + radio->users--; + if (radio->users == 0) { + /* shutdown interrupt handler */ + if (radio->int_in_running) { + radio->int_in_running = 0; + if (radio->int_in_urb) + usb_kill_urb(radio->int_in_urb); + } + + if (radio->disconnected) { + video_unregister_device(radio->videodev); + kfree(radio->int_in_buffer); + kfree(radio->buffer); + kfree(radio); + goto unlock; + } + + /* cancel read processes */ + wake_up_interruptible(&radio->read_queue); + + /* stop radio */ + retval = si470x_stop(radio); + usb_autopm_put_interface(radio->intf); + } +unlock: + mutex_unlock(&radio->disconnect_lock); +done: + return retval; +} + + +/* + * si470x_fops - file operations interface + */ +const struct v4l2_file_operations si470x_fops = { + .owner = THIS_MODULE, + .read = si470x_fops_read, + .poll = si470x_fops_poll, + .ioctl = video_ioctl2, + .open = si470x_fops_open, + .release = si470x_fops_release, +}; + + + +/************************************************************************** + * Video4Linux Interface + **************************************************************************/ + +/* + * si470x_vidioc_querycap - query device capabilities + */ +int si470x_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) +{ + struct si470x_device *radio = video_drvdata(file); + + strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); + strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); + usb_make_path(radio->usbdev, capability->bus_info, + sizeof(capability->bus_info)); + capability->version = DRIVER_KERNEL_VERSION; + capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | + V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; + + return 0; +} + + + +/************************************************************************** + * USB Interface + **************************************************************************/ + +/* + * si470x_usb_driver_probe - probe for the device + */ +static int si470x_usb_driver_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct si470x_device *radio; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i, int_end_size, retval = 0; + + /* private data allocation and initialization */ + radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); + if (!radio) { + retval = -ENOMEM; + goto err_initial; + } + radio->users = 0; + radio->disconnected = 0; + radio->usbdev = interface_to_usbdev(intf); + radio->intf = intf; + mutex_init(&radio->disconnect_lock); + mutex_init(&radio->lock); + + iface_desc = intf->cur_altsetting; + + /* Set up interrupt endpoint information. */ + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == + USB_DIR_IN) && ((endpoint->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) + radio->int_in_endpoint = endpoint; + } + if (!radio->int_in_endpoint) { + printk(KERN_INFO DRIVER_NAME + ": could not find interrupt in endpoint\n"); + retval = -EIO; + goto err_radio; + } + + int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize); + + radio->int_in_buffer = kmalloc(int_end_size, GFP_KERNEL); + if (!radio->int_in_buffer) { + printk(KERN_INFO DRIVER_NAME + "could not allocate int_in_buffer"); + retval = -ENOMEM; + goto err_radio; + } + + radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!radio->int_in_urb) { + printk(KERN_INFO DRIVER_NAME "could not allocate int_in_urb"); + retval = -ENOMEM; + goto err_intbuffer; + } + + /* video device allocation and initialization */ + radio->videodev = video_device_alloc(); + if (!radio->videodev) { + retval = -ENOMEM; + goto err_intbuffer; + } + memcpy(radio->videodev, &si470x_viddev_template, + sizeof(si470x_viddev_template)); + video_set_drvdata(radio->videodev, radio); + + /* show some infos about the specific si470x device */ + if (si470x_get_all_registers(radio) < 0) { + retval = -EIO; + goto err_video; + } + printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", + radio->registers[DEVICEID], radio->registers[CHIPID]); + + /* get software and hardware versions */ + if (si470x_get_scratch_page_versions(radio) < 0) { + retval = -EIO; + goto err_video; + } + printk(KERN_INFO DRIVER_NAME + ": software version %d, hardware version %d\n", + radio->software_version, radio->hardware_version); + + /* check if device and firmware is current */ + if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) + < RADIO_SW_VERSION_CURRENT) { + printk(KERN_WARNING DRIVER_NAME + ": This driver is known to work with " + "firmware version %hu,\n", RADIO_SW_VERSION_CURRENT); + printk(KERN_WARNING DRIVER_NAME + ": but the device has firmware version %hu.\n", + radio->registers[CHIPID] & CHIPID_FIRMWARE); + printk(KERN_WARNING DRIVER_NAME + ": If you have some trouble using this driver,\n"); + printk(KERN_WARNING DRIVER_NAME + ": please report to V4L ML at " + "linux-media@vger.kernel.org\n"); + } + + /* set initial frequency */ + si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ + + /* set led to connect state */ + si470x_set_led_state(radio, BLINK_GREEN_LED); + + /* rds buffer allocation */ + radio->buf_size = rds_buf * 3; + radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); + if (!radio->buffer) { + retval = -EIO; + goto err_video; + } + + /* rds buffer configuration */ + radio->wr_index = 0; + radio->rd_index = 0; + init_waitqueue_head(&radio->read_queue); + + /* register video device */ + retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, + radio_nr); + if (retval) { + printk(KERN_WARNING DRIVER_NAME + ": Could not register video device\n"); + goto err_all; + } + usb_set_intfdata(intf, radio); + + return 0; +err_all: + kfree(radio->buffer); +err_video: + video_device_release(radio->videodev); +err_intbuffer: + kfree(radio->int_in_buffer); +err_radio: + kfree(radio); +err_initial: + return retval; +} + + +/* + * si470x_usb_driver_suspend - suspend the device + */ +static int si470x_usb_driver_suspend(struct usb_interface *intf, + pm_message_t message) +{ + printk(KERN_INFO DRIVER_NAME ": suspending now...\n"); + + return 0; +} + + +/* + * si470x_usb_driver_resume - resume the device + */ +static int si470x_usb_driver_resume(struct usb_interface *intf) +{ + printk(KERN_INFO DRIVER_NAME ": resuming now...\n"); + + return 0; +} + + +/* + * si470x_usb_driver_disconnect - disconnect the device + */ +static void si470x_usb_driver_disconnect(struct usb_interface *intf) +{ + struct si470x_device *radio = usb_get_intfdata(intf); + + mutex_lock(&radio->disconnect_lock); + radio->disconnected = 1; + usb_set_intfdata(intf, NULL); + if (radio->users == 0) { + /* set led to disconnect state */ + si470x_set_led_state(radio, BLINK_ORANGE_LED); + + /* Free data structures. */ + usb_free_urb(radio->int_in_urb); + + kfree(radio->int_in_buffer); + video_unregister_device(radio->videodev); + kfree(radio->buffer); + kfree(radio); + } + mutex_unlock(&radio->disconnect_lock); +} + + +/* + * si470x_usb_driver - usb driver interface + */ +static struct usb_driver si470x_usb_driver = { + .name = DRIVER_NAME, + .probe = si470x_usb_driver_probe, + .disconnect = si470x_usb_driver_disconnect, + .suspend = si470x_usb_driver_suspend, + .resume = si470x_usb_driver_resume, + .id_table = si470x_usb_driver_id_table, + .supports_autosuspend = 1, +}; + + + +/************************************************************************** + * Module Interface + **************************************************************************/ + +/* + * si470x_module_init - module init + */ +static int __init si470x_module_init(void) +{ + printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n"); + return usb_register(&si470x_usb_driver); +} + + +/* + * si470x_module_exit - module exit + */ +static void __exit si470x_module_exit(void) +{ + usb_deregister(&si470x_usb_driver); +} + + +module_init(si470x_module_init); +module_exit(si470x_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); diff --git a/linux/drivers/media/radio/si470x/radio-si470x.h b/linux/drivers/media/radio/si470x/radio-si470x.h new file mode 100644 index 000000000..88d3ebbe2 --- /dev/null +++ b/linux/drivers/media/radio/si470x/radio-si470x.h @@ -0,0 +1,202 @@ +/* + * drivers/media/radio/si470x/radio-si470x.h + * + * Driver for radios with Silicon Labs Si470x FM Radio Receivers + * + * Copyright (c) 2009 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* driver definitions */ +#define DRIVER_NAME "radio-si470x" + + +/* kernel includes */ +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include +#include +#include +#include +#include +#include + + +/************************************************************************** + * Register Definitions + **************************************************************************/ +#define RADIO_REGISTER_SIZE 2 /* 16 register bit width */ +#define RADIO_REGISTER_NUM 16 /* DEVICEID ... RDSD */ +#define RDS_REGISTER_NUM 6 /* STATUSRSSI ... RDSD */ + +#define DEVICEID 0 /* Device ID */ +#define DEVICEID_PN 0xf000 /* bits 15..12: Part Number */ +#define DEVICEID_MFGID 0x0fff /* bits 11..00: Manufacturer ID */ + +#define CHIPID 1 /* Chip ID */ +#define CHIPID_REV 0xfc00 /* bits 15..10: Chip Version */ +#define CHIPID_DEV 0x0200 /* bits 09..09: Device */ +#define CHIPID_FIRMWARE 0x01ff /* bits 08..00: Firmware Version */ + +#define POWERCFG 2 /* Power Configuration */ +#define POWERCFG_DSMUTE 0x8000 /* bits 15..15: Softmute Disable */ +#define POWERCFG_DMUTE 0x4000 /* bits 14..14: Mute Disable */ +#define POWERCFG_MONO 0x2000 /* bits 13..13: Mono Select */ +#define POWERCFG_RDSM 0x0800 /* bits 11..11: RDS Mode (Si4701 only) */ +#define POWERCFG_SKMODE 0x0400 /* bits 10..10: Seek Mode */ +#define POWERCFG_SEEKUP 0x0200 /* bits 09..09: Seek Direction */ +#define POWERCFG_SEEK 0x0100 /* bits 08..08: Seek */ +#define POWERCFG_DISABLE 0x0040 /* bits 06..06: Powerup Disable */ +#define POWERCFG_ENABLE 0x0001 /* bits 00..00: Powerup Enable */ + +#define CHANNEL 3 /* Channel */ +#define CHANNEL_TUNE 0x8000 /* bits 15..15: Tune */ +#define CHANNEL_CHAN 0x03ff /* bits 09..00: Channel Select */ + +#define SYSCONFIG1 4 /* System Configuration 1 */ +#define SYSCONFIG1_RDSIEN 0x8000 /* bits 15..15: RDS Interrupt Enable (Si4701 only) */ +#define SYSCONFIG1_STCIEN 0x4000 /* bits 14..14: Seek/Tune Complete Interrupt Enable */ +#define SYSCONFIG1_RDS 0x1000 /* bits 12..12: RDS Enable (Si4701 only) */ +#define SYSCONFIG1_DE 0x0800 /* bits 11..11: De-emphasis (0=75us 1=50us) */ +#define SYSCONFIG1_AGCD 0x0400 /* bits 10..10: AGC Disable */ +#define SYSCONFIG1_BLNDADJ 0x00c0 /* bits 07..06: Stereo/Mono Blend Level Adjustment */ +#define SYSCONFIG1_GPIO3 0x0030 /* bits 05..04: General Purpose I/O 3 */ +#define SYSCONFIG1_GPIO2 0x000c /* bits 03..02: General Purpose I/O 2 */ +#define SYSCONFIG1_GPIO1 0x0003 /* bits 01..00: General Purpose I/O 1 */ + +#define SYSCONFIG2 5 /* System Configuration 2 */ +#define SYSCONFIG2_SEEKTH 0xff00 /* bits 15..08: RSSI Seek Threshold */ +#define SYSCONFIG2_BAND 0x0080 /* bits 07..06: Band Select */ +#define SYSCONFIG2_SPACE 0x0030 /* bits 05..04: Channel Spacing */ +#define SYSCONFIG2_VOLUME 0x000f /* bits 03..00: Volume */ + +#define SYSCONFIG3 6 /* System Configuration 3 */ +#define SYSCONFIG3_SMUTER 0xc000 /* bits 15..14: Softmute Attack/Recover Rate */ +#define SYSCONFIG3_SMUTEA 0x3000 /* bits 13..12: Softmute Attenuation */ +#define SYSCONFIG3_SKSNR 0x00f0 /* bits 07..04: Seek SNR Threshold */ +#define SYSCONFIG3_SKCNT 0x000f /* bits 03..00: Seek FM Impulse Detection Threshold */ + +#define TEST1 7 /* Test 1 */ +#define TEST1_AHIZEN 0x4000 /* bits 14..14: Audio High-Z Enable */ + +#define TEST2 8 /* Test 2 */ +/* TEST2 only contains reserved bits */ + +#define BOOTCONFIG 9 /* Boot Configuration */ +/* BOOTCONFIG only contains reserved bits */ + +#define STATUSRSSI 10 /* Status RSSI */ +#define STATUSRSSI_RDSR 0x8000 /* bits 15..15: RDS Ready (Si4701 only) */ +#define STATUSRSSI_STC 0x4000 /* bits 14..14: Seek/Tune Complete */ +#define STATUSRSSI_SF 0x2000 /* bits 13..13: Seek Fail/Band Limit */ +#define STATUSRSSI_AFCRL 0x1000 /* bits 12..12: AFC Rail */ +#define STATUSRSSI_RDSS 0x0800 /* bits 11..11: RDS Synchronized (Si4701 only) */ +#define STATUSRSSI_BLERA 0x0600 /* bits 10..09: RDS Block A Errors (Si4701 only) */ +#define STATUSRSSI_ST 0x0100 /* bits 08..08: Stereo Indicator */ +#define STATUSRSSI_RSSI 0x00ff /* bits 07..00: RSSI (Received Signal Strength Indicator) */ + +#define READCHAN 11 /* Read Channel */ +#define READCHAN_BLERB 0xc000 /* bits 15..14: RDS Block D Errors (Si4701 only) */ +#define READCHAN_BLERC 0x3000 /* bits 13..12: RDS Block C Errors (Si4701 only) */ +#define READCHAN_BLERD 0x0c00 /* bits 11..10: RDS Block B Errors (Si4701 only) */ +#define READCHAN_READCHAN 0x03ff /* bits 09..00: Read Channel */ + +#define RDSA 12 /* RDSA */ +#define RDSA_RDSA 0xffff /* bits 15..00: RDS Block A Data (Si4701 only) */ + +#define RDSB 13 /* RDSB */ +#define RDSB_RDSB 0xffff /* bits 15..00: RDS Block B Data (Si4701 only) */ + +#define RDSC 14 /* RDSC */ +#define RDSC_RDSC 0xffff /* bits 15..00: RDS Block C Data (Si4701 only) */ + +#define RDSD 15 /* RDSD */ +#define RDSD_RDSD 0xffff /* bits 15..00: RDS Block D Data (Si4701 only) */ + + +/************************************************************************** + * General Driver Definitions + **************************************************************************/ + +/* + * si470x_device - private data + */ +struct si470x_device { + struct video_device *videodev; + + /* reference to USB and video device */ + struct usb_device *usbdev; + struct usb_interface *intf; + + /* Interrupt endpoint handling */ + char *int_in_buffer; + struct usb_endpoint_descriptor *int_in_endpoint; + struct urb *int_in_urb; + int int_in_running; + + /* scratch page */ + unsigned char software_version; + unsigned char hardware_version; + + /* driver management */ + unsigned char disconnected; + struct mutex disconnect_lock; + unsigned int users; + + /* Silabs internal registers (0..15) */ + unsigned short registers[RADIO_REGISTER_NUM]; + + /* RDS receive buffer */ + wait_queue_head_t read_queue; + struct mutex lock; /* buffer locking */ + unsigned char *buffer; /* size is always multiple of three */ + unsigned int buf_size; + unsigned int rd_index; + unsigned int wr_index; +}; + + +/* + * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW, + * 62.5 kHz otherwise. + * The tuner is able to have a channel spacing of 50, 100 or 200 kHz. + * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW + * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000 + */ +#define FREQ_MUL (1000000 / 62.5) + + + +/************************************************************************** + * Common Functions + **************************************************************************/ +extern const struct v4l2_file_operations si470x_fops; +extern struct video_device si470x_viddev_template; +int si470x_get_register(struct si470x_device *radio, int regnr); +int si470x_set_register(struct si470x_device *radio, int regnr); +int si470x_set_freq(struct si470x_device *radio, unsigned int freq); +int si470x_start(struct si470x_device *radio); +int si470x_stop(struct si470x_device *radio); +int si470x_rds_on(struct si470x_device *radio); +int si470x_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *capability); -- cgit v1.2.3 From 951be4abc87f0caf46aba5d23981a83c323deda2 Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Sun, 9 Aug 2009 19:22:41 +0200 Subject: radio-si470x: change to dev_* macro from printk This patch is for using dev_* macro instead of printk. Signed-off-by: Joonyoung Shim Acked-by: Tobias Lorenz --- .../media/radio/si470x/radio-si470x-common.c | 50 +++++++++--------- .../drivers/media/radio/si470x/radio-si470x-usb.c | 58 +++++++++----------- 2 files changed, 52 insertions(+), 56 deletions(-) --- .../media/radio/si470x/radio-si470x-common.c | 50 +++++++++---------- .../drivers/media/radio/si470x/radio-si470x-usb.c | 58 ++++++++++------------ 2 files changed, 52 insertions(+), 56 deletions(-) diff --git a/linux/drivers/media/radio/si470x/radio-si470x-common.c b/linux/drivers/media/radio/si470x/radio-si470x-common.c index b64559d56..fd9fc4aa8 100644 --- a/linux/drivers/media/radio/si470x/radio-si470x-common.c +++ b/linux/drivers/media/radio/si470x/radio-si470x-common.c @@ -184,10 +184,10 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && (!timed_out)); if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) - printk(KERN_WARNING DRIVER_NAME ": tune does not complete\n"); + dev_warn(&radio->videodev->dev, "tune does not complete\n"); if (timed_out) - printk(KERN_WARNING DRIVER_NAME - ": tune timed out after %u ms\n", tune_timeout); + dev_warn(&radio->videodev->dev, + "tune timed out after %u ms\n", tune_timeout); stop: /* stop tuning */ @@ -320,13 +320,13 @@ static int si470x_set_seek(struct si470x_device *radio, } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && (!timed_out)); if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) - printk(KERN_WARNING DRIVER_NAME ": seek does not complete\n"); + dev_warn(&radio->videodev->dev, "seek does not complete\n"); if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) - printk(KERN_WARNING DRIVER_NAME - ": seek failed / band limit reached\n"); + dev_warn(&radio->videodev->dev, + "seek failed / band limit reached\n"); if (timed_out) - printk(KERN_WARNING DRIVER_NAME - ": seek timed out after %u ms\n", seek_timeout); + dev_warn(&radio->videodev->dev, + "seek timed out after %u ms\n", seek_timeout); stop: /* stop seeking */ @@ -435,6 +435,7 @@ int si470x_rds_on(struct si470x_device *radio) static int si470x_vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qc) { + struct si470x_device *radio = video_drvdata(file); int retval = -EINVAL; /* abort if qc->id is below V4L2_CID_BASE */ @@ -458,8 +459,8 @@ static int si470x_vidioc_queryctrl(struct file *file, void *priv, done: if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": query controls failed with %d\n", retval); + dev_warn(&radio->videodev->dev, + "query controls failed with %d\n", retval); return retval; } @@ -494,8 +495,8 @@ static int si470x_vidioc_g_ctrl(struct file *file, void *priv, done: if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": get control failed with %d\n", retval); + dev_warn(&radio->videodev->dev, + "get control failed with %d\n", retval); return retval; } @@ -534,8 +535,8 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv, done: if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": set control failed with %d\n", retval); + dev_warn(&radio->videodev->dev, + "set control failed with %d\n", retval); return retval; } @@ -632,8 +633,8 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, done: if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": get tuner failed with %d\n", retval); + dev_warn(&radio->videodev->dev, + "get tuner failed with %d\n", retval); return retval; } @@ -671,8 +672,8 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, done: if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": set tuner failed with %d\n", retval); + dev_warn(&radio->videodev->dev, + "set tuner failed with %d\n", retval); return retval; } @@ -701,8 +702,8 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, done: if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": get frequency failed with %d\n", retval); + dev_warn(&radio->videodev->dev, + "get frequency failed with %d\n", retval); return retval; } @@ -730,8 +731,8 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv, done: if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": set frequency failed with %d\n", retval); + dev_warn(&radio->videodev->dev, + "set frequency failed with %d\n", retval); return retval; } @@ -759,9 +760,8 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, done: if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": set hardware frequency seek failed with %d\n", - retval); + dev_warn(&radio->videodev->dev, + "set hardware frequency seek failed with %d\n", retval); return retval; } diff --git a/linux/drivers/media/radio/si470x/radio-si470x-usb.c b/linux/drivers/media/radio/si470x/radio-si470x-usb.c index f3d805f70..650816140 100644 --- a/linux/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/linux/drivers/media/radio/si470x/radio-si470x-usb.c @@ -223,8 +223,8 @@ static int si470x_get_report(struct si470x_device *radio, void *buf, int size) buf, size, usb_timeout); if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": si470x_get_report: usb_control_msg returned %d\n", + dev_warn(&radio->intf->dev, + "si470x_get_report: usb_control_msg returned %d\n", retval); return retval; } @@ -246,8 +246,8 @@ static int si470x_set_report(struct si470x_device *radio, void *buf, int size) buf, size, usb_timeout); if (retval < 0) - printk(KERN_WARNING DRIVER_NAME - ": si470x_set_report: usb_control_msg returned %d\n", + dev_warn(&radio->intf->dev, + "si470x_set_report: usb_control_msg returned %d\n", retval); return retval; } @@ -358,7 +358,7 @@ static int si470x_get_scratch_page_versions(struct si470x_device *radio) retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); if (retval < 0) - printk(KERN_WARNING DRIVER_NAME ": si470x_get_scratch: " + dev_warn(&radio->intf->dev, "si470x_get_scratch: " "si470x_get_report returned %d\n", retval); else { radio->software_version = buf[1]; @@ -396,8 +396,8 @@ static void si470x_int_in_callback(struct urb *urb) urb->status == -ESHUTDOWN) { return; } else { - printk(KERN_WARNING DRIVER_NAME - ": non-zero urb status (%d)\n", urb->status); + dev_warn(&radio->intf->dev, + "non-zero urb status (%d)\n", urb->status); goto resubmit; /* Maybe we can recover. */ } } @@ -482,8 +482,8 @@ resubmit: if (radio->int_in_running && radio->usbdev) { retval = usb_submit_urb(radio->int_in_urb, GFP_ATOMIC); if (retval) { - printk(KERN_WARNING DRIVER_NAME - ": resubmitting urb failed (%d)", retval); + dev_warn(&radio->intf->dev, + "resubmitting urb failed (%d)", retval); radio->int_in_running = 0; } } @@ -616,8 +616,8 @@ static int si470x_fops_open(struct file *file) retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL); if (retval) { - printk(KERN_INFO DRIVER_NAME - ": submitting int urb failed (%d)\n", retval); + dev_info(&radio->intf->dev, + "submitting int urb failed (%d)\n", retval); radio->int_in_running = 0; usb_autopm_put_interface(radio->intf); } @@ -753,8 +753,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, radio->int_in_endpoint = endpoint; } if (!radio->int_in_endpoint) { - printk(KERN_INFO DRIVER_NAME - ": could not find interrupt in endpoint\n"); + dev_info(&intf->dev, "could not find interrupt in endpoint\n"); retval = -EIO; goto err_radio; } @@ -763,15 +762,14 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, radio->int_in_buffer = kmalloc(int_end_size, GFP_KERNEL); if (!radio->int_in_buffer) { - printk(KERN_INFO DRIVER_NAME - "could not allocate int_in_buffer"); + dev_info(&intf->dev, "could not allocate int_in_buffer"); retval = -ENOMEM; goto err_radio; } radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); if (!radio->int_in_urb) { - printk(KERN_INFO DRIVER_NAME "could not allocate int_in_urb"); + dev_info(&intf->dev, "could not allocate int_in_urb"); retval = -ENOMEM; goto err_intbuffer; } @@ -791,7 +789,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, retval = -EIO; goto err_video; } - printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", + dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[CHIPID]); /* get software and hardware versions */ @@ -799,23 +797,22 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, retval = -EIO; goto err_video; } - printk(KERN_INFO DRIVER_NAME - ": software version %d, hardware version %d\n", + dev_info(&intf->dev, "software version %d, hardware version %d\n", radio->software_version, radio->hardware_version); /* check if device and firmware is current */ if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_SW_VERSION_CURRENT) { - printk(KERN_WARNING DRIVER_NAME - ": This driver is known to work with " + dev_warn(&intf->dev, + "This driver is known to work with " "firmware version %hu,\n", RADIO_SW_VERSION_CURRENT); - printk(KERN_WARNING DRIVER_NAME - ": but the device has firmware version %hu.\n", + dev_warn(&intf->dev, + "but the device has firmware version %hu.\n", radio->registers[CHIPID] & CHIPID_FIRMWARE); - printk(KERN_WARNING DRIVER_NAME - ": If you have some trouble using this driver,\n"); - printk(KERN_WARNING DRIVER_NAME - ": please report to V4L ML at " + dev_warn(&intf->dev, + "If you have some trouble using this driver,\n"); + dev_warn(&intf->dev, + "please report to V4L ML at " "linux-media@vger.kernel.org\n"); } @@ -842,8 +839,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr); if (retval) { - printk(KERN_WARNING DRIVER_NAME - ": Could not register video device\n"); + dev_warn(&intf->dev, "Could not register video device\n"); goto err_all; } usb_set_intfdata(intf, radio); @@ -868,7 +864,7 @@ err_initial: static int si470x_usb_driver_suspend(struct usb_interface *intf, pm_message_t message) { - printk(KERN_INFO DRIVER_NAME ": suspending now...\n"); + dev_info(&intf->dev, "suspending now...\n"); return 0; } @@ -879,7 +875,7 @@ static int si470x_usb_driver_suspend(struct usb_interface *intf, */ static int si470x_usb_driver_resume(struct usb_interface *intf) { - printk(KERN_INFO DRIVER_NAME ": resuming now...\n"); + dev_info(&intf->dev, "resuming now...\n"); return 0; } -- cgit v1.2.3 From edce5773436aeaa6d195d1db0cc80f4c10a7acf1 Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Sun, 9 Aug 2009 19:23:02 +0200 Subject: radio-si470x: add disconnect check function The si470x_disconnect_check is function to check disconnect state of radio in common file. The function is implemented in each interface file. Signed-off-by: Joonyoung Shim Acked-by: Tobias Lorenz --- .../media/radio/si470x/radio-si470x-common.c | 40 +++++++++---------- .../drivers/media/radio/si470x/radio-si470x-usb.c | 17 ++++++++ linux/drivers/media/radio/si470x/radio-si470x.h | 1 + 3 files changed, 37 insertions(+), 21 deletions(-) --- .../media/radio/si470x/radio-si470x-common.c | 40 ++++++++++------------ .../drivers/media/radio/si470x/radio-si470x-usb.c | 17 +++++++++ linux/drivers/media/radio/si470x/radio-si470x.h | 1 + 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/linux/drivers/media/radio/si470x/radio-si470x-common.c b/linux/drivers/media/radio/si470x/radio-si470x-common.c index fd9fc4aa8..9f9a417ca 100644 --- a/linux/drivers/media/radio/si470x/radio-si470x-common.c +++ b/linux/drivers/media/radio/si470x/radio-si470x-common.c @@ -475,10 +475,9 @@ static int si470x_vidioc_g_ctrl(struct file *file, void *priv, int retval = 0; /* safety checks */ - if (radio->disconnected) { - retval = -EIO; + retval = si470x_disconnect_check(radio); + if (retval) goto done; - } switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -511,10 +510,9 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv, int retval = 0; /* safety checks */ - if (radio->disconnected) { - retval = -EIO; + retval = si470x_disconnect_check(radio); + if (retval) goto done; - } switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -567,10 +565,10 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, int retval = 0; /* safety checks */ - if (radio->disconnected) { - retval = -EIO; + retval = si470x_disconnect_check(radio); + if (retval) goto done; - } + if (tuner->index != 0) { retval = -EINVAL; goto done; @@ -649,10 +647,10 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, int retval = -EINVAL; /* safety checks */ - if (radio->disconnected) { - retval = -EIO; + retval = si470x_disconnect_check(radio); + if (retval) goto done; - } + if (tuner->index != 0) goto done; @@ -688,10 +686,10 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, int retval = 0; /* safety checks */ - if (radio->disconnected) { - retval = -EIO; + retval = si470x_disconnect_check(radio); + if (retval) goto done; - } + if (freq->tuner != 0) { retval = -EINVAL; goto done; @@ -718,10 +716,10 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv, int retval = 0; /* safety checks */ - if (radio->disconnected) { - retval = -EIO; + retval = si470x_disconnect_check(radio); + if (retval) goto done; - } + if (freq->tuner != 0) { retval = -EINVAL; goto done; @@ -747,10 +745,10 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, int retval = 0; /* safety checks */ - if (radio->disconnected) { - retval = -EIO; + retval = si470x_disconnect_check(radio); + if (retval) goto done; - } + if (seek->tuner != 0) { retval = -EINVAL; goto done; diff --git a/linux/drivers/media/radio/si470x/radio-si470x-usb.c b/linux/drivers/media/radio/si470x/radio-si470x-usb.c index 650816140..2f5cf6c72 100644 --- a/linux/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/linux/drivers/media/radio/si470x/radio-si470x-usb.c @@ -370,6 +370,23 @@ static int si470x_get_scratch_page_versions(struct si470x_device *radio) +/************************************************************************** + * General Driver Functions - DISCONNECT_CHECK + **************************************************************************/ + +/* + * si470x_disconnect_check - check whether radio disconnects + */ +int si470x_disconnect_check(struct si470x_device *radio) +{ + if (radio->disconnected) + return -EIO; + else + return 0; +} + + + /************************************************************************** * RDS Driver Functions **************************************************************************/ diff --git a/linux/drivers/media/radio/si470x/radio-si470x.h b/linux/drivers/media/radio/si470x/radio-si470x.h index 88d3ebbe2..eb36b6451 100644 --- a/linux/drivers/media/radio/si470x/radio-si470x.h +++ b/linux/drivers/media/radio/si470x/radio-si470x.h @@ -194,6 +194,7 @@ extern const struct v4l2_file_operations si470x_fops; extern struct video_device si470x_viddev_template; int si470x_get_register(struct si470x_device *radio, int regnr); int si470x_set_register(struct si470x_device *radio, int regnr); +int si470x_disconnect_check(struct si470x_device *radio); int si470x_set_freq(struct si470x_device *radio, unsigned int freq); int si470x_start(struct si470x_device *radio); int si470x_stop(struct si470x_device *radio); -- cgit v1.2.3 From b17608ffc4ff53dbf7bab4bbcdb1d3ad808e9ff0 Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Sun, 9 Aug 2009 19:23:35 +0200 Subject: radio-si470x: add i2c driver for si470x This patch supports i2c interface of si470x. The i2c specific part exists in radio-si470x-i2c.c file and the common part uses radio-si470x-common.c file. The '#if defined' is inserted inevitably because of parts used only si470x usb in the common file. The current driver version doesn't support the RDS. Signed-off-by: Joonyoung Shim Acked-by: Tobias Lorenz --- linux/drivers/media/radio/si470x/Kconfig | 13 + linux/drivers/media/radio/si470x/Makefile | 2 + .../media/radio/si470x/radio-si470x-common.c | 6 + .../drivers/media/radio/si470x/radio-si470x-i2c.c | 254 ++++++++++++++++++++ linux/drivers/media/radio/si470x/radio-si470x.h | 6 + 5 files changed, 281 insertions(+), 0 deletions(-) create mode 100644 linux/drivers/media/radio/si470x/radio-si470x-i2c.c --- linux/drivers/media/radio/si470x/Kconfig | 13 ++ linux/drivers/media/radio/si470x/Makefile | 2 + .../media/radio/si470x/radio-si470x-common.c | 6 + .../drivers/media/radio/si470x/radio-si470x-i2c.c | 254 +++++++++++++++++++++ linux/drivers/media/radio/si470x/radio-si470x.h | 6 + 5 files changed, 281 insertions(+) create mode 100644 linux/drivers/media/radio/si470x/radio-si470x-i2c.c diff --git a/linux/drivers/media/radio/si470x/Kconfig b/linux/drivers/media/radio/si470x/Kconfig index 20d05c04f..a466654ee 100644 --- a/linux/drivers/media/radio/si470x/Kconfig +++ b/linux/drivers/media/radio/si470x/Kconfig @@ -22,3 +22,16 @@ config USB_SI470X To compile this driver as a module, choose M here: the module will be called radio-usb-si470x. + +config I2C_SI470X + tristate "Silicon Labs Si470x FM Radio Receiver support with I2C" + depends on I2C && RADIO_SI470X && !USB_SI470X + ---help--- + This is a driver for I2C devices with the Silicon Labs SI470x + chip. + + Say Y here if you want to connect this type of radio to your + computer's I2C port. + + To compile this driver as a module, choose M here: the + module will be called radio-i2c-si470x. diff --git a/linux/drivers/media/radio/si470x/Makefile b/linux/drivers/media/radio/si470x/Makefile index 3cb777fe3..06964816c 100644 --- a/linux/drivers/media/radio/si470x/Makefile +++ b/linux/drivers/media/radio/si470x/Makefile @@ -3,5 +3,7 @@ # radio-usb-si470x-objs := radio-si470x-usb.o radio-si470x-common.o +radio-i2c-si470x-objs := radio-si470x-i2c.o radio-si470x-common.o obj-$(CONFIG_USB_SI470X) += radio-usb-si470x.o +obj-$(CONFIG_I2C_SI470X) += radio-i2c-si470x.o diff --git a/linux/drivers/media/radio/si470x/radio-si470x-common.c b/linux/drivers/media/radio/si470x/radio-si470x-common.c index 9f9a417ca..f33315f2c 100644 --- a/linux/drivers/media/radio/si470x/radio-si470x-common.c +++ b/linux/drivers/media/radio/si470x/radio-si470x-common.c @@ -581,8 +581,12 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, /* driver constants */ strcpy(tuner->name, "FM"); tuner->type = V4L2_TUNER_RADIO; +#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE) tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS; +#else + tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; +#endif /* range limits */ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { @@ -608,10 +612,12 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, tuner->rxsubchans = V4L2_TUNER_SUB_MONO; else tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; +#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE) /* If there is a reliable method of detecting an RDS channel, then this code should check for that before setting this RDS subchannel. */ tuner->rxsubchans |= V4L2_TUNER_SUB_RDS; +#endif /* mono/stereo selector */ if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0) diff --git a/linux/drivers/media/radio/si470x/radio-si470x-i2c.c b/linux/drivers/media/radio/si470x/radio-si470x-i2c.c new file mode 100644 index 000000000..218102184 --- /dev/null +++ b/linux/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -0,0 +1,254 @@ +/* + * drivers/media/radio/si470x/radio-si470x-i2c.c + * + * I2C driver for radios with Silicon Labs Si470x FM Radio Receivers + * + * Copyright (C) 2009 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * + * TODO: + * - RDS support + * + */ + +#include +#include +#include +#include + +#include "radio-si470x.h" + +#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 0) +#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" +#define DRIVER_VERSION "1.0.0" + +/* starting with the upper byte of register 0x0a */ +#define READ_REG_NUM RADIO_REGISTER_NUM +#define READ_INDEX(i) ((i + RADIO_REGISTER_NUM - 0x0a) % READ_REG_NUM) + +static int si470x_get_all_registers(struct si470x_device *radio) +{ + int i; + u16 buf[READ_REG_NUM]; + struct i2c_msg msgs[1] = { + { radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM, + (void *)buf }, + }; + + if (i2c_transfer(radio->client->adapter, msgs, 1) != 1) + return -EIO; + + for (i = 0; i < READ_REG_NUM; i++) + radio->registers[i] = __be16_to_cpu(buf[READ_INDEX(i)]); + + return 0; +} + +int si470x_get_register(struct si470x_device *radio, int regnr) +{ + u16 buf[READ_REG_NUM]; + struct i2c_msg msgs[1] = { + { radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM, + (void *)buf }, + }; + + if (i2c_transfer(radio->client->adapter, msgs, 1) != 1) + return -EIO; + + radio->registers[regnr] = __be16_to_cpu(buf[READ_INDEX(regnr)]); + + return 0; +} + +/* starting with the upper byte of register 0x02h */ +#define WRITE_REG_NUM 8 +#define WRITE_INDEX(i) (i + 0x02) + +int si470x_set_register(struct si470x_device *radio, int regnr) +{ + int i; + u16 buf[WRITE_REG_NUM]; + struct i2c_msg msgs[1] = { + { radio->client->addr, 0, sizeof(u16) * WRITE_REG_NUM, + (void *)buf }, + }; + + for (i = 0; i < WRITE_REG_NUM; i++) + buf[i] = __cpu_to_be16(radio->registers[WRITE_INDEX(i)]); + + if (i2c_transfer(radio->client->adapter, msgs, 1) != 1) + return -EIO; + + return 0; +} + +int si470x_disconnect_check(struct si470x_device *radio) +{ + return 0; +} + +static int si470x_fops_open(struct file *file) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + mutex_lock(&radio->lock); + radio->users++; + + if (radio->users == 1) + /* start radio */ + retval = si470x_start(radio); + mutex_unlock(&radio->lock); + + return retval; +} + +static int si470x_fops_release(struct file *file) +{ + struct si470x_device *radio = video_drvdata(file); + int retval = 0; + + /* safety check */ + if (!radio) + return -ENODEV; + + mutex_lock(&radio->lock); + radio->users--; + if (radio->users == 0) + /* stop radio */ + retval = si470x_stop(radio); + mutex_unlock(&radio->lock); + + return retval; +} + +const struct v4l2_file_operations si470x_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, + .open = si470x_fops_open, + .release = si470x_fops_release, +}; + +int si470x_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) +{ + strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); + strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); + capability->version = DRIVER_KERNEL_VERSION; + capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | + V4L2_CAP_TUNER | V4L2_CAP_RADIO; + + return 0; +} + +static int __devinit si470x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct si470x_device *radio; + int retval = 0; + + /* private data allocation and initialization */ + radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); + if (!radio) { + retval = -ENOMEM; + goto err_initial; + } + radio->client = client; + radio->users = 0; + mutex_init(&radio->lock); + + /* video device allocation and initialization */ + radio->videodev = video_device_alloc(); + if (!radio->videodev) { + retval = -ENOMEM; + goto err_radio; + } + memcpy(radio->videodev, &si470x_viddev_template, + sizeof(si470x_viddev_template)); + video_set_drvdata(radio->videodev, radio); + + /* power up : need 110ms */ + radio->registers[POWERCFG] = POWERCFG_ENABLE; + if (si470x_set_register(radio, POWERCFG) < 0) { + retval = -EIO; + goto err_all; + } + msleep(110); + + /* show some infos about the specific si470x device */ + if (si470x_get_all_registers(radio) < 0) { + retval = -EIO; + goto err_radio; + } + dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", + radio->registers[DEVICEID], radio->registers[CHIPID]); + + /* set initial frequency */ + si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ + + /* register video device */ + retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, -1); + if (retval) { + dev_warn(&client->dev, "Could not register video device\n"); + goto err_all; + } + + i2c_set_clientdata(client, radio); + + return 0; +err_all: + video_device_release(radio->videodev); +err_radio: + kfree(radio); +err_initial: + return retval; +} + +static __devexit int si470x_i2c_remove(struct i2c_client *client) +{ + struct si470x_device *radio = i2c_get_clientdata(client); + + video_unregister_device(radio->videodev); + kfree(radio); + i2c_set_clientdata(client, NULL); + + return 0; +} + +static const struct i2c_device_id si470x_i2c_id[] = { + { "si470x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, si470x_i2c_id); + +static struct i2c_driver si470x_i2c_driver = { + .driver = { + .name = "si470x", + .owner = THIS_MODULE, + }, + .probe = si470x_i2c_probe, + .remove = __devexit_p(si470x_i2c_remove), + .id_table = si470x_i2c_id, +}; + +static int __init si470x_i2c_init(void) +{ + return i2c_add_driver(&si470x_i2c_driver); +} +module_init(si470x_i2c_init); + +static void __exit si470x_i2c_exit(void) +{ + i2c_del_driver(&si470x_i2c_driver); +} +module_exit(si470x_i2c_exit); + +MODULE_DESCRIPTION("i2c radio driver for si470x fm radio receivers"); +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/radio/si470x/radio-si470x.h b/linux/drivers/media/radio/si470x/radio-si470x.h index eb36b6451..791c367be 100644 --- a/linux/drivers/media/radio/si470x/radio-si470x.h +++ b/linux/drivers/media/radio/si470x/radio-si470x.h @@ -144,6 +144,11 @@ struct si470x_device { struct video_device *videodev; +#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE) + struct i2c_client *client; +#endif + +#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE) /* reference to USB and video device */ struct usb_device *usbdev; struct usb_interface *intf; @@ -161,6 +166,7 @@ struct si470x_device { /* driver management */ unsigned char disconnected; struct mutex disconnect_lock; +#endif unsigned int users; /* Silabs internal registers (0..15) */ -- cgit v1.2.3 From 4a8acba072fef7a5126e7193667e6b0746555606 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Aug 2009 19:39:23 -0300 Subject: em28xx: Implement g/s_register via address match From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 77ec51dad..c7d723a03 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1342,8 +1342,9 @@ static int vidioc_g_register(struct file *file, void *priv, v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); return 0; case V4L2_CHIP_MATCH_I2C_ADDR: - /* Not supported yet */ - return -EINVAL; + /* TODO: is this correct? */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); + return 0; default: if (!v4l2_chip_match_host(®->match)) return -EINVAL; @@ -1394,8 +1395,9 @@ static int vidioc_s_register(struct file *file, void *priv, v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); return 0; case V4L2_CHIP_MATCH_I2C_ADDR: - /* Not supported yet */ - return -EINVAL; + /* TODO: is this correct? */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); + return 0; default: if (!v4l2_chip_match_host(®->match)) return -EINVAL; -- cgit v1.2.3 From c3f9323429d06d88d8986100746bebe48c6b7097 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Aug 2009 19:58:14 -0300 Subject: v4l2-dbg: Add register maps for mt9v011 From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- v4l2-apps/util/v4l2-dbg-micron.h | 46 ++++++++++++++++++++++++++++++++++++++++ v4l2-apps/util/v4l2-dbg.cpp | 19 ++++++++++++----- 2 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 v4l2-apps/util/v4l2-dbg-micron.h diff --git a/v4l2-apps/util/v4l2-dbg-micron.h b/v4l2-apps/util/v4l2-dbg-micron.h new file mode 100644 index 000000000..8466322bf --- /dev/null +++ b/v4l2-apps/util/v4l2-dbg-micron.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2009 Mauro Carvalho Chehab + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "v4l2-dbg.h" + +#define MT9V011_IDENT "mt9v011" + +/* Register name prefix */ +#define MT9V011_PREFIX "MT9V011_" + +static struct board_regs mt9v011_regs[] = { + {0x00, MT9V011_PREFIX "CHIP_VERSION"}, + {0x01, MT9V011_PREFIX "ROWSTART"}, + {0x02, MT9V011_PREFIX "COLSTART"}, + {0x03, MT9V011_PREFIX "HEIGHT"}, + {0x04, MT9V011_PREFIX "WIDTH"}, + {0x05, MT9V011_PREFIX "HBLANK"}, + {0x06, MT9V011_PREFIX "VBLANK"}, + {0x07, MT9V011_PREFIX "OUT_CTRL"}, + {0x09, MT9V011_PREFIX "SHUTTER_WIDTH"}, + {0x0a, MT9V011_PREFIX "CLK_SPEED"}, + {0x0b, MT9V011_PREFIX "RESTART"}, + {0x0c, MT9V011_PREFIX "SHUTTER_DELAY"}, + {0x0d, MT9V011_PREFIX "RESET"}, + {0x1e, MT9V011_PREFIX "DIGITAL_ZOOM"}, + {0x20, MT9V011_PREFIX "READ_MODE"}, + {0x2b, MT9V011_PREFIX "GREEN_1_GAIN"}, + {0x2c, MT9V011_PREFIX "BLUE_GAIN"}, + {0x2d, MT9V011_PREFIX "RED_GAIN"}, + {0x2e, MT9V011_PREFIX "GREEN_2_GAIN"}, + {0x35, MT9V011_PREFIX "GLOBAL_GAIN"}, + {0xf1, MT9V011_PREFIX "CHIP_ENABLE"}, +}; diff --git a/v4l2-apps/util/v4l2-dbg.cpp b/v4l2-apps/util/v4l2-dbg.cpp index 424b0fffe..1a481c04c 100644 --- a/v4l2-apps/util/v4l2-dbg.cpp +++ b/v4l2-apps/util/v4l2-dbg.cpp @@ -47,6 +47,7 @@ #include "v4l2-dbg-em28xx.h" #include "v4l2-dbg-ac97.h" #include "v4l2-dbg-tvp5150.h" +#include "v4l2-dbg-micron.h" #define ARRAY_SIZE(arr) ((int)(sizeof(arr) / sizeof((arr)[0]))) @@ -61,7 +62,7 @@ struct board_list { static const struct board_list boards[] = { #define AC97_BOARD 0 - { /* From ac97-dbg.h */ + { /* From v4l2-dbg-ac97.h */ AC97_IDENT, sizeof(AC97_PREFIX) - 1, ac97_regs, @@ -69,7 +70,7 @@ static const struct board_list boards[] = { NULL, 0, }, - { /* From bttv-dbg.h */ + { /* From v4l2-dbg-bttv.h */ BTTV_IDENT, sizeof(BTTV_PREFIX) - 1, bt8xx_regs, @@ -77,7 +78,7 @@ static const struct board_list boards[] = { bt8xx_regs_other, ARRAY_SIZE(bt8xx_regs_other), }, - { /* From saa7134-dbg.h */ + { /* From v4l2-dbg-saa7134.h */ SAA7134_IDENT, sizeof(SAA7134_PREFIX) - 1, saa7134_regs, @@ -85,7 +86,7 @@ static const struct board_list boards[] = { NULL, 0, }, - { /* From em28xx-dbg.h */ + { /* From v4l2-dbg-em28xx.h */ EM28XX_IDENT, sizeof(EM28XX_PREFIX) - 1, em28xx_regs, @@ -93,7 +94,7 @@ static const struct board_list boards[] = { em28xx_alt_regs, ARRAY_SIZE(em28xx_alt_regs), }, - { /* From tvp5150-dbg.h */ + { /* From v4l2-dbg-tvp5150.h */ TVP5150_IDENT, sizeof(TVP5150_PREFIX) - 1, tvp5150_regs, @@ -101,6 +102,14 @@ static const struct board_list boards[] = { NULL, 0, }, + { /* From v4l2-dbg-micron.h */ + MT9V011_IDENT, + sizeof(MT9V011_PREFIX) - 1, + mt9v011_regs, + ARRAY_SIZE(mt9v011_regs), + NULL, + 0, + }, }; struct chipid { -- cgit v1.2.3 From 67924cd04f67ef0bf649ab43a317717fbbeb69af Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 10 Aug 2009 02:57:57 -0300 Subject: em28xx: Move the non-board dependent part to be outside em28xx_pre_card_setup() From: Mauro Carvalho Chehab em28xx_pre_card_setup() is meant to contain board-specific initialization. Also, as autodetection sometimes occur only after having i2c bus enabled, this function may need to be called later. Moving those setups to happen outside the function avoids calling it twice without need and without duplicating output lines at dmesg. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 111 +++++++++++------------- 1 file changed, 53 insertions(+), 58 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 9a09df296..ee46460f0 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1895,63 +1895,6 @@ static int em28xx_hint_sensor(struct em28xx *dev) */ void em28xx_pre_card_setup(struct em28xx *dev) { - int rc; - - em28xx_set_model(dev); - - em28xx_info("Identified as %s (card=%d)\n", - dev->board.name, dev->model); - - /* Set the default GPO/GPIO for legacy devices */ - dev->reg_gpo_num = EM2880_R04_GPO; - dev->reg_gpio_num = EM28XX_R08_GPIO; - - dev->wait_after_write = 5; - - /* Based on the Chip ID, set the device configuration */ - rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); - if (rc > 0) { - dev->chip_id = rc; - - switch (dev->chip_id) { - case CHIP_ID_EM2710: - em28xx_info("chip ID is em2710\n"); - break; - case CHIP_ID_EM2750: - em28xx_info("chip ID is em2750\n"); - break; - case CHIP_ID_EM2820: - em28xx_info("chip ID is em2820 (or em2710)\n"); - break; - case CHIP_ID_EM2840: - em28xx_info("chip ID is em2840\n"); - break; - case CHIP_ID_EM2860: - em28xx_info("chip ID is em2860\n"); - break; - case CHIP_ID_EM2870: - em28xx_info("chip ID is em2870\n"); - dev->wait_after_write = 0; - break; - case CHIP_ID_EM2874: - em28xx_info("chip ID is em2874\n"); - dev->reg_gpio_num = EM2874_R80_GPIO; - dev->wait_after_write = 0; - break; - case CHIP_ID_EM2883: - em28xx_info("chip ID is em2882/em2883\n"); - dev->wait_after_write = 0; - break; - default: - em28xx_info("em28xx chip ID = %d\n", dev->chip_id); - } - } - - /* Prepopulate cached GPO register content */ - rc = em28xx_read_reg(dev, dev->reg_gpo_num); - if (rc >= 0) - dev->reg_gpo = rc; - /* Set the initial XCLK and I2C clock values based on the board definition */ em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f); @@ -2564,7 +2507,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, int minor) { struct em28xx *dev = *devhandle; - int retval = -ENOMEM; + int retval; int errCode; dev->udev = udev; @@ -2581,6 +2524,58 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->em28xx_read_reg_req = em28xx_read_reg_req; dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800; + em28xx_set_model(dev); + + /* Set the default GPO/GPIO for legacy devices */ + dev->reg_gpo_num = EM2880_R04_GPO; + dev->reg_gpio_num = EM28XX_R08_GPIO; + + dev->wait_after_write = 5; + + /* Based on the Chip ID, set the device configuration */ + retval = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); + if (retval > 0) { + dev->chip_id = retval; + + switch (dev->chip_id) { + case CHIP_ID_EM2710: + em28xx_info("chip ID is em2710\n"); + break; + case CHIP_ID_EM2750: + em28xx_info("chip ID is em2750\n"); + break; + case CHIP_ID_EM2820: + em28xx_info("chip ID is em2820 (or em2710)\n"); + break; + case CHIP_ID_EM2840: + em28xx_info("chip ID is em2840\n"); + break; + case CHIP_ID_EM2860: + em28xx_info("chip ID is em2860\n"); + break; + case CHIP_ID_EM2870: + em28xx_info("chip ID is em2870\n"); + dev->wait_after_write = 0; + break; + case CHIP_ID_EM2874: + em28xx_info("chip ID is em2874\n"); + dev->reg_gpio_num = EM2874_R80_GPIO; + dev->wait_after_write = 0; + break; + case CHIP_ID_EM2883: + em28xx_info("chip ID is em2882/em2883\n"); + dev->wait_after_write = 0; + break; + default: + em28xx_info("em28xx chip ID = %d\n", dev->chip_id); + } + } + + /* Prepopulate cached GPO register content */ + retval = em28xx_read_reg(dev, dev->reg_gpo_num); + if (retval >= 0) + dev->reg_gpo = retval; + em28xx_pre_card_setup(dev); if (!dev->board.is_em2800) { -- cgit v1.2.3 From 3c6dcb306a9773419a04a1f47bfa00ed856b5517 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 10 Aug 2009 10:29:27 -0300 Subject: em28xx: Fix artifacts with Silvercrest webcam From: Mauro Carvalho Chehab Silvercrest mt9v011 sensor produces a 640x480 image. However, previously, the code were getting only half of the lines and merging two consecutive frames to "produce" a 640x480 image. With the addition of progressive mode, now em28xx is working with a full image. However, when the number of lines is bigger than 240, the beginning of some odd lines are filled with blank. After lots of testing, and physically checking the device for a Xtal, it was noticed experimentally that mt9v011 is using em28xx XCLK as its clock. Due to that, changing XCLK value changes the maximum speed of the stream. At the tests, it were possible to produce up to 32 fps, using a 30 MHz XCLK. However, at that rate, the artifacts happen even at 320x240. Lower values of XCLK produces artifacts only at 640x480. At some values of xclk (for example XCLKK = 6 MHz, 640x480), it is possible to see an invalid sucession of artifacts with this pattern: .xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ....xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx .xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ....xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (where the dots represent the blanked pixels) So, it seems that a waveform in the format of a ramp is interferring at the image. The cause of this interference is currently unknown. Some possibilities are: - electrical interference (maybe this device is broken?); - some issue at mt9v011 programming; - some bug at em28xx chip. So, for now, let's be conservative and use a value of XCLK that we know for sure that it won't cause artifacts. As I'm waiting for more of such devices with different em28xx chipset revisions, I'll have the opportunity to double check the issue with other pieces of hardware. Later patches can vary XCLK depending on the vertical resolutions, if a proper fix is not discovered. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 50 +++++++++++++++++-------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index ee46460f0..4b7526fa2 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -231,7 +231,7 @@ static struct em28xx_reg_seq silvercrest_reg_seq[] = { struct em28xx_board em28xx_boards[] = { [EM2750_BOARD_UNKNOWN] = { .name = "EM2710/EM2750/EM2751 webcam grabber", - .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, + .xclk = EM28XX_XCLK_FREQUENCY_20MHZ, .tuner_type = TUNER_ABSENT, .is_webcam = 1, .no_audio = 1, @@ -1838,6 +1838,7 @@ static int em28xx_hint_sensor(struct em28xx *dev) __be16 version_be; u16 version; + /* Micron sensor detection */ dev->i2c_client.addr = 0xba >> 1; cmd = 0; i2c_master_send(&dev->i2c_client, &cmd, 1); @@ -1846,16 +1847,27 @@ static int em28xx_hint_sensor(struct em28xx *dev) return -EINVAL; version = be16_to_cpu(version_be); - switch (version) { case 0x8232: /* mt9v011 640x480 1.3 Mpix sensor */ case 0x8243: /* mt9v011 rev B 640x480 1.3 Mpix sensor */ dev->model = EM2820_BOARD_SILVERCREST_WEBCAM; + em28xx_set_model(dev); + sensor_name = "mt9v011"; dev->em28xx_sensor = EM28XX_MT9V011; dev->sensor_xres = 640; dev->sensor_yres = 480; - dev->sensor_xtal = 12150000; + /* + * FIXME: mt9v011 uses I2S speed as xtal clk - at least with + * the Silvercrest cam I have here for testing - for higher + * resolutions, a high clock cause horizontal artifacts, so we + * need to use a lower xclk frequency. + * Yet, it would be possible to adjust xclk depending on the + * desired resolution, since this affects directly the + * frame rate. + */ + dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ; + dev->sensor_xtal = 4300000; /* probably means GRGB 16 bit bayer */ dev->vinmode = 0x0d; @@ -1868,6 +1880,8 @@ static int em28xx_hint_sensor(struct em28xx *dev) #endif case 0x8431: dev->model = EM2750_BOARD_UNKNOWN; + em28xx_set_model(dev); + sensor_name = "mt9m001"; dev->em28xx_sensor = EM28XX_MT9M001; em28xx_initialize_mt9m001(dev); @@ -1884,6 +1898,9 @@ static int em28xx_hint_sensor(struct em28xx *dev) return -EINVAL; } + /* Setup webcam defaults */ + em28xx_pre_card_setup(dev); + em28xx_errdev("Sensor is %s, using model %s entry.\n", sensor_name, em28xx_boards[dev->model].name); @@ -2277,7 +2294,20 @@ void em28xx_register_i2c_ir(struct em28xx *dev) void em28xx_card_setup(struct em28xx *dev) { - em28xx_set_model(dev); + /* + * If the device can be a webcam, seek for a sensor. + * If sensor is not found, then it isn't a webcam. + */ + if (dev->board.is_webcam) { + if (em28xx_hint_sensor(dev) < 0) + dev->board.is_webcam = 0; + else + dev->progressive = 1; + } else + em28xx_set_model(dev); + + em28xx_info("Identified as %s (card=%d)\n", + dev->board.name, dev->model); dev->tuner_type = em28xx_boards[dev->model].tuner_type; if (em28xx_boards[dev->model].tuner_addr) @@ -2610,18 +2640,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->vinmode = 0x10; dev->vinctl = 0x11; - /* - * If the device can be a webcam, seek for a sensor. - * If sensor is not found, then it isn't a webcam. - */ - if (dev->board.is_webcam) - if (em28xx_hint_sensor(dev) < 0) - dev->board.is_webcam = 0; - - /* It makes no sense to use de-interlacing mode on webcams */ - if (dev->board.is_webcam) - dev->progressive = 1; - /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); -- cgit v1.2.3 From a4fd422232350c268306a54f169e5b1fe3655f8f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 10 Aug 2009 10:53:00 -0300 Subject: em28xx: fix: avoid having a bad colored image with webcams From: Mauro Carvalho Chehab Register 0x13 seems to be a sort of image control, maybe gamma. Lower values produce better images, while higher values increases the contrast and shifts colors to green. 0xff produces a black image. If this register is left alone, a random value can be found at the register, producing weird results. This register doesn't seem to affect tv streams. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 10da3ad82..46cb13182 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -632,6 +632,9 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } + if (dev->board.is_webcam) + rc = em28xx_write_reg(dev, 0x13, 0x0c); + /* enable video capture */ rc = em28xx_write_reg(dev, 0x48, 0x00); -- cgit v1.2.3 From 61fe68b877246eb29fc7cccb96a73ffa3c7267fe Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Mon, 10 Aug 2009 23:44:14 +0200 Subject: I2C cleanups and version checks The structure and comments of the I2C part have been adopted to fit to the USB part. Some additional cleanups and precisements have been made to the version detection and checking functionality to clearly separate HW/SW/FW version. Signed-off-by: Tobias Lorenz --- .../drivers/media/radio/si470x/radio-si470x-i2c.c | 245 ++++++++++++++++----- .../drivers/media/radio/si470x/radio-si470x-usb.c | 43 ++-- linux/drivers/media/radio/si470x/radio-si470x.h | 45 ++-- 3 files changed, 257 insertions(+), 76 deletions(-) diff --git a/linux/drivers/media/radio/si470x/radio-si470x-i2c.c b/linux/drivers/media/radio/si470x/radio-si470x-i2c.c index 218102184..2d53b6a94 100644 --- a/linux/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/linux/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -3,53 +3,88 @@ * * I2C driver for radios with Silicon Labs Si470x FM Radio Receivers * - * Copyright (C) 2009 Samsung Electronics Co.Ltd + * Copyright (c) 2009 Samsung Electronics Co.Ltd * Author: Joonyoung Shim * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * TODO: + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * ToDo: * - RDS support - * */ -#include -#include + +/* driver definitions */ +#define DRIVER_AUTHOR "Joonyoung Shim "; +#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 0) +#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" +#define DRIVER_DESC "I2C radio driver for Si470x FM Radio Receivers" +#define DRIVER_VERSION "1.0.0" + +/* kernel includes */ #include #include #include "radio-si470x.h" -#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 0) -#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" -#define DRIVER_VERSION "1.0.0" -/* starting with the upper byte of register 0x0a */ +/* I2C Device ID List */ +static const struct i2c_device_id si470x_i2c_id[] = { + /* Generic Entry */ + { "si470x", 0 }, + /* Terminating entry */ + { } +}; +MODULE_DEVICE_TABLE(i2c, si470x_i2c_id); + + + +/************************************************************************** + * Module Parameters + **************************************************************************/ + +/* Radio Nr */ +static int radio_nr = -1; +module_param(radio_nr, int, 0444); +MODULE_PARM_DESC(radio_nr, "Radio Nr"); + + + +/************************************************************************** + * I2C Definitions + **************************************************************************/ + +/* Write starts with the upper byte of register 0x02 */ +#define WRITE_REG_NUM 8 +#define WRITE_INDEX(i) (i + 0x02) + +/* Read starts with the upper byte of register 0x0a */ #define READ_REG_NUM RADIO_REGISTER_NUM #define READ_INDEX(i) ((i + RADIO_REGISTER_NUM - 0x0a) % READ_REG_NUM) -static int si470x_get_all_registers(struct si470x_device *radio) -{ - int i; - u16 buf[READ_REG_NUM]; - struct i2c_msg msgs[1] = { - { radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM, - (void *)buf }, - }; - - if (i2c_transfer(radio->client->adapter, msgs, 1) != 1) - return -EIO; - for (i = 0; i < READ_REG_NUM; i++) - radio->registers[i] = __be16_to_cpu(buf[READ_INDEX(i)]); - return 0; -} +/************************************************************************** + * General Driver Functions - REGISTERs + **************************************************************************/ +/* + * si470x_get_register - read register + */ int si470x_get_register(struct si470x_device *radio, int regnr) { u16 buf[READ_REG_NUM]; @@ -66,10 +101,10 @@ int si470x_get_register(struct si470x_device *radio, int regnr) return 0; } -/* starting with the upper byte of register 0x02h */ -#define WRITE_REG_NUM 8 -#define WRITE_INDEX(i) (i + 0x02) +/* + * si470x_set_register - write register + */ int si470x_set_register(struct si470x_device *radio, int regnr) { int i; @@ -88,11 +123,56 @@ int si470x_set_register(struct si470x_device *radio, int regnr) return 0; } + + +/************************************************************************** + * General Driver Functions - ENTIRE REGISTERS + **************************************************************************/ + +/* + * si470x_get_all_registers - read entire registers + */ +static int si470x_get_all_registers(struct si470x_device *radio) +{ + int i; + u16 buf[READ_REG_NUM]; + struct i2c_msg msgs[1] = { + { radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM, + (void *)buf }, + }; + + if (i2c_transfer(radio->client->adapter, msgs, 1) != 1) + return -EIO; + + for (i = 0; i < READ_REG_NUM; i++) + radio->registers[i] = __be16_to_cpu(buf[READ_INDEX(i)]); + + return 0; +} + + + +/************************************************************************** + * General Driver Functions - DISCONNECT_CHECK + **************************************************************************/ + +/* + * si470x_disconnect_check - check whether radio disconnects + */ int si470x_disconnect_check(struct si470x_device *radio) { return 0; } + + +/************************************************************************** + * File Operations Interface + **************************************************************************/ + +/* + * si470x_fops_open - file open + */ static int si470x_fops_open(struct file *file) { struct si470x_device *radio = video_drvdata(file); @@ -104,11 +184,16 @@ static int si470x_fops_open(struct file *file) if (radio->users == 1) /* start radio */ retval = si470x_start(radio); + mutex_unlock(&radio->lock); return retval; } + +/* + * si470x_fops_release - file release + */ static int si470x_fops_release(struct file *file) { struct si470x_device *radio = video_drvdata(file); @@ -123,11 +208,16 @@ static int si470x_fops_release(struct file *file) if (radio->users == 0) /* stop radio */ retval = si470x_stop(radio); + mutex_unlock(&radio->lock); return retval; } + +/* + * si470x_fops - file operations interface + */ const struct v4l2_file_operations si470x_fops = { .owner = THIS_MODULE, .ioctl = video_ioctl2, @@ -135,6 +225,15 @@ const struct v4l2_file_operations si470x_fops = { .release = si470x_fops_release, }; + + +/************************************************************************** + * Video4Linux Interface + **************************************************************************/ + +/* + * si470x_vidioc_querycap - query device capabilities + */ int si470x_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *capability) { @@ -147,11 +246,21 @@ int si470x_vidioc_querycap(struct file *file, void *priv, return 0; } + + +/************************************************************************** + * I2C Interface + **************************************************************************/ + +/* + * si470x_i2c_probe - probe for the device + */ static int __devinit si470x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct si470x_device *radio; int retval = 0; + unsigned char version_warning = 0; /* private data allocation and initialization */ radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); @@ -159,8 +268,8 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client, retval = -ENOMEM; goto err_initial; } - radio->client = client; radio->users = 0; + radio->client = client; mutex_init(&radio->lock); /* video device allocation and initialization */ @@ -181,28 +290,47 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client, } msleep(110); - /* show some infos about the specific si470x device */ + /* get device and chip versions */ if (si470x_get_all_registers(radio) < 0) { retval = -EIO; - goto err_radio; + goto err_video; } dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[CHIPID]); + if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_FW_VERSION) { + dev_warn(&client->dev, + "This driver is known to work with " + "firmware version %hu,\n", RADIO_FW_VERSION); + dev_warn(&client->dev, + "but the device has firmware version %hu.\n", + radio->registers[CHIPID] & CHIPID_FIRMWARE); + version_warning = 1; + } + + /* give out version warning */ + if (version_warning == 1) { + dev_warn(&client->dev, + "If you have some trouble using this driver,\n"); + dev_warn(&client->dev, + "please report to V4L ML at " + "linux-media@vger.kernel.org\n"); + } /* set initial frequency */ si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ /* register video device */ - retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, -1); + retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, + radio_nr); if (retval) { dev_warn(&client->dev, "Could not register video device\n"); goto err_all; } - i2c_set_clientdata(client, radio); return 0; err_all: +err_video: video_device_release(radio->videodev); err_radio: kfree(radio); @@ -210,6 +338,10 @@ err_initial: return retval; } + +/* + * si470x_i2c_remove - remove the device + */ static __devexit int si470x_i2c_remove(struct i2c_client *client) { struct si470x_device *radio = i2c_get_clientdata(client); @@ -221,34 +353,49 @@ static __devexit int si470x_i2c_remove(struct i2c_client *client) return 0; } -static const struct i2c_device_id si470x_i2c_id[] = { - { "si470x", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, si470x_i2c_id); +/* + * si470x_i2c_driver - i2c driver interface + */ static struct i2c_driver si470x_i2c_driver = { .driver = { - .name = "si470x", - .owner = THIS_MODULE, + .name = "si470x", + .owner = THIS_MODULE, }, - .probe = si470x_i2c_probe, - .remove = __devexit_p(si470x_i2c_remove), - .id_table = si470x_i2c_id, + .probe = si470x_i2c_probe, + .remove = __devexit_p(si470x_i2c_remove), + .id_table = si470x_i2c_id, }; + + +/************************************************************************** + * Module Interface + **************************************************************************/ + +/* + * si470x_i2c_init - module init + */ static int __init si470x_i2c_init(void) { + printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n"); return i2c_add_driver(&si470x_i2c_driver); } -module_init(si470x_i2c_init); + +/* + * si470x_i2c_exit - module exit + */ static void __exit si470x_i2c_exit(void) { i2c_del_driver(&si470x_i2c_driver); } + + +module_init(si470x_i2c_init); module_exit(si470x_i2c_exit); -MODULE_DESCRIPTION("i2c radio driver for si470x fm radio receivers"); -MODULE_AUTHOR("Joonyoung Shim "); MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); diff --git a/linux/drivers/media/radio/si470x/radio-si470x-usb.c b/linux/drivers/media/radio/si470x/radio-si470x-usb.c index 2f5cf6c72..f2d0e1ddb 100644 --- a/linux/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/linux/drivers/media/radio/si470x/radio-si470x-usb.c @@ -138,16 +138,12 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); /************************************************************************** - * Software/Hardware Versions + * Software/Hardware Versions from Scratch Page **************************************************************************/ #define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6 #define RADIO_SW_VERSION 7 -#define RADIO_SW_VERSION_CURRENT 15 #define RADIO_HW_VERSION 1 -#define SCRATCH_PAGE_SW_VERSION 1 -#define SCRATCH_PAGE_HW_VERSION 2 - /************************************************************************** @@ -745,6 +741,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; int i, int_end_size, retval = 0; + unsigned char version_warning = 0; /* private data allocation and initialization */ radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); @@ -801,13 +798,22 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, sizeof(si470x_viddev_template)); video_set_drvdata(radio->videodev, radio); - /* show some infos about the specific si470x device */ + /* get device and chip versions */ if (si470x_get_all_registers(radio) < 0) { retval = -EIO; goto err_video; } dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[CHIPID]); + if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_FW_VERSION) { + dev_warn(&intf->dev, + "This driver is known to work with " + "firmware version %hu,\n", RADIO_FW_VERSION); + dev_warn(&intf->dev, + "but the device has firmware version %hu.\n", + radio->registers[CHIPID] & CHIPID_FIRMWARE); + version_warning = 1; + } /* get software and hardware versions */ if (si470x_get_scratch_page_versions(radio) < 0) { @@ -816,16 +822,27 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, } dev_info(&intf->dev, "software version %d, hardware version %d\n", radio->software_version, radio->hardware_version); - - /* check if device and firmware is current */ - if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) - < RADIO_SW_VERSION_CURRENT) { + if (radio->software_version < RADIO_SW_VERSION) { dev_warn(&intf->dev, "This driver is known to work with " - "firmware version %hu,\n", RADIO_SW_VERSION_CURRENT); + "software version %hu,\n", RADIO_SW_VERSION); dev_warn(&intf->dev, - "but the device has firmware version %hu.\n", - radio->registers[CHIPID] & CHIPID_FIRMWARE); + "but the device has software version %hu.\n", + radio->software_version); + version_warning = 1; + } + if (radio->hardware_version < RADIO_HW_VERSION) { + dev_warn(&intf->dev, + "This driver is known to work with " + "hardware version %hu,\n", RADIO_HW_VERSION); + dev_warn(&intf->dev, + "but the device has hardware version %hu.\n", + radio->hardware_version); + version_warning = 1; + } + + /* give out version warning */ + if (version_warning == 1) { dev_warn(&intf->dev, "If you have some trouble using this driver,\n"); dev_warn(&intf->dev, diff --git a/linux/drivers/media/radio/si470x/radio-si470x.h b/linux/drivers/media/radio/si470x/radio-si470x.h index 791c367be..f931789c2 100644 --- a/linux/drivers/media/radio/si470x/radio-si470x.h +++ b/linux/drivers/media/radio/si470x/radio-si470x.h @@ -42,6 +42,7 @@ #include + /************************************************************************** * Register Definitions **************************************************************************/ @@ -134,6 +135,7 @@ #define RDSD_RDSD 0xffff /* bits 15..00: RDS Block D Data (Si4701 only) */ + /************************************************************************** * General Driver Definitions **************************************************************************/ @@ -144,9 +146,19 @@ struct si470x_device { struct video_device *videodev; -#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE) - struct i2c_client *client; -#endif + /* driver management */ + unsigned int users; + + /* Silabs internal registers (0..15) */ + unsigned short registers[RADIO_REGISTER_NUM]; + + /* RDS receive buffer */ + wait_queue_head_t read_queue; + struct mutex lock; /* buffer locking */ + unsigned char *buffer; /* size is always multiple of three */ + unsigned int buf_size; + unsigned int rd_index; + unsigned int wr_index; #if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE) /* reference to USB and video device */ @@ -167,21 +179,26 @@ struct si470x_device { unsigned char disconnected; struct mutex disconnect_lock; #endif - unsigned int users; - - /* Silabs internal registers (0..15) */ - unsigned short registers[RADIO_REGISTER_NUM]; - /* RDS receive buffer */ - wait_queue_head_t read_queue; - struct mutex lock; /* buffer locking */ - unsigned char *buffer; /* size is always multiple of three */ - unsigned int buf_size; - unsigned int rd_index; - unsigned int wr_index; +#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE) + struct i2c_client *client; +#endif }; + +/************************************************************************** + * Firmware Versions + **************************************************************************/ + +#define RADIO_FW_VERSION 15 + + + +/************************************************************************** + * Frequency Multiplicator + **************************************************************************/ + /* * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW, * 62.5 kHz otherwise. -- cgit v1.2.3 From f2315820af3c8b9a218c05c0fd44b6f0741c4a35 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 4 Aug 2009 20:07:42 -0300 Subject: Fix incorrect type of tuner for the BeholdTV H6 card From: Beholder Intl. Ltd. Dmitry Belimov Define correct tuner in config. Radio now works fine Priority: normal Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/saa7134/saa7134-cards.c | 2 +- linux/drivers/media/video/saa7134/saa7134-dvb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index c18b41ee6..40aa654d2 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -4900,7 +4900,7 @@ struct saa7134_board saa7134_boards[] = { /* Igor Kuznetsov */ .name = "Beholder BeholdTV H6", .audio_clock = 0x00187de7, - .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .tuner_type = TUNER_PHILIPS_FMD1216MEX_MK3, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index a5263c391..afd9b7e5c 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -1461,7 +1461,7 @@ static int dvb_init(struct saa7134_dev *dev) if (fe0->dvb.frontend) { dvb_attach(simple_tuner_attach, fe0->dvb.frontend, &dev->i2c_adap, 0x61, - TUNER_PHILIPS_FMD1216ME_MK3); + TUNER_PHILIPS_FMD1216MEX_MK3); } break; case SAA7134_BOARD_AVERMEDIA_A700_PRO: -- cgit v1.2.3 From 0ffdf3c011bae5197f3e30f633d32645be8df021 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 4 Aug 2009 20:11:43 -0300 Subject: saa7134: fix the radio on Avermedia GO 007 FM From: hermann pitton We have support for radio on saa7133/35/31e cards with tda8290/8275(a) and 5.5MHz ceramic filter on the bridge chips since a while. It was previously not tested, if this card supports it too, but the old "ghost" radio with wrong filters doesn't work anymore. Thanks go to Pham Thanh Nam and Laszlo Kustan for reporting it working on that input. Priority: normal Signed-off-by: hermann pitton Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/saa7134/saa7134-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index 40aa654d2..3ab7732e6 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -1633,7 +1633,7 @@ struct saa7134_board saa7134_boards[] = { }}, .radio = { .name = name_radio, - .amux = LINE1, + .amux = TV, .gpio = 0x00300001, }, .mute = { -- cgit v1.2.3 From 84b33bda3d8447bc0f28a9eae22039ec4a0bb3bf Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 4 Aug 2009 20:33:42 -0300 Subject: em28xx: fix: some webcams don't have audio inputs From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 3 +++ linux/drivers/media/video/em28xx/em28xx-core.c | 2 +- linux/drivers/media/video/em28xx/em28xx.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 795fd16a5..2f4777f76 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -234,6 +234,7 @@ struct em28xx_board em28xx_boards[] = { .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, .tuner_type = TUNER_ABSENT, .is_webcam = 1, + .no_audio = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -261,6 +262,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Unknown EM2750/28xx video grabber", .tuner_type = TUNER_ABSENT, .is_webcam = 1, /* To enable sensor probe */ + .no_audio = 1, }, [EM2750_BOARD_DLCW_130] = { /* Beijing Huaqi Information Digital Technology Co., Ltd */ @@ -481,6 +483,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Silvercrest Webcam 1.3mpix", .tuner_type = TUNER_ABSENT, .is_webcam = 1, + .no_audio = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index f24af40df..a1de593ec 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -494,7 +494,7 @@ int em28xx_audio_setup(struct em28xx *dev) /* If device doesn't support Usb Audio Class, use vendor class */ if (!dev->has_audio_class) - dev->has_alsa_audio = 1; + dev->has_alsa_audio = ~dev->board.no_audio; dev->audio_mode.has_audio = 1; diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index df7fe9af7..3c4cd00b2 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -397,6 +397,7 @@ struct em28xx_board { unsigned int has_dvb:1; unsigned int has_snapshot_button:1; unsigned int is_webcam:1; + unsigned int no_audio:1; unsigned int valid:1; unsigned char xclk, i2c_speed; -- cgit v1.2.3 From ba28fa4eca1b69daa57024d9c0c9fe880ac0b2c9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 4 Aug 2009 22:00:24 -0300 Subject: drivers/media/video/gspca: introduce missing kfree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Julia Lawall Error handling code following a kmalloc should free the allocated data. Priority: normal Signed-off-by: Julia Lawall Acked-by: Erik Andrén Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c index 7127321ac..6b89f33a4 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c @@ -178,8 +178,10 @@ sensor_found: sens_priv->settings = kmalloc(sizeof(s32)*ARRAY_SIZE(s5k83a_ctrls), GFP_KERNEL); - if (!sens_priv->settings) + if (!sens_priv->settings) { + kfree(sens_priv); return -ENOMEM; + } sd->gspca_dev.cam.cam_mode = s5k83a_modes; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes); -- cgit v1.2.3 From fbcd7beea764d905edd7aec98388395e0d112a9d Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Wed, 5 Aug 2009 16:46:38 +0200 Subject: [patch] Added Support for STK7700D (DVB) From: Pete Hildebrandt This patch adds support for the STK7700D USB-DVB-Device. lsusb identifies it as: idVendor 0x1164 YUAN High-Tech Development Co., Ltd idProduct 0x1efc iProduct 2 STK7700D The device is build into a Samsung R55-T5500 notebook. Priority: normal Signed-off-by: Pete Hildebrandt Signed-off-by: Patrick Boettcher --- linux/drivers/media/dvb/dvb-usb/dib0700_devices.c | 8 ++++++-- linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c index 8ec98f6e0..81cda8061 100644 --- a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -1501,6 +1501,7 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_H) }, { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_T3) }, { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_T5) }, + { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -1826,7 +1827,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .num_device_descs = 8, + .num_device_descs = 9, .devices = { { "Terratec Cinergy HT USB XE", { &dib0700_usb_id_table[27], NULL }, @@ -1860,7 +1861,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { { &dib0700_usb_id_table[51], NULL }, { NULL }, }, - + { "YUAN High-Tech STK7700D", + { &dib0700_usb_id_table[54], NULL }, + { NULL }, + }, }, .rc_interval = DEFAULT_RC_INTERVAL, .rc_key_map = dib0700_rc_keys, diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 9593b7289..6eccce1a7 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -252,6 +252,7 @@ #define USB_PID_YUAN_STK7700PH 0x1f08 #define USB_PID_YUAN_PD378S 0x2edc #define USB_PID_YUAN_MC770 0x0871 +#define USB_PID_YUAN_STK7700D 0x1efc #define USB_PID_DW2102 0x2102 #define USB_PID_XTENSIONS_XD_380 0x0381 #define USB_PID_TELESTAR_STARSTICK_2 0x8000 -- cgit v1.2.3 From 2f25ba7a8bf7364ee4bd4aee7b283b0ea273b160 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 5 Aug 2009 12:58:47 -0300 Subject: media/zr364xx: fix build errors From: Randy Dunlap Fix build errors in zr364xx by adding selects: zr364xx.c:(.text+0x195ed7): undefined reference to `videobuf_streamon' zr364xx.c:(.text+0x196030): undefined reference to `videobuf_dqbuf' zr364xx.c:(.text+0x1960c4): undefined reference to `videobuf_qbuf' zr364xx.c:(.text+0x196123): undefined reference to `videobuf_querybuf' zr364xx.c:(.text+0x196182): undefined reference to `videobuf_reqbufs' zr364xx.c:(.text+0x196224): undefined reference to `videobuf_queue_is_busy' zr364xx.c:(.text+0x196390): undefined reference to `videobuf_vmalloc_free' zr364xx.c:(.text+0x196571): undefined reference to `videobuf_iolock' zr364xx.c:(.text+0x196678): undefined reference to `videobuf_mmap_mapper' zr364xx.c:(.text+0x196760): undefined reference to `videobuf_poll_stream' zr364xx.c:(.text+0x19689a): undefined reference to `videobuf_read_one' zr364xx.c:(.text+0x1969ec): undefined reference to `videobuf_mmap_free' zr364xx.c:(.text+0x197862): undefined reference to `videobuf_queue_vmalloc_init' zr364xx.c:(.text+0x197a28): undefined reference to `videobuf_streamoff' zr364xx.c:(.text+0x198203): undefined reference to `videobuf_to_vmalloc' zr364xx.c:(.text+0x198603): undefined reference to `videobuf_streamoff' drivers/built-in.o: In function `free_buffer': zr364xx.c:(.text+0x19930c): undefined reference to `videobuf_vmalloc_free' drivers/built-in.o: In function `zr364xx_open': zr364xx.c:(.text+0x19a7de): undefined reference to `videobuf_queue_vmalloc_init' drivers/built-in.o: In function `read_pipe_completion': zr364xx.c:(.text+0x19b17f): undefined reference to `videobuf_to_vmalloc' Priority: normal Signed-off-by: Randy Dunlap Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 8460013a0..e8a6e4de4 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -991,6 +991,8 @@ source "drivers/media/video/pwc/Kconfig" config USB_ZR364XX tristate "USB ZR364XX Camera support" depends on VIDEO_V4L2 + select VIDEOBUF_GEN + select VIDEOBUF_VMALLOC ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. -- cgit v1.2.3 From d3f91b69f01b8f1c132d1037137ef9b6fba4a142 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 5 Aug 2009 13:07:10 -0300 Subject: cxusb, d680 dmbth use unified lgs8gxx code instead of lgs8gl5 From: David T.L. Wong Use unified lgs8gxx frontend instead of reverse engineered lgs8gl5 frontend. After this patch, lgs8gl5 frontend could be mark as deprecated. Future development should base on unified lgs8gxx frontend. Priority: normal Signed-off-by: David T.L. Wong Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-usb/cxusb.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index f467cad8a..05da84c90 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -38,7 +38,7 @@ #include "mxl5005s.h" #include "dib7000p.h" #include "dib0070.h" -#include "lgs8gl5.h" +#include "lgs8gxx.h" /* debug */ static int dvb_usb_cxusb_debug; @@ -1097,8 +1097,18 @@ static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap) return -EIO; } -static struct lgs8gl5_config lgs8gl5_cfg = { +static struct lgs8gxx_config d680_lgs8gl5_cfg = { + .prod = LGS8GXX_PROD_LGS8GL5, .demod_address = 0x19, + .serial_ts = 0, + .ts_clk_pol = 0, + .ts_clk_gated = 1, + .if_clk_freq = 30400, /* 30.4 MHz */ + .if_freq = 5725, /* 5.725 MHz */ + .if_neg_center = 0, + .ext_adc = 0, + .adc_signed = 0, + .if_neg_edge = 0, }; static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) @@ -1138,7 +1148,7 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) msleep(100); /* Attach frontend */ - adap->fe = dvb_attach(lgs8gl5_attach, &lgs8gl5_cfg, &d->i2c_adap); + adap->fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap); if (adap->fe == NULL) return -EIO; -- cgit v1.2.3 From 431b7b75e8f6cc9c89cfca9f7e307731c3b07d0d Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 6 Aug 2009 01:06:31 +0200 Subject: soc-camera: fix recursive locking in .buf_queue() From: Guennadi Liakhovetski The .buf_queue() V4L2 driver method is called under spinlock_irqsave(q->irqlock,...), don't take the lock again inside the function. Reported-by: Antonio Ospite Signed-off-by: Guennadi Liakhovetski --- drivers/media/video/mx1_camera.c | 6 +----- drivers/media/video/mx3_camera.c | 19 ++++++++++--------- drivers/media/video/pxa_camera.c | 6 +----- drivers/media/video/sh_mobile_ceu_camera.c | 5 +---- 4 files changed, 13 insertions(+), 23 deletions(-) --- linux/drivers/media/video/mx1_camera.c | 6 +----- linux/drivers/media/video/mx3_camera.c | 19 ++++++++++--------- linux/drivers/media/video/pxa_camera.c | 6 +----- linux/drivers/media/video/sh_mobile_ceu_camera.c | 5 +---- 4 files changed, 13 insertions(+), 23 deletions(-) diff --git a/linux/drivers/media/video/mx1_camera.c b/linux/drivers/media/video/mx1_camera.c index 2d075205b..736c31d23 100644 --- a/linux/drivers/media/video/mx1_camera.c +++ b/linux/drivers/media/video/mx1_camera.c @@ -234,6 +234,7 @@ static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev) return ret; } +/* Called under spinlock_irqsave(&pcdev->lock, ...) */ static void mx1_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { @@ -241,13 +242,10 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx1_camera_dev *pcdev = ici->priv; struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); - unsigned long flags; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); - spin_lock_irqsave(&pcdev->lock, flags); - list_add_tail(&vb->queue, &pcdev->capture); vb->state = VIDEOBUF_ACTIVE; @@ -264,8 +262,6 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq, __raw_writel(temp, pcdev->base + CSICR1); } } - - spin_unlock_irqrestore(&pcdev->lock, flags); } static void mx1_videobuf_release(struct videobuf_queue *vq, diff --git a/linux/drivers/media/video/mx3_camera.c b/linux/drivers/media/video/mx3_camera.c index e605c076e..9770cb793 100644 --- a/linux/drivers/media/video/mx3_camera.c +++ b/linux/drivers/media/video/mx3_camera.c @@ -332,7 +332,10 @@ static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc) } } -/* Called with .vb_lock held */ +/* + * Called with .vb_lock mutex held and + * under spinlock_irqsave(&mx3_cam->lock, ...) + */ static void mx3_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { @@ -346,7 +349,8 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq, struct idmac_video_param *video = &ichan->params.video; const struct soc_camera_data_format *data_fmt = icd->current_fmt; dma_cookie_t cookie; - unsigned long flags; + + BUG_ON(!irqs_disabled()); /* This is the configuration of one sg-element */ video->out_pixel_fmt = fourcc_to_ipu_pix(data_fmt->fourcc); @@ -359,8 +363,6 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq, memset((void *)vb->baddr, 0xaa, vb->bsize); #endif - spin_lock_irqsave(&mx3_cam->lock, flags); - list_add_tail(&vb->queue, &mx3_cam->capture); if (!mx3_cam->active) { @@ -370,24 +372,23 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq, vb->state = VIDEOBUF_QUEUED; } - spin_unlock_irqrestore(&mx3_cam->lock, flags); + spin_unlock_irq(&mx3_cam->lock); cookie = txd->tx_submit(txd); dev_dbg(&icd->dev, "Submitted cookie %d DMA 0x%08x\n", cookie, sg_dma_address(&buf->sg)); + + spin_lock_irq(&mx3_cam->lock); + if (cookie >= 0) return; /* Submit error */ vb->state = VIDEOBUF_PREPARED; - spin_lock_irqsave(&mx3_cam->lock, flags); - list_del_init(&vb->queue); if (mx3_cam->active == buf) mx3_cam->active = NULL; - - spin_unlock_irqrestore(&mx3_cam->lock, flags); } /* Called with .vb_lock held */ diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index 3f1b63e4e..92fb8b191 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -624,6 +624,7 @@ static void pxa_camera_stop_capture(struct pxa_camera_dev *pcdev) dev_dbg(pcdev->soc_host.dev, "%s\n", __func__); } +/* Called under spinlock_irqsave(&pcdev->lock, ...) */ static void pxa_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { @@ -631,13 +632,10 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); - unsigned long flags; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d active=%p\n", __func__, vb, vb->baddr, vb->bsize, pcdev->active); - spin_lock_irqsave(&pcdev->lock, flags); - list_add_tail(&vb->queue, &pcdev->capture); vb->state = VIDEOBUF_ACTIVE; @@ -645,8 +643,6 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, if (!pcdev->active) pxa_camera_start_capture(pcdev); - - spin_unlock_irqrestore(&pcdev->lock, flags); } static void pxa_videobuf_release(struct videobuf_queue *vq, diff --git a/linux/drivers/media/video/sh_mobile_ceu_camera.c b/linux/drivers/media/video/sh_mobile_ceu_camera.c index 7ea2a0edf..1bae28a98 100644 --- a/linux/drivers/media/video/sh_mobile_ceu_camera.c +++ b/linux/drivers/media/video/sh_mobile_ceu_camera.c @@ -283,27 +283,24 @@ out: return ret; } +/* Called under spinlock_irqsave(&pcdev->lock, ...) */ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct soc_camera_device *icd = vq->priv_data; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - unsigned long flags; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, vb, vb->baddr, vb->bsize); vb->state = VIDEOBUF_QUEUED; - spin_lock_irqsave(&pcdev->lock, flags); list_add_tail(&vb->queue, &pcdev->capture); if (!pcdev->active) { pcdev->active = vb; sh_mobile_ceu_capture(pcdev); } - - spin_unlock_irqrestore(&pcdev->lock, flags); } static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq, -- cgit v1.2.3 From 672911c86336621bf8d82bb070c84d7082e53a05 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Thu, 6 Aug 2009 12:52:11 -0300 Subject: em28xx: fix empire auto-detect From: Douglas Schilling Landgraf Fixed eeprom hash table Priority: normal Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/video/em28xx/em28xx-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 2f4777f76..5b33e2dd1 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1729,7 +1729,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = { {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF}, {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF}, {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, - {0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, + {0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028}, }; -- cgit v1.2.3 From 11a870a6ffe158aa929d11a5adac7cfc3d7a0f4e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 6 Aug 2009 20:32:01 -0300 Subject: v4l2-ctl: Add support for VIDIOC_[G|S]_PARM From: Mauro Carvalho Chehab Those ioctls are used to control streaming frame rate. Signed-off-by: Mauro Carvalho Chehab --- v4l2-apps/util/v4l2-ctl.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp index b2246c557..e872a2916 100644 --- a/v4l2-apps/util/v4l2-ctl.cpp +++ b/v4l2-apps/util/v4l2-ctl.cpp @@ -77,6 +77,8 @@ enum Option { OptSetTuner = 't', OptGetVideoFormat = 'V', OptSetVideoFormat = 'v', + OptGetParm = 'P', + OptSetParm = 'p', OptGetSlicedVbiOutFormat = 128, OptGetOverlayFormat, @@ -222,6 +224,8 @@ static struct option long_options[] = { {"list-formats", no_argument, 0, OptListFormats}, {"get-standard", no_argument, 0, OptGetStandard}, {"set-standard", required_argument, 0, OptSetStandard}, + {"get-parm", no_argument, 0, OptGetParm}, + {"set-parm", required_argument, 0, OptSetParm}, {"info", no_argument, 0, OptGetDriverInfo}, {"list-ctrls", no_argument, 0, OptListCtrls}, {"list-ctrls-menus", no_argument, 0, OptListCtrlsMenus}, @@ -306,6 +310,8 @@ static void usage(void) " ntsc-X (X = M/J/K) or just 'ntsc' (V4L2_STD_NTSC)\n" " secam-X (X = B/G/H/D/K/L/Lc) or just 'secam' (V4L2_STD_SECAM)\n" " --list-standards display supported video standards [VIDIOC_ENUMSTD]\n" + " -P, --get-parm display video parameters [VIDIOC_G_PARMS]\n" + " -p, --set-parm set video rate in fps [VIDIOC_G_PARMS]\n" " -T, --get-tuner query the tuner settings [VIDIOC_G_TUNER]\n" " -t, --set-tuner=\n" " set the audio mode of the tuner [VIDIOC_S_TUNER]\n" @@ -1419,11 +1425,13 @@ int main(int argc, char **argv) struct v4l2_rect vcrop_out_overlay; /* crop rect */ struct v4l2_framebuffer fbuf; /* fbuf */ struct v4l2_jpegcompression jpegcomp; /* jpeg compression */ + struct v4l2_streamparm parm; /* get/set parm */ int input; /* set_input/get_input */ int output; /* set_output/get_output */ int txsubchans; /* set_modulator */ v4l2_std_id std; /* get_std/set_std */ double freq = 0; /* get/set frequency */ + double fps = 0; /* set framerate speed, in fps */ struct v4l2_frequency vf; /* get_freq/set_freq */ struct v4l2_standard vs; /* list_std */ int overlay; /* overlay */ @@ -1690,6 +1698,9 @@ int main(int argc, char **argv) std = strtol(optarg, 0L, 0) | (1ULL << 63); } break; + case OptSetParm: + fps = strtod(optarg, NULL); + break; case OptGetCtrl: subs = optarg; while (*subs != '\0') { @@ -1915,6 +1926,7 @@ int main(int argc, char **argv) options[OptGetAudioInput] = 1; options[OptGetAudioOutput] = 1; options[OptGetStandard] = 1; + options[OptGetParm] = 1; options[OptGetFreq] = 1; options[OptGetTuner] = 1; options[OptGetModulator] = 1; @@ -1976,6 +1988,23 @@ int main(int argc, char **argv) printf("Standard set to %08llx\n", (unsigned long long)std); } + + if (options[OptSetParm]) { + memset (&parm, 0, sizeof(parm)); + parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + parm.parm.capture.timeperframe.numerator = 1000; + parm.parm.capture.timeperframe.denominator = (__u32)(fps * parm.parm.capture.timeperframe.numerator); + + if (doioctl(fd, VIDIOC_S_PARM, &parm, "VIDIOC_S_PARM") == 0) { + struct v4l2_fract *tf = &parm.parm.capture.timeperframe; + if (!tf->denominator || !tf->numerator) + printf ("Invalid frame rate\n"); + else + printf("Frame rate set to: %.3f fps\n", + 1.0*tf->denominator/tf->numerator); + } + } + if (options[OptSetInput]) { if (doioctl(fd, VIDIOC_S_INPUT, &input, "VIDIOC_S_INPUT") == 0) { printf("Video input set to %d", input); @@ -2451,6 +2480,23 @@ set_vid_fmt_error: } } + if (options[OptGetParm]) { + memset (&parm, 0, sizeof(parm)); + parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (doioctl(fd, VIDIOC_G_PARM, &parm, "VIDIOC_G_PARM") == 0) { + struct v4l2_fract *tf = &parm.parm.capture.timeperframe; + if (parm.parm.capture.capability & V4L2_MODE_HIGHQUALITY) + printf("Has High quality imaging mode\n"); + if (parm.parm.capture.capability && V4L2_CAP_TIMEPERFRAME) + printf("Has V4L2_CAP_TIMEPERFRAME\n"); + if (!tf->denominator || !tf->numerator) + printf ("Invalid frame rate\n"); + else + printf("Frame rate is %.3f fps\n", + (1.0 * tf->denominator) / tf->numerator); + } + } + if (options[OptGetCtrl] && !get_ctrls.empty()) { struct v4l2_ext_controls ctrls = { 0 }; -- cgit v1.2.3 From 65a908e5b37515512c5cf89fa74a533c77e5ec62 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 6 Aug 2009 21:03:35 -0300 Subject: mt9v011: Add support for controlling frame rates From: Mauro Carvalho Chehab Implement g_parm/s_parm ioctls. Those are used to check the current frame rate (in fps) and to set it to a value. In practice, there are only 15 possible different speeds, due to chip limits. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/mt9v011.c | 89 ++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/linux/drivers/media/video/mt9v011.c b/linux/drivers/media/video/mt9v011.c index c9706b889..a76d02d40 100644 --- a/linux/drivers/media/video/mt9v011.c +++ b/linux/drivers/media/video/mt9v011.c @@ -166,7 +166,7 @@ static void set_balance(struct v4l2_subdev *sd) mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); } -static void calc_fps(struct v4l2_subdev *sd) +static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator) { struct mt9v011 *core = to_mt9v011(sd); unsigned height, width, hblank, vblank, speed; @@ -189,6 +189,51 @@ static void calc_fps(struct v4l2_subdev *sd) v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", tmp / 1000, tmp % 1000, t_time); + + if (numerator && denominator) { + *numerator = 1000; + *denominator = (u32)frames_per_ms; + } +} + +static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned height, width, hblank, vblank; + unsigned row_time, line_time; + u64 t_time, speed; + + /* Avoid bogus calculus */ + if (!numerator || !denominator) + return 0; + + height = mt9v011_read(sd, R03_MT9V011_HEIGHT); + width = mt9v011_read(sd, R04_MT9V011_WIDTH); + hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); + vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); + + row_time = width + 113 + hblank; + line_time = height + vblank + 1; + + t_time = core->xtal * ((u64)numerator); + /* round to the closest value */ + t_time += denominator / 2; + do_div(t_time, denominator); + + speed = t_time; + do_div(speed, row_time * line_time); + + /* Avoid having a negative value for speed */ + if (speed < 2) + speed = 0; + else + speed -= 2; + + /* Avoid speed overflow */ + if (speed > 15) + return 15; + + return (u16)speed; } static void set_res(struct v4l2_subdev *sd) @@ -217,7 +262,7 @@ static void set_res(struct v4l2_subdev *sd) mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); - calc_fps(sd); + calc_fps(sd, NULL, NULL); }; static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) @@ -332,6 +377,44 @@ static int mt9v011_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) return 0; } +static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + calc_fps(sd, + &cp->timeperframe.numerator, + &cp->timeperframe.denominator); + + return 0; +} + +static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + u16 speed; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + + speed = calc_speed(sd, tpf->numerator, tpf->denominator); + + mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed); + v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed); + + /* Recalculate and update fps info */ + calc_fps(sd, &tpf->numerator, &tpf->denominator); + + return 0; +} + static int mt9v011_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { struct v4l2_pix_format *pix = &fmt->fmt.pix; @@ -429,6 +512,8 @@ static const struct v4l2_subdev_video_ops mt9v011_video_ops = { .enum_fmt = mt9v011_enum_fmt, .try_fmt = mt9v011_try_fmt, .s_fmt = mt9v011_s_fmt, + .g_parm = mt9v011_g_parm, + .s_parm = mt9v011_s_parm, }; static const struct v4l2_subdev_ops mt9v011_ops = { -- cgit v1.2.3 From 4eec1f6fb9e9c0c57fac129490eadead77179246 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 6 Aug 2009 21:53:59 -0300 Subject: em28xx: Allow changing fps on webcams From: Mauro Carvalho Chehab em28xx doesn't have temporal scaling. However, on webcams, sensors are capable of changing the output rate. So, VIDIOC_[G|S]_PARM ioctls should be passed to the sensor for it to properly set frame rate. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 258cd38ce..f2d9564e9 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -899,6 +899,41 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) return 0; } +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc = 0; + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (dev->board.is_webcam) + rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, + video, g_parm, p); + else + v4l2_video_std_frame_period(dev->norm, + &p->parm.capture.timeperframe); + + return rc; +} + +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + if (!dev->board.is_webcam) + return -EINVAL; + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p); +} + static const char *iname[] = { [EM28XX_VMUX_COMPOSITE1] = "Composite1", [EM28XX_VMUX_COMPOSITE2] = "Composite2", @@ -2011,6 +2046,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, .vidioc_s_std = vidioc_s_std, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, -- cgit v1.2.3 From 124a5014e044c9b27016719a40c1dcf0b77210bd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 7 Aug 2009 01:09:54 -0300 Subject: m9v011: add vflip/hflip controls to control mirror/upside down From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/mt9v011.c | 53 +++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/linux/drivers/media/video/mt9v011.c b/linux/drivers/media/video/mt9v011.c index a76d02d40..70d359d84 100644 --- a/linux/drivers/media/video/mt9v011.c +++ b/linux/drivers/media/video/mt9v011.c @@ -62,13 +62,34 @@ static struct v4l2_queryctrl mt9v011_qctrl[] = { .step = 1, .default_value = 0, .flags = 0, - }, + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + } }; struct mt9v011 { struct v4l2_subdev sd; unsigned width, height; unsigned xtal; + unsigned hflip:1; + unsigned vflip:1; u16 global_gain, red_bal, blue_bal; }; @@ -141,7 +162,6 @@ static const struct i2c_reg_value mt9v011_init_default[] = { { R0A_MT9V011_CLK_SPEED, 0x0000 }, { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, - { R20_MT9V011_READ_MODE, 0x1000 }, { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ }; @@ -265,6 +285,20 @@ static void set_res(struct v4l2_subdev *sd) calc_fps(sd, NULL, NULL); }; +static void set_read_mode(struct v4l2_subdev *sd) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned mode = 0x1000; + + if (core->hflip) + mode |= 0x4000; + + if (core->vflip) + mode |= 0x8000; + + mt9v011_write(sd, R20_MT9V011_READ_MODE, mode); +} + static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) { int i; @@ -275,6 +309,7 @@ static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) set_balance(sd); set_res(sd); + set_read_mode(sd); return 0; }; @@ -295,6 +330,12 @@ static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_BLUE_BALANCE: ctrl->value = core->blue_bal; return 0; + case V4L2_CID_HFLIP: + ctrl->value = core->hflip ? 1 : 0; + return 0; + case V4L2_CID_VFLIP: + ctrl->value = core->vflip ? 1 : 0; + return 0; } return -EINVAL; } @@ -343,6 +384,14 @@ static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_BLUE_BALANCE: core->blue_bal = ctrl->value; break; + case V4L2_CID_HFLIP: + core->hflip = ctrl->value; + set_read_mode(sd); + return 0; + case V4L2_CID_VFLIP: + core->vflip = ctrl->value; + set_read_mode(sd); + return 0; default: return -EINVAL; } -- cgit v1.2.3 From cc7fbc37dcf48dbb639f032d01717600ccc540ea Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 7 Aug 2009 12:08:02 -0300 Subject: em28xx: fix: some em2710 chips use a different vendor ID From: Mauro Carvalho Chehab Thanks to hermann pitton for pointing this new variation. Priority: normal Tested-by: hermann pitton Signed-off-by: Mauro Carvalho Chehab --- linux/Documentation/video4linux/CARDLIST.em28xx | 2 +- linux/drivers/media/video/em28xx/em28xx-cards.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/linux/Documentation/video4linux/CARDLIST.em28xx b/linux/Documentation/video4linux/CARDLIST.em28xx index 084a7b624..66e5f8295 100644 --- a/linux/Documentation/video4linux/CARDLIST.em28xx +++ b/linux/Documentation/video4linux/CARDLIST.em28xx @@ -1,5 +1,5 @@ 0 -> Unknown EM2800 video grabber (em2800) [eb1a:2800] - 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883] + 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2710,eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883] 2 -> Terratec Cinergy 250 USB (em2820/em2840) [0ccd:0036] 3 -> Pinnacle PCTV USB 2 (em2820/em2840) [2304:0208] 4 -> Hauppauge WinTV USB 2 (em2820/em2840) [2040:4200,2040:4201] diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 2f4777f76..6e68d534e 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1613,6 +1613,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2750_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2800), .driver_info = EM2800_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2710), + .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2820), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2821), -- cgit v1.2.3 From 3304f06c0482f2180d240ec11da525578d33868a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 7 Aug 2009 12:13:31 -0300 Subject: em28xx: properly reports some em2710 chips From: Mauro Carvalho Chehab As reported by hermann pitton , some devices has a different chip id for em2710 (likely the older ones): em28xx: New device @ 480 Mbps (eb1a:2710, interface 0, class 0) em28xx #0: Identified as EM2710/EM2750/EM2751 webcam grabber (card=22) em28xx #0: em28xx chip ID = 17 Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 5 ++++- linux/drivers/media/video/em28xx/em28xx-reg.h | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 6e68d534e..9dddd53c7 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1914,11 +1914,14 @@ void em28xx_pre_card_setup(struct em28xx *dev) dev->chip_id = rc; switch (dev->chip_id) { + case CHIP_ID_EM2710: + em28xx_info("chip ID is em2710\n"); + break; case CHIP_ID_EM2750: em28xx_info("chip ID is em2750\n"); break; case CHIP_ID_EM2820: - em28xx_info("chip ID is em2710 or em2820\n"); + em28xx_info("chip ID is em2820 (or em2710)\n"); break; case CHIP_ID_EM2840: em28xx_info("chip ID is em2840\n"); diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index a2676d63c..6bf84bd78 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -176,7 +176,8 @@ /* FIXME: Need to be populated with the other chip ID's */ enum em28xx_chip_id { - CHIP_ID_EM2820 = 18, /* Also used by em2710 */ + CHIP_ID_EM2710 = 17, + CHIP_ID_EM2820 = 18, /* Also used by some em2710 */ CHIP_ID_EM2840 = 20, CHIP_ID_EM2750 = 33, CHIP_ID_EM2860 = 34, -- cgit v1.2.3 From eb07db27a2dcfe8263e54069db82b60c4219e670 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 7 Aug 2009 16:48:52 +0200 Subject: v4l2-ctl: fix help message From: hermann pitton For -p (--set-parm) v4l2-ctl also prints G_PARMS too. -P, --get-parm display video parameters [VIDIOC_G_PARMS] -p, --set-parm set video rate in fps [VIDIOC_G_PARMS] Priority: normal Signed-off-by: Mauro Carvalho Chehab --- v4l2-apps/util/v4l2-ctl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp index e872a2916..898c88cb7 100644 --- a/v4l2-apps/util/v4l2-ctl.cpp +++ b/v4l2-apps/util/v4l2-ctl.cpp @@ -311,7 +311,7 @@ static void usage(void) " secam-X (X = B/G/H/D/K/L/Lc) or just 'secam' (V4L2_STD_SECAM)\n" " --list-standards display supported video standards [VIDIOC_ENUMSTD]\n" " -P, --get-parm display video parameters [VIDIOC_G_PARMS]\n" - " -p, --set-parm set video rate in fps [VIDIOC_G_PARMS]\n" + " -p, --set-parm set video rate in fps [VIDIOC_S_PARMS]\n" " -T, --get-tuner query the tuner settings [VIDIOC_G_TUNER]\n" " -t, --set-tuner=\n" " set the audio mode of the tuner [VIDIOC_S_TUNER]\n" -- cgit v1.2.3 From 32405a38daa6df56e36a1569031515af7a72f192 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 7 Aug 2009 18:43:00 -0300 Subject: em28xx-cards: remove a code that doesn't seem to affect the webcam From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 9dddd53c7..27725c0ed 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -2408,10 +2408,6 @@ void em28xx_card_setup(struct em28xx *dev) em28xx_gpio_set(dev, dev->board.tuner_gpio); em28xx_set_mode(dev, EM28XX_ANALOG_MODE); break; - case EM2820_BOARD_SILVERCREST_WEBCAM: - /* FIXME: need to document the registers bellow */ - em28xx_write_reg(dev, 0x0d, 0x42); - em28xx_write_reg(dev, 0x13, 0x08); } if (dev->board.has_snapshot_button) -- cgit v1.2.3 From 3f5d03feb375c275dac631a9d5819d4d2bc25c46 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 8 Aug 2009 03:14:55 -0300 Subject: em28xx: fix: don't do image interlacing on webcams From: Mauro Carvalho Chehab Due to historical reasons, em28xx driver gets two consecutive frames and fold them into an unique framing, doing interlacing. While this works fine for TV images, this produces two bad effects with webcams: 1) webcam images are progressive. Merging two consecutive images produce interlacing artifacts on the image; 2) since the driver needs to get two frames, it reduces the maximum frame rate by two. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 4 +++ linux/drivers/media/video/em28xx/em28xx-core.c | 5 +++- linux/drivers/media/video/em28xx/em28xx-video.c | 40 +++++++++++++++++++------ linux/drivers/media/video/em28xx/em28xx.h | 3 ++ 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 27725c0ed..594461739 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -2623,6 +2623,10 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, if (em28xx_hint_sensor(dev) < 0) dev->board.is_webcam = 0; + /* It makes no sense to use de-interlacing mode on webcams */ + if (dev->board.is_webcam) + dev->progressive = 1; + /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index a1de593ec..10da3ad82 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -720,7 +720,10 @@ int em28xx_resolution_set(struct em28xx *dev) { int width, height; width = norm_maxw(dev); - height = norm_maxh(dev) >> 1; + height = norm_maxh(dev); + + if (!dev->progressive) + height >>= norm_maxh(dev); em28xx_set_outfmt(dev); diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index f2d9564e9..77ec51dad 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -212,15 +212,24 @@ static void em28xx_copy_video(struct em28xx *dev, startread = p; remain = len; - /* Interlaces frame */ - if (buf->top_field) + if (dev->progressive) fieldstart = outp; - else - fieldstart = outp + bytesperline; + else { + /* Interlaces two half frames */ + if (buf->top_field) + fieldstart = outp; + else + fieldstart = outp + bytesperline; + } linesdone = dma_q->pos / bytesperline; currlinedone = dma_q->pos % bytesperline; - offset = linesdone * bytesperline * 2 + currlinedone; + + if (dev->progressive) + offset = linesdone * bytesperline + currlinedone; + else + offset = linesdone * bytesperline * 2 + currlinedone; + startwrite = fieldstart + offset; lencopy = bytesperline - currlinedone; lencopy = lencopy > remain ? remain : lencopy; @@ -398,7 +407,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], len, (p[2] & 1) ? "odd" : "even"); - if (!(p[2] & 1)) { + if (dev->progressive || !(p[2] & 1)) { if (buf != NULL) buffer_filled(dev, dma_q, buf); get_next_buf(dma_q, &buf); @@ -742,7 +751,10 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */ - f->fmt.pix.field = dev->interlaced ? + if (dev->progressive) + f->fmt.pix.field = V4L2_FIELD_NONE; + else + f->fmt.pix.field = dev->interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; mutex_unlock(&dev->lock); @@ -806,7 +818,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.field = V4L2_FIELD_INTERLACED; + if (dev->progressive) + f->fmt.pix.field = V4L2_FIELD_NONE; + else + f->fmt.pix.field = dev->interlaced ? + V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; return 0; } @@ -1765,6 +1781,7 @@ static int em28xx_v4l2_open(struct file *filp) struct em28xx *dev; enum v4l2_buf_type fh_type; struct em28xx_fh *fh; + enum v4l2_field field; dev = em28xx_get_device(minor, &fh_type, &radio); @@ -1821,8 +1838,13 @@ static int em28xx_v4l2_open(struct file *filp) dev->users++; + if (dev->progressive) + field = V4L2_FIELD_NONE; + else + field = V4L2_FIELD_INTERLACED; + videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops, - NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED, + NULL, &dev->slock, fh->type, field, sizeof(struct em28xx_buffer), fh); mutex_unlock(&dev->lock); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 3c4cd00b2..c76da6985 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -494,6 +494,9 @@ struct em28xx { int sensor_xres, sensor_yres; int sensor_xtal; + /* Allows progressive (e. g. non-interlaced) mode */ + int progressive; + /* Vinmode/Vinctl used at the driver */ int vinmode, vinctl; -- cgit v1.2.3 From 731b07aacdb288bdc2e0d55090224470c064cac0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 8 Aug 2009 03:28:41 -0300 Subject: em28xx: Adjust Silvercrest xtal frequency From: Mauro Carvalho Chehab We don't know the xtal frequency of Silvercrest, but we need to have some value in order to allow controlling the frame rate frequency. The value is probably still wrong, since the manufacturer announces this device as being capable of 30fps, but the maximum we can get is 13.5 fps. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 594461739..9a09df296 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1855,7 +1855,7 @@ static int em28xx_hint_sensor(struct em28xx *dev) dev->em28xx_sensor = EM28XX_MT9V011; dev->sensor_xres = 640; dev->sensor_yres = 480; - dev->sensor_xtal = 6300000; + dev->sensor_xtal = 12150000; /* probably means GRGB 16 bit bayer */ dev->vinmode = 0x0d; -- cgit v1.2.3 From 73dd04edca69ade43ea5604f1842feed69d35def Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Aug 2009 10:32:20 +0200 Subject: pvrusb2: fix compile warning From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil CC: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-audio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c index 42875ec21..d8c9e6632 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -66,9 +66,10 @@ void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) u32 input; pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo"); + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes[sid]) != NULL) && + if ((sp != NULL) && (hdw->input_val >= 0) && (hdw->input_val < sp->cnt)) { input = sp->def[hdw->input_val]; -- cgit v1.2.3 From a1eb3529b0c857e8de54c34b526aca3e07f5b34a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Aug 2009 10:50:02 +0200 Subject: cx24113: fix mips compiler warning From: Hans Verkuil do_div requires an u64 as the first argument, not a s64. Priority: normal Signed-off-by: Hans Verkuil CC: Patrick Boettcher --- linux/drivers/media/dvb/frontends/cx24113.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/dvb/frontends/cx24113.c b/linux/drivers/media/dvb/frontends/cx24113.c index e4fd533a4..075b2b57c 100644 --- a/linux/drivers/media/dvb/frontends/cx24113.c +++ b/linux/drivers/media/dvb/frontends/cx24113.c @@ -303,6 +303,7 @@ static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f) { s32 N; s64 F; + u64 dividend; u8 R, r; u8 vcodiv; u8 factor; @@ -346,7 +347,10 @@ static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f) F = freq_hz; F *= (u64) (R * vcodiv * 262144); dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R); - do_div(F, state->config->xtal_khz*1000 * factor * 2); + /* do_div needs an u64 as first argument */ + dividend = F; + do_div(dividend, state->config->xtal_khz * 1000 * factor * 2); + F = dividend; dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R); F -= (N + 32) * 262144; -- cgit v1.2.3 From 84ca12cecb8b0b28efb9403b9ea6da6a9589caec Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Aug 2009 12:10:52 +0200 Subject: hdpvr: add missing initialization of current_norm From: Hans Verkuil Drivers should either set current_norm or supply a g_std callback. The hdpvr driver does neither. Since it initializes to a 60 Hz format I've initialized the current_norm to NTSC | PAL_M | PAL_60 which is the 60 Hz subset of tvnorms. Priority: normal Signed-off-by: Hans Verkuil CC: Janne Grunau --- linux/drivers/media/video/hdpvr/hdpvr-video.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux/drivers/media/video/hdpvr/hdpvr-video.c b/linux/drivers/media/video/hdpvr/hdpvr-video.c index 5937de23c..2eb9dc2eb 100644 --- a/linux/drivers/media/video/hdpvr/hdpvr-video.c +++ b/linux/drivers/media/video/hdpvr/hdpvr-video.c @@ -1222,6 +1222,8 @@ static const struct video_device hdpvr_video_template = { V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I | V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_60, + .current_norm = V4L2_STD_NTSC | V4L2_STD_PAL_M | + V4L2_STD_PAL_60, }; int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, -- cgit v1.2.3 From f4002f575cdc4c826c424b19d21b8979de737d64 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Aug 2009 12:28:16 +0200 Subject: v4l2-ioctl: fix G_STD and G_PARM default handlers From: Hans Verkuil The v4l core supplies default handlers for G_STD and G_PARM. However, both default handlers are buggy. This patch fixes the following: 1) If no g_std is supplied and current_norm == 0, then this driver does not support TV video standards (e.g. a radio or webcam driver). Return -EINVAL. This ensures that there is no bogus VIDIOC_G_STD support for such drivers. 2) The default VIDIOC_G_PARM handler used current_norm instead of first checking if the driver supported g_std and calling that to get the norm. It also didn't check if current_norm was 0, since in that case the driver does not support TV standards (or no standard was set at all) and the default handler should return -EINVAL. Note that I am very unhappy with these default handlers: I think they basically behave like some very strange and unexpected side-effect. Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/v4l2-ioctl.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/linux/drivers/media/video/v4l2-ioctl.c b/linux/drivers/media/video/v4l2-ioctl.c index 80c94dc5a..d746d9555 100644 --- a/linux/drivers/media/video/v4l2-ioctl.c +++ b/linux/drivers/media/video/v4l2-ioctl.c @@ -1088,8 +1088,10 @@ static long __video_do_ioctl(struct file *file, /* Calls the specific handler */ if (ops->vidioc_g_std) ret = ops->vidioc_g_std(file, fh, id); - else + else if (vfd->current_norm) *id = vfd->current_norm; + else + ret = -EINVAL; if (!ret) dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id); @@ -1560,12 +1562,19 @@ static long __video_do_ioctl(struct file *file, break; ret = ops->vidioc_g_parm(file, fh, p); } else { + v4l2_std_id std = vfd->current_norm; + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - v4l2_video_std_frame_period(vfd->current_norm, - &p->parm.capture.timeperframe); ret = 0; + if (ops->vidioc_g_std) + ret = ops->vidioc_g_std(file, fh, &std); + else if (std == 0) + ret = -EINVAL; + if (ret == 0) + v4l2_video_std_frame_period(std, + &p->parm.capture.timeperframe); } dbgarg(cmd, "type=%d\n", p->type); -- cgit v1.2.3 From 9bd841d241b9c9c46b86a860a279d3c576b5ca90 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Aug 2009 12:39:31 +0200 Subject: v4l2-ctl: fix get/set-parm bugs and add get/set-output-parm support From: Hans Verkuil Fixed several get/set-parm bugs, cleaned it up a bit and reformatted the output to conform to the output of the other get options. Implemented get/set-output-parm to complete the VIDIOC_G/S_PARM support. Priority: normal Signed-off-by: Hans Verkuil --- v4l2-apps/util/v4l2-ctl.cpp | 98 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 19 deletions(-) diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp index 898c88cb7..60ebc5474 100644 --- a/v4l2-apps/util/v4l2-ctl.cpp +++ b/v4l2-apps/util/v4l2-ctl.cpp @@ -136,6 +136,8 @@ enum Option { OptGetModulator, OptSetModulator, OptListDevices, + OptGetOutputParm, + OptSetOutputParm, OptLast = 256 }; @@ -226,6 +228,8 @@ static struct option long_options[] = { {"set-standard", required_argument, 0, OptSetStandard}, {"get-parm", no_argument, 0, OptGetParm}, {"set-parm", required_argument, 0, OptSetParm}, + {"get-output-parm", no_argument, 0, OptGetOutputParm}, + {"set-output-parm", required_argument, 0, OptSetOutputParm}, {"info", no_argument, 0, OptGetDriverInfo}, {"list-ctrls", no_argument, 0, OptListCtrls}, {"list-ctrls-menus", no_argument, 0, OptListCtrlsMenus}, @@ -310,8 +314,9 @@ static void usage(void) " ntsc-X (X = M/J/K) or just 'ntsc' (V4L2_STD_NTSC)\n" " secam-X (X = B/G/H/D/K/L/Lc) or just 'secam' (V4L2_STD_SECAM)\n" " --list-standards display supported video standards [VIDIOC_ENUMSTD]\n" - " -P, --get-parm display video parameters [VIDIOC_G_PARMS]\n" - " -p, --set-parm set video rate in fps [VIDIOC_S_PARMS]\n" + " -P, --get-parm display video parameters [VIDIOC_G_PARM]\n" + " -p, --set-parm=\n" + " set video framerate in [VIDIOC_S_PARM]\n" " -T, --get-tuner query the tuner settings [VIDIOC_G_TUNER]\n" " -t, --set-tuner=\n" " set the audio mode of the tuner [VIDIOC_S_TUNER]\n" @@ -424,6 +429,9 @@ static void usage(void) " bilingual: Modulate as bilingual\n" " mono-sap: Modulate as mono with Second Audio Program\n" " stereo-sap: Modulate as stereo with Second Audio Program\n" + " --get-output-parm display output video parameters [VIDIOC_G_PARM]\n" + " --set-output-parm=\n" + " set output video framerate in [VIDIOC_S_PARM]\n" "\n"); printf("Expert options:\n" " --streamoff turn the stream off [VIDIOC_STREAMOFF]\n" @@ -830,7 +838,7 @@ static void printcropcap(const struct v4l2_cropcap &cropcap) printf("\tPixel Aspect: %u/%u\n", cropcap.pixelaspect.numerator, cropcap.pixelaspect.denominator); } -static void printfmt(struct v4l2_format vfmt) +static void printfmt(const struct v4l2_format &vfmt) { const flag_def vbi_def[] = { { V4L2_VBI_UNSYNC, "unsynchronized" }, @@ -1431,7 +1439,8 @@ int main(int argc, char **argv) int txsubchans; /* set_modulator */ v4l2_std_id std; /* get_std/set_std */ double freq = 0; /* get/set frequency */ - double fps = 0; /* set framerate speed, in fps */ + double fps = 0; /* set framerate speed, in fps */ + double output_fps = 0; /* set framerate speed, in fps */ struct v4l2_frequency vf; /* get_freq/set_freq */ struct v4l2_standard vs; /* list_std */ int overlay; /* overlay */ @@ -1701,6 +1710,9 @@ int main(int argc, char **argv) case OptSetParm: fps = strtod(optarg, NULL); break; + case OptSetOutputParm: + output_fps = strtod(optarg, NULL); + break; case OptGetCtrl: subs = optarg; while (*subs != '\0') { @@ -1927,6 +1939,7 @@ int main(int argc, char **argv) options[OptGetAudioOutput] = 1; options[OptGetStandard] = 1; options[OptGetParm] = 1; + options[OptGetOutputParm] = 1; options[OptGetFreq] = 1; options[OptGetTuner] = 1; options[OptGetModulator] = 1; @@ -1990,18 +2003,38 @@ int main(int argc, char **argv) if (options[OptSetParm]) { - memset (&parm, 0, sizeof(parm)); + memset(&parm, 0, sizeof(parm)); parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; parm.parm.capture.timeperframe.numerator = 1000; - parm.parm.capture.timeperframe.denominator = (__u32)(fps * parm.parm.capture.timeperframe.numerator); + parm.parm.capture.timeperframe.denominator = + fps * parm.parm.capture.timeperframe.numerator; if (doioctl(fd, VIDIOC_S_PARM, &parm, "VIDIOC_S_PARM") == 0) { struct v4l2_fract *tf = &parm.parm.capture.timeperframe; + if (!tf->denominator || !tf->numerator) - printf ("Invalid frame rate\n"); + printf("Invalid frame rate\n"); else - printf("Frame rate set to: %.3f fps\n", - 1.0*tf->denominator/tf->numerator); + printf("Frame rate set to %.3f fps\n", + 1.0 * tf->denominator / tf->numerator); + } + } + + if (options[OptSetOutputParm]) { + memset(&parm, 0, sizeof(parm)); + parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + parm.parm.output.timeperframe.numerator = 1000; + parm.parm.output.timeperframe.denominator = + fps * parm.parm.output.timeperframe.numerator; + + if (doioctl(fd, VIDIOC_S_PARM, &parm, "VIDIOC_S_PARM") == 0) { + struct v4l2_fract *tf = &parm.parm.output.timeperframe; + + if (!tf->denominator || !tf->numerator) + printf("Invalid frame rate\n"); + else + printf("Frame rate set to %.3f fps\n", + 1.0 * tf->denominator / tf->numerator); } } @@ -2481,19 +2514,46 @@ set_vid_fmt_error: } if (options[OptGetParm]) { - memset (&parm, 0, sizeof(parm)); + memset(&parm, 0, sizeof(parm)); parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (doioctl(fd, VIDIOC_G_PARM, &parm, "VIDIOC_G_PARM") == 0) { - struct v4l2_fract *tf = &parm.parm.capture.timeperframe; - if (parm.parm.capture.capability & V4L2_MODE_HIGHQUALITY) - printf("Has High quality imaging mode\n"); - if (parm.parm.capture.capability && V4L2_CAP_TIMEPERFRAME) - printf("Has V4L2_CAP_TIMEPERFRAME\n"); - if (!tf->denominator || !tf->numerator) - printf ("Invalid frame rate\n"); + const struct v4l2_fract &tf = parm.parm.capture.timeperframe; + + printf("Streaming Parameters %s:\n", buftype2s(parm.type).c_str()); + if (parm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) + printf("\tCapabilities : timeperframe\n"); + if (parm.parm.capture.capturemode & V4L2_MODE_HIGHQUALITY) + printf("\tCapture mode : high quality\n"); + if (!tf.denominator || !tf.numerator) + printf("\tFrames per second: invalid (%d/%d)\n", + tf.denominator, tf.numerator); + else + printf("\tFrames per second: %.3f (%d/%d)\n", + (1.0 * tf.denominator) / tf.numerator, + tf.denominator, tf.numerator); + printf("\tRead buffers : %d\n", parm.parm.output.writebuffers); + } + } + + if (options[OptGetOutputParm]) { + memset(&parm, 0, sizeof(parm)); + parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (doioctl(fd, VIDIOC_G_PARM, &parm, "VIDIOC_G_PARM") == 0) { + const struct v4l2_fract &tf = parm.parm.output.timeperframe; + + printf("Streaming Parameters %s:\n", buftype2s(parm.type).c_str()); + if (parm.parm.output.capability & V4L2_CAP_TIMEPERFRAME) + printf("\tCapabilities : timeperframe\n"); + if (parm.parm.output.outputmode & V4L2_MODE_HIGHQUALITY) + printf("\tOutput mode : high quality\n"); + if (!tf.denominator || !tf.numerator) + printf("\tFrames per second: invalid (%d/%d)\n", + tf.denominator, tf.numerator); else - printf("Frame rate is %.3f fps\n", - (1.0 * tf->denominator) / tf->numerator); + printf("\tFrames per second: %.3f (%d/%d)\n", + (1.0 * tf.denominator) / tf.numerator, + tf.denominator, tf.numerator); + printf("\tWrite buffers : %d\n", parm.parm.output.writebuffers); } } -- cgit v1.2.3 From 2ba96e655011fe3e04a32e5498ef82485b930c6f Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Fri, 7 Aug 2009 08:25:06 -0400 Subject: em28xx: fix regression in Empire DualTV digital tuning From: Devin Heitmueller Restore support for digital tuning caused by regression during introduction of disable_i2c_gate parameter to zl10353 driver. Thanks to user "Xwang" for reporting the problem and testing the fix Priority: high Cc: Xwang Signed-off-by: Devin Heitmueller --- linux/drivers/media/video/em28xx/em28xx-dvb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index e32267383..3d522d547 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -479,7 +479,6 @@ static int dvb_init(struct em28xx *dev) } break; case EM2880_BOARD_KWORLD_DVB_310U: - case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->frontend = dvb_attach(zl10353_attach, &em28xx_zl10353_with_xc3028, &dev->i2c_adap); @@ -489,6 +488,7 @@ static int dvb_init(struct em28xx *dev) } break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->frontend = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, &dev->i2c_adap); -- cgit v1.2.3 From d029b650929bd8c6cda3c88135a3b754ca6ae0bb Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Sat, 8 Aug 2009 12:58:52 -0300 Subject: strlcpy() will always null terminate the string. From: Roel Kluin Priority: normal Signed-off-by: Roel Kluin Signed-off-by: Andrew Morton Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c | 2 +- linux/drivers/media/video/pwc/pwc-v4l.c | 2 +- linux/drivers/media/video/zoran/zoran_card.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c b/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c index 326f76089..cead089bb 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c @@ -19,7 +19,7 @@ int dvb_usb_i2c_init(struct dvb_usb_device *d) return -EINVAL; } - strncpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name)); + strlcpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name)); d->i2c_adap.class = I2C_CLASS_TV_DIGITAL, d->i2c_adap.algo = d->props.i2c_algo; d->i2c_adap.algo_data = NULL; diff --git a/linux/drivers/media/video/pwc/pwc-v4l.c b/linux/drivers/media/video/pwc/pwc-v4l.c index 2876ce084..bdb4ced57 100644 --- a/linux/drivers/media/video/pwc/pwc-v4l.c +++ b/linux/drivers/media/video/pwc/pwc-v4l.c @@ -1033,7 +1033,7 @@ long pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (std->index != 0) return -EINVAL; std->id = V4L2_STD_UNKNOWN; - strncpy(std->name, "webcam", sizeof(std->name)); + strlcpy(std->name, "webcam", sizeof(std->name)); return 0; } diff --git a/linux/drivers/media/video/zoran/zoran_card.c b/linux/drivers/media/video/zoran/zoran_card.c index 217fec9e4..375f158fd 100644 --- a/linux/drivers/media/video/zoran/zoran_card.c +++ b/linux/drivers/media/video/zoran/zoran_card.c @@ -1169,7 +1169,7 @@ zoran_setup_videocodec (struct zoran *zr, m->type = 0; m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER; - strncpy(m->name, ZR_DEVNAME(zr), sizeof(m->name)); + strlcpy(m->name, ZR_DEVNAME(zr), sizeof(m->name)); m->data = zr; switch (type) -- cgit v1.2.3 From b876feaf9b088d50b7519f8d6bbf15be977cd325 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Mon, 10 Aug 2009 22:17:25 -0300 Subject: stk-webcam: read buffer overflow From: Roel Kluin It tested the value of stk_sizes[i].m before checking whether i was in range. Priority: normal Signed-off-by: Roel Kluin Cc: Hans Verkuil Cc: Trent Piepho Signed-off-by: Andrew Morton Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/video/stk-webcam.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/drivers/media/video/stk-webcam.c b/linux/drivers/media/video/stk-webcam.c index 849867ab2..aaeee8319 100644 --- a/linux/drivers/media/video/stk-webcam.c +++ b/linux/drivers/media/video/stk-webcam.c @@ -1051,8 +1051,8 @@ static int stk_setup_format(struct stk_camera *dev) depth = 1; else depth = 2; - while (stk_sizes[i].m != dev->vsettings.mode - && i < ARRAY_SIZE(stk_sizes)) + while (i < ARRAY_SIZE(stk_sizes) && + stk_sizes[i].m != dev->vsettings.mode) i++; if (i == ARRAY_SIZE(stk_sizes)) { STK_ERROR("Something is broken in %s\n", __func__); -- cgit v1.2.3 From b8961acc4142f124a11a2b33594790dc49dd194a Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Mon, 10 Aug 2009 20:59:16 -0400 Subject: dvb: siano uses/depends on INPUT From: Randy Dunlap siano uses input_*() functions so it should depend on INPUT to prevent build errors: ERROR: "input_event" [drivers/media/dvb/siano/sms1xxx.ko] undefined! ERROR: "input_register_device" [drivers/media/dvb/siano/sms1xxx.ko] undefined! ERROR: "input_free_device" [drivers/media/dvb/siano/sms1xxx.ko] undefined! ERROR: "input_unregister_device" [drivers/media/dvb/siano/sms1xxx.ko] undefined! ERROR: "input_allocate_device" [drivers/media/dvb/siano/sms1xxx.ko] undefined! Priority: normal Signed-off-by: Randy Dunlap Cc: Michael Krufky Cc: Uri Shkolnik Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/dvb/siano/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/dvb/siano/Kconfig b/linux/drivers/media/dvb/siano/Kconfig index dd863f261..88847d1dc 100644 --- a/linux/drivers/media/dvb/siano/Kconfig +++ b/linux/drivers/media/dvb/siano/Kconfig @@ -4,7 +4,7 @@ config DVB_SIANO_SMS1XXX tristate "Siano SMS1XXX USB dongle support" - depends on DVB_CORE && USB + depends on DVB_CORE && USB && INPUT ---help--- Choose Y here if you have a USB dongle with a SMS1XXX chipset. -- cgit v1.2.3 From b889b807125e28911afda8c7740b18658aeb96fe Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Mon, 10 Aug 2009 21:07:54 -0400 Subject: Read buffer overflow From: Roel Kluin parport[n] is checked before n < MAX_CAMS Priority: normal Signed-off-by: Roel Kluin Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/video/bw-qcam.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/bw-qcam.c b/linux/drivers/media/video/bw-qcam.c index 3702baad6..8f6bb845e 100644 --- a/linux/drivers/media/video/bw-qcam.c +++ b/linux/drivers/media/video/bw-qcam.c @@ -993,7 +993,7 @@ static int accept_bwqcam(struct parport *port) if (parport[0] && strncmp(parport[0], "auto", 4) != 0) { /* user gave parport parameters */ - for(n=0; parport[n] && n Date: Mon, 10 Aug 2009 21:15:54 -0400 Subject: cx88: add support for WinFast DTV2000H rev. J From: Vlastimil Labsky I updated and simplyfied patch from Zbynek Hrabovsky for recent kernel. It enables autodetection of card, sound in analog TV , sound in FM radio and switching between antenna and cable RF input. Radio tuner still doesn't work, I don't even know how it works. Some guys wrote me that FM radio works with TV tuner used instead of radio part (symlink video0 -> radio0). Priority: normal Signed-off-by: Vlastimil Labsky Cc: Gerd Knorr Signed-off-by: Andrew Morton Signed-off-by: Douglas Schilling Landgraf --- linux/Documentation/video4linux/CARDLIST.cx88 | 1 + linux/drivers/media/video/cx88/cx88-cards.c | 49 +++++++++++++++++++++++++++ linux/drivers/media/video/cx88/cx88-dvb.c | 1 + linux/drivers/media/video/cx88/cx88-input.c | 1 + linux/drivers/media/video/cx88/cx88.h | 1 + 5 files changed, 53 insertions(+) diff --git a/linux/Documentation/video4linux/CARDLIST.cx88 b/linux/Documentation/video4linux/CARDLIST.cx88 index 0736518b2..3385f8b09 100644 --- a/linux/Documentation/video4linux/CARDLIST.cx88 +++ b/linux/Documentation/video4linux/CARDLIST.cx88 @@ -80,3 +80,4 @@ 79 -> Terratec Cinergy HT PCI MKII [153b:1177] 80 -> Hauppauge WinTV-IR Only [0070:9290] 81 -> Leadtek WinFast DTV1800 Hybrid [107d:6654] + 82 -> WinFast DTV2000 H rev. J [107d:6f2b] diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 67ccc59a7..19d57e698 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1316,6 +1316,51 @@ static const struct cx88_board cx88_boards[] = { }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_WINFAST_DTV2000H_J] = { + .name = "WinFast DTV2000 H rev. J", + .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x00017300, + .gpio1 = 0x00008207, + .gpio2 = 0x00000000, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x00018300, + .gpio1 = 0x0000f207, + .gpio2 = 0x00017304, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x00018301, + .gpio1 = 0x0000f207, + .gpio2 = 0x00017304, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x00018301, + .gpio1 = 0x0000f207, + .gpio2 = 0x00017304, + .gpio3 = 0x02000000, + }}, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x00015702, + .gpio1 = 0x0000f207, + .gpio2 = 0x00015702, + .gpio3 = 0x02000000, + }, + .mpeg = CX88_MPEG_DVB, + }, [CX88_BOARD_GENIATECH_DVBS] = { .name = "Geniatech DVB-S", .tuner_type = TUNER_ABSENT, @@ -2314,6 +2359,10 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x107d, .subdevice = 0x665e, .card = CX88_BOARD_WINFAST_DTV2000H, + },{ + .subvendor = 0x107d, + .subdevice = 0x6f2b, + .card = CX88_BOARD_WINFAST_DTV2000H_J, },{ .subvendor = 0x18ac, .subdevice = 0xd800, /* FusionHDTV 3 Gold (original revision) */ diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 9c88b6c0f..ab84c085f 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -703,6 +703,7 @@ static int dvb_register(struct cx8802_dev *dev) } break; case CX88_BOARD_WINFAST_DTV2000H: + case CX88_BOARD_WINFAST_DTV2000H_J: case CX88_BOARD_HAUPPAUGE_HVR1100: case CX88_BOARD_HAUPPAUGE_HVR1100LP: case CX88_BOARD_HAUPPAUGE_HVR1300: diff --git a/linux/drivers/media/video/cx88/cx88-input.c b/linux/drivers/media/video/cx88/cx88-input.c index 7c5e94916..8d0042ab2 100644 --- a/linux/drivers/media/video/cx88/cx88-input.c +++ b/linux/drivers/media/video/cx88/cx88-input.c @@ -238,6 +238,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->sampling = 1; break; case CX88_BOARD_WINFAST_DTV2000H: + case CX88_BOARD_WINFAST_DTV2000H_J: case CX88_BOARD_WINFAST_DTV1800H: ir_codes = ir_codes_winfast; ir->gpio_addr = MO_GP0_IO; diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index 59fc01a53..014004acd 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -238,6 +238,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79 #define CX88_BOARD_HAUPPAUGE_IRONLY 80 #define CX88_BOARD_WINFAST_DTV1800H 81 +#define CX88_BOARD_WINFAST_DTV2000H_J 82 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, -- cgit v1.2.3 From 84b86128572d1cef2f921fda840e365d8f01bdee Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Mon, 10 Aug 2009 21:51:01 -0400 Subject: Use kzalloc for frontend states to have struct dvb_frontend properly From: Matthias Schwarzott This patch changes most frontend drivers to allocate their state structure via kzalloc and not kmalloc. This is done to properly initialize the embedded "struct dvb_frontend frontend" field, that they all have. The visible effect of this struct being uninitalized is, that the member "id" that is used to set the name of kernel thread is totally random. Some board drivers (for example cx88-dvb) set this "id" via videobuf_dvb_alloc_frontend but most do not. So I at least get random id values for saa7134, flexcop and ttpci based cards. It looks like this in dmesg: DVB: registering adapter 1 frontend -10551321 (ST STV0299 DVB-S) The related kernel thread then also gets a strange name like "kdvb-ad-1-fe--1". Priority: normal Signed-off-by: Matthias Schwarzott CC: Michael Krufky CC: Steven Toth CC: Timothy Lee CC: Igor M. Liplianin Acked-by: Andreas Oberritter Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/dvb/frontends/at76c651.c | 2 +- linux/drivers/media/dvb/frontends/cx22700.c | 2 +- linux/drivers/media/dvb/frontends/cx22702.c | 2 +- linux/drivers/media/dvb/frontends/cx24110.c | 2 +- linux/drivers/media/dvb/frontends/dvb_dummy_fe.c | 6 +++--- linux/drivers/media/dvb/frontends/l64781.c | 2 +- linux/drivers/media/dvb/frontends/lgs8gl5.c | 2 +- linux/drivers/media/dvb/frontends/mt312.c | 2 +- linux/drivers/media/dvb/frontends/nxt6000.c | 2 +- linux/drivers/media/dvb/frontends/or51132.c | 2 +- linux/drivers/media/dvb/frontends/or51211.c | 2 +- linux/drivers/media/dvb/frontends/s5h1409.c | 2 +- linux/drivers/media/dvb/frontends/s5h1411.c | 2 +- linux/drivers/media/dvb/frontends/si21xx.c | 2 +- linux/drivers/media/dvb/frontends/sp8870.c | 2 +- linux/drivers/media/dvb/frontends/sp887x.c | 2 +- linux/drivers/media/dvb/frontends/stv0288.c | 2 +- linux/drivers/media/dvb/frontends/stv0297.c | 2 +- linux/drivers/media/dvb/frontends/stv0299.c | 2 +- linux/drivers/media/dvb/frontends/tda10021.c | 2 +- linux/drivers/media/dvb/frontends/tda10048.c | 2 +- linux/drivers/media/dvb/frontends/tda1004x.c | 4 ++-- linux/drivers/media/dvb/frontends/tda10086.c | 2 +- linux/drivers/media/dvb/frontends/tda8083.c | 2 +- linux/drivers/media/dvb/frontends/tda80xx.c | 2 +- linux/drivers/media/dvb/frontends/ves1820.c | 2 +- linux/drivers/media/dvb/frontends/ves1x93.c | 2 +- 27 files changed, 30 insertions(+), 30 deletions(-) diff --git a/linux/drivers/media/dvb/frontends/at76c651.c b/linux/drivers/media/dvb/frontends/at76c651.c index 33486d54c..510e08f5a 100644 --- a/linux/drivers/media/dvb/frontends/at76c651.c +++ b/linux/drivers/media/dvb/frontends/at76c651.c @@ -369,7 +369,7 @@ struct dvb_frontend* at76c651_attach(const struct at76c651_config* config, struct at76c651_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct at76c651_state), GFP_KERNEL); + state = kzalloc(sizeof(struct at76c651_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/linux/drivers/media/dvb/frontends/cx22700.c b/linux/drivers/media/dvb/frontends/cx22700.c index ace5cb171..fbd838eca 100644 --- a/linux/drivers/media/dvb/frontends/cx22700.c +++ b/linux/drivers/media/dvb/frontends/cx22700.c @@ -380,7 +380,7 @@ struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, struct cx22700_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct cx22700_state), GFP_KERNEL); + state = kzalloc(sizeof(struct cx22700_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/linux/drivers/media/dvb/frontends/cx22702.c b/linux/drivers/media/dvb/frontends/cx22702.c index 5d1abe34b..00b5c7e91 100644 --- a/linux/drivers/media/dvb/frontends/cx22702.c +++ b/linux/drivers/media/dvb/frontends/cx22702.c @@ -580,7 +580,7 @@ struct dvb_frontend *cx22702_attach(const struct cx22702_config *config, struct cx22702_state *state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct cx22702_state), GFP_KERNEL); + state = kzalloc(sizeof(struct cx22702_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/linux/drivers/media/dvb/frontends/cx24110.c b/linux/drivers/media/dvb/frontends/cx24110.c index 87ae29db0..ffbcfabd8 100644 --- a/linux/drivers/media/dvb/frontends/cx24110.c +++ b/linux/drivers/media/dvb/frontends/cx24110.c @@ -598,7 +598,7 @@ struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, int ret; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct cx24110_state), GFP_KERNEL); + state = kzalloc(sizeof(struct cx24110_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c b/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c index db8a937cc..a7fc7e53a 100644 --- a/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c +++ b/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c @@ -117,7 +117,7 @@ struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void) struct dvb_dummy_fe_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); if (state == NULL) goto error; /* create dvb_frontend */ @@ -137,7 +137,7 @@ struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void) struct dvb_dummy_fe_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); if (state == NULL) goto error; /* create dvb_frontend */ @@ -157,7 +157,7 @@ struct dvb_frontend *dvb_dummy_fe_qam_attach(void) struct dvb_dummy_fe_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); if (state == NULL) goto error; /* create dvb_frontend */ diff --git a/linux/drivers/media/dvb/frontends/l64781.c b/linux/drivers/media/dvb/frontends/l64781.c index e1e70e9e0..3051b64aa 100644 --- a/linux/drivers/media/dvb/frontends/l64781.c +++ b/linux/drivers/media/dvb/frontends/l64781.c @@ -501,7 +501,7 @@ struct dvb_frontend* l64781_attach(const struct l64781_config* config, { .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct l64781_state), GFP_KERNEL); + state = kzalloc(sizeof(struct l64781_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/linux/drivers/media/dvb/frontends/lgs8gl5.c b/linux/drivers/media/dvb/frontends/lgs8gl5.c index 380ae1868..8780905bc 100644 --- a/linux/drivers/media/dvb/frontends/lgs8gl5.c +++ b/linux/drivers/media/dvb/frontends/lgs8gl5.c @@ -411,7 +411,7 @@ lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c) dprintk("%s\n", __func__); /* Allocate memory for the internal state */ - state = kmalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL); + state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/linux/drivers/media/dvb/frontends/mt312.c b/linux/drivers/media/dvb/frontends/mt312.c index f348f8bea..4d14f084d 100644 --- a/linux/drivers/media/dvb/frontends/mt312.c +++ b/linux/drivers/media/dvb/frontends/mt312.c @@ -784,7 +784,7 @@ struct dvb_frontend *mt312_attach(const struct mt312_config *config, struct mt312_state *state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct mt312_state), GFP_KERNEL); + state = kzalloc(sizeof(struct mt312_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/linux/drivers/media/dvb/frontends/nxt6000.c b/linux/drivers/media/dvb/frontends/nxt6000.c index 0eef22dbf..a763ec756 100644 --- a/linux/drivers/media/dvb/frontends/nxt6000.c +++ b/linux/drivers/media/dvb/frontends/nxt6000.c @@ -545,7 +545,7 @@ struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, struct nxt6000_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct nxt6000_state), GFP_KERNEL); + state = kzalloc(sizeof(struct nxt6000_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/linux/drivers/media/dvb/frontends/or51132.c b/linux/drivers/media/dvb/frontends/or51132.c index 8133ea3cd..38e67accb 100644 --- a/linux/drivers/media/dvb/frontends/or51132.c +++ b/linux/drivers/media/dvb/frontends/or51132.c @@ -562,7 +562,7 @@ struct dvb_frontend* or51132_attach(const struct or51132_config* config, struct or51132_state* state = NULL; /* Allocate memory for the internal state */ - state = kmalloc(sizeof(struct or51132_state), GFP_KERNEL); + state = kzalloc(sizeof(struct or51132_state), GFP_KERNEL); if (state == NULL) return NULL; diff --git a/linux/drivers/media/dvb/frontends/or51211.c b/linux/drivers/media/dvb/frontends/or51211.c index 16cf2fdd5..c709ce677 100644 --- a/linux/drivers/media/dvb/frontends/or51211.c +++ b/linux/drivers/media/dvb/frontends/or51211.c @@ -527,7 +527,7 @@ struct dvb_frontend* or51211_attach(const struct or51211_config* config, struct or51211_state* state = NULL; /* Allocate memory for the internal state */ - state = kmalloc(sizeof(struct or51211_state), GFP_KERNEL); + state = kzalloc(sizeof(struct or51211_state), GFP_KERNEL); if (state == NULL) return NULL; diff --git a/linux/drivers/media/dvb/frontends/s5h1409.c b/linux/drivers/media/dvb/frontends/s5h1409.c index 3e08d985d..fb3011518 100644 --- a/linux/drivers/media/dvb/frontends/s5h1409.c +++ b/linux/drivers/media/dvb/frontends/s5h1409.c @@ -796,7 +796,7 @@ struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config, u16 reg; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct s5h1409_state), GFP_KERNEL); + state = kzalloc(sizeof(struct s5h1409_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/linux/drivers/media/dvb/frontends/s5h1411.c b/linux/drivers/media/dvb/frontends/s5h1411.c index 66e2dd6d6..d8adf1e32 100644 --- a/linux/drivers/media/dvb/frontends/s5h1411.c +++ b/linux/drivers/media/dvb/frontends/s5h1411.c @@ -844,7 +844,7 @@ struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, u16 reg; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct s5h1411_state), GFP_KERNEL); + state = kzalloc(sizeof(struct s5h1411_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/linux/drivers/media/dvb/frontends/si21xx.c b/linux/drivers/media/dvb/frontends/si21xx.c index 216a532ab..ee0a0a2dd 100644 --- a/linux/drivers/media/dvb/frontends/si21xx.c +++ b/linux/drivers/media/dvb/frontends/si21xx.c @@ -1003,7 +1003,7 @@ struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, dprintk("%s\n", __func__); /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct si21xx_state), GFP_KERNEL); + state = kzalloc(sizeof(struct si21xx_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/linux/drivers/media/dvb/frontends/sp8870.c b/linux/drivers/media/dvb/frontends/sp8870.c index 1c9a9b405..b85eb60a8 100644 --- a/linux/drivers/media/dvb/frontends/sp8870.c +++ b/linux/drivers/media/dvb/frontends/sp8870.c @@ -557,7 +557,7 @@ struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, struct sp8870_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct sp8870_state), GFP_KERNEL); + state = kzalloc(sizeof(struct sp8870_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/linux/drivers/media/dvb/frontends/sp887x.c b/linux/drivers/media/dvb/frontends/sp887x.c index 559509ab4..4a7c3d842 100644 --- a/linux/drivers/media/dvb/frontends/sp887x.c +++ b/linux/drivers/media/dvb/frontends/sp887x.c @@ -557,7 +557,7 @@ struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, struct sp887x_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct sp887x_state), GFP_KERNEL); + state = kzalloc(sizeof(struct sp887x_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/linux/drivers/media/dvb/frontends/stv0288.c b/linux/drivers/media/dvb/frontends/stv0288.c index ff1194de3..2930a5d67 100644 --- a/linux/drivers/media/dvb/frontends/stv0288.c +++ b/linux/drivers/media/dvb/frontends/stv0288.c @@ -570,7 +570,7 @@ struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, int id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct stv0288_state), GFP_KERNEL); + state = kzalloc(sizeof(struct stv0288_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/linux/drivers/media/dvb/frontends/stv0297.c b/linux/drivers/media/dvb/frontends/stv0297.c index 1c5a0ae82..2e709a7a5 100644 --- a/linux/drivers/media/dvb/frontends/stv0297.c +++ b/linux/drivers/media/dvb/frontends/stv0297.c @@ -663,7 +663,7 @@ struct dvb_frontend *stv0297_attach(const struct stv0297_config *config, struct stv0297_state *state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct stv0297_state), GFP_KERNEL); + state = kzalloc(sizeof(struct stv0297_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/linux/drivers/media/dvb/frontends/stv0299.c b/linux/drivers/media/dvb/frontends/stv0299.c index 6c1cb1973..968874469 100644 --- a/linux/drivers/media/dvb/frontends/stv0299.c +++ b/linux/drivers/media/dvb/frontends/stv0299.c @@ -667,7 +667,7 @@ struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, int id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct stv0299_state), GFP_KERNEL); + state = kzalloc(sizeof(struct stv0299_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/linux/drivers/media/dvb/frontends/tda10021.c b/linux/drivers/media/dvb/frontends/tda10021.c index c6f90f33a..242636e59 100644 --- a/linux/drivers/media/dvb/frontends/tda10021.c +++ b/linux/drivers/media/dvb/frontends/tda10021.c @@ -413,7 +413,7 @@ struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, u8 id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda10021_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda10021_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/linux/drivers/media/dvb/frontends/tda10048.c b/linux/drivers/media/dvb/frontends/tda10048.c index edf5a19a3..fd8c4cf00 100644 --- a/linux/drivers/media/dvb/frontends/tda10048.c +++ b/linux/drivers/media/dvb/frontends/tda10048.c @@ -1100,7 +1100,7 @@ struct dvb_frontend *tda10048_attach(const struct tda10048_config *config, dprintk(1, "%s()\n", __func__); /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda10048_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda10048_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/linux/drivers/media/dvb/frontends/tda1004x.c b/linux/drivers/media/dvb/frontends/tda1004x.c index 4981cef8b..f2a8abe0a 100644 --- a/linux/drivers/media/dvb/frontends/tda1004x.c +++ b/linux/drivers/media/dvb/frontends/tda1004x.c @@ -1269,7 +1269,7 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, int id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL); if (!state) { printk(KERN_ERR "Can't alocate memory for tda10045 state\n"); return NULL; @@ -1339,7 +1339,7 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, int id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL); if (!state) { printk(KERN_ERR "Can't alocate memory for tda10046 state\n"); return NULL; diff --git a/linux/drivers/media/dvb/frontends/tda10086.c b/linux/drivers/media/dvb/frontends/tda10086.c index c749f96c2..502ce67cf 100644 --- a/linux/drivers/media/dvb/frontends/tda10086.c +++ b/linux/drivers/media/dvb/frontends/tda10086.c @@ -746,7 +746,7 @@ struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, dprintk ("%s\n", __func__); /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda10086_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda10086_state), GFP_KERNEL); if (!state) return NULL; diff --git a/linux/drivers/media/dvb/frontends/tda8083.c b/linux/drivers/media/dvb/frontends/tda8083.c index 5b843b2e6..9369f7442 100644 --- a/linux/drivers/media/dvb/frontends/tda8083.c +++ b/linux/drivers/media/dvb/frontends/tda8083.c @@ -417,7 +417,7 @@ struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, struct tda8083_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda8083_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda8083_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/linux/drivers/media/dvb/frontends/tda80xx.c b/linux/drivers/media/dvb/frontends/tda80xx.c index 83fc57a22..e04fd984f 100644 --- a/linux/drivers/media/dvb/frontends/tda80xx.c +++ b/linux/drivers/media/dvb/frontends/tda80xx.c @@ -646,7 +646,7 @@ struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config, int ret; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda80xx_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda80xx_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/linux/drivers/media/dvb/frontends/ves1820.c b/linux/drivers/media/dvb/frontends/ves1820.c index a184597f1..6e78e4865 100644 --- a/linux/drivers/media/dvb/frontends/ves1820.c +++ b/linux/drivers/media/dvb/frontends/ves1820.c @@ -374,7 +374,7 @@ struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, struct ves1820_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct ves1820_state), GFP_KERNEL); + state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/linux/drivers/media/dvb/frontends/ves1x93.c b/linux/drivers/media/dvb/frontends/ves1x93.c index bd558960b..8d7854c2f 100644 --- a/linux/drivers/media/dvb/frontends/ves1x93.c +++ b/linux/drivers/media/dvb/frontends/ves1x93.c @@ -456,7 +456,7 @@ struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, u8 identity; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL); + state = kzalloc(sizeof(struct ves1x93_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ -- cgit v1.2.3 From 1537b0475bd1f694226d14373ed31298f2d0e8ef Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Mon, 10 Aug 2009 21:59:33 -0400 Subject: siano: read buffer overflow From: Roel Kluin With mode DEVICE_MODE_RAW_TUNER a read occurs past the end of smscore_fw_lkup[]. Subsequently an attempt is made to load the firmware from the resulting filename. Priority: normal Signed-off-by: Roel Kluin Signed-off-by: Andrew Morton Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/dvb/siano/smscoreapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/dvb/siano/smscoreapi.c b/linux/drivers/media/dvb/siano/smscoreapi.c index 54f59f24e..c0306af09 100644 --- a/linux/drivers/media/dvb/siano/smscoreapi.c +++ b/linux/drivers/media/dvb/siano/smscoreapi.c @@ -816,7 +816,7 @@ int smscore_set_device_mode(struct smscore_device_t *coredev, int mode) sms_debug("set device mode to %d", mode); if (coredev->device_flags & SMS_DEVICE_FAMILY2) { - if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_RAW_TUNER) { + if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) { sms_err("invalid mode specified %d", mode); return -EINVAL; } -- cgit v1.2.3 From 796c449e9807bc070a33bcbbab2f4cae6cfc6d14 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Aug 2009 12:14:26 +0200 Subject: fix device match logic From: Laurent Pinchart I forgot to change USB_DEVICE_ID_MATCH_VENDOR to USB_DEVICE_ID_MATCH_DEVICE in the last commit, defeating its whole purpose. Fix this. kernel-sync: Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/uvc/uvc_driver.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index fa13b6765..09b2e4ad5 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -1996,7 +1996,7 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* ViMicro Vega */ - { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x0ac8, .idProduct = 0x332d, @@ -2005,7 +2005,7 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, /* ViMicro - Minoru3D */ - { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x0ac8, .idProduct = 0x3410, @@ -2014,7 +2014,7 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, /* ViMicro Venus - Minoru3D */ - { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x0ac8, .idProduct = 0x3420, -- cgit v1.2.3