diff options
Diffstat (limited to 'linux/drivers/media')
111 files changed, 6864 insertions, 2098 deletions
diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 26aa3a58b..630782f88 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -14,6 +14,7 @@ #include "stv0297_cs2.h" #include "mt312.h" #include "lgdt330x.h" +#include "fe_lgh06xf.h" #include "dvb-pll.h" /* lnb control */ @@ -166,11 +167,12 @@ static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend* fe, u32 srate, return 0; } -static int samsung_tbmu24112_pll_set(struct dvb_frontend* fe, struct i2c_adapter *i2c, struct dvb_frontend_parameters* params) +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; @@ -181,8 +183,11 @@ static int samsung_tbmu24112_pll_set(struct dvb_frontend* fe, struct i2c_adapter if (params->frequency < 1500000) buf[3] |= 0x10; - if (i2c_transfer(i2c, &msg, 1) != 1) + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&fc->i2c_adap, &msg, 1) != 1) { return -EIO; + } return 0; } @@ -241,7 +246,6 @@ static struct stv0299_config samsung_tbmu24112_config = { .volt13_op0_op1 = STV0299_VOLT13_OP1, .min_delay_ms = 100, .set_symbol_rate = samsung_tbmu24112_set_symbol_rate, - .pll_set = samsung_tbmu24112_pll_set, }; /* dvb-t mt352 */ @@ -264,11 +268,14 @@ static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe) return 0; } -static int samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf) +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; @@ -276,19 +283,18 @@ static int samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_front if (params->frequency >= 161000000 && params->frequency <= 439000000) bs = 0x0a; if (params->frequency >= 447000000 && params->frequency <= 863000000) bs = 0x08; - pllbuf[0] = 0xc2; /* Note: non-linux standard PLL i2c address */ + pllbuf[0] = 0x61; pllbuf[1] = div >> 8; pllbuf[2] = div & 0xff; pllbuf[3] = 0xcc; pllbuf[4] = bs; - return 0; + return 5; } static struct mt352_config samsung_tdtc9251dh0_config = { .demod_address = 0x0f, .demod_init = samsung_tdtc9251dh0_demod_init, - .pll_set = samsung_tdtc9251dh0_pll_set, }; static int flexcop_fe_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) @@ -297,56 +303,21 @@ static int flexcop_fe_request_firmware(struct dvb_frontend* fe, const struct fir return request_firmware(fw, name, fc->dev); } -static int lgdt3303_pll_set(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) +static int lgdt3303_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct flexcop_device *fc = fe->dvb->priv; - u8 buf[4]; - struct i2c_msg msg = - { .addr = 0x61, .flags = 0, .buf = buf, .len = 4 }; - int err; - - dvb_pll_configure(&dvb_pll_tdvs_tua6034,buf, params->frequency, 0); - dprintk(1, "%s: tuner at 0x%02x bytes: 0x%02x 0x%02x 0x%02x 0x%02x\n", - __FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]); - if ((err = i2c_transfer(&fc->i2c_adap, &msg, 1)) != 1) { - printk(KERN_WARNING "lgdt3303: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - buf[0] = 0x86 | 0x18; - buf[1] = 0x50; - msg.len = 2; - if ((err = i2c_transfer(&fc->i2c_adap, &msg, 1)) != 1) { - printk(KERN_WARNING "lgdt3303: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - return 0; + return lg_h06xf_pll_set(fe, &fc->i2c_adap, params); } static struct lgdt330x_config air2pc_atsc_hd5000_config = { .demod_address = 0x59, .demod_chip = LGDT3303, .serial_mpeg = 0x04, - .pll_set = lgdt3303_pll_set, .clock_polarity_flip = 1, }; static struct nxt200x_config samsung_tbmv_config = { .demod_address = 0x0a, - .pll_address = 0xc2, - .pll_desc = &dvb_pll_samsung_tbmv, }; static struct bcm3510_config air2pc_atsc_first_gen_config = { @@ -354,7 +325,7 @@ static struct bcm3510_config air2pc_atsc_first_gen_config = { .request_firmware = flexcop_fe_request_firmware, }; -static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int skystar23_samsung_tbdu18132_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { u8 buf[4]; u32 div; @@ -371,6 +342,8 @@ static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct d if (params->frequency < 1550000) buf[3] |= 0x02; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&fc->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -379,10 +352,9 @@ static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct d static struct mt312_config skystar23_samsung_tbdu18132_config = { .demod_address = 0x0e, - .pll_set = skystar23_samsung_tbdu18132_pll_set, }; -int alps_tdee4_stv0297_pll_set (struct dvb_frontend* fe, struct dvb_frontend_parameters *fep) +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]; @@ -417,6 +389,8 @@ int alps_tdee4_stv0297_pll_set (struct dvb_frontend* fe, struct dvb_frontend_par else if (fep->frequency <= 822000000) buf[3] = 0x08; else buf[3] = 0x88; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); 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_WRITE,FC_I2C_PORT_TUNER,0x61,buf[0],&buf[1],3); deb_tuner("tuner write returned: %d\n",ret); @@ -427,7 +401,6 @@ int alps_tdee4_stv0297_pll_set (struct dvb_frontend* fe, struct dvb_frontend_par static struct stv0297_config alps_tdee4_stv0297_config = { .demod_address = 0x1c, - .pll_set = alps_tdee4_stv0297_pll_set, .fclk = STV0297_PAL_FCLK, .demodfreq = STV0297_PAL_IF - STV0297_PAL_FCLK, }; @@ -441,6 +414,8 @@ int flexcop_frontend_init(struct flexcop_device *fc) if ((fc->fe = stv0299_attach(&samsung_tbmu24112_config, &fc->i2c_adap)) != NULL) { 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; @@ -452,16 +427,19 @@ int flexcop_frontend_init(struct flexcop_device *fc) /* try the air dvb-t (mt352/Samsung tdtc9251dh0(??)) */ if ((fc->fe = mt352_attach(&samsung_tdtc9251dh0_config, &fc->i2c_adap)) != NULL ) { fc->dev_type = FC_AIR_DVB; + fc->fe->ops->tuner_ops.calc_regs = samsung_tdtc9251dh0_calc_regs; info("found the mt352 at i2c address: 0x%02x",samsung_tdtc9251dh0_config.demod_address); } else /* try the air atsc 2nd generation (nxt2002) */ if ((fc->fe = nxt200x_attach(&samsung_tbmv_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_AIR_ATSC2; + dvb_pll_attach(fc->fe, 0x61, &fc->i2c_adap, &dvb_pll_samsung_tbmv); info("found the nxt2002 at i2c address: 0x%02x",samsung_tbmv_config.demod_address); } else /* try the air atsc 3nd generation (lgdt3303) */ if ((fc->fe = lgdt330x_attach(&air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_AIR_ATSC3; + fc->fe->ops->tuner_ops.set_params = lgdt3303_tuner_set_params; info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address); } else /* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */ @@ -472,12 +450,15 @@ int flexcop_frontend_init(struct flexcop_device *fc) /* try the cable dvb (stv0297) */ if ((fc->fe = stv0297_cs2_attach(&alps_tdee4_stv0297_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_CABLE; + fc->fe->ops->tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params; info("found the stv0297 at i2c address: 0x%02x",alps_tdee4_stv0297_config.demod_address); } else /* try the sky v2.3 (vp310/Samsung tbdu18132(tsa5059)) */ if ((fc->fe = vp310_mt312_attach(&skystar23_samsung_tbdu18132_config, &fc->i2c_adap)) != NULL) { 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; diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c index 0e01863ab..c4ffdb375 100644 --- a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -148,12 +148,15 @@ static int thomson_dtt7579_demod_init(struct dvb_frontend* fe) return 0; } -static int thomson_dtt7579_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf) +static int thomson_dtt7579_tuner_calc_regs(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len) { u32 div; unsigned char bs = 0; unsigned char cp = 0; + if (buf_len < 5) + return -EINVAL; + div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; if (params->frequency < 542000000) @@ -170,27 +173,25 @@ static int thomson_dtt7579_pll_set(struct dvb_frontend* fe, struct dvb_frontend_ else bs = 0x08; - pllbuf[0] = 0xc0; // Note: non-linux standard PLL i2c address + pllbuf[0] = 0x60; pllbuf[1] = div >> 8; pllbuf[2] = div & 0xff; pllbuf[3] = cp; pllbuf[4] = bs; - return 0; + return 5; } static struct mt352_config thomson_dtt7579_config = { .demod_address = 0x0f, .demod_init = thomson_dtt7579_demod_init, - .pll_set = thomson_dtt7579_pll_set, }; static struct zl10353_config thomson_dtt7579_zl10353_config = { .demod_address = 0x0f, - .pll_set = thomson_dtt7579_pll_set, }; -static int cx24108_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int cx24108_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { u32 freq = params->frequency; @@ -243,7 +244,7 @@ static int cx24108_pll_set(struct dvb_frontend* fe, struct dvb_frontend_paramete return 0; } -static int pinnsat_pll_init(struct dvb_frontend* fe) +static int pinnsat_tuner_init(struct dvb_frontend* fe) { struct dvb_bt8xx_card *card = fe->dvb->priv; @@ -253,7 +254,7 @@ static int pinnsat_pll_init(struct dvb_frontend* fe) return 0; } -static int pinnsat_pll_sleep(struct dvb_frontend* fe) +static int pinnsat_tuner_sleep(struct dvb_frontend* fe) { struct dvb_bt8xx_card *card = fe->dvb->priv; @@ -264,12 +265,9 @@ static int pinnsat_pll_sleep(struct dvb_frontend* fe) static struct cx24110_config pctvsat_config = { .demod_address = 0x55, - .pll_init = pinnsat_pll_init, - .pll_set = cx24108_pll_set, - .pll_sleep = pinnsat_pll_sleep, }; -static int microtune_mt7202dtf_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int microtune_mt7202dtf_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; u8 cfg, cpump, band_select; @@ -303,6 +301,8 @@ static int microtune_mt7202dtf_pll_set(struct dvb_frontend* fe, struct dvb_front data[2] = ((div >> 10) & 0x60) | cfg; data[3] = (cpump << 6) | band_select; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(card->i2c_adapter, &msg, 1); return (div * 166666 - 36000000); } @@ -316,7 +316,6 @@ static int microtune_mt7202dtf_request_firmware(struct dvb_frontend* fe, const s static struct sp887x_config microtune_mt7202dtf_config = { .demod_address = 0x70, - .pll_set = microtune_mt7202dtf_pll_set, .request_firmware = microtune_mt7202dtf_request_firmware, }; @@ -343,12 +342,14 @@ static int advbt771_samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe) return 0; } -static int advbt771_samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf) +static int advbt771_samsung_tdtc9251dh0_tuner_calc_regs(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len) { u32 div; unsigned char bs = 0; unsigned char cp = 0; + if (buf_len < 5) return -EINVAL; + div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; if (params->frequency < 150000000) @@ -389,19 +390,18 @@ static int advbt771_samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct else bs = 0x08; - pllbuf[0] = 0xc2; // Note: non-linux standard PLL i2c address + pllbuf[0] = 0x61; pllbuf[1] = div >> 8; pllbuf[2] = div & 0xff; pllbuf[3] = cp; pllbuf[4] = bs; - return 0; + return 5; } static struct mt352_config advbt771_samsung_tdtc9251dh0_config = { .demod_address = 0x0f, .demod_init = advbt771_samsung_tdtc9251dh0_demod_init, - .pll_set = advbt771_samsung_tdtc9251dh0_pll_set, }; static struct dst_config dst_config = { @@ -461,7 +461,7 @@ static struct or51211_config or51211_config = { .sleep = or51211_sleep, }; -static int vp3021_alps_tded4_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int vp3021_alps_tded4_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; u8 buf[4]; @@ -484,6 +484,8 @@ static int vp3021_alps_tded4_pll_set(struct dvb_frontend* fe, struct dvb_fronten else return -EINVAL; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(card->i2c_adapter, &msg, 1); return 0; } @@ -491,7 +493,6 @@ static int vp3021_alps_tded4_pll_set(struct dvb_frontend* fe, struct dvb_fronten static struct nxt6000_config vp3021_alps_tded4_config = { .demod_address = 0x0a, .clock_inversion = 1, - .pll_set = vp3021_alps_tded4_pll_set, }; static int digitv_alps_tded4_demod_init(struct dvb_frontend* fe) @@ -512,14 +513,17 @@ static int digitv_alps_tded4_demod_init(struct dvb_frontend* fe) return 0; } -static int digitv_alps_tded4_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf) +static int digitv_alps_tded4_tuner_calc_regs(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len) { u32 div; struct dvb_ofdm_parameters *op = ¶ms->u.ofdm; + if (buf_len < 5) + return -EINVAL; + div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; - pllbuf[0] = 0xc2; + pllbuf[0] = 0x61; pllbuf[1] = (div >> 8) & 0x7F; pllbuf[2] = div & 0xFF; pllbuf[3] = 0x85; @@ -536,7 +540,7 @@ static int digitv_alps_tded4_pll_set(struct dvb_frontend* fe, struct dvb_fronten if (op->bandwidth == 8) pllbuf[4] |= 0x04; - return 0; + return 5; } static void digitv_alps_tded4_reset(struct dvb_bt8xx_card *bt) @@ -563,43 +567,18 @@ static void digitv_alps_tded4_reset(struct dvb_bt8xx_card *bt) static struct mt352_config digitv_alps_tded4_config = { .demod_address = 0x0a, .demod_init = digitv_alps_tded4_demod_init, - .pll_set = digitv_alps_tded4_pll_set, }; -static int tdvs_tua6034_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int tdvs_tua6034_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; - u8 buf[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; - int err; - - dvb_pll_configure(&dvb_pll_tdvs_tua6034, buf, params->frequency, 0); - dprintk("%s: tuner at 0x%02x bytes: 0x%02x 0x%02x 0x%02x 0x%02x\n", - __FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]); - if ((err = i2c_transfer(card->i2c_adapter, &msg, 1)) != 1) { - printk(KERN_WARNING "dvb-bt8xx: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - /* Set the Auxiliary Byte. */ - buf[2] &= ~0x20; - buf[2] |= 0x18; - buf[3] = 0x50; - i2c_transfer(card->i2c_adapter, &msg, 1); - - return 0; + return lg_h06xf_pll_set(fe, card->i2c_adapter, params); } static struct lgdt330x_config tdvs_tua6034_config = { .demod_address = 0x0e, .demod_chip = LGDT3303, .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ - .pll_set = tdvs_tua6034_pll_set, }; static void lgdt330x_reset(struct dvb_bt8xx_card *bt) @@ -629,6 +608,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) card->i2c_adapter); if (card->fe != NULL) { + card->fe->ops->tuner_ops.calc_regs = thomson_dtt7579_tuner_calc_regs; card->fe->ops->info.frequency_min = 174000000; card->fe->ops->info.frequency_max = 862000000; } @@ -637,8 +617,10 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE: lgdt330x_reset(card); card->fe = lgdt330x_attach(&tdvs_tua6034_config, card->i2c_adapter); - if (card->fe != NULL) + if (card->fe != NULL) { + card->fe->ops->tuner_ops.set_params = tdvs_tua6034_tuner_set_params; dprintk ("dvb_bt8xx: lgdt330x detected\n"); + } break; case BTTV_BOARD_NEBULA_DIGITV: @@ -651,6 +633,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) digitv_alps_tded4_reset(card); card->fe = nxt6000_attach(&vp3021_alps_tded4_config, card->i2c_adapter); if (card->fe != NULL) { + card->fe->ops->tuner_ops.set_params = vp3021_alps_tded4_tuner_set_params; dprintk ("dvb_bt8xx: an nxt6000 was detected on your digitv card\n"); break; } @@ -659,17 +642,23 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) digitv_alps_tded4_reset(card); card->fe = mt352_attach(&digitv_alps_tded4_config, card->i2c_adapter); - if (card->fe != NULL) + if (card->fe != NULL) { + card->fe->ops->tuner_ops.calc_regs = digitv_alps_tded4_tuner_calc_regs; dprintk ("dvb_bt8xx: an mt352 was detected on your digitv card\n"); + } break; case BTTV_BOARD_AVDVBT_761: card->fe = sp887x_attach(µtune_mt7202dtf_config, card->i2c_adapter); + if (card->fe) { + card->fe->ops->tuner_ops.set_params = microtune_mt7202dtf_tuner_set_params; + } break; case BTTV_BOARD_AVDVBT_771: card->fe = mt352_attach(&advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter); if (card->fe != NULL) { + card->fe->ops->tuner_ops.calc_regs = advbt771_samsung_tdtc9251dh0_tuner_calc_regs; card->fe->ops->info.frequency_min = 174000000; card->fe->ops->info.frequency_max = 862000000; } @@ -698,6 +687,11 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) case BTTV_BOARD_PINNACLESAT: card->fe = cx24110_attach(&pctvsat_config, card->i2c_adapter); + if (card->fe) { + card->fe->ops->tuner_ops.init = pinnsat_tuner_init; + card->fe->ops->tuner_ops.sleep = pinnsat_tuner_sleep; + card->fe->ops->tuner_ops.set_params = cx24108_tuner_set_params; + } break; case BTTV_BOARD_PC_HDTV: diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h index 626718774..ad261b593 100644 --- a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h @@ -40,6 +40,7 @@ #include "cx24110.h" #include "or51211.h" #include "lgdt330x.h" +#include "fe_lgh06xf.h" #include "zl10353.h" struct dvb_bt8xx_card { diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 12e890e06..bc752929f 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -215,6 +215,11 @@ static void dvb_frontend_init(struct dvb_frontend *fe) if (fe->ops->init) fe->ops->init(fe); + if (fe->ops->tuner_ops.init) { + fe->ops->tuner_ops.init(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 0); + } } void dvb_frontend_reinitialise(struct dvb_frontend *fe) @@ -577,6 +582,11 @@ static int dvb_frontend_thread(void *data) if (dvb_powerdown_on_sleep) if (fe->ops->set_voltage) fe->ops->set_voltage(fe, SEC_VOLTAGE_OFF); + if (fe->ops->tuner_ops.sleep) { + fe->ops->tuner_ops.sleep(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 0); + } if (fe->ops->sleep) fe->ops->sleep(fe); } @@ -1091,6 +1101,11 @@ int dvb_unregister_frontend(struct dvb_frontend* fe) mutex_lock(&frontend_mutex); dvb_unregister_device (fepriv->dvbdev); dvb_frontend_stop (fe); + if (fe->ops->tuner_ops.release) { + fe->ops->tuner_ops.release(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 0); + } if (fe->ops->release) fe->ops->release(fe); else diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h index 5926a3b74..05ec9954b 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -49,6 +49,44 @@ struct dvb_frontend_tune_settings { struct dvb_frontend; +struct dvb_tuner_info { + char name[128]; + + u32 frequency_min; + u32 frequency_max; + u32 frequency_step; + + u32 bandwidth_min; + u32 bandwidth_max; + u32 bandwidth_step; +}; + +struct dvb_tuner_ops { + + struct dvb_tuner_info info; + + int (*release)(struct dvb_frontend *fe); + int (*init)(struct dvb_frontend *fe); + int (*sleep)(struct dvb_frontend *fe); + + /** This is for simple PLLs - set all parameters in one go. */ + int (*set_params)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p); + + /** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */ + int (*calc_regs)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p, u8 *buf, int buf_len); + + int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency); + int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); + +#define TUNER_STATUS_LOCKED 1 + int (*get_status)(struct dvb_frontend *fe, u32 *status); + + /** These are provided seperately from set_params in order to facilitate silicon + * tuners which require sophisticated tuning loops, controlling each parameter seperately. */ + int (*set_frequency)(struct dvb_frontend *fe, u32 frequency); + int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth); +}; + struct dvb_frontend_ops { struct dvb_frontend_info info; @@ -86,6 +124,8 @@ struct dvb_frontend_ops { int (*enable_high_lnb_voltage)(struct dvb_frontend* fe, long arg); int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd); int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable); + + struct dvb_tuner_ops tuner_ops; }; #define MAX_EVENT 8 @@ -103,6 +143,7 @@ struct dvb_frontend { struct dvb_frontend_ops* ops; struct dvb_adapter *dvb; void* demodulator_priv; + void* tuner_priv; void* frontend_priv; void* misc_priv; }; diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index a34068f50..11c8c9812 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -27,6 +27,7 @@ #include "cx22702.h" #include "lgdt330x.h" +#include "fe_lgh06xf.h" #include "mt352.h" #include "mt352_priv.h" @@ -150,6 +151,15 @@ static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff) return cxusb_ctrl_msg(d, CMD_POWER_OFF, &b, 1, NULL, 0); } +static int cxusb_bluebird_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 b = 0; + if (onoff) + return cxusb_ctrl_msg(d, CMD_POWER_ON, &b, 1, NULL, 0); + else + return 0; +} + static int cxusb_streaming_ctrl(struct dvb_usb_device *d, int onoff) { u8 buf[2] = { 0x03, 0x00 }; @@ -313,62 +323,33 @@ static int cxusb_mt352_demod_init(struct dvb_frontend* fe) return 0; } -static int cxusb_lgh064f_pll_set_i2c(struct dvb_frontend *fe, - struct dvb_frontend_parameters *fep) +static int cxusb_lgh064f_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) { struct dvb_usb_device *d = fe->dvb->priv; - int ret = 0; - u8 b[5]; - struct i2c_msg msg = { .addr = d->pll_addr, .flags = 0, - .buf = &b[1], .len = 4 }; - - dvb_usb_pll_set(fe,fep,b); - - if (i2c_transfer (&d->i2c_adap, &msg, 1) != 1) { - err("tuner i2c write failed for pll_set."); - ret = -EREMOTEIO; - } - msleep(1); - - /* Set the Auxiliary Byte. */ - b[3] &= ~0x20; - b[3] |= 0x18; - b[4] = 0x50; - if (i2c_transfer(&d->i2c_adap, &msg, 1) != 1) { - err("tuner i2c write failed writing auxiliary byte."); - ret = -EREMOTEIO; - } - msleep(1); - - return ret; + return lg_h06xf_pll_set(fe, &d->i2c_adap, fep); } static struct cx22702_config cxusb_cx22702_config = { .demod_address = 0x63, .output_mode = CX22702_PARALLEL_OUTPUT, - - .pll_init = dvb_usb_pll_init_i2c, - .pll_set = dvb_usb_pll_set_i2c, }; static struct lgdt330x_config cxusb_lgdt3303_config = { .demod_address = 0x0e, .demod_chip = LGDT3303, - .pll_set = cxusb_lgh064f_pll_set_i2c, }; static struct mt352_config cxusb_dee1601_config = { .demod_address = 0x0f, .demod_init = cxusb_dee1601_demod_init, - .pll_set = dvb_usb_pll_set, }; -struct mt352_config cxusb_mt352_config = { +static struct mt352_config cxusb_mt352_config = { /* used in both lgz201 and th7579 */ .demod_address = 0x0f, .demod_init = cxusb_mt352_demod_init, - .pll_set = dvb_usb_pll_set, }; /* Callbacks for DVB USB */ @@ -381,13 +362,6 @@ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_device *d) return 0; } -static int cxusb_lgh064f_tuner_attach(struct dvb_usb_device *d) -{ - d->pll_addr = 0x61; - d->pll_desc = &dvb_pll_tdvs_tua6034; - return 0; -} - static int cxusb_dee1601_tuner_attach(struct dvb_usb_device *d) { d->pll_addr = 0x61; @@ -417,8 +391,11 @@ static int cxusb_cx22702_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, &b, 1); - if ((d->fe = cx22702_attach(&cxusb_cx22702_config, &d->i2c_adap)) != NULL) + if ((d->fe = cx22702_attach(&cxusb_cx22702_config, &d->i2c_adap)) != NULL) { + d->fe->ops->tuner_ops.init = dvb_usb_tuner_init_i2c; + d->fe->ops->tuner_ops.set_params = dvb_usb_tuner_set_params_i2c; return 0; + } return -EIO; } @@ -430,8 +407,10 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0); - if ((d->fe = lgdt330x_attach(&cxusb_lgdt3303_config, &d->i2c_adap)) != NULL) + if ((d->fe = lgdt330x_attach(&cxusb_lgdt3303_config, &d->i2c_adap)) != NULL) { + d->fe->ops->tuner_ops.set_params = cxusb_lgh064f_tuner_set_params; return 0; + } return -EIO; } @@ -443,8 +422,10 @@ static int cxusb_mt352_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0); - if ((d->fe = mt352_attach(&cxusb_mt352_config, &d->i2c_adap)) != NULL) + if ((d->fe = mt352_attach(&cxusb_mt352_config, &d->i2c_adap)) != NULL) { + d->fe->ops->tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; return 0; + } return -EIO; } @@ -456,8 +437,10 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0); - if ((d->fe = mt352_attach(&cxusb_dee1601_config, &d->i2c_adap)) != NULL) + if ((d->fe = mt352_attach(&cxusb_dee1601_config, &d->i2c_adap)) != NULL) { + d->fe->ops->tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; return 0; + } return -EIO; } @@ -570,9 +553,8 @@ static struct dvb_usb_properties cxusb_bluebird_lgh064f_properties = { .size_of_priv = sizeof(struct cxusb_state), .streaming_ctrl = cxusb_streaming_ctrl, - .power_ctrl = cxusb_power_ctrl, + .power_ctrl = cxusb_bluebird_power_ctrl, .frontend_attach = cxusb_lgdt3303_frontend_attach, - .tuner_attach = cxusb_lgh064f_tuner_attach, .i2c_algo = &cxusb_i2c_algo, @@ -615,7 +597,7 @@ static struct dvb_usb_properties cxusb_bluebird_dee1601_properties = { .size_of_priv = sizeof(struct cxusb_state), .streaming_ctrl = cxusb_streaming_ctrl, - .power_ctrl = cxusb_power_ctrl, + .power_ctrl = cxusb_bluebird_power_ctrl, .frontend_attach = cxusb_dee1601_frontend_attach, .tuner_attach = cxusb_dee1601_tuner_attach, @@ -664,7 +646,7 @@ static struct dvb_usb_properties cxusb_bluebird_lgz201_properties = { .size_of_priv = sizeof(struct cxusb_state), .streaming_ctrl = cxusb_streaming_ctrl, - .power_ctrl = cxusb_power_ctrl, + .power_ctrl = cxusb_bluebird_power_ctrl, .frontend_attach = cxusb_mt352_frontend_attach, .tuner_attach = cxusb_lgz201_tuner_attach, @@ -709,7 +691,7 @@ static struct dvb_usb_properties cxusb_bluebird_dtt7579_properties = { .size_of_priv = sizeof(struct cxusb_state), .streaming_ctrl = cxusb_streaming_ctrl, - .power_ctrl = cxusb_power_ctrl, + .power_ctrl = cxusb_bluebird_power_ctrl, .frontend_attach = cxusb_mt352_frontend_attach, .tuner_attach = cxusb_dtt7579_tuner_attach, diff --git a/linux/drivers/media/dvb/dvb-usb/dibusb-common.c b/linux/drivers/media/dvb/dvb-usb/dibusb-common.c index 2d52b7667..b0435c8f8 100644 --- a/linux/drivers/media/dvb/dvb-usb/dibusb-common.c +++ b/linux/drivers/media/dvb/dvb-usb/dibusb-common.c @@ -173,8 +173,8 @@ int dibusb_dib3000mc_frontend_attach(struct dvb_usb_device *d) struct dib3000_config demod_cfg; struct dibusb_state *st = d->priv; - demod_cfg.pll_set = dvb_usb_pll_set_i2c; - demod_cfg.pll_init = dvb_usb_pll_init_i2c; + d->fe->ops->tuner_ops.init = dvb_usb_tuner_init_i2c; + d->fe->ops->tuner_ops.set_params = dvb_usb_tuner_set_params_i2c; for (demod_cfg.demod_address = 0x8; demod_cfg.demod_address < 0xd; demod_cfg.demod_address++) if ((d->fe = dib3000mc_attach(&demod_cfg,&d->i2c_adap,&st->ops)) != NULL) { diff --git a/linux/drivers/media/dvb/dvb-usb/dibusb-mb.c b/linux/drivers/media/dvb/dvb-usb/dibusb-mb.c index 1cf2344c3..a7bcb9961 100644 --- a/linux/drivers/media/dvb/dvb-usb/dibusb-mb.c +++ b/linux/drivers/media/dvb/dvb-usb/dibusb-mb.c @@ -20,8 +20,8 @@ static int dibusb_dib3000mb_frontend_attach(struct dvb_usb_device *d) struct dibusb_state *st = d->priv; demod_cfg.demod_address = 0x8; - demod_cfg.pll_set = dvb_usb_pll_set_i2c; - demod_cfg.pll_init = dvb_usb_pll_init_i2c; + d->fe->ops->tuner_ops.init = dvb_usb_tuner_init_i2c; + d->fe->ops->tuner_ops.set_params = dvb_usb_tuner_set_params_i2c; if ((d->fe = dib3000mb_attach(&demod_cfg,&d->i2c_adap,&st->ops)) == NULL) return -ENODEV; diff --git a/linux/drivers/media/dvb/dvb-usb/digitv.c b/linux/drivers/media/dvb/dvb-usb/digitv.c index c2950c6e9..34dbf1a49 100644 --- a/linux/drivers/media/dvb/dvb-usb/digitv.c +++ b/linux/drivers/media/dvb/dvb-usb/digitv.c @@ -112,27 +112,30 @@ static int digitv_mt352_demod_init(struct dvb_frontend *fe) static struct mt352_config digitv_mt352_config = { .demod_init = digitv_mt352_demod_init, - .pll_set = dvb_usb_pll_set, }; -static int digitv_nxt6000_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +static int digitv_nxt6000_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { struct dvb_usb_device *d = fe->dvb->priv; u8 b[5]; - dvb_usb_pll_set(fe,fep,b); + dvb_usb_tuner_calc_regs(fe,fep,b, 5); return digitv_ctrl_msg(d,USB_WRITE_TUNER,0,&b[1],4,NULL,0); } static struct nxt6000_config digitv_nxt6000_config = { .clock_inversion = 1, - .pll_set = digitv_nxt6000_pll_set, }; static int digitv_frontend_attach(struct dvb_usb_device *d) { - if ((d->fe = mt352_attach(&digitv_mt352_config, &d->i2c_adap)) != NULL || - (d->fe = nxt6000_attach(&digitv_nxt6000_config, &d->i2c_adap)) != NULL) + if ((d->fe = mt352_attach(&digitv_mt352_config, &d->i2c_adap)) != NULL) { + d->fe->ops->tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; return 0; + } + if ((d->fe = nxt6000_attach(&digitv_nxt6000_config, &d->i2c_adap)) != NULL) { + d->fe->ops->tuner_ops.set_params = digitv_nxt6000_tuner_set_params; + return 0; + } return -EIO; } 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 9b254532a..6c868d667 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c @@ -46,7 +46,7 @@ int dvb_usb_i2c_exit(struct dvb_usb_device *d) return 0; } -int dvb_usb_pll_init_i2c(struct dvb_frontend *fe) +int dvb_usb_tuner_init_i2c(struct dvb_frontend *fe) { struct dvb_usb_device *d = fe->dvb->priv; struct i2c_msg msg = { .addr = d->pll_addr, .flags = 0, .buf = d->pll_init, .len = 4 }; @@ -63,6 +63,8 @@ int dvb_usb_pll_init_i2c(struct dvb_frontend *fe) deb_pll("pll-buf: %x %x %x %x\n",d->pll_init[0],d->pll_init[1], d->pll_init[2],d->pll_init[3]); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&d->i2c_adap, &msg, 1) != 1) { err("tuner i2c write failed for pll_init."); ret = -EREMOTEIO; @@ -73,38 +75,42 @@ int dvb_usb_pll_init_i2c(struct dvb_frontend *fe) d->tuner_pass_ctrl(fe,0,d->pll_addr); return ret; } -EXPORT_SYMBOL(dvb_usb_pll_init_i2c); +EXPORT_SYMBOL(dvb_usb_tuner_init_i2c); -int dvb_usb_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep, u8 b[5]) +int dvb_usb_tuner_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep, u8 *b, int buf_len) { struct dvb_usb_device *d = fe->dvb->priv; + if (buf_len != 5) + return -EINVAL; if (d->pll_desc == NULL) return 0; deb_pll("pll addr: %x, freq: %d %p\n",d->pll_addr,fep->frequency,d->pll_desc); - b[0] = d->pll_addr << 1; + b[0] = d->pll_addr; dvb_pll_configure(d->pll_desc,&b[1],fep->frequency,fep->u.ofdm.bandwidth); deb_pll("pll-buf: %x %x %x %x %x\n",b[0],b[1],b[2],b[3],b[4]); - return 0; + return 5; } -EXPORT_SYMBOL(dvb_usb_pll_set); +EXPORT_SYMBOL(dvb_usb_tuner_calc_regs); -int dvb_usb_pll_set_i2c(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +int dvb_usb_tuner_set_params_i2c(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { struct dvb_usb_device *d = fe->dvb->priv; int ret = 0; u8 b[5]; struct i2c_msg msg = { .addr = d->pll_addr, .flags = 0, .buf = &b[1], .len = 4 }; - dvb_usb_pll_set(fe,fep,b); + dvb_usb_tuner_calc_regs(fe,fep,b,5); if (d->tuner_pass_ctrl) d->tuner_pass_ctrl(fe,1,d->pll_addr); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&d->i2c_adap, &msg, 1) != 1) { err("tuner i2c write failed for pll_set."); ret = -EREMOTEIO; @@ -116,4 +122,4 @@ int dvb_usb_pll_set_i2c(struct dvb_frontend *fe, struct dvb_frontend_parameters return ret; } -EXPORT_SYMBOL(dvb_usb_pll_set_i2c); +EXPORT_SYMBOL(dvb_usb_tuner_set_params_i2c); diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb.h index b14e842d0..bf342faa4 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -341,9 +341,9 @@ extern int dvb_usb_generic_write(struct dvb_usb_device *, u8 *, u16); extern int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *, u8[], u32 *, int *); /* commonly used pll init and set functions */ -extern int dvb_usb_pll_init_i2c(struct dvb_frontend *); -extern int dvb_usb_pll_set(struct dvb_frontend *, struct dvb_frontend_parameters *, u8[]); -extern int dvb_usb_pll_set_i2c(struct dvb_frontend *, struct dvb_frontend_parameters *); +extern int dvb_usb_tuner_init_i2c(struct dvb_frontend *); +extern int dvb_usb_tuner_calc_regs(struct dvb_frontend *, struct dvb_frontend_parameters *, u8 *buf, int buf_len); +extern int dvb_usb_tuner_set_params_i2c(struct dvb_frontend *, struct dvb_frontend_parameters *); /* commonly used firmware download types and function */ struct hexline { diff --git a/linux/drivers/media/dvb/dvb-usb/umt-010.c b/linux/drivers/media/dvb/dvb-usb/umt-010.c index 8eeaa3db3..04a45f2e4 100644 --- a/linux/drivers/media/dvb/dvb-usb/umt-010.c +++ b/linux/drivers/media/dvb/dvb-usb/umt-010.c @@ -57,7 +57,7 @@ static int umt_mt352_frontend_attach(struct dvb_usb_device *d) memset(&umt_config,0,sizeof(struct mt352_config)); umt_config.demod_init = umt_mt352_demod_init; umt_config.demod_address = 0xf; - umt_config.pll_set = dvb_usb_pll_set; + d->fe->ops->tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; d->fe = mt352_attach(&umt_config, &d->i2c_adap); diff --git a/linux/drivers/media/dvb/frontends/at76c651.c b/linux/drivers/media/dvb/frontends/at76c651.c index ddcb99cc5..556689a80 100644 --- a/linux/drivers/media/dvb/frontends/at76c651.c +++ b/linux/drivers/media/dvb/frontends/at76c651.c @@ -246,9 +246,10 @@ static int at76c651_set_parameters(struct dvb_frontend* fe, int ret; struct at76c651_state* state = fe->demodulator_priv; - at76c651_writereg(state, 0x0c, 0xc3); - state->config->pll_set(fe, p); - at76c651_writereg(state, 0x0c, 0xc2); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } if ((ret = at76c651_set_symbol_rate(state, p->u.qam.symbol_rate))) return ret; @@ -268,12 +269,6 @@ static int at76c651_set_defaults(struct dvb_frontend* fe) at76c651_set_bbfreq(state); at76c651_set_auto_config(state); - if (state->config->pll_init) { - at76c651_writereg(state, 0x0c, 0xc3); - state->config->pll_init(fe); - at76c651_writereg(state, 0x0c, 0xc2); - } - return 0; } @@ -344,6 +339,17 @@ static int at76c651_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) return 0; } +static int at76c651_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct at76c651_state* state = fe->demodulator_priv; + + if (enable) { + return at76c651_writereg(state, 0x0c, 0xc3); + } else { + return at76c651_writereg(state, 0x0c, 0xc2); + } +} + static int at76c651_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *fesettings) { fesettings->min_delay_ms = 50; @@ -414,6 +420,7 @@ static struct dvb_frontend_ops at76c651_ops = { .release = at76c651_release, .init = at76c651_set_defaults, + .i2c_gate_ctrl = at76c651_i2c_gate_ctrl, .set_frontend = at76c651_set_parameters, .get_tune_settings = at76c651_get_tune_settings, diff --git a/linux/drivers/media/dvb/frontends/at76c651.h b/linux/drivers/media/dvb/frontends/at76c651.h index 34054df93..c31db11a2 100644 --- a/linux/drivers/media/dvb/frontends/at76c651.h +++ b/linux/drivers/media/dvb/frontends/at76c651.h @@ -35,10 +35,6 @@ struct at76c651_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* at76c651_attach(const struct at76c651_config* config, diff --git a/linux/drivers/media/dvb/frontends/bsbe1.h b/linux/drivers/media/dvb/frontends/bsbe1.h index 78573b22a..6b52d5abf 100644 --- a/linux/drivers/media/dvb/frontends/bsbe1.h +++ b/linux/drivers/media/dvb/frontends/bsbe1.h @@ -89,12 +89,13 @@ static int alps_bsbe1_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ra return 0; } -static int alps_bsbe1_pll_set(struct dvb_frontend* fe, struct i2c_adapter *i2c, struct dvb_frontend_parameters* params) +static int alps_bsbe1_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { int ret; u8 data[4]; u32 div; struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + struct i2c_adapter *i2c = fe->tuner_priv; if ((params->frequency < 950000) || (params->frequency > 2150000)) return -EINVAL; @@ -117,7 +118,6 @@ static struct stv0299_config alps_bsbe1_config = { .skip_reinit = 0, .min_delay_ms = 100, .set_symbol_rate = alps_bsbe1_set_symbol_rate, - .pll_set = alps_bsbe1_pll_set, }; #endif diff --git a/linux/drivers/media/dvb/frontends/bsru6.h b/linux/drivers/media/dvb/frontends/bsru6.h index 2a5366ce7..5d13c5e4d 100644 --- a/linux/drivers/media/dvb/frontends/bsru6.h +++ b/linux/drivers/media/dvb/frontends/bsru6.h @@ -101,11 +101,12 @@ static int alps_bsru6_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ra return 0; } -static int alps_bsru6_pll_set(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dvb_frontend_parameters *params) +static int alps_bsru6_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 i2c_adapter *i2c = fe->tuner_priv; if ((params->frequency < 950000) || (params->frequency > 2150000)) return -EINVAL; @@ -134,7 +135,6 @@ static struct stv0299_config alps_bsru6_config = { .volt13_op0_op1 = STV0299_VOLT13_OP1, .min_delay_ms = 100, .set_symbol_rate = alps_bsru6_set_symbol_rate, - .pll_set = alps_bsru6_pll_set, }; #endif diff --git a/linux/drivers/media/dvb/frontends/cx22700.c b/linux/drivers/media/dvb/frontends/cx22700.c index 755f774f6..02fee9047 100644 --- a/linux/drivers/media/dvb/frontends/cx22700.c +++ b/linux/drivers/media/dvb/frontends/cx22700.c @@ -247,12 +247,6 @@ static int cx22700_init (struct dvb_frontend* fe) cx22700_writereg (state, 0x00, 0x01); - if (state->config->pll_init) { - cx22700_writereg (state, 0x0a, 0x00); /* open i2c bus switch */ - state->config->pll_init(fe); - cx22700_writereg (state, 0x0a, 0x01); /* close i2c bus switch */ - } - return 0; } @@ -333,9 +327,11 @@ static int cx22700_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par cx22700_writereg (state, 0x00, 0x02); /* XXX CHECKME: soft reset*/ cx22700_writereg (state, 0x00, 0x00); - cx22700_writereg (state, 0x0a, 0x00); /* open i2c bus switch */ - state->config->pll_set(fe, p); - cx22700_writereg (state, 0x0a, 0x01); /* close i2c bus switch */ + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + cx22700_set_inversion (state, p->inversion); cx22700_set_tps (state, &p->u.ofdm); cx22700_writereg (state, 0x37, 0x01); /* PAL loop filter off */ @@ -353,6 +349,17 @@ static int cx22700_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_par return cx22700_get_tps (state, &p->u.ofdm); } +static int cx22700_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct cx22700_state* state = fe->demodulator_priv; + + if (enable) { + return cx22700_writereg(state, 0x0a, 0x00); + } else { + return cx22700_writereg(state, 0x0a, 0x01); + } +} + static int cx22700_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { fesettings->min_delay_ms = 150; @@ -413,6 +420,7 @@ static struct dvb_frontend_ops cx22700_ops = { .release = cx22700_release, .init = cx22700_init, + .i2c_gate_ctrl = cx22700_i2c_gate_ctrl, .set_frontend = cx22700_set_frontend, .get_frontend = cx22700_get_frontend, diff --git a/linux/drivers/media/dvb/frontends/cx22700.h b/linux/drivers/media/dvb/frontends/cx22700.h index c9145b458..dcd8979c1 100644 --- a/linux/drivers/media/dvb/frontends/cx22700.h +++ b/linux/drivers/media/dvb/frontends/cx22700.h @@ -29,10 +29,6 @@ struct cx22700_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, diff --git a/linux/drivers/media/dvb/frontends/cx22702.c b/linux/drivers/media/dvb/frontends/cx22702.c index 0fc899f81..a129fc9cb 100644 --- a/linux/drivers/media/dvb/frontends/cx22702.c +++ b/linux/drivers/media/dvb/frontends/cx22702.c @@ -211,22 +211,10 @@ static int cx22702_set_tps (struct dvb_frontend* fe, struct dvb_frontend_paramet u8 val; struct cx22702_state* state = fe->demodulator_priv; - /* set PLL */ - cx22702_i2c_gate_ctrl(fe, 1); - if (state->config->pll_set) { - state->config->pll_set(fe, p); - } else if (state->config->pll_desc) { - u8 pllbuf[4]; - struct i2c_msg msg = { .addr = state->config->pll_address, - .buf = pllbuf, .len = 4 }; - dvb_pll_configure(state->config->pll_desc, pllbuf, - p->frequency, - p->u.ofdm.bandwidth); - i2c_transfer(state->i2c, &msg, 1); - } else { - BUG(); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); } - cx22702_i2c_gate_ctrl(fe, 0); /* set inversion */ cx22702_set_inversion (state, p->inversion); @@ -358,10 +346,6 @@ static int cx22702_init (struct dvb_frontend* fe) cx22702_writereg (state, 0xf8, (state->config->output_mode << 1) & 0x02); - /* init PLL */ - if (state->config->pll_init) - state->config->pll_init(fe); - cx22702_i2c_gate_ctrl(fe, 0); return 0; @@ -530,6 +514,7 @@ static struct dvb_frontend_ops cx22702_ops = { .release = cx22702_release, .init = cx22702_init, + .i2c_gate_ctrl = cx22702_i2c_gate_ctrl, .set_frontend = cx22702_set_tps, .get_frontend = cx22702_get_frontend, @@ -540,7 +525,6 @@ static struct dvb_frontend_ops cx22702_ops = { .read_signal_strength = cx22702_read_signal_strength, .read_snr = cx22702_read_snr, .read_ucblocks = cx22702_read_ucblocks, - .i2c_gate_ctrl = cx22702_i2c_gate_ctrl, }; module_param(debug, int, 0644); diff --git a/linux/drivers/media/dvb/frontends/cx22702.h b/linux/drivers/media/dvb/frontends/cx22702.h index 5633976a5..7f2f241e5 100644 --- a/linux/drivers/media/dvb/frontends/cx22702.h +++ b/linux/drivers/media/dvb/frontends/cx22702.h @@ -39,13 +39,6 @@ struct cx22702_config #define CX22702_PARALLEL_OUTPUT 0 #define CX22702_SERIAL_OUTPUT 1 u8 output_mode; - - /* PLL maintenance */ - u8 pll_address; - struct dvb_pll_desc *pll_desc; - - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* cx22702_attach(const struct cx22702_config* config, diff --git a/linux/drivers/media/dvb/frontends/cx24110.c b/linux/drivers/media/dvb/frontends/cx24110.c index a8d0edd02..8d98ffb61 100644 --- a/linux/drivers/media/dvb/frontends/cx24110.c +++ b/linux/drivers/media/dvb/frontends/cx24110.c @@ -366,17 +366,6 @@ static int cx24110_initfe(struct dvb_frontend* fe) cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data); }; - if (state->config->pll_init) state->config->pll_init(fe); - - return 0; -} - -static int cx24110_sleep(struct dvb_frontend *fe) -{ - struct cx24110_state *state = fe->demodulator_priv; - - if (state->config->pll_sleep) - return state->config->pll_sleep(fe); return 0; } @@ -548,7 +537,12 @@ static int cx24110_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par { struct cx24110_state *state = fe->demodulator_priv; - state->config->pll_set(fe, p); + + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + cx24110_set_inversion (state, p->inversion); cx24110_set_fec (state, p->u.qpsk.fec_inner); cx24110_set_symbolrate (state, p->u.qpsk.symbol_rate); @@ -651,7 +645,6 @@ static struct dvb_frontend_ops cx24110_ops = { .release = cx24110_release, .init = cx24110_initfe, - .sleep = cx24110_sleep, .set_frontend = cx24110_set_frontend, .get_frontend = cx24110_get_frontend, .read_status = cx24110_read_status, diff --git a/linux/drivers/media/dvb/frontends/cx24110.h b/linux/drivers/media/dvb/frontends/cx24110.h index 609ac642b..b354a64e0 100644 --- a/linux/drivers/media/dvb/frontends/cx24110.h +++ b/linux/drivers/media/dvb/frontends/cx24110.h @@ -31,11 +31,6 @@ struct cx24110_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); - int (*pll_sleep)(struct dvb_frontend* fe); }; extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, diff --git a/linux/drivers/media/dvb/frontends/cx24123.c b/linux/drivers/media/dvb/frontends/cx24123.c index 56280cd58..89b3a6c63 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.c +++ b/linux/drivers/media/dvb/frontends/cx24123.c @@ -698,9 +698,6 @@ static int cx24123_initfe(struct dvb_frontend* fe) for (i = 0; i < sizeof(cx24123_regdata) / sizeof(cx24123_regdata[0]); i++) cx24123_writereg(state, cx24123_regdata[i].reg, cx24123_regdata[i].data); - if (state->config->pll_init) - state->config->pll_init(fe); - /* Configure the LNB for 14V */ if (state->config->use_isl6421) cx24123_writelnbreg(state, 0x0, 0x2a); diff --git a/linux/drivers/media/dvb/frontends/cx24123.h b/linux/drivers/media/dvb/frontends/cx24123.h index 0c922b5e9..7ec4ee856 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.h +++ b/linux/drivers/media/dvb/frontends/cx24123.h @@ -35,10 +35,6 @@ struct cx24123_config */ int use_isl6421; - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); - /* Need to set device param for start_dma */ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); diff --git a/linux/drivers/media/dvb/frontends/dib3000.h b/linux/drivers/media/dvb/frontends/dib3000.h index 2d5475b5c..ec927628d 100644 --- a/linux/drivers/media/dvb/frontends/dib3000.h +++ b/linux/drivers/media/dvb/frontends/dib3000.h @@ -30,10 +30,6 @@ struct dib3000_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance and the i2c address of the PLL */ - int (*pll_init)(struct dvb_frontend *fe); - int (*pll_set)(struct dvb_frontend *fe, struct dvb_frontend_parameters* params); }; struct dib_fe_xfer_ops diff --git a/linux/drivers/media/dvb/frontends/dib3000mb.c b/linux/drivers/media/dvb/frontends/dib3000mb.c index ae589adb1..f2f8071ad 100644 --- a/linux/drivers/media/dvb/frontends/dib3000mb.c +++ b/linux/drivers/media/dvb/frontends/dib3000mb.c @@ -60,8 +60,9 @@ static int dib3000mb_set_frontend(struct dvb_frontend* fe, fe_code_rate_t fe_cr = FEC_NONE; int search_state, seq; - if (tuner && state->config.pll_set) { - state->config.pll_set(fe, fep); + if (tuner && fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, fep); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); deb_setf("bandwidth: "); switch (ofdm->bandwidth) { @@ -386,9 +387,6 @@ static int dib3000mb_fe_init(struct dvb_frontend* fe, int mobile_mode) wr(DIB3000MB_REG_DATA_IN_DIVERSITY, DIB3000MB_DATA_DIVERSITY_IN_OFF); - if (state->config.pll_init) - state->config.pll_init(fe); - return 0; } diff --git a/linux/drivers/media/dvb/frontends/dib3000mc.c b/linux/drivers/media/dvb/frontends/dib3000mc.c index 3b303dbb6..68a443b3d 100644 --- a/linux/drivers/media/dvb/frontends/dib3000mc.c +++ b/linux/drivers/media/dvb/frontends/dib3000mc.c @@ -462,8 +462,9 @@ static int dib3000mc_set_frontend(struct dvb_frontend* fe, int search_state,auto_val; u16 val; - if (tuner && state->config.pll_set) { /* initial call from dvb */ - state->config.pll_set(fe,fep); + if (tuner && fe->ops->tuner_ops.set_params) { /* initial call from dvb */ + fe->ops->tuner_ops.set_params(fe, fep); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); state->last_tuned_freq = fep->frequency; // if (!scanboost) { @@ -642,9 +643,6 @@ static int dib3000mc_fe_init(struct dvb_frontend* fe, int mobile_mode) set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_DIV_IN_OFF); - if (state->config.pll_init) - state->config.pll_init(fe); - deb_info("init end\n"); return 0; } diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.c b/linux/drivers/media/dvb/frontends/dvb-pll.c index b6e2c387a..7e65841b1 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.c +++ b/linux/drivers/media/dvb/frontends/dvb-pll.c @@ -227,16 +227,16 @@ struct dvb_pll_desc dvb_pll_tua6034 = { EXPORT_SYMBOL(dvb_pll_tua6034); /* Infineon TUA6034 - * used in LG TDVS H061F and LG TDVS H062F + * used in LG TDVS-H061F, LG TDVS-H062F and LG TDVS-H064F */ struct dvb_pll_desc dvb_pll_tdvs_tua6034 = { - .name = "LG/Infineon TUA6034", + .name = "LG TDVS-H06xF", .min = 54000000, .max = 863000000, .count = 3, .entries = { - { 160000000, 44000000, 62500, 0xce, 0x01 }, - { 455000000, 44000000, 62500, 0xce, 0x02 }, + { 165000000, 44000000, 62500, 0xce, 0x01 }, + { 450000000, 44000000, 62500, 0xce, 0x02 }, { 999999999, 44000000, 62500, 0xce, 0x04 }, }, }; @@ -419,6 +419,19 @@ struct dvb_pll_desc dvb_pll_thomson_fe6600 = { }; EXPORT_SYMBOL(dvb_pll_thomson_fe6600); +struct dvb_pll_priv { + /* i2c details */ + int pll_i2c_address; + struct i2c_adapter *i2c; + + /* the PLL descriptor */ + struct dvb_pll_desc *pll_desc; + + /* cached frequency/bandwidth */ + u32 frequency; + u32 bandwidth; +}; + /* ----------------------------------------------------------- */ /* code */ @@ -443,7 +456,8 @@ int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, if (debug) printk("pll: %s: freq=%d bw=%d | i=%d/%d\n", desc->name, freq, bandwidth, i, desc->count); - BUG_ON(i == desc->count); + if (i == desc->count) + return -EINVAL; div = (freq + desc->entries[i].offset) / desc->entries[i].stepsize; buf[0] = div >> 8; @@ -462,6 +476,163 @@ int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, } EXPORT_SYMBOL(dvb_pll_configure); +static int dvb_pll_release(struct dvb_frontend *fe) +{ + if (fe->tuner_priv) + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int dvb_pll_sleep(struct dvb_frontend *fe) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + u8 buf[4]; + struct i2c_msg msg = + { .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) }; + int i; + int result; + + for (i = 0; i < priv->pll_desc->count; i++) { + if (priv->pll_desc->entries[i].limit == 0) + break; + } + if (i == priv->pll_desc->count) + return 0; + + buf[0] = 0; + buf[1] = 0; + buf[2] = priv->pll_desc->entries[i].config; + buf[3] = priv->pll_desc->entries[i].cb; + + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + return result; + } + + return 0; +} + +static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + u8 buf[4]; + struct i2c_msg msg = + { .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) }; + int result; + u32 div; + int i; + u32 bandwidth = 0; + + if (priv->i2c == NULL) + return -EINVAL; + + // DVBT bandwidth only just now + if (fe->ops->info.type == FE_OFDM) { + bandwidth = params->u.ofdm.bandwidth; + } + + if ((result = dvb_pll_configure(priv->pll_desc, buf, params->frequency, bandwidth)) != 0) + return result; + + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + return result; + } + + // calculate the frequency we set it to + for (i = 0; i < priv->pll_desc->count; i++) { + if (params->frequency > priv->pll_desc->entries[i].limit) + continue; + break; + } + div = (params->frequency + priv->pll_desc->entries[i].offset) / priv->pll_desc->entries[i].stepsize; + priv->frequency = (div * priv->pll_desc->entries[i].stepsize) - priv->pll_desc->entries[i].offset; + priv->bandwidth = bandwidth; + + return 0; +} + +static int dvb_pll_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8 *buf, int buf_len) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + int result; + u32 div; + int i; + u32 bandwidth = 0; + + if (buf_len < 5) + return -EINVAL; + + // DVBT bandwidth only just now + if (fe->ops->info.type == FE_OFDM) { + bandwidth = params->u.ofdm.bandwidth; + } + + if ((result = dvb_pll_configure(priv->pll_desc, buf+1, params->frequency, bandwidth)) != 0) + return result; + buf[0] = priv->pll_i2c_address; + + // calculate the frequency we set it to + for (i = 0; i < priv->pll_desc->count; i++) { + if (params->frequency > priv->pll_desc->entries[i].limit) + continue; + break; + } + div = (params->frequency + priv->pll_desc->entries[i].offset) / priv->pll_desc->entries[i].stepsize; + priv->frequency = (div * priv->pll_desc->entries[i].stepsize) - priv->pll_desc->entries[i].offset; + priv->bandwidth = bandwidth; + + return 5; +} + +static int dvb_pll_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int dvb_pll_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +static struct dvb_tuner_ops dvb_pll_tuner_ops = { + .release = dvb_pll_release, + .sleep = dvb_pll_sleep, + .set_params = dvb_pll_set_params, + .calc_regs = dvb_pll_calc_regs, + .get_frequency = dvb_pll_get_frequency, + .get_bandwidth = dvb_pll_get_bandwidth, +}; + +int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc) +{ + struct dvb_pll_priv *priv = NULL; + + priv = kzalloc(sizeof(struct dvb_pll_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + priv->pll_i2c_address = pll_addr; + priv->i2c = i2c; + priv->pll_desc = desc; + + memcpy(&fe->ops->tuner_ops, &dvb_pll_tuner_ops, sizeof(struct dvb_tuner_ops)); + strncpy(fe->ops->tuner_ops.info.name, desc->name, 128); + fe->ops->tuner_ops.info.frequency_min = desc->min; + fe->ops->tuner_ops.info.frequency_min = desc->max; + + fe->tuner_priv = priv; + return 0; +} +EXPORT_SYMBOL(dvb_pll_attach); + MODULE_DESCRIPTION("dvb pll library"); MODULE_AUTHOR("Gerd Knorr"); MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.h b/linux/drivers/media/dvb/frontends/dvb-pll.h index 2b8461784..3908ae1d4 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.h +++ b/linux/drivers/media/dvb/frontends/dvb-pll.h @@ -5,6 +5,9 @@ #ifndef __DVB_PLL_H__ #define __DVB_PLL_H__ +#include <linux/i2c.h> +#include "dvb_frontend.h" + struct dvb_pll_desc { char *name; u32 min; @@ -44,7 +47,18 @@ extern struct dvb_pll_desc dvb_pll_philips_td1316; extern struct dvb_pll_desc dvb_pll_thomson_fe6600; -int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, +extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, u32 freq, int bandwidth); +/** + * Attach a dvb-pll to the supplied frontend structure. + * + * @param fe Frontend to attach to. + * @param pll_addr i2c address of the PLL (if used). + * @param i2c i2c adapter to use (set to NULL if not used). + * @param desc dvb_pll_desc to use. + * @return 0 on success, nonzero on failure. + */ +extern int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc); + #endif diff --git a/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c b/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c index 645946a99..78ea4ff03 100644 --- a/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c +++ b/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c @@ -77,6 +77,11 @@ static int dvb_dummy_fe_get_frontend(struct dvb_frontend* fe, struct dvb_fronten static int dvb_dummy_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { + if (fe->ops->tuner_ops->set_params) { + fe->ops->tuner_ops->set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + return 0; } diff --git a/linux/drivers/media/dvb/frontends/fe_lgh06xf.h b/linux/drivers/media/dvb/frontends/fe_lgh06xf.h new file mode 100644 index 000000000..c5e8b5ff1 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/fe_lgh06xf.h @@ -0,0 +1,66 @@ +/* + * fe_lgh06xf.h - ATSC Tuner support for LG TDVS H06xF + * + * 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. + */ + +#ifndef FE_LGH06XF_H +#define FE_LGH06XF_H +#include "dvb-pll.h" + +static int lg_h06xf_pll_set(struct dvb_frontend* fe, struct i2c_adapter* i2c_adap, + struct dvb_frontend_parameters* params) +{ + u8 buf[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + int err; + + dvb_pll_configure(&dvb_pll_tdvs_tua6034, buf, params->frequency, 0); + if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { + printk(KERN_WARNING "lg_h06xf: %s error " + "(addr %02x <- %02x, err = %i)\n", + __FUNCTION__, buf[0], buf[1], err); + if (err < 0) + return err; + else + return -EREMOTEIO; + } + + /* Set the Auxiliary Byte. */ +#if 0 + buf[2] &= ~0x20; + buf[2] |= 0x18; + buf[3] = 0x50; +#else + buf[0] = buf[2]; + buf[0] &= ~0x20; + buf[0] |= 0x18; + buf[1] = 0x50; + msg.len = 2; +#endif + if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { + printk(KERN_WARNING "lg_h06xf: %s error " + "(addr %02x <- %02x, err = %i)\n", + __FUNCTION__, buf[0], buf[1], err); + if (err < 0) + return err; + else + return -EREMOTEIO; + } + + return 0; +} +#endif diff --git a/linux/drivers/media/dvb/frontends/l64781.c b/linux/drivers/media/dvb/frontends/l64781.c index 1c7c91224..fa4a87e00 100644 --- a/linux/drivers/media/dvb/frontends/l64781.c +++ b/linux/drivers/media/dvb/frontends/l64781.c @@ -141,7 +141,10 @@ static int apply_frontend_param (struct dvb_frontend* fe, struct dvb_frontend_pa u8 val0x06; int bw = p->bandwidth - BANDWIDTH_8_MHZ; - state->config->pll_set(fe, param); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, param); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } if (param->inversion != INVERSION_ON && param->inversion != INVERSION_OFF) @@ -463,8 +466,6 @@ static int l64781_init(struct dvb_frontend* fe) /* Everything is two's complement, soft bit and CSI_OUT too */ l64781_writereg (state, 0x1e, 0x09); - if (state->config->pll_init) state->config->pll_init(fe); - /* delay a bit after first init attempt */ if (state->first) { state->first = 0; diff --git a/linux/drivers/media/dvb/frontends/l64781.h b/linux/drivers/media/dvb/frontends/l64781.h index 947f65f87..83b8bc210 100644 --- a/linux/drivers/media/dvb/frontends/l64781.h +++ b/linux/drivers/media/dvb/frontends/l64781.h @@ -29,10 +29,6 @@ struct l64781_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; diff --git a/linux/drivers/media/dvb/frontends/lgdt330x.c b/linux/drivers/media/dvb/frontends/lgdt330x.c index 25ea3a838..cf1b0fefe 100644 --- a/linux/drivers/media/dvb/frontends/lgdt330x.c +++ b/linux/drivers/media/dvb/frontends/lgdt330x.c @@ -400,8 +400,10 @@ static int lgdt330x_set_parameters(struct dvb_frontend* fe, } /* Tune to the specified frequency */ - if (state->config->pll_set) - state->config->pll_set(fe, param); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, param); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } /* Keep track of the new frequency */ /* FIXME this is the wrong way to do this... */ diff --git a/linux/drivers/media/dvb/frontends/lgdt330x.h b/linux/drivers/media/dvb/frontends/lgdt330x.h index 2a6529ccc..bad903c6f 100644 --- a/linux/drivers/media/dvb/frontends/lgdt330x.h +++ b/linux/drivers/media/dvb/frontends/lgdt330x.h @@ -43,7 +43,6 @@ struct lgdt330x_config /* PLL interface */ int (*pll_rf_set) (struct dvb_frontend* fe, int index); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); /* Need to set device param for start_dma */ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); diff --git a/linux/drivers/media/dvb/frontends/mt312.c b/linux/drivers/media/dvb/frontends/mt312.c index d3aea83cf..46e12a8ac 100644 --- a/linux/drivers/media/dvb/frontends/mt312.c +++ b/linux/drivers/media/dvb/frontends/mt312.c @@ -277,12 +277,6 @@ static int mt312_initfe(struct dvb_frontend* fe) if ((ret = mt312_writereg(state, CS_SW_LIM, 0x69)) < 0) return ret; - if (state->config->pll_init) { - mt312_writereg(state, GPP_CTRL, 0x40); - state->config->pll_init(fe); - mt312_writereg(state, GPP_CTRL, 0x00); - } - return 0; } @@ -529,9 +523,10 @@ static int mt312_set_frontend(struct dvb_frontend* fe, return -EINVAL; } - mt312_writereg(state, GPP_CTRL, 0x40); - state->config->pll_set(fe, p); - mt312_writereg(state, GPP_CTRL, 0x00); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } /* sr = (u16)(sr * 256.0 / 1000000.0) */ sr = mt312_div(p->u.qpsk.symbol_rate * 4, 15625); @@ -578,6 +573,17 @@ static int mt312_get_frontend(struct dvb_frontend* fe, return 0; } +static int mt312_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct mt312_state* state = fe->demodulator_priv; + + if (enable) { + return mt312_writereg(state, GPP_CTRL, 0x40); + } else { + return mt312_writereg(state, GPP_CTRL, 0x00); + } +} + static int mt312_sleep(struct dvb_frontend* fe) { struct mt312_state *state = fe->demodulator_priv; @@ -633,6 +639,7 @@ static struct dvb_frontend_ops vp310_mt312_ops = { .init = mt312_initfe, .sleep = mt312_sleep, + .i2c_gate_ctrl = mt312_i2c_gate_ctrl, .set_frontend = mt312_set_frontend, .get_frontend = mt312_get_frontend, diff --git a/linux/drivers/media/dvb/frontends/mt312.h b/linux/drivers/media/dvb/frontends/mt312.h index 074d844f0..666a1bd1c 100644 --- a/linux/drivers/media/dvb/frontends/mt312.h +++ b/linux/drivers/media/dvb/frontends/mt312.h @@ -32,10 +32,6 @@ struct mt312_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config, diff --git a/linux/drivers/media/dvb/frontends/mt352.c b/linux/drivers/media/dvb/frontends/mt352.c index aaaec909d..ed3bede02 100644 --- a/linux/drivers/media/dvb/frontends/mt352.c +++ b/linux/drivers/media/dvb/frontends/mt352.c @@ -286,16 +286,33 @@ static int mt352_set_parameters(struct dvb_frontend* fe, mt352_calc_nominal_rate(state, op->bandwidth, buf+4); mt352_calc_input_freq(state, buf+6); - state->config.pll_set(fe, param, buf+8); - mt352_write(fe, buf, sizeof(buf)); + // if there is no secondary tuner, call set_params to set up a potential + // tuner attached elsewhere if (state->config.no_tuner) { - /* start decoding */ + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, param); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + + /* start decoding only */ mt352_write(fe, fsm_go, 2); - } else { - /* start tuning */ + } + + // retrieve the pllbuf - we do this even if there is no + // secondary tuner simply so we have a record of what was sent for + // debugging. + if (fe->ops->tuner_ops.calc_regs) { + fe->ops->tuner_ops.calc_regs(fe, param, buf+8, 5); + buf[8] <<= 1; + mt352_write(fe, buf, sizeof(buf)); + } + + // send PLL and start tuning and then decoding + if (!state->config.no_tuner) { mt352_write(fe, tuner_go, 2); } + return 0; } diff --git a/linux/drivers/media/dvb/frontends/mt352.h b/linux/drivers/media/dvb/frontends/mt352.h index 03040cd59..9e7ff4b8f 100644 --- a/linux/drivers/media/dvb/frontends/mt352.h +++ b/linux/drivers/media/dvb/frontends/mt352.h @@ -49,12 +49,6 @@ struct mt352_config /* Initialise the demodulator and PLL. Cannot be NULL */ int (*demod_init)(struct dvb_frontend* fe); - - /* PLL setup - fill out the supplied 5 byte buffer with your PLL settings. - * byte0: Set to pll i2c address (nonlinux; left shifted by 1) - * byte1-4: PLL configuration. - */ - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf); }; extern struct dvb_frontend* mt352_attach(const struct mt352_config* config, diff --git a/linux/drivers/media/dvb/frontends/nxt200x.c b/linux/drivers/media/dvb/frontends/nxt200x.c index 9e3535394..9b13f14f1 100644 --- a/linux/drivers/media/dvb/frontends/nxt200x.c +++ b/linux/drivers/media/dvb/frontends/nxt200x.c @@ -333,17 +333,17 @@ static int nxt200x_writetuner (struct nxt200x_state* state, u8* data) dprintk("%s\n", __FUNCTION__); - dprintk("Tuner Bytes: %02X %02X %02X %02X\n", data[0], data[1], data[2], data[3]); + dprintk("Tuner Bytes: %02X %02X %02X %02X\n", data[1], data[2], data[3], data[4]); /* if NXT2004, write directly to tuner. if NXT2002, write through NXT chip. * direct write is required for Philips TUV1236D and ALPS TDHU2 */ switch (state->demod_chip) { case NXT2004: - if (i2c_writebytes(state, state->config->pll_address, data, 4)) + if (i2c_writebytes(state, data[0], data+1, 4)) printk(KERN_WARNING "nxt200x: error writing to tuner\n"); /* wait until we have a lock */ while (count < 20) { - i2c_readbytes(state, state->config->pll_address, &buf, 1); + i2c_readbytes(state, data[0], &buf, 1); if (buf & 0x40) return 0; msleep(100); @@ -361,10 +361,10 @@ static int nxt200x_writetuner (struct nxt200x_state* state, u8* data) nxt200x_writebytes(state, 0x34, &buf, 1); /* write actual tuner bytes */ - nxt200x_writebytes(state, 0x36, data, 4); + nxt200x_writebytes(state, 0x36, data+1, 4); /* set tuner i2c address */ - buf = state->config->pll_address; + buf = data[0] << 1; nxt200x_writebytes(state, 0x35, &buf, 1); /* write UC Opmode to begin transfer */ @@ -534,7 +534,7 @@ static int nxt200x_setup_frontend_parameters (struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { struct nxt200x_state* state = fe->demodulator_priv; - u8 buf[4]; + u8 buf[5]; /* stop the micro first */ nxt200x_microcontroller_stop(state); @@ -548,7 +548,9 @@ static int nxt200x_setup_frontend_parameters (struct dvb_frontend* fe, } /* get tuning information */ - dvb_pll_configure(state->config->pll_desc, buf, p->frequency, 0); + if (fe->ops->tuner_ops.calc_regs) { + fe->ops->tuner_ops.calc_regs(fe, p, buf, 5); + } /* set additional params */ switch (p->u.vsb.modulation) { diff --git a/linux/drivers/media/dvb/frontends/nxt200x.h b/linux/drivers/media/dvb/frontends/nxt200x.h index 1d9d70bc3..34d617358 100644 --- a/linux/drivers/media/dvb/frontends/nxt200x.h +++ b/linux/drivers/media/dvb/frontends/nxt200x.h @@ -38,10 +38,6 @@ struct nxt200x_config /* the demodulator's i2c address */ u8 demod_address; - /* tuner information */ - u8 pll_address; - struct dvb_pll_desc *pll_desc; - /* used to set pll input */ int (*set_pll_input)(u8* buf, int input); diff --git a/linux/drivers/media/dvb/frontends/nxt6000.c b/linux/drivers/media/dvb/frontends/nxt6000.c index a16eeba00..bca83266a 100644 --- a/linux/drivers/media/dvb/frontends/nxt6000.c +++ b/linux/drivers/media/dvb/frontends/nxt6000.c @@ -207,12 +207,6 @@ static void nxt6000_setup(struct dvb_frontend* fe) nxt6000_writereg(state, SUB_DIAG_MODE_SEL, 0); nxt6000_writereg(state, TS_FORMAT, 0); - - if (state->config->pll_init) { - nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); /* open i2c bus switch */ - state->config->pll_init(fe); - nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); /* close i2c bus switch */ - } } static void nxt6000_dump_status(struct nxt6000_state *state) @@ -469,9 +463,10 @@ static int nxt6000_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par struct nxt6000_state* state = fe->demodulator_priv; int result; - nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); /* open i2c bus switch */ - state->config->pll_set(fe, param); - nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); /* close i2c bus switch */ + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, param); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } if ((result = nxt6000_set_bandwidth(state, param->u.ofdm.bandwidth)) < 0) return result; @@ -532,6 +527,17 @@ static int nxt6000_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_fron return 0; } +static int nxt6000_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct nxt6000_state* state = fe->demodulator_priv; + + if (enable) { + return nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); + } else { + return nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); + } +} + static struct dvb_frontend_ops nxt6000_ops; struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, @@ -584,6 +590,7 @@ static struct dvb_frontend_ops nxt6000_ops = { .release = nxt6000_release, .init = nxt6000_init, + .i2c_gate_ctrl = nxt6000_i2c_gate_ctrl, .get_tune_settings = nxt6000_fe_get_tune_settings, diff --git a/linux/drivers/media/dvb/frontends/nxt6000.h b/linux/drivers/media/dvb/frontends/nxt6000.h index b7d9bead3..117031d11 100644 --- a/linux/drivers/media/dvb/frontends/nxt6000.h +++ b/linux/drivers/media/dvb/frontends/nxt6000.h @@ -31,10 +31,6 @@ struct nxt6000_config /* should clock inversion be used? */ u8 clock_inversion:1; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, diff --git a/linux/drivers/media/dvb/frontends/or51132.c b/linux/drivers/media/dvb/frontends/or51132.c index c25ec7344..19f75e6ed 100644 --- a/linux/drivers/media/dvb/frontends/or51132.c +++ b/linux/drivers/media/dvb/frontends/or51132.c @@ -331,7 +331,6 @@ static int or51132_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *param) { int ret; - u8 buf[4]; struct or51132_state* state = fe->demodulator_priv; const struct firmware *fw; const char *fwname; @@ -384,13 +383,10 @@ static int or51132_set_parameters(struct dvb_frontend* fe, or51132_setmode(fe); } - dvb_pll_configure(state->config->pll_desc, buf, - param->frequency, 0); - dprintk("set_parameters tuner bytes: 0x%02x 0x%02x " - "0x%02x 0x%02x\n",buf[0],buf[1],buf[2],buf[3]); - if (i2c_writebytes(state, state->config->pll_address, buf, 4)) - printk(KERN_WARNING "or51132: set_parameters error " - "writing to tuner\n"); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, param); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } /* Set to current mode */ or51132_setmode(fe); diff --git a/linux/drivers/media/dvb/frontends/or51132.h b/linux/drivers/media/dvb/frontends/or51132.h index 622cdd183..89658883a 100644 --- a/linux/drivers/media/dvb/frontends/or51132.h +++ b/linux/drivers/media/dvb/frontends/or51132.h @@ -29,8 +29,6 @@ struct or51132_config { /* The demodulator's i2c address */ u8 demod_address; - u8 pll_address; - struct dvb_pll_desc *pll_desc; /* Need to set device param for start_dma */ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); diff --git a/linux/drivers/media/dvb/frontends/s5h1420.c b/linux/drivers/media/dvb/frontends/s5h1420.c index d69477596..5dee51154 100644 --- a/linux/drivers/media/dvb/frontends/s5h1420.c +++ b/linux/drivers/media/dvb/frontends/s5h1420.c @@ -584,7 +584,6 @@ static int s5h1420_set_frontend(struct dvb_frontend* fe, struct s5h1420_state* state = fe->demodulator_priv; int frequency_delta; struct dvb_frontend_tune_settings fesettings; - u32 tmp; /* check if we should do a fast-tune */ memcpy(&fesettings.parameters, p, sizeof(struct dvb_frontend_parameters)); @@ -596,10 +595,17 @@ static int s5h1420_set_frontend(struct dvb_frontend* fe, (state->fec_inner == p->u.qpsk.fec_inner) && (state->symbol_rate == p->u.qpsk.symbol_rate)) { - if (state->config->pll_set) { - s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) | 1); - state->config->pll_set(fe, p, &tmp); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + if (fe->ops->tuner_ops.get_frequency) { + u32 tmp; + fe->ops->tuner_ops.get_frequency(fe, &tmp); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); s5h1420_setfreqoffset(state, p->frequency - tmp); + } else { + s5h1420_setfreqoffset(state, 0); } return 0; } @@ -646,9 +652,9 @@ static int s5h1420_set_frontend(struct dvb_frontend* fe, s5h1420_writereg(state, 0x05, s5h1420_readreg(state, 0x05) | 1); /* set tuner PLL */ - if (state->config->pll_set) { - s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) | 1); - state->config->pll_set(fe, p, &tmp); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); s5h1420_setfreqoffset(state, 0); } @@ -708,6 +714,17 @@ static int s5h1420_get_tune_settings(struct dvb_frontend* fe, return 0; } +static int s5h1420_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct s5h1420_state* state = fe->demodulator_priv; + + if (enable) { + return s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) | 1); + } else { + return s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) & 0xfe); + } +} + static int s5h1420_init (struct dvb_frontend* fe) { struct s5h1420_state* state = fe->demodulator_priv; @@ -717,13 +734,6 @@ static int s5h1420_init (struct dvb_frontend* fe) msleep(10); s5h1420_reset(state); - /* init PLL */ - if (state->config->pll_init) { - s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) | 1); - state->config->pll_init(fe); - s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) & 0xfe); - } - return 0; } @@ -800,6 +810,7 @@ static struct dvb_frontend_ops s5h1420_ops = { .init = s5h1420_init, .sleep = s5h1420_sleep, + .i2c_gate_ctrl = s5h1420_i2c_gate_ctrl, .set_frontend = s5h1420_set_frontend, .get_frontend = s5h1420_get_frontend, diff --git a/linux/drivers/media/dvb/frontends/s5h1420.h b/linux/drivers/media/dvb/frontends/s5h1420.h index 73296f13c..4e39015fa 100644 --- a/linux/drivers/media/dvb/frontends/s5h1420.h +++ b/linux/drivers/media/dvb/frontends/s5h1420.h @@ -32,10 +32,6 @@ struct s5h1420_config /* does the inversion require inversion? */ u8 invert:1; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u32* freqout); }; extern struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config, diff --git a/linux/drivers/media/dvb/frontends/sp8870.c b/linux/drivers/media/dvb/frontends/sp8870.c index 73829e647..4d553c0da 100644 --- a/linux/drivers/media/dvb/frontends/sp8870.c +++ b/linux/drivers/media/dvb/frontends/sp8870.c @@ -262,9 +262,10 @@ static int sp8870_set_frontend_parameters (struct dvb_frontend* fe, sp8870_microcontroller_stop(state); // set tuner parameters - sp8870_writereg(state, 0x206, 0x001); - state->config->pll_set(fe, p); - sp8870_writereg(state, 0x206, 0x000); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } // sample rate correction bit [23..17] sp8870_writereg(state, 0x0319, 0x000A); @@ -349,13 +350,6 @@ static int sp8870_init (struct dvb_frontend* fe) sp8870_writereg(state, 0x0D00, 0x010); sp8870_writereg(state, 0x0D01, 0x000); - /* setup PLL */ - if (state->config->pll_init) { - sp8870_writereg(state, 0x206, 0x001); - state->config->pll_init(fe); - sp8870_writereg(state, 0x206, 0x000); - } - return 0; } @@ -541,6 +535,17 @@ static int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend return 0; } +static int sp8870_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct sp8870_state* state = fe->demodulator_priv; + + if (enable) { + return sp8870_writereg(state, 0x206, 0x001); + } else { + return sp8870_writereg(state, 0x206, 0x000); + } +} + static void sp8870_release(struct dvb_frontend* fe) { struct sp8870_state* state = fe->demodulator_priv; @@ -597,6 +602,7 @@ static struct dvb_frontend_ops sp8870_ops = { .init = sp8870_init, .sleep = sp8870_sleep, + .i2c_gate_ctrl = sp8870_i2c_gate_ctrl, .set_frontend = sp8870_set_frontend, .get_tune_settings = sp8870_get_tune_settings, diff --git a/linux/drivers/media/dvb/frontends/sp8870.h b/linux/drivers/media/dvb/frontends/sp8870.h index f3b555dbc..93afbb969 100644 --- a/linux/drivers/media/dvb/frontends/sp8870.h +++ b/linux/drivers/media/dvb/frontends/sp8870.h @@ -31,10 +31,6 @@ struct sp8870_config /* the demodulator's i2c address */ u8 demod_address; - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); - /* request firmware for device */ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; diff --git a/linux/drivers/media/dvb/frontends/sp887x.c b/linux/drivers/media/dvb/frontends/sp887x.c index eb8a60219..543dfa145 100644 --- a/linux/drivers/media/dvb/frontends/sp887x.c +++ b/linux/drivers/media/dvb/frontends/sp887x.c @@ -208,15 +208,6 @@ static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware /* bit 0x010: enable data valid signal */ sp887x_writereg(state, 0xd00, 0x010); sp887x_writereg(state, 0x0d1, 0x000); - - /* setup the PLL */ - if (state->config->pll_init) { - sp887x_writereg(state, 0x206, 0x001); - state->config->pll_init(fe); - sp887x_writereg(state, 0x206, 0x000); - } - - printk ("done.\n"); return 0; }; @@ -362,9 +353,16 @@ static int sp887x_setup_frontend_parameters (struct dvb_frontend* fe, sp887x_microcontroller_stop(state); /* setup the PLL */ - sp887x_writereg(state, 0x206, 0x001); - actual_freq = state->config->pll_set(fe, p); - sp887x_writereg(state, 0x206, 0x000); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + if (fe->ops->tuner_ops.get_frequency) { + fe->ops->tuner_ops.get_frequency(fe, &actual_freq); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } else { + actual_freq = p->frequency; + } /* read status reg in order to clear <pending irqs */ sp887x_readreg(state, 0x200); @@ -486,6 +484,17 @@ static int sp887x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) return 0; } +static int sp887x_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct sp887x_state* state = fe->demodulator_priv; + + if (enable) { + return sp887x_writereg(state, 0x206, 0x001); + } else { + return sp887x_writereg(state, 0x206, 0x000); + } +} + static int sp887x_sleep(struct dvb_frontend* fe) { struct sp887x_state* state = fe->demodulator_priv; @@ -589,6 +598,7 @@ static struct dvb_frontend_ops sp887x_ops = { .init = sp887x_init, .sleep = sp887x_sleep, + .i2c_gate_ctrl = sp887x_i2c_gate_ctrl, .set_frontend = sp887x_setup_frontend_parameters, .get_tune_settings = sp887x_get_tune_settings, diff --git a/linux/drivers/media/dvb/frontends/sp887x.h b/linux/drivers/media/dvb/frontends/sp887x.h index 6a05d8f8e..c44b0ebdf 100644 --- a/linux/drivers/media/dvb/frontends/sp887x.h +++ b/linux/drivers/media/dvb/frontends/sp887x.h @@ -13,12 +13,6 @@ struct sp887x_config /* the demodulator's i2c address */ u8 demod_address; - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - - /* this should return the actual frequency tuned to */ - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); - /* request firmware for device */ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; diff --git a/linux/drivers/media/dvb/frontends/stv0297.c b/linux/drivers/media/dvb/frontends/stv0297.c index 54f4414a7..7e8855723 100644 --- a/linux/drivers/media/dvb/frontends/stv0297.c +++ b/linux/drivers/media/dvb/frontends/stv0297.c @@ -276,12 +276,14 @@ static int stv0297_set_inversion(struct stv0297_state *state, fe_spectral_invers return 0; } -int stv0297_enable_plli2c(struct dvb_frontend *fe) +static int stv0297_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) { struct stv0297_state *state = fe->demodulator_priv; - stv0297_writereg(state, 0x87, 0x78); - stv0297_writereg(state, 0x86, 0xc8); + if (enable) { + stv0297_writereg(state, 0x87, 0x78); + stv0297_writereg(state, 0x86, 0xc8); + } return 0; } @@ -296,9 +298,6 @@ static int stv0297_init(struct dvb_frontend *fe) stv0297_writereg(state, state->config->inittab[i], state->config->inittab[i+1]); msleep(200); - if (state->config->pll_init) - state->config->pll_init(fe); - return 0; } @@ -421,7 +420,10 @@ static int stv0297_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par } stv0297_init(fe); - state->config->pll_set(fe, p); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } /* clear software interrupts */ stv0297_writereg(state, 0x82, 0x0); @@ -668,6 +670,7 @@ static struct dvb_frontend_ops stv0297_ops = { .init = stv0297_init, .sleep = stv0297_sleep, + .i2c_gate_ctrl = stv0297_i2c_gate_ctrl, .set_frontend = stv0297_set_frontend, .get_frontend = stv0297_get_frontend, @@ -684,4 +687,3 @@ MODULE_AUTHOR("Dennis Noermann and Andrew de Quincey"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(stv0297_attach); -EXPORT_SYMBOL(stv0297_enable_plli2c); diff --git a/linux/drivers/media/dvb/frontends/stv0297.h b/linux/drivers/media/dvb/frontends/stv0297.h index 9e53f019d..8ff793c90 100644 --- a/linux/drivers/media/dvb/frontends/stv0297.h +++ b/linux/drivers/media/dvb/frontends/stv0297.h @@ -37,14 +37,9 @@ struct stv0297_config /* does the "inversion" need inverted? */ u8 invert:1; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, struct i2c_adapter* i2c); -extern int stv0297_enable_plli2c(struct dvb_frontend* fe); #endif // STV0297_H diff --git a/linux/drivers/media/dvb/frontends/stv0299.c b/linux/drivers/media/dvb/frontends/stv0299.c index 5bcd00f79..5a8131023 100644 --- a/linux/drivers/media/dvb/frontends/stv0299.c +++ b/linux/drivers/media/dvb/frontends/stv0299.c @@ -131,13 +131,6 @@ static int stv0299_readregs (struct stv0299_state* state, u8 reg1, u8 *b, u8 len return ret == 2 ? 0 : ret; } -int stv0299_enable_plli2c (struct dvb_frontend* fe) -{ - struct stv0299_state* state = fe->demodulator_priv; - - return stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ -} - static int stv0299_set_FEC (struct stv0299_state* state, fe_code_rate_t fec) { dprintk ("%s\n", __FUNCTION__); @@ -457,12 +450,6 @@ static int stv0299_init (struct dvb_frontend* fe) for (i=0; !(state->config->inittab[i] == 0xff && state->config->inittab[i+1] == 0xff); i+=2) stv0299_writeregI(state, state->config->inittab[i], state->config->inittab[i+1]); - if (state->config->pll_init) { - stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ - state->config->pll_init(fe, state->i2c); - stv0299_writeregI(state, 0x05, 0x35); /* disable i2c repeater on stv0299 */ - } - return 0; } @@ -560,9 +547,10 @@ static int stv0299_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par if (state->config->invert) invval = (~invval) & 1; stv0299_writeregI(state, 0x0c, (stv0299_readreg(state, 0x0c) & 0xfe) | invval); - stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ - state->config->pll_set(fe, state->i2c, p); - stv0299_writeregI(state, 0x05, 0x35); /* disable i2c repeater on stv0299 */ + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } stv0299_set_FEC (state, p->u.qpsk.fec_inner); stv0299_set_symbolrate (fe, p->u.qpsk.symbol_rate); @@ -611,6 +599,17 @@ static int stv0299_sleep(struct dvb_frontend* fe) return 0; } +static int stv0299_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct stv0299_state* state = fe->demodulator_priv; + + if (enable) { + return stv0299_writeregI(state, 0x05, 0xb5); + } else { + return stv0299_writeregI(state, 0x05, 0x35); + } +} + static int stv0299_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { struct stv0299_state* state = fe->demodulator_priv; @@ -695,6 +694,7 @@ static struct dvb_frontend_ops stv0299_ops = { .init = stv0299_init, .sleep = stv0299_sleep, + .i2c_gate_ctrl = stv0299_i2c_gate_ctrl, .set_frontend = stv0299_set_frontend, .get_frontend = stv0299_get_frontend, @@ -721,9 +721,8 @@ MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); MODULE_DESCRIPTION("ST STV0299 DVB Demodulator driver"); MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, " - "Andreas Oberritter, Andrew de Quincey, Kenneth Aafløy"); + "Andreas Oberritter, Andrew de Quincey, Kenneth Aafly"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(stv0299_enable_plli2c); EXPORT_SYMBOL(stv0299_writereg); EXPORT_SYMBOL(stv0299_attach); diff --git a/linux/drivers/media/dvb/frontends/stv0299.h b/linux/drivers/media/dvb/frontends/stv0299.h index 32c87b4c2..1504828e4 100644 --- a/linux/drivers/media/dvb/frontends/stv0299.h +++ b/linux/drivers/media/dvb/frontends/stv0299.h @@ -87,14 +87,9 @@ struct stv0299_config /* Set the symbol rate */ int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio); - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend *fe, struct i2c_adapter *i2c); - int (*pll_set)(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dvb_frontend_parameters *params); }; extern int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data); -extern int stv0299_enable_plli2c (struct dvb_frontend* fe); extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, struct i2c_adapter* i2c); diff --git a/linux/drivers/media/dvb/frontends/tda10021.c b/linux/drivers/media/dvb/frontends/tda10021.c index 876519586..e49cfa027 100644 --- a/linux/drivers/media/dvb/frontends/tda10021.c +++ b/linux/drivers/media/dvb/frontends/tda10021.c @@ -225,13 +225,6 @@ static int tda10021_init (struct dvb_frontend *fe) //Activate PLL tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef); - - if (state->config->pll_init) { - lock_tuner(state); - state->config->pll_init(fe); - unlock_tuner(state); - } - return 0; } @@ -259,9 +252,10 @@ static int tda10021_set_parameters (struct dvb_frontend *fe, //printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->u.qam.symbol_rate); - lock_tuner(state); - state->config->pll_set(fe, p); - unlock_tuner(state); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } tda10021_set_symbolrate (state, p->u.qam.symbol_rate); tda10021_writereg (state, 0x34, state->pwm); @@ -376,6 +370,18 @@ static int tda10021_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_pa return 0; } +static int tda10021_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct tda10021_state* state = fe->demodulator_priv; + + if (enable) { + lock_tuner(state); + } else { + unlock_tuner(state); + } + return 0; +} + static int tda10021_sleep(struct dvb_frontend* fe) { struct tda10021_state* state = fe->demodulator_priv; @@ -448,6 +454,7 @@ static struct dvb_frontend_ops tda10021_ops = { .init = tda10021_init, .sleep = tda10021_sleep, + .i2c_gate_ctrl = tda10021_i2c_gate_ctrl, .set_frontend = tda10021_set_parameters, .get_frontend = tda10021_get_frontend, diff --git a/linux/drivers/media/dvb/frontends/tda10021.h b/linux/drivers/media/dvb/frontends/tda10021.h index 53be939e8..3fc338e0e 100644 --- a/linux/drivers/media/dvb/frontends/tda10021.h +++ b/linux/drivers/media/dvb/frontends/tda10021.h @@ -30,10 +30,6 @@ struct tda10021_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, diff --git a/linux/drivers/media/dvb/frontends/tda1004x.c b/linux/drivers/media/dvb/frontends/tda1004x.c index b83dafa4e..5288b44cf 100644 --- a/linux/drivers/media/dvb/frontends/tda1004x.c +++ b/linux/drivers/media/dvb/frontends/tda1004x.c @@ -600,13 +600,6 @@ static int tda10045_init(struct dvb_frontend* fe) tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC - // Init the PLL - if (state->config->pll_init) { - tda1004x_enable_tuner_i2c(state); - state->config->pll_init(fe); - tda1004x_disable_tuner_i2c(state); - } - // tda setup tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream @@ -635,16 +628,6 @@ static int tda10046_init(struct dvb_frontend* fe) return -EIO; } - // Init the tuner PLL - if (state->config->pll_init) { - tda1004x_enable_tuner_i2c(state); - if (state->config->pll_init(fe)) { - printk(KERN_ERR "tda1004x: pll init failed\n"); - return -EIO; - } - tda1004x_disable_tuner_i2c(state); - } - // tda setup tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer tda1004x_write_byteI(state, TDA1004X_AUTO, 0x87); // 100 ppm crystal, select HP stream @@ -712,12 +695,10 @@ static int tda1004x_set_fe(struct dvb_frontend* fe, } // set frequency - tda1004x_enable_tuner_i2c(state); - if (state->config->pll_set(fe, fe_params)) { - printk(KERN_ERR "tda1004x: pll set failed\n"); - return -EIO; + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, fe_params); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); } - tda1004x_disable_tuner_i2c(state); // Hardcoded to use auto as much as possible on the TDA10045 as it // is very unreliable if AUTO mode is _not_ used. @@ -1183,16 +1164,6 @@ static int tda1004x_sleep(struct dvb_frontend* fe) break; case TDA1004X_DEMOD_TDA10046: - if (state->config->pll_sleep != NULL) { - tda1004x_enable_tuner_i2c(state); - state->config->pll_sleep(fe); - if (state->config->if_freq != TDA10046_FREQ_052) { - /* special hack for Philips EUROPA Based boards: - * keep the I2c bridge open for tuner access in analog mode - */ - tda1004x_disable_tuner_i2c(state); - } - } /* set outputs to tristate */ tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0xff); tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1); @@ -1202,6 +1173,17 @@ static int tda1004x_sleep(struct dvb_frontend* fe) return 0; } +static int tda1004x_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct tda1004x_state* state = fe->demodulator_priv; + + if (enable) { + return tda1004x_enable_tuner_i2c(state); + } else { + return tda1004x_disable_tuner_i2c(state); + } +} + static int tda1004x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { fesettings->min_delay_ms = 800; @@ -1235,6 +1217,7 @@ static struct dvb_frontend_ops tda10045_ops = { .init = tda10045_init, .sleep = tda1004x_sleep, + .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl, .set_frontend = tda1004x_set_fe, .get_frontend = tda1004x_get_fe, @@ -1293,6 +1276,7 @@ static struct dvb_frontend_ops tda10046_ops = { .init = tda10046_init, .sleep = tda1004x_sleep, + .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl, .set_frontend = tda1004x_set_fe, .get_frontend = tda1004x_get_fe, diff --git a/linux/drivers/media/dvb/frontends/tda1004x.h b/linux/drivers/media/dvb/frontends/tda1004x.h index cc0c4af64..b877b23ed 100644 --- a/linux/drivers/media/dvb/frontends/tda1004x.h +++ b/linux/drivers/media/dvb/frontends/tda1004x.h @@ -66,11 +66,6 @@ struct tda1004x_config /* AGC configuration */ enum tda10046_agc agc_config; - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - void (*pll_sleep)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); - /* request firmware for device */ /* set this to NULL if the card has a firmware EEPROM */ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); diff --git a/linux/drivers/media/dvb/frontends/tda8083.c b/linux/drivers/media/dvb/frontends/tda8083.c index 91baa9ced..0aeaec890 100644 --- a/linux/drivers/media/dvb/frontends/tda8083.c +++ b/linux/drivers/media/dvb/frontends/tda8083.c @@ -293,7 +293,11 @@ static int tda8083_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par { struct tda8083_state* state = fe->demodulator_priv; - state->config->pll_set(fe, p); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + tda8083_set_inversion (state, p->inversion); tda8083_set_fec (state, p->u.qpsk.fec_inner); tda8083_set_symbolrate (state, p->u.qpsk.symbol_rate); @@ -334,8 +338,6 @@ static int tda8083_init(struct dvb_frontend* fe) for (i=0; i<44; i++) tda8083_writereg (state, i, tda8083_init_tab[i]); - if (state->config->pll_init) state->config->pll_init(fe); - tda8083_writereg (state, 0x00, 0x3c); tda8083_writereg (state, 0x00, 0x04); diff --git a/linux/drivers/media/dvb/frontends/tda8083.h b/linux/drivers/media/dvb/frontends/tda8083.h index 466663307..e7a48f61e 100644 --- a/linux/drivers/media/dvb/frontends/tda8083.h +++ b/linux/drivers/media/dvb/frontends/tda8083.h @@ -33,10 +33,6 @@ struct tda8083_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, diff --git a/linux/drivers/media/dvb/frontends/tda80xx.c b/linux/drivers/media/dvb/frontends/tda80xx.c index 051be86b3..7954e8732 100644 --- a/linux/drivers/media/dvb/frontends/tda80xx.c +++ b/linux/drivers/media/dvb/frontends/tda80xx.c @@ -523,9 +523,10 @@ static int tda80xx_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par { struct tda80xx_state* state = fe->demodulator_priv; - tda80xx_writereg(state, 0x1c, 0x80); - state->config->pll_set(fe, p); - tda80xx_writereg(state, 0x1c, 0x00); + if (fe->ops->tuner_ops->set_params) { + fe->ops->tuner_ops->set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } tda80xx_set_parameters(state, p->inversion, p->u.qpsk.symbol_rate, p->u.qpsk.fec_inner); tda80xx_set_clk(state); @@ -628,6 +629,17 @@ static void tda80xx_release(struct dvb_frontend* fe) kfree(state); } +static int tda80xx_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct tda80xx_state* state = fe->demodulator_priv; + + if (enable) { + return tda80xx_writereg(state, 0x1c, 0x80); + } else { + return tda80xx_writereg(state, 0x1c, 0x00); + } +} + static struct dvb_frontend_ops tda80xx_ops; struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config, @@ -709,6 +721,7 @@ static struct dvb_frontend_ops tda80xx_ops = { .init = tda80xx_init, .sleep = tda80xx_sleep, + .i2c_gate_ctrl = tda80xx_i2c_gate_ctrl, .set_frontend = tda80xx_set_frontend, .get_frontend = tda80xx_get_frontend, diff --git a/linux/drivers/media/dvb/frontends/tda80xx.h b/linux/drivers/media/dvb/frontends/tda80xx.h index cd639a0aa..435dd9477 100644 --- a/linux/drivers/media/dvb/frontends/tda80xx.h +++ b/linux/drivers/media/dvb/frontends/tda80xx.h @@ -39,10 +39,6 @@ struct tda80xx_config /* Register setting to use for 18v */ u8 volt18setting; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config, diff --git a/linux/drivers/media/dvb/frontends/ves1820.c b/linux/drivers/media/dvb/frontends/ves1820.c index ad8647a3c..9810e2dcb 100644 --- a/linux/drivers/media/dvb/frontends/ves1820.c +++ b/linux/drivers/media/dvb/frontends/ves1820.c @@ -204,9 +204,6 @@ static int ves1820_init(struct dvb_frontend* fe) ves1820_writereg(state, 0x34, state->pwm); - if (state->config->pll_init) - state->config->pll_init(fe); - return 0; } @@ -223,7 +220,11 @@ static int ves1820_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_p if (real_qam < 0 || real_qam > 4) return -EINVAL; - state->config->pll_set(fe, p); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + ves1820_set_symbolrate(state, p->u.qam.symbol_rate); ves1820_writereg(state, 0x34, state->pwm); diff --git a/linux/drivers/media/dvb/frontends/ves1820.h b/linux/drivers/media/dvb/frontends/ves1820.h index 355f130b1..520f09522 100644 --- a/linux/drivers/media/dvb/frontends/ves1820.h +++ b/linux/drivers/media/dvb/frontends/ves1820.h @@ -39,10 +39,6 @@ struct ves1820_config /* SELAGC control */ u8 selagc:1; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, diff --git a/linux/drivers/media/dvb/frontends/ves1x93.c b/linux/drivers/media/dvb/frontends/ves1x93.c index 821df8e83..660aa7bb9 100644 --- a/linux/drivers/media/dvb/frontends/ves1x93.c +++ b/linux/drivers/media/dvb/frontends/ves1x93.c @@ -278,12 +278,6 @@ static int ves1x93_init (struct dvb_frontend* fe) } } - if (state->config->pll_init) { - ves1x93_writereg(state, 0x00, 0x11); - state->config->pll_init(fe); - ves1x93_writereg(state, 0x00, 0x01); - } - return 0; } @@ -395,9 +389,10 @@ static int ves1x93_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par { struct ves1x93_state* state = fe->demodulator_priv; - ves1x93_writereg(state, 0x00, 0x11); - state->config->pll_set(fe, p); - ves1x93_writereg(state, 0x00, 0x01); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } ves1x93_set_inversion (state, p->inversion); ves1x93_set_fec (state, p->u.qpsk.fec_inner); ves1x93_set_symbolrate (state, p->u.qpsk.symbol_rate); @@ -442,6 +437,17 @@ static void ves1x93_release(struct dvb_frontend* fe) kfree(state); } +static int ves1x93_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct ves1x93_state* state = fe->demodulator_priv; + + if (enable) { + return ves1x93_writereg(state, 0x00, 0x11); + } else { + return ves1x93_writereg(state, 0x00, 0x01); + } +} + static struct dvb_frontend_ops ves1x93_ops; struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, @@ -523,6 +529,7 @@ static struct dvb_frontend_ops ves1x93_ops = { .init = ves1x93_init, .sleep = ves1x93_sleep, + .i2c_gate_ctrl = ves1x93_i2c_gate_ctrl, .set_frontend = ves1x93_set_frontend, .get_frontend = ves1x93_get_frontend, diff --git a/linux/drivers/media/dvb/frontends/ves1x93.h b/linux/drivers/media/dvb/frontends/ves1x93.h index 1627e37c5..ba88ae085 100644 --- a/linux/drivers/media/dvb/frontends/ves1x93.h +++ b/linux/drivers/media/dvb/frontends/ves1x93.h @@ -38,10 +38,6 @@ struct ves1x93_config /* should PWM be inverted? */ u8 invert_pwm:1; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, diff --git a/linux/drivers/media/dvb/frontends/zl10353.c b/linux/drivers/media/dvb/frontends/zl10353.c index 10bb871fa..1018baae0 100644 --- a/linux/drivers/media/dvb/frontends/zl10353.c +++ b/linux/drivers/media/dvb/frontends/zl10353.c @@ -130,6 +130,7 @@ static int zl10353_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) { struct zl10353_state *state = fe->demodulator_priv; + u8 pllbuf[6] = { 0x67 }; /* These settings set "auto-everything" and start the FSM. */ @@ -146,7 +147,30 @@ static int zl10353_set_parameters(struct dvb_frontend *fe, zl10353_single_write(fe, 0x66, 0xE9); zl10353_single_write(fe, 0x62, 0x0A); - state->config.pll_set(fe, param, pllbuf + 1); + // if there is no attached secondary tuner, we call set_params to program + // a potential tuner attached somewhere else + if (state->config.no_tuner) { + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, param); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + } + + // if pllbuf is defined, retrieve the settings + if (fe->ops->tuner_ops.calc_regs) { + fe->ops->tuner_ops.calc_regs(fe, param, pllbuf+1, 5); + pllbuf[1] <<= 1; + } else { + // fake pllbuf settings + pllbuf[1] = 0x61 << 1; + pllbuf[2] = 0; + pllbuf[3] = 0; + pllbuf[3] = 0; + pllbuf[4] = 0; + } + + // there is no call to _just_ start decoding, so we send the pllbuf anyway + // even if there isn't a PLL attached to the secondary bus zl10353_write(fe, pllbuf, sizeof(pllbuf)); zl10353_single_write(fe, 0x70, 0x01); diff --git a/linux/drivers/media/dvb/frontends/zl10353.h b/linux/drivers/media/dvb/frontends/zl10353.h index 5cc4ae718..9770cb840 100644 --- a/linux/drivers/media/dvb/frontends/zl10353.h +++ b/linux/drivers/media/dvb/frontends/zl10353.h @@ -29,10 +29,8 @@ struct zl10353_config /* demodulator's I2C address */ u8 demod_address; - /* function which configures the PLL buffer (for secondary I2C - * connected tuner) or tunes the PLL (for direct connected tuner) */ - int (*pll_set)(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params, u8 *pllbuf); + /* set if no pll is connected to the secondary i2c bus */ + int no_tuner; }; extern struct dvb_frontend* zl10353_attach(const struct zl10353_config *config, diff --git a/linux/drivers/media/dvb/pluto2/pluto2.c b/linux/drivers/media/dvb/pluto2/pluto2.c index c9afad038..5cc609d98 100644 --- a/linux/drivers/media/dvb/pluto2/pluto2.c +++ b/linux/drivers/media/dvb/pluto2/pluto2.c @@ -424,8 +424,8 @@ static inline u32 divide(u32 numerator, u32 denominator) } /* LG Innotek TDTE-E001P (Infineon TUA6034) */ -static int lg_tdtpe001p_pll_set(struct dvb_frontend *fe, - struct dvb_frontend_parameters *p) +static int lg_tdtpe001p_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) { struct pluto *pluto = frontend_to_pluto(fe); struct i2c_msg msg; @@ -473,6 +473,8 @@ static int lg_tdtpe001p_pll_set(struct dvb_frontend *fe, msg.buf = buf; msg.len = sizeof(buf); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); ret = i2c_transfer(&pluto->i2c_adap, &msg, 1); if (ret < 0) return ret; @@ -497,8 +499,6 @@ static struct tda1004x_config pluto2_fe_config __devinitdata = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .pll_set = lg_tdtpe001p_pll_set, - .pll_sleep = NULL, .request_firmware = pluto2_request_firmware, }; @@ -511,6 +511,7 @@ static int __devinit frontend_init(struct pluto *pluto) dev_err(&pluto->pdev->dev, "could not attach frontend\n"); return -ENODEV; } + pluto->fe->ops->tuner_ops.set_params = lg_tdtpe001p_tuner_set_params; ret = dvb_register_frontend(&pluto->dvb_adapter, pluto->fe); if (ret < 0) { diff --git a/linux/drivers/media/dvb/ttpci/av7110.c b/linux/drivers/media/dvb/ttpci/av7110.c index 6dedb175a..9567e03a1 100644 --- a/linux/drivers/media/dvb/ttpci/av7110.c +++ b/linux/drivers/media/dvb/ttpci/av7110.c @@ -1552,7 +1552,7 @@ static int get_firmware(struct av7110* av7110) #endif -static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_bsrv2_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct av7110* av7110 = (struct av7110*) fe->dvb->priv; u8 pwr = 0; @@ -1575,6 +1575,8 @@ static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_param // NOTE: since we're using a prescaler of 2, we set the // divisor frequency to 62.5kHz and divide by 125 above + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -1584,10 +1586,9 @@ static struct ves1x93_config alps_bsrv2_config = { .demod_address = 0x08, .xin = 90100000UL, .invert_pwm = 0, - .pll_set = alps_bsrv2_pll_set, }; -static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_tdbe2_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct av7110* av7110 = fe->dvb->priv; u32 div; @@ -1601,6 +1602,8 @@ static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_param data[2] = 0x85 | ((div >> 10) & 0x60); data[3] = (params->frequency < 174000000 ? 0x88 : params->frequency < 470000000 ? 0x84 : 0x81); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -1611,14 +1614,12 @@ static struct ves1820_config alps_tdbe2_config = { .xin = 57840000UL, .invert = 1, .selagc = VES1820_SELAGC_SIGNAMPERR, - .pll_set = alps_tdbe2_pll_set, }; -static int grundig_29504_451_pll_set(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) +static int grundig_29504_451_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct av7110* av7110 = fe->dvb->priv; u32 div; @@ -1631,6 +1632,8 @@ static int grundig_29504_451_pll_set(struct dvb_frontend* fe, data[2] = 0x8e; data[3] = 0x00; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -1638,13 +1641,11 @@ static int grundig_29504_451_pll_set(struct dvb_frontend* fe, static struct tda8083_config grundig_29504_451_config = { .demod_address = 0x68, - .pll_set = grundig_29504_451_pll_set, }; -static int philips_cd1516_pll_set(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) +static int philips_cd1516_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct av7110* av7110 = fe->dvb->priv; u32 div; @@ -1659,6 +1660,8 @@ static int philips_cd1516_pll_set(struct dvb_frontend* fe, data[2] = 0x8e; data[3] = (f < 174000000 ? 0xa1 : f < 470000000 ? 0x92 : 0x34); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -1669,12 +1672,11 @@ static struct ves1820_config philips_cd1516_config = { .xin = 57840000UL, .invert = 1, .selagc = VES1820_SELAGC_SIGNAMPERR, - .pll_set = philips_cd1516_pll_set, }; -static int alps_tdlb7_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_tdlb7_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct av7110* av7110 = fe->dvb->priv; u32 div, pwr; @@ -1693,6 +1695,8 @@ static int alps_tdlb7_pll_set(struct dvb_frontend* fe, struct dvb_frontend_param data[2] = 0x85; data[3] = pwr << 6; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -1708,7 +1712,6 @@ static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct fir static struct sp8870_config alps_tdlb7_config = { .demod_address = 0x71, - .pll_set = alps_tdlb7_pll_set, .request_firmware = alps_tdlb7_request_firmware, }; @@ -1806,7 +1809,7 @@ static u8 nexusca_stv0297_inittab[] = { 0xff, 0xff, }; -static int nexusca_stv0297_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int nexusca_stv0297_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct av7110* av7110 = fe->dvb->priv; u32 div; @@ -1832,7 +1835,8 @@ static int nexusca_stv0297_pll_set(struct dvb_frontend* fe, struct dvb_frontend_ else return -EINVAL; - stv0297_enable_plli2c(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) { printk("nexusca: pll transfer failed!\n"); return -EIO; @@ -1840,8 +1844,8 @@ static int nexusca_stv0297_pll_set(struct dvb_frontend* fe, struct dvb_frontend_ // wait for PLL lock for(i = 0; i < 20; i++) { - - stv0297_enable_plli2c(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &readmsg, 1) == 1) if (data[0] & 0x40) break; msleep(10); @@ -1855,12 +1859,11 @@ static struct stv0297_config nexusca_stv0297_config = { .demod_address = 0x1C, .inittab = nexusca_stv0297_inittab, .invert = 1, - .pll_set = nexusca_stv0297_pll_set, }; -static int grundig_29504_401_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int grundig_29504_401_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct av7110* av7110 = (struct av7110*) fe->dvb->priv; u32 div; @@ -1887,13 +1890,14 @@ static int grundig_29504_401_pll_set(struct dvb_frontend* fe, struct dvb_fronten data[2] = ((div >> 10) & 0x60) | cfg; data[3] = (cpump << 6) | band_select; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } static struct l64781_config grundig_29504_401_config = { .demod_address = 0x55, - .pll_set = grundig_29504_401_pll_set, }; @@ -2079,6 +2083,9 @@ static int frontend_init(struct av7110 *av7110) case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) av7110->fe = ves1820_attach(&philips_cd1516_config, &av7110->i2c_adap, read_pwm(av7110)); + if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = philips_cd1516_tuner_set_params; + } break; } @@ -2091,6 +2098,7 @@ static int frontend_init(struct av7110 *av7110) // try the ALPS BSRV2 first of all av7110->fe = ves1x93_attach(&alps_bsrv2_config, &av7110->i2c_adap); if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = alps_bsrv2_tuner_set_params; av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst; av7110->fe->ops->set_tone = av7110_set_tone; @@ -2101,6 +2109,9 @@ static int frontend_init(struct av7110 *av7110) // try the ALPS BSRU6 now av7110->fe = stv0299_attach(&alps_bsru6_config, &av7110->i2c_adap); if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = alps_bsru6_tuner_set_params; + av7110->fe->tuner_priv = &av7110->i2c_adap; + av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst; av7110->fe->ops->set_tone = av7110_set_tone; @@ -2111,6 +2122,7 @@ static int frontend_init(struct av7110 *av7110) // Try the grundig 29504-451 av7110->fe = tda8083_attach(&grundig_29504_451_config, &av7110->i2c_adap); if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = grundig_29504_451_tuner_set_params; av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst; av7110->fe->ops->set_tone = av7110_set_tone; @@ -2124,11 +2136,17 @@ static int frontend_init(struct av7110 *av7110) /* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */ av7110->fe = ves1820_attach(&philips_cd1516_config, &av7110->i2c_adap, read_pwm(av7110)); + if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = philips_cd1516_tuner_set_params; + } break; case 0x0003: /* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */ av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); + if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = alps_tdbe2_tuner_set_params; + } break; } break; @@ -2137,17 +2155,24 @@ static int frontend_init(struct av7110 *av7110) // ALPS TDLB7 av7110->fe = sp8870_attach(&alps_tdlb7_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = alps_tdlb7_tuner_set_params; + } break; case 0x0002: // Hauppauge/TT DVB-C premium rev2.X av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); + if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = alps_tdbe2_tuner_set_params; + } break; case 0x0004: // Galaxis DVB-S rev1.3 /* ALPS BSRV2 */ av7110->fe = ves1x93_attach(&alps_bsrv2_config, &av7110->i2c_adap); if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = alps_bsrv2_tuner_set_params; av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst; av7110->fe->ops->set_tone = av7110_set_tone; @@ -2159,6 +2184,7 @@ static int frontend_init(struct av7110 *av7110) /* Grundig 29504-451 */ av7110->fe = tda8083_attach(&grundig_29504_451_config, &av7110->i2c_adap); if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = grundig_29504_451_tuner_set_params; av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst; av7110->fe->ops->set_tone = av7110_set_tone; @@ -2169,12 +2195,17 @@ static int frontend_init(struct av7110 *av7110) case 0x0008: // Hauppauge/TT DVB-T av7110->fe = l64781_attach(&grundig_29504_401_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = grundig_29504_401_tuner_set_params; + } break; case 0x000A: // Hauppauge/TT Nexus-CA rev1.X av7110->fe = stv0297_attach(&nexusca_stv0297_config, &av7110->i2c_adap); if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = nexusca_stv0297_tuner_set_params; + /* set TDA9819 into DVB mode */ saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9198 pin9(STD) saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9198 pin30(VIF) @@ -2189,6 +2220,9 @@ static int frontend_init(struct av7110 *av7110) /* ALPS BSBE1 */ av7110->fe = stv0299_attach(&alps_bsbe1_config, &av7110->i2c_adap); if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = alps_bsbe1_tuner_set_params; + av7110->fe->tuner_priv = &av7110->i2c_adap; + if (lnbp21_init(av7110->fe, &av7110->i2c_adap, 0, 0)) { printk("dvb-ttpci: LNBP21 not found!\n"); if (av7110->fe->ops->release) diff --git a/linux/drivers/media/dvb/ttpci/budget-av.c b/linux/drivers/media/dvb/ttpci/budget-av.c index dc78aadf0..680041878 100644 --- a/linux/drivers/media/dvb/ttpci/budget-av.c +++ b/linux/drivers/media/dvb/ttpci/budget-av.c @@ -472,12 +472,12 @@ static int philips_su1278_ty_ci_set_symbol_rate(struct dvb_frontend *fe, u32 sra return 0; } -static int philips_su1278_ty_ci_pll_set(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct dvb_frontend_parameters *params) +static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) { u32 div; u8 buf[4]; + struct budget *budget = (struct budget *) fe->dvb->priv; struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; if ((params->frequency < 950000) || (params->frequency > 2150000)) @@ -501,7 +501,9 @@ static int philips_su1278_ty_ci_pll_set(struct dvb_frontend *fe, else if (params->frequency < 2150000) buf[3] |= 0xC0; - if (i2c_transfer(i2c, &msg, 1) != 1) + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } @@ -509,9 +511,8 @@ static int philips_su1278_ty_ci_pll_set(struct dvb_frontend *fe, #define MIN2(a,b) ((a) < (b) ? (a) : (b)) #define MIN3(a,b,c) MIN2(MIN2(a,b),c) -static int philips_su1278sh2_tua6100_pll_set(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct dvb_frontend_parameters *params) +static int philips_su1278sh2_tua6100_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) { u8 reg0 [2] = { 0x00, 0x00 }; u8 reg1 [4] = { 0x01, 0x00, 0x00, 0x00 }; @@ -521,6 +522,7 @@ static int philips_su1278sh2_tua6100_pll_set(struct dvb_frontend *fe, int R, A, N, P, M; struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = NULL,.len = 0 }; int freq = params->frequency; + struct budget *budget = (struct budget *) fe->dvb->priv; first_ZF = (freq) / 1000; @@ -620,21 +622,25 @@ static int philips_su1278sh2_tua6100_pll_set(struct dvb_frontend *fe, reg0[1] |= 0x03; /* already enabled - do not reenable i2c repeater or TX fails */ + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); msg.buf = reg0; msg.len = sizeof(reg0); - if (i2c_transfer(i2c, &msg, 1) != 1) + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) return -EIO; - stv0299_enable_plli2c(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); msg.buf = reg1; msg.len = sizeof(reg1); - if (i2c_transfer(i2c, &msg, 1) != 1) + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) return -EIO; - stv0299_enable_plli2c(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); msg.buf = reg2; msg.len = sizeof(reg2); - if (i2c_transfer(i2c, &msg, 1) != 1) + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -692,7 +698,6 @@ static struct stv0299_config typhoon_config = { .volt13_op0_op1 = STV0299_VOLT13_OP0, .min_delay_ms = 100, .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, - .pll_set = philips_su1278_ty_ci_pll_set, }; @@ -706,7 +711,6 @@ static struct stv0299_config cinergy_1200s_config = { .volt13_op0_op1 = STV0299_VOLT13_OP0, .min_delay_ms = 100, .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, - .pll_set = philips_su1278_ty_ci_pll_set, }; static struct stv0299_config cinergy_1200s_1894_0010_config = { @@ -719,10 +723,9 @@ static struct stv0299_config cinergy_1200s_1894_0010_config = { .volt13_op0_op1 = STV0299_VOLT13_OP0, .min_delay_ms = 100, .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, - .pll_set = philips_su1278sh2_tua6100_pll_set, }; -static int philips_cu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct budget *budget = (struct budget *) fe->dvb->priv; u8 buf[4]; @@ -738,6 +741,8 @@ static int philips_cu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_p buf[3] = (params->frequency < 150000000 ? 0x01 : params->frequency < 445000000 ? 0x02 : 0x04); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -745,19 +750,20 @@ static int philips_cu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_p static struct tda10021_config philips_cu1216_config = { .demod_address = 0x0c, - .pll_set = philips_cu1216_pll_set, }; -static int philips_tu1216_pll_init(struct dvb_frontend *fe) +static int philips_tu1216_tuner_init(struct dvb_frontend *fe) { struct budget *budget = (struct budget *) fe->dvb->priv; static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; // setup PLL configuration + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(1); @@ -765,7 +771,7 @@ static int philips_tu1216_pll_init(struct dvb_frontend *fe) return 0; } -static int philips_tu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct budget *budget = (struct budget *) fe->dvb->priv; u8 tuner_buf[4]; @@ -839,6 +845,8 @@ static int philips_tu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_p tuner_buf[2] = 0xca; tuner_buf[3] = (cp << 5) | (filter << 3) | band; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) return -EIO; @@ -862,9 +870,6 @@ static struct tda1004x_config philips_tu1216_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .pll_init = philips_tu1216_pll_init, - .pll_set = philips_tu1216_pll_set, - .pll_sleep = NULL, .request_firmware = philips_tu1216_request_firmware, }; @@ -911,13 +916,13 @@ static u8 philips_sd1878_inittab[] = { 0xff, 0xff }; -static int philips_sd1878_tda8261_pll_set(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct dvb_frontend_parameters *params) +static int philips_sd1878_tda8261_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) { u8 buf[4]; int rc; struct i2c_msg tuner_msg = {.addr=0x60,.flags=0,.buf=buf,.len=sizeof(buf)}; + struct budget *budget = (struct budget *) fe->dvb->priv; if((params->frequency < 950000) || (params->frequency > 2150000)) return -EINVAL; @@ -926,7 +931,9 @@ static int philips_sd1878_tda8261_pll_set(struct dvb_frontend *fe, params->frequency, 0); if(rc < 0) return rc; - if(i2c_transfer(i2c, &tuner_msg, 1) != 1) + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if(i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) return -EIO; return 0; @@ -977,7 +984,6 @@ static struct stv0299_config philips_sd1878_config = { .volt13_op0_op1 = STV0299_VOLT13_OP0, .min_delay_ms = 100, .set_symbol_rate = philips_sd1878_ci_set_symbol_rate, - .pll_set = philips_sd1878_tda8261_pll_set, }; static u8 read_pwm(struct budget_av *budget_av) @@ -1035,9 +1041,15 @@ static void frontend_init(struct budget_av *budget_av) if (saa->pci->subsystem_vendor == 0x1894) { fe = stv0299_attach(&cinergy_1200s_1894_0010_config, &budget_av->budget.i2c_adap); + if (fe) { + fe->ops->tuner_ops.set_params = philips_su1278sh2_tua6100_tuner_set_params; + } } else { fe = stv0299_attach(&typhoon_config, &budget_av->budget.i2c_adap); + if (fe) { + fe->ops->tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } } break; @@ -1047,17 +1059,26 @@ static void frontend_init(struct budget_av *budget_av) case SUBID_DVBS_EASYWATCH: fe = stv0299_attach(&philips_sd1878_config, &budget_av->budget.i2c_adap); + if (fe) { + fe->ops->tuner_ops.set_params = philips_sd1878_tda8261_tuner_set_params; + } break; case SUBID_DVBS_KNC1_PLUS: case SUBID_DVBS_TYPHOON: fe = stv0299_attach(&typhoon_config, &budget_av->budget.i2c_adap); + if (fe) { + fe->ops->tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } break; case SUBID_DVBS_CINERGY1200: fe = stv0299_attach(&cinergy_1200s_config, &budget_av->budget.i2c_adap); + if (fe) { + fe->ops->tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } break; case SUBID_DVBC_KNC1: @@ -1065,23 +1086,37 @@ static void frontend_init(struct budget_av *budget_av) fe = tda10021_attach(&philips_cu1216_config, &budget_av->budget.i2c_adap, read_pwm(budget_av)); + if (fe) { + fe->ops->tuner_ops.set_params = philips_cu1216_tuner_set_params; + } break; case SUBID_DVBT_KNC1: case SUBID_DVBT_KNC1_PLUS: fe = tda10046_attach(&philips_tu1216_config, &budget_av->budget.i2c_adap); + if (fe) { + fe->ops->tuner_ops.init = philips_tu1216_tuner_init; + fe->ops->tuner_ops.set_params = philips_tu1216_tuner_set_params; + } break; case SUBID_DVBC_CINERGY1200: fe = tda10021_attach(&philips_cu1216_config, &budget_av->budget.i2c_adap, read_pwm(budget_av)); + if (fe) { + fe->ops->tuner_ops.set_params = philips_cu1216_tuner_set_params; + } break; case SUBID_DVBT_CINERGY1200: fe = tda10046_attach(&philips_tu1216_config, &budget_av->budget.i2c_adap); + if (fe) { + fe->ops->tuner_ops.init = philips_tu1216_tuner_init; + fe->ops->tuner_ops.set_params = philips_tu1216_tuner_set_params; + } break; } diff --git a/linux/drivers/media/dvb/ttpci/budget-ci.c b/linux/drivers/media/dvb/ttpci/budget-ci.c index e64a609cf..ed610e56b 100644 --- a/linux/drivers/media/dvb/ttpci/budget-ci.c +++ b/linux/drivers/media/dvb/ttpci/budget-ci.c @@ -620,10 +620,10 @@ static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, return 0; } -static int philips_su1278_tt_pll_set(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct dvb_frontend_parameters *params) +static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) { + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; u32 div; u8 buf[4]; struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; @@ -649,7 +649,9 @@ static int philips_su1278_tt_pll_set(struct dvb_frontend *fe, else if (params->frequency < 2150000) buf[3] |= 0xC0; - if (i2c_transfer(i2c, &msg, 1) != 1) + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1) return -EIO; return 0; } @@ -665,12 +667,11 @@ static struct stv0299_config philips_su1278_tt_config = { .volt13_op0_op1 = STV0299_VOLT13_OP1, .min_delay_ms = 50, .set_symbol_rate = philips_su1278_tt_set_symbol_rate, - .pll_set = philips_su1278_tt_pll_set, }; -static int philips_tdm1316l_pll_init(struct dvb_frontend *fe) +static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe) { struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; @@ -679,6 +680,8 @@ static int philips_tdm1316l_pll_init(struct dvb_frontend *fe) sizeof(td1316_init) }; // setup PLL configuration + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(1); @@ -687,14 +690,18 @@ static int philips_tdm1316l_pll_init(struct dvb_frontend *fe) tuner_msg.addr = 0x65; tuner_msg.buf = disable_mc44BC374c; tuner_msg.len = sizeof(disable_mc44BC374c); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) { + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1); } return 0; } -static int philips_tdm1316l_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; u8 tuner_buf[4]; @@ -770,6 +777,8 @@ static int philips_tdm1316l_pll_set(struct dvb_frontend *fe, struct dvb_frontend tuner_buf[2] = 0xca; tuner_buf[3] = (cp << 5) | (filter << 3) | band; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) return -EIO; @@ -793,13 +802,10 @@ static struct tda1004x_config philips_tdm1316l_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .pll_init = philips_tdm1316l_pll_init, - .pll_set = philips_tdm1316l_pll_set, - .pll_sleep = NULL, .request_firmware = philips_tdm1316l_request_firmware, }; -static int dvbc_philips_tdm1316l_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; u8 tuner_buf[5]; @@ -857,13 +863,15 @@ static int dvbc_philips_tdm1316l_pll_set(struct dvb_frontend *fe, struct dvb_fro tuner_buf[3] = (cp << 5) | (filter << 3) | band; tuner_buf[4] = 0x80; - stv0297_enable_plli2c(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(50); - stv0297_enable_plli2c(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) return -EIO; @@ -969,7 +977,6 @@ static struct stv0297_config dvbc_philips_tdm1316l_config = { .demod_address = 0x1c, .inittab = dvbc_philips_tdm1316l_inittab, .invert = 0, - .pll_set = dvbc_philips_tdm1316l_pll_set, }; @@ -982,6 +989,8 @@ static void frontend_init(struct budget_ci *budget_ci) budget_ci->budget.dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops->tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; break; } break; @@ -990,6 +999,7 @@ static void frontend_init(struct budget_ci *budget_ci) budget_ci->budget.dvb_frontend = stv0299_attach(&philips_su1278_tt_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops->tuner_ops.set_params = philips_su1278_tt_tuner_set_params; break; } break; @@ -999,6 +1009,7 @@ static void frontend_init(struct budget_ci *budget_ci) budget_ci->budget.dvb_frontend = stv0297_attach(&dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops->tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params; break; } break; @@ -1008,6 +1019,8 @@ static void frontend_init(struct budget_ci *budget_ci) budget_ci->budget.dvb_frontend = tda10045_attach(&philips_tdm1316l_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops->tuner_ops.init = philips_tdm1316l_tuner_init; + budget_ci->budget.dvb_frontend->ops->tuner_ops.set_params = philips_tdm1316l_tuner_set_params; break; } break; @@ -1017,6 +1030,8 @@ static void frontend_init(struct budget_ci *budget_ci) budget_ci->budget.dvb_frontend = tda10046_attach(&philips_tdm1316l_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops->tuner_ops.init = philips_tdm1316l_tuner_init; + budget_ci->budget.dvb_frontend->ops->tuner_ops.set_params = philips_tdm1316l_tuner_set_params; break; } break; @@ -1024,6 +1039,9 @@ static void frontend_init(struct budget_ci *budget_ci) case 0x1017: // TT S-1500 PCI budget_ci->budget.dvb_frontend = stv0299_attach(&alps_bsbe1_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops->tuner_ops.set_params = alps_bsbe1_tuner_set_params; + budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; + budget_ci->budget.dvb_frontend->ops->dishnetwork_send_legacy_command = NULL; if (lnbp21_init(budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0)) { printk("%s: No LNBP21 found!\n", __FUNCTION__); diff --git a/linux/drivers/media/dvb/ttpci/budget-patch.c b/linux/drivers/media/dvb/ttpci/budget-patch.c index 25e40fc1f..843d6245a 100644 --- a/linux/drivers/media/dvb/ttpci/budget-patch.c +++ b/linux/drivers/media/dvb/ttpci/budget-patch.c @@ -258,7 +258,7 @@ static int budget_patch_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_c return 0; } -static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_bsrv2_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; u8 pwr = 0; @@ -281,7 +281,10 @@ static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_param // NOTE: since we're using a prescaler of 2, we set the // divisor frequency to 62.5kHz and divide by 125 above - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) + return -EIO; return 0; } @@ -289,10 +292,9 @@ static struct ves1x93_config alps_bsrv2_config = { .demod_address = 0x08, .xin = 90100000UL, .invert_pwm = 0, - .pll_set = alps_bsrv2_pll_set, }; -static int grundig_29504_451_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int grundig_29504_451_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; u32 div; @@ -305,13 +307,15 @@ static int grundig_29504_451_pll_set(struct dvb_frontend* fe, struct dvb_fronten data[2] = 0x8e; data[3] = 0x00; - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) + return -EIO; return 0; } static struct tda8083_config grundig_29504_451_config = { .demod_address = 0x68, - .pll_set = grundig_29504_451_pll_set, }; static void frontend_init(struct budget_patch* budget) @@ -323,6 +327,7 @@ static void frontend_init(struct budget_patch* budget) // try the ALPS BSRV2 first of all budget->dvb_frontend = ves1x93_attach(&alps_bsrv2_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = alps_bsrv2_tuner_set_params; budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd; budget->dvb_frontend->ops->diseqc_send_burst = budget_patch_diseqc_send_burst; budget->dvb_frontend->ops->set_tone = budget_patch_set_tone; @@ -332,6 +337,9 @@ static void frontend_init(struct budget_patch* budget) // try the ALPS BSRU6 now budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_diseqc_send_master_cmd; budget->dvb_frontend->ops->diseqc_send_burst = budget_diseqc_send_burst; budget->dvb_frontend->ops->set_tone = budget_set_tone; @@ -341,6 +349,7 @@ static void frontend_init(struct budget_patch* budget) // Try the grundig 29504-451 budget->dvb_frontend = tda8083_attach(&grundig_29504_451_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = grundig_29504_451_tuner_set_params; budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_diseqc_send_master_cmd; budget->dvb_frontend->ops->diseqc_send_burst = budget_diseqc_send_burst; budget->dvb_frontend->ops->set_tone = budget_set_tone; diff --git a/linux/drivers/media/dvb/ttpci/budget.c b/linux/drivers/media/dvb/ttpci/budget.c index c23c02d95..a231975cb 100644 --- a/linux/drivers/media/dvb/ttpci/budget.c +++ b/linux/drivers/media/dvb/ttpci/budget.c @@ -186,7 +186,7 @@ static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t m return 0; } -static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_bsrv2_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct budget* budget = (struct budget*) fe->dvb->priv; u8 pwr = 0; @@ -209,6 +209,8 @@ static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_param // NOTE: since we're using a prescaler of 2, we set the // divisor frequency to 62.5kHz and divide by 125 above + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } @@ -218,10 +220,9 @@ static struct ves1x93_config alps_bsrv2_config = .demod_address = 0x08, .xin = 90100000UL, .invert_pwm = 0, - .pll_set = alps_bsrv2_pll_set, }; -static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_tdbe2_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct budget* budget = (struct budget*) fe->dvb->priv; u32 div; @@ -235,6 +236,8 @@ static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_param data[2] = 0x85 | ((div >> 10) & 0x60); data[3] = (params->frequency < 174000000 ? 0x88 : params->frequency < 470000000 ? 0x84 : 0x81); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } @@ -244,10 +247,9 @@ static struct ves1820_config alps_tdbe2_config = { .xin = 57840000UL, .invert = 1, .selagc = VES1820_SELAGC_SIGNAMPERR, - .pll_set = alps_tdbe2_pll_set, }; -static int grundig_29504_401_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int grundig_29504_401_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct budget* budget = (struct budget*) fe->dvb->priv; u32 div; @@ -274,16 +276,17 @@ static int grundig_29504_401_pll_set(struct dvb_frontend* fe, struct dvb_fronten data[2] = ((div >> 10) & 0x60) | cfg; data[3] = (cpump << 6) | band_select; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } static struct l64781_config grundig_29504_401_config = { .demod_address = 0x55, - .pll_set = grundig_29504_401_pll_set, }; -static int grundig_29504_451_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int grundig_29504_451_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct budget* budget = (struct budget*) fe->dvb->priv; u32 div; @@ -296,16 +299,17 @@ static int grundig_29504_451_pll_set(struct dvb_frontend* fe, struct dvb_fronten data[2] = 0x8e; data[3] = 0x00; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } static struct tda8083_config grundig_29504_451_config = { .demod_address = 0x68, - .pll_set = grundig_29504_451_pll_set, }; -static int s5h1420_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u32* freqout) +static int s5h1420_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct budget* budget = (struct budget*) fe->dvb->priv; u32 div; @@ -326,16 +330,16 @@ static int s5h1420_pll_set(struct dvb_frontend* fe, struct dvb_frontend_paramete else data[3] = 0xc0; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; - *freqout = div * 1000; return 0; } static struct s5h1420_config s5h1420_config = { .demod_address = 0x53, .invert = 1, - .pll_set = s5h1420_pll_set, }; static u8 read_pwm(struct budget* budget) @@ -359,6 +363,7 @@ static void frontend_init(struct budget *budget) // try the ALPS BSRV2 first of all budget->dvb_frontend = ves1x93_attach(&alps_bsrv2_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = alps_bsrv2_tuner_set_params; budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_diseqc_send_master_cmd; budget->dvb_frontend->ops->diseqc_send_burst = budget_diseqc_send_burst; budget->dvb_frontend->ops->set_tone = budget_set_tone; @@ -368,6 +373,8 @@ static void frontend_init(struct budget *budget) // try the ALPS BSRU6 now budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_diseqc_send_master_cmd; budget->dvb_frontend->ops->diseqc_send_burst = budget_diseqc_send_burst; budget->dvb_frontend->ops->set_tone = budget_set_tone; @@ -378,18 +385,26 @@ static void frontend_init(struct budget *budget) case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) budget->dvb_frontend = ves1820_attach(&alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget)); - if (budget->dvb_frontend) break; + if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = alps_tdbe2_tuner_set_params; + break; + } break; case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060)) budget->dvb_frontend = l64781_attach(&grundig_29504_401_config, &budget->i2c_adap); - if (budget->dvb_frontend) break; + if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = grundig_29504_401_tuner_set_params; + break; + } break; case 0x4f60: // Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/ALPS BSRU6(tsa5059)) budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; budget->dvb_frontend->ops->set_voltage = siemens_budget_set_voltage; budget->dvb_frontend->ops->dishnetwork_send_legacy_command = NULL; } @@ -398,6 +413,7 @@ static void frontend_init(struct budget *budget) case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522)) budget->dvb_frontend = tda8083_attach(&grundig_29504_451_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = grundig_29504_451_tuner_set_params; budget->dvb_frontend->ops->set_voltage = siemens_budget_set_voltage; budget->dvb_frontend->ops->dishnetwork_send_legacy_command = NULL; } @@ -406,6 +422,7 @@ static void frontend_init(struct budget *budget) case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260)) budget->dvb_frontend = s5h1420_attach(&s5h1420_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = s5h1420_tuner_set_params; if (lnbp21_init(budget->dvb_frontend, &budget->i2c_adap, 0, 0)) { printk("%s: No LNBP21 found!\n", __FUNCTION__); goto error_out; diff --git a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index e9347c42d..f2aa23d76 100644 --- a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -1056,7 +1056,7 @@ static u32 functionality(struct i2c_adapter *adapter) -static int alps_tdmb7_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_tdmb7_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; u8 data[4]; @@ -1076,14 +1076,13 @@ static int alps_tdmb7_pll_set(struct dvb_frontend* fe, struct dvb_frontend_param static struct cx22700_config alps_tdmb7_config = { .demod_address = 0x43, - .pll_set = alps_tdmb7_pll_set, }; -static int philips_tdm1316l_pll_init(struct dvb_frontend* fe) +static int philips_tdm1316l_tuner_init(struct dvb_frontend* fe) { struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; @@ -1105,7 +1104,7 @@ static int philips_tdm1316l_pll_init(struct dvb_frontend* fe) return 0; } -static int philips_tdm1316l_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int philips_tdm1316l_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; u8 tuner_buf[4]; @@ -1184,8 +1183,6 @@ static struct tda1004x_config philips_tdm1316l_config = { .demod_address = 0x8, .invert = 1, .invert_oclk = 0, - .pll_init = philips_tdm1316l_pll_init, - .pll_set = philips_tdm1316l_pll_set, .request_firmware = philips_tdm1316l_request_firmware, }; @@ -1307,7 +1304,7 @@ static int alps_stv0299_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 return 0; } -static int philips_tsa5059_pll_set(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dvb_frontend_parameters *params) +static int philips_tsa5059_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; u8 buf[4]; @@ -1330,7 +1327,7 @@ static int philips_tsa5059_pll_set(struct dvb_frontend *fe, struct i2c_adapter * if (ttusb->revision == TTUSB_REV_2_2) buf[3] |= 0x20; - if (i2c_transfer(i2c, &msg, 1) != 1) + if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -1346,10 +1343,9 @@ static struct stv0299_config alps_stv0299_config = { .volt13_op0_op1 = STV0299_VOLT13_OP1, .min_delay_ms = 100, .set_symbol_rate = alps_stv0299_set_symbol_rate, - .pll_set = philips_tsa5059_pll_set, }; -static int ttusb_novas_grundig_29504_491_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int ttusb_novas_grundig_29504_491_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; u8 buf[4]; @@ -1372,10 +1368,9 @@ static int ttusb_novas_grundig_29504_491_pll_set(struct dvb_frontend *fe, struct static struct tda8083_config ttusb_novas_grundig_29504_491_config = { .demod_address = 0x68, - .pll_set = ttusb_novas_grundig_29504_491_pll_set, }; -static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_tdbe2_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct ttusb* ttusb = fe->dvb->priv; u32 div; @@ -1401,7 +1396,6 @@ static struct ves1820_config alps_tdbe2_config = { .xin = 57840000UL, .invert = 1, .selagc = VES1820_SELAGC_SIGNAMPERR, - .pll_set = alps_tdbe2_pll_set, }; static u8 read_pwm(struct ttusb* ttusb) @@ -1425,6 +1419,8 @@ static void frontend_init(struct ttusb* ttusb) // try the stv0299 based first ttusb->fe = stv0299_attach(&alps_stv0299_config, &ttusb->i2c_adap); if (ttusb->fe != NULL) { + ttusb->fe->ops->tuner_ops.set_params = philips_tsa5059_tuner_set_params; + if(ttusb->revision == TTUSB_REV_2_2) { // ALPS BSBE1 alps_stv0299_config.inittab = alps_bsbe1_inittab; ttusb->fe->ops->set_voltage = lnbp21_set_voltage; @@ -1437,28 +1433,35 @@ static void frontend_init(struct ttusb* ttusb) // Grundig 29504-491 ttusb->fe = tda8083_attach(&ttusb_novas_grundig_29504_491_config, &ttusb->i2c_adap); if (ttusb->fe != NULL) { + ttusb->fe->ops->tuner_ops.set_params = ttusb_novas_grundig_29504_491_tuner_set_params; ttusb->fe->ops->set_voltage = ttusb_set_voltage; break; } - break; case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) ttusb->fe = ves1820_attach(&alps_tdbe2_config, &ttusb->i2c_adap, read_pwm(ttusb)); - if (ttusb->fe != NULL) + if (ttusb->fe != NULL) { + ttusb->fe->ops->tuner_ops.set_params = alps_tdbe2_tuner_set_params; break; + } break; case 0x1005: // Hauppauge/TT Nova-USB-t budget (tda10046/Philips td1316(tda6651tt) OR cx22700/ALPS TDMB7(??)) // try the ALPS TDMB7 first ttusb->fe = cx22700_attach(&alps_tdmb7_config, &ttusb->i2c_adap); - if (ttusb->fe != NULL) + if (ttusb->fe != NULL) { + ttusb->fe->ops->tuner_ops.set_params = alps_tdmb7_tuner_set_params; break; + } // Philips td1316 ttusb->fe = tda10046_attach(&philips_tdm1316l_config, &ttusb->i2c_adap); - if (ttusb->fe != NULL) + if (ttusb->fe != NULL) { + ttusb->fe->ops->tuner_ops.init = philips_tdm1316l_tuner_init; + ttusb->fe->ops->tuner_ops.set_params = philips_tdm1316l_tuner_set_params; break; + } break; } diff --git a/linux/drivers/media/video/cx25840/cx25840-firmware.c b/linux/drivers/media/video/cx25840/cx25840-firmware.c index 539de43dd..e14e0bc1f 100644 --- a/linux/drivers/media/video/cx25840/cx25840-firmware.c +++ b/linux/drivers/media/video/cx25840/cx25840-firmware.c @@ -44,34 +44,16 @@ #define FWDEV(x) (x)->name #endif -static int fastfw = 1; static char *firmware = FWFILE; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) -module_param(fastfw, bool, 0444); module_param(firmware, charp, 0444); #else -MODULE_PARM(fastfw, "i"); MODULE_PARM(firmware, "s"); #endif -MODULE_PARM_DESC(fastfw, "Load firmware fast [0=100MHz 1=333MHz (default)]"); MODULE_PARM_DESC(firmware, "Firmware image [default: " FWFILE "]"); -static void set_i2c_delay(struct i2c_client *client, int delay) -{ - struct i2c_algo_bit_data *algod = client->adapter->algo_data; - - /* We aren't guaranteed to be using algo_bit, - * so avoid the null pointer dereference - * and disable the 'fast firmware load' */ - if (algod) { - algod->udelay = delay; - } else { - fastfw = 0; - } -} - static void start_fw_load(struct i2c_client *client) { /* DL_ADDR_LB=0 DL_ADDR_HB=0 */ @@ -81,16 +63,10 @@ static void start_fw_load(struct i2c_client *client) cx25840_write(client, 0x803, 0x0b); /* AUTO_INC_DIS=1 */ cx25840_write(client, 0x000, 0x20); - - if (fastfw) - set_i2c_delay(client, 3); } static void end_fw_load(struct i2c_client *client) { - if (fastfw) - set_i2c_delay(client, 10); - /* AUTO_INC_DIS=0 */ cx25840_write(client, 0x000, 0x00); /* DL_ENABLE=0 */ @@ -117,30 +93,8 @@ static int fw_write(struct i2c_client *client, u8 * data, int size) int sent; if ((sent = i2c_master_send(client, data, size)) < size) { - - if (fastfw) { - v4l_err(client, "333MHz i2c firmware load failed\n"); - fastfw = 0; - set_i2c_delay(client, 10); - - if (sent > 2) { - u16 dl_addr = cx25840_read(client, 0x801) << 8; - dl_addr |= cx25840_read(client, 0x800); - dl_addr -= sent - 2; - cx25840_write(client, 0x801, dl_addr >> 8); - cx25840_write(client, 0x800, dl_addr & 0xff); - } - - if (i2c_master_send(client, data, size) < size) { - v4l_err(client, "100MHz i2c firmware load failed\n"); - return -ENOSYS; - } - - } else { - v4l_err(client, "firmware load i2c failure\n"); - return -ENOSYS; - } - + v4l_err(client, "firmware load i2c failure\n"); + return -ENOSYS; } return 0; diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index e1e9580b2..f34a76511 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -1602,6 +1602,7 @@ static int mpeg_release(struct inode *inode, struct file *file) BLACKBIRD_RAW_BITS_NONE ); + cx8802_cancel_buffers(fh->dev); /* stop mpeg capture */ if (fh->mpegq.streaming) videobuf_streamoff(&fh->mpegq); @@ -1719,12 +1720,25 @@ static int __devinit blackbird_probe(struct pci_dev *pci_dev, memcpy(&dev->params,&default_mpeg_params,sizeof(default_mpeg_params)); memcpy(&dev->dnr_params,&default_dnr_params,sizeof(default_dnr_params)); - if (core->tuner_formats & V4L2_STD_525_60) { - dev->height = 480; - dev->params.vi_frame_rate = 30; - } else { - dev->height = 576; - dev->params.vi_frame_rate = 25; + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_ROSLYN: + if (core->tuner_formats & V4L2_STD_525_60) { + dev->height = 480; + dev->params.vi_frame_rate = 30; + } else { + dev->height = 576; + dev->params.vi_frame_rate = 25; + } + break; + case CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT: + if (core->tvnorm->id & V4L2_STD_525_60) { + dev->height = 480; + dev->params.vi_frame_rate = 30; + } else { + dev->height = 576; + dev->params.vi_frame_rate = 25; + } + break; } err = cx8802_init_common(dev); diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 6d9417687..fbd8387de 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -52,6 +52,7 @@ #endif #ifdef HAVE_LGDT330X # include "lgdt330x.h" +# include "fe_lgh06xf.h" #endif #ifdef HAVE_NXT200X # include "nxt200x.h" @@ -114,21 +115,6 @@ static struct videobuf_queue_ops dvb_qops = { /* ------------------------------------------------------------------ */ -#if defined(HAVE_MT352) || defined(HAVE_ZL10353) -static int zarlink_pll_set(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params, - u8 *pllbuf) -{ - struct cx8802_dev *dev = fe->dvb->priv; - - pllbuf[0] = dev->core->pll_addr << 1; - dvb_pll_configure(dev->core->pll_desc, pllbuf + 1, - params->frequency, - params->u.ofdm.bandwidth); - return 0; -} -#endif - #ifdef HAVE_MT352 static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe) { @@ -197,19 +183,16 @@ static int dntv_live_dvbt_demod_init(struct dvb_frontend* fe) static struct mt352_config dvico_fusionhdtv = { .demod_address = 0x0F, .demod_init = dvico_fusionhdtv_demod_init, - .pll_set = zarlink_pll_set, }; static struct mt352_config dntv_live_dvbt_config = { .demod_address = 0x0f, .demod_init = dntv_live_dvbt_demod_init, - .pll_set = zarlink_pll_set, }; static struct mt352_config dvico_fusionhdtv_dual = { .demod_address = 0x0F, .demod_init = dvico_dual_demod_init, - .pll_set = zarlink_pll_set, }; #ifdef HAVE_VP3054_I2C @@ -247,6 +230,8 @@ static int philips_fmd1216_pll_init(struct dvb_frontend *fe) .buf = fmd1216_init, .len = sizeof(fmd1216_init) }; int err; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) { if (err < 0) return err; @@ -257,14 +242,14 @@ static int philips_fmd1216_pll_init(struct dvb_frontend *fe) return 0; } -static int dntv_live_dvbt_pro_pll_set(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params, - u8* pllbuf) +static int dntv_live_dvbt_pro_tuner_set_params(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params) { struct cx8802_dev *dev= fe->dvb->priv; + u8 buf[4]; struct i2c_msg msg = { .addr = dev->core->pll_addr, .flags = 0, - .buf = pllbuf+1, .len = 4 }; + .buf = buf, .len = 4 }; int err; /* Switch PLL to DVB mode */ @@ -273,14 +258,16 @@ static int dntv_live_dvbt_pro_pll_set(struct dvb_frontend* fe, return err; /* Tune PLL */ - pllbuf[0] = dev->core->pll_addr << 1; - dvb_pll_configure(dev->core->pll_desc, pllbuf+1, + dvb_pll_configure(dev->core->pll_desc, buf, params->frequency, params->u.ofdm.bandwidth); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) { + printk(KERN_WARNING "cx88-dvb: %s error " "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, pllbuf[0], pllbuf[1], err); + __FUNCTION__, dev->core->pll_addr, buf[0], err); if (err < 0) return err; else @@ -294,27 +281,27 @@ static struct mt352_config dntv_live_dvbt_pro_config = { .demod_address = 0x0f, .no_tuner = 1, .demod_init = dntv_live_dvbt_pro_demod_init, - .pll_set = dntv_live_dvbt_pro_pll_set, }; #endif #endif #ifdef HAVE_ZL10353 -static int dvico_hybrid_tune_pll(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params, - u8 *pllbuf) +static int dvico_hybrid_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) { + u8 pllbuf[4]; struct cx8802_dev *dev= fe->dvb->priv; struct i2c_msg msg = { .addr = dev->core->pll_addr, .flags = 0, - .buf = pllbuf + 1, .len = 4 }; + .buf = pllbuf, .len = 4 }; int err; - pllbuf[0] = dev->core->pll_addr << 1; - dvb_pll_configure(dev->core->pll_desc, pllbuf + 1, + dvb_pll_configure(dev->core->pll_desc, pllbuf, params->frequency, params->u.ofdm.bandwidth); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) { printk(KERN_WARNING "cx88-dvb: %s error " "(addr %02x <- %02x, err = %i)\n", @@ -330,12 +317,11 @@ static int dvico_hybrid_tune_pll(struct dvb_frontend *fe, static struct zl10353_config dvico_fusionhdtv_hybrid = { .demod_address = 0x0F, - .pll_set = dvico_hybrid_tune_pll, + .no_tuner = 1, }; static struct zl10353_config dvico_fusionhdtv_plus_v1_1 = { .demod_address = 0x0F, - .pll_set = zarlink_pll_set, }; #endif @@ -343,21 +329,15 @@ static struct zl10353_config dvico_fusionhdtv_plus_v1_1 = { static struct cx22702_config connexant_refboard_config = { .demod_address = 0x43, .output_mode = CX22702_SERIAL_OUTPUT, - .pll_address = 0x60, - .pll_desc = &dvb_pll_thomson_dtt7579, }; static struct cx22702_config hauppauge_novat_config = { .demod_address = 0x43, .output_mode = CX22702_SERIAL_OUTPUT, - .pll_address = 0x61, - .pll_desc = &dvb_pll_thomson_dtt759x, }; static struct cx22702_config hauppauge_hvr1100_config = { .demod_address = 0x63, .output_mode = CX22702_SERIAL_OUTPUT, - .pll_address = 0x61, - .pll_desc = &dvb_pll_fmd1216me, }; #endif @@ -372,15 +352,13 @@ static int or51132_set_ts_param(struct dvb_frontend* fe, static struct or51132_config pchdtv_hd3000 = { .demod_address = 0x15, - .pll_address = 0x61, - .pll_desc = &dvb_pll_thomson_dtt761x, .set_ts_params = or51132_set_ts_param, }; #endif #ifdef HAVE_LGDT330X -static int lgdt330x_pll_set(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) +static int lgdt3302_tuner_set_params(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params) { /* FIXME make this routine use the tuner-simple code. * It could probably be shared with a number of ATSC @@ -393,12 +371,12 @@ static int lgdt330x_pll_set(struct dvb_frontend* fe, { .addr = dev->core->pll_addr, .flags = 0, .buf = buf, .len = 4 }; int err; - /* Put the analog decoder in standby to keep it quiet */ - cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); - dvb_pll_configure(core->pll_desc, buf, params->frequency, 0); dprintk(1, "%s: tuner at 0x%02x bytes: 0x%02x 0x%02x 0x%02x 0x%02x\n", __FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]); + + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if ((err = i2c_transfer(&core->i2c_adap, &msg, 1)) != 1) { printk(KERN_WARNING "cx88-dvb: %s error " "(addr %02x <- %02x, err = %i)\n", @@ -408,16 +386,21 @@ static int lgdt330x_pll_set(struct dvb_frontend* fe, else return -EREMOTEIO; } - if (core->tuner_type == TUNER_LG_TDVS_H062F) { - /* Set the Auxiliary Byte. */ - buf[2] &= ~0x20; - buf[2] |= 0x18; - buf[3] = 0x50; - i2c_transfer(&core->i2c_adap, &msg, 1); - } return 0; } +static int lgdt3303_tuner_set_params(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params) +{ + struct cx8802_dev *dev= fe->dvb->priv; + struct cx88_core *core = dev->core; + + /* Put the analog decoder in standby to keep it quiet */ + cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); + + return lg_h06xf_pll_set(fe, &core->i2c_adap, params); +} + static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index) { struct cx8802_dev *dev= fe->dvb->priv; @@ -445,7 +428,6 @@ static struct lgdt330x_config fusionhdtv_3_gold = { .demod_address = 0x0e, .demod_chip = LGDT3302, .serial_mpeg = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */ - .pll_set = lgdt330x_pll_set, .set_ts_params = lgdt330x_set_ts_param, }; @@ -453,7 +435,6 @@ static struct lgdt330x_config fusionhdtv_5_gold = { .demod_address = 0x0e, .demod_chip = LGDT3303, .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ - .pll_set = lgdt330x_pll_set, .set_ts_params = lgdt330x_set_ts_param, }; @@ -461,7 +442,6 @@ static struct lgdt330x_config pchdtv_hd5500 = { .demod_address = 0x59, .demod_chip = LGDT3303, .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ - .pll_set = lgdt330x_pll_set, .set_ts_params = lgdt330x_set_ts_param, }; #endif @@ -486,8 +466,6 @@ static int nxt200x_set_pll_input(u8* buf, int input) static struct nxt200x_config ati_hdtvwonder = { .demod_address = 0x0a, - .pll_address = 0x61, - .pll_desc = &dvb_pll_tuv1236d, .set_pll_input = nxt200x_set_pll_input, .set_ts_params = nxt200x_set_ts_param, }; @@ -539,6 +517,9 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_HAUPPAUGE_DVB_T1: dev->dvb.frontend = cx22702_attach(&hauppauge_novat_config, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->core->i2c_adap, &dvb_pll_thomson_dtt759x); + } break; case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: case CX88_BOARD_CONEXANT_DVB_T1: @@ -546,44 +527,55 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_WINFAST_DTV1000: dev->dvb.frontend = cx22702_attach(&connexant_refboard_config, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x60, &dev->core->i2c_adap, &dvb_pll_thomson_dtt7579); + } break; case CX88_BOARD_HAUPPAUGE_HVR1100: case CX88_BOARD_HAUPPAUGE_HVR1100LP: dev->dvb.frontend = cx22702_attach(&hauppauge_hvr1100_config, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->core->i2c_adap, &dvb_pll_fmd1216me); + } break; #endif #if defined(HAVE_MT352) || defined(HAVE_ZL10353) case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: - dev->core->pll_addr = 0x60; - dev->core->pll_desc = &dvb_pll_thomson_dtt7579; #ifdef HAVE_MT352 dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv, &dev->core->i2c_adap); - if (dev->dvb.frontend != NULL) + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x60, &dev->core->i2c_adap, &dvb_pll_thomson_dtt7579); break; + } #endif #ifdef HAVE_ZL10353 /* ZL10353 replaces MT352 on later cards */ dev->dvb.frontend = zl10353_attach(&dvico_fusionhdtv_plus_v1_1, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x60, &dev->core->i2c_adap, &dvb_pll_thomson_dtt7579); + } #endif break; #endif /* HAVE_MT352 || HAVE_ZL10353 */ #ifdef HAVE_MT352 case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: - dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_lg_z201; dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->core->i2c_adap, &dvb_pll_lg_z201); + } break; case CX88_BOARD_KWORLD_DVB_T: case CX88_BOARD_DNTV_LIVE_DVB_T: case CX88_BOARD_ADSTECH_DVB_T_PCI: - dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_unknown_1; dev->dvb.frontend = mt352_attach(&dntv_live_dvbt_config, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->core->i2c_adap, &dvb_pll_unknown_1); + } break; case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: #ifdef HAVE_VP3054_I2C @@ -591,6 +583,9 @@ static int dvb_register(struct cx8802_dev *dev) dev->core->pll_desc = &dvb_pll_fmd1216me; dev->dvb.frontend = mt352_attach(&dntv_live_dvbt_pro_config, &((struct vp3054_i2c_state *)dev->card_priv)->adap); + if (dev->dvb.frontend != NULL) { + dev->dvb.frontend->ops->tuner_ops.set_params = dntv_live_dvbt_pro_tuner_set_params; + } #else printk("%s: built without vp3054 support\n", dev->core->name); #endif @@ -598,10 +593,11 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL: /* The tin box says DEE1601, but it seems to be DTT7579 * compatible, with a slightly different MT352 AGC gain. */ - dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_thomson_dtt7579; dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv_dual, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->core->i2c_adap, &dvb_pll_thomson_dtt7579); + } break; #endif #ifdef HAVE_ZL10353 @@ -610,12 +606,18 @@ static int dvb_register(struct cx8802_dev *dev) dev->core->pll_desc = &dvb_pll_thomson_fe6600; dev->dvb.frontend = zl10353_attach(&dvico_fusionhdtv_hybrid, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dev->dvb.frontend->ops->tuner_ops.set_params = dvico_hybrid_tuner_set_params; + } break; #endif #ifdef HAVE_OR51132 case CX88_BOARD_PCHDTV_HD3000: dev->dvb.frontend = or51132_attach(&pchdtv_hd3000, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->core->i2c_adap, &dvb_pll_thomson_dtt761x); + } break; #endif #ifdef HAVE_LGDT330X @@ -636,6 +638,9 @@ static int dvb_register(struct cx8802_dev *dev) dev->core->pll_desc = &dvb_pll_microtune_4042; dev->dvb.frontend = lgdt330x_attach(&fusionhdtv_3_gold, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dev->dvb.frontend->ops->tuner_ops.set_params = lgdt3302_tuner_set_params; + } } break; case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T: @@ -652,6 +657,9 @@ static int dvb_register(struct cx8802_dev *dev) dev->core->pll_desc = &dvb_pll_thomson_dtt761x; dev->dvb.frontend = lgdt330x_attach(&fusionhdtv_3_gold, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dev->dvb.frontend->ops->tuner_ops.set_params = lgdt3302_tuner_set_params; + } } break; case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: @@ -664,10 +672,11 @@ static int dvb_register(struct cx8802_dev *dev) mdelay(100); cx_set(MO_GP0_IO, 1); mdelay(200); - dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_tdvs_tua6034; dev->dvb.frontend = lgdt330x_attach(&fusionhdtv_5_gold, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dev->dvb.frontend->ops->tuner_ops.set_params = lgdt3303_tuner_set_params; + } } break; case CX88_BOARD_PCHDTV_HD5500: @@ -680,10 +689,11 @@ static int dvb_register(struct cx8802_dev *dev) mdelay(100); cx_set(MO_GP0_IO, 1); mdelay(200); - dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_tdvs_tua6034; dev->dvb.frontend = lgdt330x_attach(&pchdtv_hd5500, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dev->dvb.frontend->ops->tuner_ops.set_params = lgdt3303_tuner_set_params; + } } break; #endif @@ -691,6 +701,9 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_ATI_HDTVWONDER: dev->dvb.frontend = nxt200x_attach(&ati_hdtvwonder, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->core->i2c_adap, &dvb_pll_tuv1236d); + } break; #endif #ifdef HAVE_CX24123 diff --git a/linux/drivers/media/video/pwc/Kconfig b/linux/drivers/media/video/pwc/Kconfig index 53cbc950f..697145e0b 100644 --- a/linux/drivers/media/video/pwc/Kconfig +++ b/linux/drivers/media/video/pwc/Kconfig @@ -7,6 +7,7 @@ config USB_PWC * Philips PCA645, PCA646 * Philips PCVC675, PCVC680, PCVC690 * Philips PCVC720/40, PCVC730, PCVC740, PCVC750 + * Philips SPC900NC * Askey VC010 * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro' and 'Orbit'/'Sphere' @@ -19,10 +20,18 @@ config USB_PWC and never will be, but the 665 and 720/20 are supported by other drivers. - See <file:Documentation/usb/philips.txt> for more information and - installation instructions. + Some newer logitech webcams are not handled by this driver but by the + Usb Video Class driver (linux-uvc). The built-in microphone is enabled by selecting USB Audio support. To compile this driver as a module, choose M here: the module will be called pwc. + +config USB_PWC_DEBUG + bool "USB Philips Cameras verbose debug" + depends USB_PWC + help + Say Y here in order to have the pwc driver generate verbose debugging + messages. + A special module options 'trace' is used to control the verbosity. diff --git a/linux/drivers/media/video/pwc/Makefile b/linux/drivers/media/video/pwc/Makefile index 33d60126c..9db2260d1 100644 --- a/linux/drivers/media/video/pwc/Makefile +++ b/linux/drivers/media/video/pwc/Makefile @@ -1,3 +1,12 @@ -pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o pwc-timon.o pwc-kiara.o +pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-v4l.o pwc-uncompress.o +pwc-objs += pwc-dec1.o pwc-dec23.o pwc-kiara.o pwc-timon.o obj-$(CONFIG_USB_PWC) += pwc.o + +ifeq ($(CONFIG_USB_PWC_DEBUG),y) +EXTRA_CFLAGS += -DCONFIG_PWC_DEBUG=1 +else +EXTRA_CFLAGS += -DCONFIG_PWC_DEBUG=0 +endif + + diff --git a/linux/drivers/media/video/pwc/pwc-ctrl.c b/linux/drivers/media/video/pwc/pwc-ctrl.c index 4ba549bfa..c64f498f1 100644 --- a/linux/drivers/media/video/pwc/pwc-ctrl.c +++ b/linux/drivers/media/video/pwc/pwc-ctrl.c @@ -2,7 +2,7 @@ Functions that send various control messages to the webcam, including video modes. (C) 1999-2003 Nemosoft Unv. - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -41,12 +41,14 @@ #include <asm/uaccess.h> #endif #include <asm/errno.h> +#include <linux/version.h> #include "pwc.h" -#include "pwc-ioctl.h" #include "pwc-uncompress.h" #include "pwc-kiara.h" #include "pwc-timon.h" +#include "pwc-dec1.h" +#include "pwc-dec23.h" /* Request types: video */ #define SET_LUM_CTL 0x01 @@ -57,6 +59,10 @@ #define GET_STATUS_CTL 0x06 #define SET_EP_STREAM_CTL 0x07 #define GET_EP_STREAM_CTL 0x08 +#define GET_XX_CTL 0x09 +#define SET_XX_CTL 0x0A +#define GET_XY_CTL 0x0B +#define SET_XY_CTL 0x0C #define SET_MPT_CTL 0x0D #define GET_MPT_CTL 0x0E @@ -93,12 +99,20 @@ #define READ_SHUTTER_FORMATTER 0x0600 #define READ_RED_GAIN_FORMATTER 0x0700 #define READ_BLUE_GAIN_FORMATTER 0x0800 +#define GET_STATUS_B00 0x0B00 #define SENSOR_TYPE_FORMATTER1 0x0C00 +#define GET_STATUS_3000 0x3000 #define READ_RAW_Y_MEAN_FORMATTER 0x3100 #define SET_POWER_SAVE_MODE_FORMATTER 0x3200 #define MIRROR_IMAGE_FORMATTER 0x3300 #define LED_FORMATTER 0x3400 +#define LOWLIGHT 0x3500 +#define GET_STATUS_3600 0x3600 #define SENSOR_TYPE_FORMATTER2 0x3700 +#define GET_STATUS_3800 0x3800 +#define GET_STATUS_4000 0x4000 +#define GET_STATUS_4100 0x4100 /* Get */ +#define CTL_STATUS_4200 0x4200 /* [GS] 1 */ /* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ #define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 @@ -138,6 +152,7 @@ static struct Nala_table_entry Nala_table[PSZ_MAX][8] = #include "pwc-nala.h" }; +static void pwc_set_image_buffer_size(struct pwc_device *pdev); /****************************************************************************/ @@ -159,31 +174,7 @@ static struct Nala_table_entry Nala_table[PSZ_MAX][8] = &buf, buflen, 500) -#if PWC_DEBUG -void pwc_hexdump(void *p, int len) -{ - int i; - unsigned char *s; - char buf[100], *d; - - s = (unsigned char *)p; - d = buf; - *d = '\0'; - Debug("Doing hexdump @ %p, %d bytes.\n", p, len); - for (i = 0; i < len; i++) { - d += sprintf(d, "%02X ", *s++); - if ((i & 0xF) == 0xF) { - Debug("%s\n", buf); - d = buf; - *d = '\0'; - } - } - if ((i & 0xF) != 0) - Debug("%s\n", buf); -} -#endif - -static inline int send_video_command(struct usb_device *udev, int index, void *buf, int buflen) +static int send_video_command(struct usb_device *udev, int index, void *buf, int buflen) { return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), @@ -196,7 +187,7 @@ static inline int send_video_command(struct usb_device *udev, int index, void *b -static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) +static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) { unsigned char buf[3]; int ret, fps; @@ -229,34 +220,14 @@ static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int fra if (pEntry->alternate == 0) return -EINVAL; - if (pEntry->compressed) - return -ENOENT; /* Not supported. */ - memcpy(buf, pEntry->mode, 3); ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3); if (ret < 0) { - Debug("Failed to send video command... %d\n", ret); + PWC_DEBUG_MODULE("Failed to send video command... %d\n", ret); return ret; } if (pEntry->compressed && pdev->vpalette != VIDEO_PALETTE_RAW) - { - switch(pdev->type) { - case 645: - case 646: -/* pwc_dec1_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ - break; - - case 675: - case 680: - case 690: - case 720: - case 730: - case 740: - case 750: -/* pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ - break; - } - } + pwc_dec1_init(pdev->type, pdev->release, buf, pdev->decompress_data); pdev->cmd_len = 3; memcpy(pdev->cmd_buf, buf, 3); @@ -283,7 +254,7 @@ static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int fra } -static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) +static int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) { unsigned char buf[13]; const struct Timon_table_entry *pChoose; @@ -315,8 +286,8 @@ static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int fr if (ret < 0) return ret; -/* if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) - pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ + if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) + pwc_dec23_init(pdev, pdev->type, buf); pdev->cmd_len = 13; memcpy(pdev->cmd_buf, buf, 13); @@ -336,7 +307,7 @@ static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int fr } -static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) +static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) { const struct Kiara_table_entry *pChoose = NULL; int fps, ret; @@ -350,21 +321,14 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr fps = (frames / 5) - 1; /* special case: VGA @ 5 fps and snapshot is raw bayer mode */ - if (size == PSZ_VGA && frames == 5 && snapshot) + if (size == PSZ_VGA && frames == 5 && snapshot && pdev->vpalette == VIDEO_PALETTE_RAW) { /* Only available in case the raw palette is selected or we have the decompressor available. This mode is only available in compressed form */ - if (pdev->vpalette == VIDEO_PALETTE_RAW) - { - Info("Choosing VGA/5 BAYER mode (%d).\n", pdev->vpalette); - pChoose = &RawEntry; - } - else - { - Info("VGA/5 BAYER mode _must_ have a decompressor available, or use RAW palette.\n"); - } + PWC_DEBUG_SIZE("Choosing VGA/5 BAYER mode.\n"); + pChoose = &RawEntry; } else { @@ -372,6 +336,7 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr if the preferred ratio is not available. Skip this step when using RAW modes. */ + snapshot = 0; while (compression <= 3) { pChoose = &Kiara_table[size][fps][compression]; if (pChoose->alternate != 0) @@ -382,7 +347,7 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr if (pChoose == NULL || pChoose->alternate == 0) return -ENOENT; /* Not supported. */ - Debug("Using alternate setting %d.\n", pChoose->alternate); + PWC_TRACE("Using alternate setting %d.\n", pChoose->alternate); /* usb_control_msg won't take staticly allocated arrays as argument?? */ memcpy(buf, pChoose->mode, 12); @@ -394,8 +359,8 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr if (ret < 0) return ret; -/* if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) - pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ + if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) + pwc_dec23_init(pdev, pdev->type, buf); pdev->cmd_len = 12; memcpy(pdev->cmd_buf, buf, 12); @@ -410,49 +375,13 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4; else pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; + PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vsnapshot=%d, vbandlength=%d\n", + pdev->frame_size,pdev->vframes,pdev->vsize,pdev->vsnapshot,pdev->vbandlength); return 0; } -static void pwc_set_image_buffer_size(struct pwc_device *pdev) -{ - int i, factor = 0, filler = 0; - - /* for PALETTE_YUV420P */ - switch(pdev->vpalette) - { - case VIDEO_PALETTE_YUV420P: - factor = 6; - filler = 128; - break; - case VIDEO_PALETTE_RAW: - factor = 6; /* can be uncompressed YUV420P */ - filler = 0; - break; - } - - /* Set sizes in bytes */ - pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; - pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; - - /* Align offset, or you'll get some very weird results in - YUV420 mode... x must be multiple of 4 (to get the Y's in - place), and y even (or you'll mixup U & V). This is less of a - problem for YUV420P. - */ - pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; - pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; - - /* Fill buffers with gray or black */ - for (i = 0; i < MAX_IMAGES; i++) { - if (pdev->image_ptr[i] != NULL) - memset(pdev->image_ptr[i], filler, pdev->view.size); - } -} - - - /** @pdev: device structure @width: viewport width @@ -465,50 +394,78 @@ int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frame { int ret, size; - Trace(TRACE_FLOW, "set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette); + PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette); size = pwc_decode_size(pdev, width, height); if (size < 0) { - Debug("Could not find suitable size.\n"); + PWC_DEBUG_MODULE("Could not find suitable size.\n"); return -ERANGE; } - Debug("decode_size = %d.\n", size); + PWC_TRACE("decode_size = %d.\n", size); - ret = -EINVAL; - switch(pdev->type) { - case 645: - case 646: + if (DEVICE_USE_CODEC1(pdev->type)) { ret = set_video_mode_Nala(pdev, size, frames); - break; - case 675: - case 680: - case 690: - ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); - break; - - case 720: - case 730: - case 740: - case 750: + } else if (DEVICE_USE_CODEC3(pdev->type)) { ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot); - break; + + } else { + ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); } if (ret < 0) { - if (ret == -ENOENT) - Info("Video mode %s@%d fps is only supported with the decompressor module (pwcx).\n", size2name[size], frames); - else { - Err("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); - } + PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); return ret; } pdev->view.x = width; pdev->view.y = height; pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; pwc_set_image_buffer_size(pdev); - Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y); + PWC_DEBUG_SIZE("Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y); return 0; } +#define BLACK_Y 0 +#define BLACK_U 128 +#define BLACK_V 128 + +static void pwc_set_image_buffer_size(struct pwc_device *pdev) +{ + int i, factor = 0; + + /* for PALETTE_YUV420P */ + switch(pdev->vpalette) + { + case VIDEO_PALETTE_YUV420P: + factor = 6; + break; + case VIDEO_PALETTE_RAW: + factor = 6; /* can be uncompressed YUV420P */ + break; + } + + /* Set sizes in bytes */ + pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; + pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; + + /* Align offset, or you'll get some very weird results in + YUV420 mode... x must be multiple of 4 (to get the Y's in + place), and y even (or you'll mixup U & V). This is less of a + problem for YUV420P. + */ + pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; + pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; + + /* Fill buffers with black colors */ + for (i = 0; i < pwc_mbufs; i++) { + unsigned char *p = pdev->image_data + pdev->images[i].offset; + memset(p, BLACK_Y, pdev->view.x * pdev->view.y); + p += pdev->view.x * pdev->view.y; + memset(p, BLACK_U, pdev->view.x * pdev->view.y/4); + p += pdev->view.x * pdev->view.y/4; + memset(p, BLACK_V, pdev->view.x * pdev->view.y/4); + } +} + + /* BRIGHTNESS */ @@ -520,7 +477,7 @@ int pwc_get_brightness(struct pwc_device *pdev) ret = RecvControlMsg(GET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); if (ret < 0) return ret; - return buf << 9; + return buf; } int pwc_set_brightness(struct pwc_device *pdev, int value) @@ -545,7 +502,7 @@ int pwc_get_contrast(struct pwc_device *pdev) ret = RecvControlMsg(GET_LUM_CTL, CONTRAST_FORMATTER, 1); if (ret < 0) return ret; - return buf << 10; + return buf; } int pwc_set_contrast(struct pwc_device *pdev, int value) @@ -570,7 +527,7 @@ int pwc_get_gamma(struct pwc_device *pdev) ret = RecvControlMsg(GET_LUM_CTL, GAMMA_FORMATTER, 1); if (ret < 0) return ret; - return buf << 11; + return buf; } int pwc_set_gamma(struct pwc_device *pdev, int value) @@ -588,37 +545,47 @@ int pwc_set_gamma(struct pwc_device *pdev, int value) /* SATURATION */ -int pwc_get_saturation(struct pwc_device *pdev) +/* return a value between [-100 , 100] */ +int pwc_get_saturation(struct pwc_device *pdev, int *value) { char buf; - int ret; + int ret, saturation_register; if (pdev->type < 675) - return -1; - ret = RecvControlMsg(GET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); + return -EINVAL; + if (pdev->type < 730) + saturation_register = SATURATION_MODE_FORMATTER2; + else + saturation_register = SATURATION_MODE_FORMATTER1; + ret = RecvControlMsg(GET_CHROM_CTL, saturation_register, 1); if (ret < 0) return ret; - return 32768 + buf * 327; + *value = (signed)buf; + return 0; } +/* @param value saturation color between [-100 , 100] */ int pwc_set_saturation(struct pwc_device *pdev, int value) { char buf; + int saturation_register; if (pdev->type < 675) return -EINVAL; - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - /* saturation ranges from -100 to +100 */ - buf = (value - 32768) / 327; - return SendControlMsg(SET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); + if (value < -100) + value = -100; + if (value > 100) + value = 100; + if (pdev->type < 730) + saturation_register = SATURATION_MODE_FORMATTER2; + else + saturation_register = SATURATION_MODE_FORMATTER1; + return SendControlMsg(SET_CHROM_CTL, saturation_register, 1); } /* AGC */ -static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value) +int pwc_set_agc(struct pwc_device *pdev, int mode, int value) { char buf; int ret; @@ -643,7 +610,7 @@ static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value) return 0; } -static inline int pwc_get_agc(struct pwc_device *pdev, int *value) +int pwc_get_agc(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; @@ -673,7 +640,7 @@ static inline int pwc_get_agc(struct pwc_device *pdev, int *value) return 0; } -static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) +int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) { char buf[2]; int speed, ret; @@ -691,23 +658,16 @@ static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int v value = 0; if (value > 0xffff) value = 0xffff; - switch(pdev->type) { - case 675: - case 680: - case 690: + + if (DEVICE_USE_CODEC2(pdev->type)) { /* speed ranges from 0x0 to 0x290 (656) */ speed = (value / 100); buf[1] = speed >> 8; buf[0] = speed & 0xff; - break; - case 720: - case 730: - case 740: - case 750: + } else if (DEVICE_USE_CODEC3(pdev->type)) { /* speed seems to range from 0x0 to 0xff */ buf[1] = 0; buf[0] = value >> 8; - break; } ret = SendControlMsg(SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, 2); @@ -715,6 +675,25 @@ static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int v return ret; } +/* This function is not exported to v4l1, so output values between 0 -> 256 */ +int pwc_get_shutter_speed(struct pwc_device *pdev, int *value) +{ + unsigned char buf[2]; + int ret; + + ret = RecvControlMsg(GET_STATUS_CTL, READ_SHUTTER_FORMATTER, 2); + if (ret < 0) + return ret; + *value = buf[0] + (buf[1] << 8); + if (DEVICE_USE_CODEC2(pdev->type)) { + /* speed ranges from 0x0 to 0x290 (656) */ + *value *= 256/656; + } else if (DEVICE_USE_CODEC3(pdev->type)) { + /* speed seems to range from 0x0 to 0xff */ + } + return 0; +} + /* POWER */ @@ -736,19 +715,19 @@ int pwc_camera_power(struct pwc_device *pdev, int power) /* private calls */ -static inline int pwc_restore_user(struct pwc_device *pdev) +int pwc_restore_user(struct pwc_device *pdev) { char buf; /* dummy */ return SendControlMsg(SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, 0); } -static inline int pwc_save_user(struct pwc_device *pdev) +int pwc_save_user(struct pwc_device *pdev) { char buf; /* dummy */ return SendControlMsg(SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, 0); } -static inline int pwc_restore_factory(struct pwc_device *pdev) +int pwc_restore_factory(struct pwc_device *pdev) { char buf; /* dummy */ return SendControlMsg(SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, 0); @@ -766,7 +745,7 @@ static inline int pwc_restore_factory(struct pwc_device *pdev) * 03: manual * 04: auto */ -static inline int pwc_set_awb(struct pwc_device *pdev, int mode) +int pwc_set_awb(struct pwc_device *pdev, int mode) { char buf; int ret; @@ -786,7 +765,7 @@ static inline int pwc_set_awb(struct pwc_device *pdev, int mode) return 0; } -static inline int pwc_get_awb(struct pwc_device *pdev) +int pwc_get_awb(struct pwc_device *pdev) { unsigned char buf; int ret; @@ -798,7 +777,7 @@ static inline int pwc_get_awb(struct pwc_device *pdev) return buf; } -static inline int pwc_set_red_gain(struct pwc_device *pdev, int value) +int pwc_set_red_gain(struct pwc_device *pdev, int value) { unsigned char buf; @@ -811,7 +790,7 @@ static inline int pwc_set_red_gain(struct pwc_device *pdev, int value) return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); } -static inline int pwc_get_red_gain(struct pwc_device *pdev, int *value) +int pwc_get_red_gain(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; @@ -824,7 +803,7 @@ static inline int pwc_get_red_gain(struct pwc_device *pdev, int *value) } -static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value) +int pwc_set_blue_gain(struct pwc_device *pdev, int value) { unsigned char buf; @@ -837,7 +816,7 @@ static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value) return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); } -static inline int pwc_get_blue_gain(struct pwc_device *pdev, int *value) +int pwc_get_blue_gain(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; @@ -854,7 +833,7 @@ static inline int pwc_get_blue_gain(struct pwc_device *pdev, int *value) internal red/blue gains, which may be different from the manual gains set or read above. */ -static inline int pwc_read_red_gain(struct pwc_device *pdev, int *value) +static int pwc_read_red_gain(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; @@ -866,7 +845,7 @@ static inline int pwc_read_red_gain(struct pwc_device *pdev, int *value) return 0; } -static inline int pwc_read_blue_gain(struct pwc_device *pdev, int *value) +static int pwc_read_blue_gain(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; @@ -879,7 +858,7 @@ static inline int pwc_read_blue_gain(struct pwc_device *pdev, int *value) } -static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed) +static int pwc_set_wb_speed(struct pwc_device *pdev, int speed) { unsigned char buf; @@ -888,7 +867,7 @@ static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed) return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); } -static inline int pwc_get_wb_speed(struct pwc_device *pdev, int *value) +static int pwc_get_wb_speed(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; @@ -901,7 +880,7 @@ static inline int pwc_get_wb_speed(struct pwc_device *pdev, int *value) } -static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay) +static int pwc_set_wb_delay(struct pwc_device *pdev, int delay) { unsigned char buf; @@ -910,7 +889,7 @@ static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay) return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); } -static inline int pwc_get_wb_delay(struct pwc_device *pdev, int *value) +static int pwc_get_wb_delay(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; @@ -946,7 +925,7 @@ int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2); } -static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) +int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) { unsigned char buf[2]; int ret; @@ -965,7 +944,7 @@ static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) return 0; } -static inline int pwc_set_contour(struct pwc_device *pdev, int contour) +int pwc_set_contour(struct pwc_device *pdev, int contour) { unsigned char buf; int ret; @@ -990,7 +969,7 @@ static inline int pwc_set_contour(struct pwc_device *pdev, int contour) return 0; } -static inline int pwc_get_contour(struct pwc_device *pdev, int *contour) +int pwc_get_contour(struct pwc_device *pdev, int *contour) { unsigned char buf; int ret; @@ -1012,7 +991,7 @@ static inline int pwc_get_contour(struct pwc_device *pdev, int *contour) } -static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight) +int pwc_set_backlight(struct pwc_device *pdev, int backlight) { unsigned char buf; @@ -1023,7 +1002,7 @@ static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight) return SendControlMsg(SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); } -static inline int pwc_get_backlight(struct pwc_device *pdev, int *backlight) +int pwc_get_backlight(struct pwc_device *pdev, int *backlight) { int ret; unsigned char buf; @@ -1031,12 +1010,35 @@ static inline int pwc_get_backlight(struct pwc_device *pdev, int *backlight) ret = RecvControlMsg(GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); if (ret < 0) return ret; - *backlight = buf; + *backlight = !!buf; + return 0; +} + +int pwc_set_colour_mode(struct pwc_device *pdev, int colour) +{ + unsigned char buf; + + if (colour) + buf = 0xff; + else + buf = 0x0; + return SendControlMsg(SET_CHROM_CTL, COLOUR_MODE_FORMATTER, 1); +} + +int pwc_get_colour_mode(struct pwc_device *pdev, int *colour) +{ + int ret; + unsigned char buf; + + ret = RecvControlMsg(GET_CHROM_CTL, COLOUR_MODE_FORMATTER, 1); + if (ret < 0) + return ret; + *colour = !!buf; return 0; } -static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker) +int pwc_set_flicker(struct pwc_device *pdev, int flicker) { unsigned char buf; @@ -1047,7 +1049,7 @@ static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker) return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); } -static inline int pwc_get_flicker(struct pwc_device *pdev, int *flicker) +int pwc_get_flicker(struct pwc_device *pdev, int *flicker) { int ret; unsigned char buf; @@ -1055,12 +1057,11 @@ static inline int pwc_get_flicker(struct pwc_device *pdev, int *flicker) ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); if (ret < 0) return ret; - *flicker = buf; + *flicker = !!buf; return 0; } - -static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) +int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) { unsigned char buf; @@ -1072,7 +1073,7 @@ static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); } -static inline int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) +int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) { int ret; unsigned char buf; @@ -1084,7 +1085,7 @@ static inline int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) return 0; } -static int pwc_mpt_reset(struct pwc_device *pdev, int flags) +static int _pwc_mpt_reset(struct pwc_device *pdev, int flags) { unsigned char buf; @@ -1092,7 +1093,18 @@ static int pwc_mpt_reset(struct pwc_device *pdev, int flags) return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1); } -static inline int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) +int pwc_mpt_reset(struct pwc_device *pdev, int flags) +{ + int ret; + ret = _pwc_mpt_reset(pdev, flags); + if (ret >= 0) { + pdev->pan_angle = 0; + pdev->tilt_angle = 0; + } + return ret; +} + +static int _pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) { unsigned char buf[4]; @@ -1110,7 +1122,35 @@ static inline int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) return SendControlMsg(SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, 4); } -static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status) +int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) +{ + int ret; + + /* check absolute ranges */ + if (pan < pdev->angle_range.pan_min || + pan > pdev->angle_range.pan_max || + tilt < pdev->angle_range.tilt_min || + tilt > pdev->angle_range.tilt_max) + return -ERANGE; + + /* go to relative range, check again */ + pan -= pdev->pan_angle; + tilt -= pdev->tilt_angle; + /* angles are specified in degrees * 100, thus the limit = 36000 */ + if (pan < -36000 || pan > 36000 || tilt < -36000 || tilt > 36000) + return -ERANGE; + + ret = _pwc_mpt_set_angle(pdev, pan, tilt); + if (ret >= 0) { + pdev->pan_angle += pan; + pdev->tilt_angle += tilt; + } + if (ret == -EPIPE) /* stall -> out of range */ + ret = -ERANGE; + return ret; +} + +static int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status) { int ret; unsigned char buf[5]; @@ -1151,6 +1191,45 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) /* End of Add-Ons */ /* ************************************************* */ +/* Linux 2.5.something and 2.6 pass direct pointers to arguments of + ioctl() calls. With 2.4, you have to do tedious copy_from_user() + and copy_to_user() calls. With these macros we circumvent this, + and let me maintain only one source file. The functionality is + exactly the same otherwise. + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + +/* define local variable for arg */ +#define ARG_DEF(ARG_type, ARG_name)\ + ARG_type *ARG_name = arg; +/* copy arg to local variable */ +#define ARG_IN(ARG_name) /* nothing */ +/* argument itself (referenced) */ +#define ARGR(ARG_name) (*ARG_name) +/* argument address */ +#define ARGA(ARG_name) ARG_name +/* copy local variable to arg */ +#define ARG_OUT(ARG_name) /* nothing */ + +#else + +#define ARG_DEF(ARG_type, ARG_name)\ + ARG_type ARG_name; +#define ARG_IN(ARG_name)\ + if (copy_from_user(&ARG_name, arg, sizeof(ARG_name))) {\ + ret = -EFAULT;\ + break;\ + } +#define ARGR(ARG_name) ARG_name +#define ARGA(ARG_name) &ARG_name +#define ARG_OUT(ARG_name)\ + if (copy_to_user(arg, &ARG_name, sizeof(ARG_name))) {\ + ret = -EFAULT;\ + break;\ + } + +#endif int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) { @@ -1180,206 +1259,243 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) case VIDIOCPWCSCQUAL: { - int *qual = arg; + ARG_DEF(int, qual) - if (*qual < 0 || *qual > 3) + ARG_IN(qual) + if (ARGR(qual) < 0 || ARGR(qual) > 3) ret = -EINVAL; else - ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, *qual, pdev->vsnapshot); + ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot); if (ret >= 0) - pdev->vcompression = *qual; + pdev->vcompression = ARGR(qual); break; } case VIDIOCPWCGCQUAL: { - int *qual = arg; - *qual = pdev->vcompression; + ARG_DEF(int, qual) + + ARGR(qual) = pdev->vcompression; + ARG_OUT(qual) break; } case VIDIOCPWCPROBE: { - struct pwc_probe *probe = arg; - strcpy(probe->name, pdev->vdev->name); - probe->type = pdev->type; + ARG_DEF(struct pwc_probe, probe) + + strcpy(ARGR(probe).name, pdev->vdev->name); + ARGR(probe).type = pdev->type; + ARG_OUT(probe) break; } case VIDIOCPWCGSERIAL: { - struct pwc_serial *serial = arg; - strcpy(serial->serial, pdev->serial); + ARG_DEF(struct pwc_serial, serial) + + strcpy(ARGR(serial).serial, pdev->serial); + ARG_OUT(serial) break; } case VIDIOCPWCSAGC: { - int *agc = arg; - if (pwc_set_agc(pdev, *agc < 0 ? 1 : 0, *agc)) + ARG_DEF(int, agc) + + ARG_IN(agc) + if (pwc_set_agc(pdev, ARGR(agc) < 0 ? 1 : 0, ARGR(agc))) ret = -EINVAL; break; } case VIDIOCPWCGAGC: { - int *agc = arg; + ARG_DEF(int, agc) - if (pwc_get_agc(pdev, agc)) + if (pwc_get_agc(pdev, ARGA(agc))) ret = -EINVAL; + ARG_OUT(agc) break; } case VIDIOCPWCSSHUTTER: { - int *shutter_speed = arg; - ret = pwc_set_shutter_speed(pdev, *shutter_speed < 0 ? 1 : 0, *shutter_speed); + ARG_DEF(int, shutter_speed) + + ARG_IN(shutter_speed) + ret = pwc_set_shutter_speed(pdev, ARGR(shutter_speed) < 0 ? 1 : 0, ARGR(shutter_speed)); break; } case VIDIOCPWCSAWB: { - struct pwc_whitebalance *wb = arg; + ARG_DEF(struct pwc_whitebalance, wb) - ret = pwc_set_awb(pdev, wb->mode); - if (ret >= 0 && wb->mode == PWC_WB_MANUAL) { - pwc_set_red_gain(pdev, wb->manual_red); - pwc_set_blue_gain(pdev, wb->manual_blue); + ARG_IN(wb) + ret = pwc_set_awb(pdev, ARGR(wb).mode); + if (ret >= 0 && ARGR(wb).mode == PWC_WB_MANUAL) { + pwc_set_red_gain(pdev, ARGR(wb).manual_red); + pwc_set_blue_gain(pdev, ARGR(wb).manual_blue); } break; } case VIDIOCPWCGAWB: { - struct pwc_whitebalance *wb = arg; + ARG_DEF(struct pwc_whitebalance, wb) - memset(wb, 0, sizeof(struct pwc_whitebalance)); - wb->mode = pwc_get_awb(pdev); - if (wb->mode < 0) + memset(ARGA(wb), 0, sizeof(struct pwc_whitebalance)); + ARGR(wb).mode = pwc_get_awb(pdev); + if (ARGR(wb).mode < 0) ret = -EINVAL; else { - if (wb->mode == PWC_WB_MANUAL) { - ret = pwc_get_red_gain(pdev, &wb->manual_red); + if (ARGR(wb).mode == PWC_WB_MANUAL) { + ret = pwc_get_red_gain(pdev, &ARGR(wb).manual_red); if (ret < 0) break; - ret = pwc_get_blue_gain(pdev, &wb->manual_blue); + ret = pwc_get_blue_gain(pdev, &ARGR(wb).manual_blue); if (ret < 0) break; } - if (wb->mode == PWC_WB_AUTO) { - ret = pwc_read_red_gain(pdev, &wb->read_red); + if (ARGR(wb).mode == PWC_WB_AUTO) { + ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red); if (ret < 0) break; - ret = pwc_read_blue_gain(pdev, &wb->read_blue); + ret =pwc_read_blue_gain(pdev, &ARGR(wb).read_blue); if (ret < 0) break; } } + ARG_OUT(wb) break; } case VIDIOCPWCSAWBSPEED: { - struct pwc_wb_speed *wbs = arg; + ARG_DEF(struct pwc_wb_speed, wbs) - if (wbs->control_speed > 0) { - ret = pwc_set_wb_speed(pdev, wbs->control_speed); + if (ARGR(wbs).control_speed > 0) { + ret = pwc_set_wb_speed(pdev, ARGR(wbs).control_speed); } - if (wbs->control_delay > 0) { - ret = pwc_set_wb_delay(pdev, wbs->control_delay); + if (ARGR(wbs).control_delay > 0) { + ret = pwc_set_wb_delay(pdev, ARGR(wbs).control_delay); } break; } case VIDIOCPWCGAWBSPEED: { - struct pwc_wb_speed *wbs = arg; + ARG_DEF(struct pwc_wb_speed, wbs) - ret = pwc_get_wb_speed(pdev, &wbs->control_speed); + ret = pwc_get_wb_speed(pdev, &ARGR(wbs).control_speed); if (ret < 0) break; - ret = pwc_get_wb_delay(pdev, &wbs->control_delay); + ret = pwc_get_wb_delay(pdev, &ARGR(wbs).control_delay); if (ret < 0) break; + ARG_OUT(wbs) break; } case VIDIOCPWCSLED: { - struct pwc_leds *leds = arg; - ret = pwc_set_leds(pdev, leds->led_on, leds->led_off); + ARG_DEF(struct pwc_leds, leds) + + ARG_IN(leds) + ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off); break; } case VIDIOCPWCGLED: { - struct pwc_leds *leds = arg; - ret = pwc_get_leds(pdev, &leds->led_on, &leds->led_off); + ARG_DEF(struct pwc_leds, leds) + + ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off); + ARG_OUT(leds) break; } case VIDIOCPWCSCONTOUR: { - int *contour = arg; - ret = pwc_set_contour(pdev, *contour); + ARG_DEF(int, contour) + + ARG_IN(contour) + ret = pwc_set_contour(pdev, ARGR(contour)); break; } case VIDIOCPWCGCONTOUR: { - int *contour = arg; - ret = pwc_get_contour(pdev, contour); + ARG_DEF(int, contour) + + ret = pwc_get_contour(pdev, ARGA(contour)); + ARG_OUT(contour) break; } case VIDIOCPWCSBACKLIGHT: { - int *backlight = arg; - ret = pwc_set_backlight(pdev, *backlight); + ARG_DEF(int, backlight) + + ARG_IN(backlight) + ret = pwc_set_backlight(pdev, ARGR(backlight)); break; } case VIDIOCPWCGBACKLIGHT: { - int *backlight = arg; - ret = pwc_get_backlight(pdev, backlight); + ARG_DEF(int, backlight) + + ret = pwc_get_backlight(pdev, ARGA(backlight)); + ARG_OUT(backlight) break; } case VIDIOCPWCSFLICKER: { - int *flicker = arg; - ret = pwc_set_flicker(pdev, *flicker); + ARG_DEF(int, flicker) + + ARG_IN(flicker) + ret = pwc_set_flicker(pdev, ARGR(flicker)); break; } case VIDIOCPWCGFLICKER: { - int *flicker = arg; - ret = pwc_get_flicker(pdev, flicker); + ARG_DEF(int, flicker) + + ret = pwc_get_flicker(pdev, ARGA(flicker)); + ARG_OUT(flicker) break; } case VIDIOCPWCSDYNNOISE: { - int *dynnoise = arg; - ret = pwc_set_dynamic_noise(pdev, *dynnoise); + ARG_DEF(int, dynnoise) + + ARG_IN(dynnoise) + ret = pwc_set_dynamic_noise(pdev, ARGR(dynnoise)); break; } case VIDIOCPWCGDYNNOISE: { - int *dynnoise = arg; - ret = pwc_get_dynamic_noise(pdev, dynnoise); + ARG_DEF(int, dynnoise) + + ret = pwc_get_dynamic_noise(pdev, ARGA(dynnoise)); + ARG_OUT(dynnoise); break; } case VIDIOCPWCGREALSIZE: { - struct pwc_imagesize *size = arg; - size->width = pdev->image.x; - size->height = pdev->image.y; + ARG_DEF(struct pwc_imagesize, size) + + ARGR(size).width = pdev->image.x; + ARGR(size).height = pdev->image.y; + ARG_OUT(size) break; } @@ -1387,14 +1503,10 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) { if (pdev->features & FEATURE_MOTOR_PANTILT) { - int *flags = arg; + ARG_DEF(int, flags) - ret = pwc_mpt_reset(pdev, *flags); - if (ret >= 0) - { - pdev->pan_angle = 0; - pdev->tilt_angle = 0; - } + ARG_IN(flags) + ret = pwc_mpt_reset(pdev, ARGR(flags)); } else { @@ -1407,8 +1519,10 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) { if (pdev->features & FEATURE_MOTOR_PANTILT) { - struct pwc_mpt_range *range = arg; - *range = pdev->angle_range; + ARG_DEF(struct pwc_mpt_range, range) + + ARGR(range) = pdev->angle_range; + ARG_OUT(range) } else { @@ -1423,48 +1537,23 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) if (pdev->features & FEATURE_MOTOR_PANTILT) { - struct pwc_mpt_angles *angles = arg; + ARG_DEF(struct pwc_mpt_angles, angles) + + ARG_IN(angles) /* The camera can only set relative angles, so do some calculations when getting an absolute angle . */ - if (angles->absolute) + if (ARGR(angles).absolute) { - new_pan = angles->pan; - new_tilt = angles->tilt; + new_pan = ARGR(angles).pan; + new_tilt = ARGR(angles).tilt; } else { - new_pan = pdev->pan_angle + angles->pan; - new_tilt = pdev->tilt_angle + angles->tilt; - } - /* check absolute ranges */ - if (new_pan < pdev->angle_range.pan_min || - new_pan > pdev->angle_range.pan_max || - new_tilt < pdev->angle_range.tilt_min || - new_tilt > pdev->angle_range.tilt_max) - { - ret = -ERANGE; - } - else - { - /* go to relative range, check again */ - new_pan -= pdev->pan_angle; - new_tilt -= pdev->tilt_angle; - /* angles are specified in degrees * 100, thus the limit = 36000 */ - if (new_pan < -36000 || new_pan > 36000 || new_tilt < -36000 || new_tilt > 36000) - ret = -ERANGE; - } - if (ret == 0) /* no errors so far */ - { - ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt); - if (ret >= 0) - { - pdev->pan_angle += new_pan; - pdev->tilt_angle += new_tilt; - } - if (ret == -EPIPE) /* stall -> out of range */ - ret = -ERANGE; + new_pan = pdev->pan_angle + ARGR(angles).pan; + new_tilt = pdev->tilt_angle + ARGR(angles).tilt; } + ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt); } else { @@ -1478,11 +1567,12 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) if (pdev->features & FEATURE_MOTOR_PANTILT) { - struct pwc_mpt_angles *angles = arg; + ARG_DEF(struct pwc_mpt_angles, angles) - angles->absolute = 1; - angles->pan = pdev->pan_angle; - angles->tilt = pdev->tilt_angle; + ARGR(angles).absolute = 1; + ARGR(angles).pan = pdev->pan_angle; + ARGR(angles).tilt = pdev->tilt_angle; + ARG_OUT(angles) } else { @@ -1495,8 +1585,10 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) { if (pdev->features & FEATURE_MOTOR_PANTILT) { - struct pwc_mpt_status *status = arg; - ret = pwc_mpt_get_status(pdev, status); + ARG_DEF(struct pwc_mpt_status, status) + + ret = pwc_mpt_get_status(pdev, ARGA(status)); + ARG_OUT(status) } else { @@ -1507,22 +1599,24 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) case VIDIOCPWCGVIDCMD: { - struct pwc_video_command *cmd = arg; - - cmd->type = pdev->type; - cmd->release = pdev->release; - cmd->command_len = pdev->cmd_len; - memcpy(&cmd->command_buf, pdev->cmd_buf, pdev->cmd_len); - cmd->bandlength = pdev->vbandlength; - cmd->frame_size = pdev->frame_size; + ARG_DEF(struct pwc_video_command, cmd); + + ARGR(cmd).type = pdev->type; + ARGR(cmd).release = pdev->release; + ARGR(cmd).command_len = pdev->cmd_len; + memcpy(&ARGR(cmd).command_buf, pdev->cmd_buf, pdev->cmd_len); + ARGR(cmd).bandlength = pdev->vbandlength; + ARGR(cmd).frame_size = pdev->frame_size; + ARG_OUT(cmd) break; } /* case VIDIOCPWCGVIDTABLE: { - struct pwc_table_init_buffer *table = arg; - table->len = pdev->cmd_len; - memcpy(&table->buffer, pdev->decompress_data, pdev->decompressor->table_size); + ARG_DEF(struct pwc_table_init_buffer, table); + ARGR(table).len = pdev->cmd_len; + memcpy(&ARGR(table).buffer, pdev->decompress_data, pdev->decompressor->table_size); + ARG_OUT(table) break; } */ @@ -1538,4 +1632,4 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) } - +/* vim: set cinoptions= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/linux/drivers/media/video/pwc/pwc-dec1.c b/linux/drivers/media/video/pwc/pwc-dec1.c new file mode 100644 index 000000000..c29593f58 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-dec1.c @@ -0,0 +1,50 @@ +/* Linux driver for Philips webcam + Decompression for chipset version 1 + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to <luc@saillard.org>. + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + 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 +*/ + + + +#include "pwc-dec1.h" + + +void pwc_dec1_init(int type, int release, void *buffer, void *table) +{ + +} + +void pwc_dec1_exit(void) +{ + + + +} + +int pwc_dec1_alloc(struct pwc_device *pwc) +{ + pwc->decompress_data = kmalloc(sizeof(struct pwc_dec1_private), GFP_KERNEL); + if (pwc->decompress_data == NULL) + return -ENOMEM; + return 0; +} + diff --git a/linux/drivers/media/video/pwc/pwc-dec1.h b/linux/drivers/media/video/pwc/pwc-dec1.h new file mode 100644 index 000000000..8b62ddcc5 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-dec1.h @@ -0,0 +1,43 @@ +/* Linux driver for Philips webcam + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to <luc@saillard.org>. + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + 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 PWC_DEC1_H +#define PWC_DEC1_H + +#include "pwc.h" + +struct pwc_dec1_private +{ + int version; + +}; + +int pwc_dec1_alloc(struct pwc_device *pwc); +void pwc_dec1_init(int type, int release, void *buffer, void *private_data); +void pwc_dec1_exit(void); + +#endif + diff --git a/linux/drivers/media/video/pwc/pwc-dec23.c b/linux/drivers/media/video/pwc/pwc-dec23.c new file mode 100644 index 000000000..f9096c72c --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-dec23.c @@ -0,0 +1,941 @@ +/* Linux driver for Philips webcam + Decompression for chipset version 2 et 3 + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to <luc@saillard.org>. + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + 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 + +*/ + +#include "pwc-timon.h" +#include "pwc-kiara.h" +#include "pwc-dec23.h" +#include <media/pwc-ioctl.h> + +#include <linux/string.h> + +/* + * USE_LOOKUP_TABLE_TO_CLAMP + * 0: use a C version of this tests: { a<0?0:(a>255?255:a) } + * 1: use a faster lookup table for cpu with a big cache (intel) + */ +#define USE_LOOKUP_TABLE_TO_CLAMP 1 +/* + * UNROLL_LOOP_FOR_COPYING_BLOCK + * 0: use a loop for a smaller code (but little slower) + * 1: when unrolling the loop, gcc produces some faster code (perhaps only + * valid for intel processor class). Activating this option, automaticaly + * activate USE_LOOKUP_TABLE_TO_CLAMP + */ +#define UNROLL_LOOP_FOR_COPY 1 +#if UNROLL_LOOP_FOR_COPY +# undef USE_LOOKUP_TABLE_TO_CLAMP +# define USE_LOOKUP_TABLE_TO_CLAMP 1 +#endif + +/* + * ENABLE_BAYER_DECODER + * 0: bayer decoder is not build (save some space) + * 1: bayer decoder is build and can be used + */ +#define ENABLE_BAYER_DECODER 0 + +static void build_subblock_pattern(struct pwc_dec23_private *pdec) +{ + static const unsigned int initial_values[12] = { + -0x526500, -0x221200, 0x221200, 0x526500, + -0x3de200, 0x3de200, + -0x6db480, -0x2d5d00, 0x2d5d00, 0x6db480, + -0x12c200, 0x12c200 + + }; + static const unsigned int values_derivated[12] = { + 0xa4ca, 0x4424, -0x4424, -0xa4ca, + 0x7bc4, -0x7bc4, + 0xdb69, 0x5aba, -0x5aba, -0xdb69, + 0x2584, -0x2584 + }; + unsigned int temp_values[12]; + int i, j; + + memcpy(temp_values, initial_values, sizeof(initial_values)); + for (i = 0; i < 256; i++) { + for (j = 0; j < 12; j++) { + pdec->table_subblock[i][j] = temp_values[j]; + temp_values[j] += values_derivated[j]; + } + } +} + +static void build_bit_powermask_table(struct pwc_dec23_private *pdec) +{ + unsigned char *p; + unsigned int bit, byte, mask, val; + unsigned int bitpower = 1; + + for (bit = 0; bit < 8; bit++) { + mask = bitpower - 1; + p = pdec->table_bitpowermask[bit]; + for (byte = 0; byte < 256; byte++) { + val = (byte & mask); + if (byte & bitpower) + val = -val; + *p++ = val; + } + bitpower<<=1; + } +} + + +static void build_table_color(const unsigned int romtable[16][8], + unsigned char p0004[16][1024], + unsigned char p8004[16][256]) +{ + int compression_mode, j, k, bit, pw; + unsigned char *p0, *p8; + const unsigned int *r; + + /* We have 16 compressions tables */ + for (compression_mode = 0; compression_mode < 16; compression_mode++) { + p0 = p0004[compression_mode]; + p8 = p8004[compression_mode]; + r = romtable[compression_mode]; + + for (j = 0; j < 8; j++, r++, p0 += 128) { + + for (k = 0; k < 16; k++) { + if (k == 0) + bit = 1; + else if (k >= 1 && k < 3) + bit = (r[0] >> 15) & 7; + else if (k >= 3 && k < 6) + bit = (r[0] >> 12) & 7; + else if (k >= 6 && k < 10) + bit = (r[0] >> 9) & 7; + else if (k >= 10 && k < 13) + bit = (r[0] >> 6) & 7; + else if (k >= 13 && k < 15) + bit = (r[0] >> 3) & 7; + else + bit = (r[0]) & 7; + if (k == 0) + *p8++ = 8; + else + *p8++ = j - bit; + *p8++ = bit; + + pw = 1 << bit; + p0[k + 0x00] = (1 * pw) + 0x80; + p0[k + 0x10] = (2 * pw) + 0x80; + p0[k + 0x20] = (3 * pw) + 0x80; + p0[k + 0x30] = (4 * pw) + 0x80; + p0[k + 0x40] = (-1 * pw) + 0x80; + p0[k + 0x50] = (-2 * pw) + 0x80; + p0[k + 0x60] = (-3 * pw) + 0x80; + p0[k + 0x70] = (-4 * pw) + 0x80; + } /* end of for (k=0; k<16; k++, p8++) */ + } /* end of for (j=0; j<8; j++ , table++) */ + } /* end of foreach compression_mode */ +} + +/* + * + */ +static void fill_table_dc00_d800(struct pwc_dec23_private *pdec) +{ +#define SCALEBITS 15 +#define ONE_HALF (1UL << (SCALEBITS - 1)) + int i; + unsigned int offset1 = ONE_HALF; + unsigned int offset2 = 0x0000; + + for (i=0; i<256; i++) { + pdec->table_dc00[i] = offset1 & ~(ONE_HALF); + pdec->table_d800[i] = offset2; + + offset1 += 0x7bc4; + offset2 += 0x7bc4; + } +} + +/* + * To decode the stream: + * if look_bits(2) == 0: # op == 2 in the lookup table + * skip_bits(2) + * end of the stream + * elif look_bits(3) == 7: # op == 1 in the lookup table + * skip_bits(3) + * yyyy = get_bits(4) + * xxxx = get_bits(8) + * else: # op == 0 in the lookup table + * skip_bits(x) + * + * For speedup processing, we build a lookup table and we takes the first 6 bits. + * + * struct { + * unsigned char op; // operation to execute + * unsigned char bits; // bits use to perform operation + * unsigned char offset1; // offset to add to access in the table_0004 % 16 + * unsigned char offset2; // offset to add to access in the table_0004 + * } + * + * How to build this table ? + * op == 2 when (i%4)==0 + * op == 1 when (i%8)==7 + * op == 0 otherwise + * + */ +static const unsigned char hash_table_ops[64*4] = { + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x10, + 0x00, 0x06, 0x01, 0x30, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x01, 0x20, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x50, + 0x00, 0x05, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x03, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x10, + 0x00, 0x06, 0x02, 0x10, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x01, 0x60, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x50, + 0x00, 0x05, 0x02, 0x40, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x03, 0x40, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x10, + 0x00, 0x06, 0x01, 0x70, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x01, 0x20, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x50, + 0x00, 0x05, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x03, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x10, + 0x00, 0x06, 0x02, 0x50, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x01, 0x60, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x50, + 0x00, 0x05, 0x02, 0x40, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x03, 0x40, + 0x01, 0x00, 0x00, 0x00 +}; + +/* + * + */ +static const unsigned int MulIdx[16][16] = { + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, + {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,}, + {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,}, + {4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4,}, + {6, 7, 8, 9, 7, 10, 11, 8, 8, 11, 10, 7, 9, 8, 7, 6,}, + {4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4,}, + {1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2,}, + {0, 3, 3, 0, 1, 2, 2, 1, 2, 1, 1, 2, 3, 0, 0, 3,}, + {0, 1, 2, 3, 3, 2, 1, 0, 3, 2, 1, 0, 0, 1, 2, 3,}, + {1, 1, 1, 1, 3, 3, 3, 3, 0, 0, 0, 0, 2, 2, 2, 2,}, + {7, 10, 11, 8, 9, 8, 7, 6, 6, 7, 8, 9, 8, 11, 10, 7,}, + {4, 5, 5, 4, 5, 4, 4, 5, 5, 4, 4, 5, 4, 5, 5, 4,}, + {7, 9, 6, 8, 10, 8, 7, 11, 11, 7, 8, 10, 8, 6, 9, 7,}, + {1, 3, 0, 2, 2, 0, 3, 1, 2, 0, 3, 1, 1, 3, 0, 2,}, + {1, 2, 2, 1, 3, 0, 0, 3, 0, 3, 3, 0, 2, 1, 1, 2,}, + {10, 8, 7, 11, 8, 6, 9, 7, 7, 9, 6, 8, 11, 7, 8, 10} +}; + +#if USE_LOOKUP_TABLE_TO_CLAMP +#define MAX_OUTER_CROP_VALUE (512) +static unsigned char pwc_crop_table[256 + 2*MAX_OUTER_CROP_VALUE]; +#define CLAMP(x) (pwc_crop_table[MAX_OUTER_CROP_VALUE+(x)]) +#else +#define CLAMP(x) ((x)>255?255:((x)<0?0:x)) +#endif + + +/* If the type or the command change, we rebuild the lookup table */ +int pwc_dec23_init(struct pwc_device *pwc, int type, unsigned char *cmd) +{ + int flags, version, shift, i; + struct pwc_dec23_private *pdec; + + if (pwc->decompress_data == NULL) { + pdec = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); + if (pdec == NULL) + return -ENOMEM; + pwc->decompress_data = pdec; + } + pdec = pwc->decompress_data; + + if (DEVICE_USE_CODEC3(type)) { + flags = cmd[2] & 0x18; + if (flags == 8) + pdec->nbits = 7; /* More bits, mean more bits to encode the stream, but better quality */ + else if (flags == 0x10) + pdec->nbits = 8; + else + pdec->nbits = 6; + + version = cmd[2] >> 5; + build_table_color(KiaraRomTable[version][0], pdec->table_0004_pass1, pdec->table_8004_pass1); + build_table_color(KiaraRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2); + + } else { + + flags = cmd[2] & 6; + if (flags == 2) + pdec->nbits = 7; + else if (flags == 4) + pdec->nbits = 8; + else + pdec->nbits = 6; + + version = cmd[2] >> 3; + build_table_color(TimonRomTable[version][0], pdec->table_0004_pass1, pdec->table_8004_pass1); + build_table_color(TimonRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2); + } + + /* Informations can be coded on a variable number of bits but never less than 8 */ + shift = 8 - pdec->nbits; + pdec->scalebits = SCALEBITS - shift; + pdec->nbitsmask = 0xFF >> shift; + + fill_table_dc00_d800(pdec); + build_subblock_pattern(pdec); + build_bit_powermask_table(pdec); + +#if USE_LOOKUP_TABLE_TO_CLAMP + /* Build the static table to clamp value [0-255] */ + for (i=0;i<MAX_OUTER_CROP_VALUE;i++) + pwc_crop_table[i] = 0; + for (i=0; i<256; i++) + pwc_crop_table[MAX_OUTER_CROP_VALUE+i] = i; + for (i=0; i<MAX_OUTER_CROP_VALUE; i++) + pwc_crop_table[MAX_OUTER_CROP_VALUE+256+i] = 255; +#endif + + return 0; +} + +/* + * Copy the 4x4 image block to Y plane buffer + */ +static void copy_image_block_Y(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) +{ +#if UNROLL_LOOP_FOR_COPY + const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; + const int *c = src; + unsigned char *d = dst; + + *d++ = cm[c[0] >> scalebits]; + *d++ = cm[c[1] >> scalebits]; + *d++ = cm[c[2] >> scalebits]; + *d++ = cm[c[3] >> scalebits]; + + d = dst + bytes_per_line; + *d++ = cm[c[4] >> scalebits]; + *d++ = cm[c[5] >> scalebits]; + *d++ = cm[c[6] >> scalebits]; + *d++ = cm[c[7] >> scalebits]; + + d = dst + bytes_per_line*2; + *d++ = cm[c[8] >> scalebits]; + *d++ = cm[c[9] >> scalebits]; + *d++ = cm[c[10] >> scalebits]; + *d++ = cm[c[11] >> scalebits]; + + d = dst + bytes_per_line*3; + *d++ = cm[c[12] >> scalebits]; + *d++ = cm[c[13] >> scalebits]; + *d++ = cm[c[14] >> scalebits]; + *d++ = cm[c[15] >> scalebits]; +#else + int i; + const int *c = src; + unsigned char *d = dst; + for (i = 0; i < 4; i++, c++) + *d++ = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line; + for (i = 0; i < 4; i++, c++) + *d++ = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line*2; + for (i = 0; i < 4; i++, c++) + *d++ = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line*3; + for (i = 0; i < 4; i++, c++) + *d++ = CLAMP((*c) >> scalebits); +#endif +} + +/* + * Copy the 4x4 image block to a CrCb plane buffer + * + */ +static void copy_image_block_CrCb(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) +{ +#if UNROLL_LOOP_FOR_COPY + /* Unroll all loops */ + const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; + const int *c = src; + unsigned char *d = dst; + + *d++ = cm[c[0] >> scalebits]; + *d++ = cm[c[4] >> scalebits]; + *d++ = cm[c[1] >> scalebits]; + *d++ = cm[c[5] >> scalebits]; + *d++ = cm[c[2] >> scalebits]; + *d++ = cm[c[6] >> scalebits]; + *d++ = cm[c[3] >> scalebits]; + *d++ = cm[c[7] >> scalebits]; + + d = dst + bytes_per_line; + *d++ = cm[c[12] >> scalebits]; + *d++ = cm[c[8] >> scalebits]; + *d++ = cm[c[13] >> scalebits]; + *d++ = cm[c[9] >> scalebits]; + *d++ = cm[c[14] >> scalebits]; + *d++ = cm[c[10] >> scalebits]; + *d++ = cm[c[15] >> scalebits]; + *d++ = cm[c[11] >> scalebits]; +#else + int i; + const int *c1 = src; + const int *c2 = src + 4; + unsigned char *d = dst; + + for (i = 0; i < 4; i++, c1++, c2++) { + *d++ = CLAMP((*c1) >> scalebits); + *d++ = CLAMP((*c2) >> scalebits); + } + c1 = src + 12; + d = dst + bytes_per_line; + for (i = 0; i < 4; i++, c1++, c2++) { + *d++ = CLAMP((*c1) >> scalebits); + *d++ = CLAMP((*c2) >> scalebits); + } +#endif +} + +#if ENABLE_BAYER_DECODER +/* + * Format: 8x2 pixels + * . G . G . G . G . G . G . G + * . . . . . . . . . . . . . . + * . G . G . G . G . G . G . G + * . . . . . . . . . . . . . . + * or + * . . . . . . . . . . . . . . + * G . G . G . G . G . G . G . + * . . . . . . . . . . . . . . + * G . G . G . G . G . G . G . +*/ +static void copy_image_block_Green(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) +{ +#if UNROLL_LOOP_FOR_COPY + /* Unroll all loops */ + const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; + unsigned char *d = dst; + const int *c = src; + + d[0] = cm[c[0] >> scalebits]; + d[2] = cm[c[1] >> scalebits]; + d[4] = cm[c[2] >> scalebits]; + d[6] = cm[c[3] >> scalebits]; + d[8] = cm[c[4] >> scalebits]; + d[10] = cm[c[5] >> scalebits]; + d[12] = cm[c[6] >> scalebits]; + d[14] = cm[c[7] >> scalebits]; + + d = dst + bytes_per_line; + d[0] = cm[c[8] >> scalebits]; + d[2] = cm[c[9] >> scalebits]; + d[4] = cm[c[10] >> scalebits]; + d[6] = cm[c[11] >> scalebits]; + d[8] = cm[c[12] >> scalebits]; + d[10] = cm[c[13] >> scalebits]; + d[12] = cm[c[14] >> scalebits]; + d[14] = cm[c[15] >> scalebits]; +#else + int i; + unsigned char *d; + const int *c = src; + + d = dst; + for (i = 0; i < 8; i++, c++) + d[i*2] = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line; + for (i = 0; i < 8; i++, c++) + d[i*2] = CLAMP((*c) >> scalebits); +#endif +} +#endif + +#if ENABLE_BAYER_DECODER +/* + * Format: 4x4 pixels + * R . R . R . R + * . B . B . B . + * R . R . R . R + * . B . B . B . + */ +static void copy_image_block_RedBlue(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) +{ +#if UNROLL_LOOP_FOR_COPY + /* Unroll all loops */ + const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; + unsigned char *d = dst; + const int *c = src; + + d[0] = cm[c[0] >> scalebits]; + d[2] = cm[c[1] >> scalebits]; + d[4] = cm[c[2] >> scalebits]; + d[6] = cm[c[3] >> scalebits]; + + d = dst + bytes_per_line; + d[1] = cm[c[4] >> scalebits]; + d[3] = cm[c[5] >> scalebits]; + d[5] = cm[c[6] >> scalebits]; + d[7] = cm[c[7] >> scalebits]; + + d = dst + bytes_per_line*2; + d[0] = cm[c[8] >> scalebits]; + d[2] = cm[c[9] >> scalebits]; + d[4] = cm[c[10] >> scalebits]; + d[6] = cm[c[11] >> scalebits]; + + d = dst + bytes_per_line*3; + d[1] = cm[c[12] >> scalebits]; + d[3] = cm[c[13] >> scalebits]; + d[5] = cm[c[14] >> scalebits]; + d[7] = cm[c[15] >> scalebits]; +#else + int i; + unsigned char *d; + const int *c = src; + + d = dst; + for (i = 0; i < 4; i++, c++) + d[i*2] = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line; + for (i = 0; i < 4; i++, c++) + d[i*2+1] = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line*2; + for (i = 0; i < 4; i++, c++) + d[i*2] = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line*3; + for (i = 0; i < 4; i++, c++) + d[i*2+1] = CLAMP((*c) >> scalebits); +#endif +} +#endif + +/* + * To manage the stream, we keep bits in a 32 bits register. + * fill_nbits(n): fill the reservoir with at least n bits + * skip_bits(n): discard n bits from the reservoir + * get_bits(n): fill the reservoir, returns the first n bits and discard the + * bits from the reservoir. + * __get_nbits(n): faster version of get_bits(n), but asumes that the reservoir + * contains at least n bits. bits returned is discarded. + */ +#define fill_nbits(pdec, nbits_wanted) do { \ + while (pdec->nbits_in_reservoir<(nbits_wanted)) \ + { \ + pdec->reservoir |= (*(pdec->stream)++) << (pdec->nbits_in_reservoir); \ + pdec->nbits_in_reservoir += 8; \ + } \ +} while(0); + +#define skip_nbits(pdec, nbits_to_skip) do { \ + pdec->reservoir >>= (nbits_to_skip); \ + pdec->nbits_in_reservoir -= (nbits_to_skip); \ +} while(0); + +#define get_nbits(pdec, nbits_wanted, result) do { \ + fill_nbits(pdec, nbits_wanted); \ + result = (pdec->reservoir) & ((1U<<(nbits_wanted))-1); \ + skip_nbits(pdec, nbits_wanted); \ +} while(0); + +#define __get_nbits(pdec, nbits_wanted, result) do { \ + result = (pdec->reservoir) & ((1U<<(nbits_wanted))-1); \ + skip_nbits(pdec, nbits_wanted); \ +} while(0); + +#define look_nbits(pdec, nbits_wanted) \ + ((pdec->reservoir) & ((1U<<(nbits_wanted))-1)) + +/* + * Decode a 4x4 pixel block + */ +static void decode_block(struct pwc_dec23_private *pdec, + const unsigned char *ptable0004, + const unsigned char *ptable8004) +{ + unsigned int primary_color; + unsigned int channel_v, offset1, op; + int i; + + fill_nbits(pdec, 16); + __get_nbits(pdec, pdec->nbits, primary_color); + + if (look_nbits(pdec,2) == 0) { + skip_nbits(pdec, 2); + /* Very simple, the color is the same for all pixels of the square */ + for (i = 0; i < 16; i++) + pdec->temp_colors[i] = pdec->table_dc00[primary_color]; + + return; + } + + /* This block is encoded with small pattern */ + for (i = 0; i < 16; i++) + pdec->temp_colors[i] = pdec->table_d800[primary_color]; + + __get_nbits(pdec, 3, channel_v); + channel_v = ((channel_v & 1) << 2) | (channel_v & 2) | ((channel_v & 4) >> 2); + + ptable0004 += (channel_v * 128); + ptable8004 += (channel_v * 32); + + offset1 = 0; + do + { + unsigned int htable_idx, rows = 0; + const unsigned int *block; + + /* [ zzzz y x x ] + * xx == 00 :=> end of the block def, remove the two bits from the stream + * yxx == 111 + * yxx == any other value + * + */ + fill_nbits(pdec, 16); + htable_idx = look_nbits(pdec, 6); + op = hash_table_ops[htable_idx * 4]; + + if (op == 2) { + skip_nbits(pdec, 2); + + } else if (op == 1) { + /* 15bits [ xxxx xxxx yyyy 111 ] + * yyy => offset in the table8004 + * xxx => offset in the tabled004 (tree) + */ + unsigned int mask, shift; + unsigned int nbits, col1; + unsigned int yyyy; + + skip_nbits(pdec, 3); + /* offset1 += yyyy */ + __get_nbits(pdec, 4, yyyy); + offset1 += 1 + yyyy; + offset1 &= 0x0F; + nbits = ptable8004[offset1 * 2]; + + /* col1 = xxxx xxxx */ + __get_nbits(pdec, nbits+1, col1); + + /* Bit mask table */ + mask = pdec->table_bitpowermask[nbits][col1]; + shift = ptable8004[offset1 * 2 + 1]; + rows = ((mask << shift) + 0x80) & 0xFF; + + block = pdec->table_subblock[rows]; + for (i = 0; i < 16; i++) + pdec->temp_colors[i] += block[MulIdx[offset1][i]]; + + } else { + /* op == 0 + * offset1 is coded on 3 bits + */ + unsigned int shift; + + offset1 += hash_table_ops [htable_idx * 4 + 2]; + offset1 &= 0x0F; + + rows = ptable0004[offset1 + hash_table_ops [htable_idx * 4 + 3]]; + block = pdec->table_subblock[rows]; + for (i = 0; i < 16; i++) + pdec->temp_colors[i] += block[MulIdx[offset1][i]]; + + shift = hash_table_ops[htable_idx * 4 + 1]; + skip_nbits(pdec, shift); + } + + } while (op != 2); + +} + +static void DecompressBand23(struct pwc_dec23_private *pdec, + const unsigned char *rawyuv, + unsigned char *planar_y, + unsigned char *planar_u, + unsigned char *planar_v, + unsigned int compressed_image_width, + unsigned int real_image_width) +{ + int compression_index, nblocks; + const unsigned char *ptable0004; + const unsigned char *ptable8004; + + pdec->reservoir = 0; + pdec->nbits_in_reservoir = 0; + pdec->stream = rawyuv + 1; /* The first byte of the stream is skipped */ + + get_nbits(pdec, 4, compression_index); + + /* pass 1: uncompress Y component */ + nblocks = compressed_image_width / 4; + + ptable0004 = pdec->table_0004_pass1[compression_index]; + ptable8004 = pdec->table_8004_pass1[compression_index]; + + /* Each block decode a square of 4x4 */ + while (nblocks) { + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_Y(pdec->temp_colors, planar_y, real_image_width, pdec->scalebits); + planar_y += 4; + nblocks--; + } + + /* pass 2: uncompress UV component */ + nblocks = compressed_image_width / 8; + + ptable0004 = pdec->table_0004_pass2[compression_index]; + ptable8004 = pdec->table_8004_pass2[compression_index]; + + /* Each block decode a square of 4x4 */ + while (nblocks) { + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_CrCb(pdec->temp_colors, planar_u, real_image_width/2, pdec->scalebits); + + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_CrCb(pdec->temp_colors, planar_v, real_image_width/2, pdec->scalebits); + + planar_v += 8; + planar_u += 8; + nblocks -= 2; + } + +} + +#if ENABLE_BAYER_DECODER +/* + * Size need to be a multiple of 8 in width + * + * Return a block of four line encoded like this: + * + * G R G R G R G R G R G R G R G R + * B G B G B G B G B G B G B G B G + * G R G R G R G R G R G R G R G R + * B G B G B G B G B G B G B G B G + * + */ +static void DecompressBandBayer(struct pwc_dec23_private *pdec, + const unsigned char *rawyuv, + unsigned char *rgbbayer, + unsigned int compressed_image_width, + unsigned int real_image_width) +{ + int compression_index, nblocks; + const unsigned char *ptable0004; + const unsigned char *ptable8004; + unsigned char *dest; + + pdec->reservoir = 0; + pdec->nbits_in_reservoir = 0; + pdec->stream = rawyuv + 1; /* The first byte of the stream is skipped */ + + get_nbits(pdec, 4, compression_index); + + /* pass 1: uncompress RB component */ + nblocks = compressed_image_width / 4; + + ptable0004 = pdec->table_0004_pass1[compression_index]; + ptable8004 = pdec->table_8004_pass1[compression_index]; + dest = rgbbayer; + + /* Each block decode a square of 4x4 */ + while (nblocks) { + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_RedBlue(pdec->temp_colors, rgbbayer, real_image_width, pdec->scalebits); + dest += 8; + nblocks--; + } + + /* pass 2: uncompress G component */ + nblocks = compressed_image_width / 8; + + ptable0004 = pdec->table_0004_pass2[compression_index]; + ptable8004 = pdec->table_8004_pass2[compression_index]; + + /* Each block decode a square of 4x4 */ + while (nblocks) { + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_Green(pdec->temp_colors, rgbbayer+1, real_image_width, pdec->scalebits); + + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_Green(pdec->temp_colors, rgbbayer+real_image_width, real_image_width, pdec->scalebits); + + rgbbayer += 16; + nblocks -= 2; + } +} +#endif + + +/** + * + * Uncompress a pwc23 buffer. + * + * pwc.view: size of the image wanted + * pwc.image: size of the image returned by the camera + * pwc.offset: (x,y) to displayer image in the view + * + * src: raw data + * dst: image output + * flags: PWCX_FLAG_PLANAR or PWCX_FLAG_BAYER + */ +void pwc_dec23_decompress(const struct pwc_device *pwc, + const void *src, + void *dst, + int flags) +{ + int bandlines_left, stride, bytes_per_block; + + bandlines_left = pwc->image.y / 4; + bytes_per_block = pwc->view.x * 4; + + if (flags & PWCX_FLAG_BAYER) { +#if ENABLE_BAYER_DECODER + /* RGB Bayer format */ + unsigned char *rgbout; + + stride = pwc->view.x * pwc->offset.y; + rgbout = dst + stride + pwc->offset.x; + + + while (bandlines_left--) { + + DecompressBandBayer(pwc->decompress_data, + src, + rgbout, + pwc->image.x, pwc->view.x); + + src += pwc->vbandlength; + rgbout += bytes_per_block; + + } +#else + memcpy(dst, 0, pwc->view.x * pwc->view.y); +#endif + + } else { + /* YUV420P image format */ + unsigned char *pout_planar_y; + unsigned char *pout_planar_u; + unsigned char *pout_planar_v; + unsigned int plane_size; + + plane_size = pwc->view.x * pwc->view.y; + + /* offset in Y plane */ + stride = pwc->view.x * pwc->offset.y; + pout_planar_y = dst + stride + pwc->offset.x; + + /* offsets in U/V planes */ + stride = (pwc->view.x * pwc->offset.y) / 4 + pwc->offset.x / 2; + pout_planar_u = dst + plane_size + stride; + pout_planar_v = dst + plane_size + plane_size / 4 + stride; + + while (bandlines_left--) { + + DecompressBand23(pwc->decompress_data, + src, + pout_planar_y, pout_planar_u, pout_planar_v, + pwc->image.x, pwc->view.x); + src += pwc->vbandlength; + pout_planar_y += bytes_per_block; + pout_planar_u += pwc->view.x; + pout_planar_v += pwc->view.x; + + } + + } + +} + +void pwc_dec23_exit(void) +{ + /* Do nothing */ + +} + +/** + * Allocate a private structure used by lookup table. + * You must call kfree() to free the memory allocated. + */ +int pwc_dec23_alloc(struct pwc_device *pwc) +{ + pwc->decompress_data = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); + if (pwc->decompress_data == NULL) + return -ENOMEM; + return 0; +} + +/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/linux/drivers/media/video/pwc/pwc-dec23.h b/linux/drivers/media/video/pwc/pwc-dec23.h new file mode 100644 index 000000000..1c55298ad --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-dec23.h @@ -0,0 +1,67 @@ +/* Linux driver for Philips webcam + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to <luc@saillard.org>. + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + 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 PWC_DEC23_H +#define PWC_DEC23_H + +#include "pwc.h" + +struct pwc_dec23_private +{ + unsigned int scalebits; + unsigned int nbitsmask, nbits; /* Number of bits of a color in the compressed stream */ + + unsigned int reservoir; + unsigned int nbits_in_reservoir; + const unsigned char *stream; + int temp_colors[16]; + + unsigned char table_0004_pass1[16][1024]; + unsigned char table_0004_pass2[16][1024]; + unsigned char table_8004_pass1[16][256]; + unsigned char table_8004_pass2[16][256]; + unsigned int table_subblock[256][12]; + + unsigned char table_bitpowermask[8][256]; + unsigned int table_d800[256]; + unsigned int table_dc00[256]; + +}; + + +int pwc_dec23_alloc(struct pwc_device *pwc); +int pwc_dec23_init(struct pwc_device *pwc, int type, unsigned char *cmd); +void pwc_dec23_exit(void); +void pwc_dec23_decompress(const struct pwc_device *pwc, + const void *src, + void *dst, + int flags); + + + +#endif + + +/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ + diff --git a/linux/drivers/media/video/pwc/pwc-if.c b/linux/drivers/media/video/pwc/pwc-if.c index 7c97270d3..fbe7ae2a9 100644 --- a/linux/drivers/media/video/pwc/pwc-if.c +++ b/linux/drivers/media/video/pwc/pwc-if.c @@ -1,7 +1,7 @@ /* Linux driver for Philips webcam USB and Video4Linux interface part. (C) 1999-2004 Nemosoft Unv. - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -62,18 +62,21 @@ #include <linux/poll.h> #include <linux/slab.h> #include <linux/vmalloc.h> +#include <linux/version.h> #include <asm/io.h> +#include <linux/moduleparam.h> #include "pwc.h" -#include "pwc-ioctl.h" #include "pwc-kiara.h" #include "pwc-timon.h" +#include "pwc-dec23.h" +#include "pwc-dec1.h" #include "pwc-uncompress.h" /* Function prototypes and driver templates */ /* hotplug device table support */ -static struct usb_device_id pwc_device_table [] = { +static const struct usb_device_id pwc_device_table [] = { { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */ { USB_DEVICE(0x0471, 0x0303) }, { USB_DEVICE(0x0471, 0x0304) }, @@ -81,9 +84,10 @@ static struct usb_device_id pwc_device_table [] = { { USB_DEVICE(0x0471, 0x0308) }, { USB_DEVICE(0x0471, 0x030C) }, { USB_DEVICE(0x0471, 0x0310) }, - { USB_DEVICE(0x0471, 0x0311) }, + { USB_DEVICE(0x0471, 0x0311) }, /* Philips ToUcam PRO II */ { USB_DEVICE(0x0471, 0x0312) }, { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */ + { USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */ { USB_DEVICE(0x069A, 0x0001) }, /* Askey */ { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */ { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */ @@ -94,8 +98,9 @@ static struct usb_device_id pwc_device_table [] = { { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */ { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */ { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ - { USB_DEVICE(0x055D, 0x9000) }, /* Samsung */ - { USB_DEVICE(0x055D, 0x9001) }, + { USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */ + { USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */ + { USB_DEVICE(0x055D, 0x9002) }, /* Samsung SNC-35E (Ver3.0) */ { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */ { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */ { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */ @@ -122,11 +127,13 @@ static struct usb_driver pwc_driver = { static int default_size = PSZ_QCIF; static int default_fps = 10; static int default_fbufs = 3; /* Default number of frame buffers */ -static int default_mbufs = 2; /* Default number of mmap() buffers */ - int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX; + int pwc_mbufs = 2; /* Default number of mmap() buffers */ +#if CONFIG_PWC_DEBUG + int pwc_trace = PWC_DEBUG_LEVEL; +#endif static int power_save = 0; static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */ -static int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */ + int pwc_preferred_compression = 1; /* 0..3 = uncompressed..high */ static struct { int type; char serial_number[30]; @@ -138,7 +145,7 @@ static struct { static int pwc_video_open(struct inode *inode, struct file *file); static int pwc_video_close(struct inode *inode, struct file *file); -static ssize_t pwc_video_read(struct file *file, char __user * buf, +static ssize_t pwc_video_read(struct file *file, char __user *buf, size_t count, loff_t *ppos); static unsigned int pwc_video_poll(struct file *file, poll_table *wait); static int pwc_video_ioctl(struct inode *inode, struct file *file, @@ -153,7 +160,6 @@ static struct file_operations pwc_fops = { .poll = pwc_video_poll, .mmap = pwc_video_mmap, .ioctl = pwc_video_ioctl, - .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; static struct video_device pwc_template = { @@ -203,7 +209,8 @@ static struct video_device pwc_template = { /* Here we want the physical address of the memory. * This is used when initializing the contents of the area. */ -static inline unsigned long kvirt_to_pa(unsigned long adr) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) +static unsigned long kvirt_to_pa(unsigned long adr) { unsigned long kva, ret; @@ -212,43 +219,59 @@ static inline unsigned long kvirt_to_pa(unsigned long adr) ret = __pa(kva); return ret; } +#endif + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) +/** + * kzalloc - allocate memory. The memory is set to zero. + * @size: how many bytes of memory are required. + * @flags: the type of memory to allocate. + */ +void *kzalloc(size_t size, int flags) +{ + void *ret = kmalloc(size, flags); + if (ret) + memset(ret, 0, size); + return ret; +} +#endif -static void * rvmalloc(unsigned long size) +static void *pwc_rvmalloc(unsigned long size) { void * mem; unsigned long adr; - size=PAGE_ALIGN(size); mem=vmalloc_32(size); - if (mem) - { - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr=(unsigned long) mem; - while (size > 0) - { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - } + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr=(unsigned long) mem; + while (size > 0) + { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } return mem; } -static void rvfree(void * mem, unsigned long size) +static void pwc_rvfree(void * mem, unsigned long size) { unsigned long adr; - if (mem) - { - adr=(unsigned long) mem; - while ((long) size > 0) - { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - vfree(mem); - } + if (!mem) + return; + + adr=(unsigned long) mem; + while ((long) size > 0) + { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); } @@ -256,100 +279,83 @@ static void rvfree(void * mem, unsigned long size) static int pwc_allocate_buffers(struct pwc_device *pdev) { - int i; + int i, err; void *kbuf; - Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev); + PWC_DEBUG_MEMORY(">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev); if (pdev == NULL) return -ENXIO; -#ifdef PWC_MAGIC - if (pdev->magic != PWC_MAGIC) { - Err("allocate_buffers(): magic failed.\n"); - return -ENXIO; - } -#endif - /* Allocate Isochronous pipe buffers */ + /* Allocate Isochronuous pipe buffers */ for (i = 0; i < MAX_ISO_BUFS; i++) { if (pdev->sbuf[i].data == NULL) { - kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL); + kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL); if (kbuf == NULL) { - Err("Failed to allocate iso buffer %d.\n", i); + PWC_ERROR("Failed to allocate iso buffer %d.\n", i); return -ENOMEM; } - Trace(TRACE_MEMORY, "Allocated iso buffer at %p.\n", kbuf); + PWC_DEBUG_MEMORY("Allocated iso buffer at %p.\n", kbuf); pdev->sbuf[i].data = kbuf; - memset(kbuf, 0, ISO_BUFFER_SIZE); } } /* Allocate frame buffer structure */ if (pdev->fbuf == NULL) { - kbuf = kmalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL); + kbuf = kzalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL); if (kbuf == NULL) { - Err("Failed to allocate frame buffer structure.\n"); + PWC_ERROR("Failed to allocate frame buffer structure.\n"); return -ENOMEM; } - Trace(TRACE_MEMORY, "Allocated frame buffer structure at %p.\n", kbuf); + PWC_DEBUG_MEMORY("Allocated frame buffer structure at %p.\n", kbuf); pdev->fbuf = kbuf; - memset(kbuf, 0, default_fbufs * sizeof(struct pwc_frame_buf)); } + /* create frame buffers, and make circular ring */ for (i = 0; i < default_fbufs; i++) { if (pdev->fbuf[i].data == NULL) { kbuf = vmalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */ if (kbuf == NULL) { - Err("Failed to allocate frame buffer %d.\n", i); + PWC_ERROR("Failed to allocate frame buffer %d.\n", i); return -ENOMEM; } - Trace(TRACE_MEMORY, "Allocated frame buffer %d at %p.\n", i, kbuf); + PWC_DEBUG_MEMORY("Allocated frame buffer %d at %p.\n", i, kbuf); pdev->fbuf[i].data = kbuf; - memset(kbuf, 128, PWC_FRAME_SIZE); + memset(kbuf, 0, PWC_FRAME_SIZE); } } /* Allocate decompressor table space */ - kbuf = NULL; - switch (pdev->type) - { - case 675: - case 680: - case 690: - case 720: - case 730: - case 740: - case 750: -#if 0 /* keep */ - Trace(TRACE_MEMORY,"private_data(%zu)\n",sizeof(struct pwc_dec23_private)); - kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); /* Timon & Kiara */ - break; - case 645: - case 646: - /* TODO & FIXME */ - kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); - break; -#endif - ; - } - pdev->decompress_data = kbuf; + if (DEVICE_USE_CODEC1(pdev->type)) + err = pwc_dec1_alloc(pdev); + else + err = pwc_dec23_alloc(pdev); + + if (err) { + PWC_ERROR("Failed to allocate decompress table.\n"); + return err; + } /* Allocate image buffer; double buffer for mmap() */ - kbuf = rvmalloc(default_mbufs * pdev->len_per_image); + kbuf = pwc_rvmalloc(pwc_mbufs * pdev->len_per_image); if (kbuf == NULL) { - Err("Failed to allocate image buffer(s). needed (%d)\n",default_mbufs * pdev->len_per_image); + PWC_ERROR("Failed to allocate image buffer(s). needed (%d)\n", + pwc_mbufs * pdev->len_per_image); return -ENOMEM; } - Trace(TRACE_MEMORY, "Allocated image buffer at %p.\n", kbuf); + PWC_DEBUG_MEMORY("Allocated image buffer at %p.\n", kbuf); pdev->image_data = kbuf; - for (i = 0; i < default_mbufs; i++) - pdev->image_ptr[i] = kbuf + i * pdev->len_per_image; - for (; i < MAX_IMAGES; i++) - pdev->image_ptr[i] = NULL; + for (i = 0; i < pwc_mbufs; i++) { + pdev->images[i].offset = i * pdev->len_per_image; + pdev->images[i].vma_use_count = 0; + } + for (; i < MAX_IMAGES; i++) { + pdev->images[i].offset = 0; + } kbuf = NULL; - Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n"); + PWC_DEBUG_MEMORY("<< pwc_allocate_buffers()\n"); return 0; } @@ -357,21 +363,14 @@ static void pwc_free_buffers(struct pwc_device *pdev) { int i; - Trace(TRACE_MEMORY, "Entering free_buffers(%p).\n", pdev); + PWC_DEBUG_MEMORY("Entering free_buffers(%p).\n", pdev); if (pdev == NULL) return; -#ifdef PWC_MAGIC - if (pdev->magic != PWC_MAGIC) { - Err("free_buffers(): magic failed.\n"); - return; - } -#endif - /* Release Iso-pipe buffers */ for (i = 0; i < MAX_ISO_BUFS; i++) if (pdev->sbuf[i].data != NULL) { - Trace(TRACE_MEMORY, "Freeing ISO buffer at %p.\n", pdev->sbuf[i].data); + PWC_DEBUG_MEMORY("Freeing ISO buffer at %p.\n", pdev->sbuf[i].data); kfree(pdev->sbuf[i].data); pdev->sbuf[i].data = NULL; } @@ -380,7 +379,7 @@ static void pwc_free_buffers(struct pwc_device *pdev) if (pdev->fbuf != NULL) { for (i = 0; i < default_fbufs; i++) { if (pdev->fbuf[i].data != NULL) { - Trace(TRACE_MEMORY, "Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data); + PWC_DEBUG_MEMORY("Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data); vfree(pdev->fbuf[i].data); pdev->fbuf[i].data = NULL; } @@ -391,20 +390,19 @@ static void pwc_free_buffers(struct pwc_device *pdev) /* Intermediate decompression buffer & tables */ if (pdev->decompress_data != NULL) { - Trace(TRACE_MEMORY, "Freeing decompression buffer at %p.\n", pdev->decompress_data); + PWC_DEBUG_MEMORY("Freeing decompression buffer at %p.\n", pdev->decompress_data); kfree(pdev->decompress_data); pdev->decompress_data = NULL; } - pdev->decompressor = NULL; /* Release image buffers */ if (pdev->image_data != NULL) { - Trace(TRACE_MEMORY, "Freeing image buffer at %p.\n", pdev->image_data); - rvfree(pdev->image_data, default_mbufs * pdev->len_per_image); + PWC_DEBUG_MEMORY("Freeing image buffer at %p.\n", pdev->image_data); + pwc_rvfree(pdev->image_data, pwc_mbufs * pdev->len_per_image); } pdev->image_data = NULL; - Trace(TRACE_MEMORY, "Leaving free_buffers().\n"); + PWC_DEBUG_MEMORY("Leaving free_buffers().\n"); } /* The frame & image buffer mess. @@ -464,7 +462,7 @@ static void pwc_free_buffers(struct pwc_device *pdev) /** \brief Find next frame buffer to fill. Take from empty or full list, whichever comes first. */ -static inline int pwc_next_fill_frame(struct pwc_device *pdev) +static int pwc_next_fill_frame(struct pwc_device *pdev) { int ret; unsigned long flags; @@ -489,23 +487,17 @@ static inline int pwc_next_fill_frame(struct pwc_device *pdev) } else { /* Hmm. Take it from the full list */ -#if PWC_DEBUG /* sanity check */ if (pdev->full_frames == NULL) { - Err("Neither empty or full frames available!\n"); + PWC_ERROR("Neither empty or full frames available!\n"); spin_unlock_irqrestore(&pdev->ptrlock, flags); return -EINVAL; } -#endif pdev->fill_frame = pdev->full_frames; pdev->full_frames = pdev->full_frames->next; ret = 1; } pdev->fill_frame->next = NULL; -#if PWC_DEBUG - Trace(TRACE_SEQUENCE, "Assigning sequence number %d.\n", pdev->sequence); - pdev->fill_frame->sequence = pdev->sequence++; -#endif spin_unlock_irqrestore(&pdev->ptrlock, flags); return ret; } @@ -521,6 +513,8 @@ static void pwc_reset_buffers(struct pwc_device *pdev) int i; unsigned long flags; + PWC_DEBUG_MEMORY(">> %s __enter__\n", __FUNCTION__); + spin_lock_irqsave(&pdev->ptrlock, flags); pdev->full_frames = NULL; pdev->full_frames_tail = NULL; @@ -540,13 +534,15 @@ static void pwc_reset_buffers(struct pwc_device *pdev) pdev->image_read_pos = 0; pdev->fill_image = 0; spin_unlock_irqrestore(&pdev->ptrlock, flags); + + PWC_DEBUG_MEMORY("<< %s __leaving__\n", __FUNCTION__); } /** \brief Do all the handling for getting one frame: get pointer, decompress, advance pointers. */ -static int pwc_handle_frame(struct pwc_device *pdev) +int pwc_handle_frame(struct pwc_device *pdev) { int ret = 0; unsigned long flags; @@ -556,41 +552,40 @@ static int pwc_handle_frame(struct pwc_device *pdev) we can release the lock after this without problems */ if (pdev->read_frame != NULL) { /* This can't theoretically happen */ - Err("Huh? Read frame still in use?\n"); + PWC_ERROR("Huh? Read frame still in use?\n"); + spin_unlock_irqrestore(&pdev->ptrlock, flags); + return ret; + } + + + if (pdev->full_frames == NULL) { + PWC_ERROR("Woops. No frames ready.\n"); } else { - if (pdev->full_frames == NULL) { - Err("Woops. No frames ready.\n"); + pdev->read_frame = pdev->full_frames; + pdev->full_frames = pdev->full_frames->next; + pdev->read_frame->next = NULL; + } + + if (pdev->read_frame != NULL) { + /* Decompression is a lenghty process, so it's outside of the lock. + This gives the isoc_handler the opportunity to fill more frames + in the mean time. + */ + spin_unlock_irqrestore(&pdev->ptrlock, flags); + ret = pwc_decompress(pdev); + spin_lock_irqsave(&pdev->ptrlock, flags); + + /* We're done with read_buffer, tack it to the end of the empty buffer list */ + if (pdev->empty_frames == NULL) { + pdev->empty_frames = pdev->read_frame; + pdev->empty_frames_tail = pdev->empty_frames; } else { - pdev->read_frame = pdev->full_frames; - pdev->full_frames = pdev->full_frames->next; - pdev->read_frame->next = NULL; - } - - if (pdev->read_frame != NULL) { -#if PWC_DEBUG - Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence); -#endif - /* Decompression is a lenghty process, so it's outside of the lock. - This gives the isoc_handler the opportunity to fill more frames - in the mean time. - */ - spin_unlock_irqrestore(&pdev->ptrlock, flags); - ret = pwc_decompress(pdev); - spin_lock_irqsave(&pdev->ptrlock, flags); - - /* We're done with read_buffer, tack it to the end of the empty buffer list */ - if (pdev->empty_frames == NULL) { - pdev->empty_frames = pdev->read_frame; - pdev->empty_frames_tail = pdev->empty_frames; - } - else { - pdev->empty_frames_tail->next = pdev->read_frame; - pdev->empty_frames_tail = pdev->read_frame; - } - pdev->read_frame = NULL; + pdev->empty_frames_tail->next = pdev->read_frame; + pdev->empty_frames_tail = pdev->read_frame; } + pdev->read_frame = NULL; } spin_unlock_irqrestore(&pdev->ptrlock, flags); return ret; @@ -599,12 +594,114 @@ static int pwc_handle_frame(struct pwc_device *pdev) /** \brief Advance pointers of image buffer (after each user request) */ -static inline void pwc_next_image(struct pwc_device *pdev) +void pwc_next_image(struct pwc_device *pdev) { pdev->image_used[pdev->fill_image] = 0; - pdev->fill_image = (pdev->fill_image + 1) % default_mbufs; + pdev->fill_image = (pdev->fill_image + 1) % pwc_mbufs; +} + +/** + * Print debug information when a frame is discarded because all of our buffer + * is full + */ +static void pwc_frame_dumped(struct pwc_device *pdev) +{ + pdev->vframes_dumped++; + if (pdev->vframe_count < FRAME_LOWMARK) + return; + + if (pdev->vframes_dumped < 20) + PWC_DEBUG_FLOW("Dumping frame %d\n", pdev->vframe_count); + else if (pdev->vframes_dumped == 20) + PWC_DEBUG_FLOW("Dumping frame %d (last message)\n", + pdev->vframe_count); } +static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_buf *fbuf) +{ + int awake = 0; + + /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus + frames on the USB wire after an exposure change. This conditition is + however detected in the cam and a bit is set in the header. + */ + if (pdev->type == 730) { + unsigned char *ptr = (unsigned char *)fbuf->data; + + if (ptr[1] == 1 && ptr[0] & 0x10) { + PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n"); + pdev->drop_frames += 2; + pdev->vframes_error++; + } + if ((ptr[0] ^ pdev->vmirror) & 0x01) { + if (ptr[0] & 0x01) { + pdev->snapshot_button_status = 1; + PWC_TRACE("Snapshot button pressed.\n"); + } + else { + PWC_TRACE("Snapshot button released.\n"); + } + } + if ((ptr[0] ^ pdev->vmirror) & 0x02) { + if (ptr[0] & 0x02) + PWC_TRACE("Image is mirrored.\n"); + else + PWC_TRACE("Image is normal.\n"); + } + pdev->vmirror = ptr[0] & 0x03; + /* Sometimes the trailer of the 730 is still sent as a 4 byte packet + after a short frame; this condition is filtered out specifically. A 4 byte + frame doesn't make sense anyway. + So we get either this sequence: + drop_bit set -> 4 byte frame -> short frame -> good frame + Or this one: + drop_bit set -> short frame -> good frame + So we drop either 3 or 2 frames in all! + */ + if (fbuf->filled == 4) + pdev->drop_frames++; + } + else if (pdev->type == 740 || pdev->type == 720) { + unsigned char *ptr = (unsigned char *)fbuf->data; + if ((ptr[0] ^ pdev->vmirror) & 0x01) { + if (ptr[0] & 0x01) { + pdev->snapshot_button_status = 1; + PWC_TRACE("Snapshot button pressed.\n"); + } + else + PWC_TRACE("Snapshot button released.\n"); + } + pdev->vmirror = ptr[0] & 0x03; + } + + /* In case we were instructed to drop the frame, do so silently. + The buffer pointers are not updated either (but the counters are reset below). + */ + if (pdev->drop_frames > 0) + pdev->drop_frames--; + else { + /* Check for underflow first */ + if (fbuf->filled < pdev->frame_total_size) { + PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);" + " discarded.\n", fbuf->filled); + pdev->vframes_error++; + } + else { + /* Send only once per EOF */ + awake = 1; /* delay wake_ups */ + + /* Find our next frame to fill. This will always succeed, since we + * nick a frame from either empty or full list, but if we had to + * take it from the full list, it means a frame got dropped. + */ + if (pwc_next_fill_frame(pdev)) + pwc_frame_dumped(pdev); + + } + } /* !drop_frames */ + pdev->vframe_count++; + return awake; +} /* This gets called for the Isochronous pipe (video). This is done in * interrupt time, so it has to be fast, not crash, and not stall. Neat. @@ -620,17 +717,12 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) awake = 0; pdev = (struct pwc_device *)urb->context; if (pdev == NULL) { - Err("isoc_handler() called with NULL device?!\n"); - return; - } -#ifdef PWC_MAGIC - if (pdev->magic != PWC_MAGIC) { - Err("isoc_handler() called with bad magic!\n"); + PWC_ERROR("isoc_handler() called with NULL device?!\n"); return; } -#endif + if (urb->status == -ENOENT || urb->status == -ECONNRESET) { - Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a"); + PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a"); return; } if (urb->status != -EINPROGRESS && urb->status != 0) { @@ -645,13 +737,13 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break; case -ETIMEDOUT: errmsg = "NAK (device does not respond)"; break; } - Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg); + PWC_DEBUG_FLOW("pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg); /* Give up after a number of contiguous errors on the USB bus. Appearantly something is wrong so we simulate an unplug event. */ if (++pdev->visoc_errors > MAX_ISOC_ERRORS) { - Info("Too many ISOC errors, bailing out.\n"); + PWC_INFO("Too many ISOC errors, bailing out.\n"); pdev->error_status = EIO; awake = 1; wake_up_interruptible(&pdev->frameq); @@ -661,7 +753,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) fbuf = pdev->fill_frame; if (fbuf == NULL) { - Err("pwc_isoc_handler without valid fill frame.\n"); + PWC_ERROR("pwc_isoc_handler without valid fill frame.\n"); awake = 1; goto handler_end; } @@ -688,7 +780,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) /* ...copy data to frame buffer, if possible */ if (flen + fbuf->filled > pdev->frame_total_size) { - Trace(TRACE_FLOW, "Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size); + PWC_DEBUG_FLOW("Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size); pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */ pdev->vframes_error++; } @@ -704,96 +796,28 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) /* Shorter packet... We probably have the end of an image-frame; wake up read() process and let select()/poll() do something. Decompression is done in user time over there. - */ + */ if (pdev->vsync == 2) { - /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus - frames on the USB wire after an exposure change. This conditition is - however detected in the cam and a bit is set in the header. - */ - if (pdev->type == 730) { - unsigned char *ptr = (unsigned char *)fbuf->data; - - if (ptr[1] == 1 && ptr[0] & 0x10) { -#if PWC_DEBUG - Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence); -#endif - pdev->drop_frames += 2; - pdev->vframes_error++; - } - if ((ptr[0] ^ pdev->vmirror) & 0x01) { - if (ptr[0] & 0x01) - Info("Snapshot button pressed.\n"); - else - Info("Snapshot button released.\n"); - } - if ((ptr[0] ^ pdev->vmirror) & 0x02) { - if (ptr[0] & 0x02) - Info("Image is mirrored.\n"); - else - Info("Image is normal.\n"); - } - pdev->vmirror = ptr[0] & 0x03; - /* Sometimes the trailer of the 730 is still sent as a 4 byte packet - after a short frame; this condition is filtered out specifically. A 4 byte - frame doesn't make sense anyway. - So we get either this sequence: - drop_bit set -> 4 byte frame -> short frame -> good frame - Or this one: - drop_bit set -> short frame -> good frame - So we drop either 3 or 2 frames in all! - */ - if (fbuf->filled == 4) - pdev->drop_frames++; + if (pwc_rcv_short_packet(pdev, fbuf)) { + awake = 1; + fbuf = pdev->fill_frame; } - - /* In case we were instructed to drop the frame, do so silently. - The buffer pointers are not updated either (but the counters are reset below). - */ - if (pdev->drop_frames > 0) - pdev->drop_frames--; - else { - /* Check for underflow first */ - if (fbuf->filled < pdev->frame_total_size) { - Trace(TRACE_FLOW, "Frame buffer underflow (%d bytes); discarded.\n", fbuf->filled); - pdev->vframes_error++; - } - else { - /* Send only once per EOF */ - awake = 1; /* delay wake_ups */ - - /* Find our next frame to fill. This will always succeed, since we - * nick a frame from either empty or full list, but if we had to - * take it from the full list, it means a frame got dropped. - */ - if (pwc_next_fill_frame(pdev)) { - pdev->vframes_dumped++; - if ((pdev->vframe_count > FRAME_LOWMARK) && (pwc_trace & TRACE_FLOW)) { - if (pdev->vframes_dumped < 20) - Trace(TRACE_FLOW, "Dumping frame %d.\n", pdev->vframe_count); - if (pdev->vframes_dumped == 20) - Trace(TRACE_FLOW, "Dumping frame %d (last message).\n", pdev->vframe_count); - } - } - fbuf = pdev->fill_frame; - } - } /* !drop_frames */ - pdev->vframe_count++; } fbuf->filled = 0; fillptr = fbuf->data; pdev->vsync = 1; - } /* .. flen < last_packet_size */ + } + pdev->vlast_packet_size = flen; } /* ..status == 0 */ -#if PWC_DEBUG - /* This is normally not interesting to the user, unless you are really debugging something */ else { + /* This is normally not interesting to the user, unless + * you are really debugging something */ static int iso_error = 0; iso_error++; if (iso_error < 20) - Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst); + PWC_DEBUG_FLOW("Iso frame %d of USB has error %d\n", i, fst); } -#endif } handler_end: @@ -803,11 +827,11 @@ handler_end: urb->dev = pdev->udev; i = usb_submit_urb(urb, GFP_ATOMIC); if (i != 0) - Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); + PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); } -static int pwc_isoc_init(struct pwc_device *pdev) +int pwc_isoc_init(struct pwc_device *pdev) { struct usb_device *udev; struct urb *urb; @@ -826,30 +850,38 @@ static int pwc_isoc_init(struct pwc_device *pdev) /* Get the current alternate interface, adjust packet size */ if (!udev->actconfig) return -EFAULT; - +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5) + idesc = &udev->actconfig->interface[0]->altsetting[pdev->valternate]; +#else intf = usb_ifnum_to_if(udev, 0); if (intf) idesc = usb_altnum_to_altsetting(intf, pdev->valternate); +#endif if (!idesc) return -EFAULT; /* Search video endpoint */ pdev->vmax_packet_size = -1; - for (i = 0; i < idesc->desc.bNumEndpoints; i++) + for (i = 0; i < idesc->desc.bNumEndpoints; i++) { if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) + pdev->vmax_packet_size = idesc->endpoint[i].desc.wMaxPacketSize; +#else pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize); +#endif break; } + } if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) { - Err("Failed to find packet size for video endpoint in current alternate setting.\n"); + PWC_ERROR("Failed to find packet size for video endpoint in current alternate setting.\n"); return -ENFILE; /* Odd error, that should be noticeable */ } /* Set alternate interface */ ret = 0; - Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate); + PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate); ret = usb_set_interface(pdev->udev, 0, pdev->valternate); if (ret < 0) return ret; @@ -857,12 +889,12 @@ static int pwc_isoc_init(struct pwc_device *pdev) for (i = 0; i < MAX_ISO_BUFS; i++) { urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); if (urb == NULL) { - Err("Failed to allocate urb %d\n", i); + PWC_ERROR("Failed to allocate urb %d\n", i); ret = -ENOMEM; break; } pdev->sbuf[i].urb = urb; - Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb); + PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb); } if (ret) { /* De-allocate in reverse order */ @@ -899,24 +931,26 @@ static int pwc_isoc_init(struct pwc_device *pdev) for (i = 0; i < MAX_ISO_BUFS; i++) { ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL); if (ret) - Err("isoc_init() submit_urb %d failed with error %d\n", i, ret); + PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret); else - Trace(TRACE_MEMORY, "URB 0x%p submitted.\n", pdev->sbuf[i].urb); + PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->sbuf[i].urb); } /* All is done... */ pdev->iso_init = 1; - Trace(TRACE_OPEN, "<< pwc_isoc_init()\n"); + PWC_DEBUG_OPEN("<< pwc_isoc_init()\n"); return 0; } -static void pwc_isoc_cleanup(struct pwc_device *pdev) +void pwc_isoc_cleanup(struct pwc_device *pdev) { int i; - Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n"); + PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n"); if (pdev == NULL) return; + if (pdev->iso_init == 0) + return; /* Unlinking ISOC buffers one by one */ for (i = 0; i < MAX_ISO_BUFS; i++) { @@ -925,10 +959,10 @@ static void pwc_isoc_cleanup(struct pwc_device *pdev) urb = pdev->sbuf[i].urb; if (urb != 0) { if (pdev->iso_init) { - Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb); + PWC_DEBUG_MEMORY("Unlinking URB %p\n", urb); usb_kill_urb(urb); } - Trace(TRACE_MEMORY, "Freeing URB\n"); + PWC_DEBUG_MEMORY("Freeing URB\n"); usb_free_urb(urb); pdev->sbuf[i].urb = NULL; } @@ -938,12 +972,12 @@ static void pwc_isoc_cleanup(struct pwc_device *pdev) is signalled by EPIPE) */ if (pdev->error_status && pdev->error_status != EPIPE) { - Trace(TRACE_OPEN, "Setting alternate interface 0.\n"); + PWC_DEBUG_OPEN("Setting alternate interface 0.\n"); usb_set_interface(pdev->udev, 0, 0); } pdev->iso_init = 0; - Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n"); + PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n"); } int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot) @@ -957,18 +991,18 @@ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_f /* Try to set video mode... */ start = ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot); if (ret) { - Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n"); + PWC_DEBUG_FLOW("pwc_set_video_mode attempt 1 failed.\n"); /* That failed... restore old mode (we know that worked) */ start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); if (start) { - Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n"); + PWC_DEBUG_FLOW("pwc_set_video_mode attempt 2 failed.\n"); } } if (start == 0) { if (pwc_isoc_init(pdev) < 0) { - Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n"); + PWC_WARNING("Failed to restart ISOC transfers in pwc_try_video_mode.\n"); ret = -EAGAIN; /* let's try again, who knows if it works a second time */ } } @@ -976,54 +1010,129 @@ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_f return ret; /* Return original error code */ } +/********* + * sysfs + *********/ +static struct pwc_device *cd_to_pwc(struct class_device *cd) +{ + struct video_device *vdev = to_video_device(cd); + return video_get_drvdata(vdev); +} + +static ssize_t show_pan_tilt(struct class_device *class_dev, char *buf) +{ + struct pwc_device *pdev = cd_to_pwc(class_dev); + return sprintf(buf, "%d %d\n", pdev->pan_angle, pdev->tilt_angle); +} + +static ssize_t store_pan_tilt(struct class_device *class_dev, const char *buf, + size_t count) +{ + struct pwc_device *pdev = cd_to_pwc(class_dev); + int pan, tilt; + int ret = -EINVAL; + + if (strncmp(buf, "reset", 5) == 0) + ret = pwc_mpt_reset(pdev, 0x3); + + else if (sscanf(buf, "%d %d", &pan, &tilt) > 0) + ret = pwc_mpt_set_angle(pdev, pan, tilt); + + if (ret < 0) + return ret; + return strlen(buf); +} +static CLASS_DEVICE_ATTR(pan_tilt, S_IRUGO | S_IWUSR, show_pan_tilt, + store_pan_tilt); + +static ssize_t show_snapshot_button_status(struct class_device *class_dev, char *buf) +{ + struct pwc_device *pdev = cd_to_pwc(class_dev); + int status = pdev->snapshot_button_status; + pdev->snapshot_button_status = 0; + return sprintf(buf, "%d\n", status); +} + +static CLASS_DEVICE_ATTR(button, S_IRUGO | S_IWUSR, show_snapshot_button_status, + NULL); + +static void pwc_create_sysfs_files(struct video_device *vdev) +{ + struct pwc_device *pdev = video_get_drvdata(vdev); + if (pdev->features & FEATURE_MOTOR_PANTILT) + video_device_create_file(vdev, &class_device_attr_pan_tilt); + video_device_create_file(vdev, &class_device_attr_button); +} + +static void pwc_remove_sysfs_files(struct video_device *vdev) +{ + struct pwc_device *pdev = video_get_drvdata(vdev); + if (pdev->features & FEATURE_MOTOR_PANTILT) + video_device_remove_file(vdev, &class_device_attr_pan_tilt); + video_device_remove_file(vdev, &class_device_attr_button); +} + +#if CONFIG_PWC_DEBUG +static const char *pwc_sensor_type_to_string(unsigned int sensor_type) +{ + switch(sensor_type) { + case 0x00: + return "Hyundai CMOS sensor"; + case 0x20: + return "Sony CCD sensor + TDA8787"; + case 0x2E: + return "Sony CCD sensor + Exas 98L59"; + case 0x2F: + return "Sony CCD sensor + ADI 9804"; + case 0x30: + return "Sharp CCD sensor + TDA8787"; + case 0x3E: + return "Sharp CCD sensor + Exas 98L59"; + case 0x3F: + return "Sharp CCD sensor + ADI 9804"; + case 0x40: + return "UPA 1021 sensor"; + case 0x100: + return "VGA sensor"; + case 0x101: + return "PAL MR sensor"; + default: + return "unknown type of sensor"; + } +} +#endif /***************************************************************************/ /* Video4Linux functions */ static int pwc_video_open(struct inode *inode, struct file *file) { - int i; + int i, ret; struct video_device *vdev = video_devdata(file); struct pwc_device *pdev; - Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev); + PWC_DEBUG_OPEN(">> video_open called(vdev = 0x%p).\n", vdev); pdev = (struct pwc_device *)vdev->priv; if (pdev == NULL) BUG(); - if (pdev->vopen) + if (pdev->vopen) { + PWC_DEBUG_OPEN("I'm busy, someone is using the device.\n"); return -EBUSY; + } down(&pdev->modlock); if (!pdev->usb_init) { - Trace(TRACE_OPEN, "Doing first time initialization.\n"); + PWC_DEBUG_OPEN("Doing first time initialization.\n"); pdev->usb_init = 1; - if (pwc_trace & TRACE_OPEN) + /* Query sensor type */ + ret = pwc_get_cmos_sensor(pdev, &i); + if (ret >= 0) { - /* Query sensor type */ - const char *sensor_type = NULL; - int ret; - - ret = pwc_get_cmos_sensor(pdev, &i); - if (ret >= 0) - { - switch(i) { - case 0x00: sensor_type = "Hyundai CMOS sensor"; break; - case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break; - case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59"; break; - case 0x2F: sensor_type = "Sony CCD sensor + ADI 9804"; break; - case 0x30: sensor_type = "Sharp CCD sensor + TDA8787"; break; - case 0x3E: sensor_type = "Sharp CCD sensor + Exas 98L59"; break; - case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break; - case 0x40: sensor_type = "UPA 1021 sensor"; break; - case 0x100: sensor_type = "VGA sensor"; break; - case 0x101: sensor_type = "PAL MR sensor"; break; - default: sensor_type = "unknown type of sensor"; break; - } - } - if (sensor_type != NULL) - Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i); + PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n", + pdev->vdev->name, + pwc_sensor_type_to_string(i), i); } } @@ -1031,34 +1140,32 @@ static int pwc_video_open(struct inode *inode, struct file *file) if (power_save) { i = pwc_camera_power(pdev, 1); if (i < 0) - Info("Failed to restore power to the camera! (%d)\n", i); + PWC_DEBUG_OPEN("Failed to restore power to the camera! (%d)\n", i); } /* Set LED on/off time */ if (pwc_set_leds(pdev, led_on, led_off) < 0) - Info("Failed to set LED on/off time.\n"); + PWC_DEBUG_OPEN("Failed to set LED on/off time.\n"); pwc_construct(pdev); /* set min/max sizes correct */ /* So far, so good. Allocate memory. */ i = pwc_allocate_buffers(pdev); if (i < 0) { - Trace(TRACE_OPEN, "Failed to allocate buffer memory.\n"); + PWC_DEBUG_OPEN("Failed to allocate buffers memory.\n"); + pwc_free_buffers(pdev); up(&pdev->modlock); return i; } /* Reset buffers & parameters */ pwc_reset_buffers(pdev); - for (i = 0; i < default_mbufs; i++) + for (i = 0; i < pwc_mbufs; i++) pdev->image_used[i] = 0; pdev->vframe_count = 0; pdev->vframes_dumped = 0; pdev->vframes_error = 0; pdev->visoc_errors = 0; pdev->error_status = 0; -#if PWC_DEBUG - pdev->sequence = 0; -#endif pwc_construct(pdev); /* set min/max sizes correct */ /* Set some defaults */ @@ -1070,29 +1177,44 @@ static int pwc_video_open(struct inode *inode, struct file *file) */ i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0); if (i) { - Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n"); - if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750) - i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QSIF].x, pwc_image_sizes[PSZ_QSIF].y, 10, pdev->vcompression, 0); + unsigned int default_resolution; + PWC_DEBUG_OPEN("First attempt at set_video_mode failed.\n"); + if (pdev->type>= 730) + default_resolution = PSZ_QSIF; else - i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QCIF].x, pwc_image_sizes[PSZ_QCIF].y, 10, pdev->vcompression, 0); + default_resolution = PSZ_QCIF; + + i = pwc_set_video_mode(pdev, + pwc_image_sizes[default_resolution].x, + pwc_image_sizes[default_resolution].y, + 10, + pdev->vcompression, + 0); } if (i) { - Trace(TRACE_OPEN, "Second attempt at set_video_mode failed.\n"); + PWC_DEBUG_OPEN("Second attempt at set_video_mode failed.\n"); + pwc_free_buffers(pdev); up(&pdev->modlock); return i; } i = pwc_isoc_init(pdev); if (i) { - Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i); + PWC_DEBUG_OPEN("Failed to init ISOC stuff = %d.\n", i); + pwc_isoc_cleanup(pdev); + pwc_free_buffers(pdev); up(&pdev->modlock); return i; } + /* Initialize the webcam to sane value */ + pwc_set_brightness(pdev, 0x7fff); + pwc_set_agc(pdev, 1, 0); + pdev->vopen++; file->private_data = vdev; up(&pdev->modlock); - Trace(TRACE_OPEN, "<< video_open() returns 0.\n"); + PWC_DEBUG_OPEN("<< video_open() returns 0.\n"); return 0; } @@ -1103,35 +1225,23 @@ static int pwc_video_close(struct inode *inode, struct file *file) struct pwc_device *pdev; int i; - Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev); + PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev); pdev = (struct pwc_device *)vdev->priv; if (pdev->vopen == 0) - Info("video_close() called on closed device?\n"); + PWC_DEBUG_MODULE("video_close() called on closed device?\n"); /* Dump statistics, but only if a reasonable amount of frames were processed (to prevent endless log-entries in case of snap-shot programs) */ if (pdev->vframe_count > 20) - Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error); + PWC_DEBUG_MODULE("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error); - switch (pdev->type) - { - case 675: - case 680: - case 690: - case 720: - case 730: - case 740: - case 750: -/* pwc_dec23_exit(); *//* Timon & Kiara */ - break; - case 645: - case 646: -/* pwc_dec1_exit(); */ - break; - } + if (DEVICE_USE_CODEC1(pdev->type)) + pwc_dec1_exit(); + else + pwc_dec23_exit(); pwc_isoc_cleanup(pdev); pwc_free_buffers(pdev); @@ -1140,15 +1250,15 @@ static int pwc_video_close(struct inode *inode, struct file *file) if (pdev->error_status != EPIPE) { /* Turn LEDs off */ if (pwc_set_leds(pdev, 0, 0) < 0) - Info("Failed to set LED on/off time.\n"); + PWC_DEBUG_MODULE("Failed to set LED on/off time.\n"); if (power_save) { i = pwc_camera_power(pdev, 0); if (i < 0) - Err("Failed to power down camera (%d)\n", i); + PWC_ERROR("Failed to power down camera (%d)\n", i); } } - pdev->vopen = 0; - Trace(TRACE_OPEN, "<< video_close()\n"); + pdev->vopen--; + PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen); return 0; } @@ -1164,7 +1274,7 @@ static int pwc_video_close(struct inode *inode, struct file *file) device is tricky anyhow. */ -static ssize_t pwc_video_read(struct file *file, char __user * buf, +static ssize_t pwc_video_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct video_device *vdev = file->private_data; @@ -1172,8 +1282,10 @@ static ssize_t pwc_video_read(struct file *file, char __user * buf, int noblock = file->f_flags & O_NONBLOCK; DECLARE_WAITQUEUE(wait, current); int bytes_to_read; + void *image_buffer_addr; - Trace(TRACE_READ, "video_read(0x%p, %p, %zu) called.\n", vdev, buf, count); + PWC_DEBUG_READ("pwc_video_read(vdev=0x%p, buf=%p, count=%zd) called.\n", + vdev, buf, count); if (vdev == NULL) return -EFAULT; pdev = vdev->priv; @@ -1214,16 +1326,19 @@ static ssize_t pwc_video_read(struct file *file, char __user * buf, return -EFAULT; } - Trace(TRACE_READ, "Copying data to user space.\n"); + PWC_DEBUG_READ("Copying data to user space.\n"); if (pdev->vpalette == VIDEO_PALETTE_RAW) - bytes_to_read = pdev->frame_size; + bytes_to_read = pdev->frame_size + sizeof(struct pwc_raw_frame); else bytes_to_read = pdev->view.size; /* copy bytes to user space; we allow for partial reads */ if (count + pdev->image_read_pos > bytes_to_read) count = bytes_to_read - pdev->image_read_pos; - if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count)) + image_buffer_addr = pdev->image_data; + image_buffer_addr += pdev->images[pdev->fill_image].offset; + image_buffer_addr += pdev->image_read_pos; + if (copy_to_user(buf, image_buffer_addr, count)) return -EFAULT; pdev->image_read_pos += count; if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */ @@ -1253,370 +1368,62 @@ static unsigned int pwc_video_poll(struct file *file, poll_table *wait) return 0; } -static int pwc_video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - struct video_device *vdev = file->private_data; - struct pwc_device *pdev; - DECLARE_WAITQUEUE(wait, current); - - if (vdev == NULL) - return -EFAULT; - pdev = vdev->priv; - if (pdev == NULL) - return -EFAULT; - - switch (cmd) { - /* Query cabapilities */ - case VIDIOCGCAP: - { - struct video_capability *caps = arg; - - strcpy(caps->name, vdev->name); - caps->type = VID_TYPE_CAPTURE; - caps->channels = 1; - caps->audios = 1; - caps->minwidth = pdev->view_min.x; - caps->minheight = pdev->view_min.y; - caps->maxwidth = pdev->view_max.x; - caps->maxheight = pdev->view_max.y; - break; - } - - /* Channel functions (simulate 1 channel) */ - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - - if (v->channel != 0) - return -EINVAL; - v->flags = 0; - v->tuners = 0; - v->type = VIDEO_TYPE_CAMERA; - strcpy(v->name, "Webcam"); - return 0; - } - - case VIDIOCSCHAN: - { - /* The spec says the argument is an integer, but - the bttv driver uses a video_channel arg, which - makes sense becasue it also has the norm flag. - */ - struct video_channel *v = arg; - if (v->channel != 0) - return -EINVAL; - return 0; - } - - - /* Picture functions; contrast etc. */ - case VIDIOCGPICT: - { - struct video_picture *p = arg; - int val; - - val = pwc_get_brightness(pdev); - if (val >= 0) - p->brightness = val; - else - p->brightness = 0xffff; - val = pwc_get_contrast(pdev); - if (val >= 0) - p->contrast = val; - else - p->contrast = 0xffff; - /* Gamma, Whiteness, what's the difference? :) */ - val = pwc_get_gamma(pdev); - if (val >= 0) - p->whiteness = val; - else - p->whiteness = 0xffff; - val = pwc_get_saturation(pdev); - if (val >= 0) - p->colour = val; - else - p->colour = 0xffff; - p->depth = 24; - p->palette = pdev->vpalette; - p->hue = 0xFFFF; /* N/A */ - break; - } - - case VIDIOCSPICT: - { - struct video_picture *p = arg; - /* - * FIXME: Suppose we are mid read - ANSWER: No problem: the firmware of the camera - can handle brightness/contrast/etc - changes at _any_ time, and the palette - is used exactly once in the uncompress - routine. - */ - pwc_set_brightness(pdev, p->brightness); - pwc_set_contrast(pdev, p->contrast); - pwc_set_gamma(pdev, p->whiteness); - pwc_set_saturation(pdev, p->colour); - if (p->palette && p->palette != pdev->vpalette) { - switch (p->palette) { - case VIDEO_PALETTE_YUV420P: - case VIDEO_PALETTE_RAW: - pdev->vpalette = p->palette; - return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); - break; - default: - return -EINVAL; - break; - } - } - break; - } - - /* Window/size parameters */ - case VIDIOCGWIN: - { - struct video_window *vw = arg; - - vw->x = 0; - vw->y = 0; - vw->width = pdev->view.x; - vw->height = pdev->view.y; - vw->chromakey = 0; - vw->flags = (pdev->vframes << PWC_FPS_SHIFT) | - (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0); - break; - } - - case VIDIOCSWIN: - { - struct video_window *vw = arg; - int fps, snapshot, ret; - - fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; - snapshot = vw->flags & PWC_FPS_SNAPSHOT; - if (fps == 0) - fps = pdev->vframes; - if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot) - return 0; - ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot); - if (ret) - return ret; - break; - } - - /* We don't have overlay support (yet) */ - case VIDIOCGFBUF: - { - struct video_buffer *vb = arg; - - memset(vb,0,sizeof(*vb)); - break; - } - - /* mmap() functions */ - case VIDIOCGMBUF: - { - /* Tell the user program how much memory is needed for a mmap() */ - struct video_mbuf *vm = arg; - int i; - - memset(vm, 0, sizeof(*vm)); - vm->size = default_mbufs * pdev->len_per_image; - vm->frames = default_mbufs; /* double buffering should be enough for most applications */ - for (i = 0; i < default_mbufs; i++) - vm->offsets[i] = i * pdev->len_per_image; - break; - } - - case VIDIOCMCAPTURE: - { - /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */ - struct video_mmap *vm = arg; - - Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format); - if (vm->frame < 0 || vm->frame >= default_mbufs) - return -EINVAL; - - /* xawtv is nasty. It probes the available palettes - by setting a very small image size and trying - various palettes... The driver doesn't support - such small images, so I'm working around it. - */ - if (vm->format) - { - switch (vm->format) - { - case VIDEO_PALETTE_YUV420P: - case VIDEO_PALETTE_RAW: - break; - default: - return -EINVAL; - break; - } - } - - if ((vm->width != pdev->view.x || vm->height != pdev->view.y) && - (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) { - int ret; - - Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n"); - ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot); - if (ret) - return ret; - } /* ... size mismatch */ - - /* FIXME: should we lock here? */ - if (pdev->image_used[vm->frame]) - return -EBUSY; /* buffer wasn't available. Bummer */ - pdev->image_used[vm->frame] = 1; - - /* Okay, we're done here. In the SYNC call we wait until a - frame comes available, then expand image into the given - buffer. - In contrast to the CPiA cam the Philips cams deliver a - constant stream, almost like a grabber card. Also, - we have separate buffers for the rawdata and the image, - meaning we can nearly always expand into the requested buffer. - */ - Trace(TRACE_READ, "VIDIOCMCAPTURE done.\n"); - break; - } - - case VIDIOCSYNC: - { - /* The doc says: "Whenever a buffer is used it should - call VIDIOCSYNC to free this frame up and continue." - - The only odd thing about this whole procedure is - that MCAPTURE flags the buffer as "in use", and - SYNC immediately unmarks it, while it isn't - after SYNC that you know that the buffer actually - got filled! So you better not start a CAPTURE in - the same frame immediately (use double buffering). - This is not a problem for this cam, since it has - extra intermediate buffers, but a hardware - grabber card will then overwrite the buffer - you're working on. - */ - int *mbuf = arg; - int ret; - - Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", *mbuf); - - /* bounds check */ - if (*mbuf < 0 || *mbuf >= default_mbufs) - return -EINVAL; - /* check if this buffer was requested anyway */ - if (pdev->image_used[*mbuf] == 0) - return -EINVAL; - - /* Add ourselves to the frame wait-queue. - - FIXME: needs auditing for safety. - QUESTION: In what respect? I think that using the - frameq is safe now. - */ - add_wait_queue(&pdev->frameq, &wait); - while (pdev->full_frames == NULL) { - if (pdev->error_status) { - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - return -pdev->error_status; - } - - if (signal_pending(current)) { - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - return -ERESTARTSYS; - } - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - - /* The frame is ready. Expand in the image buffer - requested by the user. I don't care if you - mmap() 5 buffers and request data in this order: - buffer 4 2 3 0 1 2 3 0 4 3 1 . . . - Grabber hardware may not be so forgiving. - */ - Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n"); - pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */ - /* Decompress, etc */ - ret = pwc_handle_frame(pdev); - pdev->image_used[*mbuf] = 0; - if (ret) - return -EFAULT; - break; - } - - case VIDIOCGAUDIO: - { - struct video_audio *v = arg; - - strcpy(v->name, "Microphone"); - v->audio = -1; /* unknown audio minor */ - v->flags = 0; - v->mode = VIDEO_SOUND_MONO; - v->volume = 0; - v->bass = 0; - v->treble = 0; - v->balance = 0x8000; - v->step = 1; - break; - } - - case VIDIOCSAUDIO: - { - /* Dummy: nothing can be set */ - break; - } - - case VIDIOCGUNIT: - { - struct video_unit *vu = arg; - - vu->video = pdev->vdev->minor & 0x3F; - vu->audio = -1; /* not known yet */ - vu->vbi = -1; - vu->radio = -1; - vu->teletext = -1; - break; - } - default: - return pwc_ioctl(pdev, cmd, arg); - } /* ..switch */ - return 0; -} - static int pwc_video_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { return video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl); } - static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) { struct video_device *vdev = file->private_data; struct pwc_device *pdev; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - unsigned long page, pos; + unsigned long start; + unsigned long size; + unsigned long page, pos = 0; + int index; - Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size); + PWC_DEBUG_MEMORY(">> %s\n", __FUNCTION__); pdev = vdev->priv; + size = vma->vm_end - vma->vm_start; + start = vma->vm_start; + + /* Find the idx buffer for this mapping */ + for (index = 0; index < pwc_mbufs; index++) { + pos = pdev->images[index].offset; + if ((pos>>PAGE_SHIFT) == vma->vm_pgoff) + break; + } + if (index == MAX_IMAGES) + return -EINVAL; + if (index == 0) { + /* + * Special case for v4l1. In v4l1, we map only one big buffer, + * but in v4l2 each buffer is mapped + */ + unsigned long total_size; + total_size = pwc_mbufs * pdev->len_per_image; + if (size != pdev->len_per_image && size != total_size) { + PWC_ERROR("Wrong size (%lu) needed to be len_per_image=%d or total_size=%lu\n", + size, pdev->len_per_image, total_size); + return -EINVAL; + } + } else if (size > pdev->len_per_image) + return -EINVAL; - vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_IO; /* from 2.6.9-acX */ - pos = (unsigned long)pdev->image_data; + pos += (unsigned long)pdev->image_data; while (size > 0) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) + page = kvirt_to_pa(pos); + if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; +#else page = vmalloc_to_pfn((void *)pos); if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) return -EAGAIN; - +#endif start += PAGE_SIZE; pos += PAGE_SIZE; if (size > PAGE_SIZE) @@ -1624,7 +1431,6 @@ static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) else size = 0; } - return 0; } @@ -1645,10 +1451,17 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id int video_nr = -1; /* default: use next available device */ char serial_number[30], *name; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) + vendor_id = udev->descriptor.idVendor; + product_id = udev->descriptor.idProduct; +#else + vendor_id = le16_to_cpu(udev->descriptor.idVendor); + product_id = le16_to_cpu(udev->descriptor.idProduct); +#endif + /* Check if we can handle this device */ - Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct), + PWC_DEBUG_PROBE("probe() called [%04X %04X], if %d\n", + vendor_id, product_id, intf->altsetting->desc.bInterfaceNumber); /* the interfaces are probed one by one. We are only interested in the @@ -1658,61 +1471,63 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id if (intf->altsetting->desc.bInterfaceNumber > 0) return -ENODEV; - vendor_id = le16_to_cpu(udev->descriptor.idVendor); - product_id = le16_to_cpu(udev->descriptor.idProduct); - if (vendor_id == 0x0471) { switch (product_id) { case 0x0302: - Info("Philips PCA645VC USB webcam detected.\n"); + PWC_INFO("Philips PCA645VC USB webcam detected.\n"); name = "Philips 645 webcam"; type_id = 645; break; case 0x0303: - Info("Philips PCA646VC USB webcam detected.\n"); + PWC_INFO("Philips PCA646VC USB webcam detected.\n"); name = "Philips 646 webcam"; type_id = 646; break; case 0x0304: - Info("Askey VC010 type 2 USB webcam detected.\n"); + PWC_INFO("Askey VC010 type 2 USB webcam detected.\n"); name = "Askey VC010 webcam"; type_id = 646; break; case 0x0307: - Info("Philips PCVC675K (Vesta) USB webcam detected.\n"); + PWC_INFO("Philips PCVC675K (Vesta) USB webcam detected.\n"); name = "Philips 675 webcam"; type_id = 675; break; case 0x0308: - Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n"); + PWC_INFO("Philips PCVC680K (Vesta Pro) USB webcam detected.\n"); name = "Philips 680 webcam"; type_id = 680; break; case 0x030C: - Info("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n"); + PWC_INFO("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n"); name = "Philips 690 webcam"; type_id = 690; break; case 0x0310: - Info("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n"); + PWC_INFO("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n"); name = "Philips 730 webcam"; type_id = 730; break; case 0x0311: - Info("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n"); + PWC_INFO("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n"); name = "Philips 740 webcam"; type_id = 740; break; case 0x0312: - Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n"); + PWC_INFO("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n"); name = "Philips 750 webcam"; type_id = 750; break; case 0x0313: - Info("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n"); + PWC_INFO("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n"); name = "Philips 720K/40 webcam"; type_id = 720; break; + case 0x0329: + PWC_INFO("Philips SPC 900NC USB webcam detected.\n"); + name = "Philips SPC 900NC webcam"; + type_id = 720; + break; default: return -ENODEV; break; @@ -1721,7 +1536,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id else if (vendor_id == 0x069A) { switch(product_id) { case 0x0001: - Info("Askey VC010 type 1 USB webcam detected.\n"); + PWC_INFO("Askey VC010 type 1 USB webcam detected.\n"); name = "Askey VC010 webcam"; type_id = 645; break; @@ -1733,32 +1548,33 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id else if (vendor_id == 0x046d) { switch(product_id) { case 0x08b0: - Info("Logitech QuickCam Pro 3000 USB webcam detected.\n"); + PWC_INFO("Logitech QuickCam Pro 3000 USB webcam detected.\n"); name = "Logitech QuickCam Pro 3000"; type_id = 740; /* CCD sensor */ break; case 0x08b1: - Info("Logitech QuickCam Notebook Pro USB webcam detected.\n"); + PWC_INFO("Logitech QuickCam Notebook Pro USB webcam detected.\n"); name = "Logitech QuickCam Notebook Pro"; type_id = 740; /* CCD sensor */ break; case 0x08b2: - Info("Logitech QuickCam 4000 Pro USB webcam detected.\n"); + PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n"); name = "Logitech QuickCam Pro 4000"; type_id = 740; /* CCD sensor */ break; case 0x08b3: - Info("Logitech QuickCam Zoom USB webcam detected.\n"); + PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n"); name = "Logitech QuickCam Zoom"; type_id = 740; /* CCD sensor */ break; case 0x08B4: - Info("Logitech QuickCam Zoom (new model) USB webcam detected.\n"); + PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n"); name = "Logitech QuickCam Zoom"; type_id = 740; /* CCD sensor */ + power_save = 1; break; case 0x08b5: - Info("Logitech QuickCam Orbit/Sphere USB webcam detected.\n"); + PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n"); name = "Logitech QuickCam Orbit"; type_id = 740; /* CCD sensor */ features |= FEATURE_MOTOR_PANTILT; @@ -1766,7 +1582,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id case 0x08b6: case 0x08b7: case 0x08b8: - Info("Logitech QuickCam detected (reserved ID).\n"); + PWC_INFO("Logitech QuickCam detected (reserved ID).\n"); name = "Logitech QuickCam (res.)"; type_id = 730; /* Assuming CMOS */ break; @@ -1782,15 +1598,20 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id */ switch(product_id) { case 0x9000: - Info("Samsung MPC-C10 USB webcam detected.\n"); + PWC_INFO("Samsung MPC-C10 USB webcam detected.\n"); name = "Samsung MPC-C10"; type_id = 675; break; case 0x9001: - Info("Samsung MPC-C30 USB webcam detected.\n"); + PWC_INFO("Samsung MPC-C30 USB webcam detected.\n"); name = "Samsung MPC-C30"; type_id = 675; break; + case 0x9002: + PWC_INFO("Samsung SNC-35E (v3.0) USB webcam detected.\n"); + name = "Samsung MPC-C30"; + type_id = 740; + break; default: return -ENODEV; break; @@ -1799,12 +1620,12 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id else if (vendor_id == 0x041e) { switch(product_id) { case 0x400c: - Info("Creative Labs Webcam 5 detected.\n"); + PWC_INFO("Creative Labs Webcam 5 detected.\n"); name = "Creative Labs Webcam 5"; type_id = 730; break; case 0x4011: - Info("Creative Labs Webcam Pro Ex detected.\n"); + PWC_INFO("Creative Labs Webcam Pro Ex detected.\n"); name = "Creative Labs Webcam Pro Ex"; type_id = 740; break; @@ -1816,7 +1637,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id else if (vendor_id == 0x04cc) { switch(product_id) { case 0x8116: - Info("Sotec Afina Eye USB webcam detected.\n"); + PWC_INFO("Sotec Afina Eye USB webcam detected.\n"); name = "Sotec Afina Eye"; type_id = 730; break; @@ -1829,7 +1650,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id switch(product_id) { case 0x8116: /* This is essentially the same cam as the Sotec Afina Eye */ - Info("AME Co. Afina Eye USB webcam detected.\n"); + PWC_INFO("AME Co. Afina Eye USB webcam detected.\n"); name = "AME Co. Afina Eye"; type_id = 750; break; @@ -1842,12 +1663,12 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id else if (vendor_id == 0x0d81) { switch(product_id) { case 0x1900: - Info("Visionite VCS-UC300 USB webcam detected.\n"); + PWC_INFO("Visionite VCS-UC300 USB webcam detected.\n"); name = "Visionite VCS-UC300"; type_id = 740; /* CCD sensor */ break; case 0x1910: - Info("Visionite VCS-UM100 USB webcam detected.\n"); + PWC_INFO("Visionite VCS-UM100 USB webcam detected.\n"); name = "Visionite VCS-UM100"; type_id = 730; /* CMOS sensor */ break; @@ -1861,15 +1682,15 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id memset(serial_number, 0, 30); usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29); - Trace(TRACE_PROBE, "Device serial number is %s\n", serial_number); + PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number); if (udev->descriptor.bNumConfigurations > 1) - Info("Warning: more than 1 configuration available.\n"); + PWC_WARNING("Warning: more than 1 configuration available.\n"); /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */ pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL); if (pdev == NULL) { - Err("Oops, could not allocate memory for pwc_device.\n"); + PWC_ERROR("Oops, could not allocate memory for pwc_device.\n"); return -ENOMEM; } pdev->type = type_id; @@ -1900,17 +1721,22 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vdev = video_device_alloc(); if (pdev->vdev == 0) { - Err("Err, cannot allocate video_device struture. Failing probe."); + PWC_ERROR("Err, cannot allocate video_device struture. Failing probe."); kfree(pdev); return -ENOMEM; } memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template)); + pdev->vdev->dev = &(udev->dev); strcpy(pdev->vdev->name, name); pdev->vdev->owner = THIS_MODULE; video_set_drvdata(pdev->vdev, pdev); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) + pdev->release = udev->descriptor.bcdDevice; +#else pdev->release = le16_to_cpu(udev->descriptor.bcdDevice); - Trace(TRACE_PROBE, "Release: %04x\n", pdev->release); +#endif + PWC_DEBUG_PROBE("Release: %04x\n", pdev->release); /* Now search device_hint[] table for a match, so we can hint a node number. */ for (hint = 0; hint < MAX_DEV_HINTS; hint++) { @@ -1920,7 +1746,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) { /* match! */ video_nr = device_hint[hint].device_node; - Trace(TRACE_PROBE, "Found hint, will try to register as /dev/video%d\n", video_nr); + PWC_DEBUG_PROBE("Found hint, will try to register as /dev/video%d\n", video_nr); break; } } @@ -1929,21 +1755,27 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vdev->release = video_device_release; i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr); if (i < 0) { - Err("Failed to register as video device (%d).\n", i); + PWC_ERROR("Failed to register as video device (%d).\n", i); video_device_release(pdev->vdev); /* Drip... drip... drip... */ kfree(pdev); /* Oops, no memory leaks please */ return -EIO; } else { - Info("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F); + PWC_INFO("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F); } /* occupy slot */ if (hint < MAX_DEV_HINTS) device_hint[hint].pdev = pdev; - Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev); + PWC_DEBUG_PROBE("probe() function returning struct at 0x%p.\n", pdev); usb_set_intfdata (intf, pdev); + pwc_create_sysfs_files(pdev->vdev); + + /* Set the leds off */ + pwc_set_leds(pdev, 0, 0); + pwc_camera_power(pdev, 0); + return 0; } @@ -1957,27 +1789,21 @@ static void usb_pwc_disconnect(struct usb_interface *intf) pdev = usb_get_intfdata (intf); usb_set_intfdata (intf, NULL); if (pdev == NULL) { - Err("pwc_disconnect() Called without private pointer.\n"); + PWC_ERROR("pwc_disconnect() Called without private pointer.\n"); goto disconnect_out; } if (pdev->udev == NULL) { - Err("pwc_disconnect() already called for %p\n", pdev); + PWC_ERROR("pwc_disconnect() already called for %p\n", pdev); goto disconnect_out; } if (pdev->udev != interface_to_usbdev(intf)) { - Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n"); - goto disconnect_out; - } -#ifdef PWC_MAGIC - if (pdev->magic != PWC_MAGIC) { - Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n"); + PWC_ERROR("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n"); goto disconnect_out; } -#endif /* We got unplugged; this is signalled by an EPIPE error code */ if (pdev->vopen) { - Info("Disconnected while webcam is in use!\n"); + PWC_INFO("Disconnected while webcam is in use!\n"); pdev->error_status = EPIPE; } @@ -1987,7 +1813,8 @@ static void usb_pwc_disconnect(struct usb_interface *intf) while (pdev->vopen) schedule(); /* Device is now closed, so we can safely unregister it */ - Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n"); + PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n"); + pwc_remove_sysfs_files(pdev->vdev); video_unregister_device(pdev->vdev); /* Free memory (don't set pdev to 0 just yet) */ @@ -2021,58 +1848,69 @@ static int pwc_atoi(const char *s) * Initialization code & module stuff */ -static char size[10]; -static int fps = 0; -static int fbufs = 0; -static int mbufs = 0; -static int trace = -1; +static char *size; +static int fps; +static int fbufs; +static int mbufs; static int compression = -1; static int leds[2] = { -1, -1 }; -static char *dev_hint[MAX_DEV_HINTS] = { }; +static int leds_nargs; +static char *dev_hint[MAX_DEV_HINTS]; +static int dev_hint_nargs; + +module_param(size, charp, 0444); +module_param(fps, int, 0444); +module_param(fbufs, int, 0444); +module_param(mbufs, int, 0444); +#if CONFIG_PWC_DEBUG +module_param_named(trace, pwc_trace, int, 0644); +#endif +module_param(power_save, int, 0444); +module_param(compression, int, 0444); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) +module_param_array(leds, int, leds_nargs, 0444); +module_param_array(dev_hint, charp, dev_hint_nargs, 0444); +#else +module_param_array(leds, int, &leds_nargs, 0444); +module_param_array(dev_hint, charp, &dev_hint_nargs, 0444); +#endif -module_param_string(size, size, sizeof(size), 0); MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga"); -module_param(fps, int, 0000); MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30"); -module_param(fbufs, int, 0000); MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve"); -module_param(mbufs, int, 0000); MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers"); -module_param(trace, int, 0000); MODULE_PARM_DESC(trace, "For debugging purposes"); -module_param(power_save, bool, 0000); MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off"); -module_param(compression, int, 0000); MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)"); -module_param_array(leds, int, NULL, 0000); MODULE_PARM_DESC(leds, "LED on,off time in milliseconds"); -module_param_array(dev_hint, charp, NULL, 0000); MODULE_PARM_DESC(dev_hint, "Device node hints"); MODULE_DESCRIPTION("Philips & OEM USB webcam driver"); MODULE_AUTHOR("Luc Saillard <luc@saillard.org>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("pwcx"); +MODULE_VERSION( PWC_VERSION ); static int __init usb_pwc_init(void) { int i, sz; char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" }; - Info("Philips webcam module version " PWC_VERSION " loaded.\n"); - Info("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n"); - Info("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n"); - Info("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n"); + PWC_INFO("Philips webcam module version " PWC_VERSION " loaded.\n"); + PWC_INFO("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n"); + PWC_INFO("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n"); + PWC_INFO("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n"); if (fps) { if (fps < 4 || fps > 30) { - Err("Framerate out of bounds (4-30).\n"); + PWC_ERROR("Framerate out of bounds (4-30).\n"); return -EINVAL; } default_fps = fps; - Info("Default framerate set to %d.\n", default_fps); + PWC_DEBUG_MODULE("Default framerate set to %d.\n", default_fps); } - if (size[0]) { + if (size) { /* string; try matching with array */ for (sz = 0; sz < PSZ_MAX; sz++) { if (!strcmp(sizenames[sz], size)) { /* Found! */ @@ -2081,41 +1919,42 @@ static int __init usb_pwc_init(void) } } if (sz == PSZ_MAX) { - Err("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n"); + PWC_ERROR("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n"); return -EINVAL; } - Info("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y); + PWC_DEBUG_MODULE("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y); } if (mbufs) { if (mbufs < 1 || mbufs > MAX_IMAGES) { - Err("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES); + PWC_ERROR("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES); return -EINVAL; } - default_mbufs = mbufs; - Info("Number of image buffers set to %d.\n", default_mbufs); + pwc_mbufs = mbufs; + PWC_DEBUG_MODULE("Number of image buffers set to %d.\n", pwc_mbufs); } if (fbufs) { if (fbufs < 2 || fbufs > MAX_FRAMES) { - Err("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES); + PWC_ERROR("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES); return -EINVAL; } default_fbufs = fbufs; - Info("Number of frame buffers set to %d.\n", default_fbufs); + PWC_DEBUG_MODULE("Number of frame buffers set to %d.\n", default_fbufs); } - if (trace >= 0) { - Info("Trace options: 0x%04x\n", trace); - pwc_trace = trace; +#if CONFIG_PWC_DEBUG + if (pwc_trace >= 0) { + PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace); } +#endif if (compression >= 0) { if (compression > 3) { - Err("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n"); + PWC_ERROR("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n"); return -EINVAL; } pwc_preferred_compression = compression; - Info("Preferred compression set to %d.\n", pwc_preferred_compression); + PWC_DEBUG_MODULE("Preferred compression set to %d.\n", pwc_preferred_compression); } if (power_save) - Info("Enabling power save on open/close.\n"); + PWC_DEBUG_MODULE("Enabling power save on open/close.\n"); if (leds[0] >= 0) led_on = leds[0]; if (leds[1] >= 0) @@ -2146,14 +1985,14 @@ static int __init usb_pwc_init(void) dot++; /* Few sanity checks */ if (*dot != '\0' && dot > colon) { - Err("Malformed camera hint: the colon must be after the dot.\n"); + PWC_ERROR("Malformed camera hint: the colon must be after the dot.\n"); return -EINVAL; } if (*colon == '\0') { /* No colon */ if (*dot != '\0') { - Err("Malformed camera hint: no colon + device node given.\n"); + PWC_ERROR("Malformed camera hint: no colon + device node given.\n"); return -EINVAL; } else { @@ -2178,28 +2017,27 @@ static int __init usb_pwc_init(void) device_hint[i].serial_number[k] = '\0'; } } -#if PWC_DEBUG - Debug("device_hint[%d]:\n", i); - Debug(" type : %d\n", device_hint[i].type); - Debug(" serial# : %s\n", device_hint[i].serial_number); - Debug(" node : %d\n", device_hint[i].device_node); -#endif + PWC_TRACE("device_hint[%d]:\n", i); + PWC_TRACE(" type : %d\n", device_hint[i].type); + PWC_TRACE(" serial# : %s\n", device_hint[i].serial_number); + PWC_TRACE(" node : %d\n", device_hint[i].device_node); } else device_hint[i].type = 0; /* not filled */ } /* ..for MAX_DEV_HINTS */ - Trace(TRACE_PROBE, "Registering driver at address 0x%p.\n", &pwc_driver); + PWC_DEBUG_PROBE("Registering driver at address 0x%p.\n", &pwc_driver); return usb_register(&pwc_driver); } static void __exit usb_pwc_exit(void) { - Trace(TRACE_MODULE, "Deregistering driver.\n"); + PWC_DEBUG_MODULE("Deregistering driver.\n"); usb_deregister(&pwc_driver); - Info("Philips webcam module removed.\n"); + PWC_INFO("Philips webcam module removed.\n"); } module_init(usb_pwc_init); module_exit(usb_pwc_exit); +/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/linux/drivers/media/video/pwc/pwc-kiara.c b/linux/drivers/media/video/pwc/pwc-kiara.c index 4c96037f7..fec39cc5a 100644 --- a/linux/drivers/media/video/pwc/pwc-kiara.c +++ b/linux/drivers/media/video/pwc/pwc-kiara.c @@ -1,5 +1,5 @@ /* Linux driver for Philips webcam - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -316,3 +316,576 @@ const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] = }, }; + +/* + * Rom table for kiara chips + * + * 32 roms tables (one for each resolution ?) + * 2 tables per roms (one for each passes) (Y, and U&V) + * 128 bytes per passes + */ + +const unsigned int KiaraRomTable [8][2][16][8] = +{ + { /* version 0 */ + { /* version 0, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009292,0x00009292,0x00009493,0x000124db}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x0000a493,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x000124db,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 0, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000001,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009292, + 0x00009492,0x00009493,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000126dc,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 1 */ + { /* version 1, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009492,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 1, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000049,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 2 */ + { /* version 2, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x0000a49b}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 2, passes 1 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x0000a49b,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x0001249b,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 3 */ + { /* version 3, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001b925,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 3, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 4 */ + { /* version 4, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 4, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 5 */ + { /* version 5, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 5, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x00009252,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 6 */ + { /* version 6, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 6, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 7 */ + { /* version 7, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 7, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + } +}; + diff --git a/linux/drivers/media/video/pwc/pwc-kiara.h b/linux/drivers/media/video/pwc/pwc-kiara.h index 12929abbb..0bdb22547 100644 --- a/linux/drivers/media/video/pwc/pwc-kiara.h +++ b/linux/drivers/media/video/pwc/pwc-kiara.h @@ -1,5 +1,5 @@ /* Linux driver for Philips webcam - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -27,7 +27,7 @@ #ifndef PWC_KIARA_H #define PWC_KIARA_H -#include "pwc-ioctl.h" +#include <media/pwc-ioctl.h> struct Kiara_table_entry { @@ -37,8 +37,8 @@ struct Kiara_table_entry unsigned char mode[12]; /* precomputed mode settings for cam */ }; -const extern struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4]; -const extern unsigned int KiaraRomTable[8][2][16][8]; +extern const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4]; +extern const unsigned int KiaraRomTable[8][2][16][8]; #endif diff --git a/linux/drivers/media/video/pwc/pwc-misc.c b/linux/drivers/media/video/pwc/pwc-misc.c index 58fe79747..589c68743 100644 --- a/linux/drivers/media/video/pwc/pwc-misc.c +++ b/linux/drivers/media/video/pwc/pwc-misc.c @@ -1,7 +1,7 @@ /* Linux driver for Philips webcam Various miscellaneous functions and tables. (C) 1999-2003 Nemosoft Unv. - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -24,18 +24,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <linux/slab.h> #include "pwc.h" -struct pwc_coord pwc_image_sizes[PSZ_MAX] = +const struct pwc_coord pwc_image_sizes[PSZ_MAX] = { - { 128, 96, 0 }, - { 160, 120, 0 }, - { 176, 144, 0 }, - { 320, 240, 0 }, - { 352, 288, 0 }, - { 640, 480, 0 }, + { 128, 96, 0 }, /* sqcif */ + { 160, 120, 0 }, /* qsif */ + { 176, 144, 0 }, /* qcif */ + { 320, 240, 0 }, /* sif */ + { 352, 288, 0 }, /* cif */ + { 640, 480, 0 }, /* vga */ }; /* x,y -> PSZ_ */ @@ -52,7 +51,7 @@ int pwc_decode_size(struct pwc_device *pdev, int width, int height) { if (width > pdev->abs_max.x || height > pdev->abs_max.y) { - Debug("VIDEO_PALETTE_RAW: going beyond abs_max.\n"); + PWC_DEBUG_SIZE("VIDEO_PALETTE_RAW: going beyond abs_max.\n"); return -1; } } @@ -60,7 +59,7 @@ int pwc_decode_size(struct pwc_device *pdev, int width, int height) { if (width > pdev->view_max.x || height > pdev->view_max.y) { - Debug("VIDEO_PALETTE_ not RAW: going beyond view_max.\n"); + PWC_DEBUG_SIZE("VIDEO_PALETTE_not RAW: going beyond view_max.\n"); return -1; } } @@ -81,9 +80,8 @@ int pwc_decode_size(struct pwc_device *pdev, int width, int height) /* initialize variables depending on type and decompressor*/ void pwc_construct(struct pwc_device *pdev) { - switch(pdev->type) { - case 645: - case 646: + if (DEVICE_USE_CODEC1(pdev->type)) { + pdev->view_min.x = 128; pdev->view_min.y = 96; pdev->view_max.x = 352; @@ -95,10 +93,23 @@ void pwc_construct(struct pwc_device *pdev) pdev->vendpoint = 4; pdev->frame_header_size = 0; pdev->frame_trailer_size = 0; - break; - case 675: - case 680: - case 690: + + } else if (DEVICE_USE_CODEC3(pdev->type)) { + + pdev->view_min.x = 160; + pdev->view_min.y = 120; + pdev->view_max.x = 640; + pdev->view_max.y = 480; + pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; + pdev->abs_max.x = 640; + pdev->abs_max.y = 480; + pdev->vcinterface = 3; + pdev->vendpoint = 5; + pdev->frame_header_size = TOUCAM_HEADER_SIZE; + pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE; + + } else /* if (DEVICE_USE_CODEC2(pdev->type)) */ { + pdev->view_min.x = 128; pdev->view_min.y = 96; /* Anthill bug #38: PWC always reports max size, even without PWCX */ @@ -111,30 +122,12 @@ void pwc_construct(struct pwc_device *pdev) pdev->vendpoint = 4; pdev->frame_header_size = 0; pdev->frame_trailer_size = 0; - break; - case 720: - case 730: - case 740: - case 750: - pdev->view_min.x = 160; - pdev->view_min.y = 120; - pdev->view_max.x = 640; - pdev->view_max.y = 480; - pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; - pdev->abs_max.x = 640; - pdev->abs_max.y = 480; - pdev->vcinterface = 3; - pdev->vendpoint = 5; - pdev->frame_header_size = TOUCAM_HEADER_SIZE; - pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE; - break; } - Debug("type = %d\n",pdev->type); pdev->vpalette = VIDEO_PALETTE_YUV420P; /* default */ pdev->view_min.size = pdev->view_min.x * pdev->view_min.y; pdev->view_max.size = pdev->view_max.x * pdev->view_max.y; /* length of image, in YUV format; always allocate enough memory. */ - pdev->len_per_image = (pdev->abs_max.x * pdev->abs_max.y * 3) / 2; + pdev->len_per_image = PAGE_ALIGN((pdev->abs_max.x * pdev->abs_max.y * 3) / 2); } diff --git a/linux/drivers/media/video/pwc/pwc-timon.c b/linux/drivers/media/video/pwc/pwc-timon.c index 175250d08..be65bdcd1 100644 --- a/linux/drivers/media/video/pwc/pwc-timon.c +++ b/linux/drivers/media/video/pwc/pwc-timon.c @@ -1,5 +1,5 @@ /* Linux driver for Philips webcam - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -314,3 +314,1133 @@ const struct Timon_table_entry Timon_table[PSZ_MAX][6][4] = }, }; +/* + * 16 versions: + * 2 tables (one for Y, and one for U&V) + * 16 levels of details per tables + * 8 blocs + */ + +const unsigned int TimonRomTable [16][2][16][8] = +{ + { /* version 0 */ + { /* version 0, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000001,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000001, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 0, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000001,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000001, + 0x00000001,0x00000009,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000009,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000009,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 1 */ + { /* version 1, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 1, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000001,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000049,0x00000249,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 2 */ + { /* version 2, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009492,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 2, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000049,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 3 */ + { /* version 3, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009292,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 3, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00000049,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009292, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 4 */ + { /* version 4, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x0000a49b}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 4, passes 1 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x0000a49b,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x0001249b,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 5 */ + { /* version 5, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 5, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x000124db,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009493,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 6 */ + { /* version 6, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001b925,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 6, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 7 */ + { /* version 7, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x0002496e}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001c96e,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0002496d,0x00025bb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 7, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001b724,0x0001b925,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 8 */ + { /* version 8, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x00024b76,0x00024b77}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x00024b76,0x00025bbf}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001c92d,0x00024b76,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001b724,0x00024b6d,0x0002ddb6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 8, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0002496d,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 9 */ + { /* version 9, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 9, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 10 */ + { /* version 10, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0002496d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 10, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x00009493,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009493,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 11 */ + { /* version 11, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 11, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x00009252,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 12 */ + { /* version 12, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 12, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 13 */ + { /* version 13, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 13, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 14 */ + { /* version 14, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0002496d,0x00024b76,0x00024b77}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002db6d,0x00036db6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 14, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000136e4,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 15 */ + { /* version 15, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0002496d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x00024b6d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002db6d,0x00036db6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 15, passes 1 */ + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009292,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000124db,0x0001b724,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x0001b724,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x0001b724,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x0001b724,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001c924,0x0001b724,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001c924,0x0001b724,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001c924,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002496d,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + } +}; diff --git a/linux/drivers/media/video/pwc/pwc-timon.h b/linux/drivers/media/video/pwc/pwc-timon.h index a86b3782a..eef9e2cd4 100644 --- a/linux/drivers/media/video/pwc/pwc-timon.h +++ b/linux/drivers/media/video/pwc/pwc-timon.h @@ -1,5 +1,5 @@ /* Linux driver for Philips webcam - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -42,7 +42,7 @@ #ifndef PWC_TIMON_H #define PWC_TIMON_H -#include "pwc-ioctl.h" +#include <media/pwc-ioctl.h> struct Timon_table_entry { @@ -52,8 +52,8 @@ struct Timon_table_entry unsigned char mode[13]; /* precomputed mode settings for cam */ }; -const extern struct Timon_table_entry Timon_table[PSZ_MAX][6][4]; -const extern unsigned int TimonRomTable [16][2][16][8]; +extern const struct Timon_table_entry Timon_table[PSZ_MAX][6][4]; +extern const unsigned int TimonRomTable [16][2][16][8]; #endif diff --git a/linux/drivers/media/video/pwc/pwc-uncompress.c b/linux/drivers/media/video/pwc/pwc-uncompress.c index eec2c3c45..5d82028ef 100644 --- a/linux/drivers/media/video/pwc/pwc-uncompress.c +++ b/linux/drivers/media/video/pwc/pwc-uncompress.c @@ -1,7 +1,7 @@ /* Linux driver for Philips webcam Decompression frontend. (C) 1999-2003 Nemosoft Unv. - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -22,6 +22,8 @@ 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 + + vim: set ts=8: */ #include <asm/current.h> @@ -29,6 +31,8 @@ #include "pwc.h" #include "pwc-uncompress.h" +#include "pwc-dec1.h" +#include "pwc-dec23.h" int pwc_decompress(struct pwc_device *pdev) { @@ -40,107 +44,95 @@ int pwc_decompress(struct pwc_device *pdev) if (pdev == NULL) return -EFAULT; -#if defined(__KERNEL__) && defined(PWC_MAGIC) - if (pdev->magic != PWC_MAGIC) { - Err("pwc_decompress(): magic failed.\n"); - return -EFAULT; - } -#endif fbuf = pdev->read_frame; if (fbuf == NULL) return -EFAULT; - image = pdev->image_ptr[pdev->fill_image]; - if (!image) - return -EFAULT; + image = pdev->image_data; + image += pdev->images[pdev->fill_image].offset; yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ /* Raw format; that's easy... */ if (pdev->vpalette == VIDEO_PALETTE_RAW) { - memcpy(image, yuv, pdev->frame_size); + struct pwc_raw_frame *raw_frame = image; + raw_frame->type = cpu_to_le16(pdev->type); + raw_frame->vbandlength = cpu_to_le16(pdev->vbandlength); + /* cmd_buf is always 4 bytes, but sometimes, only the + * first 3 bytes is filled (Nala case). We can + * determine this using the type of the webcam */ + memcpy(raw_frame->cmd, pdev->cmd_buf, 4); + memcpy(raw_frame+1, yuv, pdev->frame_size); return 0; } if (pdev->vbandlength == 0) { - /* Uncompressed mode. We copy the data into the output buffer, - using the viewport size (which may be larger than the image - size). Unfortunately we have to do a bit of byte stuffing - to get the desired output format/size. + /* Uncompressed mode. + * We copy the data into the output buffer, using the viewport + * size (which may be larger than the image size). + * Unfortunately we have to do a bit of byte stuffing to get + * the desired output format/size. + * + * We do some byte shuffling here to go from the + * native format to YUV420P. */ - /* - * We do some byte shuffling here to go from the - * native format to YUV420P. - */ - src = (u16 *)yuv; - n = pdev->view.x * pdev->view.y; - - /* offset in Y plane */ - stride = pdev->view.x * pdev->offset.y + pdev->offset.x; - dsty = (u16 *)(image + stride); - - /* offsets in U/V planes */ - stride = pdev->view.x * pdev->offset.y / 4 + pdev->offset.x / 2; - dstu = (u16 *)(image + n + stride); - dstv = (u16 *)(image + n + n / 4 + stride); - - /* increment after each line */ - stride = (pdev->view.x - pdev->image.x) / 2; /* u16 is 2 bytes */ - - for (line = 0; line < pdev->image.y; line++) { - for (col = 0; col < pdev->image.x; col += 4) { - *dsty++ = *src++; - *dsty++ = *src++; - if (line & 1) - *dstv++ = *src++; - else - *dstu++ = *src++; - } - dsty += stride; + src = (u16 *)yuv; + n = pdev->view.x * pdev->view.y; + + /* offset in Y plane */ + stride = pdev->view.x * pdev->offset.y + pdev->offset.x; + dsty = (u16 *)(image + stride); + + /* offsets in U/V planes */ + stride = pdev->view.x * pdev->offset.y / 4 + pdev->offset.x / 2; + dstu = (u16 *)(image + n + stride); + dstv = (u16 *)(image + n + n / 4 + stride); + + /* increment after each line */ + stride = (pdev->view.x - pdev->image.x) / 2; /* u16 is 2 bytes */ + + for (line = 0; line < pdev->image.y; line++) { + for (col = 0; col < pdev->image.x; col += 4) { + *dsty++ = *src++; + *dsty++ = *src++; if (line & 1) - dstv += (stride >> 1); + *dstv++ = *src++; else - dstu += (stride >> 1); + *dstu++ = *src++; } + dsty += stride; + if (line & 1) + dstv += (stride >> 1); + else + dstu += (stride >> 1); + } + + return 0; } - else { - /* Compressed; the decompressor routines will write the data - in planar format immediately. - */ - int flags; - - flags = PWCX_FLAG_PLANAR; - if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot) - { - printk(KERN_ERR "pwc: Mode Bayer is not supported for now\n"); - flags |= PWCX_FLAG_BAYER; - return -ENXIO; /* No such device or address: missing decompressor */ - } - -#if 0 /* keep */ - switch (pdev->type) - { - case 675: - case 680: - case 690: - case 720: - case 730: - case 740: - case 750: - pwc_dec23_decompress(&pdev->image, &pdev->view, - &pdev->offset, yuv, image, flags, - pdev->decompress_data, pdev->vbandlength); - break; - case 645: - case 646: - /* TODO & FIXME */ - return -ENXIO; /* Missing decompressor */ - break; - } -#endif + + /* + * Compressed; + * the decompressor routines will write the data in planar format + * immediately. + */ + if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot) { + PWC_ERROR("Mode Bayer is not supported for now\n"); + /* flags |= PWCX_FLAG_BAYER; */ + return -ENXIO; /* No such device or address: missing decompressor */ + } + + if (DEVICE_USE_CODEC1(pdev->type)) { + + /* TODO & FIXME */ + PWC_ERROR("This chipset is not supported for now\n"); + return -ENXIO; /* No such device or address: missing decompressor */ + + } else { + pwc_dec23_decompress(pdev, yuv, image, PWCX_FLAG_PLANAR); } return 0; } +/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/linux/drivers/media/video/pwc/pwc-uncompress.h b/linux/drivers/media/video/pwc/pwc-uncompress.h index f75e1b6cb..041227f65 100644 --- a/linux/drivers/media/video/pwc/pwc-uncompress.h +++ b/linux/drivers/media/video/pwc/pwc-uncompress.h @@ -1,5 +1,5 @@ /* (C) 1999-2003 Nemosoft Unv. - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -32,7 +32,7 @@ #include <linux/config.h> -#include "pwc-ioctl.h" +#include <media/pwc-ioctl.h> /* from pwc-dec.h */ #define PWCX_FLAG_PLANAR 0x0001 diff --git a/linux/drivers/media/video/pwc/pwc-v4l.c b/linux/drivers/media/video/pwc/pwc-v4l.c new file mode 100644 index 000000000..961ce2ddf --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-v4l.c @@ -0,0 +1,1233 @@ +/* Linux driver for Philips webcam + USB and Video4Linux interface part. + (C) 1999-2004 Nemosoft Unv. + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to <luc@saillard.org>. + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + 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 + +*/ + +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <asm/io.h> + +#include "pwc.h" + +static struct v4l2_queryctrl pwc_controls[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 128, + .step = 1, + .default_value = 64, + }, + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 64, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = -100, + .maximum = 100, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = 0, + .maximum = 32, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Gain", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Gain", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto White Balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Shutter Speed (Exposure)", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 200, + }, + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain Enabled", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain Level", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0, + }, +#if XAWTV_HAS_BEEN_FIXED + { + .id = V4L2_CID_PRIVATE_SAVE_USER, + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Save User Settings", + .minimum = 0, + .maximum = 0, + .step = 0, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_RESTORE_USER, + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Restore User Settings", + .minimum = 0, + .maximum = 0, + .step = 0, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_RESTORE_FACTORY, + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Restore Factory Settings", + .minimum = 0, + .maximum = 0, + .step = 0, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_COLOUR_MODE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Colour mode", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_AUTOCONTOUR, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto contour", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_CONTOUR, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contour", + .minimum = 0, + .maximum = 63, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_BACKLIGHT, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Backlight compensation", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_FLICKERLESS, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flickerless", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_NOISE_REDUCTION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Noise reduction", + .minimum = 0, + .maximum = 3, + .step = 1, + .default_value = 0, + }, +#endif +}; + +#if CONFIG_PWC_DEBUG +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) +static const char *v4l1_ioctls[] = { + "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", + "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", + "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", + "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", + "SMICROCODE", "GVBIFMT", "SVBIFMT" }; +#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls) +#else +/* In 2.6.16-rc1 v4l_printk_ioctl is not defined but exported */ +extern void v4l_printk_ioctl(unsigned int cmd); +#endif +#endif + +static void pwc_vidioc_fill_fmt(const struct pwc_device *pdev, struct v4l2_format *f) +{ + memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); + f->fmt.pix.width = pdev->view.x; + f->fmt.pix.height = pdev->view.y; + f->fmt.pix.field = V4L2_FIELD_NONE; + if (pdev->vpalette == VIDEO_PALETTE_YUV420P) { + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; + f->fmt.pix.bytesperline = (f->fmt.pix.width * 3)/2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + } else { + /* vbandlength contains 4 lines ... */ + f->fmt.pix.bytesperline = pdev->vbandlength/4; + f->fmt.pix.sizeimage = pdev->frame_size + sizeof(struct pwc_raw_frame); + if (DEVICE_USE_CODEC1(pdev->type)) + f->fmt.pix.pixelformat = V4L2_PIX_FMT_PWC1; + else + f->fmt.pix.pixelformat = V4L2_PIX_FMT_PWC2; + } + PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() " + "width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n", + f->fmt.pix.width, + f->fmt.pix.height, + f->fmt.pix.bytesperline, + f->fmt.pix.sizeimage, + (f->fmt.pix.pixelformat)&255, + (f->fmt.pix.pixelformat>>8)&255, + (f->fmt.pix.pixelformat>>16)&255, + (f->fmt.pix.pixelformat>>24)&255); +} + +/* ioctl(VIDIOC_TRY_FMT) */ +static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + PWC_DEBUG_IOCTL("Bad video type must be V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + return -EINVAL; + } + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUV420: + break; + case V4L2_PIX_FMT_PWC1: + if (DEVICE_USE_CODEC23(pdev->type)) { + PWC_DEBUG_IOCTL("codec1 is only supported for old pwc webcam\n"); + return -EINVAL; + } + break; + case V4L2_PIX_FMT_PWC2: + if (DEVICE_USE_CODEC1(pdev->type)) { + PWC_DEBUG_IOCTL("codec23 is only supported for new pwc webcam\n"); + return -EINVAL; + } + break; + default: + PWC_DEBUG_IOCTL("Unsupported pixel format\n"); + return -EINVAL; + + } + + if (f->fmt.pix.width > pdev->view_max.x) + f->fmt.pix.width = pdev->view_max.x; + else if (f->fmt.pix.width < pdev->view_min.x) + f->fmt.pix.width = pdev->view_min.x; + + if (f->fmt.pix.height > pdev->view_max.y) + f->fmt.pix.height = pdev->view_max.y; + else if (f->fmt.pix.height < pdev->view_min.y) + f->fmt.pix.height = pdev->view_min.y; + + return 0; +} + +/* ioctl(VIDIOC_SET_FMT) */ +static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f) +{ + int ret, fps, snapshot, compression, pixelformat; + + ret = pwc_vidioc_try_fmt(pdev, f); + if (ret<0) + return ret; + + pixelformat = f->fmt.pix.pixelformat; + compression = pdev->vcompression; + snapshot = 0; + fps = pdev->vframes; + if (f->fmt.pix.priv) { + compression = (f->fmt.pix.priv & PWC_QLT_MASK) >> PWC_QLT_SHIFT; + snapshot = !!(f->fmt.pix.priv & PWC_FPS_SNAPSHOT); + fps = (f->fmt.pix.priv & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; + if (fps == 0) + fps = pdev->vframes; + } + + if (pixelformat == V4L2_PIX_FMT_YUV420) + pdev->vpalette = VIDEO_PALETTE_YUV420P; + else + pdev->vpalette = VIDEO_PALETTE_RAW; + + PWC_DEBUG_IOCTL("Try to change format to: width=%d height=%d fps=%d " + "compression=%d snapshot=%d format=%c%c%c%c\n", + f->fmt.pix.width, f->fmt.pix.height, fps, + compression, snapshot, + (pixelformat)&255, + (pixelformat>>8)&255, + (pixelformat>>16)&255, + (pixelformat>>24)&255); + + ret = pwc_try_video_mode(pdev, + f->fmt.pix.width, + f->fmt.pix.height, + fps, + compression, + snapshot); + + PWC_DEBUG_IOCTL("pwc_try_video_mode(), return=%d\n", ret); + + if (ret) + return ret; + + pwc_vidioc_fill_fmt(pdev, f); + + return 0; + +} + +int pwc_video_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct pwc_device *pdev; + DECLARE_WAITQUEUE(wait, current); + + if (vdev == NULL) + return -EFAULT; + pdev = vdev->priv; + if (pdev == NULL) + return -EFAULT; + +#if CONFIG_PWC_DEBUG +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) + switch (_IOC_TYPE(cmd)) { + case 'v': + PWC_DEBUG_IOCTL("ioctl 0x%x (v4l1, VIDIOC%s)\n", cmd, + (_IOC_NR(cmd) < V4L1_IOCTLS) ? v4l1_ioctls[_IOC_NR(cmd)] : "???"); + break; + case 'V': + PWC_DEBUG_IOCTL("ioctl 0x%x (v4l2, %s)\n", cmd, + v4l2_ioctl_names[_IOC_NR(cmd)]); + break; + default: + PWC_DEBUG_IOCTL("ioctl 0x%x (unknown)\n", cmd); + } +#else + if (PWC_DEBUG_LEVEL_IOCTL & pwc_trace) + v4l_printk_ioctl(cmd); +#endif +#endif + + + switch (cmd) { + /* Query cabapilities */ + case VIDIOCGCAP: + { + struct video_capability *caps = arg; + + strcpy(caps->name, vdev->name); + caps->type = VID_TYPE_CAPTURE; + caps->channels = 1; + caps->audios = 1; + caps->minwidth = pdev->view_min.x; + caps->minheight = pdev->view_min.y; + caps->maxwidth = pdev->view_max.x; + caps->maxheight = pdev->view_max.y; + break; + } + + /* Channel functions (simulate 1 channel) */ + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + v->flags = 0; + v->tuners = 0; + v->type = VIDEO_TYPE_CAMERA; + strcpy(v->name, "Webcam"); + return 0; + } + + case VIDIOCSCHAN: + { + /* The spec says the argument is an integer, but + the bttv driver uses a video_channel arg, which + makes sense becasue it also has the norm flag. + */ + struct video_channel *v = arg; + if (v->channel != 0) + return -EINVAL; + return 0; + } + + + /* Picture functions; contrast etc. */ + case VIDIOCGPICT: + { + struct video_picture *p = arg; + int val; + + val = pwc_get_brightness(pdev); + if (val >= 0) + p->brightness = (val<<9); + else + p->brightness = 0xffff; + val = pwc_get_contrast(pdev); + if (val >= 0) + p->contrast = (val<<10); + else + p->contrast = 0xffff; + /* Gamma, Whiteness, what's the difference? :) */ + val = pwc_get_gamma(pdev); + if (val >= 0) + p->whiteness = (val<<11); + else + p->whiteness = 0xffff; + if (pwc_get_saturation(pdev, &val)<0) + p->colour = 0xffff; + else + p->colour = 32768 + val * 327; + p->depth = 24; + p->palette = pdev->vpalette; + p->hue = 0xFFFF; /* N/A */ + break; + } + + case VIDIOCSPICT: + { + struct video_picture *p = arg; + /* + * FIXME: Suppose we are mid read + ANSWER: No problem: the firmware of the camera + can handle brightness/contrast/etc + changes at _any_ time, and the palette + is used exactly once in the uncompress + routine. + */ + pwc_set_brightness(pdev, p->brightness); + pwc_set_contrast(pdev, p->contrast); + pwc_set_gamma(pdev, p->whiteness); + pwc_set_saturation(pdev, (p->colour-32768)/327); + if (p->palette && p->palette != pdev->vpalette) { + switch (p->palette) { + case VIDEO_PALETTE_YUV420P: + case VIDEO_PALETTE_RAW: + pdev->vpalette = p->palette; + return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + break; + default: + return -EINVAL; + break; + } + } + break; + } + + /* Window/size parameters */ + case VIDIOCGWIN: + { + struct video_window *vw = arg; + + vw->x = 0; + vw->y = 0; + vw->width = pdev->view.x; + vw->height = pdev->view.y; + vw->chromakey = 0; + vw->flags = (pdev->vframes << PWC_FPS_SHIFT) | + (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0); + break; + } + + case VIDIOCSWIN: + { + struct video_window *vw = arg; + int fps, snapshot, ret; + + fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; + snapshot = vw->flags & PWC_FPS_SNAPSHOT; + if (fps == 0) + fps = pdev->vframes; + if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot) + return 0; + ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot); + if (ret) + return ret; + break; + } + + /* We don't have overlay support (yet) */ + case VIDIOCGFBUF: + { + struct video_buffer *vb = arg; + + memset(vb,0,sizeof(*vb)); + break; + } + + /* mmap() functions */ + case VIDIOCGMBUF: + { + /* Tell the user program how much memory is needed for a mmap() */ + struct video_mbuf *vm = arg; + int i; + + memset(vm, 0, sizeof(*vm)); + vm->size = pwc_mbufs * pdev->len_per_image; + vm->frames = pwc_mbufs; /* double buffering should be enough for most applications */ + for (i = 0; i < pwc_mbufs; i++) + vm->offsets[i] = i * pdev->len_per_image; + break; + } + + case VIDIOCMCAPTURE: + { + /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */ + struct video_mmap *vm = arg; + + PWC_DEBUG_READ("VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format); + if (vm->frame < 0 || vm->frame >= pwc_mbufs) + return -EINVAL; + + /* xawtv is nasty. It probes the available palettes + by setting a very small image size and trying + various palettes... The driver doesn't support + such small images, so I'm working around it. + */ + if (vm->format) + { + switch (vm->format) + { + case VIDEO_PALETTE_YUV420P: + case VIDEO_PALETTE_RAW: + break; + default: + return -EINVAL; + break; + } + } + + if ((vm->width != pdev->view.x || vm->height != pdev->view.y) && + (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) { + int ret; + + PWC_DEBUG_OPEN("VIDIOCMCAPTURE: changing size to please xawtv :-(.\n"); + ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + if (ret) + return ret; + } /* ... size mismatch */ + + /* FIXME: should we lock here? */ + if (pdev->image_used[vm->frame]) + return -EBUSY; /* buffer wasn't available. Bummer */ + pdev->image_used[vm->frame] = 1; + + /* Okay, we're done here. In the SYNC call we wait until a + frame comes available, then expand image into the given + buffer. + In contrast to the CPiA cam the Philips cams deliver a + constant stream, almost like a grabber card. Also, + we have separate buffers for the rawdata and the image, + meaning we can nearly always expand into the requested buffer. + */ + PWC_DEBUG_READ("VIDIOCMCAPTURE done.\n"); + break; + } + + case VIDIOCSYNC: + { + /* The doc says: "Whenever a buffer is used it should + call VIDIOCSYNC to free this frame up and continue." + + The only odd thing about this whole procedure is + that MCAPTURE flags the buffer as "in use", and + SYNC immediately unmarks it, while it isn't + after SYNC that you know that the buffer actually + got filled! So you better not start a CAPTURE in + the same frame immediately (use double buffering). + This is not a problem for this cam, since it has + extra intermediate buffers, but a hardware + grabber card will then overwrite the buffer + you're working on. + */ + int *mbuf = arg; + int ret; + + PWC_DEBUG_READ("VIDIOCSYNC called (%d).\n", *mbuf); + + /* bounds check */ + if (*mbuf < 0 || *mbuf >= pwc_mbufs) + return -EINVAL; + /* check if this buffer was requested anyway */ + if (pdev->image_used[*mbuf] == 0) + return -EINVAL; + + /* Add ourselves to the frame wait-queue. + + FIXME: needs auditing for safety. + QUESTION: In what respect? I think that using the + frameq is safe now. + */ + add_wait_queue(&pdev->frameq, &wait); + while (pdev->full_frames == NULL) { + /* Check for unplugged/etc. here */ + if (pdev->error_status) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -pdev->error_status; + } + + if (signal_pending(current)) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -ERESTARTSYS; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + + /* The frame is ready. Expand in the image buffer + requested by the user. I don't care if you + mmap() 5 buffers and request data in this order: + buffer 4 2 3 0 1 2 3 0 4 3 1 . . . + Grabber hardware may not be so forgiving. + */ + PWC_DEBUG_READ("VIDIOCSYNC: frame ready.\n"); + pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */ + /* Decompress, etc */ + ret = pwc_handle_frame(pdev); + pdev->image_used[*mbuf] = 0; + if (ret) + return -EFAULT; + break; + } + + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + + strcpy(v->name, "Microphone"); + v->audio = -1; /* unknown audio minor */ + v->flags = 0; + v->mode = VIDEO_SOUND_MONO; + v->volume = 0; + v->bass = 0; + v->treble = 0; + v->balance = 0x8000; + v->step = 1; + break; + } + + case VIDIOCSAUDIO: + { + /* Dummy: nothing can be set */ + break; + } + + case VIDIOCGUNIT: + { + struct video_unit *vu = arg; + + vu->video = pdev->vdev->minor & 0x3F; + vu->audio = -1; /* not known yet */ + vu->vbi = -1; + vu->radio = -1; + vu->teletext = -1; + break; + } + + /* V4L2 Layer */ + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCAP) This application "\ + "try to use the v4l2 layer\n"); + strcpy(cap->driver,PWC_NAME); + strlcpy(cap->card, vdev->name, sizeof(cap->card)); + usb_make_path(pdev->udev,cap->bus_info,sizeof(cap->bus_info)); + cap->version = PWC_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + return 0; + } + + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *i = arg; + + if ( i->index ) /* Only one INPUT is supported */ + return -EINVAL; + + memset(i, 0, sizeof(struct v4l2_input)); + strcpy(i->name, "usb"); + return 0; + } + + case VIDIOC_G_INPUT: + { + int *i = arg; + *i = 0; /* Only one INPUT is supported */ + return 0; + } + case VIDIOC_S_INPUT: + { + int *i = arg; + + if ( *i ) { /* Only one INPUT is supported */ + PWC_DEBUG_IOCTL("Only one input source is"\ + " supported with this webcam.\n"); + return -EINVAL; + } + return 0; + } + + /* TODO: */ + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *c = arg; + int i; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) query id=%d\n", c->id); + for (i=0; i<sizeof(pwc_controls)/sizeof(struct v4l2_queryctrl); i++) { + if (pwc_controls[i].id == c->id) { + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n"); + memcpy(c,&pwc_controls[i],sizeof(struct v4l2_queryctrl)); + return 0; + } + } + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) not found\n"); + + return -EINVAL; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *c = arg; + int ret; + + switch (c->id) + { + case V4L2_CID_BRIGHTNESS: + c->value = pwc_get_brightness(pdev); + if (c->value<0) + return -EINVAL; + return 0; + case V4L2_CID_CONTRAST: + c->value = pwc_get_contrast(pdev); + if (c->value<0) + return -EINVAL; + return 0; + case V4L2_CID_SATURATION: + ret = pwc_get_saturation(pdev, &c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_GAMMA: + c->value = pwc_get_gamma(pdev); + if (c->value<0) + return -EINVAL; + return 0; + case V4L2_CID_RED_BALANCE: + ret = pwc_get_red_gain(pdev, &c->value); + if (ret<0) + return -EINVAL; + c->value >>= 8; + return 0; + case V4L2_CID_BLUE_BALANCE: + ret = pwc_get_blue_gain(pdev, &c->value); + if (ret<0) + return -EINVAL; + c->value >>= 8; + return 0; + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = pwc_get_awb(pdev); + if (ret<0) + return -EINVAL; + c->value = (ret == PWC_WB_MANUAL)?0:1; + return 0; + case V4L2_CID_GAIN: + ret = pwc_get_agc(pdev, &c->value); + if (ret<0) + return -EINVAL; + c->value >>= 8; + return 0; + case V4L2_CID_AUTOGAIN: + ret = pwc_get_agc(pdev, &c->value); + if (ret<0) + return -EINVAL; + c->value = (c->value < 0)?1:0; + return 0; + case V4L2_CID_EXPOSURE: + ret = pwc_get_shutter_speed(pdev, &c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_COLOUR_MODE: + ret = pwc_get_colour_mode(pdev, &c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_AUTOCONTOUR: + ret = pwc_get_contour(pdev, &c->value); + if (ret < 0) + return -EINVAL; + c->value=(c->value == -1?1:0); + return 0; + case V4L2_CID_PRIVATE_CONTOUR: + ret = pwc_get_contour(pdev, &c->value); + if (ret < 0) + return -EINVAL; + c->value >>= 10; + return 0; + case V4L2_CID_PRIVATE_BACKLIGHT: + ret = pwc_get_backlight(pdev, &c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_FLICKERLESS: + ret = pwc_get_flicker(pdev, &c->value); + if (ret < 0) + return -EINVAL; + c->value=(c->value?1:0); + return 0; + case V4L2_CID_PRIVATE_NOISE_REDUCTION: + ret = pwc_get_dynamic_noise(pdev, &c->value); + if (ret < 0) + return -EINVAL; + return 0; + + case V4L2_CID_PRIVATE_SAVE_USER: + case V4L2_CID_PRIVATE_RESTORE_USER: + case V4L2_CID_PRIVATE_RESTORE_FACTORY: + return -EINVAL; + } + return -EINVAL; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *c = arg; + int ret; + + switch (c->id) + { + case V4L2_CID_BRIGHTNESS: + c->value <<= 9; + ret = pwc_set_brightness(pdev, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_CONTRAST: + c->value <<= 10; + ret = pwc_set_contrast(pdev, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_SATURATION: + ret = pwc_set_saturation(pdev, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_GAMMA: + c->value <<= 11; + ret = pwc_set_gamma(pdev, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_RED_BALANCE: + c->value <<= 8; + ret = pwc_set_red_gain(pdev, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_BLUE_BALANCE: + c->value <<= 8; + ret = pwc_set_blue_gain(pdev, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_AUTO_WHITE_BALANCE: + c->value = (c->value == 0)?PWC_WB_MANUAL:PWC_WB_AUTO; + ret = pwc_set_awb(pdev, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_EXPOSURE: + c->value <<= 8; + ret = pwc_set_shutter_speed(pdev, c->value?0:1, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_AUTOGAIN: + /* autogain off means nothing without a gain */ + if (c->value == 0) + return 0; + ret = pwc_set_agc(pdev, c->value, 0); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_GAIN: + c->value <<= 8; + ret = pwc_set_agc(pdev, 0, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_SAVE_USER: + if (pwc_save_user(pdev)) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_RESTORE_USER: + if (pwc_restore_user(pdev)) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_RESTORE_FACTORY: + if (pwc_restore_factory(pdev)) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_COLOUR_MODE: + ret = pwc_set_colour_mode(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_AUTOCONTOUR: + c->value=(c->value == 1)?-1:0; + ret = pwc_set_contour(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_CONTOUR: + c->value <<= 10; + ret = pwc_set_contour(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_BACKLIGHT: + ret = pwc_set_backlight(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_FLICKERLESS: + ret = pwc_set_flicker(pdev, c->value); + if (ret < 0) + return -EINVAL; + case V4L2_CID_PRIVATE_NOISE_REDUCTION: + ret = pwc_set_dynamic_noise(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + + } + return -EINVAL; + } + + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *f = arg; + int index; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* We only support two format: the raw format, and YUV */ + index = f->index; + memset(f,0,sizeof(struct v4l2_fmtdesc)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->index = index; + switch(index) + { + case 0: + /* RAW format */ + f->pixelformat = pdev->type<=646?V4L2_PIX_FMT_PWC1:V4L2_PIX_FMT_PWC2; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strlcpy(f->description,"Raw Philips Webcam",sizeof(f->description)); + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_YUV420; + strlcpy(f->description,"4:2:0, planar, Y-Cb-Cr",sizeof(f->description)); + break; + default: + return -EINVAL; + } + return 0; + } + + case VIDIOC_G_FMT: + { + struct v4l2_format *f = arg; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",pdev->image.x,pdev->image.y); + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + pwc_vidioc_fill_fmt(pdev, f); + + return 0; + } + + case VIDIOC_TRY_FMT: + return pwc_vidioc_try_fmt(pdev, arg); + + case VIDIOC_S_FMT: + return pwc_vidioc_set_fmt(pdev, arg); + + case VIDIOC_G_STD: + { + v4l2_std_id *std = arg; + *std = V4L2_STD_UNKNOWN; + return 0; + } + + case VIDIOC_S_STD: + { + v4l2_std_id *std = arg; + if (*std != V4L2_STD_UNKNOWN) + return -EINVAL; + return 0; + } + + case VIDIOC_ENUMSTD: + { + struct v4l2_standard *std = arg; + if (std->index != 0) + return -EINVAL; + std->id = V4L2_STD_UNKNOWN; + strncpy(std->name, "webcam", sizeof(std->name)); + return 0; + } + + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *rb = arg; + int nbuffers; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n",rb->count); + if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (rb->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + nbuffers = rb->count; + if (nbuffers < 2) + nbuffers = 2; + else if (nbuffers > pwc_mbufs) + nbuffers = pwc_mbufs; + /* Force to use our # of buffers */ + rb->count = pwc_mbufs; + return 0; + } + + case VIDIOC_QUERYBUF: + { + struct v4l2_buffer *buf = arg; + int index; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n",buf->index); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n"); + return -EINVAL; + } + if (buf->memory != V4L2_MEMORY_MMAP) { + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad memory type\n"); + return -EINVAL; + } + index = buf->index; + if (index < 0 || index >= pwc_mbufs) { + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index); + return -EINVAL; + } + + memset(buf, 0, sizeof(struct v4l2_buffer)); + buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->index = index; + buf->m.offset = index * pdev->len_per_image; + if (pdev->vpalette == VIDEO_PALETTE_RAW) + buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame); + else + buf->bytesused = pdev->view.size; + buf->field = V4L2_FIELD_NONE; + buf->memory = V4L2_MEMORY_MMAP; + //buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->length = pdev->len_per_image; + + PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n",buf->index); + PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n",buf->m.offset); + PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n",buf->bytesused); + + return 0; + } + + case VIDIOC_QBUF: + { + struct v4l2_buffer *buf = arg; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n",buf->index); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + if (buf->index < 0 || buf->index >= pwc_mbufs) + return -EINVAL; + + buf->flags |= V4L2_BUF_FLAG_QUEUED; + buf->flags &= ~V4L2_BUF_FLAG_DONE; + + return 0; + } + + case VIDIOC_DQBUF: + { + struct v4l2_buffer *buf = arg; + int ret; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n"); + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* Add ourselves to the frame wait-queue. + + FIXME: needs auditing for safety. + QUESTION: In what respect? I think that using the + frameq is safe now. + */ + add_wait_queue(&pdev->frameq, &wait); + while (pdev->full_frames == NULL) { + if (pdev->error_status) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -pdev->error_status; + } + + if (signal_pending(current)) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -ERESTARTSYS; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n"); + /* Decompress data in pdev->images[pdev->fill_image] */ + ret = pwc_handle_frame(pdev); + if (ret) + return -EFAULT; + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n"); + + buf->index = pdev->fill_image; + if (pdev->vpalette == VIDEO_PALETTE_RAW) + buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame); + else + buf->bytesused = pdev->view.size; + buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->field = V4L2_FIELD_NONE; + do_gettimeofday(&buf->timestamp); + buf->sequence = 0; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = pdev->fill_image * pdev->len_per_image; + buf->length = buf->bytesused; + pwc_next_image(pdev); + + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n",buf->index); + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n",buf->length); + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n",buf->m.offset); + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n",buf->bytesused); + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n"); + return 0; + + } + + case VIDIOC_STREAMON: + { + /* WARNING: pwc_try_video_mode() called pwc_isoc_init */ + pwc_isoc_init(pdev); + return 0; + } + + case VIDIOC_STREAMOFF: + { + pwc_isoc_cleanup(pdev); + return 0; + } + + default: + return pwc_ioctl(pdev, cmd, arg); + } /* ..switch */ + return 0; +} + +/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/linux/drivers/media/video/pwc/pwc.h b/linux/drivers/media/video/pwc/pwc.h index 74d003695..1fd8c34d1 100644 --- a/linux/drivers/media/video/pwc/pwc.h +++ b/linux/drivers/media/video/pwc/pwc.h @@ -1,5 +1,5 @@ /* (C) 1999-2003 Nemosoft Unv. - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -29,52 +29,87 @@ #include <linux/module.h> #include <linux/usb.h> #include <linux/spinlock.h> -#include "compat.h" -#include <linux/videodev.h> #include <linux/wait.h> #include <linux/smp_lock.h> +#include <linux/version.h> #include <asm/semaphore.h> #include <asm/errno.h> +#include <linux/videodev.h> +#include <linux/videodev2.h> #include "pwc-uncompress.h" -#include "pwc-ioctl.h" - -/* Defines and structures for the Philips webcam */ -/* Used for checking memory corruption/pointer validation */ -#define PWC_MAGIC 0x89DC10ABUL -#undef PWC_MAGIC +#include <media/pwc-ioctl.h> /* Turn some debugging options on/off */ -#define PWC_DEBUG 0 +#ifndef CONFIG_PWC_DEBUG +#define CONFIG_PWC_DEBUG 1 +#endif + +/* Version block */ +#define PWC_MAJOR 10 +#define PWC_MINOR 0 +#define PWC_EXTRAMINOR 12 +#define PWC_VERSION_CODE KERNEL_VERSION(PWC_MAJOR,PWC_MINOR,PWC_EXTRAMINOR) +#define PWC_VERSION "10.0.12" +#define PWC_NAME "pwc" +#define PFX PWC_NAME ": " + /* Trace certain actions in the driver */ -#define TRACE_MODULE 0x0001 -#define TRACE_PROBE 0x0002 -#define TRACE_OPEN 0x0004 -#define TRACE_READ 0x0008 -#define TRACE_MEMORY 0x0010 -#define TRACE_FLOW 0x0020 -#define TRACE_SIZE 0x0040 -#define TRACE_PWCX 0x0080 -#define TRACE_SEQUENCE 0x1000 - -#define Trace(R, A...) if (pwc_trace & R) printk(KERN_DEBUG PWC_NAME " " A) -#define Debug(A...) printk(KERN_DEBUG PWC_NAME " " A) -#define Info(A...) printk(KERN_INFO PWC_NAME " " A) -#define Err(A...) printk(KERN_ERR PWC_NAME " " A) +#define PWC_DEBUG_LEVEL_MODULE (1<<0) +#define PWC_DEBUG_LEVEL_PROBE (1<<1) +#define PWC_DEBUG_LEVEL_OPEN (1<<2) +#define PWC_DEBUG_LEVEL_READ (1<<3) +#define PWC_DEBUG_LEVEL_MEMORY (1<<4) +#define PWC_DEBUG_LEVEL_FLOW (1<<5) +#define PWC_DEBUG_LEVEL_SIZE (1<<6) +#define PWC_DEBUG_LEVEL_IOCTL (1<<7) +#define PWC_DEBUG_LEVEL_TRACE (1<<8) + +#define PWC_DEBUG_MODULE(fmt, args...) PWC_DEBUG(MODULE, fmt, ##args) +#define PWC_DEBUG_PROBE(fmt, args...) PWC_DEBUG(PROBE, fmt, ##args) +#define PWC_DEBUG_OPEN(fmt, args...) PWC_DEBUG(OPEN, fmt, ##args) +#define PWC_DEBUG_READ(fmt, args...) PWC_DEBUG(READ, fmt, ##args) +#define PWC_DEBUG_MEMORY(fmt, args...) PWC_DEBUG(MEMORY, fmt, ##args) +#define PWC_DEBUG_FLOW(fmt, args...) PWC_DEBUG(FLOW, fmt, ##args) +#define PWC_DEBUG_SIZE(fmt, args...) PWC_DEBUG(SIZE, fmt, ##args) +#define PWC_DEBUG_IOCTL(fmt, args...) PWC_DEBUG(IOCTL, fmt, ##args) +#define PWC_DEBUG_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args) + + +#if CONFIG_PWC_DEBUG + +#define PWC_DEBUG_LEVEL (PWC_DEBUG_LEVEL_MODULE) + +#define PWC_DEBUG(level, fmt, args...) do {\ + if ((PWC_DEBUG_LEVEL_ ##level) & pwc_trace) \ + printk(KERN_DEBUG PFX fmt, ##args); \ + } while(0) + +#define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args) +#define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args) +#define PWC_INFO(fmt, args...) printk(KERN_INFO PFX fmt, ##args) +#define PWC_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args) + +#else /* if ! CONFIG_PWC_DEBUG */ + +#define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args) +#define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args) +#define PWC_INFO(fmt, args...) printk(KERN_INFO PFX fmt, ##args) +#define PWC_TRACE(fmt, args...) do { } while(0) +#define PWC_DEBUG(level, fmt, args...) do { } while(0) + +#define pwc_trace 0 +#endif /* Defines for ToUCam cameras */ #define TOUCAM_HEADER_SIZE 8 #define TOUCAM_TRAILER_SIZE 4 #define FEATURE_MOTOR_PANTILT 0x0001 - -/* Version block */ -#define PWC_MAJOR 9 -#define PWC_MINOR 0 -#define PWC_VERSION "9.0.2-unofficial" -#define PWC_NAME "pwc" +#define FEATURE_CODEC1 0x0002 +#define FEATURE_CODEC2 0x0004 /* Turn certain features on/off */ #define PWC_INT_PIPE 0 @@ -96,6 +131,18 @@ /* Absolute maximum number of buffers available for mmap() */ #define MAX_IMAGES 10 +/* Some macros to quickly find the type of a webcam */ +#define DEVICE_USE_CODEC1(x) ((x)<675) +#define DEVICE_USE_CODEC2(x) ((x)>=675 && (x)<700) +#define DEVICE_USE_CODEC3(x) ((x)>=700) +#define DEVICE_USE_CODEC23(x) ((x)>=675) + + +#ifndef V4L2_PIX_FMT_PWC1 +#define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P','W','C','1') +#define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') +#endif + /* The following structures were based on cpia.h. Why reinvent the wheel? :-) */ struct pwc_iso_buf { @@ -111,17 +158,19 @@ struct pwc_frame_buf void *data; volatile int filled; /* number of bytes filled */ struct pwc_frame_buf *next; /* list */ -#if PWC_DEBUG - int sequence; /* Sequence number */ -#endif +}; + +/* additionnal informations used when dealing image between kernel and userland */ +struct pwc_imgbuf +{ + unsigned long offset; /* offset of this buffer in the big array of image_data */ + int vma_use_count; /* count the number of time this memory is mapped */ }; struct pwc_device { struct video_device *vdev; -#ifdef PWC_MAGIC - int magic; -#endif + /* Pointer to our usb_device */ struct usb_device *udev; @@ -178,12 +227,8 @@ struct pwc_device int frame_size; int frame_total_size; /* including header & trailer */ int drop_frames; -#if PWC_DEBUG - int sequence; /* Debugging aid */ -#endif /* 3: decompression */ - struct pwc_decompressor *decompressor; /* function block with decompression routines */ void *decompress_data; /* private data for decompression engine */ /* 4: image */ @@ -199,7 +244,7 @@ struct pwc_device struct pwc_coord offset; /* offset within the viewport */ void *image_data; /* total buffer, which is subdivided into ... */ - void *image_ptr[MAX_IMAGES]; /* ...several images... */ + struct pwc_imgbuf images[MAX_IMAGES];/* ...several images... */ int fill_image; /* ...which are rotated. */ int len_per_image; /* length per image */ int image_read_pos; /* In case we read data in pieces, keep track of were we are in the imagebuffer */ @@ -212,6 +257,7 @@ struct pwc_device struct pwc_mpt_range angle_range; int pan_angle; /* in degrees * 100 */ int tilt_angle; /* absolute angle; 0,0 is home position */ + int snapshot_button_status; /* set to 1 when the user push the button, reset to 0 when this value is read */ /*** Misc. data ***/ wait_queue_head_t frameq; /* When waiting for a frame to finish... */ @@ -220,20 +266,27 @@ struct pwc_device #endif }; - #ifdef __cplusplus extern "C" { #endif -/* Global variable */ +/* Global variables */ +#if CONFIG_PWC_DEBUG extern int pwc_trace; +#endif +extern int pwc_preferred_compression; +extern int pwc_mbufs; /** functions in pwc-if.c */ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot); +int pwc_handle_frame(struct pwc_device *pdev); +void pwc_next_image(struct pwc_device *pdev); +int pwc_isoc_init(struct pwc_device *pdev); +void pwc_isoc_cleanup(struct pwc_device *pdev); /** Functions in pwc-misc.c */ /* sizes in pixels */ -extern struct pwc_coord pwc_image_sizes[PSZ_MAX]; +extern const struct pwc_coord pwc_image_sizes[PSZ_MAX]; int pwc_decode_size(struct pwc_device *pdev, int width, int height); void pwc_construct(struct pwc_device *pdev); @@ -241,6 +294,9 @@ void pwc_construct(struct pwc_device *pdev); /** Functions in pwc-ctrl.c */ /* Request a certain video mode. Returns < 0 if not possible */ extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot); +/* Calculate the number of bytes per image (not frame) */ +extern int pwc_mpt_reset(struct pwc_device *pdev, int flags); +extern int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt); /* Various controls; should be obvious. Value 0..65535, or < 0 on error */ extern int pwc_get_brightness(struct pwc_device *pdev); @@ -249,10 +305,37 @@ extern int pwc_get_contrast(struct pwc_device *pdev); extern int pwc_set_contrast(struct pwc_device *pdev, int value); extern int pwc_get_gamma(struct pwc_device *pdev); extern int pwc_set_gamma(struct pwc_device *pdev, int value); -extern int pwc_get_saturation(struct pwc_device *pdev); +extern int pwc_get_saturation(struct pwc_device *pdev, int *value); extern int pwc_set_saturation(struct pwc_device *pdev, int value); extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); +extern int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value); extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); +extern int pwc_restore_user(struct pwc_device *pdev); +extern int pwc_save_user(struct pwc_device *pdev); +extern int pwc_restore_factory(struct pwc_device *pdev); + +/* exported for use by v4l2 controls */ +extern int pwc_get_red_gain(struct pwc_device *pdev, int *value); +extern int pwc_set_red_gain(struct pwc_device *pdev, int value); +extern int pwc_get_blue_gain(struct pwc_device *pdev, int *value); +extern int pwc_set_blue_gain(struct pwc_device *pdev, int value); +extern int pwc_get_awb(struct pwc_device *pdev); +extern int pwc_set_awb(struct pwc_device *pdev, int mode); +extern int pwc_set_agc(struct pwc_device *pdev, int mode, int value); +extern int pwc_get_agc(struct pwc_device *pdev, int *value); +extern int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value); +extern int pwc_get_shutter_speed(struct pwc_device *pdev, int *value); + +extern int pwc_set_colour_mode(struct pwc_device *pdev, int colour); +extern int pwc_get_colour_mode(struct pwc_device *pdev, int *colour); +extern int pwc_set_contour(struct pwc_device *pdev, int contour); +extern int pwc_get_contour(struct pwc_device *pdev, int *contour); +extern int pwc_set_backlight(struct pwc_device *pdev, int backlight); +extern int pwc_get_backlight(struct pwc_device *pdev, int *backlight); +extern int pwc_set_flicker(struct pwc_device *pdev, int flicker); +extern int pwc_get_flicker(struct pwc_device *pdev, int *flicker); +extern int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise); +extern int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise); /* Power down or up the camera; not supported by all models */ extern int pwc_camera_power(struct pwc_device *pdev, int power); @@ -260,6 +343,9 @@ extern int pwc_camera_power(struct pwc_device *pdev, int power); /* Private ioctl()s; see pwc-ioctl.h */ extern int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg); +/** Functions in pwc-v4l.c */ +extern int pwc_video_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg); /** pwc-uncompress.c */ /* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ @@ -271,3 +357,4 @@ extern int pwc_decompress(struct pwc_device *pdev); #endif +/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index 3629aee32..04812f5f8 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -3581,6 +3581,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) /* power-up tuner chip */ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00040000, 0x00040000); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00040000, 0x00000000); + break; case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL: /* this turns the remote control chip off to work around a bug in it */ saa_writeb(SAA7134_GPIO_GPMODE1, 0x80); diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 985ef0422..b659b96e5 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -133,9 +133,9 @@ static int mt352_aver777_init(struct dvb_frontend* fe) return 0; } -static int mt352_pinnacle_pll_set(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params, - u8* pllbuf) +static int mt352_pinnacle_tuner_calc_regs(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params, + u8* pllbuf, int buf_len) { u8 off[] = { 0x00, 0xf1}; u8 on[] = { 0x00, 0x71}; @@ -144,6 +144,9 @@ static int mt352_pinnacle_pll_set(struct dvb_frontend* fe, struct saa7134_dev *dev = fe->dvb->priv; struct v4l2_frequency f; + if (buf_len < 5) + return -EINVAL; + /* set frequency (mt2050) */ f.tuner = 0; f.type = V4L2_TUNER_DIGITAL_TV; @@ -157,21 +160,24 @@ static int mt352_pinnacle_pll_set(struct dvb_frontend* fe, /* mt352 setup */ mt352_pinnacle_init(fe); - pllbuf[0] = 0xc2; + pllbuf[0] = 0x61; pllbuf[1] = 0x00; pllbuf[2] = 0x00; pllbuf[3] = 0x80; pllbuf[4] = 0x00; - return 0; + return 5; } -static int mt352_aver777_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8* pllbuf) +static int mt352_aver777_tuner_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8* pllbuf, int buf_len) { - pllbuf[0] = 0xc2; + if (buf_len < 5) + return -EINVAL; + + pllbuf[0] = 0x61; dvb_pll_configure(&dvb_pll_philips_td1316, pllbuf+1, params->frequency, params->u.ofdm.bandwidth); - return 0; + return 5; } static struct mt352_config pinnacle_300i = { @@ -180,13 +186,11 @@ static struct mt352_config pinnacle_300i = { .if2 = 36150, .no_tuner = 1, .demod_init = mt352_pinnacle_init, - .pll_set = mt352_pinnacle_pll_set, }; static struct mt352_config avermedia_777 = { .demod_address = 0xf, .demod_init = mt352_aver777_init, - .pll_set = mt352_aver777_pll_set, }; #endif @@ -269,6 +273,8 @@ static int philips_tda6651_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_ tuner_buf[2] = 0xca; tuner_buf[3] = (cp << 5) | (filter << 3) | band; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(1); @@ -282,6 +288,8 @@ static int philips_tda6651_pll_init(u8 addr, struct dvb_frontend *fe) struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; /* setup PLL configuration */ + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(1); @@ -291,12 +299,12 @@ static int philips_tda6651_pll_init(u8 addr, struct dvb_frontend *fe) /* ------------------------------------------------------------------ */ -static int philips_tu1216_pll_60_init(struct dvb_frontend *fe) +static int philips_tu1216_tuner_60_init(struct dvb_frontend *fe) { return philips_tda6651_pll_init(0x60, fe); } -static int philips_tu1216_pll_60_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tu1216_tuner_60_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { return philips_tda6651_pll_set(0x60, fe, params); } @@ -316,20 +324,17 @@ static struct tda1004x_config philips_tu1216_60_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .pll_init = philips_tu1216_pll_60_init, - .pll_set = philips_tu1216_pll_60_set, - .pll_sleep = NULL, .request_firmware = philips_tu1216_request_firmware, }; /* ------------------------------------------------------------------ */ -static int philips_tu1216_pll_61_init(struct dvb_frontend *fe) +static int philips_tu1216_tuner_61_init(struct dvb_frontend *fe) { return philips_tda6651_pll_init(0x61, fe); } -static int philips_tu1216_pll_61_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tu1216_tuner_61_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { return philips_tda6651_pll_set(0x61, fe, params); } @@ -342,21 +347,20 @@ static struct tda1004x_config philips_tu1216_61_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .pll_init = philips_tu1216_pll_61_init, - .pll_set = philips_tu1216_pll_61_set, - .pll_sleep = NULL, .request_firmware = philips_tu1216_request_firmware, }; /* ------------------------------------------------------------------ */ -static int philips_europa_pll_init(struct dvb_frontend *fe) +static int philips_europa_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab }; struct i2c_msg init_msg = {.addr = 0x61,.flags = 0,.buf = msg,.len = sizeof(msg) }; /* setup PLL configuration */ + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) return -EIO; msleep(1); @@ -366,18 +370,20 @@ static int philips_europa_pll_init(struct dvb_frontend *fe) init_msg.len = 0x02; msg[0] = 0x00; msg[1] = 0x40; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) return -EIO; return 0; } -static int philips_td1316_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_td1316_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { return philips_tda6651_pll_set(0x61, fe, params); } -static void philips_europa_analog(struct dvb_frontend *fe) +static int philips_europa_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; /* this message actually turns the tuner back to analog mode */ @@ -392,7 +398,20 @@ static void philips_europa_analog(struct dvb_frontend *fe) analog_msg.len = 0x02; msg[0] = 0x00; msg[1] = 0x14; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &analog_msg, 1); + return 0; +} + +static int philips_europa_demod_sleep(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + + if (dev->original_demod_sleep) + dev->original_demod_sleep(fe); + fe->ops->i2c_gate_ctrl(fe, 1); + return 0; } static struct tda1004x_config philips_europa_config = { @@ -403,21 +422,20 @@ static struct tda1004x_config philips_europa_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_IFO_AUTO_POS, .if_freq = TDA10046_FREQ_052, - .pll_init = philips_europa_pll_init, - .pll_set = philips_td1316_pll_set, - .pll_sleep = philips_europa_analog, .request_firmware = NULL, }; /* ------------------------------------------------------------------ */ -static int philips_fmd1216_pll_init(struct dvb_frontend *fe) +static int philips_fmd1216_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; /* this message is to set up ATC and ALC */ static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0xa0 }; struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(1); @@ -425,22 +443,27 @@ static int philips_fmd1216_pll_init(struct dvb_frontend *fe) return 0; } -static void philips_fmd1216_analog(struct dvb_frontend *fe) +static int philips_fmd1216_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; /* this message actually turns the tuner back to analog mode */ static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0x60 }; struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); msleep(1); fmd1216_init[2] = 0x86; fmd1216_init[3] = 0x54; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); msleep(1); + return 0; } -static int philips_fmd1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_fmd1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct saa7134_dev *dev = fe->dvb->priv; u8 tuner_buf[4]; @@ -517,6 +540,8 @@ static int philips_fmd1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_ tuner_buf[2] = 0x80 | (cp << 6) | (mode << 3) | 4; tuner_buf[3] = 0x40 | band; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) return -EIO; return 0; @@ -529,9 +554,6 @@ static struct tda1004x_config medion_cardbus = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_IFO_AUTO_NEG, .if_freq = TDA10046_FREQ_3613, - .pll_init = philips_fmd1216_pll_init, - .pll_set = philips_fmd1216_pll_set, - .pll_sleep = philips_fmd1216_analog, .request_firmware = NULL, }; @@ -579,12 +601,12 @@ static struct tda827x_data tda827x_dvbt[] = { { .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0} }; -static int philips_tda827x_pll_init(struct dvb_frontend *fe) +static int philips_tda827x_tuner_init(struct dvb_frontend *fe) { return 0; } -static int philips_tda827x_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tda827x_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct saa7134_dev *dev = fe->dvb->priv; u8 tuner_buf[14]; @@ -631,6 +653,8 @@ static int philips_tda827x_pll_set(struct dvb_frontend *fe, struct dvb_frontend_ tuner_buf[13] = 0x40; tuner_msg.len = 14; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) return -EIO; @@ -639,18 +663,23 @@ static int philips_tda827x_pll_set(struct dvb_frontend *fe, struct dvb_frontend_ tuner_buf[0] = 0x30; tuner_buf[1] = 0x50 + tda827x_dvbt[i].cp; tuner_msg.len = 2; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); return 0; } -static void philips_tda827x_pll_sleep(struct dvb_frontend *fe) +static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; static u8 tda827x_sleep[] = { 0x30, 0xd0}; struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tda827x_sleep, .len = sizeof(tda827x_sleep) }; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); + return 0; } static struct tda1004x_config tda827x_lifeview_config = { @@ -660,9 +689,6 @@ static struct tda1004x_config tda827x_lifeview_config = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_TDA827X, .if_freq = TDA10046_FREQ_045, - .pll_init = philips_tda827x_pll_init, - .pll_set = philips_tda827x_pll_set, - .pll_sleep = philips_tda827x_pll_sleep, .request_firmware = NULL, }; @@ -754,6 +780,8 @@ static int philips_tda827xa_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb tuner_buf[12] = 0x00; tuner_buf[13] = 0x39; // lpsel msg.len = 14; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) return -EIO; @@ -761,10 +789,14 @@ static int philips_tda827xa_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb msg.len = 2; reg2[0] = 0x60; reg2[1] = 0x3c; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); reg2[0] = 0xa0; reg2[1] = 0x40; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); msleep(2); @@ -772,36 +804,43 @@ static int philips_tda827xa_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb reg2[0] = 0x30; reg2[1] = 0x10 + tda827xa_dvbt[i].scr; msg.len = 2; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); msleep(550); reg2[0] = 0x50; reg2[1] = 0x4f + (tda827xa_dvbt[i].gc3 << 4); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); return 0; } -static void philips_tda827xa_pll_sleep(u8 addr, struct dvb_frontend *fe) +static int philips_tda827xa_tuner_sleep(u8 addr, struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; static u8 tda827xa_sleep[] = { 0x30, 0x90}; struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tda827xa_sleep, .len = sizeof(tda827xa_sleep) }; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); - + return 0; } /* ------------------------------------------------------------------ */ -static int philips_tiger_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tiger_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { int ret; struct saa7134_dev *dev = fe->dvb->priv; static u8 tda8290_close[] = { 0x21, 0xc0}; static u8 tda8290_open[] = { 0x21, 0x80}; struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2}; + /* close tda8290 i2c bridge */ tda8290_msg.buf = tda8290_close; ret = i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1); @@ -817,7 +856,7 @@ static int philips_tiger_pll_set(struct dvb_frontend *fe, struct dvb_frontend_pa return ret; } -static int philips_tiger_dvb_mode(struct dvb_frontend *fe) +static int philips_tiger_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; static u8 data[] = { 0x3c, 0x33, 0x6a}; @@ -828,14 +867,15 @@ static int philips_tiger_dvb_mode(struct dvb_frontend *fe) return 0; } -static void philips_tiger_analog_mode(struct dvb_frontend *fe) +static int philips_tiger_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; static u8 data[] = { 0x3c, 0x33, 0x68}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); - philips_tda827xa_pll_sleep( 0x61, fe); + philips_tda827xa_tuner_sleep( 0x61, fe); + return 0; } static struct tda1004x_config philips_tiger_config = { @@ -845,15 +885,12 @@ static struct tda1004x_config philips_tiger_config = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_TDA827X, .if_freq = TDA10046_FREQ_045, - .pll_init = philips_tiger_dvb_mode, - .pll_set = philips_tiger_pll_set, - .pll_sleep = philips_tiger_analog_mode, .request_firmware = NULL, }; /* ------------------------------------------------------------------ */ -static int lifeview_trio_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int lifeview_trio_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { int ret; @@ -861,16 +898,12 @@ static int lifeview_trio_pll_set(struct dvb_frontend *fe, struct dvb_frontend_pa return ret; } -static int lifeview_trio_dvb_mode(struct dvb_frontend *fe) +static int lifeview_trio_tuner_sleep(struct dvb_frontend *fe) { + philips_tda827xa_tuner_sleep(0x60, fe); return 0; } -static void lifeview_trio_analog_mode(struct dvb_frontend *fe) -{ - philips_tda827xa_pll_sleep(0x60, fe); -} - static struct tda1004x_config lifeview_trio_config = { .demod_address = 0x09, .invert = 1, @@ -878,15 +911,12 @@ static struct tda1004x_config lifeview_trio_config = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_TDA827X_GPL, .if_freq = TDA10046_FREQ_045, - .pll_init = lifeview_trio_dvb_mode, - .pll_set = lifeview_trio_pll_set, - .pll_sleep = lifeview_trio_analog_mode, .request_firmware = NULL, }; /* ------------------------------------------------------------------ */ -static int ads_duo_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int ads_duo_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { int ret; @@ -894,7 +924,7 @@ static int ads_duo_pll_set(struct dvb_frontend *fe, struct dvb_frontend_paramete return ret; } -static int ads_duo_dvb_mode(struct dvb_frontend *fe) +static int ads_duo_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; /* route TDA8275a AGC input to the channel decoder */ @@ -902,12 +932,13 @@ static int ads_duo_dvb_mode(struct dvb_frontend *fe) return 0; } -static void ads_duo_analog_mode(struct dvb_frontend *fe) +static int ads_duo_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; /* route TDA8275a AGC input to the analog IF chip*/ saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x20); - philips_tda827xa_pll_sleep( 0x61, fe); + philips_tda827xa_tuner_sleep( 0x61, fe); + return 0; } static struct tda1004x_config ads_tech_duo_config = { @@ -917,31 +948,24 @@ static struct tda1004x_config ads_tech_duo_config = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_TDA827X_GPL, .if_freq = TDA10046_FREQ_045, - .pll_init = ads_duo_dvb_mode, - .pll_set = ads_duo_pll_set, - .pll_sleep = ads_duo_analog_mode, .request_firmware = NULL, }; /* ------------------------------------------------------------------ */ -static int tevion_dvb220rf_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int tevion_dvb220rf_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { int ret; ret = philips_tda827xa_pll_set(0x60, fe, params); return ret; } -static int tevion_dvb220rf_pll_init(struct dvb_frontend *fe) +static int tevion_dvb220rf_tuner_sleep(struct dvb_frontend *fe) { + philips_tda827xa_tuner_sleep( 0x61, fe); return 0; } -static void tevion_dvb220rf_pll_sleep(struct dvb_frontend *fe) -{ - philips_tda827xa_pll_sleep( 0x61, fe); -} - static struct tda1004x_config tevion_dvbt220rf_config = { .demod_address = 0x08, .invert = 1, @@ -949,9 +973,6 @@ static struct tda1004x_config tevion_dvbt220rf_config = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_TDA827X, .if_freq = TDA10046_FREQ_045, - .pll_init = tevion_dvb220rf_pll_init, - .pll_set = tevion_dvb220rf_pll_set, - .pll_sleep = tevion_dvb220rf_pll_sleep, .request_firmware = NULL, }; @@ -962,8 +983,6 @@ static struct tda1004x_config tevion_dvbt220rf_config = { #ifdef HAVE_NXT200X static struct nxt200x_config avertvhda180 = { .demod_address = 0x0a, - .pll_address = 0x61, - .pll_desc = &dvb_pll_tdhu2, }; static int nxt200x_set_pll_input(u8 *buf, int input) @@ -977,8 +996,6 @@ static int nxt200x_set_pll_input(u8 *buf, int input) static struct nxt200x_config kworldatsc110 = { .demod_address = 0x0a, - .pll_address = 0x61, - .pll_desc = &dvb_pll_tuv1236d, .set_pll_input = nxt200x_set_pll_input, }; #endif @@ -1004,78 +1021,158 @@ static int dvb_init(struct saa7134_dev *dev) printk("%s: pinnacle 300i dvb setup\n",dev->name); dev->dvb.frontend = mt352_attach(&pinnacle_300i, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.calc_regs = mt352_pinnacle_tuner_calc_regs; + } break; case SAA7134_BOARD_AVERMEDIA_777: printk("%s: avertv 777 dvb setup\n",dev->name); dev->dvb.frontend = mt352_attach(&avermedia_777, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.calc_regs = mt352_aver777_tuner_calc_regs; + } break; #endif #ifdef HAVE_TDA1004X case SAA7134_BOARD_MD7134: dev->dvb.frontend = tda10046_attach(&medion_cardbus, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_fmd1216_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_fmd1216_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_fmd1216_tuner_set_params; + } break; case SAA7134_BOARD_PHILIPS_TOUGH: dev->dvb.frontend = tda10046_attach(&philips_tu1216_60_config, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_tu1216_tuner_60_init; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tu1216_tuner_60_set_params; + } break; case SAA7134_BOARD_FLYDVBTDUO: dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_tda827x_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tda827x_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tda827x_tuner_set_params; + } break; case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS: dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_tda827x_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tda827x_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tda827x_tuner_set_params; + } break; case SAA7134_BOARD_PHILIPS_EUROPA: dev->dvb.frontend = tda10046_attach(&philips_europa_config, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->original_demod_sleep = dev->dvb.frontend->ops->sleep; + dev->dvb.frontend->ops->sleep = philips_europa_demod_sleep; + dev->dvb.frontend->ops->tuner_ops.init = philips_europa_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_europa_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_td1316_tuner_set_params; + } break; case SAA7134_BOARD_VIDEOMATE_DVBT_300: dev->dvb.frontend = tda10046_attach(&philips_europa_config, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_europa_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_europa_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_td1316_tuner_set_params; + } break; case SAA7134_BOARD_VIDEOMATE_DVBT_200: dev->dvb.frontend = tda10046_attach(&philips_tu1216_61_config, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_tu1216_tuner_61_init; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tu1216_tuner_61_set_params; + } break; case SAA7134_BOARD_PHILIPS_TIGER: dev->dvb.frontend = tda10046_attach(&philips_tiger_config, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_tiger_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tiger_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tiger_tuner_set_params; + } break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: dev->dvb.frontend = tda10046_attach(&philips_tiger_config, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_tiger_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tiger_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tiger_tuner_set_params; + } break; case SAA7134_BOARD_FLYDVBT_LR301: dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_tda827x_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tda827x_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tda827x_tuner_set_params; + } break; case SAA7134_BOARD_FLYDVB_TRIO: dev->dvb.frontend = tda10046_attach(&lifeview_trio_config, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.sleep = lifeview_trio_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = lifeview_trio_tuner_set_params; + } break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: dev->dvb.frontend = tda10046_attach(&ads_tech_duo_config, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = ads_duo_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = ads_duo_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = ads_duo_tuner_set_params; + } break; case SAA7134_BOARD_TEVION_DVBT_220RF: dev->dvb.frontend = tda10046_attach(&tevion_dvbt220rf_config, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.sleep = tevion_dvb220rf_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = tevion_dvb220rf_tuner_set_params; + } break; case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: dev->dvb.frontend = tda10046_attach(&ads_tech_duo_config, &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = ads_duo_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = ads_duo_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = ads_duo_tuner_set_params; + } break; #endif #ifdef HAVE_NXT200X case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180: dev->dvb.frontend = nxt200x_attach(&avertvhda180, &dev->i2c_adap); + if (dev->dvb.frontend) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->i2c_adap, &dvb_pll_tdhu2); + } break; case SAA7134_BOARD_KWORLD_ATSC110: dev->dvb.frontend = nxt200x_attach(&kworldatsc110, &dev->i2c_adap); + if (dev->dvb.frontend) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->i2c_adap, &dvb_pll_tuv1236d); + } break; #endif default: diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index 21562ed12..a53d63505 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -555,6 +555,7 @@ struct saa7134_dev { #ifdef HAVE_VIDEO_BUF_DVB /* SAA7134_MPEG_DVB only */ struct videobuf_dvb dvb; + int (*original_demod_sleep)(struct dvb_frontend* fe); #endif }; diff --git a/linux/drivers/media/video/sn9c102/sn9c102.h b/linux/drivers/media/video/sn9c102/sn9c102.h index 3f644c63f..a6a793d35 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102.h +++ b/linux/drivers/media/video/sn9c102/sn9c102.h @@ -34,7 +34,9 @@ #include <linux/types.h> #include <linux/param.h> #include <linux/rwsem.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include <linux/mutex.h> +#endif #include <linux/string.h> #include <linux/stddef.h> @@ -146,7 +148,11 @@ struct sn9c102_device { enum sn9c102_dev_state state; u8 users; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex dev_mutex, fileop_mutex; +#else + struct semaphore dev_mutex, fileop_mutex; +#endif spinlock_t queue_lock; wait_queue_head_t open, wait_frame, wait_stream; }; diff --git a/linux/drivers/media/video/sn9c102/sn9c102_core.c b/linux/drivers/media/video/sn9c102/sn9c102_core.c index ea4394dc9..20999b5fe 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_core.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_core.c @@ -1769,7 +1769,13 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) pos = cam->frame[i].bufmem; while (size > 0) { /* size is page-aligned */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) + unsigned long page = vmalloc_to_pfn(pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, + vma->vm_page_prot)) { +#else if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { +#endif mutex_unlock(&cam->fileop_mutex); return -EAGAIN; } diff --git a/linux/drivers/media/video/tuner-types.c b/linux/drivers/media/video/tuner-types.c index 409603c38..f1d3a7556 100644 --- a/linux/drivers/media/video/tuner-types.c +++ b/linux/drivers/media/video/tuner-types.c @@ -903,8 +903,8 @@ static struct tuner_params tuner_philips_fmd1216me_mk3_params[] = { /* ------------ TUNER_LG_TDVS_H062F - INFINEON ATSC ------------ */ static struct tuner_range tuner_tua6034_ntsc_ranges[] = { - { 16 * 160.00 /*MHz*/, 0x8e, 0x01 }, - { 16 * 455.00 /*MHz*/, 0x8e, 0x02 }, + { 16 * 165.00 /*MHz*/, 0x8e, 0x01 }, + { 16 * 450.00 /*MHz*/, 0x8e, 0x02 }, { 16 * 999.99 , 0x8e, 0x04 }, }; @@ -1072,6 +1072,22 @@ static struct tuner_params tuner_thomson_fe6600_params[] = { }, }; +/* ------------ TUNER_SAMSUNG_TCPG_6121P30A - Samsung PAL ------------ */ + +static struct tuner_range tuner_samsung_tcpg_6121p30a_pal_ranges[] = { + { 16 * 146.25 /*MHz*/, 0xce, 0x01, }, + { 16 * 428.50 /*MHz*/, 0xce, 0x02, }, + { 16 * 999.99 , 0xce, 0x08, }, +}; + +static struct tuner_params tuner_samsung_tcpg_6121p30a_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_samsung_tcpg_6121p30a_pal_ranges, + .count = ARRAY_SIZE(tuner_samsung_tcpg_6121p30a_pal_ranges), + }, +}; + /* --------------------------------------------------------------------- */ struct tunertype tuners[] = { @@ -1403,7 +1419,7 @@ struct tunertype tuners[] = { .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_params), }, [TUNER_LG_TDVS_H062F] = { /* LGINNOTEK ATSC */ - .name = "LG TDVS-H062F/TUA6034", + .name = "LG TDVS-H06xF", /* H061F, H062F & H064F */ .params = tuner_tua6034_params, .count = ARRAY_SIZE(tuner_tua6034_params), }, @@ -1448,6 +1464,11 @@ struct tunertype tuners[] = { .params = tuner_thomson_fe6600_params, .count = ARRAY_SIZE(tuner_thomson_fe6600_params), }, + [TUNER_SAMSUNG_TCPG_6121P30A] = { /* Samsung PAL */ + .name = "Samsung TCPG 6121P30A", + .params = tuner_samsung_tcpg_6121p30a_params, + .count = ARRAY_SIZE(tuner_samsung_tcpg_6121p30a_params), + }, }; unsigned const int tuner_count = ARRAY_SIZE(tuners); diff --git a/linux/drivers/media/video/tveeprom.c b/linux/drivers/media/video/tveeprom.c index 8dad3c6a8..635f618ef 100644 --- a/linux/drivers/media/video/tveeprom.c +++ b/linux/drivers/media/video/tveeprom.c @@ -204,7 +204,7 @@ hauppauge_tuner[] = { TUNER_ABSENT, "Philips FQ1286A MK4"}, { TUNER_ABSENT, "Philips FQ1216ME MK5"}, { TUNER_ABSENT, "Philips FQ1236 MK5"}, - { TUNER_ABSENT, "Samsung TCPG_6121P30A"}, + { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"}, { TUNER_TCL_2002MB, "TCL 2002MB_3H"}, { TUNER_ABSENT, "TCL 2002MI_3H"}, { TUNER_TCL_2002N, "TCL 2002N 5H"}, @@ -222,7 +222,7 @@ hauppauge_tuner[] = /* 110-119 */ { TUNER_ABSENT, "Thompson DTT75105"}, { TUNER_ABSENT, "Conexant_CX24109"}, - { TUNER_ABSENT, "TCL M2523_5N_E"}, + { TUNER_TCL_2002N, "TCL M2523_5N_E"}, { TUNER_ABSENT, "TCL M2523_3DB_E"}, { TUNER_ABSENT, "Philips 8275A"}, { TUNER_ABSENT, "Microtune MT2060"}, diff --git a/linux/drivers/media/video/usbvideo/usbvideo.h b/linux/drivers/media/video/usbvideo/usbvideo.h index 0d2066b7d..de266e78a 100644 --- a/linux/drivers/media/video/usbvideo/usbvideo.h +++ b/linux/drivers/media/video/usbvideo/usbvideo.h @@ -20,7 +20,9 @@ #include "compat.h" #include <linux/videodev.h> #include <linux/usb.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include <linux/mutex.h> +#endif /* Most helpful debugging aid */ #define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__)))) @@ -215,7 +217,11 @@ struct uvd { unsigned long flags; /* FLAGS_USBVIDEO_xxx */ unsigned long paletteBits; /* Which palettes we accept? */ unsigned short defaultPalette; /* What palette to use for read() */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; +#else + struct semaphore lock; +#endif int user; /* user count for exclusive use */ videosize_t videosize; /* Current setting */ @@ -274,7 +280,11 @@ struct usbvideo { int num_cameras; /* As allocated */ struct usb_driver usbdrv; /* Interface to the USB stack */ char drvName[80]; /* Driver name */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; /* Mutex protecting camera structures */ +#else + struct semaphore lock; +#endif struct usbvideo_cb cb; /* Table of callbacks (virtual methods) */ struct video_device vdt; /* Video device template */ struct uvd *cam; /* Array of camera structures */ diff --git a/linux/drivers/media/video/usbvideo/vicam.c b/linux/drivers/media/video/usbvideo/vicam.c index 98cb58e89..0ae33d432 100644 --- a/linux/drivers/media/video/usbvideo/vicam.c +++ b/linux/drivers/media/video/usbvideo/vicam.c @@ -43,7 +43,9 @@ #include <linux/vmalloc.h> #include <linux/slab.h> #include <linux/proc_fs.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include <linux/mutex.h> +#endif #include "usbvideo.h" // #define VICAM_DEBUG @@ -409,7 +411,11 @@ struct vicam_camera { struct usb_device *udev; // usb device /* guard against simultaneous accesses to the camera */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex cam_lock; +#else + struct semaphore cam_lock; +#endif int is_initialized; u8 open_count; diff --git a/linux/drivers/media/video/zc0301/Kconfig b/linux/drivers/media/video/zc0301/Kconfig index 115833e4f..a859a6920 100644 --- a/linux/drivers/media/video/zc0301/Kconfig +++ b/linux/drivers/media/video/zc0301/Kconfig @@ -1,9 +1,9 @@ config USB_ZC0301 - tristate "USB ZC0301 Image Processor and Control Chip support" + tristate "USB ZC0301[P] Image Processor and Control Chip support" depends on USB && VIDEO_V4L1 ---help--- - Say Y here if you want support for cameras based on the ZC0301 - Image Processor and Control Chip. + Say Y here if you want support for cameras based on the ZC0301 or + ZC0301P Image Processors and Control Chips. See <file:Documentation/video4linux/zc0301.txt> for more info. diff --git a/linux/drivers/media/video/zc0301/Makefile b/linux/drivers/media/video/zc0301/Makefile index d749199d8..d9e6d97fa 100644 --- a/linux/drivers/media/video/zc0301/Makefile +++ b/linux/drivers/media/video/zc0301/Makefile @@ -1,3 +1,3 @@ -zc0301-objs := zc0301_core.o zc0301_pas202bcb.o +zc0301-objs := zc0301_core.o zc0301_pb0330.o zc0301_pas202bcb.o obj-$(CONFIG_USB_ZC0301) += zc0301.o diff --git a/linux/drivers/media/video/zc0301/zc0301_core.c b/linux/drivers/media/video/zc0301/zc0301_core.c index 0fad39754..7b7a3629b 100644 --- a/linux/drivers/media/video/zc0301/zc0301_core.c +++ b/linux/drivers/media/video/zc0301/zc0301_core.c @@ -52,8 +52,8 @@ #define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia" #define ZC0301_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" #define ZC0301_MODULE_LICENSE "GPL" -#define ZC0301_MODULE_VERSION "1:1.03" -#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 3) +#define ZC0301_MODULE_VERSION "1:1.04" +#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 4) /*****************************************************************************/ diff --git a/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c b/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c index eaadf0252..ecfd39a56 100644 --- a/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c +++ b/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c @@ -1,10 +1,10 @@ /*************************************************************************** - * Plug-in for PAS202BCB image sensor connected to the ZC030! Image * + * Plug-in for PAS202BCB image sensor connected to the ZC0301[P] Image * * Processor and Control Chip * * * * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * * * - * Initialization values of the ZC0301 have been taken from the SPCA5XX * + * Initialization values of the ZC0301[P] have been taken from the SPCA5XX * * driver maintained by Michel Xhaard <mxhaard@magic.fr> * * * * This program is free software; you can redistribute it and/or modify * diff --git a/linux/drivers/media/video/zc0301/zc0301_pb0330.c b/linux/drivers/media/video/zc0301/zc0301_pb0330.c new file mode 100644 index 000000000..ed8542e6c --- /dev/null +++ b/linux/drivers/media/video/zc0301/zc0301_pb0330.c @@ -0,0 +1,187 @@ +/*************************************************************************** + * Plug-in for PB-0330 image sensor connected to the ZC0301[P] Image * + * Processor and Control Chip * + * * + * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * * + * Initialization values of the ZC0301[P] have been taken from the SPCA5XX * + * driver maintained by Michel Xhaard <mxhaard@magic.fr> * + * * + * 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. * + ***************************************************************************/ + +#include <linux/delay.h> +#include "zc0301_sensor.h" + + +static struct zc0301_sensor pb0330; + + +static int pb0330_init(struct zc0301_device* cam) +{ + int err = 0; + + err += zc0301_write_reg(cam, 0x0000, 0x01); + err += zc0301_write_reg(cam, 0x0008, 0x03); + err += zc0301_write_reg(cam, 0x0010, 0x0A); + err += zc0301_write_reg(cam, 0x0002, 0x00); + err += zc0301_write_reg(cam, 0x0003, 0x02); + err += zc0301_write_reg(cam, 0x0004, 0x80); + err += zc0301_write_reg(cam, 0x0005, 0x01); + err += zc0301_write_reg(cam, 0x0006, 0xE0); + err += zc0301_write_reg(cam, 0x0001, 0x01); + err += zc0301_write_reg(cam, 0x0012, 0x05); + err += zc0301_write_reg(cam, 0x0012, 0x07); + err += zc0301_write_reg(cam, 0x0098, 0x00); + err += zc0301_write_reg(cam, 0x009A, 0x00); + err += zc0301_write_reg(cam, 0x011A, 0x00); + err += zc0301_write_reg(cam, 0x011C, 0x00); + err += zc0301_write_reg(cam, 0x0012, 0x05); + + err += zc0301_i2c_write(cam, 0x01, 0x0006); + err += zc0301_i2c_write(cam, 0x02, 0x0011); + err += zc0301_i2c_write(cam, 0x03, 0x01E7); + err += zc0301_i2c_write(cam, 0x04, 0x0287); + err += zc0301_i2c_write(cam, 0x06, 0x0003); + err += zc0301_i2c_write(cam, 0x07, 0x3002); + err += zc0301_i2c_write(cam, 0x20, 0x1100); + err += zc0301_i2c_write(cam, 0x2F, 0xF7B0); + err += zc0301_i2c_write(cam, 0x30, 0x0005); + err += zc0301_i2c_write(cam, 0x31, 0x0000); + err += zc0301_i2c_write(cam, 0x34, 0x0100); + err += zc0301_i2c_write(cam, 0x35, 0x0060); + err += zc0301_i2c_write(cam, 0x3D, 0x068F); + err += zc0301_i2c_write(cam, 0x40, 0x01E0); + err += zc0301_i2c_write(cam, 0x58, 0x0078); + err += zc0301_i2c_write(cam, 0x62, 0x0411); + + err += zc0301_write_reg(cam, 0x0087, 0x10); + err += zc0301_write_reg(cam, 0x0101, 0x37); + err += zc0301_write_reg(cam, 0x0012, 0x05); + err += zc0301_write_reg(cam, 0x0100, 0x0D); + err += zc0301_write_reg(cam, 0x0189, 0x06); + err += zc0301_write_reg(cam, 0x01AD, 0x00); + err += zc0301_write_reg(cam, 0x01C5, 0x03); + err += zc0301_write_reg(cam, 0x01CB, 0x13); + err += zc0301_write_reg(cam, 0x0250, 0x08); + err += zc0301_write_reg(cam, 0x0301, 0x08); + err += zc0301_write_reg(cam, 0x01A8, 0x60); + err += zc0301_write_reg(cam, 0x018D, 0x6C); + err += zc0301_write_reg(cam, 0x01AD, 0x09); + err += zc0301_write_reg(cam, 0x01AE, 0x15); + err += zc0301_write_reg(cam, 0x010A, 0x50); + err += zc0301_write_reg(cam, 0x010B, 0xF8); + err += zc0301_write_reg(cam, 0x010C, 0xF8); + err += zc0301_write_reg(cam, 0x010D, 0xF8); + err += zc0301_write_reg(cam, 0x010E, 0x50); + err += zc0301_write_reg(cam, 0x010F, 0xF8); + err += zc0301_write_reg(cam, 0x0110, 0xF8); + err += zc0301_write_reg(cam, 0x0111, 0xF8); + err += zc0301_write_reg(cam, 0x0112, 0x50); + err += zc0301_write_reg(cam, 0x0008, 0x03); + err += zc0301_write_reg(cam, 0x01C6, 0x08); + err += zc0301_write_reg(cam, 0x01CB, 0x0F); + err += zc0301_write_reg(cam, 0x010A, 0x50); + err += zc0301_write_reg(cam, 0x010B, 0xF8); + err += zc0301_write_reg(cam, 0x010C, 0xF8); + err += zc0301_write_reg(cam, 0x010D, 0xF8); + err += zc0301_write_reg(cam, 0x010E, 0x50); + err += zc0301_write_reg(cam, 0x010F, 0xF8); + err += zc0301_write_reg(cam, 0x0110, 0xF8); + err += zc0301_write_reg(cam, 0x0111, 0xF8); + err += zc0301_write_reg(cam, 0x0112, 0x50); + err += zc0301_write_reg(cam, 0x0180, 0x00); + err += zc0301_write_reg(cam, 0x0019, 0x00); + + err += zc0301_i2c_write(cam, 0x05, 0x0066); + err += zc0301_i2c_write(cam, 0x09, 0x02B2); + err += zc0301_i2c_write(cam, 0x10, 0x0002); + + err += zc0301_write_reg(cam, 0x011D, 0x60); + err += zc0301_write_reg(cam, 0x0190, 0x00); + err += zc0301_write_reg(cam, 0x0191, 0x07); + err += zc0301_write_reg(cam, 0x0192, 0x8C); + err += zc0301_write_reg(cam, 0x0195, 0x00); + err += zc0301_write_reg(cam, 0x0196, 0x00); + err += zc0301_write_reg(cam, 0x0197, 0x8A); + err += zc0301_write_reg(cam, 0x018C, 0x10); + err += zc0301_write_reg(cam, 0x018F, 0x20); + err += zc0301_write_reg(cam, 0x01A9, 0x14); + err += zc0301_write_reg(cam, 0x01AA, 0x24); + err += zc0301_write_reg(cam, 0x001D, 0xD7); + err += zc0301_write_reg(cam, 0x001E, 0xF0); + err += zc0301_write_reg(cam, 0x001F, 0xF8); + err += zc0301_write_reg(cam, 0x0020, 0xFF); + err += zc0301_write_reg(cam, 0x01AD, 0x09); + err += zc0301_write_reg(cam, 0x01AE, 0x15); + err += zc0301_write_reg(cam, 0x0180, 0x40); + err += zc0301_write_reg(cam, 0x0180, 0x42); + + msleep(100); + + return err; +} + + +static struct zc0301_sensor pb0330 = { + .name = "PB-0330", + .init = &pb0330_init, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_JPEG, + .priv = 8, + }, +}; + + +int zc0301_probe_pb0330(struct zc0301_device* cam) +{ + int r0, err = 0; + + err += zc0301_write_reg(cam, 0x0000, 0x01); + err += zc0301_write_reg(cam, 0x0010, 0x0a); + err += zc0301_write_reg(cam, 0x0001, 0x01); + err += zc0301_write_reg(cam, 0x0012, 0x03); + err += zc0301_write_reg(cam, 0x0012, 0x01); + + msleep(10); + + r0 = zc0301_i2c_read(cam, 0x00, 2); + + if (r0 < 0 || err) + return -EIO; + + if (r0 != 0x8243) + return -ENODEV; + + zc0301_attach_sensor(cam, &pb0330); + + return 0; +} diff --git a/linux/drivers/media/video/zc0301/zc0301_sensor.h b/linux/drivers/media/video/zc0301/zc0301_sensor.h index 7f11465ce..fd8abf969 100644 --- a/linux/drivers/media/video/zc0301/zc0301_sensor.h +++ b/linux/drivers/media/video/zc0301/zc0301_sensor.h @@ -1,5 +1,5 @@ /*************************************************************************** - * API for image sensors connected to the ZC030! Image Processor and * + * API for image sensors connected to the ZC0301 Image Processor and * * Control Chip * * * * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * @@ -36,11 +36,13 @@ struct zc0301_sensor; /*****************************************************************************/ extern int zc0301_probe_pas202bcb(struct zc0301_device* cam); +extern int zc0301_probe_pb0330(struct zc0301_device* cam); #define ZC0301_SENSOR_TABLE \ /* Weak detections must go at the end of the list */ \ static int (*zc0301_sensor_table[])(struct zc0301_device*) = { \ &zc0301_probe_pas202bcb, \ + &zc0301_probe_pb0330, \ NULL, \ }; @@ -59,14 +61,28 @@ zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor); #define ZC0301_ID_TABLE \ static const struct usb_device_id zc0301_id_table[] = { \ - { ZC0301_USB_DEVICE(0x041e, 0x4017, 0xff), }, \ + { ZC0301_USB_DEVICE(0x041e, 0x4017, 0xff), }, /* ICM105 */ \ { ZC0301_USB_DEVICE(0x041e, 0x401c, 0xff), }, /* PAS106 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x401e, 0xff), }, /* HV7131B */ \ + { ZC0301_USB_DEVICE(0x041e, 0x401e, 0xff), }, /* HV7131 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x401f, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x4022, 0xff), }, \ { ZC0301_USB_DEVICE(0x041e, 0x4034, 0xff), }, /* PAS106 */ \ { ZC0301_USB_DEVICE(0x041e, 0x4035, 0xff), }, /* PAS106 */ \ - { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202BCB */ \ + { ZC0301_USB_DEVICE(0x041e, 0x4036, 0xff), }, /* HV7131 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x403a, 0xff), }, /* HV7131 */ \ + { ZC0301_USB_DEVICE(0x0458, 0x7007, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x0458, 0x700C, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x0458, 0x700f, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ + { ZC0301_USB_DEVICE(0x055f, 0xd003, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x055f, 0xd004, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ { ZC0301_USB_DEVICE(0x0ac8, 0x0301, 0xff), }, \ - { ZC0301_USB_DEVICE(0x10fd, 0x8050, 0xff), }, /* TAS5130D */ \ + { ZC0301_USB_DEVICE(0x0ac8, 0x301b, 0xff), }, /* PB-0330/HV7131 */ \ + { ZC0301_USB_DEVICE(0x0ac8, 0x303b, 0xff), }, /* PB-0330 */ \ + { ZC0301_USB_DEVICE(0x10fd, 0x0128, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x10fd, 0x8050, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x10fd, 0x804e, 0xff), }, /* TAS5130 */ \ { } \ }; |