diff options
Diffstat (limited to 'linux/drivers')
92 files changed, 6252 insertions, 1103 deletions
diff --git a/linux/drivers/media/Kconfig b/linux/drivers/media/Kconfig index 1f7244cff..128bb9cd5 100644 --- a/linux/drivers/media/Kconfig +++ b/linux/drivers/media/Kconfig @@ -30,7 +30,7 @@ config VIDEO_V4L2_COMMON depends on (I2C || I2C=n) && VIDEO_DEV default (I2C || I2C=n) && VIDEO_DEV -config VIDEO_V4L1 +config VIDEO_ALLOW_V4L1 bool "Enable Video For Linux API 1 (DEPRECATED)" depends on VIDEO_DEV && VIDEO_V4L2_COMMON default VIDEO_DEV && VIDEO_V4L2_COMMON @@ -59,10 +59,15 @@ config VIDEO_V4L1_COMPAT If you are unsure as to whether this is required, answer Y. config VIDEO_V4L2 - bool + tristate depends on VIDEO_DEV && VIDEO_V4L2_COMMON default VIDEO_DEV && VIDEO_V4L2_COMMON +config VIDEO_V4L1 + tristate + depends on VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1 + default VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1 + source "drivers/media/video/Kconfig" source "drivers/media/radio/Kconfig" diff --git a/linux/drivers/media/common/ir-keymaps.c b/linux/drivers/media/common/ir-keymaps.c index 4e5db8cd5..64d3df831 100644 --- a/linux/drivers/media/common/ir-keymaps.c +++ b/linux/drivers/media/common/ir-keymaps.c @@ -772,7 +772,11 @@ IR_KEYTAB_TYPE ir_codes_flyvideo[IR_KEYTAB_SIZE] = { [ 0x12 ] = KEY_CHANNELUP, // Channel + [ 0x13 ] = KEY_CHANNELDOWN, // Channel - [ 0x06 ] = KEY_AGAIN, // Recall - [ 0x10 ] = KEY_ENTER, // Enter + [ 0x10 ] = KEY_ENTER, // Enter + + [ 0x19 ] = KEY_BACK, // Rewind ( <<< ) + [ 0x1f ] = KEY_FORWARD, // Forward ( >>> ) + [ 0x0a ] = KEY_ANGLE, // (no label, may be used as the PAUSE button) }; EXPORT_SYMBOL_GPL(ir_codes_flyvideo); diff --git a/linux/drivers/media/common/saa7146_core.c b/linux/drivers/media/common/saa7146_core.c index 67b7dbc81..973989dd4 100644 --- a/linux/drivers/media/common/saa7146_core.c +++ b/linux/drivers/media/common/saa7146_core.c @@ -310,25 +310,22 @@ static irqreturn_t interrupt_hw(int irq, void *dev_id) return IRQ_NONE; } - if( NULL != (dev->ext)) { - if( 0 != (dev->ext->irq_mask & isr )) { - if( NULL != dev->ext->irq_func ) { + if (dev->ext) { + if (dev->ext->irq_mask & isr) { + if (dev->ext->irq_func) dev->ext->irq_func(dev, &isr); - } isr &= ~dev->ext->irq_mask; } } if (0 != (isr & (MASK_27))) { DEB_INT(("irq: RPS0 (0x%08x).\n",isr)); - if( NULL != dev->vv_data && NULL != dev->vv_callback) { + if (dev->vv_data && dev->vv_callback) dev->vv_callback(dev,isr); - } isr &= ~MASK_27; } if (0 != (isr & (MASK_28))) { - if( NULL != dev->vv_data && NULL != dev->vv_callback) { + if (dev->vv_data && dev->vv_callback) dev->vv_callback(dev,isr); - } isr &= ~MASK_28; } if (0 != (isr & (MASK_16|MASK_17))) { diff --git a/linux/drivers/media/common/saa7146_fops.c b/linux/drivers/media/common/saa7146_fops.c index fee81488f..ac035cbcf 100644 --- a/linux/drivers/media/common/saa7146_fops.c +++ b/linux/drivers/media/common/saa7146_fops.c @@ -273,7 +273,7 @@ static int fops_open(struct inode *inode, struct file *file) result = 0; out: - if( fh != NULL && result != 0 ) { + if (fh && result != 0) { kfree(fh); file->private_data = NULL; } diff --git a/linux/drivers/media/dvb/b2c2/Kconfig b/linux/drivers/media/dvb/b2c2/Kconfig index 8193d88d1..6ec5afba1 100644 --- a/linux/drivers/media/dvb/b2c2/Kconfig +++ b/linux/drivers/media/dvb/b2c2/Kconfig @@ -10,6 +10,8 @@ config DVB_B2C2_FLEXCOP select DVB_BCM3510 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE select TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select DVB_S5H1420 if !DVB_FE_CUSTOMISE + select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE select DVB_CX24123 if !DVB_FE_CUSTOMISE help diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 6759c3ad2..04989b7a1 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -19,6 +19,9 @@ #include "dvb-pll.h" #include "tuner-simple.h" +#include "s5h1420.h" +#include "itd1000.h" + #include "cx24123.h" #include "cx24113.h" @@ -482,6 +485,18 @@ static struct stv0297_config alps_tdee4_stv0297_config = { }; +/* SkyStar2 rev2.7 (a/u) */ +static struct s5h1420_config skystar2_rev2_7_s5h1420_config = { + .demod_address = 0x53, + .invert = 1, + .repeated_start_workaround = 1, +}; + +static struct itd1000_config skystar2_rev2_7_itd1000_config = { + .i2c_address = 0x61, +}; + +/* SkyStar2 rev2.8 */ static struct cx24123_config skystar2_rev2_8_cx24123_config = { .demod_address = 0x55, .dont_use_pll = 1, @@ -500,6 +515,39 @@ int flexcop_frontend_init(struct flexcop_device *fc) struct i2c_adapter *i2c = &fc->fc_i2c_adap[0].i2c_adap; struct i2c_adapter *i2c_tuner; + /* enable no_base_addr - no repeated start when reading */ + fc->fc_i2c_adap[0].no_base_addr = 1; + fc->fe = dvb_attach(s5h1420_attach, &skystar2_rev2_7_s5h1420_config, i2c); + if (fc->fe != NULL) { + flexcop_ibi_value r108; + i2c_tuner = s5h1420_get_tuner_i2c_adapter(fc->fe); + ops = &fc->fe->ops; + + fc->fe_sleep = ops->sleep; + ops->sleep = flexcop_sleep; + + fc->dev_type = FC_SKY_REV27; + + /* enable no_base_addr - no repeated start when reading */ + fc->fc_i2c_adap[2].no_base_addr = 1; + if (dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap, 0x08, 1, 1) == NULL) + err("ISL6421 could NOT be attached"); + else + info("ISL6421 successfully attached"); + + /* the ITD1000 requires a lower i2c clock - it slows down the stuff for everyone - but is it a problem ? */ + r108.raw = 0x00000506; + fc->write_ibi_reg(fc, tw_sm_c_108, r108); + if (i2c_tuner) { + if (dvb_attach(itd1000_attach, fc->fe, i2c_tuner, &skystar2_rev2_7_itd1000_config) == NULL) + err("ITD1000 could NOT be attached"); + else + info("ITD1000 successfully attached"); + } + goto fe_found; + } + fc->fc_i2c_adap[0].no_base_addr = 0; /* for the next devices we need it again */ + /* try the sky v2.8 (cx24123, isl6421) */ fc->fe = dvb_attach(cx24123_attach, &skystar2_rev2_8_cx24123_config, i2c); diff --git a/linux/drivers/media/dvb/dvb-core/dvb_net.c b/linux/drivers/media/dvb/dvb-core/dvb_net.c index 86b11c3cc..04c9e00b0 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_net.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_net.c @@ -810,8 +810,8 @@ static int dvb_net_ts_callback(const u8 *buffer1, size_t buffer1_len, { struct net_device *dev = feed->priv; - if (buffer2 != 0) - printk(KERN_WARNING "buffer2 not 0: %p.\n", buffer2); + if (buffer2) + printk(KERN_WARNING "buffer2 not NULL: %p.\n", buffer2); if (buffer1_len > 32768) printk(KERN_WARNING "length > 32k: %zu.\n", buffer1_len); /* printk("TS callback: %u bytes, %u TS cells @ %p.\n", diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index 8017f1e8a..ddfa0cff7 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -33,7 +33,6 @@ #include "mt352_priv.h" #include "zl10353.h" #include "tuner-xc2028.h" -#include "tuner-xc2028-types.h" #include "tuner-simple.h" /* debug */ @@ -521,7 +520,7 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap) static struct xc2028_ctrl ctl = { .fname = "xc3028-dvico-au-01.fw", .max_len = 64, - .scode_table = ZARLINK456, + .scode_table = XC3028_FE_ZARLINK456, }; fe = dvb_attach(xc2028_attach, adap->fe, &cfg); diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index 0209644f2..68fab616f 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -188,6 +188,14 @@ config DVB_DIB7000P A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. +config DVB_TDA10048 + tristate "Philips TDA10048HN based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + select FW_LOADER + help + A DVB-T tuner module. Say Y when you want to support this frontend. + comment "DVB-C (cable) frontends" depends on DVB_CORE @@ -291,6 +299,14 @@ config DVB_S5H1409 An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. +config DVB_AU8522 + tristate "Auvitek AU8522 based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + to support this frontend. + comment "Tuners/PLL support" depends on DVB_CORE @@ -369,6 +385,11 @@ config DVB_TUNER_XC5000 This device is only used inside a SiP called togther with a demodulator for now. +config DVB_TUNER_ITD1000 + tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + comment "Miscellaneous devices" depends on DVB_CORE diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile index 23304b377..2f873fc0f 100644 --- a/linux/drivers/media/dvb/frontends/Makefile +++ b/linux/drivers/media/dvb/frontends/Makefile @@ -52,3 +52,6 @@ obj-$(CONFIG_DVB_TUA6100) += tua6100.o obj-$(CONFIG_DVB_TUNER_MT2131) += mt2131.o obj-$(CONFIG_DVB_S5H1409) += s5h1409.o obj-$(CONFIG_DVB_TUNER_XC5000) += xc5000.o +obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o +obj-$(CONFIG_DVB_AU8522) += au8522.o +obj-$(CONFIG_DVB_TDA10048) += tda10048.o diff --git a/linux/drivers/media/dvb/frontends/au8522.c b/linux/drivers/media/dvb/frontends/au8522.c new file mode 100644 index 000000000..d536454b6 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/au8522.c @@ -0,0 +1,698 @@ +/* + Auvitek AU8522 QAM/8VSB demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + + 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/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include "dvb_frontend.h" +#include "dvb-pll.h" +#include "au8522.h" + +struct au8522_state { + + struct i2c_adapter *i2c; + + /* configuration settings */ + const struct au8522_config *config; + + struct dvb_frontend frontend; + + u32 current_frequency; + fe_modulation_t current_modulation; + +}; + +static int debug; + +#define dprintk(arg...) do { \ + if (debug) \ + printk(arg); \ + } while (0) + +/* 16 bit registers, 8 bit values */ +static int au8522_writereg(struct au8522_state *state, u16 reg, u8 data) +{ + int ret; + u8 buf [] = { reg >> 8, reg & 0xff, data }; + + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = 0, .buf = buf, .len = 3 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, " + "ret == %i)\n", __func__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static u8 au8522_readreg(struct au8522_state *state, u16 reg) +{ + int ret; + u8 b0 [] = { reg >> 8, reg & 0xff }; + u8 b1 [] = { 0 }; + + struct i2c_msg msg [] = { + { .addr = state->config->demod_address, .flags = 0, + .buf = b0, .len = 2 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, + .buf = b1, .len = 1 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + printk(KERN_ERR "%s: readreg error (ret == %i)\n", + __func__, ret); + return b1[0]; +} + +static int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct au8522_state *state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __func__, enable); + + if (enable) + return au8522_writereg(state, 0x106, 1); + else + return au8522_writereg(state, 0x106, 0); +} + +struct mse2snr_tab { + u16 val; + u16 data; +}; + +/* VSB SNR lookup table */ +static struct mse2snr_tab vsb_mse2snr_tab[] = { + { 0, 270 }, + { 2, 250 }, + { 3, 240 }, + { 5, 230 }, + { 7, 220 }, + { 9, 210 }, + { 12, 200 }, + { 13, 195 }, + { 15, 190 }, + { 17, 185 }, + { 19, 180 }, + { 21, 175 }, + { 24, 170 }, + { 27, 165 }, + { 31, 160 }, + { 32, 158 }, + { 33, 156 }, + { 36, 152 }, + { 37, 150 }, + { 39, 148 }, + { 40, 146 }, + { 41, 144 }, + { 43, 142 }, + { 44, 140 }, + { 48, 135 }, + { 50, 130 }, + { 43, 142 }, + { 53, 125 }, + { 56, 120 }, + { 256, 115 }, +}; + +/* QAM64 SNR lookup table */ +static struct mse2snr_tab qam64_mse2snr_tab[] = { + { 15, 0 }, + { 16, 290 }, + { 17, 288 }, + { 18, 286 }, + { 19, 284 }, + { 20, 282 }, + { 21, 281 }, + { 22, 279 }, + { 23, 277 }, + { 24, 275 }, + { 25, 273 }, + { 26, 271 }, + { 27, 269 }, + { 28, 268 }, + { 29, 266 }, + { 30, 264 }, + { 31, 262 }, + { 32, 260 }, + { 33, 259 }, + { 34, 258 }, + { 35, 256 }, + { 36, 255 }, + { 37, 254 }, + { 38, 252 }, + { 39, 251 }, + { 40, 250 }, + { 41, 249 }, + { 42, 248 }, + { 43, 246 }, + { 44, 245 }, + { 45, 244 }, + { 46, 242 }, + { 47, 241 }, + { 48, 240 }, + { 50, 239 }, + { 51, 238 }, + { 53, 237 }, + { 54, 236 }, + { 56, 235 }, + { 57, 234 }, + { 59, 233 }, + { 60, 232 }, + { 62, 231 }, + { 63, 230 }, + { 65, 229 }, + { 67, 228 }, + { 68, 227 }, + { 70, 226 }, + { 71, 225 }, + { 73, 224 }, + { 74, 223 }, + { 76, 222 }, + { 78, 221 }, + { 80, 220 }, + { 82, 219 }, + { 85, 218 }, + { 88, 217 }, + { 90, 216 }, + { 92, 215 }, + { 93, 214 }, + { 94, 212 }, + { 95, 211 }, + { 97, 210 }, + { 99, 209 }, + { 101, 208 }, + { 102, 207 }, + { 104, 206 }, + { 107, 205 }, + { 111, 204 }, + { 114, 203 }, + { 118, 202 }, + { 122, 201 }, + { 125, 200 }, + { 128, 199 }, + { 130, 198 }, + { 132, 197 }, + { 256, 190 }, +}; + +/* QAM256 SNR lookup table */ +static struct mse2snr_tab qam256_mse2snr_tab[] = { + { 16, 0 }, + { 17, 400 }, + { 18, 398 }, + { 19, 396 }, + { 20, 394 }, + { 21, 392 }, + { 22, 390 }, + { 23, 388 }, + { 24, 386 }, + { 25, 384 }, + { 26, 382 }, + { 27, 380 }, + { 28, 379 }, + { 29, 378 }, + { 30, 377 }, + { 31, 376 }, + { 32, 375 }, + { 33, 374 }, + { 34, 373 }, + { 35, 372 }, + { 36, 371 }, + { 37, 370 }, + { 38, 362 }, + { 39, 354 }, + { 40, 346 }, + { 41, 338 }, + { 42, 330 }, + { 43, 328 }, + { 44, 326 }, + { 45, 324 }, + { 46, 322 }, + { 47, 320 }, + { 48, 319 }, + { 49, 318 }, + { 50, 317 }, + { 51, 316 }, + { 52, 315 }, + { 53, 314 }, + { 54, 313 }, + { 55, 312 }, + { 56, 311 }, + { 57, 310 }, + { 58, 308 }, + { 59, 306 }, + { 60, 304 }, + { 61, 302 }, + { 62, 300 }, + { 63, 298 }, + { 65, 295 }, + { 68, 294 }, + { 70, 293 }, + { 73, 292 }, + { 76, 291 }, + { 78, 290 }, + { 79, 289 }, + { 81, 288 }, + { 82, 287 }, + { 83, 286 }, + { 84, 285 }, + { 85, 284 }, + { 86, 283 }, + { 88, 282 }, + { 89, 281 }, + { 256, 280 }, +}; + +static int au8522_mse2snr_lookup(struct mse2snr_tab *tab, int sz, int mse, + u16 *snr) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __func__); + + for (i = 0; i < sz; i++) { + if (mse < tab[i].val) { + *snr = tab[i].data; + ret = 0; + break; + } + } + dprintk("%s() snr=%d\n", __func__, *snr); + return ret; +} + +/* VSB Modulation table */ +static struct { + u16 reg; + u16 data; +} VSB_mod_tab[] = { + { 0x8090, 0x84 }, + { 0x4092, 0x11 }, + { 0x2005, 0x00 }, + { 0x8091, 0x80 }, + { 0x80a3, 0x0c }, + { 0x80a4, 0xe8 }, + { 0x8081, 0xc4 }, + { 0x80a5, 0x40 }, + { 0x80a7, 0x40 }, + { 0x80a6, 0x67 }, + { 0x8262, 0x20 }, + { 0x821c, 0x30 }, + { 0x80d8, 0x1a }, + { 0x8227, 0xa0 }, + { 0x8121, 0xff }, + { 0x80a8, 0xf0 }, + { 0x80a9, 0x05 }, + { 0x80aa, 0x77 }, + { 0x80ab, 0xf0 }, + { 0x80ac, 0x05 }, + { 0x80ad, 0x77 }, + { 0x80ae, 0x41 }, + { 0x80af, 0x66 }, + { 0x821b, 0xcc }, + { 0x821d, 0x80 }, + { 0x80b5, 0xfb }, + { 0x80b6, 0x8e }, + { 0x80b7, 0x39 }, + { 0x80a4, 0xe8 }, + { 0x8231, 0x13 }, +}; + +/* QAM Modulation table */ +static struct { + u16 reg; + u16 data; +} QAM_mod_tab[] = { + { 0x80a3, 0x09 }, + { 0x80a4, 0x00 }, + { 0x8081, 0xc4 }, + { 0x80a5, 0x40 }, + { 0x80b5, 0xfb }, + { 0x80b6, 0x8e }, + { 0x80b7, 0x39 }, + { 0x80aa, 0x77 }, + { 0x80ad, 0x77 }, + { 0x80a6, 0x67 }, + { 0x8262, 0x20 }, + { 0x821c, 0x30 }, + { 0x80b8, 0x3e }, + { 0x80b9, 0xf0 }, + { 0x80ba, 0x01 }, + { 0x80bb, 0x18 }, + { 0x80bc, 0x50 }, + { 0x80bd, 0x00 }, + { 0x80be, 0xea }, + { 0x80bf, 0xef }, + { 0x80c0, 0xfc }, + { 0x80c1, 0xbd }, + { 0x80c2, 0x1f }, + { 0x80c3, 0xfc }, + { 0x80c4, 0xdd }, + { 0x80c5, 0xaf }, + { 0x80c6, 0x00 }, + { 0x80c7, 0x38 }, + { 0x80c8, 0x30 }, + { 0x80c9, 0x05 }, + { 0x80ca, 0x4a }, + { 0x80cb, 0xd0 }, + { 0x80cc, 0x01 }, + { 0x80cd, 0xd9 }, + { 0x80ce, 0x6f }, + { 0x80cf, 0xf9 }, + { 0x80d0, 0x70 }, + { 0x80d1, 0xdf }, + { 0x80d2, 0xf7 }, + { 0x80d3, 0xc2 }, + { 0x80d4, 0xdf }, + { 0x80d5, 0x02 }, + { 0x80d6, 0x9a }, + { 0x80d7, 0xd0 }, + { 0x8250, 0x0d }, + { 0x8251, 0xcd }, + { 0x8252, 0xe0 }, + { 0x8253, 0x05 }, + { 0x8254, 0xa7 }, + { 0x8255, 0xff }, + { 0x8256, 0xed }, + { 0x8257, 0x5b }, + { 0x8258, 0xae }, + { 0x8259, 0xe6 }, + { 0x825a, 0x3d }, + { 0x825b, 0x0f }, + { 0x825c, 0x0d }, + { 0x825d, 0xea }, + { 0x825e, 0xf2 }, + { 0x825f, 0x51 }, + { 0x8260, 0xf5 }, + { 0x8261, 0x06 }, + { 0x821a, 0x00 }, + { 0x8546, 0x40 }, + { 0x8210, 0x26 }, + { 0x8211, 0xf6 }, + { 0x8212, 0x84 }, + { 0x8213, 0x02 }, + { 0x8502, 0x01 }, + { 0x8121, 0x04 }, + { 0x8122, 0x04 }, + { 0x852e, 0x10 }, + { 0x80a4, 0xca }, + { 0x80a7, 0x40 }, + { 0x8526, 0x01 }, +}; + +static int au8522_enable_modulation(struct dvb_frontend *fe, + fe_modulation_t m) +{ + struct au8522_state *state = fe->demodulator_priv; + int i; + + dprintk("%s(0x%08x)\n", __func__, m); + + switch (m) { + case VSB_8: + dprintk("%s() VSB_8\n", __func__); + for (i = 0; i < ARRAY_SIZE(VSB_mod_tab); i++) + au8522_writereg(state, + VSB_mod_tab[i].reg, + VSB_mod_tab[i].data); + break; + case QAM_64: + case QAM_256: + dprintk("%s() QAM 64/256\n", __func__); + for (i = 0; i < ARRAY_SIZE(QAM_mod_tab); i++) + au8522_writereg(state, + QAM_mod_tab[i].reg, + QAM_mod_tab[i].data); + break; + default: + dprintk("%s() Invalid modulation\n", __func__); + return -EINVAL; + } + + state->current_modulation = m; + + return 0; +} + +/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +static int au8522_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct au8522_state *state = fe->demodulator_priv; + + dprintk("%s(frequency=%d)\n", __func__, p->frequency); + + state->current_frequency = p->frequency; + + au8522_enable_modulation(fe, p->u.vsb.modulation); + + /* Allow the demod to settle */ + msleep(100); + + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe, p); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + return 0; +} + +/* Reset the demod hardware and reset all of the configuration registers + to a default state. */ +static int au8522_init(struct dvb_frontend *fe) +{ + struct au8522_state *state = fe->demodulator_priv; + dprintk("%s()\n", __func__); + + au8522_writereg(state, 0xa4, 1 << 5); + + au8522_i2c_gate_ctrl(fe, 1); + + return 0; +} + +static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct au8522_state *state = fe->demodulator_priv; + u8 reg; + u32 tuner_status = 0; + + *status = 0; + + if (state->current_modulation == VSB_8) { + dprintk("%s() Checking VSB_8\n", __func__); + reg = au8522_readreg(state, 0x4088); + if (reg & 0x01) + *status |= FE_HAS_VITERBI; + if (reg & 0x02) + *status |= FE_HAS_LOCK | FE_HAS_SYNC; + } else { + dprintk("%s() Checking QAM\n", __func__); + reg = au8522_readreg(state, 0x4541); + if (reg & 0x80) + *status |= FE_HAS_VITERBI; + if (reg & 0x20) + *status |= FE_HAS_LOCK | FE_HAS_SYNC; + } + + switch (state->config->status_mode) { + case AU8522_DEMODLOCKING: + dprintk("%s() DEMODLOCKING\n", __func__); + if (*status & FE_HAS_VITERBI) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + break; + case AU8522_TUNERLOCKING: + /* Get the tuner status */ + dprintk("%s() TUNERLOCKING\n", __func__); + if (fe->ops.tuner_ops.get_status) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + fe->ops.tuner_ops.get_status(fe, &tuner_status); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + if (tuner_status) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + break; + } + + dprintk("%s() status 0x%08x\n", __func__, *status); + + return 0; +} + +static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct au8522_state *state = fe->demodulator_priv; + int ret = -EINVAL; + + dprintk("%s()\n", __func__); + + if (state->current_modulation == QAM_256) + ret = au8522_mse2snr_lookup(qam256_mse2snr_tab, + ARRAY_SIZE(qam256_mse2snr_tab), + au8522_readreg(state, 0x4522), + snr); + else if (state->current_modulation == QAM_64) + ret = au8522_mse2snr_lookup(qam64_mse2snr_tab, + ARRAY_SIZE(qam64_mse2snr_tab), + au8522_readreg(state, 0x4522), + snr); + else /* VSB_8 */ + ret = au8522_mse2snr_lookup(vsb_mse2snr_tab, + ARRAY_SIZE(vsb_mse2snr_tab), + au8522_readreg(state, 0x4311), + snr); + + return ret; +} + +static int au8522_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + return au8522_read_snr(fe, signal_strength); +} + +static int au8522_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct au8522_state *state = fe->demodulator_priv; + + if (state->current_modulation == VSB_8) + *ucblocks = au8522_readreg(state, 0x4087); + else + *ucblocks = au8522_readreg(state, 0x4543); + + return 0; +} + +static int au8522_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + return au8522_read_ucblocks(fe, ber); +} + +static int au8522_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct au8522_state *state = fe->demodulator_priv; + + p->frequency = state->current_frequency; + p->u.vsb.modulation = state->current_modulation; + + return 0; +} + +static int au8522_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void au8522_release(struct dvb_frontend *fe) +{ + struct au8522_state *state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops au8522_ops; + +struct dvb_frontend *au8522_attach(const struct au8522_config *config, + struct i2c_adapter *i2c) +{ + struct au8522_state *state = NULL; + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct au8522_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; +#if 0 + /* check if the demod exists */ + reg = au8522_readreg(state, 0x04); + if ((reg != 0x0066) && (reg != 0x007f)) + goto error; +#endif + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &au8522_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + if (au8522_init(&state->frontend) != 0) { + printk(KERN_ERR "%s: Failed to initialize correctly\n", + __func__); + goto error; + } + + /* Note: Leaving the I2C gate open here. */ + au8522_i2c_gate_ctrl(&state->frontend, 1); + + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(au8522_attach); + +static struct dvb_frontend_ops au8522_ops = { + + .info = { + .name = "Auvitek AU8522 QAM/8VSB Frontend", + .type = FE_ATSC, + .frequency_min = 54000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + + .init = au8522_init, + .i2c_gate_ctrl = au8522_i2c_gate_ctrl, + .set_frontend = au8522_set_frontend, + .get_frontend = au8522_get_frontend, + .get_tune_settings = au8522_get_tune_settings, + .read_status = au8522_read_status, + .read_ber = au8522_read_ber, + .read_signal_strength = au8522_read_signal_strength, + .read_snr = au8522_read_snr, + .read_ucblocks = au8522_read_ucblocks, + .release = au8522_release, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable verbose debug messages"); + +MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/au8522.h b/linux/drivers/media/dvb/frontends/au8522.h new file mode 100644 index 000000000..d7affa3cd --- /dev/null +++ b/linux/drivers/media/dvb/frontends/au8522.h @@ -0,0 +1,56 @@ +/* + Auvitek AU8522 QAM/8VSB demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + + 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 __AU8522_H__ +#define __AU8522_H__ + +#include <linux/dvb/frontend.h> + +struct au8522_config { + /* the demodulator's i2c address */ + u8 demod_address; + + /* Return lock status based on tuner lock, or demod lock */ +#define AU8522_TUNERLOCKING 0 +#define AU8522_DEMODLOCKING 1 + u8 status_mode; +}; + +#if defined(CONFIG_DVB_AU8522) || \ + (defined(CONFIG_DVB_AU8522_MODULE) && defined(MODULE)) +extern struct dvb_frontend *au8522_attach(const struct au8522_config *config, + struct i2c_adapter *i2c); +#else +static inline +struct dvb_frontend *au8522_attach(const struct au8522_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_AU8522 */ + +#endif /* __AU8522_H__ */ + +/* + * Local variables: + * c-basic-offset: 8 + */ diff --git a/linux/drivers/media/dvb/frontends/itd1000.c b/linux/drivers/media/dvb/frontends/itd1000.c new file mode 100644 index 000000000..abcd0ddae --- /dev/null +++ b/linux/drivers/media/dvb/frontends/itd1000.c @@ -0,0 +1,408 @@ +/* + * Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite" + * + * Copyright (c) 2007-8 Patrick Boettcher <pb@linuxtv.org> + * + * 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/module.h> +#include <linux/moduleparam.h> +#include <linux/delay.h> +#include <linux/dvb/frontend.h> +#include <linux/i2c.h> + +#include "dvb_frontend.h" + +#include "itd1000.h" +#include "itd1000_priv.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +#define deb(args...) do { \ + if (debug) { \ + printk(KERN_DEBUG "ITD1000: " args);\ + printk("\n"); \ + } \ +} while (0) + +#define warn(args...) do { \ + printk(KERN_WARNING "ITD1000: " args); \ + printk("\n"); \ +} while (0) + +#define info(args...) do { \ + printk(KERN_INFO "ITD1000: " args); \ + printk("\n"); \ +} while (0) + +/* don't write more than one byte with flexcop behind */ +static int itd1000_write_regs(struct itd1000_state *state, u8 reg, u8 v[], u8 len) +{ + u8 buf[1+len]; + struct i2c_msg msg = { + .addr = state->cfg->i2c_address, .flags = 0, .buf = buf, .len = len+1 + }; + buf[0] = reg; + memcpy(&buf[1], v, len); + + /* deb("wr %02x: %02x", reg, v[0]); */ + + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "itd1000 I2C write failed\n"); + return -EREMOTEIO; + } + return 0; +} + +static int itd1000_read_reg(struct itd1000_state *state, u8 reg) +{ + u8 val; + struct i2c_msg msg[2] = { + { .addr = state->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, + { .addr = state->cfg->i2c_address, .flags = I2C_M_RD, .buf = &val, .len = 1 }, + }; + + /* ugly flexcop workaround */ + itd1000_write_regs(state, (reg - 1) & 0xff, &state->shadow[(reg - 1) & 0xff], 1); + + if (i2c_transfer(state->i2c, msg, 2) != 2) { + warn("itd1000 I2C read failed"); + return -EREMOTEIO; + } + return val; +} + +static inline int itd1000_write_reg(struct itd1000_state *state, u8 r, u8 v) +{ + int ret = itd1000_write_regs(state, r, &v, 1); + state->shadow[r] = v; + return ret; +} + +#if 0 +static void reg_dump(struct itd1000_state *state) +{ + int i; + for (i = 0x65; i < 0x9c; i++) + printk(KERN_DEBUG "ITD: %02x: %02x\n", i, itd1000_read_reg(state, i)); +} +#endif + +static struct { + u32 symbol_rate; + u8 pgaext : 4; /* PLLFH */ + u8 bbgvmin : 4; /* BBGVMIN */ +} itd1000_lpf_pga[] = { + { 0, 0x8, 0x3 }, + { 5200000, 0x8, 0x3 }, + { 12200000, 0x4, 0x3 }, + { 15400000, 0x2, 0x3 }, + { 19800000, 0x2, 0x3 }, + { 21500000, 0x2, 0x3 }, + { 24500000, 0x2, 0x3 }, + { 28400000, 0x2, 0x3 }, + { 33400000, 0x2, 0x3 }, + { 34400000, 0x1, 0x4 }, + { 34400000, 0x1, 0x4 }, + { 38400000, 0x1, 0x4 }, + { 38400000, 0x1, 0x4 }, + { 40400000, 0x1, 0x4 }, + { 45400000, 0x1, 0x4 }, +}; + +static void itd1000_set_lpf_bw(struct itd1000_state *state, u32 symbol_rate) +{ + u8 i; + u8 con1 = itd1000_read_reg(state, CON1) & 0xfd; + u8 pllfh = itd1000_read_reg(state, PLLFH) & 0x0f; + u8 bbgvmin = itd1000_read_reg(state, BBGVMIN) & 0xf0; + u8 bw = itd1000_read_reg(state, BW) & 0xf0; + + deb("symbol_rate = %d", symbol_rate); + + /* not sure what is that ? - starting to download the table */ + itd1000_write_reg(state, CON1, con1 | (1 << 1)); + + for (i = 0; i < ARRAY_SIZE(itd1000_lpf_pga); i++) + if (symbol_rate < itd1000_lpf_pga[i].symbol_rate) { + deb("symrate: index: %d pgaext: %x, bbgvmin: %x", i, itd1000_lpf_pga[i].pgaext, itd1000_lpf_pga[i].bbgvmin); + itd1000_write_reg(state, PLLFH, pllfh | (itd1000_lpf_pga[i].pgaext << 4)); + itd1000_write_reg(state, BBGVMIN, bbgvmin | (itd1000_lpf_pga[i].bbgvmin)); + itd1000_write_reg(state, BW, bw | (i & 0x0f)); + break; + } + + itd1000_write_reg(state, CON1, con1 | (0 << 1)); +} + +static struct { + u8 vcorg; + u32 fmax_rg; +} itd1000_vcorg[] = { + { 1, 920000 }, + { 2, 971000 }, + { 3, 1031000 }, + { 4, 1091000 }, + { 5, 1171000 }, + { 6, 1281000 }, + { 7, 1381000 }, + { 8, 500000 }, /* this is intentional. */ + { 9, 1451000 }, + { 10, 1531000 }, + { 11, 1631000 }, + { 12, 1741000 }, + { 13, 1891000 }, + { 14, 2071000 }, + { 15, 2250000 }, +}; + +static void itd1000_set_vco(struct itd1000_state *state, u32 freq_khz) +{ + u8 i; + u8 gvbb_i2c = itd1000_read_reg(state, GVBB_I2C) & 0xbf; + u8 vco_chp1_i2c = itd1000_read_reg(state, VCO_CHP1_I2C) & 0x0f; + u8 adcout; + + /* reserved bit again (reset ?) */ + itd1000_write_reg(state, GVBB_I2C, gvbb_i2c | (1 << 6)); + + for (i = 0; i < ARRAY_SIZE(itd1000_vcorg); i++) { + if (freq_khz < itd1000_vcorg[i].fmax_rg) { + itd1000_write_reg(state, VCO_CHP1_I2C, vco_chp1_i2c | (itd1000_vcorg[i].vcorg << 4)); + msleep(1); + + adcout = itd1000_read_reg(state, PLLLOCK) & 0x0f; + + deb("VCO: %dkHz: %d -> ADCOUT: %d %02x", freq_khz, itd1000_vcorg[i].vcorg, adcout, vco_chp1_i2c); + + if (adcout > 13) { + if (!(itd1000_vcorg[i].vcorg == 7 || itd1000_vcorg[i].vcorg == 15)) + itd1000_write_reg(state, VCO_CHP1_I2C, vco_chp1_i2c | ((itd1000_vcorg[i].vcorg + 1) << 4)); + } else if (adcout < 2) { + if (!(itd1000_vcorg[i].vcorg == 1 || itd1000_vcorg[i].vcorg == 9)) + itd1000_write_reg(state, VCO_CHP1_I2C, vco_chp1_i2c | ((itd1000_vcorg[i].vcorg - 1) << 4)); + } + break; + } + } +} + +struct { + u32 freq; + u8 values[10]; /* RFTR, RFST1 - RFST9 */ +} itd1000_fre_values[] = { + { 1075000, { 0x59, 0x1d, 0x1c, 0x17, 0x16, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, + { 1250000, { 0x89, 0x1e, 0x1d, 0x17, 0x15, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, + { 1450000, { 0x89, 0x1e, 0x1d, 0x17, 0x15, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, + { 1650000, { 0x69, 0x1e, 0x1d, 0x17, 0x15, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, + { 1750000, { 0x69, 0x1e, 0x17, 0x15, 0x14, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, + { 1850000, { 0x69, 0x1d, 0x17, 0x16, 0x14, 0x0f, 0x0e, 0x0d, 0x0b, 0x0a } }, + { 1900000, { 0x69, 0x1d, 0x17, 0x15, 0x14, 0x0f, 0x0e, 0x0d, 0x0b, 0x0a } }, + { 1950000, { 0x69, 0x1d, 0x17, 0x16, 0x14, 0x13, 0x0e, 0x0d, 0x0b, 0x0a } }, + { 2050000, { 0x69, 0x1e, 0x1d, 0x17, 0x16, 0x14, 0x13, 0x0e, 0x0b, 0x0a } }, + { 2150000, { 0x69, 0x1d, 0x1c, 0x17, 0x15, 0x14, 0x13, 0x0f, 0x0e, 0x0b } } +}; + + +#define FREF 16 + +static void itd1000_set_lo(struct itd1000_state *state, u32 freq_khz) +{ + int i, j; + u32 plln, pllf; + u64 tmp; + + plln = (freq_khz * 1000) / 2 / FREF; + + /* Compute the factional part times 1000 */ + tmp = plln % 1000000; + plln /= 1000000; + + tmp *= 1048576; + do_div(tmp, 1000000); + pllf = (u32) tmp; + + state->frequency = ((plln * 1000) + (pllf * 1000)/1048576) * 2*FREF; + deb("frequency: %dkHz (wanted) %dkHz (set), PLLF = %d, PLLN = %d", freq_khz, state->frequency, pllf, plln); + + itd1000_write_reg(state, PLLNH, 0x80); /* PLLNH */; + itd1000_write_reg(state, PLLNL, plln & 0xff); + itd1000_write_reg(state, PLLFH, (itd1000_read_reg(state, PLLFH) & 0xf0) | ((pllf >> 16) & 0x0f)); + itd1000_write_reg(state, PLLFM, (pllf >> 8) & 0xff); + itd1000_write_reg(state, PLLFL, (pllf >> 0) & 0xff); + + for (i = 0; i < ARRAY_SIZE(itd1000_fre_values); i++) { + if (freq_khz <= itd1000_fre_values[i].freq) { + deb("fre_values: %d", i); + itd1000_write_reg(state, RFTR, itd1000_fre_values[i].values[0]); + for (j = 0; j < 9; j++) + itd1000_write_reg(state, RFST1+j, itd1000_fre_values[i].values[j+1]); + break; + } + } + + itd1000_set_vco(state, freq_khz); +} + +static int itd1000_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) +{ + struct itd1000_state *state = fe->tuner_priv; + u8 pllcon1; + + itd1000_set_lo(state, p->frequency); + itd1000_set_lpf_bw(state, p->u.qpsk.symbol_rate); + + pllcon1 = itd1000_read_reg(state, PLLCON1) & 0x7f; + itd1000_write_reg(state, PLLCON1, pllcon1 | (1 << 7)); + itd1000_write_reg(state, PLLCON1, pllcon1); + + return 0; +} + +static int itd1000_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct itd1000_state *state = fe->tuner_priv; + *frequency = state->frequency; + return 0; +} + +static int itd1000_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + return 0; +} + +static u8 itd1000_init_tab[][2] = { + { PLLCON1, 0x65 }, /* Register does not change */ + { PLLNH, 0x80 }, /* Bits [7:6] do not change */ + { RESERVED_0X6D, 0x3b }, + { VCO_CHP2_I2C, 0x12 }, + { 0x72, 0xf9 }, /* No such regsister defined */ + { RESERVED_0X73, 0xff }, + { RESERVED_0X74, 0xb2 }, + { RESERVED_0X75, 0xc7 }, + { EXTGVBBRF, 0xf0 }, + { DIVAGCCK, 0x80 }, + { BBTR, 0xa0 }, + { RESERVED_0X7E, 0x4f }, + { 0x82, 0x88 }, /* No such regsister defined */ + { 0x83, 0x80 }, /* No such regsister defined */ + { 0x84, 0x80 }, /* No such regsister defined */ + { RESERVED_0X85, 0x74 }, + { RESERVED_0X86, 0xff }, + { RESERVED_0X88, 0x02 }, + { RESERVED_0X89, 0x16 }, + { RFST0, 0x1f }, + { RESERVED_0X94, 0x66 }, + { RESERVED_0X95, 0x66 }, + { RESERVED_0X96, 0x77 }, + { RESERVED_0X97, 0x99 }, + { RESERVED_0X98, 0xff }, + { RESERVED_0X99, 0xfc }, + { RESERVED_0X9A, 0xba }, + { RESERVED_0X9B, 0xaa }, +}; + +static u8 itd1000_reinit_tab[][2] = { + { VCO_CHP1_I2C, 0x8a }, + { BW, 0x87 }, + { GVBB_I2C, 0x03 }, + { BBGVMIN, 0x03 }, + { CON1, 0x2e }, +}; + + +static int itd1000_init(struct dvb_frontend *fe) +{ + struct itd1000_state *state = fe->tuner_priv; + int i; + + for (i = 0; i < ARRAY_SIZE(itd1000_init_tab); i++) + itd1000_write_reg(state, itd1000_init_tab[i][0], itd1000_init_tab[i][1]); + + for (i = 0; i < ARRAY_SIZE(itd1000_reinit_tab); i++) + itd1000_write_reg(state, itd1000_reinit_tab[i][0], itd1000_reinit_tab[i][1]); + + return 0; +} + +static int itd1000_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +static int itd1000_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static const struct dvb_tuner_ops itd1000_tuner_ops = { + .info = { + .name = "Integrant ITD1000", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_step = 125, /* kHz for QPSK frontends */ + }, + + .release = itd1000_release, + + .init = itd1000_init, + .sleep = itd1000_sleep, + + .set_params = itd1000_set_parameters, + .get_frequency = itd1000_get_frequency, + .get_bandwidth = itd1000_get_bandwidth +}; + + +struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg) +{ + struct itd1000_state *state = NULL; + u8 i = 0; + + state = kzalloc(sizeof(struct itd1000_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + state->cfg = cfg; + state->i2c = i2c; + + i = itd1000_read_reg(state, 0); + if (i != 0) { + kfree(state); + return NULL; + } + info("successfully identified (ID: %d)", i); + + memset(state->shadow, 0xff, sizeof(state->shadow)); + for (i = 0x65; i < 0x9c; i++) + state->shadow[i] = itd1000_read_reg(state, i); + + memcpy(&fe->ops.tuner_ops, &itd1000_tuner_ops, sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = state; + + return fe; +} +EXPORT_SYMBOL(itd1000_attach); + +MODULE_AUTHOR("Patrick Boettcher <pb@linuxtv.org>"); +MODULE_DESCRIPTION("Integrant ITD1000 driver"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/itd1000.h b/linux/drivers/media/dvb/frontends/itd1000.h new file mode 100644 index 000000000..5e18df071 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/itd1000.h @@ -0,0 +1,42 @@ +/* + * Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite" + * + * Copyright (c) 2007 Patrick Boettcher <pb@linuxtv.org> + * + * 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 ITD1000_H +#define ITD1000_H + +struct dvb_frontend; +struct i2c_adapter; + +struct itd1000_config { + u8 i2c_address; +}; + +#if defined(CONFIG_DVB_TUNER_ITD1000) || (defined(CONFIG_DVB_TUNER_ITD1000_MODULE) && defined(MODULE)) +extern struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg); +#else +static inline struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/linux/drivers/media/dvb/frontends/itd1000_priv.h b/linux/drivers/media/dvb/frontends/itd1000_priv.h new file mode 100644 index 000000000..8cdc54e57 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/itd1000_priv.h @@ -0,0 +1,88 @@ +/* + * Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite" + * + * Copyright (c) 2007 Patrick Boettcher <pb@linuxtv.org> + * + * 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 ITD1000_PRIV_H +#define ITD1000_PRIV_H + +struct itd1000_state { + struct itd1000_config *cfg; + struct i2c_adapter *i2c; + + u32 frequency; /* contains the value resulting from the LO-setting */ + + /* ugly workaround for flexcop's incapable i2c-controller + * FIXME, if possible + */ + u8 shadow[255]; +}; + +enum itd1000_register { + VCO_CHP1 = 0x65, + VCO_CHP2, + PLLCON1, + PLLNH, + PLLNL, + PLLFH, + PLLFM, + PLLFL, + RESERVED_0X6D, + PLLLOCK, + VCO_CHP2_I2C, + VCO_CHP1_I2C, + BW, + RESERVED_0X73 = 0x73, + RESERVED_0X74, + RESERVED_0X75, + GVBB, + GVRF, + GVBB_I2C, + EXTGVBBRF, + DIVAGCCK, + BBTR, + RFTR, + BBGVMIN, + RESERVED_0X7E, + RESERVED_0X85 = 0x85, + RESERVED_0X86, + CON1, + RESERVED_0X88, + RESERVED_0X89, + RFST0, + RFST1, + RFST2, + RFST3, + RFST4, + RFST5, + RFST6, + RFST7, + RFST8, + RFST9, + RESERVED_0X94, + RESERVED_0X95, + RESERVED_0X96, + RESERVED_0X97, + RESERVED_0X98, + RESERVED_0X99, + RESERVED_0X9A, + RESERVED_0X9B, +}; + +#endif diff --git a/linux/drivers/media/dvb/frontends/mt312.c b/linux/drivers/media/dvb/frontends/mt312.c index daca855c8..081ca3398 100644 --- a/linux/drivers/media/dvb/frontends/mt312.c +++ b/linux/drivers/media/dvb/frontends/mt312.c @@ -1,7 +1,8 @@ /* - Driver for Zarlink VP310/MT312 Satellite Channel Decoder + Driver for Zarlink VP310/MT312/ZL10313 Satellite Channel Decoder Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org> + Copyright (C) 2008 Matthias Schwarzott <zzam@gentoo.org> 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 @@ -43,7 +44,8 @@ struct mt312_state { struct dvb_frontend frontend; u8 id; - u8 frequency; + unsigned long xtal; + u8 freq_mult; }; static int debug; @@ -53,12 +55,11 @@ static int debug; printk(KERN_DEBUG "mt312: " args); \ } while (0) -#define MT312_SYS_CLK 90000000UL /* 90 MHz */ -#define MT312_LPOWER_SYS_CLK 60000000UL /* 60 MHz */ #define MT312_PLL_CLK 10000000UL /* 10 MHz */ +#define MT312_PLL_CLK_10_111 10111000UL /* 10.111 MHz */ static int mt312_read(struct mt312_state *state, const enum mt312_reg_addr reg, - void *buf, const size_t count) + u8 *buf, const size_t count) { int ret; struct i2c_msg msg[2]; @@ -84,7 +85,7 @@ static int mt312_read(struct mt312_state *state, const enum mt312_reg_addr reg, int i; dprintk("R(%d):", reg & 0x7f); for (i = 0; i < count; i++) - printk(" %02x", ((const u8 *) buf)[i]); + printk(" %02x", buf[i]); printk("\n"); } @@ -92,7 +93,7 @@ static int mt312_read(struct mt312_state *state, const enum mt312_reg_addr reg, } static int mt312_write(struct mt312_state *state, const enum mt312_reg_addr reg, - const void *src, const size_t count) + const u8 *src, const size_t count) { int ret; u8 buf[count + 1]; @@ -102,7 +103,7 @@ static int mt312_write(struct mt312_state *state, const enum mt312_reg_addr reg, int i; dprintk("W(%d):", reg & 0x7f); for (i = 0; i < count; i++) - printk(" %02x", ((const u8 *) src)[i]); + printk(" %02x", src[i]); printk("\n"); } @@ -209,7 +210,7 @@ static int mt312_get_symbol_rate(struct mt312_state *state, u32 *sr) dprintk("sym_rat_op=%d dec_ratio=%d\n", sym_rat_op, dec_ratio); dprintk("*sr(manual) = %lu\n", - (((MT312_PLL_CLK * 8192) / (sym_rat_op + 8192)) * + (((state->xtal * 8192) / (sym_rat_op + 8192)) * 2) - dec_ratio); } @@ -242,7 +243,7 @@ static int mt312_initfe(struct dvb_frontend *fe) /* wake up */ ret = mt312_writereg(state, CONFIG, - (state->frequency == 60 ? 0x88 : 0x8c)); + (state->freq_mult == 6 ? 0x88 : 0x8c)); if (ret < 0) return ret; @@ -265,12 +266,37 @@ static int mt312_initfe(struct dvb_frontend *fe) return ret; } + switch (state->id) { + case ID_ZL10313: + /* enable ADC */ + ret = mt312_writereg(state, GPP_CTRL, 0x80); + if (ret < 0) + return ret; + + /* configure ZL10313 for optimal ADC performance */ + buf[0] = 0x80; + buf[1] = 0xB0; + ret = mt312_write(state, HW_CTRL, buf, 2); + if (ret < 0) + return ret; + + /* enable MPEG output and ADCs */ + ret = mt312_writereg(state, HW_CTRL, 0x00); + if (ret < 0) + return ret; + + ret = mt312_writereg(state, MPEG_CTRL, 0x00); + if (ret < 0) + return ret; + + break; + } + /* SYS_CLK */ - buf[0] = mt312_div((state->frequency == 60 ? MT312_LPOWER_SYS_CLK : - MT312_SYS_CLK) * 2, 1000000); + buf[0] = mt312_div(state->xtal * state->freq_mult * 2, 1000000); /* DISEQC_RATIO */ - buf[1] = mt312_div(MT312_PLL_CLK, 15000 * 4); + buf[1] = mt312_div(state->xtal, 22000 * 4); ret = mt312_write(state, SYS_CLK, buf, sizeof(buf)); if (ret < 0) @@ -280,7 +306,17 @@ static int mt312_initfe(struct dvb_frontend *fe) if (ret < 0) return ret; - ret = mt312_writereg(state, OP_CTRL, 0x53); + /* different MOCLK polarity */ + switch (state->id) { + case ID_ZL10313: + buf[0] = 0x33; + break; + default: + buf[0] = 0x53; + break; + } + + ret = mt312_writereg(state, OP_CTRL, buf[0]); if (ret < 0) return ret; @@ -323,6 +359,9 @@ static int mt312_send_master_cmd(struct dvb_frontend *fe, if (ret < 0) return ret; + /* is there a better way to wait for message to be transmitted */ + msleep(100); + /* set DISEQC_MODE[2:0] to zero if a return message is expected */ if (c->msg[0] & 0x02) { ret = mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40)); @@ -383,11 +422,16 @@ static int mt312_set_voltage(struct dvb_frontend *fe, const fe_sec_voltage_t v) { struct mt312_state *state = fe->demodulator_priv; const u8 volt_tab[3] = { 0x00, 0x40, 0x00 }; + u8 val; if (v > SEC_VOLTAGE_OFF) return -EINVAL; - return mt312_writereg(state, DISEQC_MODE, volt_tab[v]); + val = volt_tab[v]; + if (state->config->voltage_inverted) + val ^= 0x40; + + return mt312_writereg(state, DISEQC_MODE, val); } static int mt312_read_status(struct dvb_frontend *fe, fe_status_t *s) @@ -463,7 +507,7 @@ static int mt312_read_snr(struct dvb_frontend *fe, u16 *snr) int ret; u8 buf[2]; - ret = mt312_read(state, M_SNR_H, &buf, sizeof(buf)); + ret = mt312_read(state, M_SNR_H, buf, sizeof(buf)); if (ret < 0) return ret; @@ -478,7 +522,7 @@ static int mt312_read_ucblocks(struct dvb_frontend *fe, u32 *ubc) int ret; u8 buf[2]; - ret = mt312_read(state, RS_UBC_H, &buf, sizeof(buf)); + ret = mt312_read(state, RS_UBC_H, buf, sizeof(buf)); if (ret < 0) return ret; @@ -532,17 +576,17 @@ static int mt312_set_frontend(struct dvb_frontend *fe, return ret; if (p->u.qpsk.symbol_rate >= 30000000) { /* Note that 30MS/s should use 90MHz */ - if ((config_val & 0x0c) == 0x08) { + if (state->freq_mult == 6) { /* We are running 60MHz */ - state->frequency = 90; + state->freq_mult = 9; ret = mt312_initfe(fe); if (ret < 0) return ret; } } else { - if ((config_val & 0x0c) == 0x0C) { + if (state->freq_mult == 9) { /* We are running 90MHz */ - state->frequency = 60; + state->freq_mult = 6; ret = mt312_initfe(fe); if (ret < 0) return ret; @@ -551,6 +595,7 @@ static int mt312_set_frontend(struct dvb_frontend *fe, break; case ID_MT312: + case ID_ZL10313: break; default: @@ -616,11 +661,29 @@ 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); + u8 val = 0x00; + int ret; + + switch (state->id) { + case ID_ZL10313: + ret = mt312_readreg(state, GPP_CTRL, &val); + if (ret < 0) + goto error; + + /* preserve this bit to not accidently shutdown ADC */ + val &= 0x80; + break; } + + if (enable) + val |= 0x40; + else + val &= ~0x40; + + ret = mt312_writereg(state, GPP_CTRL, val); + +error: + return ret; } static int mt312_sleep(struct dvb_frontend *fe) @@ -634,6 +697,18 @@ static int mt312_sleep(struct dvb_frontend *fe) if (ret < 0) return ret; + if (state->id == ID_ZL10313) { + /* reset ADC */ + ret = mt312_writereg(state, GPP_CTRL, 0x00); + if (ret < 0) + return ret; + + /* full shutdown of ADCs, mpeg bus tristated */ + ret = mt312_writereg(state, HW_CTRL, 0x0d); + if (ret < 0) + return ret; + } + ret = mt312_readreg(state, CONFIG, &config); if (ret < 0) return ret; @@ -661,6 +736,7 @@ static void mt312_release(struct dvb_frontend *fe) kfree(state); } +#define MT312_SYS_CLK 90000000UL /* 90 MHz */ static struct dvb_frontend_ops vp310_mt312_ops = { .info = { @@ -668,8 +744,8 @@ static struct dvb_frontend_ops vp310_mt312_ops = { .type = FE_QPSK, .frequency_min = 950000, .frequency_max = 2150000, - .frequency_stepsize = (MT312_PLL_CLK / 1000) / 128, - .symbol_rate_min = MT312_SYS_CLK / 128, + .frequency_stepsize = (MT312_PLL_CLK / 1000) / 128, /* FIXME: adjust freq to real used xtal */ + .symbol_rate_min = MT312_SYS_CLK / 128, /* FIXME as above */ .symbol_rate_max = MT312_SYS_CLK / 2, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | @@ -726,14 +802,21 @@ struct dvb_frontend *vp310_mt312_attach(const struct mt312_config *config, switch (state->id) { case ID_VP310: strcpy(state->frontend.ops.info.name, "Zarlink VP310 DVB-S"); - state->frequency = 90; + state->xtal = MT312_PLL_CLK; + state->freq_mult = 9; break; case ID_MT312: strcpy(state->frontend.ops.info.name, "Zarlink MT312 DVB-S"); - state->frequency = 60; + state->xtal = MT312_PLL_CLK; + state->freq_mult = 6; + break; + case ID_ZL10313: + strcpy(state->frontend.ops.info.name, "Zarlink ZL10313 DVB-S"); + state->xtal = MT312_PLL_CLK_10_111; + state->freq_mult = 9; break; default: - printk(KERN_WARNING "Only Zarlink VP310/MT312" + printk(KERN_WARNING "Only Zarlink VP310/MT312/ZL10313" " are supported chips.\n"); goto error; } @@ -749,7 +832,7 @@ EXPORT_SYMBOL(vp310_mt312_attach); module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("Zarlink VP310/MT312 DVB-S Demodulator driver"); +MODULE_DESCRIPTION("Zarlink VP310/MT312/ZL10313 DVB-S Demodulator driver"); MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>"); MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/mt312.h b/linux/drivers/media/dvb/frontends/mt312.h index afe24fd82..96338f0c4 100644 --- a/linux/drivers/media/dvb/frontends/mt312.h +++ b/linux/drivers/media/dvb/frontends/mt312.h @@ -31,6 +31,9 @@ struct mt312_config { /* the demodulator's i2c address */ u8 demod_address; + + /* inverted voltage setting */ + int voltage_inverted:1; }; #if defined(CONFIG_DVB_MT312) || (defined(CONFIG_DVB_MT312_MODULE) && defined(MODULE)) diff --git a/linux/drivers/media/dvb/frontends/mt312_priv.h b/linux/drivers/media/dvb/frontends/mt312_priv.h index 5e0b95b53..a3959f94d 100644 --- a/linux/drivers/media/dvb/frontends/mt312_priv.h +++ b/linux/drivers/media/dvb/frontends/mt312_priv.h @@ -110,6 +110,8 @@ enum mt312_reg_addr { VIT_ERRPER_H = 83, VIT_ERRPER_M = 84, VIT_ERRPER_L = 85, + HW_CTRL = 84, /* ZL10313 only */ + MPEG_CTRL = 85, /* ZL10313 only */ VIT_SETUP = 86, VIT_REF0 = 87, VIT_REF1 = 88, @@ -156,7 +158,8 @@ enum mt312_reg_addr { enum mt312_model_id { ID_VP310 = 1, - ID_MT312 = 3 + ID_MT312 = 3, + ID_ZL10313 = 5, }; #endif /* DVB_FRONTENDS_MT312_PRIV */ diff --git a/linux/drivers/media/dvb/frontends/s5h1420.c b/linux/drivers/media/dvb/frontends/s5h1420.c index 1e2d602d3..4f4f8f918 100644 --- a/linux/drivers/media/dvb/frontends/s5h1420.c +++ b/linux/drivers/media/dvb/frontends/s5h1420.c @@ -1,24 +1,26 @@ /* -Driver for Samsung S5H1420 QPSK Demodulator - -Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net> - -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. - -*/ + * Driver for + * Samsung S5H1420 and + * PnpNetwork PN1010 QPSK Demodulator + * + * Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net> + * Copyright (C) 2005-8 Patrick Boettcher <pb@linuxtv.org> + * + * 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/kernel.h> #include <linux/module.h> @@ -29,23 +31,35 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include <linux/jiffies.h> #include <asm/div64.h> -#include "dvb_frontend.h" -#include "s5h1420.h" +#include <linux/i2c.h> +#include "dvb_frontend.h" +#include "s5h1420.h" +#include "s5h1420_priv.h" #define TONE_FREQ 22000 struct s5h1420_state { struct i2c_adapter* i2c; const struct s5h1420_config* config; + struct dvb_frontend frontend; + struct i2c_adapter tuner_i2c_adapter; + + u8 CON_1_val; u8 postlocked:1; u32 fclk; u32 tunedfreq; fe_code_rate_t fec_inner; u32 symbol_rate; + + /* FIXME: ugly workaround for flexcop's incapable i2c-controller + * it does not support repeated-start, workaround: write addr-1 + * and then read + */ + u8 shadow[255]; }; static u32 s5h1420_getsymbolrate(struct s5h1420_state* state); @@ -54,43 +68,65 @@ static int s5h1420_get_tune_settings(struct dvb_frontend* fe, static int debug; -#define dprintk if (debug) printk +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debugging"); + +#define dprintk(x...) do { \ + if (debug) \ + printk(KERN_DEBUG "S5H1420: " x); \ +} while (0) + +static u8 s5h1420_readreg(struct s5h1420_state *state, u8 reg) +{ + int ret; + u8 b[2]; + struct i2c_msg msg[] = { + { .addr = state->config->demod_address, .flags = 0, .buf = b, .len = 2 }, + { .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = 1 }, + }; + + b[0] = (reg - 1) & 0xff; + b[1] = state->shadow[(reg - 1) & 0xff]; + + if (state->config->repeated_start_workaround) { + ret = i2c_transfer(state->i2c, msg, 3); + if (ret != 3) + return ret; + } else { + ret = i2c_transfer(state->i2c, &msg[1], 2); + if (ret != 2) + return ret; + } + + /* dprintk("rd(%02x): %02x %02x\n", state->config->demod_address, reg, b[0]); */ + + return b[0]; +} static int s5h1420_writereg (struct s5h1420_state* state, u8 reg, u8 data) { - u8 buf [] = { reg, data }; + u8 buf[] = { reg, data }; struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; int err; - if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { - dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data); + /* dprintk("wr(%02x): %02x %02x\n", state->config->demod_address, reg, data); */ + err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + dprintk("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data); return -EREMOTEIO; } + state->shadow[reg] = data; return 0; } -static u8 s5h1420_readreg (struct s5h1420_state* state, u8 reg) -{ - int ret; - u8 b0 [] = { reg }; - u8 b1 [] = { 0 }; - struct i2c_msg msg1 = { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }; - struct i2c_msg msg2 = { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 }; - - if ((ret = i2c_transfer (state->i2c, &msg1, 1)) != 1) - return ret; - - if ((ret = i2c_transfer (state->i2c, &msg2, 1)) != 1) - return ret; - - return b1[0]; -} - static int s5h1420_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) { struct s5h1420_state* state = fe->demodulator_priv; + dprintk("enter %s\n", __func__); + switch(voltage) { case SEC_VOLTAGE_13: s5h1420_writereg(state, 0x3c, @@ -106,6 +142,7 @@ static int s5h1420_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltag break; } + dprintk("leave %s\n", __func__); return 0; } @@ -113,6 +150,7 @@ static int s5h1420_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) { struct s5h1420_state* state = fe->demodulator_priv; + dprintk("enter %s\n", __func__); switch(tone) { case SEC_TONE_ON: s5h1420_writereg(state, 0x3b, @@ -124,6 +162,7 @@ static int s5h1420_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) (s5h1420_readreg(state, 0x3b) & 0x74) | 0x01); break; } + dprintk("leave %s\n", __func__); return 0; } @@ -137,6 +176,7 @@ static int s5h1420_send_master_cmd (struct dvb_frontend* fe, unsigned long timeout; int result = 0; + dprintk("enter %s\n", __func__); if (cmd->msg_len > 8) return -EINVAL; @@ -168,6 +208,7 @@ static int s5h1420_send_master_cmd (struct dvb_frontend* fe, /* restore original settings */ s5h1420_writereg(state, 0x3b, val); msleep(15); + dprintk("leave %s\n", __func__); return result; } @@ -289,6 +330,8 @@ static int s5h1420_read_status(struct dvb_frontend* fe, fe_status_t* status) struct s5h1420_state* state = fe->demodulator_priv; u8 val; + dprintk("enter %s\n", __func__); + if (status == NULL) return -EINVAL; @@ -297,13 +340,13 @@ static int s5h1420_read_status(struct dvb_frontend* fe, fe_status_t* status) /* fix for FEC 5/6 inversion issue - if it doesn't quite lock, invert the inversion, wait a bit and check again */ - if (*status == (FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI)) { - val = s5h1420_readreg(state, 0x32); + if (*status == (FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI)) { + val = s5h1420_readreg(state, Vit10); if ((val & 0x07) == 0x03) { if (val & 0x08) - s5h1420_writereg(state, 0x31, 0x13); + s5h1420_writereg(state, Vit09, 0x13); else - s5h1420_writereg(state, 0x31, 0x1b); + s5h1420_writereg(state, Vit09, 0x1b); /* wait a bit then update lock status */ mdelay(200); @@ -312,68 +355,77 @@ static int s5h1420_read_status(struct dvb_frontend* fe, fe_status_t* status) } /* perform post lock setup */ - if ((*status & FE_HAS_LOCK) && (!state->postlocked)) { + if ((*status & FE_HAS_LOCK) && !state->postlocked) { /* calculate the data rate */ u32 tmp = s5h1420_getsymbolrate(state); - switch(s5h1420_readreg(state, 0x32) & 0x07) { - case 0: - tmp = (tmp * 2 * 1) / 2; - break; - - case 1: - tmp = (tmp * 2 * 2) / 3; - break; - - case 2: - tmp = (tmp * 2 * 3) / 4; - break; - - case 3: - tmp = (tmp * 2 * 5) / 6; - break; - - case 4: - tmp = (tmp * 2 * 6) / 7; - break; - - case 5: - tmp = (tmp * 2 * 7) / 8; - break; + switch (s5h1420_readreg(state, Vit10) & 0x07) { + case 0: tmp = (tmp * 2 * 1) / 2; break; + case 1: tmp = (tmp * 2 * 2) / 3; break; + case 2: tmp = (tmp * 2 * 3) / 4; break; + case 3: tmp = (tmp * 2 * 5) / 6; break; + case 4: tmp = (tmp * 2 * 6) / 7; break; + case 5: tmp = (tmp * 2 * 7) / 8; break; } + if (tmp == 0) { - printk("s5h1420: avoided division by 0\n"); + printk(KERN_ERR "s5h1420: avoided division by 0\n"); tmp = 1; } tmp = state->fclk / tmp; + /* set the MPEG_CLK_INTL for the calculated data rate */ - if (tmp < 4) + if (tmp < 2) val = 0x00; - else if (tmp < 8) + else if (tmp < 5) val = 0x01; - else if (tmp < 12) + else if (tmp < 9) val = 0x02; - else if (tmp < 16) + else if (tmp < 13) val = 0x03; - else if (tmp < 24) + else if (tmp < 17) val = 0x04; - else if (tmp < 32) + else if (tmp < 25) val = 0x05; - else + else if (tmp < 33) val = 0x06; - s5h1420_writereg(state, 0x22, val); - - /* DC freeze */ - s5h1420_writereg(state, 0x1f, s5h1420_readreg(state, 0x1f) | 0x01); - - /* kicker disable + remove DC offset */ - s5h1420_writereg(state, 0x05, s5h1420_readreg(state, 0x05) & 0x6f); + else + val = 0x07; + dprintk("for MPEG_CLK_INTL %d %x\n", tmp, val); + + s5h1420_writereg(state, FEC01, 0x18); + s5h1420_writereg(state, FEC01, 0x10); + s5h1420_writereg(state, FEC01, val); + + /* Enable "MPEG_Out" */ + val = s5h1420_readreg(state, Mpeg02); + s5h1420_writereg(state, Mpeg02, val | (1 << 6)); + + /* kicker disable */ + val = s5h1420_readreg(state, QPSK01) & 0x7f; + s5h1420_writereg(state, QPSK01, val); + + /* DC freeze TODO it was never activated by default or it can stay activated */ +#if 0 + val = s5h1420_readreg(state, 0x1f); + s5h1420_writereg(state, 0x1f, val | 0x01); +#endif + + if (s5h1420_getsymbolrate(state) >= 20000000) { + s5h1420_writereg(state, Loop04, 0x8a); + s5h1420_writereg(state, Loop05, 0x6a); + } else { + s5h1420_writereg(state, Loop04, 0x58); + s5h1420_writereg(state, Loop05, 0x27); + } /* post-lock processing has been done! */ state->postlocked = 1; } + dprintk("leave %s\n", __func__); + return 0; } @@ -414,6 +466,7 @@ static int s5h1420_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) static void s5h1420_reset(struct s5h1420_state* state) { + dprintk("%s\n", __func__); s5h1420_writereg (state, 0x01, 0x08); s5h1420_writereg (state, 0x01, 0x00); udelay(10); @@ -422,54 +475,79 @@ static void s5h1420_reset(struct s5h1420_state* state) static void s5h1420_setsymbolrate(struct s5h1420_state* state, struct dvb_frontend_parameters *p) { + u8 v; u64 val; + dprintk("enter %s\n", __func__); + val = ((u64) p->u.qpsk.symbol_rate / 1000ULL) * (1ULL<<24); - if (p->u.qpsk.symbol_rate <= 21000000) { + if (p->u.qpsk.symbol_rate < 29000000) val *= 2; - } do_div(val, (state->fclk / 1000)); - s5h1420_writereg(state, 0x09, s5h1420_readreg(state, 0x09) & 0x7f); - s5h1420_writereg(state, 0x11, val >> 16); - s5h1420_writereg(state, 0x12, val >> 8); - s5h1420_writereg(state, 0x13, val & 0xff); - s5h1420_writereg(state, 0x09, s5h1420_readreg(state, 0x09) | 0x80); + dprintk("symbol rate register: %06llx\n", val); + + v = s5h1420_readreg(state, Loop01); + s5h1420_writereg(state, Loop01, v & 0x7f); + s5h1420_writereg(state, Tnco01, val >> 16); + s5h1420_writereg(state, Tnco02, val >> 8); + s5h1420_writereg(state, Tnco03, val & 0xff); + s5h1420_writereg(state, Loop01, v | 0x80); + dprintk("leave %s\n", __func__); } static u32 s5h1420_getsymbolrate(struct s5h1420_state* state) { +#if 0 + /* TODO getsymbolrate gives strange values - something is wrong with + * configuring the read + */ + u64 val = 0; + u8 v; int sampling = 2; - if (s5h1420_readreg(state, 0x05) & 0x2) - sampling = 1; + v = s5h1420_readreg(state, QPSK02); + s5h1420_writereg(state, QPSK02, v | 0x08); + val = s5h1420_readreg(state, Tnco01) << 16; + val |= s5h1420_readreg(state, Tnco02) << 8; + val |= s5h1420_readreg(state, Tnco03); + s5h1420_writereg(state, QPSK02, v & 0xf7); - s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) | 0x08); - val = s5h1420_readreg(state, 0x11) << 16; - val |= s5h1420_readreg(state, 0x12) << 8; - val |= s5h1420_readreg(state, 0x13); - s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) & 0xf7); + dprintk("get_symbolrate: raw: %x\n", val); - val *= (state->fclk / 1000ULL); - do_div(val, ((1<<24) * sampling)); + val *= (state->fclk / 1000); + if (s5h1420_readreg(state, QPSK01) & 0x2) + do_div(val, 1 << 24); + else + do_div(val, 1 << 25); + dprintk("get_symbolrate: %d\n", (u32) (val * 1000ULL)); return (u32) (val * 1000ULL); +#endif + return state->symbol_rate; } static void s5h1420_setfreqoffset(struct s5h1420_state* state, int freqoffset) { int val; + u8 v; + + dprintk("enter %s\n", __func__); /* remember freqoffset is in kHz, but the chip wants the offset in Hz, so * divide fclk by 1000000 to get the correct value. */ val = -(int) ((freqoffset * (1<<24)) / (state->fclk / 1000000)); - s5h1420_writereg(state, 0x09, s5h1420_readreg(state, 0x09) & 0xbf); - s5h1420_writereg(state, 0x0e, val >> 16); - s5h1420_writereg(state, 0x0f, val >> 8); - s5h1420_writereg(state, 0x10, val & 0xff); - s5h1420_writereg(state, 0x09, s5h1420_readreg(state, 0x09) | 0x40); + dprintk("phase rotator/freqoffset: %d %06x\n", freqoffset, val); + + v = s5h1420_readreg(state, Loop01); + s5h1420_writereg(state, Loop01, v & 0xbf); + s5h1420_writereg(state, Pnco01, val >> 16); + s5h1420_writereg(state, Pnco02, val >> 8); + s5h1420_writereg(state, Pnco03, val & 0xff); + s5h1420_writereg(state, Loop01, v | 0x40); + dprintk("leave %s\n", __func__); } static int s5h1420_getfreqoffset(struct s5h1420_state* state) @@ -496,52 +574,53 @@ static void s5h1420_setfec_inversion(struct s5h1420_state* state, struct dvb_frontend_parameters *p) { u8 inversion = 0; + u8 vit08, vit09; + + dprintk("enter %s\n", __func__); - if (p->inversion == INVERSION_OFF) { + if (p->inversion == INVERSION_OFF) inversion = state->config->invert ? 0x08 : 0; - } else if (p->inversion == INVERSION_ON) { + else if (p->inversion == INVERSION_ON) inversion = state->config->invert ? 0 : 0x08; - } if ((p->u.qpsk.fec_inner == FEC_AUTO) || (p->inversion == INVERSION_AUTO)) { - s5h1420_writereg(state, 0x30, 0x3f); - s5h1420_writereg(state, 0x31, 0x00 | inversion); + vit08 = 0x3f; + vit09 = 0; } else { switch(p->u.qpsk.fec_inner) { case FEC_1_2: - s5h1420_writereg(state, 0x30, 0x01); - s5h1420_writereg(state, 0x31, 0x10 | inversion); + vit08 = 0x01; vit09 = 0x10; break; case FEC_2_3: - s5h1420_writereg(state, 0x30, 0x02); - s5h1420_writereg(state, 0x31, 0x11 | inversion); + vit08 = 0x02; vit09 = 0x11; break; case FEC_3_4: - s5h1420_writereg(state, 0x30, 0x04); - s5h1420_writereg(state, 0x31, 0x12 | inversion); + vit08 = 0x04; vit09 = 0x12; break; case FEC_5_6: - s5h1420_writereg(state, 0x30, 0x08); - s5h1420_writereg(state, 0x31, 0x13 | inversion); + vit08 = 0x08; vit09 = 0x13; break; case FEC_6_7: - s5h1420_writereg(state, 0x30, 0x10); - s5h1420_writereg(state, 0x31, 0x14 | inversion); + vit08 = 0x10; vit09 = 0x14; break; case FEC_7_8: - s5h1420_writereg(state, 0x30, 0x20); - s5h1420_writereg(state, 0x31, 0x15 | inversion); + vit08 = 0x20; vit09 = 0x15; break; default: return; } } + vit09 |= inversion; + dprintk("fec: %02x %02x\n", vit08, vit09); + s5h1420_writereg(state, Vit08, vit08); + s5h1420_writereg(state, Vit09, vit09); + dprintk("leave %s\n", __func__); } static fe_code_rate_t s5h1420_getfec(struct s5h1420_state* state) @@ -583,16 +662,19 @@ 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; + uint8_t clock_settting; + + dprintk("enter %s\n", __func__); /* check if we should do a fast-tune */ memcpy(&fesettings.parameters, p, sizeof(struct dvb_frontend_parameters)); s5h1420_get_tune_settings(fe, &fesettings); frequency_delta = p->frequency - state->tunedfreq; if ((frequency_delta > -fesettings.max_drift) && - (frequency_delta < fesettings.max_drift) && - (frequency_delta != 0) && - (state->fec_inner == p->u.qpsk.fec_inner) && - (state->symbol_rate == p->u.qpsk.symbol_rate)) { + (frequency_delta < fesettings.max_drift) && + (frequency_delta != 0) && + (state->fec_inner == p->u.qpsk.fec_inner) && + (state->symbol_rate == p->u.qpsk.symbol_rate)) { if (fe->ops.tuner_ops.set_params) { fe->ops.tuner_ops.set_params(fe, p); @@ -606,54 +688,93 @@ static int s5h1420_set_frontend(struct dvb_frontend* fe, } else { s5h1420_setfreqoffset(state, 0); } + dprintk("simple tune\n"); return 0; } + dprintk("tuning demod\n"); /* first of all, software reset */ s5h1420_reset(state); /* set s5h1420 fclk PLL according to desired symbol rate */ - if (p->u.qpsk.symbol_rate > 28000000) { - state->fclk = 88000000; - s5h1420_writereg(state, 0x03, 0x50); - s5h1420_writereg(state, 0x04, 0x40); - s5h1420_writereg(state, 0x05, 0xae); - } else if (p->u.qpsk.symbol_rate > 21000000) { + if (p->u.qpsk.symbol_rate > 33000000) + state->fclk = 80000000; + else if (p->u.qpsk.symbol_rate > 28500000) state->fclk = 59000000; - s5h1420_writereg(state, 0x03, 0x33); - s5h1420_writereg(state, 0x04, 0x40); - s5h1420_writereg(state, 0x05, 0xae); - } else { + else if (p->u.qpsk.symbol_rate > 25000000) + state->fclk = 86000000; + else if (p->u.qpsk.symbol_rate > 1900000) state->fclk = 88000000; - s5h1420_writereg(state, 0x03, 0x50); - s5h1420_writereg(state, 0x04, 0x40); - s5h1420_writereg(state, 0x05, 0xac); + else + state->fclk = 44000000; + + /* Clock */ + switch (state->fclk) { + default: + case 88000000: + clock_settting = 80; + break; + case 86000000: + clock_settting = 78; + break; + case 80000000: + clock_settting = 72; + break; + case 59000000: + clock_settting = 51; + break; + case 44000000: + clock_settting = 36; + break; } + dprintk("pll01: %d, ToneFreq: %d\n", state->fclk/1000000 - 8, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32)); + s5h1420_writereg(state, PLL01, state->fclk/1000000 - 8); + s5h1420_writereg(state, PLL02, 0x40); + s5h1420_writereg(state, DiS01, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32)); - /* set misc registers */ - s5h1420_writereg(state, 0x02, 0x00); - s5h1420_writereg(state, 0x06, 0x00); - s5h1420_writereg(state, 0x07, 0xb0); - s5h1420_writereg(state, 0x0a, 0xe7); - s5h1420_writereg(state, 0x0b, 0x78); - s5h1420_writereg(state, 0x0c, 0x48); - s5h1420_writereg(state, 0x0d, 0x6b); - s5h1420_writereg(state, 0x2e, 0x8e); - s5h1420_writereg(state, 0x35, 0x33); - s5h1420_writereg(state, 0x38, 0x01); - s5h1420_writereg(state, 0x39, 0x7d); - s5h1420_writereg(state, 0x3a, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32)); - s5h1420_writereg(state, 0x3c, 0x00); - s5h1420_writereg(state, 0x45, 0x61); - s5h1420_writereg(state, 0x46, 0x1d); + /* TODO DC offset removal, config parameter ? */ + if (p->u.qpsk.symbol_rate > 29000000) + s5h1420_writereg(state, QPSK01, 0xae | 0x10); + else + s5h1420_writereg(state, QPSK01, 0xac | 0x10); - /* start QPSK */ - s5h1420_writereg(state, 0x05, s5h1420_readreg(state, 0x05) | 1); + /* set misc registers */ + s5h1420_writereg(state, CON_1, 0x00); + s5h1420_writereg(state, QPSK02, 0x00); + s5h1420_writereg(state, Pre01, 0xb0); + + s5h1420_writereg(state, Loop01, 0xF0); + s5h1420_writereg(state, Loop02, 0x2a); /* e7 for s5h1420 */ + s5h1420_writereg(state, Loop03, 0x79); /* 78 for s5h1420 */ + if (p->u.qpsk.symbol_rate > 20000000) + s5h1420_writereg(state, Loop04, 0x79); + else + s5h1420_writereg(state, Loop04, 0x58); + s5h1420_writereg(state, Loop05, 0x6b); + + if (p->u.qpsk.symbol_rate >= 8000000) + s5h1420_writereg(state, Post01, (0 << 6) | 0x10); + else if (p->u.qpsk.symbol_rate >= 4000000) + s5h1420_writereg(state, Post01, (1 << 6) | 0x10); + else + s5h1420_writereg(state, Post01, (3 << 6) | 0x10); + + s5h1420_writereg(state, Monitor12, 0x00); /* unfreeze DC compensation */ + + s5h1420_writereg(state, Sync01, 0x33); + s5h1420_writereg(state, Mpeg01, state->config->cdclk_polarity); + s5h1420_writereg(state, Mpeg02, 0x3d); /* Parallel output more, disabled -> enabled later */ + s5h1420_writereg(state, Err01, 0x03); /* 0x1d for s5h1420 */ + + s5h1420_writereg(state, Vit06, 0x6e); /* 0x8e for s5h1420 */ + s5h1420_writereg(state, DiS03, 0x00); + s5h1420_writereg(state, Rf01, 0x61); /* Tuner i2c address - for the gate controller */ /* set tuner PLL */ 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.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); s5h1420_setfreqoffset(state, 0); } @@ -661,10 +782,15 @@ static int s5h1420_set_frontend(struct dvb_frontend* fe, s5h1420_setsymbolrate(state, p); s5h1420_setfec_inversion(state, p); + /* start QPSK */ + s5h1420_writereg(state, QPSK01, s5h1420_readreg(state, QPSK01) | 1); + state->fec_inner = p->u.qpsk.fec_inner; state->symbol_rate = p->u.qpsk.symbol_rate; state->postlocked = 0; state->tunedfreq = p->frequency; + + dprintk("leave %s\n", __func__); return 0; } @@ -717,11 +843,10 @@ 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); - } + if (enable) + return s5h1420_writereg(state, 0x02, state->CON_1_val | 1); + else + return s5h1420_writereg(state, 0x02, state->CON_1_val & 0xfe); } static int s5h1420_init (struct dvb_frontend* fe) @@ -729,7 +854,8 @@ static int s5h1420_init (struct dvb_frontend* fe) struct s5h1420_state* state = fe->demodulator_priv; /* disable power down and do reset */ - s5h1420_writereg(state, 0x02, 0x10); + state->CON_1_val = 0x10; + s5h1420_writereg(state, 0x02, state->CON_1_val); msleep(10); s5h1420_reset(state); @@ -739,26 +865,60 @@ static int s5h1420_init (struct dvb_frontend* fe) static int s5h1420_sleep(struct dvb_frontend* fe) { struct s5h1420_state* state = fe->demodulator_priv; - - return s5h1420_writereg(state, 0x02, 0x12); + state->CON_1_val = 0x12; + return s5h1420_writereg(state, 0x02, state->CON_1_val); } static void s5h1420_release(struct dvb_frontend* fe) { struct s5h1420_state* state = fe->demodulator_priv; + i2c_del_adapter(&state->tuner_i2c_adapter); kfree(state); } -static struct dvb_frontend_ops s5h1420_ops; +static u32 s5h1420_tuner_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static int s5h1420_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +{ + struct s5h1420_state *state = i2c_get_adapdata(i2c_adap); + struct i2c_msg m[1 + num]; + u8 tx_open[2] = { CON_1, state->CON_1_val | 1 }; /* repeater stops once there was a stop condition */ + + memset(m, 0, sizeof(struct i2c_msg) * (1 + num)); + + m[0].addr = state->config->demod_address; + m[0].buf = tx_open; + m[0].len = 2; + + memcpy(&m[1], msg, sizeof(struct i2c_msg) * num); -struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config, - struct i2c_adapter* i2c) + return i2c_transfer(state->i2c, m, 1+num) == 1 + num ? num : -EIO; +} + +static struct i2c_algorithm s5h1420_tuner_i2c_algo = { + .master_xfer = s5h1420_tuner_i2c_tuner_xfer, + .functionality = s5h1420_tuner_i2c_func, +}; + +struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe) { - struct s5h1420_state* state = NULL; - u8 identity; + struct s5h1420_state *state = fe->demodulator_priv; + return &state->tuner_i2c_adapter; +} +EXPORT_SYMBOL(s5h1420_get_tuner_i2c_adapter); + +static struct dvb_frontend_ops s5h1420_ops; +struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, + struct i2c_adapter *i2c) +{ /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct s5h1420_state), GFP_KERNEL); + struct s5h1420_state *state = kzalloc(sizeof(struct s5h1420_state), GFP_KERNEL); + u8 i; + if (state == NULL) goto error; @@ -772,24 +932,42 @@ struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config, state->symbol_rate = 0; /* check if the demod is there + identify it */ - identity = s5h1420_readreg(state, 0x00); - if (identity != 0x03) + i = s5h1420_readreg(state, ID01); + if (i != 0x03) goto error; + memset(state->shadow, 0xff, sizeof(state->shadow)); + + for (i = 0; i < 0x50; i++) + state->shadow[i] = s5h1420_readreg(state, i); + /* create dvb_frontend */ memcpy(&state->frontend.ops, &s5h1420_ops, sizeof(struct dvb_frontend_ops)); state->frontend.demodulator_priv = state; + + /* create tuner i2c adapter */ + strncpy(state->tuner_i2c_adapter.name, "S5H1420-PN1010 tuner I2C bus", I2C_NAME_SIZE); + state->tuner_i2c_adapter.class = I2C_CLASS_TV_DIGITAL, + state->tuner_i2c_adapter.algo = &s5h1420_tuner_i2c_algo; + state->tuner_i2c_adapter.algo_data = NULL; + i2c_set_adapdata(&state->tuner_i2c_adapter, state); + if (i2c_add_adapter(&state->tuner_i2c_adapter) < 0) { + printk(KERN_ERR "S5H1420/PN1010: tuner i2c bus could not be initialized\n"); + goto error; + } + return &state->frontend; error: kfree(state); return NULL; } +EXPORT_SYMBOL(s5h1420_attach); static struct dvb_frontend_ops s5h1420_ops = { .info = { - .name = "Samsung S5H1420 DVB-S", + .name = "Samsung S5H1420/PnpNetwork PN1010 DVB-S", .type = FE_QPSK, .frequency_min = 950000, .frequency_max = 2150000, @@ -826,10 +1004,6 @@ static struct dvb_frontend_ops s5h1420_ops = { .set_voltage = s5h1420_set_voltage, }; -module_param(debug, int, 0644); - -MODULE_DESCRIPTION("Samsung S5H1420 DVB-S Demodulator driver"); -MODULE_AUTHOR("Andrew de Quincey"); +MODULE_DESCRIPTION("Samsung S5H1420/PnpNetwork PN1010 DVB-S Demodulator driver"); +MODULE_AUTHOR("Andrew de Quincey, Patrick Boettcher"); MODULE_LICENSE("GPL"); - -EXPORT_SYMBOL(s5h1420_attach); diff --git a/linux/drivers/media/dvb/frontends/s5h1420.h b/linux/drivers/media/dvb/frontends/s5h1420.h index 2cc785012..4c913f142 100644 --- a/linux/drivers/media/dvb/frontends/s5h1420.h +++ b/linux/drivers/media/dvb/frontends/s5h1420.h @@ -1,25 +1,26 @@ /* - Driver for S5H1420 QPSK Demodulators - - Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net> - - 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. - -*/ - + * Driver for + * Samsung S5H1420 and + * PnpNetwork PN1010 QPSK Demodulator + * + * Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net> + * Copyright (C) 2005-8 Patrick Boettcher <pb@linuxtv.org> + * + * 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 S5H1420_H #define S5H1420_H @@ -31,19 +32,28 @@ struct s5h1420_config u8 demod_address; /* does the inversion require inversion? */ - u8 invert:1; + u8 invert : 1; + + u8 repeated_start_workaround : 1; + u8 cdclk_polarity : 1; /* 1 == falling edge, 0 == raising edge */ }; #if defined(CONFIG_DVB_S5H1420) || (defined(CONFIG_DVB_S5H1420_MODULE) && defined(MODULE)) -extern struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config, - struct i2c_adapter* i2c); +extern struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, + struct i2c_adapter *i2c); +extern struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe); #else -static inline struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config, - struct i2c_adapter* i2c) +static inline struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, + struct i2c_adapter *i2c) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } + +static inline struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe) +{ + return NULL; +} #endif // CONFIG_DVB_S5H1420 #endif // S5H1420_H diff --git a/linux/drivers/media/dvb/frontends/s5h1420_priv.h b/linux/drivers/media/dvb/frontends/s5h1420_priv.h new file mode 100644 index 000000000..7962bbfb6 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/s5h1420_priv.h @@ -0,0 +1,380 @@ +/* + * Driver for + * Samsung S5H1420 and + * PnpNetwork PN1010 QPSK Demodulator + * + * Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net> + * Copyright (C) 2005 Patrick Boettcher <pb@linuxtv.org> + * + * 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 S5H1420_PRIV +#define S5H1420_PRIV + +#include <asm/types.h> + +enum s5h1420_register { + ID01 = 0x00, + CON_0 = 0x01, + CON_1 = 0x02, + PLL01 = 0x03, + PLL02 = 0x04, + QPSK01 = 0x05, + QPSK02 = 0x06, + Pre01 = 0x07, + Post01 = 0x08, + Loop01 = 0x09, + Loop02 = 0x0a, + Loop03 = 0x0b, + Loop04 = 0x0c, + Loop05 = 0x0d, + Pnco01 = 0x0e, + Pnco02 = 0x0f, + Pnco03 = 0x10, + Tnco01 = 0x11, + Tnco02 = 0x12, + Tnco03 = 0x13, + Monitor01 = 0x14, + Monitor02 = 0x15, + Monitor03 = 0x16, + Monitor04 = 0x17, + Monitor05 = 0x18, + Monitor06 = 0x19, + Monitor07 = 0x1a, + Monitor12 = 0x1f, + + FEC01 = 0x22, + Soft01 = 0x23, + Soft02 = 0x24, + Soft03 = 0x25, + Soft04 = 0x26, + Soft05 = 0x27, + Soft06 = 0x28, + Vit01 = 0x29, + Vit02 = 0x2a, + Vit03 = 0x2b, + Vit04 = 0x2c, + Vit05 = 0x2d, + Vit06 = 0x2e, + Vit07 = 0x2f, + Vit08 = 0x30, + Vit09 = 0x31, + Vit10 = 0x32, + Vit11 = 0x33, + Vit12 = 0x34, + Sync01 = 0x35, + Sync02 = 0x36, + Rs01 = 0x37, + Mpeg01 = 0x38, + Mpeg02 = 0x39, + DiS01 = 0x3a, + DiS02 = 0x3b, + DiS03 = 0x3c, + DiS04 = 0x3d, + DiS05 = 0x3e, + DiS06 = 0x3f, + DiS07 = 0x40, + DiS08 = 0x41, + DiS09 = 0x42, + DiS10 = 0x43, + DiS11 = 0x44, + Rf01 = 0x45, + Err01 = 0x46, + Err02 = 0x47, + Err03 = 0x48, + Err04 = 0x49, +}; + +#if 0 + +regsiter mapping follows + +/* ID01 */ +#define PN1010_ID 7, 0 + +/* CON_0 */ +#define QPSK_ONLY 4, 4 +#define SOFT_RST 3, 3 /* System soft reset mode (active high) [1] Enable [0] Disable */ +#define CLK_MODE 2, 2 +#define BPSK_QPSK 1, 1 +#define DSS_DVB 0, 0 /* DSS/DVB mode selection [1] DSS [0] DVB */ + +/* CON_1 */ +#define ADC_SPD 6, 6 +#define PLL_OEN 5, 5 +#define SER_SEL 4, 4 +#define XTAL_OEN 3, 3 +#define Q_TEST 2, 2 +#define PWR_DN 1, 1 /* Power down mode [1] Power down enable, [0] Power down disable */ +#define I2C_RPT 0, 0 /* I2C repeater control [1] I2C repeater enable, [0] I2C repeater disable. + * Note: The master should be set this bit to "1" in order to interface with the tuner. + * When the master is not communicated with the tuner, this bit should be set to "0" */ + +/* PLL programming information + * Fout = ((M+8)*Fin)/((P+2)*2^S), Fin = 4 MHz */ +/* PLL01 */ +#define M 7, 0 + +/* PLL02 */ +#define P 5, 0 +#define S 7, 6 + +/* QPSK01 */ +#define KICK_EN 7, 7 /* [1] PLL Kicker enable [0] Disable */ +#define ROLL_OFF 6, 6 +#define SCALE_EN 5, 5 +#define DC_EN 4, 4 /* DC offset remove [1] Enable [0] Disable */ +#define VMODE 3, 3 +#define TGL_MSB 2, 2 +#define MODE_QPSK 1, 1 /* QPSK operation mode [1] 1 sampling/1 symbol [0] 2 sampling/1 symbol */ +#define Q_SRESET_N 0, 0 /* QPSK start signal [1] Start [0] Idle */ + +/* QPSK02 */ +#define DUMP_ACC 3, 3 /* Dump phase loop filter & timing loop filter accumulator [0 and then 1] + * The read operation enabled, when user set DUMP_ACC "0" and then "1". */ +#define DC_WIN 2, 0 /* Window position from MSB (0 <= DC_WIN <= 7) */ + +/* AGC Controls */ +/* Pre01 */ +#define PRE_TH 4, 0 /* PRE-AGC threshold */ +#define INV_PULSE 7, 7 /* PWM signal is reversed [1] PWM signal active low [0] PWM signal active high */ + +/* Post01 */ +#define RRC_SCALE 7, 6 +#define POST_TH 5, 0 /* POST-AGC threshold */ + +/* Loop Controls */ +/* Loop01 */ +#define WT_TNCO 7, 7 /* Write TNCO center frequency [0 and then 1] The write operation enabled, when user set WT_TNCO "0" and then "1" */ +#define WT_PNCO 6, 6 /* Write PNCO center frequency [0 and then 1] The write operation enabled, when user set WT_PNCO "0" and then "1" */ + +/* Loop02 */ +#define LOOP_OUT 7, 7 /* Loop filter monitoring selection [1] Loop filter accumulator + NCO [0] Loop filter accumulator */ +#define KICK_VAL 6, 4 /* The value that gets injected into the accumulator when a "kick" is needed. */ +#define KICK_MUL 3, 0 /* The number of bits KICK_VAL is up-shifted (2^N) before it is injected into the accumulator. */ + +/* Loop03 */ +#define IGA_PLF 7, 4 /* Phase loop, integral gain (2^IGA_PLF) in the acquisition mode */ +#define PGA_PLF 3, 0 /* Phase loop, proportional gain (2^PGA_PLF) in the acquisition mode (default +8 added) */ + +/* Loop04 */ +#define IGT_PLF 7, 4 /* Integral gain in the tracking mode */ +#define PGT_PLF 3, 0 /* Phase loop, proportional gain in the tracking mode (default +8 added) */ + +/* Loop 05 */ +#define IG_TLF 7, 4 /* Timing loop, integral gain */ +#define PG_TLF 3, 0 /* Timing loop, proportional gain (default +8 added) */ + +/* NCO controls + * LOOP_OUT [1] Read PLF accumulator + PNCO, LOOP_OUT [0] Read PLF accumulator */ +/* Pnco01 */ +#define PNCO0 7, 0 +/* Pnco02 */ +#define PNCO1 7, 0 +/* Pnco03 */ +#define PNCO2 7, 0 + +/* LOOP_OUT [1], Read TLF accumulator + TNCO, LOOP_OUT [0], Read TLF accumulator */ +/* Tnco01 */ +#define TNCO0 7, 0 +/* Tnco02 */ +#define TNCO1 7, 0 +/* Tnco03 */ +#define TNCO2 7, 0 + +/* Monitor01 */ +#define A_SCL 3, 3 +#define TLOCK 1, 1 /* Timing loop lock (Symbol sync) [1] Timing loop has locked [0] Timing loop has not locked */ +#define PLOCK 0, 0 /* Phase loop lock (Carrier sync) [1] Phase loop has locked [0] Phase loop has not locked */ + +/* Monitor02 */ +#define PRE_LEVEL 7, 0 /* PRE-AGC gain level */ + +/* Monitor03 */ +#define POST_LEVEL 7, 0 /* POST-AGC gain level */ + +/* Monitor04 */ +#define DC_I_LEVEL 7, 0 /* DC offset of I samples */ + +/* Monitor05 */ +#define DC_Q_LEVEL 7, 0 /* DC offset of Q samples */ + +/* Monitor06 */ +#define PLOCK_CNT 7, 0 + +/* Monitor07 */ +#define TLOCK_CNT 7, 0 + +/* Monitor12 */ +#define QPSK_OUT 6, 1 /* QPSK output monitoring */ +#define DC_FREEZE 0, 0 /* [1] Do not update DC_OFFSET */ + +/* FEC Block */ +/* FEC01 */ +#define DERAND_BPAS 6, 6 +#define RS_BPAS 5, 5 +#define FEC_SRST_CNTL 4, 4 +#define FEC_SRESET 3, 3 +#define MPEG_CLK_INTL 2, 0 + /* Tmp=(FMClk/FSR) x (1 / (2xCR)); FMClk: System Clock Frequency, FSR: Symbol Rate, CR: Code Rate + * 0: 1 < Tmp <= 2, 4: 13 < Tmp <= 17, + * 1: 2 < Tmp <= 5, 5: 17 < Tmp <= 25, + * 2: 5 < Tmp <= 9, 6: 25 < Tmp <= 33, + * 3: 9 < Tmp <= 13, 7: 33 < Tmp */ + +/* Soft01 */ +#define ST_VAL_R12 2, 0 +#define STEP_R12 5, 3 +#define STEP_R23 5, 3 +#define STEP_R34 5, 3 +#define STEP_R56 5, 3 +#define STEP_R67 5, 3 +#define STEP_R78 5, 3 +/* Soft02 */ +#define ST_VAL_R23 7, 0 +/* Soft03 */ +#define ST_VAL_R34 7, 0 +/* Soft04 */ +#define ST_VAL_R56 7, 0 +/* Soft05 */ +#define ST_VAL_R67 7, 0 +/* Soft06 */ +#define ST_VAL_R78 7, 0 + +/* Vit01 */ +#define RENORM_R12 7, 0 +/* Vit02 */ +#define RENORM_R23 7, 0 +/* Vit03 */ +#define RENORM_R34 7, 0 +/* Vit04 */ +#define RENORM_R56 7, 0 +/* Vit05 */ +#define RENORM_R67 7, 0 +/* Vit06 */ +#define RENORM_R78 7, 0 +/* Vit07 */ +#define RENORM_PRD 7, 0 + +/* Vit08 */ +#define VIT_IN_SR78 7, 7 +#define VIT_IN_SR34 6, 6 +#define VIT_SR78 5, 5 +#define VIT_SR67 4, 4 +#define VIT_SR56 3, 3 +#define VIT_SR34 2, 2 +#define VIT_SR23 1, 1 /* .... */ +#define VIT_SR12 0, 0 /* Include CR 1/2 in search */ + +/* Vit09 */ +#define PARM_FIX 4, 4 /* Parameter fix mode: [1] Known parameter [0] Unknown parameter */ +#define VIT_INV_SPECPARM_FIX 3, 3 /* Initial spectrum information */ +#define VIT_FR 2, 0 /* Start synchronization search at code rate as follows */ + #define VIT_CR_12 0x00 + #define VIT_CR_23 0x01 + #define VIT_CR_34 0x02 + #define VIT_CR_56 0x03 + #define VIT_CR_67 0x04 + #define VIT_CR_78 0x05 + +/* Vit10 */ +#define INV_SPEC_STS 3, 3 /* Spectrum information monitoring [1] Inv spectrum [0] Not inv spectrum */ +#define VIT_CR 2, 0 /* Viterbi decoder current code rate [0] R=1/2 [1] R=2/3 [2] R=3/4 [3] R=5/6 [4] R=6/7 [5] R=7/8 */ + +/* Vit11 */ +#define VIT_CER 7, 0 + +/* Vit12 */ +#define RENORM_RAT 7, 0 + +/* Sync01 */ +#define SYNC_MISS_TH 7, 4 /* Sync byte detector?s miss threshold */ +#define SYNC_HIT_TH 3, 0 /* Sync byte detector?s hit threshold This value should be greater than 2 */ + +/* Sync02 */ +#define BYTE_SYNC 5, 5 /* [1] Acquire byte sync [0] Not acquire byte sync */ +#define SYNC_BYTE_STS 4, 2 +#define VIT_SRCH_STS 1, 1 +#define VIT_SYNC 0, 0 /* [1] Viterbi decoder is in sync [0] Viterbi decoder is out of sync */ + +/* Rs01 */ +#define RS_ERR 3, 0 +#define PKT_ERR 4, 4 + +/* Mpeg01 */ +#define ERR_POL 3, 3 /* Packet error polarity [1] Active low [0] Active high */ +#define SYNC_POL 2, 2 /* Sync polarity [1] Active low [0] Active high */ +#define VALID_POL 1, 1 /* Data valid polarity [1] Active low [0] Active high */ +#define CDCLK_POL 0, 0 /* CDCLK polarity [1] Falling edge event [0] Rising edge event */ + +/* Mpeg02 */ +#define MPEG_OEN 6, 6 +#define DOUT_CONT 5, 5 +#define ERR_CONT 4, 4 +#define CLK_CONT 3, 3 /* Clock continuous mode [1] Continuous clock, [0] Clock is enable during payload data transfer */ +#define ENVELOPE 2, 2 +#define SER_PAR 1, 1 /* Serial / Parallel mode [1] Serial mode, [0] Parallel mode */ +#define DSS_SYNC 0, 0 /* DSS sync mode [1] Output sync, [0] No output sync */ + +/* DiS01 */ +#define TONE_FREQ 7, 0 /* Tone frequency ratio ftone = fclk / (TONE_FREQ x 32) */ + +/* DiS02 */ +#define RCV_EN 7, 7 /* DiSEqC receive enable mode [1] Receive enable [0] Receive disable */ +#define DIS_LENGTH 6, 4 /* Message length */ +#define DIS_RDY 3, 3 /* Data Transfer ready / finish [1] Ready [0] Finish */ +#define SWITCH_CON 2, 2 /* Satellite switch in tone burst mode [1] Satellite B [0] Satellite A */ +#define LNB_CON 1, 0 /* LNB control mode, [0] Continuous mode, [1] Tone burst mode, [2] DiSEqC mode, [3] Reserved */ + +/* DiS03 */ +#define OLF_N 2, 2 /* [1] Disable [0] OLF (active low) */ +#define LNB_DN 1, 1 /* [1] LNB down [0] Disable (active high) */ +#define V18_13V 0, 0 /* 13V/18V select register (0=13V, 1=18V) */ + +/* DiS04 .. DiS11*/ +#define LNB_MESGE0 7, 0 +#define LNB_MESGE1 7, 0 +#define LNB_MESGE2 7, 0 +#define LNB_MESGE3 7, 0 +#define LNB_MESGE4 7, 0 +#define LNB_MESGE5 7, 0 +#define LNB_MESGE6 7, 0 +#define LNB_MESGE7 7, 0 + +/* Rf01 */ +#define SLAVE_ADDR 6, 0 /* RF tuner slave Address (SOC VERSION) */ + +/* Err01 */ +#define ALARM_MODE 4, 4 +#define ERR_CNT_PRD 3, 2 +#define ERR_SRC 1, 0 /* Error monitoring source */ + #define QPSK_BIT_ERRORS 0x0 + #define VITERBI_BIT_ERRORS 0x1 + #define VITERBI_BYTE_ERRORS 0x2 + #define PACKET_ERRORS 0x3 + +/* Err02 */ +#define ERR_CNT_L 7, 0 /* Error counter value register (LSB 8 bits) */ + +/* Err03 */ +#define ERR_CNT_H 7, 0 /* Error counter value register (MSB 8 bits) */ + +/* Err04 */ +#define PARITY_ERR 7, 0 /* Error flag for DiSEqC receive data */ + +#endif + +#endif diff --git a/linux/drivers/media/dvb/frontends/tda10048.c b/linux/drivers/media/dvb/frontends/tda10048.c new file mode 100644 index 000000000..2b9b1d93c --- /dev/null +++ b/linux/drivers/media/dvb/frontends/tda10048.c @@ -0,0 +1,710 @@ +/* + NXP TDA10048HN DVB OFDM demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + + 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/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include "dvb_frontend.h" +#include "tda10048.h" + +#define TDA10048_DEFAULT_FIRMWARE "dvb-fe-tda10048-1.0.fw" +#define TDA10048_DEFAULT_FIRMWARE_SIZE 24878 + +/* Register name definitions */ +#define TDA10048_IDENTITY 0x00 +#define TDA10048_VERSION 0x01 +#define TDA10048_DSP_CODE_CPT 0x0C +#define TDA10048_DSP_CODE_IN 0x0E +#define TDA10048_IN_CONF1 0x10 +#define TDA10048_IN_CONF2 0x11 +#define TDA10048_IN_CONF3 0x12 +#define TDA10048_OUT_CONF1 0x14 +#define TDA10048_OUT_CONF2 0x15 +#define TDA10048_OUT_CONF3 0x16 +#define TDA10048_AUTO 0x18 +#define TDA10048_SYNC_STATUS 0x1A +#define TDA10048_CONF_C4_1 0x1E +#define TDA10048_CONF_C4_2 0x1F +#define TDA10048_CODE_IN_RAM 0x20 +#define TDA10048_CHANNEL_INFO_1_R 0x22 +#define TDA10048_CHANNEL_INFO_2_R 0x23 +#define TDA10048_CHANNEL_INFO1 0x24 +#define TDA10048_CHANNEL_INFO2 0x25 +#define TDA10048_TIME_ERROR_R 0x26 +#define TDA10048_TIME_ERROR 0x27 +#define TDA10048_FREQ_ERROR_LSB_R 0x28 +#define TDA10048_FREQ_ERROR_MSB_R 0x29 +#define TDA10048_FREQ_ERROR_LSB 0x2A +#define TDA10048_FREQ_ERROR_MSB 0x2B +#define TDA10048_IT_SEL 0x30 +#define TDA10048_IT_STAT 0x32 +#define TDA10048_DSP_AD_LSB 0x3C +#define TDA10048_DSP_AD_MSB 0x3D +#define TDA10048_DSP_REF_LSB 0x3E +#define TDA10048_DSP_REF_MSB 0x3F +#define TDA10048_CONF_TRISTATE1 0x44 +#define TDA10048_CONF_TRISTATE2 0x45 +#define TDA10048_CONF_POLARITY 0x46 +#define TDA10048_GPIO_SP_DS0 0x48 +#define TDA10048_GPIO_SP_DS1 0x49 +#define TDA10048_GPIO_SP_DS2 0x4A +#define TDA10048_GPIO_SP_DS3 0x4B +#define TDA10048_GPIO_OUT_SEL 0x4C +#define TDA10048_GPIO_SELECT 0x4D +#define TDA10048_IC_MODE 0x4E +#define TDA10048_CONF_XO 0x50 +#define TDA10048_CONF_PLL1 0x51 +#define TDA10048_CONF_PLL2 0x52 +#define TDA10048_CONF_PLL3 0x53 +#define TDA10048_CONF_ADC 0x54 +#define TDA10048_CONF_ADC_2 0x55 +#define TDA10048_CONF_C1_1 0x60 +#define TDA10048_CONF_C1_3 0x62 +#define TDA10048_AGC_CONF 0x70 +#define TDA10048_AGC_THRESHOLD_LSB 0x72 +#define TDA10048_AGC_THRESHOLD_MSB 0x73 +#define TDA10048_AGC_RENORM 0x74 +#define TDA10048_AGC_GAINS 0x76 +#define TDA10048_AGC_TUN_MIN 0x78 +#define TDA10048_AGC_TUN_MAX 0x79 +#define TDA10048_AGC_IF_MIN 0x7A +#define TDA10048_AGC_IF_MAX 0x7B +#define TDA10048_AGC_TUN_LEVEL 0x7E +#define TDA10048_AGC_IF_LEVEL 0x7F +#define TDA10048_DIG_AGC_LEVEL 0x81 +#define TDA10048_FREQ_PHY2_LSB 0x86 +#define TDA10048_FREQ_PHY2_MSB 0x87 +#define TDA10048_TIME_INVWREF_LSB 0x88 +#define TDA10048_TIME_INVWREF_MSB 0x89 +#define TDA10048_TIME_WREF_LSB 0x8A +#define TDA10048_TIME_WREF_MID1 0x8B +#define TDA10048_TIME_WREF_MID2 0x8C +#define TDA10048_TIME_WREF_MSB 0x8D +#define TDA10048_NP_OUT 0xA2 +#define TDA10048_CELL_ID_LSB 0xA4 +#define TDA10048_CELL_ID_MSB 0xA5 +#define TDA10048_EXTTPS_ODD 0xAA +#define TDA10048_EXTTPS_EVEN 0xAB +#define TDA10048_TPS_LENGTH 0xAC +#define TDA10048_FREE_REG_1 0xB2 +#define TDA10048_FREE_REG_2 0xB3 +#define TDA10048_CONF_C3_1 0xC0 +#define TDA10048_CYBER_CTRL 0xC2 +#define TDA10048_CBER_NMAX_LSB 0xC4 +#define TDA10048_CBER_NMAX_MSB 0xC5 +#define TDA10048_CBER_LSB 0xC6 +#define TDA10048_CBER_MSB 0xC7 +#define TDA10048_VBER_LSB 0xC8 +#define TDA10048_VBER_MID 0xC9 +#define TDA10048_VBER_MSB 0xCA +#define TDA10048_CYBER_LUT 0xCC +#define TDA10048_UNCOR_CTRL 0xCD +#define TDA10048_UNCOR_CPT_LSB 0xCE +#define TDA10048_UNCOR_CPT_MSB 0xCF +#define TDA10048_SOFT_IT_C3 0xD6 +#define TDA10048_CONF_TS2 0xE0 +#define TDA10048_CONF_TS1 0xE1 + +static unsigned int debug; + +#define dprintk(level, fmt, arg...)\ + do { if (debug >= level)\ + printk(KERN_DEBUG "tda10048: " fmt, ## arg);\ + } while (0) + +struct tda10048_state { + + struct i2c_adapter *i2c; + + /* configuration settings */ + const struct tda10048_config *config; + struct dvb_frontend frontend; + + int fwloaded; +}; + +static struct init_tab { + u8 reg; + u16 data; +} init_tab[] = { + { TDA10048_CONF_PLL1, 0x08 }, + { TDA10048_CONF_ADC_2, 0x00 }, + { TDA10048_CONF_C4_1, 0x00 }, + { TDA10048_CONF_PLL1, 0x0f }, + { TDA10048_CONF_PLL2, 0x0a }, + { TDA10048_CONF_PLL3, 0x43 }, + { TDA10048_FREQ_PHY2_LSB, 0x02 }, + { TDA10048_FREQ_PHY2_MSB, 0x0a }, + { TDA10048_TIME_WREF_LSB, 0xbd }, + { TDA10048_TIME_WREF_MID1, 0xe4 }, + { TDA10048_TIME_WREF_MID2, 0xa8 }, + { TDA10048_TIME_WREF_MSB, 0x02 }, + { TDA10048_TIME_INVWREF_LSB, 0x04 }, + { TDA10048_TIME_INVWREF_MSB, 0x06 }, + { TDA10048_CONF_C4_1, 0x00 }, + { TDA10048_CONF_C1_1, 0xa8 }, + { TDA10048_AGC_CONF, 0x16 }, + { TDA10048_CONF_C1_3, 0x0b }, + { TDA10048_AGC_TUN_MIN, 0x00 }, + { TDA10048_AGC_TUN_MAX, 0xff }, + { TDA10048_AGC_IF_MIN, 0x00 }, + { TDA10048_AGC_IF_MAX, 0xff }, + { TDA10048_AGC_THRESHOLD_MSB, 0x00 }, + { TDA10048_AGC_THRESHOLD_LSB, 0x70 }, + { TDA10048_CYBER_CTRL, 0x38 }, + { TDA10048_AGC_GAINS, 0x12 }, + { TDA10048_CONF_XO, 0x00 }, + { TDA10048_CONF_TS1, 0x07 }, + { TDA10048_IC_MODE, 0x00 }, + { TDA10048_CONF_TS2, 0xc0 }, + { TDA10048_CONF_TRISTATE1, 0x21 }, + { TDA10048_CONF_TRISTATE2, 0x00 }, + { TDA10048_CONF_POLARITY, 0x00 }, + { TDA10048_CONF_C4_2, 0x04 }, + { TDA10048_CONF_ADC, 0x60 }, + { TDA10048_CONF_ADC_2, 0x10 }, + { TDA10048_CONF_ADC, 0x60 }, + { TDA10048_CONF_ADC_2, 0x00 }, + { TDA10048_CONF_C1_1, 0xa8 }, + { TDA10048_UNCOR_CTRL, 0x00 }, + { TDA10048_CONF_C4_2, 0x04 }, +}; + +static int tda10048_writereg(struct tda10048_state *state, u8 reg, u8 data) +{ + int ret; + u8 buf [] = { reg, data }; + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, .buf = buf, .len = 2 }; + + dprintk(2, "%s(reg = 0x%02x, data = 0x%02x)\n", __func__, reg, data); + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + printk("%s: writereg error (ret == %i)\n", __func__, ret); + + return (ret != 1) ? -1 : 0; +} + +static u8 tda10048_readreg(struct tda10048_state *state, u8 reg) +{ + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { + { .addr = state->config->demod_address, + .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, + .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + dprintk(2, "%s(reg = 0x%02x)\n", __func__, reg); + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + printk(KERN_ERR "%s: readreg error (ret == %i)\n", + __func__, ret); + + return b1[0]; +} + +static int tda10048_writeregbulk(struct tda10048_state *state, u8 reg, + u8 *data, u16 len) +{ + int ret = -EREMOTEIO; + struct i2c_msg msg; + u8 *buf; + + dprintk(2, "%s(%d, ?, len = %d)\n", __func__, reg, len); + + buf = kmalloc(len + 1, GFP_KERNEL); + if (buf == NULL) { + ret = -ENOMEM; + goto error; + } + + *buf = reg; + memcpy(buf + 1, data, len); + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.buf = buf; + msg.len = len + 1; + + dprintk(2, "%s(): write len = %d\n", + __func__, msg.len); + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) { + printk(KERN_ERR "%s(): writereg error err %i\n", + __func__, ret); + ret = -EREMOTEIO; + } + +error: + kfree(buf); + + return ret; +} + +static int tda10048_firmware_upload(struct dvb_frontend *fe) +{ + struct tda10048_state *state = fe->demodulator_priv; + const struct firmware *fw; + int ret; + int pos = 0; + int cnt; + u8 wlen = state->config->fwbulkwritelen; + + if ((wlen != TDA10048_BULKWRITE_200) && (wlen != TDA10048_BULKWRITE_50)) + wlen = TDA10048_BULKWRITE_200; + + /* request the firmware, this will block and timeout */ + printk(KERN_INFO "%s: waiting for firmware upload (%s)...\n", + __func__, + TDA10048_DEFAULT_FIRMWARE); + + ret = request_firmware(&fw, TDA10048_DEFAULT_FIRMWARE, + &state->i2c->dev); + if (ret) { + printk(KERN_ERR "%s: Upload failed. (file not found?)\n", + __func__); + return -EIO; + } else { + printk(KERN_INFO "%s: firmware read %Zu bytes.\n", + __func__, + fw->size); + ret = 0; + } + + if (fw->size != TDA10048_DEFAULT_FIRMWARE_SIZE) { + printk(KERN_ERR "%s: firmware incorrect size\n", __func__); + return -EIO; + } else { + printk(KERN_INFO "%s: firmware uploading\n", __func__); + + /* Soft reset */ + tda10048_writereg(state, TDA10048_CONF_TRISTATE1, + tda10048_readreg(state, TDA10048_CONF_TRISTATE1) + & 0xfe); + tda10048_writereg(state, TDA10048_CONF_TRISTATE1, + tda10048_readreg(state, TDA10048_CONF_TRISTATE1) + | 0x01); + + /* Put the demod into host download mode */ + tda10048_writereg(state, TDA10048_CONF_C4_1, + tda10048_readreg(state, TDA10048_CONF_C4_1) & 0xf9); + + /* Boot the DSP */ + tda10048_writereg(state, TDA10048_CONF_C4_1, + tda10048_readreg(state, TDA10048_CONF_C4_1) | 0x08); + + /* Prepare for download */ + tda10048_writereg(state, TDA10048_DSP_CODE_CPT, 0); + + /* Download the firmware payload */ + while (pos < fw->size) { + + if ((fw->size - pos) > wlen) + cnt = wlen; + else + cnt = fw->size - pos; + + tda10048_writeregbulk(state, TDA10048_DSP_CODE_IN, + &fw->data[pos], cnt); + + pos += cnt; + } + + ret = -EIO; + /* Wait up to 250ms for the DSP to boot */ + for (cnt = 0; cnt < 250 ; cnt += 10) { + + msleep(10); + + if (tda10048_readreg(state, TDA10048_SYNC_STATUS) + & 0x40) { + ret = 0; + break; + } + } + } + + release_firmware(fw); + + if (ret == 0) { + printk(KERN_INFO "%s: firmware uploaded\n", __func__); + state->fwloaded = 1; + } else + printk(KERN_ERR "%s: firmware upload failed\n", __func__); + + return ret; +} + +static int tda10048_set_inversion(struct dvb_frontend *fe, int inversion) +{ + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s(%d)\n", __func__, inversion); + + if (inversion == TDA10048_INVERSION_ON) + tda10048_writereg(state, TDA10048_CONF_C1_1, + tda10048_readreg(state, TDA10048_CONF_C1_1) | 0x20); + else + tda10048_writereg(state, TDA10048_CONF_C1_1, + tda10048_readreg(state, TDA10048_CONF_C1_1) & 0xdf); + + return 0; +} + +/* Retrieve the demod settings */ +static int tda10048_get_tps(struct tda10048_state *state, + struct dvb_ofdm_parameters *p) +{ + u8 val; + + /* Make sure the TPS regs are valid */ + if (!(tda10048_readreg(state, TDA10048_AUTO) & 0x01)) + return -EAGAIN; + + val = tda10048_readreg(state, TDA10048_OUT_CONF2); + switch ((val & 0x60) >> 5) { + case 0: p->constellation = QPSK; break; + case 1: p->constellation = QAM_16; break; + case 2: p->constellation = QAM_64; break; + } + switch ((val & 0x18) >> 3) { + case 0: p->hierarchy_information = HIERARCHY_NONE; break; + case 1: p->hierarchy_information = HIERARCHY_1; break; + case 2: p->hierarchy_information = HIERARCHY_2; break; + case 3: p->hierarchy_information = HIERARCHY_4; break; + } + switch (val & 0x07) { + case 0: p->code_rate_HP = FEC_1_2; break; + case 1: p->code_rate_HP = FEC_2_3; break; + case 2: p->code_rate_HP = FEC_3_4; break; + case 3: p->code_rate_HP = FEC_5_6; break; + case 4: p->code_rate_HP = FEC_7_8; break; + } + + val = tda10048_readreg(state, TDA10048_OUT_CONF3); + switch (val & 0x07) { + case 0: p->code_rate_LP = FEC_1_2; break; + case 1: p->code_rate_LP = FEC_2_3; break; + case 2: p->code_rate_LP = FEC_3_4; break; + case 3: p->code_rate_LP = FEC_5_6; break; + case 4: p->code_rate_LP = FEC_7_8; break; + } + + val = tda10048_readreg(state, TDA10048_OUT_CONF1); + switch ((val & 0x0c) >> 2) { + case 0: p->guard_interval = GUARD_INTERVAL_1_32; break; + case 1: p->guard_interval = GUARD_INTERVAL_1_16; break; + case 2: p->guard_interval = GUARD_INTERVAL_1_8; break; + case 3: p->guard_interval = GUARD_INTERVAL_1_4; break; + } + switch (val & 0x02) { + case 0: p->transmission_mode = TRANSMISSION_MODE_2K; break; + case 1: p->transmission_mode = TRANSMISSION_MODE_8K; break; + } + + return 0; +} + +static int tda10048_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct tda10048_state *state = fe->demodulator_priv; + dprintk(1, "%s(%d)\n", __func__, enable); + + if (enable) + return tda10048_writereg(state, TDA10048_CONF_C4_1, + tda10048_readreg(state, TDA10048_CONF_C4_1) | 0x02); + else + return tda10048_writereg(state, TDA10048_CONF_C4_1, + tda10048_readreg(state, TDA10048_CONF_C4_1) & 0xfd); +} + +static int tda10048_output_mode(struct dvb_frontend *fe, int serial) +{ + struct tda10048_state *state = fe->demodulator_priv; + dprintk(1, "%s(%d)\n", __func__, serial); + + /* Ensure pins are out of tri-state */ + tda10048_writereg(state, TDA10048_CONF_TRISTATE1, 0x21); + tda10048_writereg(state, TDA10048_CONF_TRISTATE2, 0x00); + + if (serial) { + tda10048_writereg(state, TDA10048_IC_MODE, 0x80 | 0x20); + tda10048_writereg(state, TDA10048_CONF_TS2, 0xc0); + } else { + tda10048_writereg(state, TDA10048_IC_MODE, 0x00); + tda10048_writereg(state, TDA10048_CONF_TS2, 0x01); + } + + return 0; +} + +/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +/* TODO: Support manual tuning with specific params */ +static int tda10048_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s(frequency=%d)\n", __func__, p->frequency); + + if (fe->ops.tuner_ops.set_params) { + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + fe->ops.tuner_ops.set_params(fe, p); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* Enable demod TPS auto detection and begin acquisition */ + tda10048_writereg(state, TDA10048_AUTO, 0x57); + + return 0; +} + +/* Establish sane defaults and load firmware. */ +static int tda10048_init(struct dvb_frontend *fe) +{ + struct tda10048_state *state = fe->demodulator_priv; + int ret = 0, i; + + dprintk(1, "%s()\n", __func__); + + /* Apply register defaults */ + for (i = 0; i < ARRAY_SIZE(init_tab); i++) + tda10048_writereg(state, init_tab[i].reg, init_tab[i].data); + + if (state->fwloaded == 0) + ret = tda10048_firmware_upload(fe); + + /* Set either serial or parallel */ + tda10048_output_mode(fe, state->config->output_mode); + + /* set inversion */ + tda10048_set_inversion(fe, state->config->inversion); + + /* Ensure we leave the gate closed */ + tda10048_i2c_gate_ctrl(fe, 0); + + return ret; +} + +static int tda10048_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct tda10048_state *state = fe->demodulator_priv; + u8 reg; + + *status = 0; + + reg = tda10048_readreg(state, TDA10048_SYNC_STATUS); + + dprintk(1, "%s() status =0x%02x\n", __func__, reg); + + if (reg & 0x02) + *status |= FE_HAS_CARRIER; + + if (reg & 0x04) + *status |= FE_HAS_SIGNAL; + + if (reg & 0x08) { + *status |= FE_HAS_LOCK; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + } + + return 0; +} + +static int tda10048_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s()\n", __func__); + + /* TODO: A reset may be required here */ + *ber = tda10048_readreg(state, TDA10048_CBER_MSB) << 8 | + tda10048_readreg(state, TDA10048_CBER_LSB); + + return 0; +} + +static int tda10048_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct tda10048_state *state = fe->demodulator_priv; + u16 v; + + dprintk(1, "%s()\n", __func__); + + v = tda10048_readreg(state, TDA10048_NP_OUT); + if (v == 0) + *signal_strength = 100; + else { + /* TODO: Apply .db math for correct values */ + *signal_strength = v; + } + + return 0; +} + +static int tda10048_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s()\n", __func__); + + /* TODO: This result should be the same as signal strength */ + *snr = tda10048_readreg(state, TDA10048_NP_OUT); + + return 0; +} + +static int tda10048_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s()\n", __func__); +#if 0 + /* Reset and begin counting */ + tda10048_writereg(state, TDA10048_UNCOR_CTRL, 0x01); + msleep(10); +#endif + + *ucblocks = tda10048_readreg(state, TDA10048_UNCOR_CPT_MSB) << 8 | + tda10048_readreg(state, TDA10048_UNCOR_CPT_LSB); + + return 0; +} + +static int tda10048_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s()\n", __func__); + + p->inversion = tda10048_readreg(state, TDA10048_CONF_C1_1) + & 0x20 ? INVERSION_ON : INVERSION_OFF; + + return tda10048_get_tps(state, &p->u.ofdm); +} + +static int tda10048_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void tda10048_release(struct dvb_frontend *fe) +{ + struct tda10048_state *state = fe->demodulator_priv; + dprintk(1, "%s()\n", __func__); + kfree(state); +} + +static struct dvb_frontend_ops tda10048_ops; + +struct dvb_frontend *tda10048_attach(const struct tda10048_config *config, + struct i2c_adapter *i2c) +{ + struct tda10048_state *state = NULL; + + dprintk(1, "%s()\n", __func__); + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct tda10048_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->fwloaded = 0; + + /* check if the demod is present */ + if (tda10048_readreg(state, TDA10048_IDENTITY) != 0x048) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &tda10048_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + /* Leave the gate closed */ + tda10048_i2c_gate_ctrl(&state->frontend, 0); + + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(tda10048_attach); + +static struct dvb_frontend_ops tda10048_ops = { + + .info = { + .name = "NXP TDA10048HN DVB-T", + .type = FE_OFDM, + .frequency_min = 177000000, + .frequency_max = 858000000, + .frequency_stepsize = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER + }, + + .release = tda10048_release, + .init = tda10048_init, + .i2c_gate_ctrl = tda10048_i2c_gate_ctrl, + .set_frontend = tda10048_set_frontend, + .get_frontend = tda10048_get_frontend, + .get_tune_settings = tda10048_get_tune_settings, + .read_status = tda10048_read_status, + .read_ber = tda10048_read_ber, + .read_signal_strength = tda10048_read_signal_strength, + .read_snr = tda10048_read_snr, + .read_ucblocks = tda10048_read_ucblocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable verbose debug messages"); + +MODULE_DESCRIPTION("NXP TDA10048HN DVB-T Demodulator driver"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); + diff --git a/linux/drivers/media/dvb/frontends/tda10048.h b/linux/drivers/media/dvb/frontends/tda10048.h new file mode 100644 index 000000000..2b5c78e62 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/tda10048.h @@ -0,0 +1,63 @@ +/* + NXP TDA10048HN DVB OFDM demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + + 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 TDA10048_H +#define TDA10048_H + +#include <linux/dvb/frontend.h> +#include <linux/firmware.h> + +struct tda10048_config { + + /* the demodulator's i2c address */ + u8 demod_address; + + /* serial/parallel output */ +#define TDA10048_PARALLEL_OUTPUT 0 +#define TDA10048_SERIAL_OUTPUT 1 + u8 output_mode; + +#define TDA10048_BULKWRITE_200 200 +#define TDA10048_BULKWRITE_50 50 + u8 fwbulkwritelen; + + /* Spectral Inversion */ +#define TDA10048_INVERSION_OFF 0 +#define TDA10048_INVERSION_ON 1 + u8 inversion; +}; + +#if defined(CONFIG_DVB_TDA10048) || \ + (defined(CONFIG_DVB_TDA10048_MODULE) && defined(MODULE)) +extern struct dvb_frontend *tda10048_attach( + const struct tda10048_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *tda10048_attach( + const struct tda10048_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_TDA10048 */ + +#endif /* TDA10048_H */ diff --git a/linux/drivers/media/dvb/ttpci/budget.c b/linux/drivers/media/dvb/ttpci/budget.c index 108788bf0..7adfe17b0 100644 --- a/linux/drivers/media/dvb/ttpci/budget.c +++ b/linux/drivers/media/dvb/ttpci/budget.c @@ -358,6 +358,7 @@ static int s5h1420_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend static struct s5h1420_config s5h1420_config = { .demod_address = 0x53, .invert = 1, + .cdclk_polarity = 1, }; static struct tda10086_config tda10086_config = { diff --git a/linux/drivers/media/radio/radio-si470x.c b/linux/drivers/media/radio/radio-si470x.c index 83333734e..9f4207edc 100644 --- a/linux/drivers/media/radio/radio-si470x.c +++ b/linux/drivers/media/radio/radio-si470x.c @@ -532,8 +532,7 @@ static int si470x_get_register(struct si470x_device *radio, int regnr) retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); if (retval >= 0) - radio->registers[regnr] = be16_to_cpu(get_unaligned( - (unsigned short *) &buf[1])); + radio->registers[regnr] = get_unaligned_be16(&buf[1]); return (retval < 0) ? -EINVAL : 0; } @@ -548,8 +547,7 @@ static int si470x_set_register(struct si470x_device *radio, int regnr) int retval; buf[0] = REGISTER_REPORT(regnr); - put_unaligned(cpu_to_be16(radio->registers[regnr]), - (unsigned short *) &buf[1]); + put_unaligned_be16(radio->registers[regnr], &buf[1]); retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); @@ -572,9 +570,8 @@ static int si470x_get_all_registers(struct si470x_device *radio) if (retval >= 0) for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) - radio->registers[regnr] = be16_to_cpu(get_unaligned( - (unsigned short *) - &buf[regnr * RADIO_REGISTER_SIZE + 1])); + radio->registers[regnr] = get_unaligned_be16( + &buf[regnr * RADIO_REGISTER_SIZE + 1]); return (retval < 0) ? -EINVAL : 0; } @@ -605,8 +602,7 @@ static int si470x_get_rds_registers(struct si470x_device *radio) if (retval >= 0) for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) radio->registers[STATUSRSSI + regnr] = - be16_to_cpu(get_unaligned((unsigned short *) - &buf[regnr * RADIO_REGISTER_SIZE + 1])); + get_unaligned_be16(&buf[regnr * RADIO_REGISTER_SIZE + 1]); return (retval < 0) ? -EINVAL : 0; } @@ -854,7 +850,7 @@ static void si470x_rds(struct si470x_device *radio) }; /* Fill the V4L2 RDS buffer */ - put_unaligned(cpu_to_le16(rds), (unsigned short *) &tmpbuf); + put_unaligned_le16(rds, &tmpbuf); tmpbuf[2] = blocknum; /* offset name */ tmpbuf[2] |= blocknum << 3; /* received offset */ if (bler > max_rds_errors) diff --git a/linux/drivers/media/radio/radio-typhoon.c b/linux/drivers/media/radio/radio-typhoon.c index 22c999461..5db38526e 100644 --- a/linux/drivers/media/radio/radio-typhoon.c +++ b/linux/drivers/media/radio/radio-typhoon.c @@ -35,6 +35,7 @@ #include <linux/init.h> /* Initdata */ #include <linux/ioport.h> /* request_region */ #include <linux/proc_fs.h> /* radio card status report */ +#include <linux/seq_file.h> #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ #include "compat.h" @@ -98,9 +99,6 @@ static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency); static void typhoon_mute(struct typhoon_device *dev); static void typhoon_unmute(struct typhoon_device *dev); static int typhoon_setvol(struct typhoon_device *dev, int vol); -#ifdef CONFIG_RADIO_TYPHOON_PROC_FS -static int typhoon_get_info(char *buf, char **start, off_t offset, int len); -#endif static void typhoon_setvol_generic(struct typhoon_device *dev, int vol) { @@ -373,30 +371,39 @@ static struct video_device typhoon_radio = #ifdef CONFIG_RADIO_TYPHOON_PROC_FS -static int typhoon_get_info(char *buf, char **start, off_t offset, int len) +static int typhoon_proc_show(struct seq_file *m, void *v) { - char *out = buf; - #ifdef MODULE #define MODULEPROCSTRING "Driver loaded as a module" #else #define MODULEPROCSTRING "Driver compiled into kernel" #endif - /* output must be kept under PAGE_SIZE */ - out += sprintf(out, BANNER); - out += sprintf(out, "Load type: " MODULEPROCSTRING "\n\n"); - out += sprintf(out, "frequency = %lu kHz\n", + seq_puts(m, BANNER); + seq_puts(m, "Load type: " MODULEPROCSTRING "\n\n"); + seq_printf(m, "frequency = %lu kHz\n", typhoon_unit.curfreq >> 4); - out += sprintf(out, "volume = %d\n", typhoon_unit.curvol); - out += sprintf(out, "mute = %s\n", typhoon_unit.muted ? + seq_printf(m, "volume = %d\n", typhoon_unit.curvol); + seq_printf(m, "mute = %s\n", typhoon_unit.muted ? "on" : "off"); - out += sprintf(out, "iobase = 0x%x\n", typhoon_unit.iobase); - out += sprintf(out, "mute frequency = %lu kHz\n", + seq_printf(m, "iobase = 0x%x\n", typhoon_unit.iobase); + seq_printf(m, "mute frequency = %lu kHz\n", typhoon_unit.mutefreq >> 4); - return out - buf; + return 0; } +static int typhoon_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, typhoon_proc_show, NULL); +} + +static const struct file_operations typhoon_proc_fops = { + .owner = THIS_MODULE, + .open = typhoon_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; #endif /* CONFIG_RADIO_TYPHOON_PROC_FS */ MODULE_AUTHOR("Dr. Henrik Seidel"); @@ -457,8 +464,7 @@ static int __init typhoon_init(void) typhoon_mute(&typhoon_unit); #ifdef CONFIG_RADIO_TYPHOON_PROC_FS - if (!create_proc_info_entry("driver/radio-typhoon", 0, NULL, - typhoon_get_info)) + if (!proc_create("driver/radio-typhoon", 0, NULL, &typhoon_proc_fops)) printk(KERN_ERR "radio-typhoon: registering /proc/driver/radio-typhoon failed\n"); #endif diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 54b9f84e9..fe9a4cc14 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -698,6 +698,8 @@ source "drivers/media/video/cx88/Kconfig" source "drivers/media/video/cx23885/Kconfig" +source "drivers/media/video/au0828/Kconfig" + source "drivers/media/video/ivtv/Kconfig" config VIDEO_M32R_AR diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index caf9085c2..f190c0702 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -144,5 +144,7 @@ obj-$(CONFIG_SOC_CAMERA) += soc_camera.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o +obj-$(CONFIG_VIDEO_AU0828) += au0828/ + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/linux/drivers/media/video/adv7170.c b/linux/drivers/media/video/adv7170.c index 0d15ce53d..54ef7aa63 100644 --- a/linux/drivers/media/video/adv7170.c +++ b/linux/drivers/media/video/adv7170.c @@ -409,7 +409,7 @@ adv7170_detect_client (struct i2c_adapter *adapter, return 0; client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == 0) + if (!client) return -ENOMEM; client->addr = address; client->adapter = adapter; diff --git a/linux/drivers/media/video/adv7175.c b/linux/drivers/media/video/adv7175.c index 86345ba5c..b8618c021 100644 --- a/linux/drivers/media/video/adv7175.c +++ b/linux/drivers/media/video/adv7175.c @@ -427,7 +427,7 @@ adv7175_detect_client (struct i2c_adapter *adapter, return 0; client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == 0) + if (!client) return -ENOMEM; client->addr = address; client->adapter = adapter; diff --git a/linux/drivers/media/video/au0828/Kconfig b/linux/drivers/media/video/au0828/Kconfig new file mode 100644 index 000000000..f04c80bc8 --- /dev/null +++ b/linux/drivers/media/video/au0828/Kconfig @@ -0,0 +1,13 @@ + +config VIDEO_AU0828 + tristate "Auvitek AU0828 support" + depends on VIDEO_DEV && I2C && INPUT + select I2C_ALGOBIT + select DVB_AU8522 if !DVB_FE_CUSTOMIZE + select DVB_TUNER_XC5000 if !DVB_FE_CUSTOMIZE + ---help--- + This is a video4linux driver for Auvitek's USB device. + + To compile this driver as a module, choose M here: the + module will be called au0828 + diff --git a/linux/drivers/media/video/au0828/Makefile b/linux/drivers/media/video/au0828/Makefile new file mode 100644 index 000000000..9f4f572c8 --- /dev/null +++ b/linux/drivers/media/video/au0828/Makefile @@ -0,0 +1,9 @@ +au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o + +obj-$(CONFIG_VIDEO_AU0828) += au0828.o + +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends + +EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) diff --git a/linux/drivers/media/video/au0828/au0828-cards.c b/linux/drivers/media/video/au0828/au0828-cards.c new file mode 100644 index 000000000..8ca91f814 --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828-cards.c @@ -0,0 +1,182 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * 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 "au0828.h" +#include "au0828-cards.h" + +struct au0828_board au0828_boards[] = { + [AU0828_BOARD_UNKNOWN] = { + .name = "Unknown board", + }, + [AU0828_BOARD_HAUPPAUGE_HVR850] = { + .name = "Hauppauge HVR850", + }, + [AU0828_BOARD_HAUPPAUGE_HVR950Q] = { + .name = "Hauppauge HVR950Q", + }, + [AU0828_BOARD_DVICO_FUSIONHDTV7] = { + .name = "DViCO FusionHDTV USB", + }, +}; +const unsigned int au0828_bcount = ARRAY_SIZE(au0828_boards); + +/* Tuner callback function for au0828 boards. Currently only needed + * for HVR1500Q, which has an xc5000 tuner. + */ +int au0828_tuner_callback(void *priv, int command, int arg) +{ + struct au0828_dev *dev = priv; + + dprintk(1, "%s()\n", __func__); + + switch (dev->board) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + case AU0828_BOARD_DVICO_FUSIONHDTV7: + if (command == 0) { + /* Tuner Reset Command from xc5000 */ + /* Drive the tuner into reset and out */ + au0828_clear(dev, REG_001, 2); + mdelay(200); + au0828_set(dev, REG_001, 2); + mdelay(50); + return 0; + } else { + printk(KERN_ERR + "%s(): Unknown command.\n", __func__); + return -EINVAL; + } + break; + } + + return 0; /* Should never be here */ +} + +static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) +{ + struct tveeprom tv; + + tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data); + + /* Make sure we support the board model */ + switch (tv.model) { + case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and basic analog video */ + case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and basic analog video */ + break; + default: + printk(KERN_WARNING "%s: warning: " + "unknown hauppauge model #%d\n", __func__, tv.model); + break; + } + + printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n", + __func__, tv.model); +} + +void au0828_card_setup(struct au0828_dev *dev) +{ + static u8 eeprom[256]; + + dprintk(1, "%s()\n", __func__); + + if (dev->i2c_rc == 0) { + dev->i2c_client.addr = 0xa0 >> 1; + tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom)); + } + + switch (dev->board) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + if (dev->i2c_rc == 0) + hauppauge_eeprom(dev, eeprom+0xa0); + break; + } +} + +/* + * The bridge has between 8 and 12 gpios. + * Regs 1 and 0 deal with output enables. + * Regs 3 and 2 deal with direction. + */ +void au0828_gpio_setup(struct au0828_dev *dev) +{ + dprintk(1, "%s()\n", __func__); + + switch (dev->board) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + /* GPIO's + * 4 - CS5340 + * 5 - AU8522 Demodulator + * 6 - eeprom W/P + * 9 - XC5000 Tuner + */ + + /* Into reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_002, 0x88 | 0x20); + au0828_write(dev, REG_001, 0x0); + au0828_write(dev, REG_000, 0x0); + msleep(100); + + /* Out of reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_001, 0x02); + au0828_write(dev, REG_002, 0x88 | 0x20); + au0828_write(dev, REG_000, 0x88 | 0x20 | 0x40); + msleep(250); + break; + case AU0828_BOARD_DVICO_FUSIONHDTV7: + /* GPIO's + * 6 - ? + * 8 - AU8522 Demodulator + * 9 - XC5000 Tuner + */ + + /* Into reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_002, 0xa0); + au0828_write(dev, REG_001, 0x0); + au0828_write(dev, REG_000, 0x0); + msleep(100); + + /* Out of reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_002, 0xa0); + au0828_write(dev, REG_001, 0x02); + au0828_write(dev, REG_000, 0xa0); + msleep(250); + break; + } +} + +/* table of devices that work with this driver */ +struct usb_device_id au0828_usb_id_table [] = { + { USB_DEVICE(0x2040, 0x7200), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7240), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 }, + { USB_DEVICE(0x0fe9, 0xd620), + .driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 }, + { }, +}; + +MODULE_DEVICE_TABLE(usb, au0828_usb_id_table); diff --git a/linux/drivers/media/video/au0828/au0828-cards.h b/linux/drivers/media/video/au0828/au0828-cards.h new file mode 100644 index 000000000..118629771 --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828-cards.h @@ -0,0 +1,26 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * 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. + */ + +#define AU0828_BOARD_UNKNOWN 0 +#define AU0828_BOARD_HAUPPAUGE_HVR950Q 1 +#define AU0828_BOARD_HAUPPAUGE_HVR850 2 +#define AU0828_BOARD_DVICO_FUSIONHDTV7 3 + diff --git a/linux/drivers/media/video/au0828/au0828-core.c b/linux/drivers/media/video/au0828/au0828-core.c new file mode 100644 index 000000000..b3a4346b1 --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828-core.c @@ -0,0 +1,277 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * 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/module.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include "compat.h" +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) +#include <linux/mutex.h> +#endif + +#include "au0828.h" + +/* + * 1 = General debug messages + * 2 = USB handling + * 4 = I2C related + * 8 = Bridge related + */ +unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +unsigned int usb_debug; +module_param(usb_debug, int, 0644); +MODULE_PARM_DESC(usb_debug, "enable usb debug messages"); + +unsigned int bridge_debug; +module_param(bridge_debug, int, 0644); +MODULE_PARM_DESC(bridge_debug, "enable bridge debug messages"); + +#define _AU0828_BULKPIPE 0x03 +#define _BULKPIPESIZE 0xffff + +static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index, unsigned char *cp, u16 size); +static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index, unsigned char *cp, u16 size); + +/* USB Direction */ +#define CMD_REQUEST_IN 0x00 +#define CMD_REQUEST_OUT 0x01 + +u32 au0828_readreg(struct au0828_dev *dev, u16 reg) +{ + recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, dev->ctrlmsg, 1); + dprintk(8, "%s(0x%x) = 0x%x\n", __func__, reg, dev->ctrlmsg[0]); + return dev->ctrlmsg[0]; +} + +u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val) +{ + dprintk(8, "%s(0x%x, 0x%x)\n", __func__, reg, val); + return send_control_msg(dev, CMD_REQUEST_OUT, val, reg, + dev->ctrlmsg, 0); +} + +static void cmd_msg_dump(struct au0828_dev *dev) +{ + int i; + + for (i = 0; i < sizeof(dev->ctrlmsg); i += 16) + dprintk(2, "%s() %02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + __func__, + dev->ctrlmsg[i+0], dev->ctrlmsg[i+1], + dev->ctrlmsg[i+2], dev->ctrlmsg[i+3], + dev->ctrlmsg[i+4], dev->ctrlmsg[i+5], + dev->ctrlmsg[i+6], dev->ctrlmsg[i+7], + dev->ctrlmsg[i+8], dev->ctrlmsg[i+9], + dev->ctrlmsg[i+10], dev->ctrlmsg[i+11], + dev->ctrlmsg[i+12], dev->ctrlmsg[i+13], + dev->ctrlmsg[i+14], dev->ctrlmsg[i+15]); +} + +static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index, unsigned char *cp, u16 size) +{ + int status = -ENODEV; + mutex_lock(&dev->mutex); + if (dev->usbdev) { + + /* cp must be memory that has been allocated by kmalloc */ + status = usb_control_msg(dev->usbdev, + usb_sndctrlpipe(dev->usbdev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + cp, size, 1000); + + status = min(status, 0); + + if (status < 0) { + printk(KERN_ERR "%s() Failed sending control message, error %d.\n", + __func__, status); + } + + } + mutex_unlock(&dev->mutex); + return status; +} + +static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index, unsigned char *cp, u16 size) +{ + int status = -ENODEV; + mutex_lock(&dev->mutex); + if (dev->usbdev) { + + memset(dev->ctrlmsg, 0, sizeof(dev->ctrlmsg)); + + /* cp must be memory that has been allocated by kmalloc */ + status = usb_control_msg(dev->usbdev, + usb_rcvctrlpipe(dev->usbdev, 0), + request, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + cp, size, 1000); + + status = min(status, 0); + + if (status < 0) { + printk(KERN_ERR "%s() Failed receiving control message, error %d.\n", + __func__, status); + } else + cmd_msg_dump(dev); + } + mutex_unlock(&dev->mutex); + return status; +} + +static void au0828_usb_disconnect(struct usb_interface *interface) +{ + struct au0828_dev *dev = usb_get_intfdata(interface); + + dprintk(1, "%s()\n", __func__); + + /* Digital TV */ + au0828_dvb_unregister(dev); + + /* I2C */ + au0828_i2c_unregister(dev); + + usb_set_intfdata(interface, NULL); + + mutex_lock(&dev->mutex); + dev->usbdev = NULL; + mutex_unlock(&dev->mutex); + + kfree(dev); + +} + +static int au0828_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int ifnum; + struct au0828_dev *dev; + struct usb_device *usbdev = interface_to_usbdev(interface); + + ifnum = interface->altsetting->desc.bInterfaceNumber; + + if (ifnum != 0) + return -ENODEV; + + dprintk(1, "%s() vendor id 0x%x device id 0x%x ifnum:%d\n", __func__, + le16_to_cpu(usbdev->descriptor.idVendor), + le16_to_cpu(usbdev->descriptor.idProduct), + ifnum); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + printk(KERN_ERR "%s() Unable to allocate memory\n", __func__); + return -ENOMEM; + } + + mutex_init(&dev->mutex); + mutex_init(&dev->dvb.lock); + dev->usbdev = usbdev; + dev->board = id->driver_info; + + usb_set_intfdata(interface, dev); + + /* Power Up the bridge */ + au0828_write(dev, REG_600, 1 << 4); + + /* Bring up the GPIO's and supporting devices */ + au0828_gpio_setup(dev); + + /* I2C */ + au0828_i2c_register(dev); + + /* Setup */ + au0828_card_setup(dev); + + /* Digital TV */ + au0828_dvb_register(dev); + + printk(KERN_INFO "Registered device AU0828 [%s]\n", + au0828_boards[dev->board].name == NULL ? "Unset" : + au0828_boards[dev->board].name); + + return 0; +} + +static struct usb_driver au0828_usb_driver = { + .name = DRIVER_NAME, + .probe = au0828_usb_probe, + .disconnect = au0828_usb_disconnect, + .id_table = au0828_usb_id_table, +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15) + .owner = THIS_MODULE, +#endif +}; + +static int __init au0828_init(void) +{ + int ret; + + if (debug) + printk(KERN_INFO "%s() Debugging is enabled\n", __func__); + + if (usb_debug) { + printk(KERN_INFO "%s() USB Debugging is enabled\n", __func__); + debug |= 2; + } + + if (i2c_debug) { + printk(KERN_INFO "%s() I2C Debugging is enabled\n", __func__); + debug |= 4; + } + + if (bridge_debug) { + printk(KERN_INFO "%s() Bridge Debugging is enabled\n", + __func__); + debug |= 8; + } + + printk(KERN_INFO "au0828 driver loaded\n"); + + ret = usb_register(&au0828_usb_driver); + if (ret) + printk(KERN_ERR "usb_register failed, error = %d\n", ret); + + return ret; +} + +static void __exit au0828_exit(void) +{ + usb_deregister(&au0828_usb_driver); +} + +module_init(au0828_init); +module_exit(au0828_exit); + +MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products"); +MODULE_AUTHOR("Steven Toth <stoth@hauppauge.com>"); +MODULE_LICENSE("GPL"); + diff --git a/linux/drivers/media/video/au0828/au0828-dvb.c b/linux/drivers/media/video/au0828/au0828-dvb.c new file mode 100644 index 000000000..f911bbac6 --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828-dvb.c @@ -0,0 +1,402 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * 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/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/suspend.h> +#include <media/v4l2-common.h> +#include "compat.h" + +#include "au0828.h" +#include "au8522.h" +#include "xc5000.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define _AU0828_BULKPIPE 0x83 +#define _BULKPIPESIZE 0xe522 + +static struct au8522_config hauppauge_hvr950q_config = { + .demod_address = 0x8e >> 1, + .status_mode = AU8522_DEMODLOCKING, +}; + +static struct xc5000_config hauppauge_hvr950q_tunerconfig = { + .i2c_address = 0x61, + .if_khz = 6000, + .tuner_callback = au0828_tuner_callback +}; + +/*-------------------------------------------------------------------*/ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void urb_completion(struct urb *purb, struct pt_regs *regs) +#else +static void urb_completion(struct urb *purb) +#endif +{ + u8 *ptr; + struct au0828_dev *dev = purb->context; + int ptype = usb_pipetype(purb->pipe); + + dprintk(2, "%s()\n", __func__); + + if (!dev) + return; + + if (dev->urb_streaming == 0) + return; + + if (ptype != PIPE_BULK) { + printk(KERN_ERR "%s() Unsupported URB type %d\n", + __func__, ptype); + return; + } + + ptr = (u8 *)purb->transfer_buffer; + + /* Feed the transport payload into the kernel demux */ + dvb_dmx_swfilter_packets(&dev->dvb.demux, + purb->transfer_buffer, purb->actual_length / 188); + + /* Clean the buffer before we requeue */ + memset(purb->transfer_buffer, 0, URB_BUFSIZE); + + /* Requeue URB */ + usb_submit_urb(purb, GFP_ATOMIC); +} + +static int stop_urb_transfer(struct au0828_dev *dev) +{ + int i; + + dprintk(2, "%s()\n", __func__); + + for (i = 0; i < URB_COUNT; i++) { + usb_kill_urb(dev->urbs[i]); + kfree(dev->urbs[i]->transfer_buffer); + usb_free_urb(dev->urbs[i]); + } + + dev->urb_streaming = 0; + + return 0; +} + +static int start_urb_transfer(struct au0828_dev *dev) +{ + struct urb *purb; + int i, ret = -ENOMEM; + + dprintk(2, "%s()\n", __func__); + + if (dev->urb_streaming) { + dprintk(2, "%s: iso xfer already running!\n", __func__); + return 0; + } + + for (i = 0; i < URB_COUNT; i++) { + + dev->urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urbs[i]) + goto err; + + purb = dev->urbs[i]; + + purb->transfer_buffer = kzalloc(URB_BUFSIZE, GFP_KERNEL); + if (!purb->transfer_buffer) { + usb_free_urb(purb); + dev->urbs[i] = 0; + goto err; + } + + purb->status = -EINPROGRESS; + usb_fill_bulk_urb(purb, + dev->usbdev, + usb_rcvbulkpipe(dev->usbdev, _AU0828_BULKPIPE), + purb->transfer_buffer, + URB_BUFSIZE, + urb_completion, + dev); + + } + + for (i = 0; i < URB_COUNT; i++) { + ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC); + if (ret != 0) { + stop_urb_transfer(dev); + printk(KERN_ERR "%s: failed urb submission, " + "err = %d\n", __func__, ret); + return ret; + } + } + + dev->urb_streaming = 1; + ret = 0; + +err: + return ret; +} + +static int au0828_dvb_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct au0828_dev *dev = (struct au0828_dev *) demux->priv; + struct au0828_dvb *dvb = &dev->dvb; + int ret = 0; + + dprintk(1, "%s()\n", __func__); + + if (!demux->dmx.frontend) + return -EINVAL; + + if (dvb) { + mutex_lock(&dvb->lock); + if (dvb->feeding++ == 0) { + /* Start transport */ + au0828_write(dev, 0x608, 0x90); + au0828_write(dev, 0x609, 0x72); + au0828_write(dev, 0x60a, 0x71); + au0828_write(dev, 0x60b, 0x01); + ret = start_urb_transfer(dev); + } + mutex_unlock(&dvb->lock); + } + + return ret; +} + +static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct au0828_dev *dev = (struct au0828_dev *) demux->priv; + struct au0828_dvb *dvb = &dev->dvb; + int ret = 0; + + dprintk(1, "%s()\n", __func__); + + if (dvb) { + mutex_lock(&dvb->lock); + if (--dvb->feeding == 0) { + /* Stop transport */ + au0828_write(dev, 0x608, 0x00); + au0828_write(dev, 0x609, 0x00); + au0828_write(dev, 0x60a, 0x00); + au0828_write(dev, 0x60b, 0x00); + ret = stop_urb_transfer(dev); + } + mutex_unlock(&dvb->lock); + } + + return ret; +} + +int dvb_register(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + int result; + + dprintk(1, "%s()\n", __func__); + + /* register adapter */ + result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE, + &dev->usbdev->dev, adapter_nr); + if (result < 0) { + printk(KERN_ERR "%s: dvb_register_adapter failed " + "(errno = %d)\n", DRIVER_NAME, result); + goto fail_adapter; + } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb->adapter.priv = dev; + + /* register frontend */ + result = dvb_register_frontend(&dvb->adapter, dvb->frontend); +#else + dvb->adapter->priv = dev; + + /* register frontend */ + result = dvb_register_frontend(dvb->adapter, dvb->frontend); +#endif + if (result < 0) { + printk(KERN_ERR "%s: dvb_register_frontend failed " + "(errno = %d)\n", DRIVER_NAME, result); + goto fail_frontend; + } + + /* register demux stuff */ + dvb->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dev; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = au0828_dvb_start_feed; + dvb->demux.stop_feed = au0828_dvb_stop_feed; + result = dvb_dmx_init(&dvb->demux); + if (result < 0) { + printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_dmx; + } + + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = &dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); +#else + result = dvb_dmxdev_init(&dvb->dmxdev, dvb->adapter); +#endif + if (result < 0) { + printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_dmxdev; + } + + dvb->fe_hw.source = DMX_FRONTEND_0; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_ERR "%s: add_frontend failed " + "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result); + goto fail_fe_hw; + } + + dvb->fe_mem.source = DMX_MEMORY_FE; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); + if (result < 0) { + printk(KERN_ERR "%s: add_frontend failed " + "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result); + goto fail_fe_mem; + } + + result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_fe_conn; + } + + /* register network adapter */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); +#else + dvb_net_init(dvb->adapter, &dvb->net, &dvb->demux.dmx); +#endif + return 0; + +fail_fe_conn: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); +fail_fe_mem: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); +fail_fe_hw: + dvb_dmxdev_release(&dvb->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb->demux); +fail_dmx: + dvb_unregister_frontend(dvb->frontend); +fail_frontend: + dvb_frontend_detach(dvb->frontend); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb_unregister_adapter(&dvb->adapter); +#else + dvb_unregister_adapter(dvb->adapter); +#endif +fail_adapter: + return result; +} + +void au0828_dvb_unregister(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + + dprintk(1, "%s()\n", __func__); + + if (dvb->frontend == NULL) + return; + + dvb_net_release(&dvb->net); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb_unregister_adapter(&dvb->adapter); +#else + dvb_unregister_adapter(dvb->adapter); +#endif +} + +/* All the DVB attach calls go here, this function get's modified + * for each new card. No other function in this file needs + * to change. + */ +int au0828_dvb_register(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + int ret; + + dprintk(1, "%s()\n", __func__); + + /* init frontend */ + switch (dev->board) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + case AU0828_BOARD_DVICO_FUSIONHDTV7: + dvb->frontend = dvb_attach(au8522_attach, + &hauppauge_hvr950q_config, + &dev->i2c_adap); + if (dvb->frontend != NULL) { + hauppauge_hvr950q_tunerconfig.priv = dev; + dvb_attach(xc5000_attach, dvb->frontend, + &dev->i2c_adap, + &hauppauge_hvr950q_tunerconfig); + } + break; + default: + printk(KERN_WARNING "The frontend of your DVB/ATSC card " + "isn't supported yet\n"); + break; + } + if (NULL == dvb->frontend) { + printk(KERN_ERR "%s() Frontend initialization failed\n", + __func__); + return -1; + } + + /* Put the analog decoder in standby to keep it quiet */ + au0828_call_i2c_clients(dev, TUNER_SET_STANDBY, NULL); + + if (dvb->frontend->ops.analog_ops.standby) + dvb->frontend->ops.analog_ops.standby(dvb->frontend); + + /* register everything */ + ret = dvb_register(dev); + if (ret < 0) { + if (dvb->frontend->ops.release) + dvb->frontend->ops.release(dvb->frontend); + return ret; + } + + return 0; +} + diff --git a/linux/drivers/media/video/au0828/au0828-i2c.c b/linux/drivers/media/video/au0828/au0828-i2c.c new file mode 100644 index 000000000..08d2a6080 --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828-i2c.c @@ -0,0 +1,388 @@ +/* + * Driver for the Auvitek AU0828 USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * 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/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include "compat.h" +#include "au0828.h" + +#include <media/v4l2-common.h> + +unsigned int i2c_debug; +module_param(i2c_debug, int, 0444); +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +unsigned int i2c_scan; +module_param(i2c_scan, int, 0444); +MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); + +#define I2C_WAIT_DELAY 512 +#define I2C_WAIT_RETRY 64 + +static inline int i2c_slave_did_write_ack(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, REG_201) & 0x08 ? 0 : 1; +} + +static inline int i2c_slave_did_read_ack(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, REG_201) & 0x02 ? 0 : 1; +} + +static int i2c_wait_read_ack(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (!i2c_slave_did_read_ack(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +static inline int i2c_is_read_busy(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, REG_201) & 0x01 ? 0 : 1; +} + +static int i2c_wait_read_done(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (!i2c_is_read_busy(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +static inline int i2c_is_write_done(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, REG_201) & 0x04 ? 1 : 0; +} + +static int i2c_wait_write_done(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (i2c_is_write_done(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +static inline int i2c_is_busy(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, REG_201) & 0x10 ? 1 : 0; +} + +static int i2c_wait_done(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (!i2c_is_busy(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +/* FIXME: Implement join handling correctly */ +static int i2c_sendbytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg, int joined_rlen) +{ + int i, strobe = 0; + struct au0828_dev *dev = i2c_adap->algo_data; + + dprintk(4, "%s()\n", __func__); + + au0828_write(dev, REG_2FF, 0x01); + au0828_write(dev, REG_202, 0x07); + + /* Hardware needs 8 bit addresses */ + au0828_write(dev, REG_203, msg->addr << 1); + + dprintk(4, "SEND: %02x\n", msg->addr); + + for (i = 0; i < msg->len;) { + + dprintk(4, " %02x\n", msg->buf[i]); + + au0828_write(dev, REG_205, msg->buf[i]); + + strobe++; + i++; + + if ((strobe >= 4) || (i >= msg->len)) { + + /* Strobe the byte into the bus */ + if (i < msg->len) + au0828_write(dev, REG_200, 0x41); + else + au0828_write(dev, REG_200, 0x01); + + /* Reset strobe trigger */ + strobe = 0; + + if (!i2c_wait_write_done(i2c_adap)) + return -EIO; + + } + + } + if (!i2c_wait_done(i2c_adap)) + return -EIO; + + dprintk(4, "\n"); + + return msg->len; +} + +/* FIXME: Implement join handling correctly */ +static int i2c_readbytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg, int joined) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + int i; + + dprintk(4, "%s()\n", __func__); + + au0828_write(dev, REG_2FF, 0x01); + au0828_write(dev, REG_202, 0x07); + + /* Hardware needs 8 bit addresses */ + au0828_write(dev, REG_203, msg->addr << 1); + + dprintk(4, " RECV:\n"); + + /* Deal with i2c_scan */ + if (msg->len == 0) { + au0828_write(dev, REG_200, 0x20); + if (i2c_wait_read_ack(i2c_adap)) + return -EIO; + return 0; + } + + for (i = 0; i < msg->len;) { + + i++; + + if (i < msg->len) + au0828_write(dev, REG_200, 0x60); + else + au0828_write(dev, REG_200, 0x20); + + if (!i2c_wait_read_done(i2c_adap)) + return -EIO; + + msg->buf[i-1] = au0828_read(dev, REG_209) & 0xff; + + dprintk(4, " %02x\n", msg->buf[i-1]); + } + if (!i2c_wait_done(i2c_adap)) + return -EIO; + + dprintk(4, "\n"); + + return msg->len; +} + +static int i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, int num) +{ + int i, retval = 0; + + dprintk(4, "%s(num = %d)\n", __func__, num); + + for (i = 0; i < num; i++) { + dprintk(4, "%s(num = %d) addr = 0x%02x len = 0x%x\n", + __func__, num, msgs[i].addr, msgs[i].len); + if (msgs[i].flags & I2C_M_RD) { + /* read */ + retval = i2c_readbytes(i2c_adap, &msgs[i], 0); + } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr) { + /* write then read from same address */ + retval = i2c_sendbytes(i2c_adap, &msgs[i], + msgs[i + 1].len); + if (retval < 0) + goto err; + i++; + retval = i2c_readbytes(i2c_adap, &msgs[i], 1); + } else { + /* write */ + retval = i2c_sendbytes(i2c_adap, &msgs[i], 0); + } + if (retval < 0) + goto err; + } + return num; + +err: + return retval; +} + +static int attach_inform(struct i2c_client *client) +{ + dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", + client->driver->driver.name, client->addr, client->name); + + if (!client->driver->command) + return 0; + + return 0; +} + +static int detach_inform(struct i2c_client *client) +{ + dprintk(1, "i2c detach [client=%s]\n", client->name); + + return 0; +} + +void au0828_call_i2c_clients(struct au0828_dev *dev, + unsigned int cmd, void *arg) +{ + if (dev->i2c_rc != 0) + return; + + i2c_clients_command(&dev->i2c_adap, cmd, arg); +} + +static u32 au0828_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; +} + +static struct i2c_algorithm au0828_i2c_algo_template = { + .master_xfer = i2c_xfer, + .functionality = au0828_functionality, +}; + +/* ----------------------------------------------------------------------- */ + +static struct i2c_adapter au0828_i2c_adap_template = { + .name = DRIVER_NAME, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + .owner = THIS_MODULE, +#endif + .id = I2C_HW_B_AU0828, + .algo = &au0828_i2c_algo_template, + .class = I2C_CLASS_TV_ANALOG, + .client_register = attach_inform, + .client_unregister = detach_inform, +}; + +static struct i2c_client au0828_i2c_client_template = { + .name = "au0828 internal", +}; + +static char *i2c_devs[128] = { + [0x8e >> 1] = "au8522", + [0xa0 >> 1] = "eeprom", + [0xc2 >> 1] = "tuner/xc5000", +}; + +static void do_i2c_scan(char *name, struct i2c_client *c) +{ + unsigned char buf; + int i, rc; + + for (i = 0; i < 128; i++) { + c->addr = i; + rc = i2c_master_recv(c, &buf, 0); + if (rc < 0) + continue; + printk(KERN_INFO "%s: i2c scan: found device @ 0x%x [%s]\n", + name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); + } +} + +/* init + register i2c algo-bit adapter */ +int au0828_i2c_register(struct au0828_dev *dev) +{ + dprintk(1, "%s()\n", __func__); + + memcpy(&dev->i2c_adap, &au0828_i2c_adap_template, + sizeof(dev->i2c_adap)); + memcpy(&dev->i2c_algo, &au0828_i2c_algo_template, + sizeof(dev->i2c_algo)); + memcpy(&dev->i2c_client, &au0828_i2c_client_template, + sizeof(dev->i2c_client)); + + dev->i2c_adap.dev.parent = &dev->usbdev->dev; + + strlcpy(dev->i2c_adap.name, DRIVER_NAME, + sizeof(dev->i2c_adap.name)); + + dev->i2c_algo.data = dev; + dev->i2c_adap.algo_data = dev; + i2c_set_adapdata(&dev->i2c_adap, dev); + i2c_add_adapter(&dev->i2c_adap); + + dev->i2c_client.adapter = &dev->i2c_adap; + + if (0 == dev->i2c_rc) { + printk(KERN_INFO "%s: i2c bus registered\n", DRIVER_NAME); + if (i2c_scan) + do_i2c_scan(DRIVER_NAME, &dev->i2c_client); + } else + printk(KERN_INFO "%s: i2c bus register FAILED\n", DRIVER_NAME); + + return dev->i2c_rc; +} + +int au0828_i2c_unregister(struct au0828_dev *dev) +{ + i2c_del_adapter(&dev->i2c_adap); + return 0; +} + diff --git a/linux/drivers/media/video/au0828/au0828-reg.h b/linux/drivers/media/video/au0828/au0828-reg.h new file mode 100644 index 000000000..398275508 --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828-reg.h @@ -0,0 +1,38 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * 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. + */ + +/* We'll start to rename these registers once we have a better + * understanding of their meaning. + */ +#define REG_000 0x000 +#define REG_001 0x001 +#define REG_002 0x002 +#define REG_003 0x003 + +#define REG_200 0x200 +#define REG_201 0x201 +#define REG_202 0x202 +#define REG_203 0x203 +#define REG_205 0x205 +#define REG_209 0x209 +#define REG_2FF 0x2ff + +#define REG_600 0x600 diff --git a/linux/drivers/media/video/au0828/au0828.h b/linux/drivers/media/video/au0828/au0828.h new file mode 100644 index 000000000..4b0ad1b3e --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828.h @@ -0,0 +1,137 @@ +/* + * Driver for the Auvitek AU0828 USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * 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/usb.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <media/tveeprom.h> + +/* DVB */ +#include "demux.h" +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" +#include "dvbdev.h" + +#include "au0828-reg.h" +#include "au0828-cards.h" + +#define DRIVER_NAME "au0828" +#define URB_COUNT 16 +#define URB_BUFSIZE (0xe522) + +struct au0828_board { + char *name; +}; + +struct au0828_dvb { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) + struct mutex lock; +#else + struct semaphore lock; +#endif + struct dvb_adapter adapter; + struct dvb_frontend *frontend; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + struct dvb_net net; + int feeding; +}; + +struct au0828_dev { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) + struct mutex mutex; +#else + struct semaphore mutex; +#endif + struct usb_device *usbdev; + int board; + u8 ctrlmsg[64]; + + /* I2C */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + u32 i2c_rc; + + /* Digital */ + struct au0828_dvb dvb; + + /* USB / URB Related */ + int urb_streaming; + struct urb *urbs[URB_COUNT]; + +}; + +struct au0828_buff { + struct au0828_dev *dev; + struct urb *purb; + struct list_head buff_list; +}; + +/* ----------------------------------------------------------- */ +#define au0828_read(dev, reg) au0828_readreg(dev, reg) +#define au0828_write(dev, reg, value) au0828_writereg(dev, reg, value) +#define au0828_andor(dev, reg, mask, value) \ + au0828_writereg(dev, reg, \ + (au0828_readreg(dev, reg) & ~(mask)) | ((value) & (mask))) + +#define au0828_set(dev, reg, bit) au0828_andor(dev, (reg), (bit), (bit)) +#define au0828_clear(dev, reg, bit) au0828_andor(dev, (reg), (bit), 0) + +/* ----------------------------------------------------------- */ +/* au0828-core.c */ +extern u32 au0828_read(struct au0828_dev *dev, u16 reg); +extern u32 au0828_write(struct au0828_dev *dev, u16 reg, u32 val); +extern unsigned int debug; +extern unsigned int usb_debug; +extern unsigned int bridge_debug; + +/* ----------------------------------------------------------- */ +/* au0828-cards.c */ +extern struct au0828_board au0828_boards[]; +extern struct usb_device_id au0828_usb_id_table[]; +extern const unsigned int au0828_bcount; +extern void au0828_gpio_setup(struct au0828_dev *dev); +extern int au0828_tuner_callback(void *priv, int command, int arg); +extern void au0828_card_setup(struct au0828_dev *dev); + +/* ----------------------------------------------------------- */ +/* au0828-i2c.c */ +extern int au0828_i2c_register(struct au0828_dev *dev); +extern int au0828_i2c_unregister(struct au0828_dev *dev); +extern void au0828_call_i2c_clients(struct au0828_dev *dev, + unsigned int cmd, void *arg); +extern unsigned int i2c_debug; + +/* ----------------------------------------------------------- */ +/* au0828-dvb.c */ +extern int au0828_dvb_register(struct au0828_dev *dev); +extern void au0828_dvb_unregister(struct au0828_dev *dev); + +#define dprintk(level, fmt, arg...)\ + do { if (debug & level)\ + printk(KERN_DEBUG DRIVER_NAME "/0: " fmt, ## arg);\ + } while (0) + diff --git a/linux/drivers/media/video/bt819.c b/linux/drivers/media/video/bt819.c index 9264e2cff..402bccaf7 100644 --- a/linux/drivers/media/video/bt819.c +++ b/linux/drivers/media/video/bt819.c @@ -525,7 +525,7 @@ bt819_detect_client (struct i2c_adapter *adapter, return 0; client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == 0) + if (!client) return -ENOMEM; client->addr = address; client->adapter = adapter; diff --git a/linux/drivers/media/video/bt856.c b/linux/drivers/media/video/bt856.c index 7abfad9a4..50ca987ad 100644 --- a/linux/drivers/media/video/bt856.c +++ b/linux/drivers/media/video/bt856.c @@ -312,7 +312,7 @@ bt856_detect_client (struct i2c_adapter *adapter, return 0; client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == 0) + if (!client) return -ENOMEM; client->addr = address; client->adapter = adapter; diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index c1173a94a..303422115 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -3506,6 +3506,9 @@ static int radio_release(struct inode *inode, struct file *file) struct bttv *btv = fh->btv; struct rds_command cmd; + file->private_data = NULL; + kfree(fh); + btv->radio_user--; bttv_call_i2c_clients(btv, RDS_CMD_CLOSE, &cmd); diff --git a/linux/drivers/media/video/c-qcam.c b/linux/drivers/media/video/c-qcam.c index 479ef617b..a5f5b8a4a 100644 --- a/linux/drivers/media/video/c-qcam.c +++ b/linux/drivers/media/video/c-qcam.c @@ -39,6 +39,7 @@ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include <linux/mutex.h> #endif +#include <linux/jiffies.h> #include <asm/uaccess.h> @@ -102,7 +103,8 @@ static unsigned int qcam_await_ready1(struct qcam_device *qcam, unsigned long oldjiffies = jiffies; unsigned int i; - for (oldjiffies = jiffies; (jiffies - oldjiffies) < msecs_to_jiffies(40); ) + for (oldjiffies = jiffies; + time_before(jiffies, oldjiffies + msecs_to_jiffies(40)); ) if (qcam_ready1(qcam) == value) return 0; @@ -127,7 +129,8 @@ static unsigned int qcam_await_ready2(struct qcam_device *qcam, int value) unsigned long oldjiffies = jiffies; unsigned int i; - for (oldjiffies = jiffies; (jiffies - oldjiffies) < msecs_to_jiffies(40); ) + for (oldjiffies = jiffies; + time_before(jiffies, oldjiffies + msecs_to_jiffies(40)); ) if (qcam_ready2(qcam) == value) return 0; diff --git a/linux/drivers/media/video/cx23885/Kconfig b/linux/drivers/media/video/cx23885/Kconfig index 1fd326fe4..ca5fbce3a 100644 --- a/linux/drivers/media/video/cx23885/Kconfig +++ b/linux/drivers/media/video/cx23885/Kconfig @@ -8,6 +8,7 @@ config VIDEO_CX23885 select VIDEO_TVEEPROM select VIDEO_IR select VIDEOBUF_DVB + select VIDEO_CX25840 select DVB_TUNER_MT2131 if !DVB_FE_CUSTOMISE select DVB_S5H1409 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE @@ -16,6 +17,7 @@ config VIDEO_CX23885 select TUNER_TDA8290 if !DVB_FE_CUSTOMIZE select DVB_TDA18271 if !DVB_FE_CUSTOMIZE select DVB_TUNER_XC5000 if !DVB_FE_CUSTOMIZE + select DVB_TDA10048 if !DVB_FE_CUSTOMIZE ---help--- This is a video4linux driver for Conexant 23885 based TV cards. diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c index 6f04004b7..0c25b7dd1 100644 --- a/linux/drivers/media/video/cx23885/cx23885-cards.c +++ b/linux/drivers/media/video/cx23885/cx23885-cards.c @@ -131,6 +131,14 @@ struct cx23885_board cx23885_boards[] = { .name = "Hauppauge WinTV-HVR1500", .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_HAUPPAUGE_HVR1200] = { + .name = "Hauppauge WinTV-HVR1200", + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_HAUPPAUGE_HVR1700] = { + .name = "Hauppauge WinTV-HVR1700", + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -182,6 +190,14 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x0070, .subdevice = 0x7717, .card = CX23885_BOARD_HAUPPAUGE_HVR1500, + }, { + .subvendor = 0x0070, + .subdevice = 0x71d1, + .card = CX23885_BOARD_HAUPPAUGE_HVR1200, + }, { + .subvendor = 0x0070, + .subdevice = 0x8101, + .card = CX23885_BOARD_HAUPPAUGE_HVR1700, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -236,6 +252,9 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) case 79561: /* WinTV-HVR1250 (PCIe, OEM, No IR, half height, ATSC and Basic analog */ case 79571: /* WinTV-HVR1250 (PCIe, OEM, No IR, full height, ATSC and Basic analog */ case 79671: /* WinTV-HVR1250 (PCIe, OEM, No IR, half height, ATSC and Basic analog */ + case 81519: + /* WinTV-HVR1700 (PCIe, Retail, No IR, half height, + * DVB-T and MPEG2 HW Encoder */ break; default: printk("%s: warning: unknown hauppauge model #%d\n", dev->name, tv.model); @@ -315,6 +334,38 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00040004); mdelay(20); break; + case CX23885_BOARD_HAUPPAUGE_HVR1200: + /* GPIO-0 tda10048 demodulator reset */ + /* GPIO-2 tda18271 tuner reset */ + + /* Put the parts into reset and back */ + cx_set(GP0_IO, 0x00050000); + mdelay(20); + cx_clear(GP0_IO, 0x00000005); + mdelay(20); + cx_set(GP0_IO, 0x00050005); + break; + case CX23885_BOARD_HAUPPAUGE_HVR1700: + /* GPIO-0 TDA10048 demodulator reset */ + /* GPIO-2 TDA8295A Reset */ + /* GPIO-3-10 cx23417 data0-7 */ + /* GPIO-11-14 cx23417 addr0-3 */ + /* GPIO-15-18 cx23417 READY, CS, RD, WR */ + + /* The following GPIO's are on the interna AVCore (cx25840) */ + /* GPIO-19 IR_RX */ + /* GPIO-20 IR_TX 416/DVBT Select */ + /* GPIO-21 IIS DAT */ + /* GPIO-22 IIS WCLK */ + /* GPIO-23 IIS BCLK */ + + /* Put the parts into reset and back */ + cx_set(GP0_IO, 0x00050000); + mdelay(20); + cx_clear(GP0_IO, 0x00000005); + mdelay(20); + cx_set(GP0_IO, 0x00050005); + break; } } @@ -325,6 +376,7 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: case CX23885_BOARD_HAUPPAUGE_HVR1800: + case CX23885_BOARD_HAUPPAUGE_HVR1200: /* FIXME: Implement me */ break; } @@ -354,6 +406,8 @@ void cx23885_card_setup(struct cx23885_dev *dev) break; case CX23885_BOARD_HAUPPAUGE_HVR1800: case CX23885_BOARD_HAUPPAUGE_HVR1800lp: + case CX23885_BOARD_HAUPPAUGE_HVR1200: + case CX23885_BOARD_HAUPPAUGE_HVR1700: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -370,12 +424,24 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1500Q: case CX23885_BOARD_HAUPPAUGE_HVR1800: case CX23885_BOARD_HAUPPAUGE_HVR1800lp: + case CX23885_BOARD_HAUPPAUGE_HVR1200: + case CX23885_BOARD_HAUPPAUGE_HVR1700: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; } + /* Certain boards support analog, or require the avcore to be + * loaded, ensure this happens. + */ + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1800: + case CX23885_BOARD_HAUPPAUGE_HVR1800lp: + case CX23885_BOARD_HAUPPAUGE_HVR1700: + request_module("cx25840"); + break; + } } /* ------------------------------------------------------------------ */ diff --git a/linux/drivers/media/video/cx23885/cx23885-core.c b/linux/drivers/media/video/cx23885/cx23885-core.c index 309c17586..968b31716 100644 --- a/linux/drivers/media/video/cx23885/cx23885-core.c +++ b/linux/drivers/media/video/cx23885/cx23885-core.c @@ -745,8 +745,8 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) cx23885_i2c_register(&dev->i2c_bus[0]); cx23885_i2c_register(&dev->i2c_bus[1]); cx23885_i2c_register(&dev->i2c_bus[2]); - cx23885_call_i2c_clients (&dev->i2c_bus[0], TUNER_SET_STANDBY, NULL); cx23885_card_setup(dev); + cx23885_call_i2c_clients (&dev->i2c_bus[0], TUNER_SET_STANDBY, NULL); cx23885_ir_init(dev); if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) { diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 689c54541..5923f214e 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -37,9 +37,9 @@ #include "tda18271.h" #include "lgdt330x.h" #include "xc5000.h" +#include "tda10048.h" #include "dvb-pll.h" #include "tuner-xc2028.h" -#include "tuner-xc2028-types.h" #include "tuner-simple.h" static unsigned int debug; @@ -108,6 +108,13 @@ static struct s5h1409_config hauppauge_generic_config = { .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, }; +static struct tda10048_config hauppauge_hvr1200_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_SERIAL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_200, + .inversion = TDA10048_INVERSION_ON +}; + static struct s5h1409_config hauppauge_ezqam_config = { .demod_address = 0x32 >> 1, .output_mode = S5H1409_SERIAL_OUTPUT, @@ -179,6 +186,10 @@ static struct tda18271_config hauppauge_tda18271_config = { .gate = TDA18271_GATE_ANALOG, }; +static struct tda18271_config hauppauge_hvr1200_tuner_config = { + .gate = TDA18271_GATE_ANALOG, +}; + static int cx23885_hvr1500_xc3028_callback(void *ptr, int command, int arg) { struct cx23885_tsport *port = ptr; @@ -309,7 +320,7 @@ static int dvb_register(struct cx23885_tsport *port) static struct xc2028_ctrl ctl = { .fname = "xc3028-v27.fw", .max_len = 64, - .scode_table = OREN538, + .scode_table = XC3028_FE_OREN538, }; fe = dvb_attach(xc2028_attach, @@ -318,6 +329,21 @@ static int dvb_register(struct cx23885_tsport *port) fe->ops.tuner_ops.set_config(fe, &ctl); } break; + case CX23885_BOARD_HAUPPAUGE_HVR1200: + case CX23885_BOARD_HAUPPAUGE_HVR1700: + i2c_bus = &dev->i2c_bus[0]; + port->dvb.frontend = dvb_attach(tda10048_attach, + &hauppauge_hvr1200_config, + &i2c_bus->i2c_adap); + if (port->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, port->dvb.frontend, + &dev->i2c_bus[1].i2c_adap, 0x42, + &tda829x_no_probe); + dvb_attach(tda18271_attach, port->dvb.frontend, + 0x60, &dev->i2c_bus[1].i2c_adap, + &hauppauge_hvr1200_tuner_config); + } + break; default: printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n", dev->name); diff --git a/linux/drivers/media/video/cx23885/cx23885-i2c.c b/linux/drivers/media/video/cx23885/cx23885-i2c.c index a06d50396..972c58cc9 100644 --- a/linux/drivers/media/video/cx23885/cx23885-i2c.c +++ b/linux/drivers/media/video/cx23885/cx23885-i2c.c @@ -374,6 +374,7 @@ static struct i2c_client cx23885_i2c_client_template = { }; static char *i2c_devs[128] = { + [0x10 >> 1] = "tda10048", [ 0x1c >> 1 ] = "lgdt3303", [ 0x86 >> 1 ] = "tda9887", [ 0x32 >> 1 ] = "cx24227", diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h index d66fe2484..7eaadeab6 100644 --- a/linux/drivers/media/video/cx23885/cx23885.h +++ b/linux/drivers/media/video/cx23885/cx23885.h @@ -62,6 +62,8 @@ #define CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP 4 #define CX23885_BOARD_HAUPPAUGE_HVR1500Q 5 #define CX23885_BOARD_HAUPPAUGE_HVR1500 6 +#define CX23885_BOARD_HAUPPAUGE_HVR1200 7 +#define CX23885_BOARD_HAUPPAUGE_HVR1700 8 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ diff --git a/linux/drivers/media/video/cx25840/cx25840-core.c b/linux/drivers/media/video/cx25840/cx25840-core.c index 82e7fb267..7e21143e2 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.c +++ b/linux/drivers/media/video/cx25840/cx25840-core.c @@ -1297,6 +1297,12 @@ static int cx25840_probe(struct i2c_client *client) state->id = id; state->rev = device_id; + if (state->is_cx23885) { + /* Drive GPIO2 direction and values */ + cx25840_write(client, 0x160, 0x1d); + cx25840_write(client, 0x164, 0x00); + } + return 0; } diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index fcecfeed9..0809039a5 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1410,6 +1410,10 @@ static const struct cx88_board cx88_boards[] = { }}, /* fixme: Add radio support */ .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0xe780, + }, }, [CX88_BOARD_ADSTECH_PTV_390] = { .name = "ADS Tech Instant Video PCI", diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 8559ef873..7d35590f9 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -46,7 +46,6 @@ #include "nxt200x.h" #include "cx24123.h" #include "isl6421.h" -#include "tuner-xc2028-types.h" #include "tuner-simple.h" #include "tda9887.h" @@ -820,7 +819,7 @@ static int dvb_register(struct cx8802_dev *dev) static struct xc2028_ctrl ctl = { .fname = "xc3028-v27.fw", .max_len = 64, - .scode_table = OREN538, + .scode_table = XC3028_FE_OREN538, }; fe = dvb_attach(xc2028_attach, diff --git a/linux/drivers/media/video/dpc7146.c b/linux/drivers/media/video/dpc7146.c index 258d46407..4bfb3076b 100644 --- a/linux/drivers/media/video/dpc7146.c +++ b/linux/drivers/media/video/dpc7146.c @@ -132,7 +132,7 @@ static int dpc_probe(struct saa7146_dev* dev) device_for_each_child(&dpc->i2c_adapter.dev, dpc, dpc_check_clients); /* check if all devices are present */ - if( 0 == dpc->saa7111a ) { + if (!dpc->saa7111a) { DEB_D(("dpc_v4l2.o: dpc_attach failed for this device.\n")); i2c_del_adapter(&dpc->i2c_adapter); kfree(dpc); diff --git a/linux/drivers/media/video/em28xx/Kconfig b/linux/drivers/media/video/em28xx/Kconfig index 0f7a0bd86..3d9082fa6 100644 --- a/linux/drivers/media/video/em28xx/Kconfig +++ b/linux/drivers/media/video/em28xx/Kconfig @@ -1,11 +1,13 @@ config VIDEO_EM28XX - tristate "Empia EM2800/2820/2840 USB video capture support" + tristate "Empia EM28xx USB video capture support" depends on VIDEO_DEV && I2C && INPUT select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_IR + select VIDEOBUF_VMALLOC select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO ---help--- This is a video4linux driver for Empia 28xx based TV cards. @@ -27,3 +29,14 @@ config VIDEO_EM28XX_ALSA To compile this driver as a module, choose M here: the module will be called em28xx-alsa +config VIDEO_EM28XX_DVB + tristate "DVB/ATSC Support for em28xx based TV cards" + depends on VIDEO_EM28XX && DVB_CORE + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select VIDEOBUF_DVB + select FW_LOADER + ---help--- + This adds support for DVB cards based on the + Empiatech em28xx chips. + diff --git a/linux/drivers/media/video/em28xx/Makefile b/linux/drivers/media/video/em28xx/Makefile index 092455099..3d1c3cc33 100644 --- a/linux/drivers/media/video/em28xx/Makefile +++ b/linux/drivers/media/video/em28xx/Makefile @@ -5,6 +5,7 @@ em28xx-alsa-objs := em28xx-audio.o obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o +obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o EXTRA_CFLAGS += -Idrivers/media/video EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 06a4e4a2b..bdf64cd9c 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -37,7 +37,6 @@ #include <media/v4l2-common.h> #include "em28xx.h" -#include "tuner-xc2028.h" static int tuner = -1; module_param(tuner, int, 0444); @@ -53,26 +52,6 @@ struct em28xx_hash_table { unsigned int tuner; }; -/* Boards supported by driver */ - -#define EM2800_BOARD_UNKNOWN 0 -#define EM2820_BOARD_UNKNOWN 1 -#define EM2820_BOARD_TERRATEC_CINERGY_250 2 -#define EM2820_BOARD_PINNACLE_USB_2 3 -#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4 -#define EM2820_BOARD_MSI_VOX_USB_2 5 -#define EM2800_BOARD_TERRATEC_CINERGY_200 6 -#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7 -#define EM2800_BOARD_KWORLD_USB2800 8 -#define EM2820_BOARD_PINNACLE_DVC_90 9 -#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10 -#define EM2880_BOARD_TERRATEC_HYBRID_XS 11 -#define EM2820_BOARD_KWORLD_PVRTV2800RF 12 -#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13 -#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14 -#define EM2800_BOARD_VGEAR_POCKETTV 15 -#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 16 - struct em28xx_board em28xx_boards[] = { [EM2800_BOARD_UNKNOWN] = { .name = "Unknown EM2800 video grabber", @@ -201,6 +180,7 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .mts_firmware = 1, .has_12mhz_i2s = 1, + .has_dvb = 1, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -215,9 +195,6 @@ struct em28xx_board em28xx_boards[] = { .vmux = TVP5150_SVIDEO, .amux = 1, } }, - - /* gpio's 4, 1, 0 */ - .analog_gpio = 0x003d2d, }, [EM2880_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Hybrid XS", @@ -454,7 +431,36 @@ struct usb_device_id em28xx_id_table [] = { }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); -/* EEPROM hash table for devices with generic USB IDs */ +/* + * Reset sequences for analog/digital modes + */ + +/* Board Hauppauge WinTV HVR 900 analog */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = { + {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10}, + {0x05, 0xff, 0x10, 10}, + { -1, -1, -1, -1}, +}; + +/* Board Hauppauge WinTV HVR 900 digital */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = { + {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x04, 0x0f, 10}, + {EM2880_R04_GPO, 0x0c, 0x0f, 10}, + { -1, -1, -1, -1}, +}; + +/* Board Hauppauge WinTV HVR 900 tuner_callback */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900_tuner_callback[] = { + {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, + {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10}, + {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* + * EEPROM hash table for devices with generic USB IDs + */ static struct em28xx_hash_table em28xx_eeprom_hash [] = { /* P/N: SA 60002070465 Tuner: TVF7533-MF */ {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF}, @@ -466,79 +472,113 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = { {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, }; +int em28xx_tuner_callback(void *ptr, int command, int arg) +{ + int rc = 0; + struct em28xx *dev = ptr; + + if (dev->tuner_type != TUNER_XC2028) + return 0; + + if (command != XC2028_TUNER_RESET) + return 0; + + if (dev->mode == EM28XX_ANALOG_MODE) + rc = em28xx_gpio_set(dev, dev->tun_analog_gpio); + else + rc = em28xx_gpio_set(dev, dev->tun_digital_gpio); + + return rc; +} +EXPORT_SYMBOL_GPL(em28xx_tuner_callback); + +static void em28xx_set_model(struct em28xx *dev) +{ + dev->is_em2800 = em28xx_boards[dev->model].is_em2800; + dev->has_msp34xx = em28xx_boards[dev->model].has_msp34xx; + dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; + dev->decoder = em28xx_boards[dev->model].decoder; + dev->video_inputs = em28xx_boards[dev->model].vchannels; + dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s; + dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480; + dev->has_dvb = em28xx_boards[dev->model].has_dvb; +} + /* Since em28xx_pre_card_setup() requires a proper dev->model, * this won't work for boards with generic PCI IDs */ void em28xx_pre_card_setup(struct em28xx *dev) { + int rc; + + rc = em28xx_read_reg(dev, EM2880_R04_GPO); + if (rc >= 0) + dev->reg_gpo = rc; + + dev->wait_after_write = 5; + rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); + if (rc > 0) { + switch (rc) { + case CHIP_ID_EM2883: + em28xx_info("chip ID is em2882/em2883\n"); + dev->wait_after_write = 0; + break; + default: + em28xx_info("em28xx chip ID = %d\n", rc); + } + } + em28xx_set_model(dev); + /* request some modules */ switch (dev->model) { case EM2880_BOARD_TERRATEC_PRODIGY_XS: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_TERRATEC_HYBRID_XS: - em28xx_write_regs(dev, XCLK_REG, "\x27", 1); - em28xx_write_regs(dev, I2C_CLK_REG, "\x40", 1); - em28xx_write_regs(dev, 0x08, "\xff", 1); - em28xx_write_regs(dev, 0x04, "\x00", 1); - msleep(100); - em28xx_write_regs(dev, 0x04, "\x08", 1); - msleep(100); - em28xx_write_regs(dev, 0x08, "\xff", 1); - msleep(50); - em28xx_write_regs(dev, 0x08, "\x2d", 1); + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); msleep(50); - em28xx_write_regs(dev, 0x08, "\x3d", 1); + + /* Sets GPO/GPIO sequences for this device */ + dev->analog_gpio = hauppauge_wintv_hvr_900_analog; + dev->digital_gpio = hauppauge_wintv_hvr_900_digital; + dev->tun_analog_gpio = hauppauge_wintv_hvr_900_tuner_callback; + dev->tun_digital_gpio = hauppauge_wintv_hvr_900_tuner_callback; + break; } + + em28xx_gpio_set(dev, dev->tun_analog_gpio); + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); + + /* Unlock device */ + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); } -static int em28xx_tuner_callback(void *ptr, int command, int arg) +static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) { - int rc = 0; - struct em28xx *dev = ptr; + memset(ctl, 0, sizeof(*ctl)); - if (dev->tuner_type != TUNER_XC2028) - return 0; - - switch (command) { - case XC2028_TUNER_RESET: - { - /* GPIO and initialization codes for analog TV and radio - This code should be complemented for DTV, since reset - codes are different. - */ - - dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); - dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1); - - if (dev->analog_gpio) { - char gpio0 = dev->analog_gpio & 0xff; - char gpio1 = (dev->analog_gpio >> 8) & 0xff; - char gpio4 = dev->analog_gpio >> 24; - - if (gpio4) { - dev->em28xx_write_regs(dev, 0x04, &gpio4, 1); - msleep(140); - } - - msleep(6); - dev->em28xx_write_regs(dev, 0x08, &gpio0, 1); - msleep(10); - dev->em28xx_write_regs(dev, 0x08, &gpio1, 1); - msleep(5); - } + ctl->fname = XC2028_DEFAULT_FIRMWARE; + ctl->max_len = 64; + ctl->mts = em28xx_boards[dev->model].mts_firmware; + switch (dev->model) { + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + ctl->demod = XC3028_FE_ZARLINK456; break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + /* FIXME: Better to specify the needed IF */ + ctl->demod = XC3028_FE_DEFAULT; + break; + default: + ctl->demod = XC3028_FE_OREN538; } - } - return rc; } static void em28xx_config_tuner(struct em28xx *dev) { struct v4l2_priv_tun_config xc2028_cfg; - struct xc2028_ctrl ctl; struct tuner_setup tun_setup; struct v4l2_frequency f; @@ -553,11 +593,9 @@ static void em28xx_config_tuner(struct em28xx *dev) em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup); if (dev->tuner_type == TUNER_XC2028) { - memset(&ctl, 0, sizeof(ctl)); + struct xc2028_ctrl ctl; - ctl.fname = XC2028_DEFAULT_FIRMWARE; - ctl.max_len = 64; - ctl.mts = em28xx_boards[dev->model].mts_firmware; + em28xx_setup_xc3028(dev, &ctl); xc2028_cfg.tuner = TUNER_XC2028; xc2028_cfg.priv = &ctl; @@ -655,19 +693,6 @@ static int em28xx_hint_board(struct em28xx *dev) return -1; } - -static void em28xx_set_model(struct em28xx *dev) -{ - dev->is_em2800 = em28xx_boards[dev->model].is_em2800; - dev->has_msp34xx = em28xx_boards[dev->model].has_msp34xx; - dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; - dev->decoder = em28xx_boards[dev->model].decoder; - dev->video_inputs = em28xx_boards[dev->model].vchannels; - dev->analog_gpio = em28xx_boards[dev->model].analog_gpio; - dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s; - dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480; -} - /* ----------------------------------------------------------------------- */ void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir) { diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 97635abb9..393425487 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -53,6 +53,12 @@ static int alt = EM28XX_PINOUT; module_param(alt, int, 0644); MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); +/* FIXME */ +#define em28xx_isocdbg(fmt, arg...) do {\ + if (core_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + /* * em28xx_read_reg_req() * reads data from the usb device specifying bRequest @@ -71,11 +77,11 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0000, reg, buf, len, HZ); - if (reg_debug){ + if (reg_debug) { printk(ret < 0 ? " failed!\n" : "%02x values: ", ret); - for (byte = 0; byte < len; byte++) { + for (byte = 0; byte < len; byte++) printk(" %02x", (unsigned char)buf[byte]); - } + printk("\n"); } @@ -128,7 +134,10 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, unsigned char *bufs; if (dev->state & DEV_DISCONNECTED) - return(-ENODEV); + return -ENODEV; + + if (len < 1) + return -EINVAL; bufs = kmalloc(len, GFP_KERNEL); @@ -137,8 +146,8 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, if (reg_debug) { int i; for (i = 0; i < len; ++i) - printk (" %02x", (unsigned char)buf[i]); - printk ("\n"); + printk(" %02x", (unsigned char)buf[i]); + printk("\n"); } if (!bufs) @@ -147,14 +156,32 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0000, reg, bufs, len, HZ); - msleep(5); /* FIXME: magic number */ + if (dev->wait_after_write) + msleep(dev->wait_after_write); + kfree(bufs); return ret; } int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len) { - return em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len); + int rc; + + rc = em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len); + + /* Stores GPO/GPIO values at the cache, if changed + Only write values should be stored, since input on a GPIO + register will return the input bits. + Not sure what happens on reading GPO register. + */ + if (rc >= 0) { + if (reg == EM2880_R04_GPO) + dev->reg_gpo = buf[0]; + else if (reg == EM28XX_R08_GPIO) + dev->reg_gpio = buf[0]; + } + + return rc; } /* @@ -167,9 +194,20 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, { int oldval; u8 newval; - if ((oldval = em28xx_read_reg(dev, reg)) < 0) + + /* Uses cache for gpo/gpio registers */ + if (reg == EM2880_R04_GPO) + oldval = dev->reg_gpo; + else if (reg == EM28XX_R08_GPIO) + oldval = dev->reg_gpio; + else + oldval = em28xx_read_reg(dev, reg); + + if (oldval < 0) return oldval; + newval = (((u8) oldval) & ~bitmask) | (val & bitmask); + return em28xx_write_regs(dev, reg, &newval, 1); } @@ -181,20 +219,26 @@ static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val) { int ret, i; u8 addr = reg & 0x7f; - if ((ret = em28xx_write_regs(dev, AC97LSB_REG, val, 2)) < 0) + + ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, val, 2); + if (ret < 0) return ret; - if ((ret = em28xx_write_regs(dev, AC97ADDR_REG, &addr, 1)) < 0) + + ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1); + if (ret < 0) return ret; /* Wait up to 50 ms for AC97 command to complete */ for (i = 0; i < 10; i++) { - if ((ret = em28xx_read_reg(dev, AC97BUSY_REG)) < 0) + ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY); + if (ret < 0) return ret; + if (!(ret & 0x01)) return 0; msleep(5); } - em28xx_warn ("AC97 command still being executed: not handled properly!\n"); + em28xx_warn("AC97 command still being executed: not handled properly!\n"); return 0; } @@ -212,7 +256,7 @@ static int em28xx_set_audio_source(struct em28xx *dev) else input = EM2800_AUDIO_SRC_TUNER; - ret = em28xx_write_regs(dev, EM2800_AUDIOSRC_REG, &input, 1); + ret = em28xx_write_regs(dev, EM2800_R08_AUDIOSRC, &input, 1); if (ret < 0) return ret; } @@ -238,7 +282,7 @@ static int em28xx_set_audio_source(struct em28xx *dev) } } - ret = em28xx_write_reg_bits(dev, AUDIOSRC_REG, input, 0xc0); + ret = em28xx_write_reg_bits(dev, EM28XX_R0E_AUDIOSRC, input, 0xc0); if (ret < 0) return ret; msleep(5); @@ -246,11 +290,11 @@ static int em28xx_set_audio_source(struct em28xx *dev) /* Sets AC97 mixer registers This is seems to be needed, even for non-ac97 configs */ - ret = em28xx_write_ac97(dev, VIDEO_AC97, video); + ret = em28xx_write_ac97(dev, EM28XX_R14_VIDEO_AC97, video); if (ret < 0) return ret; - ret = em28xx_write_ac97(dev, LINE_IN_AC97, line); + ret = em28xx_write_ac97(dev, EM28XX_R10_LINE_IN_AC97, line); return ret; } @@ -266,7 +310,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) /* Mute */ s[1] |= 0x80; - ret = em28xx_write_ac97(dev, MASTER_AC97, s); + ret = em28xx_write_ac97(dev, EM28XX_R02_MASTER_AC97, s); if (ret < 0) return ret; @@ -277,7 +321,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) if (!dev->mute) xclk |= 0x80; - ret = em28xx_write_reg_bits(dev, XCLK_REG, xclk, 0xa7); + ret = em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, xclk, 0xa7); if (ret < 0) return ret; msleep(10); @@ -288,7 +332,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) /* Unmute device */ if (!dev->mute) s[1] &= ~0x80; - ret = em28xx_write_ac97(dev, MASTER_AC97, s); + ret = em28xx_write_ac97(dev, EM28XX_R02_MASTER_AC97, s); return ret; } @@ -296,50 +340,68 @@ EXPORT_SYMBOL_GPL(em28xx_audio_analog_set); int em28xx_colorlevels_set_default(struct em28xx *dev) { - em28xx_write_regs(dev, YGAIN_REG, "\x10", 1); /* contrast */ - em28xx_write_regs(dev, YOFFSET_REG, "\x00", 1); /* brightness */ - em28xx_write_regs(dev, UVGAIN_REG, "\x10", 1); /* saturation */ - em28xx_write_regs(dev, UOFFSET_REG, "\x00", 1); - em28xx_write_regs(dev, VOFFSET_REG, "\x00", 1); - em28xx_write_regs(dev, SHARPNESS_REG, "\x00", 1); - - em28xx_write_regs(dev, GAMMA_REG, "\x20", 1); - em28xx_write_regs(dev, RGAIN_REG, "\x20", 1); - em28xx_write_regs(dev, GGAIN_REG, "\x20", 1); - em28xx_write_regs(dev, BGAIN_REG, "\x20", 1); - em28xx_write_regs(dev, ROFFSET_REG, "\x00", 1); - em28xx_write_regs(dev, GOFFSET_REG, "\x00", 1); - return em28xx_write_regs(dev, BOFFSET_REG, "\x00", 1); + em28xx_write_regs(dev, EM28XX_R20_YGAIN, "\x10", 1); /* contrast */ + em28xx_write_regs(dev, EM28XX_R21_YOFFSET, "\x00", 1); /* brightness */ + em28xx_write_regs(dev, EM28XX_R22_UVGAIN, "\x10", 1); /* saturation */ + em28xx_write_regs(dev, EM28XX_R23_UOFFSET, "\x00", 1); + em28xx_write_regs(dev, EM28XX_R24_VOFFSET, "\x00", 1); + em28xx_write_regs(dev, EM28XX_R25_SHARPNESS, "\x00", 1); + + em28xx_write_regs(dev, EM28XX_R14_GAMMA, "\x20", 1); + em28xx_write_regs(dev, EM28XX_R15_RGAIN, "\x20", 1); + em28xx_write_regs(dev, EM28XX_R16_GGAIN, "\x20", 1); + em28xx_write_regs(dev, EM28XX_R17_BGAIN, "\x20", 1); + em28xx_write_regs(dev, EM28XX_R18_ROFFSET, "\x00", 1); + em28xx_write_regs(dev, EM28XX_R19_GOFFSET, "\x00", 1); + return em28xx_write_regs(dev, EM28XX_R1A_BOFFSET, "\x00", 1); } int em28xx_capture_start(struct em28xx *dev, int start) { - int ret; + int rc; /* FIXME: which is the best order? */ /* video registers are sampled by VREF */ - if ((ret = em28xx_write_reg_bits(dev, USBSUSP_REG, start ? 0x10 : 0x00, - 0x10)) < 0) - return ret; + rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP, + start ? 0x10 : 0x00, 0x10); + if (rc < 0) + return rc; + + if (!start) { + /* disable video capture */ + rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x27", 1); + return rc; + } + /* enable video capture */ - return em28xx_write_regs(dev, VINENABLE_REG, start ? "\x67" : "\x27", 1); + rc = em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); + + if (dev->mode == EM28XX_ANALOG_MODE) + rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x67", 1); + else + rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x37", 1); + + msleep(6); + + return rc; } int em28xx_outfmt_set_yuv422(struct em28xx *dev) { - em28xx_write_regs(dev, OUTFMT_REG, "\x34", 1); - em28xx_write_regs(dev, VINMODE_REG, "\x10", 1); - return em28xx_write_regs(dev, VINCTRL_REG, "\x11", 1); + em28xx_write_regs(dev, EM28XX_R27_OUTFMT, "\x34", 1); + em28xx_write_regs(dev, EM28XX_R10_VINMODE, "\x10", 1); + return em28xx_write_regs(dev, EM28XX_R11_VINCTRL, "\x11", 1); } static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, u8 ymin, u8 ymax) { - em28xx_coredbg("em28xx Scale: (%d,%d)-(%d,%d)\n", xmin, ymin, xmax, ymax); + em28xx_coredbg("em28xx Scale: (%d,%d)-(%d,%d)\n", + xmin, ymin, xmax, ymax); - em28xx_write_regs(dev, XMIN_REG, &xmin, 1); - em28xx_write_regs(dev, XMAX_REG, &xmax, 1); - em28xx_write_regs(dev, YMIN_REG, &ymin, 1); - return em28xx_write_regs(dev, YMAX_REG, &ymax, 1); + em28xx_write_regs(dev, EM28XX_R28_XMIN, &xmin, 1); + em28xx_write_regs(dev, EM28XX_R29_XMAX, &xmax, 1); + em28xx_write_regs(dev, EM28XX_R2A_YMIN, &ymin, 1); + return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1); } static int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, @@ -349,34 +411,36 @@ static int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, u8 cheight = height; u8 overflow = (height >> 7 & 0x02) | (width >> 8 & 0x01); - em28xx_coredbg("em28xx Area Set: (%d,%d)\n", (width | (overflow & 2) << 7), + em28xx_coredbg("em28xx Area Set: (%d,%d)\n", + (width | (overflow & 2) << 7), (height | (overflow & 1) << 8)); - em28xx_write_regs(dev, HSTART_REG, &hstart, 1); - em28xx_write_regs(dev, VSTART_REG, &vstart, 1); - em28xx_write_regs(dev, CWIDTH_REG, &cwidth, 1); - em28xx_write_regs(dev, CHEIGHT_REG, &cheight, 1); - return em28xx_write_regs(dev, OFLOW_REG, &overflow, 1); + em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1); + em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1); + em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1); + em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1); + return em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1); } static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) { u8 mode; /* the em2800 scaler only supports scaling down to 50% */ - if(dev->is_em2800) + if (dev->is_em2800) mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00); else { u8 buf[2]; buf[0] = h; buf[1] = h >> 8; - em28xx_write_regs(dev, HSCALELOW_REG, (char *)buf, 2); + em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2); buf[0] = v; buf[1] = v >> 8; - em28xx_write_regs(dev, VSCALELOW_REG, (char *)buf, 2); - /* it seems that both H and V scalers must be active to work correctly */ + em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2); + /* it seems that both H and V scalers must be active + to work correctly */ mode = (h || v)? 0x30: 0x00; } - return em28xx_write_reg_bits(dev, COMPR_REG, mode, 0x30); + return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30); } /* FIXME: this only function read values from dev */ @@ -426,10 +490,246 @@ int em28xx_set_alternate(struct em28xx *dev) dev->alt, dev->max_pkt_size); errCode = usb_set_interface(dev->udev, 0, dev->alt); if (errCode < 0) { - em28xx_errdev ("cannot change alternate number to %d (error=%i)\n", + em28xx_errdev("cannot change alternate number to %d (error=%i)\n", dev->alt, errCode); return errCode; } } return 0; } + +int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio) +{ + int rc = 0; + + if (!gpio) + return rc; + + dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); + if (dev->mode == EM28XX_ANALOG_MODE) + dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1); + else + dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x37", 1); + msleep(6); + + /* Send GPIO reset sequences specified at board entry */ + while (gpio->sleep >= 0) { + if (gpio->reg >= 0) { + rc = em28xx_write_reg_bits(dev, + gpio->reg, + gpio->val, + gpio->mask); + if (rc < 0) + return rc; + } + if (gpio->sleep > 0) + msleep(gpio->sleep); + + gpio++; + } + return rc; +} + +int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode) +{ + if (dev->mode == set_mode) + return 0; + + if (set_mode == EM28XX_MODE_UNDEFINED) { + dev->mode = set_mode; + return 0; + } + +#if 0 + /* Resource is locked */ + if (dev->mode != EM28XX_MODE_UNDEFINED) + return -EINVAL; +#endif + dev->mode = set_mode; + + if (dev->mode == EM28XX_DIGITAL_MODE) + return em28xx_gpio_set(dev, dev->digital_gpio); + else + return em28xx_gpio_set(dev, dev->analog_gpio); +} +EXPORT_SYMBOL_GPL(em28xx_set_mode); + +/* ------------------------------------------------------------------ + URB control + ------------------------------------------------------------------*/ + +/* + * IRQ callback, called by URB callback + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void em28xx_irq_callback(struct urb *urb, struct pt_regs *regs) +#else +static void em28xx_irq_callback(struct urb *urb) +#endif +{ + struct em28xx_dmaqueue *dma_q = urb->context; + struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + int rc, i; + + /* Copy data from URB */ + spin_lock(&dev->slock); + rc = dev->isoc_ctl.isoc_copy(dev, urb); + spin_unlock(&dev->slock); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + urb->status = 0; + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) { + em28xx_isocdbg("urb resubmit failed (error=%i)\n", + urb->status); + } +} + +/* + * Stop and Deallocate URBs + */ +void em28xx_uninit_isoc(struct em28xx *dev) +{ + struct urb *urb; + int i; + + em28xx_isocdbg("em28xx: called em28xx_uninit_isoc\n"); + + dev->isoc_ctl.nfields = -1; + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = dev->isoc_ctl.urb[i]; + if (urb) { + usb_kill_urb(urb); + usb_unlink_urb(urb); + if (dev->isoc_ctl.transfer_buffer[i]) { + usb_buffer_free(dev->udev, + urb->transfer_buffer_length, + dev->isoc_ctl.transfer_buffer[i], + urb->transfer_dma); + } + usb_free_urb(urb); + dev->isoc_ctl.urb[i] = NULL; + } + dev->isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + + dev->isoc_ctl.urb = NULL; + dev->isoc_ctl.transfer_buffer = NULL; + dev->isoc_ctl.num_bufs = 0; + + em28xx_capture_start(dev, 0); +} +EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); + +/* + * Allocate URBs and start IRQ + */ +int em28xx_init_isoc(struct em28xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) +{ + struct em28xx_dmaqueue *dma_q = &dev->vidq; + int i; + int sb_size, pipe; + struct urb *urb; + int j, k; + int rc; + + em28xx_isocdbg("em28xx: called em28xx_prepare_isoc\n"); + + /* De-allocates all pending stuff */ + em28xx_uninit_isoc(dev); + + dev->isoc_ctl.isoc_copy = isoc_copy; + dev->isoc_ctl.num_bufs = num_bufs; + + dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + em28xx_errdev("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + em28xx_errdev("cannot allocate memory for usbtransfer\n"); + kfree(dev->isoc_ctl.urb); + return -ENOMEM; + } + + dev->isoc_ctl.max_pkt_size = max_pkt_size; + dev->isoc_ctl.buf = NULL; + + sb_size = max_packets * dev->isoc_ctl.max_pkt_size; + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); + em28xx_uninit_isoc(dev); + return -ENOMEM; + } + dev->isoc_ctl.urb[i] = urb; + + dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->udev, + sb_size, GFP_KERNEL, &urb->transfer_dma); + if (!dev->isoc_ctl.transfer_buffer[i]) { + em28xx_err("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt()?" while in int":""); + em28xx_uninit_isoc(dev); + return -ENOMEM; + } + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + + /* FIXME: this is a hack - should be + 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK' + should also be using 'desc.bInterval' + */ + pipe = usb_rcvisocpipe(dev->udev, + dev->mode == EM28XX_ANALOG_MODE ? 0x82 : 0x84); + + usb_fill_int_urb(urb, dev->udev, pipe, + dev->isoc_ctl.transfer_buffer[i], sb_size, + em28xx_irq_callback, dma_q, 1); + + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP; + + k = 0; + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + dev->isoc_ctl.max_pkt_size; + k += dev->isoc_ctl.max_pkt_size; + } + } + + init_waitqueue_head(&dma_q->wq); + + em28xx_capture_start(dev, 1); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + if (rc) { + em28xx_err("submit of urb %i failed (error=%i)\n", i, + rc); + em28xx_uninit_isoc(dev); + return rc; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(em28xx_init_isoc); diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c new file mode 100644 index 000000000..5c9941d92 --- /dev/null +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -0,0 +1,506 @@ +/* + DVB device driver for em28xx + + (c) 2008 Mauro Carvalho Chehab <mchehab@infradead.org> + + (c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com> + - Fixes for the driver to properly work with HVR-950 + + (c) 2008 Aidan Thornton <makosoft@googlemail.com> + + Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by: + (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> + (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] + + 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. + */ + +#include <linux/kernel.h> +#include <linux/usb.h> +#include "compat.h" + +#include "em28xx.h" +#include <media/v4l2-common.h> +#include <media/videobuf-vmalloc.h> + +#include "lgdt330x.h" +#include "zl10353.h" + +MODULE_DESCRIPTION("driver for em28xx based DVB cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); +MODULE_LICENSE("GPL"); + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages [dvb]"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define dprintk(level, fmt, arg...) do { \ +if (debug >= level) \ + printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \ +} while (0) + +#define EM28XX_DVB_NUM_BUFS 5 +#define EM28XX_DVB_MAX_PACKETSIZE 564 +#define EM28XX_DVB_MAX_PACKETS 64 + +struct em28xx_dvb { + struct dvb_frontend *frontend; + + /* feed count management */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) + struct mutex lock; +#else + struct semaphore lock; +#endif + int nfeeds; + + /* general boilerplate stuff */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + struct dvb_adapter adapter; +#else + struct dvb_adapter *adapter; +#endif + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + struct dvb_net net; +}; + + +static inline void print_err_status(struct em28xx *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(1, "URB status %d [%s].\n", status, errmsg); + } else { + dprintk(1, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static inline int dvb_isoc_copy(struct em28xx *dev, struct urb *urb) +{ + int i; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + } + + return 0; +} + +static int start_streaming(struct em28xx_dvb *dvb) +{ + int rc; + struct em28xx *dev = dvb->adapter.priv; + + usb_set_interface(dev->udev, 0, 1); + rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); + if (rc < 0) + return rc; + + return em28xx_init_isoc(dev, EM28XX_DVB_MAX_PACKETS, + EM28XX_DVB_NUM_BUFS, EM28XX_DVB_MAX_PACKETSIZE, + dvb_isoc_copy); +} + +static int stop_streaming(struct em28xx_dvb *dvb) +{ + struct em28xx *dev = dvb->adapter.priv; + + em28xx_uninit_isoc(dev); + + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + + return 0; +} + +static int start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct em28xx_dvb *dvb = demux->priv; + int rc, ret; + + if (!demux->dmx.frontend) + return -EINVAL; + + mutex_lock(&dvb->lock); + dvb->nfeeds++; + rc = dvb->nfeeds; + + if (dvb->nfeeds == 1) { + ret = start_streaming(dvb); + if (ret < 0) + rc = ret; + } + + mutex_unlock(&dvb->lock); + return rc; +} + +static int stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct em28xx_dvb *dvb = demux->priv; + int err = 0; + + mutex_lock(&dvb->lock); + dvb->nfeeds--; + + if (0 == dvb->nfeeds) + err = stop_streaming(dvb); + + mutex_unlock(&dvb->lock); + return err; +} + + + +/* ------------------------------------------------------------------ */ +static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct em28xx *dev = fe->dvb->priv; + + if (acquire) + return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); + else + return em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); +} + +/* ------------------------------------------------------------------ */ + +static struct lgdt330x_config em2880_lgdt3303_dev = { + .demod_address = 0x0e, + .demod_chip = LGDT3303, +}; + +static struct zl10353_config em28xx_zl10353_with_xc3028 = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .parallel_ts = 1, + .if2 = 45600, +}; + +/* ------------------------------------------------------------------ */ + +static int attach_xc3028(u8 addr, struct em28xx *dev) +{ + struct dvb_frontend *fe; + struct xc2028_config cfg; + + memset(&cfg, 0, sizeof(cfg)); + cfg.i2c_adap = &dev->i2c_adap; + cfg.i2c_addr = addr; + cfg.callback = em28xx_tuner_callback; + + if (!dev->dvb->frontend) { + printk(KERN_ERR "%s/2: dvb frontend not attached. " + "Can't attach xc3028\n", + dev->name); + return -EINVAL; + } + + fe = dvb_attach(xc2028_attach, dev->dvb->frontend, &cfg); + if (!fe) { + printk(KERN_ERR "%s/2: xc3028 attach failed\n", + dev->name); + dvb_frontend_detach(dev->dvb->frontend); + dvb_unregister_frontend(dev->dvb->frontend); + dev->dvb->frontend = NULL; + return -EINVAL; + } + + printk(KERN_INFO "%s/2: xc3028 attached\n", dev->name); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +int register_dvb(struct em28xx_dvb *dvb, + struct module *module, + struct em28xx *dev, + struct device *device) +{ + int result; + + mutex_init(&dvb->lock); + + /* register adapter */ + result = dvb_register_adapter(&dvb->adapter, dev->name, module, device, + adapter_nr); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n", + dev->name, result); + goto fail_adapter; + } + + /* Ensure all frontends negotiate bus access */ + dvb->frontend->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb->adapter.priv = dev; + + /* register frontend */ + result = dvb_register_frontend(&dvb->adapter, dvb->frontend); +#else + dvb->adapter->priv = dev; + + /* register frontend */ + result = dvb_register_frontend(dvb->adapter, dvb->frontend); +#endif + if (result < 0) { + printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n", + dev->name, result); + goto fail_frontend; + } + + /* register demux stuff */ + dvb->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dvb; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = start_feed; + dvb->demux.stop_feed = stop_feed; + + result = dvb_dmx_init(&dvb->demux); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", + dev->name, result); + goto fail_dmx; + } + + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = &dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); +#else + result = dvb_dmxdev_init(&dvb->dmxdev, dvb->adapter); +#endif + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n", + dev->name, result); + goto fail_dmxdev; + } + + dvb->fe_hw.source = DMX_FRONTEND_0; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", + dev->name, result); + goto fail_fe_hw; + } + + dvb->fe_mem.source = DMX_MEMORY_FE; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); + if (result < 0) { + printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", + dev->name, result); + goto fail_fe_mem; + } + + result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n", + dev->name, result); + goto fail_fe_conn; + } + + /* register network adapter */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); +#else + dvb_net_init(dvb->adapter, &dvb->net, &dvb->demux.dmx); +#endif + return 0; + +fail_fe_conn: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); +fail_fe_mem: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); +fail_fe_hw: + dvb_dmxdev_release(&dvb->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb->demux); +fail_dmx: + dvb_unregister_frontend(dvb->frontend); +fail_frontend: + dvb_frontend_detach(dvb->frontend); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb_unregister_adapter(&dvb->adapter); +#else + dvb_unregister_adapter(dvb->adapter); +#endif +fail_adapter: + return result; +} + +static void unregister_dvb(struct em28xx_dvb *dvb) +{ + dvb_net_release(&dvb->net); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb_unregister_adapter(&dvb->adapter); +#else + dvb_unregister_adapter(dvb->adapter); +#endif +} + + +static int dvb_init(struct em28xx *dev) +{ + int result = 0; + struct em28xx_dvb *dvb; + + dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); + + if (dvb == NULL) { + printk(KERN_INFO "em28xx_dvb: memory allocation failed\n"); + return -ENOMEM; + } + dev->dvb = dvb; + + em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); + /* init frontend */ + switch (dev->model) { + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + dvb->frontend = dvb_attach(lgdt330x_attach, + &em2880_lgdt3303_dev, + &dev->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + dvb->frontend = dvb_attach(zl10353_attach, + &em28xx_zl10353_with_xc3028, + &dev->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; + default: + printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" + " isn't supported yet\n", + dev->name); + break; + } + if (NULL == dvb->frontend) { + printk(KERN_ERR + "%s/2: frontend initialization failed\n", + dev->name); + result = -EINVAL; + goto out_free; + } + + /* register everything */ + result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev); + + if (result < 0) + goto out_free; + + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + printk(KERN_INFO "Successfully loaded em28xx-dvb\n"); + return 0; + +out_free: + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + kfree(dvb); + dev->dvb = NULL; + return result; +} + +static int dvb_fini(struct em28xx *dev) +{ + if (dev->dvb) { + unregister_dvb(dev->dvb); + dev->dvb = NULL; + } + + return 0; +} + +static struct em28xx_ops dvb_ops = { + .id = EM28XX_DVB, + .name = "Em28xx dvb Extension", + .init = dvb_init, + .fini = dvb_fini, +}; + +static int __init em28xx_dvb_register(void) +{ + return em28xx_register_extension(&dvb_ops); +} + +static void __exit em28xx_dvb_unregister(void) +{ + em28xx_unregister_extension(&dvb_ops); +} + +module_init(em28xx_dvb_register); +module_exit(em28xx_dvb_unregister); diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c index f1e52e5d1..c22e73019 100644 --- a/linux/drivers/media/video/em28xx/em28xx-i2c.c +++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c @@ -41,11 +41,21 @@ static unsigned int i2c_debug; module_param(i2c_debug, int, 0644); MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); -#define dprintk1(lvl,fmt, args...) if (i2c_debug>=lvl) do {\ - printk(fmt, ##args); } while (0) -#define dprintk2(lvl,fmt, args...) if (i2c_debug>=lvl) do{ \ - printk(KERN_DEBUG "%s at %s: " fmt, \ - dev->name, __func__ , ##args); } while (0) + +#define dprintk1(lvl, fmt, args...) \ +do { \ + if (i2c_debug >= lvl) { \ + printk(fmt, ##args); \ + } \ +} while (0) + +#define dprintk2(lvl, fmt, args...) \ +do { \ + if (i2c_debug >= lvl) { \ + printk(KERN_DEBUG "%s at %s: " fmt, \ + dev->name, __func__ , ##args); \ + } \ +} while (0) /* * em2800_i2c_send_max4() @@ -235,16 +245,16 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, return 0; for (i = 0; i < num; i++) { addr = msgs[i].addr << 1; - dprintk2(2,"%s %s addr=%x len=%d:", + dprintk2(2, "%s %s addr=%x len=%d:", (msgs[i].flags & I2C_M_RD) ? "read" : "write", i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); - if (!msgs[i].len) { /* no len: check only for device presence */ + if (!msgs[i].len) { /* no len: check only for device presence */ if (dev->is_em2800) rc = em2800_i2c_check_for_device(dev, addr); else rc = em28xx_i2c_check_for_device(dev, addr); if (rc < 0) { - dprintk2(2," no device\n"); + dprintk2(2, " no device\n"); return rc; } @@ -258,14 +268,13 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, rc = em28xx_i2c_recv_bytes(dev, addr, msgs[i].buf, msgs[i].len); - if (i2c_debug>=2) { - for (byte = 0; byte < msgs[i].len; byte++) { + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) printk(" %02x", msgs[i].buf[byte]); - } } } else { /* write bytes */ - if (i2c_debug>=2) { + if (i2c_debug >= 2) { for (byte = 0; byte < msgs[i].len; byte++) printk(" %02x", msgs[i].buf[byte]); } @@ -281,13 +290,13 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, } if (rc < 0) goto err; - if (i2c_debug>=2) + if (i2c_debug >= 2) printk("\n"); } return num; - err: - dprintk2(2," ERROR: %i\n", rc); +err: + dprintk2(2, " ERROR: %i\n", rc); return rc; } @@ -330,7 +339,9 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) return -1; buf = 0; - if (1 != (err = i2c_master_send(&dev->i2c_client, &buf, 1))) { + + err = i2c_master_send(&dev->i2c_client, &buf, 1); + if (err != 1) { printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", dev->name, err); return -1; @@ -403,8 +414,10 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) break; } printk(KERN_INFO "Table at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n", - em_eeprom->string_idx_table,em_eeprom->string1, - em_eeprom->string2,em_eeprom->string3); + em_eeprom->string_idx_table, + em_eeprom->string1, + em_eeprom->string2, + em_eeprom->string3); return 0; } @@ -441,58 +454,61 @@ static int attach_inform(struct i2c_client *client) struct em28xx *dev = client->adapter->algo_data; switch (client->addr << 1) { - case 0x86: - case 0x84: - case 0x96: - case 0x94: - { - struct v4l2_priv_tun_config tda9887_cfg; - - struct tuner_setup tun_setup; - - tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.type = TUNER_TDA9887; - tun_setup.addr = client->addr; - - em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup); - - tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &dev->tda9887_conf; - em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG, - &tda9887_cfg); - break; - } - case 0x42: - dprintk1(1,"attach_inform: saa7114 detected.\n"); - break; - case 0x4a: - dprintk1(1,"attach_inform: saa7113 detected.\n"); - break; - case 0xa0: - dprintk1(1,"attach_inform: eeprom detected.\n"); - break; - case 0x60: - case 0x8e: - { - struct IR_i2c *ir = i2c_get_clientdata(client); - dprintk1(1,"attach_inform: IR detected (%s).\n",ir->phys); - em28xx_set_ir(dev,ir); - break; - } - case 0x80: - case 0x88: - dprintk1(1,"attach_inform: msp34xx detected.\n"); - break; - case 0xb8: - case 0xba: - dprintk1(1,"attach_inform: tvp5150 detected.\n"); - break; + case 0x86: + case 0x84: + case 0x96: + case 0x94: + { + struct v4l2_priv_tun_config tda9887_cfg; + + struct tuner_setup tun_setup; + + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.type = TUNER_TDA9887; + tun_setup.addr = client->addr; + + em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, + &tun_setup); + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &dev->tda9887_conf; + em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG, + &tda9887_cfg); + break; + } + case 0x42: + dprintk1(1, "attach_inform: saa7114 detected.\n"); + break; + case 0x4a: + dprintk1(1, "attach_inform: saa7113 detected.\n"); + break; + case 0xa0: + dprintk1(1, "attach_inform: eeprom detected.\n"); + break; + case 0x60: + case 0x8e: + { + struct IR_i2c *ir = i2c_get_clientdata(client); + dprintk1(1, "attach_inform: IR detected (%s).\n", + ir->phys); + em28xx_set_ir(dev, ir); + break; + } + case 0x80: + case 0x88: + dprintk1(1, "attach_inform: msp34xx detected.\n"); + break; + case 0xb8: + case 0xba: + dprintk1(1, "attach_inform: tvp5150 detected.\n"); + break; - default: - if (!dev->tuner_addr) - dev->tuner_addr = client->addr; + default: + if (!dev->tuner_addr) + dev->tuner_addr = client->addr; - dprintk1(1,"attach inform: detected I2C address %x\n", client->addr << 1); + dprintk1(1, "attach inform: detected I2C address %x\n", + client->addr << 1); } @@ -522,7 +538,7 @@ static struct i2c_adapter em28xx_adap_template = { static struct i2c_client em28xx_client_template = { .name = "em28xx internal", -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15) .flags = I2C_CLIENT_ALLOW_USE, #endif }; diff --git a/linux/drivers/media/video/em28xx/em28xx-input.c b/linux/drivers/media/video/em28xx/em28xx-input.c index 5c78eceff..49c40f083 100644 --- a/linux/drivers/media/video/em28xx/em28xx-input.c +++ b/linux/drivers/media/video/em28xx/em28xx-input.c @@ -33,10 +33,12 @@ static unsigned int ir_debug; module_param(ir_debug, int, 0644); -MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]"); +MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); -#define dprintk(fmt, arg...) if (ir_debug) \ - printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg) +#define dprintk(fmt, arg...) \ + if (ir_debug) { \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \ + } /* ----------------------------------------------------------------------- */ @@ -45,7 +47,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(&ir->c,&b,1)) { + if (1 != i2c_master_recv(&ir->c, &b, 1)) { dprintk("read error\n"); return -EIO; } @@ -75,29 +77,30 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char code; /* poll IR chip */ - if (2 != i2c_master_recv(&ir->c,buf,2)) + if (2 != i2c_master_recv(&ir->c, buf, 2)) return -EIO; /* Does eliminate repeated parity code */ - if (buf[1]==0xff) + if (buf[1] == 0xff) return 0; #if 0 /* avoid reapeating keystrokes */ - if (buf[1]==ir->old) + if (buf[1] == ir->old) return 0; #endif - ir->old=buf[1]; + ir->old = buf[1]; /* Rearranges bits to the right order */ - code= ((buf[0]&0x01)<<5) | /* 0010 0000 */ + code = ((buf[0]&0x01)<<5) | /* 0010 0000 */ ((buf[0]&0x02)<<3) | /* 0001 0000 */ ((buf[0]&0x04)<<1) | /* 0000 1000 */ ((buf[0]&0x08)>>1) | /* 0000 0100 */ ((buf[0]&0x10)>>3) | /* 0000 0010 */ ((buf[0]&0x20)>>5); /* 0000 0001 */ - dprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n",code,buf[0]); + dprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n", + code, buf[0]); /* return key */ *ir_key = code; @@ -112,15 +115,14 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, /* poll IR chip */ - if (3 != i2c_master_recv(&ir->c,buf,3)) { + if (3 != i2c_master_recv(&ir->c, buf, 3)) { dprintk("read error\n"); return -EIO; } dprintk("key %02x\n", buf[2]&0x3f); - if (buf[0]!=0x00){ + if (buf[0] != 0x00) return 0; - } *ir_key = buf[2]&0x3f; *ir_raw = buf[2]&0x3f; diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h new file mode 100644 index 000000000..9058bed07 --- /dev/null +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -0,0 +1,88 @@ +#define EM_GPIO_0 (1 << 0) +#define EM_GPIO_1 (1 << 1) +#define EM_GPIO_2 (1 << 2) +#define EM_GPIO_3 (1 << 3) +#define EM_GPIO_4 (1 << 4) +#define EM_GPIO_5 (1 << 5) +#define EM_GPIO_6 (1 << 6) +#define EM_GPIO_7 (1 << 7) + +#define EM_GPO_0 (1 << 0) +#define EM_GPO_1 (1 << 1) +#define EM_GPO_2 (1 << 2) +#define EM_GPO_3 (1 << 3) + +/* em2800 registers */ +#define EM2800_R08_AUDIOSRC 0x08 + +/* em28xx registers */ + + /* GPIO/GPO registers */ +#define EM2880_R04_GPO 0x04 /* em2880-em2883 only */ +#define EM28XX_R08_GPIO 0x08 /* em2820 or upper */ + +#define EM28XX_R06_I2C_CLK 0x06 +#define EM28XX_R0A_CHIPID 0x0a +#define EM28XX_R0C_USBSUSP 0x0c /* */ + +#define EM28XX_R0E_AUDIOSRC 0x0e +#define EM28XX_R0F_XCLK 0x0f + +#define EM28XX_R10_VINMODE 0x10 +#define EM28XX_R11_VINCTRL 0x11 +#define EM28XX_R12_VINENABLE 0x12 /* */ + +#define EM28XX_R14_GAMMA 0x14 +#define EM28XX_R15_RGAIN 0x15 +#define EM28XX_R16_GGAIN 0x16 +#define EM28XX_R17_BGAIN 0x17 +#define EM28XX_R18_ROFFSET 0x18 +#define EM28XX_R19_GOFFSET 0x19 +#define EM28XX_R1A_BOFFSET 0x1a + +#define EM28XX_R1B_OFLOW 0x1b +#define EM28XX_R1C_HSTART 0x1c +#define EM28XX_R1D_VSTART 0x1d +#define EM28XX_R1E_CWIDTH 0x1e +#define EM28XX_R1F_CHEIGHT 0x1f + +#define EM28XX_R20_YGAIN 0x20 +#define EM28XX_R21_YOFFSET 0x21 +#define EM28XX_R22_UVGAIN 0x22 +#define EM28XX_R23_UOFFSET 0x23 +#define EM28XX_R24_VOFFSET 0x24 +#define EM28XX_R25_SHARPNESS 0x25 + +#define EM28XX_R26_COMPR 0x26 +#define EM28XX_R27_OUTFMT 0x27 + +#define EM28XX_R28_XMIN 0x28 +#define EM28XX_R29_XMAX 0x29 +#define EM28XX_R2A_YMIN 0x2a +#define EM28XX_R2B_YMAX 0x2b + +#define EM28XX_R30_HSCALELOW 0x30 +#define EM28XX_R31_HSCALEHIGH 0x31 +#define EM28XX_R32_VSCALELOW 0x32 +#define EM28XX_R33_VSCALEHIGH 0x33 + +#define EM28XX_R40_AC97LSB 0x40 +#define EM28XX_R41_AC97MSB 0x41 +#define EM28XX_R42_AC97ADDR 0x42 +#define EM28XX_R43_AC97BUSY 0x43 + +/* em202 registers */ +#define EM28XX_R02_MASTER_AC97 0x02 +#define EM28XX_R10_LINE_IN_AC97 0x10 +#define EM28XX_R14_VIDEO_AC97 0x14 + +/* register settings */ +#define EM2800_AUDIO_SRC_TUNER 0x0d +#define EM2800_AUDIO_SRC_LINE 0x0c +#define EM28XX_AUDIO_SRC_TUNER 0xc0 +#define EM28XX_AUDIO_SRC_LINE 0x80 + +/* FIXME: Need to be populated with the other chip ID's */ +enum em28xx_chip_id { + CHIP_ID_EM2883 = 36, +}; diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 924f29214..093fef4bb 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1,5 +1,6 @@ /* - em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB video capture devices + em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB + video capture devices Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> Markus Rechberger <mrechberger@gmail.com> @@ -33,7 +34,7 @@ #include <linux/i2c.h> #include <linux/version.h> #include <linux/mm.h> -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) #include <linux/mutex.h> #endif @@ -41,7 +42,7 @@ #include <media/v4l2-common.h> #include <media/msp3400.h> #include <media/tuner.h> -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) #include "i2c-compat.h" #endif @@ -63,14 +64,13 @@ static unsigned int isoc_debug; module_param(isoc_debug, int, 0644); MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); -#define em28xx_isocdbg(fmt, arg...) do {\ - if (isoc_debug) \ +#define em28xx_isocdbg(fmt, arg...) \ +do {\ + if (isoc_debug) { \ printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __func__ , ##arg); } while (0) - -/* Limits minimum and default number of buffers */ -#define EM28XX_MIN_BUF 4 -#define EM28XX_DEF_BUF 8 + dev->name, __func__ , ##arg); \ + } \ + } while (0) MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); @@ -83,13 +83,13 @@ static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) MODULE_PARM(card, "1-" __stringify(EM28XX_MAXBOARDS) "i"); MODULE_PARM(video_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i"); MODULE_PARM(vbi_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i"); MODULE_PARM(radio_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i"); #else -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10) static int dummy; module_param_array(card, int, dummy, 0444); module_param_array(video_nr, int, dummy, 0444); @@ -108,8 +108,8 @@ MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); MODULE_PARM_DESC(radio_nr, "radio device numbers"); static unsigned int video_debug; -module_param(video_debug,int,0644); -MODULE_PARM_DESC(video_debug,"enable debug messages [video]"); +module_param(video_debug, int, 0644); +MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */ static unsigned long em28xx_devused; @@ -126,7 +126,7 @@ static struct v4l2_queryctrl em28xx_qctrl[] = { .step = 0x1, .default_value = 0x1f, .flags = 0, - },{ + }, { .id = V4L2_CID_AUDIO_MUTE, .type = V4L2_CTRL_TYPE_BOOLEAN, .name = "Mute", @@ -313,11 +313,10 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, /* * Controls the isoc copy of each urb packet */ -static inline int em28xx_isoc_copy(struct urb *urb) +static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) { struct em28xx_buffer *buf; struct em28xx_dmaqueue *dma_q = urb->context; - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); unsigned char *outp = NULL; int i, len = 0, rc = 1; unsigned char *p; @@ -399,192 +398,6 @@ static inline int em28xx_isoc_copy(struct urb *urb) } /* ------------------------------------------------------------------ - URB control - ------------------------------------------------------------------*/ - -/* - * IRQ callback, called by URB callback - */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) -static void em28xx_irq_callback(struct urb *urb, struct pt_regs *regs) -#else -static void em28xx_irq_callback(struct urb *urb) -#endif -{ - struct em28xx_dmaqueue *dma_q = urb->context; - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); - int rc, i; - - /* Copy data from URB */ - spin_lock(&dev->slock); - rc = em28xx_isoc_copy(urb); - spin_unlock(&dev->slock); - - /* Reset urb buffers */ - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - urb->status = 0; - - urb->status = usb_submit_urb(urb, GFP_ATOMIC); - if (urb->status) { - em28xx_err("urb resubmit failed (error=%i)\n", - urb->status); - } -} - -/* - * Stop and Deallocate URBs - */ -static void em28xx_uninit_isoc(struct em28xx *dev) -{ - struct urb *urb; - int i; - - em28xx_isocdbg("em28xx: called em28xx_uninit_isoc\n"); - - dev->isoc_ctl.nfields = -1; - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = dev->isoc_ctl.urb[i]; - if (urb) { - usb_kill_urb(urb); - usb_unlink_urb(urb); - if (dev->isoc_ctl.transfer_buffer[i]) { - usb_buffer_free(dev->udev, - urb->transfer_buffer_length, - dev->isoc_ctl.transfer_buffer[i], - urb->transfer_dma); - } - usb_free_urb(urb); - dev->isoc_ctl.urb[i] = NULL; - } - dev->isoc_ctl.transfer_buffer[i] = NULL; - } - - kfree(dev->isoc_ctl.urb); - kfree(dev->isoc_ctl.transfer_buffer); - dev->isoc_ctl.urb = NULL; - dev->isoc_ctl.transfer_buffer = NULL; - - dev->isoc_ctl.num_bufs = 0; - - em28xx_capture_start(dev, 0); -} - -/* - * Allocate URBs and start IRQ - */ -static int em28xx_prepare_isoc(struct em28xx *dev, int max_packets, - int num_bufs) -{ - struct em28xx_dmaqueue *dma_q = &dev->vidq; - int i; - int sb_size, pipe; - struct urb *urb; - int j, k; - - em28xx_isocdbg("em28xx: called em28xx_prepare_isoc\n"); - - /* De-allocates all pending stuff */ - em28xx_uninit_isoc(dev); - - dev->isoc_ctl.num_bufs = num_bufs; - - dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!dev->isoc_ctl.urb) { - em28xx_errdev("cannot alloc memory for usb buffers\n"); - return -ENOMEM; - } - - dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, - GFP_KERNEL); - if (!dev->isoc_ctl.urb) { - em28xx_errdev("cannot allocate memory for usbtransfer\n"); - kfree(dev->isoc_ctl.urb); - return -ENOMEM; - } - - dev->isoc_ctl.max_pkt_size = dev->max_pkt_size; - dev->isoc_ctl.buf = NULL; - - sb_size = max_packets * dev->isoc_ctl.max_pkt_size; - - /* allocate urbs and transfer buffers */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = usb_alloc_urb(max_packets, GFP_KERNEL); - if (!urb) { - em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); - em28xx_uninit_isoc(dev); - return -ENOMEM; - } - dev->isoc_ctl.urb[i] = urb; - - dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->udev, - sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!dev->isoc_ctl.transfer_buffer[i]) { - em28xx_err("unable to allocate %i bytes for transfer" - " buffer %i%s\n", - sb_size, i, - in_interrupt()?" while in int":""); - em28xx_uninit_isoc(dev); - return -ENOMEM; - } - memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); - - /* FIXME: this is a hack - should be - 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK' - should also be using 'desc.bInterval' - */ - pipe = usb_rcvisocpipe(dev->udev, 0x82); - usb_fill_int_urb(urb, dev->udev, pipe, - dev->isoc_ctl.transfer_buffer[i], sb_size, - em28xx_irq_callback, dma_q, 1); - - urb->number_of_packets = max_packets; - urb->transfer_flags = URB_ISO_ASAP; - - k = 0; - for (j = 0; j < max_packets; j++) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - dev->isoc_ctl.max_pkt_size; - k += dev->isoc_ctl.max_pkt_size; - } - } - - return 0; -} - -static int em28xx_start_thread(struct em28xx_dmaqueue *dma_q) -{ - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); - int i, rc = 0; - - em28xx_videodbg("Called em28xx_start_thread\n"); - - init_waitqueue_head(&dma_q->wq); - - em28xx_capture_start(dev, 1); - - /* submit urbs and enables IRQ */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); - if (rc) { - em28xx_err("submit of urb %i failed (error=%i)\n", i, - rc); - em28xx_uninit_isoc(dev); - return rc; - } - } - - if (rc < 0) - return rc; - - return 0; -} - -/* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ @@ -592,6 +405,8 @@ static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + struct v4l2_frequency f; *size = 16 * fh->dev->width * fh->dev->height >> 3; if (0 == *count) @@ -600,6 +415,12 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) if (*count < EM28XX_MIN_BUF) *count = EM28XX_MIN_BUF; + /* Ask tuner to go to analog mode */ + memset(&f, 0, sizeof(f)); + f.frequency = dev->ctl_freq; + + em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f); + return 0; } @@ -637,7 +458,6 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, struct em28xx_fh *fh = vq->priv_data; struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); struct em28xx *dev = fh->dev; - struct em28xx_dmaqueue *vidq = &dev->vidq; int rc = 0, urb_init = 0; /* FIXME: It assumes depth = 16 */ @@ -661,12 +481,9 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, urb_init = 1; if (urb_init) { - rc = em28xx_prepare_isoc(dev, EM28XX_NUM_PACKETS, - EM28XX_NUM_BUFS); - if (rc < 0) - goto fail; - - rc = em28xx_start_thread(vidq); + rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + EM28XX_NUM_BUFS, dev->max_pkt_size, + em28xx_isoc_copy); if (rc < 0) goto fail; } @@ -692,7 +509,8 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } -static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) { struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); struct em28xx_fh *fh = vq->priv_data; @@ -710,7 +528,7 @@ static struct videobuf_queue_ops em28xx_video_qops = { .buf_release = buffer_release, }; -/********************* v4l2 interface ******************************************/ +/********************* v4l2 interface **************************************/ /* * em28xx_config() @@ -726,9 +544,9 @@ static int em28xx_config(struct em28xx *dev) #if 1 /* enable vbi capturing */ -/* em28xx_write_regs_req(dev,0x00,0x0e,"\xC0",1); audio register */ -/* em28xx_write_regs_req(dev,0x00,0x0f,"\x80",1); clk register */ - em28xx_write_regs_req(dev,0x00,0x11,"\x51",1); +/* em28xx_write_regs_req(dev, 0x00, 0x0e, "\xC0", 1); audio register */ +/* em28xx_write_regs_req(dev, 0x00, 0x0f, "\x80", 1); clk register */ + em28xx_write_regs_req(dev, 0x00, 0x11, "\x51", 1); #endif dev->mute = 1; /* maybe not the right place... */ @@ -768,12 +586,15 @@ static void video_mux(struct em28xx *dev, int index) em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); if (dev->has_msp34xx) { - if (dev->i2s_speed) - em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed); + if (dev->i2s_speed) { + em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, + &dev->i2s_speed); + } route.input = dev->ctl_ainput; route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); /* Note: this is msp3400 specific */ - em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route); + em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, + &route); } em28xx_audio_analog_set(dev); @@ -1013,7 +834,7 @@ out: return rc; } -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; @@ -1130,11 +951,11 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) index = dev->ctl_ainput; - if (index == 0) { + if (index == 0) strcpy(a->name, "Television"); - } else { + else strcpy(a->name, "Line In"); - } + a->capability = V4L2_AUDCAP_STEREO; a->index = index; @@ -1345,9 +1166,9 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int em28xx_reg_len(int reg) { switch (reg) { - case AC97LSB_REG: - case HSCALELOW_REG: - case VSCALELOW_REG: + case EM28XX_R40_AC97LSB: + case EM28XX_R30_HSCALELOW: + case EM28XX_R32_VSCALELOW: return 2; default: return 1; @@ -1749,7 +1570,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) { int minor = iminor(inode); int errCode = 0, radio = 0; - struct em28xx *h,*dev = NULL; + struct em28xx *h, *dev = NULL; struct em28xx_fh *fh; enum v4l2_buf_type fh_type = 0; @@ -1774,8 +1595,15 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) em28xx_videodbg("open minor=%d type=%s users=%d\n", minor, v4l2_type_names[fh_type], dev->users); - fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL); +#if 0 + errCode = em28xx_set_mode(dev, EM28XX_ANALOG_MODE); + if (errCode < 0) { + em28xx_errdev("Device locked on digital mode. Can't open analog\n"); + return -EBUSY; + } +#endif + fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL); if (!fh) { em28xx_errdev("em28xx-video.c: Out of memory?!\n"); return -ENOMEM; @@ -1792,9 +1620,15 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) dev->hscale = 0; dev->vscale = 0; + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); em28xx_set_alternate(dev); em28xx_resolution_set(dev); + /* Needed, since GPIO might have disabled power of + some i2c device + */ + em28xx_config_i2c(dev); + #if 0 /* device needs to be initialized before isoc transfer */ video_mux(dev, 0); @@ -1815,6 +1649,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) sizeof(struct em28xx_buffer), fh); mutex_unlock(&dev->lock); + return errCode; } @@ -1857,12 +1692,13 @@ static void em28xx_release_resources(struct em28xx *dev) usb_put_dev(dev->udev); /* Mark device as unused */ - em28xx_devused&=~(1<<dev->devno); + em28xx_devused &= ~(1<<dev->devno); } /* * em28xx_v4l2_close() - * stops streaming and deallocates all resources allocated by the v4l2 calls and ioctls + * stops streaming and deallocates all resources allocated by the v4l2 + * calls and ioctls */ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) { @@ -1893,6 +1729,7 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) /* do this before setting alternate! */ em28xx_uninit_isoc(dev); + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); /* set alternate 0 */ dev->alt = 0; @@ -1915,7 +1752,7 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) * will allocate buffers when called for the first time */ static ssize_t -em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count, +em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, loff_t *pos) { struct em28xx_fh *fh = filp->private_data; @@ -2089,7 +1926,7 @@ static struct video_device em28xx_radio_template = { #endif }; -/******************************** usb interface *****************************************/ +/******************************** usb interface ******************************/ static LIST_HEAD(em28xx_extension_devlist); @@ -2184,10 +2021,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->em28xx_read_reg_req = em28xx_read_reg_req; dev->is_em2800 = em28xx_boards[dev->model].is_em2800; - errCode = em28xx_read_reg(dev, CHIPID_REG); - if (errCode >= 0) - em28xx_info("em28xx chip ID = %d\n", errCode); - em28xx_pre_card_setup(dev); errCode = em28xx_config(dev); @@ -2331,6 +2164,9 @@ static void request_module_async(struct work_struct *work) request_module("snd-usb-audio"); else request_module("em28xx-alsa"); + + if (dev->has_dvb) + request_module("em28xx-dvb"); } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) @@ -2369,22 +2205,24 @@ static int em28xx_usb_probe(struct usb_interface *interface, ifnum = interface->altsetting[0].desc.bInterfaceNumber; /* Check to see next free device and mark as used */ - nr=find_first_zero_bit(&em28xx_devused,EM28XX_MAXBOARDS); - em28xx_devused|=1<<nr; + nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS); + em28xx_devused |= 1<<nr; /* Don't register audio interfaces */ if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { em28xx_err(DRIVER_NAME " audio device (%04x:%04x): interface %i, class %i\n", - udev->descriptor.idVendor,udev->descriptor.idProduct, + udev->descriptor.idVendor, + udev->descriptor.idProduct, ifnum, interface->altsetting[0].desc.bInterfaceClass); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); return -ENODEV; } em28xx_err(DRIVER_NAME " new video device (%04x:%04x): interface %i, class %i\n", - udev->descriptor.idVendor,udev->descriptor.idProduct, + udev->descriptor.idVendor, + udev->descriptor.idProduct, ifnum, interface->altsetting[0].desc.bInterfaceClass); @@ -2394,18 +2232,19 @@ static int em28xx_usb_probe(struct usb_interface *interface, if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) { em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n"); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); return -ENODEV; } if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { em28xx_err(DRIVER_NAME " probing error: endpoint is ISO OUT endpoint!\n"); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); return -ENODEV; } if (nr >= EM28XX_MAXBOARDS) { - printk (DRIVER_NAME ": Supports only %i em28xx boards.\n",EM28XX_MAXBOARDS); - em28xx_devused&=~(1<<nr); + printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", + EM28XX_MAXBOARDS); + em28xx_devused &= ~(1<<nr); return -ENOMEM; } @@ -2413,7 +2252,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { em28xx_err(DRIVER_NAME ": out of memory!\n"); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); return -ENOMEM; } @@ -2437,14 +2276,14 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* compute alternate max packet sizes */ uif = udev->actconfig->interface[0]; - dev->num_alt=uif->num_altsetting; - em28xx_info("Alternate settings: %i\n",dev->num_alt); -// dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)* - dev->alt_max_pkt_size = kmalloc(32* - dev->num_alt,GFP_KERNEL); + dev->num_alt = uif->num_altsetting; + em28xx_info("Alternate settings: %i\n", dev->num_alt); +/* dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)* */ + dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL); + if (dev->alt_max_pkt_size == NULL) { em28xx_errdev("out of memory!\n"); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); kfree(dev); return -ENOMEM; } @@ -2454,11 +2293,11 @@ static int em28xx_usb_probe(struct usb_interface *interface, wMaxPacketSize); dev->alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - em28xx_info("Alternate setting %i, max size= %i\n",i, - dev->alt_max_pkt_size[i]); + em28xx_info("Alternate setting %i, max size= %i\n", i, + dev->alt_max_pkt_size[i]); } - if ((card[nr]>=0)&&(card[nr]<em28xx_bcount)) + if ((card[nr] >= 0) && (card[nr] < em28xx_bcount)) dev->model = card[nr]; /* allocate device struct */ @@ -2494,7 +2333,8 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) em28xx_info("disconnecting %s\n", dev->vdev->name); - /* wait until all current v4l2 io is finished then deallocate resources */ + /* wait until all current v4l2 io is finished then deallocate + resources */ mutex_lock(&dev->lock); wake_up_interruptible_all(&dev->open); @@ -2531,7 +2371,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) } static struct usb_driver em28xx_usb_driver = { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15) .owner = THIS_MODULE, #endif .name = "em28xx", diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index cf6efe070..d31d11a5e 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -30,10 +30,38 @@ #include <media/videobuf-vmalloc.h> #include <linux/i2c.h> -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) #include <linux/mutex.h> #endif #include <media/ir-kbd-i2c.h> +#if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) +#include <media/videobuf-dvb.h> +#endif +#include "tuner-xc2028.h" +#include "em28xx-reg.h" + +/* Boards supported by driver */ +#define EM2800_BOARD_UNKNOWN 0 +#define EM2820_BOARD_UNKNOWN 1 +#define EM2820_BOARD_TERRATEC_CINERGY_250 2 +#define EM2820_BOARD_PINNACLE_USB_2 3 +#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4 +#define EM2820_BOARD_MSI_VOX_USB_2 5 +#define EM2800_BOARD_TERRATEC_CINERGY_200 6 +#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7 +#define EM2800_BOARD_KWORLD_USB2800 8 +#define EM2820_BOARD_PINNACLE_DVC_90 9 +#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10 +#define EM2880_BOARD_TERRATEC_HYBRID_XS 11 +#define EM2820_BOARD_KWORLD_PVRTV2800RF 12 +#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13 +#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14 +#define EM2800_BOARD_VGEAR_POCKETTV 15 +#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 16 + +/* Limits minimum and default number of buffers */ +#define EM28XX_MIN_BUF 4 +#define EM28XX_DEF_BUF 8 /* maximum number of em28xx boards */ #define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */ @@ -84,12 +112,20 @@ /* time in msecs to wait for i2c writes to finish */ #define EM2800_I2C_WRITE_TIMEOUT 20 +enum em28xx_mode { + EM28XX_MODE_UNDEFINED, + EM28XX_ANALOG_MODE, + EM28XX_DIGITAL_MODE, +}; + enum em28xx_stream_state { STREAM_OFF, STREAM_INTERRUPT, STREAM_ON, }; +struct em28xx; + struct em28xx_usb_isoc_ctl { /* max packet size of isoc transaction */ int max_pkt_size; @@ -119,6 +155,10 @@ struct em28xx_usb_isoc_ctl { /* Stores the number of received fields */ int nfields; + + /* isoc urb callback */ + int (*isoc_copy) (struct em28xx *dev, struct urb *urb); + }; struct em28xx_fmt { @@ -190,6 +230,12 @@ enum em28xx_decoder { EM28XX_SAA7114 }; +struct em28xx_reg_seq { + int reg; + unsigned char val, mask; + int sleep; +}; + struct em28xx_board { char *name; int vchannels; @@ -203,8 +249,7 @@ struct em28xx_board { unsigned int mts_firmware:1; unsigned int has_12mhz_i2s:1; unsigned int max_range_640_480:1; - - unsigned int analog_gpio; + unsigned int has_dvb:1; enum em28xx_decoder decoder; @@ -237,7 +282,10 @@ enum em28xx_dev_state { #define EM28XX_NUM_AUDIO_PACKETS 64 #define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */ #define EM28XX_CAPTURE_STREAM_EN 1 + +/* em28xx extensions */ #define EM28XX_AUDIO 0x10 +#define EM28XX_DVB 0x20 struct em28xx_audio { char name[50]; @@ -263,13 +311,24 @@ struct em28xx_audio { spinlock_t slock; }; +struct em28xx; + +struct em28xx_fh { + struct em28xx *dev; + unsigned int stream_on:1; /* Locks streams */ + int radio; + + struct videobuf_queue vb_vidq; + + enum v4l2_buf_type type; +}; + /* main device struct */ struct em28xx { /* generic device properties */ char name[30]; /* name (including minor) of the device */ int model; /* index in the device_data struct */ int devno; /* marks the number of this device */ - unsigned int analog_gpio; unsigned int is_em2800:1; unsigned int has_msp34xx:1; unsigned int has_tda9887:1; @@ -277,6 +336,16 @@ struct em28xx { unsigned int has_audio_class:1; unsigned int has_12mhz_i2s:1; unsigned int max_range_640_480:1; + unsigned int has_dvb:1; + + /* Some older em28xx chips needs a waiting time after writing */ + unsigned int wait_after_write; + + /* GPIO sequences for analog and digital mode */ + struct em28xx_reg_seq *analog_gpio, *digital_gpio; + + /* GPIO sequences for tuner callbacks */ + struct em28xx_reg_seq *tun_analog_gpio, *tun_digital_gpio; int video_inputs; /* number of video inputs */ struct list_head devlist; @@ -309,7 +378,8 @@ struct em28xx { unsigned int video_bytesread; /* Number of bytes read */ unsigned long hash; /* eeprom hash - for boards with generic ID */ - unsigned long i2c_hash; /* i2c devicelist hash - for boards with generic ID */ + unsigned long i2c_hash; /* i2c devicelist hash - + for boards with generic ID */ struct em28xx_audio *adev; @@ -320,7 +390,7 @@ struct em28xx { struct work_struct request_module_wk; /* locks */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) struct mutex lock; #else struct semaphore lock, fileop_lock; @@ -347,24 +417,21 @@ struct em28xx { struct urb *urb[EM28XX_NUM_BUFS]; /* urb for isoc transfers */ char *transfer_buffer[EM28XX_NUM_BUFS]; /* transfer buffers for isoc transfer */ /* helper funcs that call usb_control_msg */ - int (*em28xx_write_regs) (struct em28xx * dev, u16 reg, char *buf, - int len); - int (*em28xx_read_reg) (struct em28xx * dev, u16 reg); - int (*em28xx_read_reg_req_len) (struct em28xx * dev, u8 req, u16 reg, + int (*em28xx_write_regs) (struct em28xx *dev, u16 reg, + char *buf, int len); + int (*em28xx_read_reg) (struct em28xx *dev, u16 reg); + int (*em28xx_read_reg_req_len) (struct em28xx *dev, u8 req, u16 reg, char *buf, int len); - int (*em28xx_write_regs_req) (struct em28xx * dev, u8 req, u16 reg, + int (*em28xx_write_regs_req) (struct em28xx *dev, u8 req, u16 reg, char *buf, int len); - int (*em28xx_read_reg_req) (struct em28xx * dev, u8 req, u16 reg); -}; + int (*em28xx_read_reg_req) (struct em28xx *dev, u8 req, u16 reg); -struct em28xx_fh { - struct em28xx *dev; - unsigned int stream_on:1; /* Locks streams */ - int radio; + enum em28xx_mode mode; - struct videobuf_queue vb_vidq; + /* Caches GPO and GPIO registers */ + unsigned char reg_gpo, reg_gpio; - enum v4l2_buf_type type; + struct em28xx_dvb *dvb; }; struct em28xx_ops { @@ -402,19 +469,26 @@ int em28xx_capture_start(struct em28xx *dev, int start); int em28xx_outfmt_set_yuv422(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); +int em28xx_init_isoc(struct em28xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); +void em28xx_uninit_isoc(struct em28xx *dev); +int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); +int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); /* Provided by em28xx-video.c */ int em28xx_register_extension(struct em28xx_ops *dev); void em28xx_unregister_extension(struct em28xx_ops *dev); /* Provided by em28xx-cards.c */ -extern int em2800_variant_detect(struct usb_device* udev,int model); +extern int em2800_variant_detect(struct usb_device *udev, int model); extern void em28xx_pre_card_setup(struct em28xx *dev); extern void em28xx_card_setup(struct em28xx *dev); extern struct em28xx_board em28xx_boards[]; extern struct usb_device_id em28xx_id_table[]; extern const unsigned int em28xx_bcount; void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir); +int em28xx_tuner_callback(void *ptr, int command, int arg); /* Provided by em28xx-input.c */ /* TODO: Check if the standard get_key handlers on ir-common can be used */ @@ -423,71 +497,6 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); -/* em2800 registers */ -#define EM2800_AUDIOSRC_REG 0x08 - -/* em28xx registers */ -#define I2C_CLK_REG 0x06 -#define CHIPID_REG 0x0a -#define USBSUSP_REG 0x0c /* */ - -#define AUDIOSRC_REG 0x0e -#define XCLK_REG 0x0f - -#define VINMODE_REG 0x10 -#define VINCTRL_REG 0x11 -#define VINENABLE_REG 0x12 /* */ - -#define GAMMA_REG 0x14 -#define RGAIN_REG 0x15 -#define GGAIN_REG 0x16 -#define BGAIN_REG 0x17 -#define ROFFSET_REG 0x18 -#define GOFFSET_REG 0x19 -#define BOFFSET_REG 0x1a - -#define OFLOW_REG 0x1b -#define HSTART_REG 0x1c -#define VSTART_REG 0x1d -#define CWIDTH_REG 0x1e -#define CHEIGHT_REG 0x1f - -#define YGAIN_REG 0x20 -#define YOFFSET_REG 0x21 -#define UVGAIN_REG 0x22 -#define UOFFSET_REG 0x23 -#define VOFFSET_REG 0x24 -#define SHARPNESS_REG 0x25 - -#define COMPR_REG 0x26 -#define OUTFMT_REG 0x27 - -#define XMIN_REG 0x28 -#define XMAX_REG 0x29 -#define YMIN_REG 0x2a -#define YMAX_REG 0x2b - -#define HSCALELOW_REG 0x30 -#define HSCALEHIGH_REG 0x31 -#define VSCALELOW_REG 0x32 -#define VSCALEHIGH_REG 0x33 - -#define AC97LSB_REG 0x40 -#define AC97MSB_REG 0x41 -#define AC97ADDR_REG 0x42 -#define AC97BUSY_REG 0x43 - -/* em202 registers */ -#define MASTER_AC97 0x02 -#define LINE_IN_AC97 0x10 -#define VIDEO_AC97 0x14 - -/* register settings */ -#define EM2800_AUDIO_SRC_TUNER 0x0d -#define EM2800_AUDIO_SRC_LINE 0x0c -#define EM28XX_AUDIO_SRC_TUNER 0xc0 -#define EM28XX_AUDIO_SRC_LINE 0x80 - /* printk macros */ #define em28xx_err(fmt, arg...) do {\ @@ -504,80 +513,80 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, printk(KERN_WARNING "%s: "fmt,\ dev->name , ##arg); } while (0) -inline static int em28xx_compression_disable(struct em28xx *dev) +static inline int em28xx_compression_disable(struct em28xx *dev) { /* side effect of disabling scaler and mixer */ - return em28xx_write_regs(dev, COMPR_REG, "\x00", 1); + return em28xx_write_regs(dev, EM28XX_R26_COMPR, "\x00", 1); } -inline static int em28xx_contrast_get(struct em28xx *dev) +static inline int em28xx_contrast_get(struct em28xx *dev) { - return em28xx_read_reg(dev, YGAIN_REG) & 0x1f; + return em28xx_read_reg(dev, EM28XX_R20_YGAIN) & 0x1f; } -inline static int em28xx_brightness_get(struct em28xx *dev) +static inline int em28xx_brightness_get(struct em28xx *dev) { - return em28xx_read_reg(dev, YOFFSET_REG); + return em28xx_read_reg(dev, EM28XX_R21_YOFFSET); } -inline static int em28xx_saturation_get(struct em28xx *dev) +static inline int em28xx_saturation_get(struct em28xx *dev) { - return em28xx_read_reg(dev, UVGAIN_REG) & 0x1f; + return em28xx_read_reg(dev, EM28XX_R22_UVGAIN) & 0x1f; } -inline static int em28xx_u_balance_get(struct em28xx *dev) +static inline int em28xx_u_balance_get(struct em28xx *dev) { - return em28xx_read_reg(dev, UOFFSET_REG); + return em28xx_read_reg(dev, EM28XX_R23_UOFFSET); } -inline static int em28xx_v_balance_get(struct em28xx *dev) +static inline int em28xx_v_balance_get(struct em28xx *dev) { - return em28xx_read_reg(dev, VOFFSET_REG); + return em28xx_read_reg(dev, EM28XX_R24_VOFFSET); } -inline static int em28xx_gamma_get(struct em28xx *dev) +static inline int em28xx_gamma_get(struct em28xx *dev) { - return em28xx_read_reg(dev, GAMMA_REG) & 0x3f; + return em28xx_read_reg(dev, EM28XX_R14_GAMMA) & 0x3f; } -inline static int em28xx_contrast_set(struct em28xx *dev, s32 val) +static inline int em28xx_contrast_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, YGAIN_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R20_YGAIN, &tmp, 1); } -inline static int em28xx_brightness_set(struct em28xx *dev, s32 val) +static inline int em28xx_brightness_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, YOFFSET_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R21_YOFFSET, &tmp, 1); } -inline static int em28xx_saturation_set(struct em28xx *dev, s32 val) +static inline int em28xx_saturation_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, UVGAIN_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R22_UVGAIN, &tmp, 1); } -inline static int em28xx_u_balance_set(struct em28xx *dev, s32 val) +static inline int em28xx_u_balance_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, UOFFSET_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R23_UOFFSET, &tmp, 1); } -inline static int em28xx_v_balance_set(struct em28xx *dev, s32 val) +static inline int em28xx_v_balance_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, VOFFSET_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R24_VOFFSET, &tmp, 1); } -inline static int em28xx_gamma_set(struct em28xx *dev, s32 val) +static inline int em28xx_gamma_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, GAMMA_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R14_GAMMA, &tmp, 1); } /*FIXME: maxw should be dependent of alt mode */ -inline static unsigned int norm_maxw(struct em28xx *dev) +static inline unsigned int norm_maxw(struct em28xx *dev) { if (dev->max_range_640_480) return 640; @@ -585,7 +594,7 @@ inline static unsigned int norm_maxw(struct em28xx *dev) return 720; } -inline static unsigned int norm_maxh(struct em28xx *dev) +static inline unsigned int norm_maxh(struct em28xx *dev) { if (dev->max_range_640_480) return 480; diff --git a/linux/drivers/media/video/ivtv/ivtv-fileops.c b/linux/drivers/media/video/ivtv/ivtv-fileops.c index d949a8133..a7640c49f 100644 --- a/linux/drivers/media/video/ivtv/ivtv-fileops.c +++ b/linux/drivers/media/video/ivtv/ivtv-fileops.c @@ -219,7 +219,9 @@ static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, /* Process pending program info updates and pending VBI data */ ivtv_update_pgm_info(itv); - if (jiffies - itv->dualwatch_jiffies > msecs_to_jiffies(1000)) { + if (time_after(jiffies, + itv->dualwatch_jiffies + + msecs_to_jiffies(1000))) { itv->dualwatch_jiffies = jiffies; ivtv_dualwatch(itv); } diff --git a/linux/drivers/media/video/ivtv/ivtv-mailbox.c b/linux/drivers/media/video/ivtv/ivtv-mailbox.c index 13a6c374d..1b5c0ac09 100644 --- a/linux/drivers/media/video/ivtv/ivtv-mailbox.c +++ b/linux/drivers/media/video/ivtv/ivtv-mailbox.c @@ -177,7 +177,8 @@ static int get_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int f /* Sleep before a retry, if not atomic */ if (!(flags & API_NO_WAIT_MB)) { - if (jiffies - then > msecs_to_jiffies(10*retries)) + if (time_after(jiffies, + then + msecs_to_jiffies(10*retries))) break; ivtv_msleep_timeout(10, 0); } @@ -244,7 +245,9 @@ static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[]) data, then just return 0 as there is no need to issue this command again. Just an optimization to prevent unnecessary use of mailboxes. */ if (itv->api_cache[cmd].last_jiffies && - jiffies - itv->api_cache[cmd].last_jiffies < msecs_to_jiffies(1800000) && + time_before(jiffies, + itv->api_cache[cmd].last_jiffies + + msecs_to_jiffies(1800000)) && !memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) { itv->api_cache[cmd].last_jiffies = jiffies; return 0; @@ -299,7 +302,7 @@ static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[]) } } while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) { - if (jiffies - then > api_timeout) { + if (time_after(jiffies, then + api_timeout)) { IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name); /* reset the mailbox, but it is likely too late already */ write_sync(0, &mbox->flags); @@ -311,7 +314,7 @@ static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[]) else ivtv_msleep_timeout(1, 0); } - if (jiffies - then > msecs_to_jiffies(100)) + if (time_after(jiffies, then + msecs_to_jiffies(100))) IVTV_DEBUG_WARN("%s took %u jiffies\n", api_info[cmd].name, jiffies_to_msecs(jiffies - then)); diff --git a/linux/drivers/media/video/ivtv/ivtv-streams.c b/linux/drivers/media/video/ivtv/ivtv-streams.c index 24d98ecf3..4ab8d3683 100644 --- a/linux/drivers/media/video/ivtv/ivtv-streams.c +++ b/linux/drivers/media/video/ivtv/ivtv-streams.c @@ -768,7 +768,8 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) /* wait 2s for EOS interrupt */ while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) && - jiffies < then + msecs_to_jiffies (2000)) { + time_before(jiffies, + then + msecs_to_jiffies(2000))) { schedule_timeout(msecs_to_jiffies(10)); } diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index ef780ed60..716f30eb5 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -410,7 +410,7 @@ int msp_sleep(struct msp_state *state, int timeout) } /* ------------------------------------------------------------------------ */ -#ifdef CONFIG_VIDEO_V4L1 +#ifdef CONFIG_VIDEO_ALLOW_V4L1 static int msp_mode_v4l2_to_v4l1(int rxsubchans, int audmode) { if (rxsubchans == V4L2_TUNER_SUB_MONO) @@ -558,7 +558,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a kernel pointer here... */ -#ifdef CONFIG_VIDEO_V4L1 +#ifdef CONFIG_VIDEO_ALLOW_V4L1 case VIDIOCGAUDIO: { struct video_audio *va = arg; diff --git a/linux/drivers/media/video/mxb.c b/linux/drivers/media/video/mxb.c index 97cc92c82..bf2462a58 100644 --- a/linux/drivers/media/video/mxb.c +++ b/linux/drivers/media/video/mxb.c @@ -222,9 +222,8 @@ static int mxb_probe(struct saa7146_dev* dev) device_for_each_child(&mxb->i2c_adapter.dev, mxb, mxb_check_clients); /* check if all devices are present */ - if( 0 == mxb->tea6420_1 || 0 == mxb->tea6420_2 || 0 == mxb->tea6415c - || 0 == mxb->tda9840 || 0 == mxb->saa7111a || 0 == mxb->tuner ) { - + if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c || + !mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) { printk("mxb: did not find all i2c devices. aborting\n"); i2c_del_adapter(&mxb->i2c_adapter); kfree(mxb); diff --git a/linux/drivers/media/video/ov511.c b/linux/drivers/media/video/ov511.c index cba851d18..4c8050667 100644 --- a/linux/drivers/media/video/ov511.c +++ b/linux/drivers/media/video/ov511.c @@ -5838,7 +5838,7 @@ ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id) goto error; memcpy(ov->vdev, &vdev_template, sizeof(*ov->vdev)); - ov->vdev->dev = &dev->dev; + ov->vdev->dev = &intf->dev; video_set_drvdata(ov->vdev, ov); for (i = 0; i < OV511_MAX_UNIT_VIDEO; i++) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c index 32e43e9d1..8b500528d 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c @@ -68,7 +68,7 @@ int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val) int ret = 0; if (!cptr) return -EINVAL; LOCK_TAKE(cptr->hdw->big_lock); do { - if (cptr->info->set_value != 0) { + if (cptr->info->set_value) { if (cptr->info->type == pvr2_ctl_bitmask) { mask &= cptr->info->def.type_bitmask.valid_bits; } else if ((cptr->info->type == pvr2_ctl_int)|| @@ -269,7 +269,7 @@ unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *cptr) int pvr2_ctrl_is_writable(struct pvr2_ctrl *cptr) { if (!cptr) return 0; - return cptr->info->set_value != 0; + return cptr->info->set_value != NULL; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 77edc72a2..8356bc0e4 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2357,7 +2357,7 @@ static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw) for (idx = 0; idx < hdw->control_cnt; idx++) { cptr = hdw->controls + idx; - if (cptr->info->is_dirty == 0) continue; + if (!cptr->info->is_dirty) continue; if (!cptr->info->is_dirty(cptr)) continue; commit_flag = !0; @@ -2706,7 +2706,7 @@ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, u16 address; unsigned int pipe; LOCK_TAKE(hdw->big_lock); do { - if ((hdw->fw_buffer == 0) == !enable_flag) break; + if ((hdw->fw_buffer == NULL) == !enable_flag) break; if (!enable_flag) { pvr2_trace(PVR2_TRACE_FIRMWARE, @@ -2775,7 +2775,7 @@ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, /* Return true if we're in a mode for retrieval CPU firmware */ int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *hdw) { - return hdw->fw_buffer != 0; + return hdw->fw_buffer != NULL; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-io.c b/linux/drivers/media/video/pvrusb2/pvrusb2-io.c index 7b3be67f5..e4aae2c97 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-io.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-io.c @@ -603,7 +603,7 @@ void pvr2_stream_kill(struct pvr2_stream *sp) struct pvr2_buffer *bp; mutex_lock(&sp->mutex); do { pvr2_stream_internal_flush(sp); - while ((bp = pvr2_stream_get_ready_buffer(sp)) != 0) { + while ((bp = pvr2_stream_get_ready_buffer(sp)) != NULL) { pvr2_buffer_set_idle(bp); } if (sp->buffer_total_count != sp->buffer_target_count) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c index aef12fecb..d7bbbe00f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c @@ -174,7 +174,7 @@ static int pvr2_ioread_start(struct pvr2_ioread *cp) if (!(cp->stream)) return 0; pvr2_trace(PVR2_TRACE_START_STOP, "/*---TRACE_READ---*/ pvr2_ioread_start id=%p",cp); - while ((bp = pvr2_stream_get_idle_buffer(cp->stream)) != 0) { + while ((bp = pvr2_stream_get_idle_buffer(cp->stream)) != NULL) { stat = pvr2_buffer_queue(bp); if (stat < 0) { pvr2_trace(PVR2_TRACE_DATA_FLOW, diff --git a/linux/drivers/media/video/pwc/pwc-if.c b/linux/drivers/media/video/pwc/pwc-if.c index 6a7db17ba..8236b2b2d 100644 --- a/linux/drivers/media/video/pwc/pwc-if.c +++ b/linux/drivers/media/video/pwc/pwc-if.c @@ -929,7 +929,7 @@ static void pwc_iso_stop(struct pwc_device *pdev) struct urb *urb; urb = pdev->sbuf[i].urb; - if (urb != 0) { + if (urb) { PWC_DEBUG_MEMORY("Unlinking URB %p\n", urb); usb_kill_urb(urb); } @@ -945,7 +945,7 @@ static void pwc_iso_free(struct pwc_device *pdev) struct urb *urb; urb = pdev->sbuf[i].urb; - if (urb != 0) { + if (urb) { PWC_DEBUG_MEMORY("Freeing URB\n"); usb_free_urb(urb); pdev->sbuf[i].urb = NULL; @@ -1784,8 +1784,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id /* Allocate video_device structure */ pdev->vdev = video_device_alloc(); - if (pdev->vdev == 0) - { + if (!pdev->vdev) { PWC_ERROR("Err, cannot allocate video_device struture. Failing probe."); kfree(pdev); return -ENOMEM; diff --git a/linux/drivers/media/video/saa7110.c b/linux/drivers/media/video/saa7110.c index eb561ce6c..0692e4bef 100644 --- a/linux/drivers/media/video/saa7110.c +++ b/linux/drivers/media/video/saa7110.c @@ -489,7 +489,7 @@ saa7110_detect_client (struct i2c_adapter *adapter, return 0; client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == 0) + if (!client) return -ENOMEM; client->addr = address; client->adapter = adapter; @@ -497,7 +497,7 @@ saa7110_detect_client (struct i2c_adapter *adapter, strlcpy(I2C_NAME(client), "saa7110", sizeof(I2C_NAME(client))); decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL); - if (decoder == 0) { + if (!decoder) { kfree(client); return -ENOMEM; } diff --git a/linux/drivers/media/video/saa7111.c b/linux/drivers/media/video/saa7111.c index df44aed92..917fb2553 100644 --- a/linux/drivers/media/video/saa7111.c +++ b/linux/drivers/media/video/saa7111.c @@ -503,7 +503,7 @@ saa7111_detect_client (struct i2c_adapter *adapter, return 0; client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == 0) + if (!client) return -ENOMEM; client->addr = address; client->adapter = adapter; diff --git a/linux/drivers/media/video/saa7114.c b/linux/drivers/media/video/saa7114.c index ebf42cf9e..158bd123c 100644 --- a/linux/drivers/media/video/saa7114.c +++ b/linux/drivers/media/video/saa7114.c @@ -842,7 +842,7 @@ saa7114_detect_client (struct i2c_adapter *adapter, return 0; client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == 0) + if (!client) return -ENOMEM; client->addr = address; client->adapter = adapter; diff --git a/linux/drivers/media/video/saa717x.c b/linux/drivers/media/video/saa717x.c index 030d07dd6..bdae62189 100644 --- a/linux/drivers/media/video/saa717x.c +++ b/linux/drivers/media/video/saa717x.c @@ -1529,7 +1529,6 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, #endif }; - #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) EXPORT_NO_SYMBOLS; #endif diff --git a/linux/drivers/media/video/saa7185.c b/linux/drivers/media/video/saa7185.c index 2ac144d9a..7a6c9c1fb 100644 --- a/linux/drivers/media/video/saa7185.c +++ b/linux/drivers/media/video/saa7185.c @@ -404,7 +404,7 @@ saa7185_detect_client (struct i2c_adapter *adapter, return 0; client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == 0) + if (!client) return -ENOMEM; client->addr = address; client->adapter = adapter; diff --git a/linux/drivers/media/video/tda9840.c b/linux/drivers/media/video/tda9840.c index 72b6f476c..c41e50ba6 100644 --- a/linux/drivers/media/video/tda9840.c +++ b/linux/drivers/media/video/tda9840.c @@ -181,7 +181,7 @@ static int detect(struct i2c_adapter *adapter, int address, int kind) /* allocate memory for client structure */ client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (0 == client) { + if (!client) { printk("not enough kernel memory\n"); return -ENOMEM; } diff --git a/linux/drivers/media/video/tea6415c.c b/linux/drivers/media/video/tea6415c.c index 363b5be2c..16f369195 100644 --- a/linux/drivers/media/video/tea6415c.c +++ b/linux/drivers/media/video/tea6415c.c @@ -73,7 +73,7 @@ static int detect(struct i2c_adapter *adapter, int address, int kind) /* allocate memory for client structure */ client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (0 == client) { + if (!client) { return -ENOMEM; } diff --git a/linux/drivers/media/video/tea6420.c b/linux/drivers/media/video/tea6420.c index 0e3e039f2..7a8fec9cf 100644 --- a/linux/drivers/media/video/tea6420.c +++ b/linux/drivers/media/video/tea6420.c @@ -110,7 +110,7 @@ static int tea6420_detect(struct i2c_adapter *adapter, int address, int kind) /* allocate memory for client structure */ client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (0 == client) { + if (!client) { return -ENOMEM; } diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index 38a90afb6..fa218e910 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -802,7 +802,7 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) if (analog_ops->standby) analog_ops->standby(&t->fe); break; -#ifdef CONFIG_VIDEO_V4L1 +#ifdef CONFIG_VIDEO_ALLOW_V4L1 case VIDIOCSAUDIO: if (check_mode(t, "VIDIOCSAUDIO") == EINVAL) return 0; diff --git a/linux/drivers/media/video/tuner-xc2028-types.h b/linux/drivers/media/video/tuner-xc2028-types.h index d0057fbf0..e38970f1c 100644 --- a/linux/drivers/media/video/tuner-xc2028-types.h +++ b/linux/drivers/media/video/tuner-xc2028-types.h @@ -1,6 +1,9 @@ /* tuner-xc2028_types * - * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org) + * This file includes internal tipes to be used inside tuner-xc2028. + * Shouldn't be included outside tuner-xc2028 + * + * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org) * This code is placed under the terms of the GNU General Public License v2 */ @@ -85,11 +88,28 @@ /* This flag identifies that the scode table has a new format */ #define HAS_IF (1 << 30) -#define SCODE_TYPES (MTS|DTV6|QAM|DTV7|DTV78|DTV8|LCD|NOGD|MONO|ATSC|IF| \ +#if 0 +/* The current seek algorithm doesn'use those flags. Instead, this is + currently useful only for debug process, since this is a HINT table + associating an alias to an IF table that can be used by that digital + video standard (in the case of DTV6/7/78/8, QAM and ATSC), or to a + particular demod type + */ +#define SCODE_TYPES (DTV6|QAM|DTV7|DTV78|DTV8|LCD|NOGD|MONO|ATSC|IF| \ LG60|ATI638|OREN538|OREN36|TOYOTA388|TOYOTA794| \ DIBCOM52|ZARLINK456|CHINA|F6MHZ|SCODE) +#else +#define SCODE_TYPES SCODE +#endif -/* Newer types to be moved to videodev2.h */ + +/* Newer types not defined on videodev2.h. + The original idea were to move all those types to videodev2.h, but + it seemed overkill, since, with the exception of SECAM/K3, the other + types seem to be autodetected. + It is not clear where secam/k3 is used, nor we have a feedback of this + working or being autodetected by the standard secam firmware. + */ #define V4L2_STD_SECAM_K3 (0x04000000) diff --git a/linux/drivers/media/video/tuner-xc2028.c b/linux/drivers/media/video/tuner-xc2028.c index b333b911c..ce066dc97 100644 --- a/linux/drivers/media/video/tuner-xc2028.c +++ b/linux/drivers/media/video/tuner-xc2028.c @@ -1,6 +1,6 @@ /* tuner-xc2028 * - * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org) + * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org) * * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com) * - frontend interface @@ -145,7 +145,7 @@ struct xc2028_data { _rc; \ }) -static unsigned int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val) +static int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val) { unsigned char buf[2]; unsigned char ibuf[2]; @@ -250,6 +250,7 @@ static v4l2_std_id parse_audio_std_option(void) static void free_firmware(struct xc2028_data *priv) { int i; + tuner_dbg("%s called\n", __func__); if (!priv->firm) return; @@ -418,7 +419,7 @@ static int seek_firmware(struct dvb_frontend *fe, unsigned int type, { struct xc2028_data *priv = fe->tuner_priv; int i, best_i = -1, best_nr_matches = 0; - unsigned int ign_firm_type_mask = 0; + unsigned int type_mask = 0; tuner_dbg("%s called, want type=", __func__); if (debug) { @@ -435,18 +436,23 @@ static int seek_firmware(struct dvb_frontend *fe, unsigned int type, *id = V4L2_STD_PAL; if (type & BASE) - type &= BASE_TYPES; + type_mask = BASE_TYPES; else if (type & SCODE) { type &= SCODE_TYPES; - ign_firm_type_mask = HAS_IF; + type_mask = SCODE_TYPES &~ HAS_IF; } else if (type & DTV_TYPES) - type &= DTV_TYPES; + type_mask = DTV_TYPES; else if (type & STD_SPECIFIC_TYPES) - type &= STD_SPECIFIC_TYPES; + type_mask = STD_SPECIFIC_TYPES; + + type &= type_mask; + + if (!type & SCODE) + type_mask = ~0; /* Seek for exact match */ for (i = 0; i < priv->firm_size; i++) { - if ((type == (priv->firm[i].type & ~ign_firm_type_mask)) && + if ((type == (priv->firm[i].type & type_mask)) && (*id == priv->firm[i].id)) goto found; } @@ -456,7 +462,7 @@ static int seek_firmware(struct dvb_frontend *fe, unsigned int type, v4l2_std_id match_mask; int nr_matches; - if (type != (priv->firm[i].type & ~ign_firm_type_mask)) + if (type != (priv->firm[i].type & type_mask)) continue; match_mask = *id & priv->firm[i].id; @@ -793,10 +799,10 @@ check_device: goto fail; } - tuner_info("Device is Xceive %d version %d.%d, " - "firmware version %d.%d\n", - hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8, - (version & 0xf0) >> 4, version & 0xf); + tuner_dbg("Device is Xceive %d version %d.%d, " + "firmware version %d.%d\n", + hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8, + (version & 0xf0) >> 4, version & 0xf); /* Check firmware version against what we downloaded. */ if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) { @@ -1065,6 +1071,10 @@ static int xc2028_set_params(struct dvb_frontend *fe, T_DIGITAL_TV, type, 0, demod); } +#if 0 +/* This is needed at sleep (S1/S3), but not at fe_standby. Otherwise, + firmware will be loaded on every open() + */ static int xc2028_sleep(struct dvb_frontend *fe) { struct xc2028_data *priv = fe->tuner_priv; @@ -1085,7 +1095,7 @@ static int xc2028_sleep(struct dvb_frontend *fe) return rc; } - +#endif static int xc2028_dvb_release(struct dvb_frontend *fe) { @@ -1133,21 +1143,21 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) mutex_lock(&priv->lock); - kfree(priv->ctrl.fname); - free_firmware(priv); - memcpy(&priv->ctrl, p, sizeof(priv->ctrl)); - priv->ctrl.fname = NULL; + if (priv->ctrl.max_len < 9) + priv->ctrl.max_len = 13; if (p->fname) { + if (priv->ctrl.fname && strcmp(p->fname, priv->ctrl.fname)) { + kfree(priv->ctrl.fname); + free_firmware(priv); + } + priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL); if (priv->ctrl.fname == NULL) rc = -ENOMEM; } - if (priv->ctrl.max_len < 9) - priv->ctrl.max_len = 13; - mutex_unlock(&priv->lock); return rc; @@ -1167,8 +1177,9 @@ static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = { .get_frequency = xc2028_get_frequency, .get_rf_strength = xc2028_signal, .set_params = xc2028_set_params, +#if 0 .sleep = xc2028_sleep, - +#endif #if 0 int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); int (*get_status)(struct dvb_frontend *fe, u32 *status); diff --git a/linux/drivers/media/video/tuner-xc2028.h b/linux/drivers/media/video/tuner-xc2028.h index 612e49063..fc2f132a5 100644 --- a/linux/drivers/media/video/tuner-xc2028.h +++ b/linux/drivers/media/video/tuner-xc2028.h @@ -1,6 +1,6 @@ /* tuner-xc2028 * - * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org) + * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org) * This code is placed under the terms of the GNU General Public License v2 */ @@ -12,7 +12,7 @@ #define XC2028_DEFAULT_FIRMWARE "xc3028-v27.fw" /* Dmoduler IF (kHz) */ -#define XC3028_FE_DEFAULT 0 +#define XC3028_FE_DEFAULT 0 /* Don't load SCODE */ #define XC3028_FE_LG60 6000 #define XC3028_FE_ATI638 6380 #define XC3028_FE_OREN538 5380 diff --git a/linux/drivers/media/video/tvp5150.c b/linux/drivers/media/video/tvp5150.c index e6907db8b..7dc6623cc 100644 --- a/linux/drivers/media/video/tvp5150.c +++ b/linux/drivers/media/video/tvp5150.c @@ -1175,12 +1175,12 @@ static int tvp5150_detect_client(struct i2c_adapter *adapter, return 0; c = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (c == 0) + if (!c) return -ENOMEM; memcpy(c, &client_template, sizeof(struct i2c_client)); core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL); - if (core == 0) { + if (!core) { kfree(c); return -ENOMEM; } diff --git a/linux/drivers/media/video/usbvideo/ibmcam.c b/linux/drivers/media/video/usbvideo/ibmcam.c index 1261593cf..1afbcc147 100644 --- a/linux/drivers/media/video/usbvideo/ibmcam.c +++ b/linux/drivers/media/video/usbvideo/ibmcam.c @@ -802,6 +802,21 @@ static enum ParseState ibmcam_model2_320x240_parse_lines( return scan_Continue; } +/* + * ibmcam_model3_parse_lines() + * + * | Even lines | Odd Lines | + * -----------------------------------| + * |YYY........Y|UYVYUYVY.........UYVY| + * |YYY........Y|UYVYUYVY.........UYVY| + * |............|.....................| + * |YYY........Y|UYVYUYVY.........UYVY| + * |------------+---------------------| + * + * There is one (U, V) chroma pair for every four luma (Y) values. This + * function reads a pair of lines at a time and obtains missing chroma values + * from adjacent pixels. + */ static enum ParseState ibmcam_model3_parse_lines( struct uvd *uvd, struct usbvideo_frame *frame, @@ -816,6 +831,7 @@ static enum ParseState ibmcam_model3_parse_lines( const int ccm = 128; /* Color correction median - see below */ int i, u, v, rw, data_w=0, data_h=0, color_corr; static unsigned char lineBuffer[640*3]; + int line; color_corr = (uvd->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1); @@ -869,15 +885,15 @@ static enum ParseState ibmcam_model3_parse_lines( return scan_NextFrame; } - /* Make sure there's enough data for the entire line */ - len = 3 * data_w; /* <y-data> <uv-data> */ + /* Make sure that lineBuffer can store two lines of data */ + len = 3 * data_w; /* <y-data> <uyvy-data> */ assert(len <= sizeof(lineBuffer)); - /* Make sure there's enough data for the entire line */ + /* Make sure there's enough data for two lines */ if (RingQueue_GetLength(&uvd->dp) < len) return scan_Out; - /* Suck one line out of the ring queue */ + /* Suck two lines of data out of the ring queue */ RingQueue_Dequeue(&uvd->dp, lineBuffer, len); data = lineBuffer; @@ -887,15 +903,23 @@ static enum ParseState ibmcam_model3_parse_lines( rw = (int)VIDEOSIZE_Y(frame->request) - (int)(frame->curline) - 1; RESTRICT_TO_RANGE(rw, 0, VIDEOSIZE_Y(frame->request)-1); - for (i = 0; i < VIDEOSIZE_X(frame->request); i++) { - int y, rv, gv, bv; /* RGB components */ + /* Iterate over two lines. */ + for (line = 0; line < 2; line++) { + for (i = 0; i < VIDEOSIZE_X(frame->request); i++) { + int y; + int rv, gv, bv; /* RGB components */ - if (i < data_w) { - y = data[i]; /* Luminosity is the first line */ + if (i >= data_w) { + RGB24_PUTPIXEL(frame, i, rw, 0, 0, 0); + continue; + } + + /* first line is YYY...Y; second is UYVY...UYVY */ + y = data[(line == 0) ? i : (i*2 + 1)]; /* Apply static color correction */ - u = color[i*2] + hue_corr; - v = color[i*2 + 1] + hue2_corr; + u = color[(i/2)*4] + hue_corr; + v = color[(i/2)*4 + 2] + hue2_corr; /* Apply color correction */ if (color_corr != 0) { @@ -903,13 +927,21 @@ static enum ParseState ibmcam_model3_parse_lines( u = 128 + ((ccm + color_corr) * (u - 128)) / ccm; v = 128 + ((ccm + color_corr) * (v - 128)) / ccm; } - } else - y = 0, u = v = 128; - YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv); - RGB24_PUTPIXEL(frame, i, rw, rv, gv, bv); /* Done by deinterlacing now */ + + YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv); + RGB24_PUTPIXEL(frame, i, rw, rv, gv, bv); /* No deinterlacing */ + } + + /* Check for the end of requested data */ + if (rw == 0) + break; + + /* Prepare for the second line */ + rw--; + data = lineBuffer + data_w; } - frame->deinterlace = Deinterlace_FillEvenLines; + frame->deinterlace = Deinterlace_None; /* * Account for number of bytes that we wrote into output V4L frame. diff --git a/linux/drivers/media/video/videocodec.c b/linux/drivers/media/video/videocodec.c index b30b6b2a9..cf24956f3 100644 --- a/linux/drivers/media/video/videocodec.c +++ b/linux/drivers/media/video/videocodec.c @@ -39,6 +39,7 @@ #ifdef CONFIG_PROC_FS #include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <asm/uaccess.h> #endif @@ -320,56 +321,22 @@ videocodec_unregister (const struct videocodec *codec) } #ifdef CONFIG_PROC_FS -/* ============ */ -/* procfs stuff */ -/* ============ */ - -static char *videocodec_buf = NULL; -static int videocodec_bufsize; - -static int -videocodec_build_table (void) +static int proc_videocodecs_show(struct seq_file *m, void *v) { struct codec_list *h = codeclist_top; struct attached_list *a; - int i = 0, size; - - // sum up amount of slaves plus their attached masters - while (h) { - i += h->attached + 1; - h = h->next; - } -#define LINESIZE 100 - size = LINESIZE * (i + 1); - dprintk(3, "videocodec_build table: %d entries, %d bytes\n", i, - size); - - kfree(videocodec_buf); - videocodec_buf = kmalloc(size, GFP_KERNEL); - - if (!videocodec_buf) - return 0; - - i = 0; - i += scnprintf(videocodec_buf + i, size - 1, - "<S>lave or attached <M>aster name type flags magic "); - i += scnprintf(videocodec_buf + i, size -i - 1, "(connected as)\n"); + seq_printf(m, "<S>lave or attached <M>aster name type flags magic "); + seq_printf(m, "(connected as)\n"); h = codeclist_top; while (h) { - if (i > (size - LINESIZE)) - break; // security check - i += scnprintf(videocodec_buf + i, size -i -1, - "S %32s %04x %08lx %08lx (TEMPLATE)\n", + seq_printf(m, "S %32s %04x %08lx %08lx (TEMPLATE)\n", h->codec->name, h->codec->type, h->codec->flags, h->codec->magic); a = h->list; while (a) { - if (i > (size - LINESIZE)) - break; // security check - i += scnprintf(videocodec_buf + i, size -i -1, - "M %32s %04x %08lx %08lx (%s)\n", + seq_printf(m, "M %32s %04x %08lx %08lx (%s)\n", a->codec->master_data->name, a->codec->master_data->type, a->codec->master_data->flags, @@ -380,54 +347,21 @@ videocodec_build_table (void) h = h->next; } - return i; + return 0; } -//The definition: -//typedef int (read_proc_t)(char *page, char **start, off_t off, -// int count, int *eof, void *data); - -static int -videocodec_info (char *buffer, - char **buffer_location, - off_t offset, - int buffer_length, - int *eof, - void *data) +static int proc_videocodecs_open(struct inode *inode, struct file *file) { - int size; - - dprintk(3, "videocodec_info: offset: %ld, len %d / size %d\n", - offset, buffer_length, videocodec_bufsize); - - if (offset == 0) { - videocodec_bufsize = videocodec_build_table(); - } - if ((offset < 0) || (offset >= videocodec_bufsize)) { - dprintk(4, - "videocodec_info: call delivers no result, return 0\n"); - *eof = 1; - return 0; - } - - if (buffer_length < (videocodec_bufsize - offset)) { - dprintk(4, "videocodec_info: %ld needed, %d got\n", - videocodec_bufsize - offset, buffer_length); - size = buffer_length; - } else { - dprintk(4, "videocodec_info: last reading of %ld bytes\n", - videocodec_bufsize - offset); - size = videocodec_bufsize - offset; - *eof = 1; - } - - memcpy(buffer, videocodec_buf + offset, size); - /* doesn't work... */ - /* copy_to_user(buffer, videocodec_buf+offset, size); */ - /* *buffer_location = videocodec_buf+offset; */ - - return size; + return single_open(file, proc_videocodecs_show, NULL); } + +static const struct file_operations videocodecs_proc_fops = { + .owner = THIS_MODULE, + .open = proc_videocodecs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; #endif /* ===================== */ @@ -444,16 +378,8 @@ videocodec_init (void) VIDEOCODEC_VERSION); #ifdef CONFIG_PROC_FS - videocodec_buf = NULL; - videocodec_bufsize = 0; - - videocodec_proc_entry = create_proc_entry("videocodecs", 0, NULL); - if (videocodec_proc_entry) { - videocodec_proc_entry->read_proc = videocodec_info; - videocodec_proc_entry->write_proc = NULL; - videocodec_proc_entry->data = NULL; - videocodec_proc_entry->owner = THIS_MODULE; - } else { + videocodec_proc_entry = proc_create("videocodecs", 0, NULL, &videocodecs_proc_fops); + if (!videocodec_proc_entry) { dprintk(1, KERN_ERR "videocodec: can't init procfs.\n"); } #endif @@ -465,7 +391,6 @@ videocodec_exit (void) { #ifdef CONFIG_PROC_FS remove_proc_entry("videocodecs", NULL); - kfree(videocodec_buf); #endif } diff --git a/linux/drivers/media/video/zoran.h b/linux/drivers/media/video/zoran.h index 848dbcf25..5f74688c1 100644 --- a/linux/drivers/media/video/zoran.h +++ b/linux/drivers/media/video/zoran.h @@ -243,10 +243,8 @@ struct zoran_format { #ifdef CONFIG_VIDEO_V4L1_COMPAT int palette; #endif -#ifdef CONFIG_VIDEO_V4L2 __u32 fourcc; int colorspace; -#endif int depth; __u32 flags; __u32 vfespfr; @@ -271,20 +269,6 @@ struct zoran_v4l_settings { const struct zoran_format *format; /* capture format */ }; -/* whoops, this one is undeclared if !v4l2 */ -#ifndef CONFIG_VIDEO_V4L2 -struct v4l2_jpegcompression { - int quality; - int APPn; - int APP_len; - char APP_data[60]; - int COM_len; - char COM_data[60]; - __u32 jpeg_markers; - __u8 reserved[116]; -}; -#endif - /* jpg-capture/-playback settings */ struct zoran_jpg_settings { int decimation; /* this bit is used to set everything to default */ diff --git a/linux/drivers/media/video/zoran_driver.c b/linux/drivers/media/video/zoran_driver.c index aede0b584..6b7a7c137 100644 --- a/linux/drivers/media/video/zoran_driver.c +++ b/linux/drivers/media/video/zoran_driver.c @@ -88,7 +88,6 @@ #include "zoran_device.h" #include "zoran_card.h" -#ifdef CONFIG_VIDEO_V4L2 /* we declare some card type definitions here, they mean * the same as the v4l1 ZORAN_VID_TYPE above, except it's v4l2 */ #define ZORAN_V4L2_VID_FLAGS ( \ @@ -97,19 +96,15 @@ V4L2_CAP_VIDEO_OUTPUT |\ V4L2_CAP_VIDEO_OVERLAY \ ) -#endif #include <asm/byteorder.h> -#if defined(CONFIG_VIDEO_V4L2) && defined(CONFIG_VIDEO_V4L1_COMPAT) +#if defined(CONFIG_VIDEO_V4L1_COMPAT) #define ZFMT(pal, fcc, cs) \ .palette = (pal), .fourcc = (fcc), .colorspace = (cs) -#elif defined(CONFIG_VIDEO_V4L2) -#define ZFMT(pal, fcc, cs) \ - .fourcc = (fcc), .colorspace = (cs) #else #define ZFMT(pal, fcc, cs) \ - .palette = (pal) + .fourcc = (fcc), .colorspace = (cs) #endif const struct zoran_format zoran_formats[] = { @@ -219,7 +214,6 @@ static int lock_norm; /* 0 = default 1 = Don't change TV standard (norm) */ module_param(lock_norm, int, 0644); MODULE_PARM_DESC(lock_norm, "Prevent norm changes (1 = ignore, >1 = fail)"); -#ifdef CONFIG_VIDEO_V4L2 /* small helper function for calculating buffersizes for v4l2 * we calculate the nearest higher power-of-two, which * will be the recommended buffersize */ @@ -242,7 +236,6 @@ zoran_v4l2_calc_bufsize (struct zoran_jpg_settings *settings) return 8192; return result; } -#endif /* forward references */ static void v4l_fbuffer_free(struct file *file); @@ -353,7 +346,7 @@ v4l_fbuffer_alloc (struct file *file) /* Use kmalloc */ mem = kmalloc(fh->v4l_buffers.buffer_size, GFP_KERNEL); - if (mem == 0) { + if (!mem) { dprintk(1, KERN_ERR "%s: v4l_fbuffer_alloc() - kmalloc for V4L buf %d failed\n", @@ -1776,7 +1769,6 @@ setup_overlay (struct file *file, return wait_grab_pending(zr); } -#ifdef CONFIG_VIDEO_V4L2 /* get the status of a buffer in the clients buffer queue */ static int zoran_v4l2_buffer_status (struct file *file, @@ -1882,7 +1874,6 @@ zoran_v4l2_buffer_status (struct file *file, return 0; } -#endif static int zoran_set_norm (struct zoran *zr, @@ -2691,8 +2682,6 @@ zoran_do_ioctl (struct inode *inode, } break; -#ifdef CONFIG_VIDEO_V4L2 - /* The new video4linux2 capture interface - much nicer than video4linux1, since * it allows for integrating the JPEG capturing calls inside standard v4l2 */ @@ -4264,7 +4253,6 @@ zoran_do_ioctl (struct inode *inode, return 0; } break; -#endif default: dprintk(1, KERN_DEBUG "%s: UNKNOWN ioctl cmd: 0x%x\n", @@ -4724,9 +4712,7 @@ static const struct file_operations zoran_fops = { struct video_device zoran_template __devinitdata = { .name = ZORAN_NAME, .type = ZORAN_VID_TYPE, -#ifdef CONFIG_VIDEO_V4L2 .type2 = ZORAN_V4L2_VID_FLAGS, -#endif .fops = &zoran_fops, .release = &zoran_vdev_release, .minor = -1 diff --git a/linux/drivers/media/video/zr364xx.c b/linux/drivers/media/video/zr364xx.c index 0afd1c9c1..c2abb3af9 100644 --- a/linux/drivers/media/video/zr364xx.c +++ b/linux/drivers/media/video/zr364xx.c @@ -395,7 +395,7 @@ static int read_frame(struct zr364xx_camera *cam, int framenum) } -static ssize_t zr364xx_read(struct file *file, char *buf, size_t cnt, +static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t cnt, loff_t * ppos) { unsigned long count = cnt; |