diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-12-31 01:41:32 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-12-31 01:41:32 (GMT) |
commit | f54a6ec0fd85002d94d05b4bb679508eeb066683 (patch) | |
tree | 0f24dd66cce563d2c5e7656c2489e5b96eef31f9 /drivers/media | |
parent | 5ed1836814d908f45cafde0e79cb85314ab9d41d (diff) | |
parent | 134179823b3ca9c8b98e0631906459dbb022ff9b (diff) | |
download | linux-fsl-qoriq-f54a6ec0fd85002d94d05b4bb679508eeb066683.tar.xz |
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (583 commits)
V4L/DVB (10130): use USB API functions rather than constants
V4L/DVB (10129): dvb: remove deprecated use of RW_LOCK_UNLOCKED in frontends
V4L/DVB (10128): modify V4L documentation to be a valid XHTML
V4L/DVB (10127): stv06xx: Avoid having y unitialized
V4L/DVB (10125): em28xx: Don't do AC97 vendor detection for i2s audio devices
V4L/DVB (10124): em28xx: expand output formats available
V4L/DVB (10123): em28xx: fix reversed definitions of I2S audio modes
V4L/DVB (10122): em28xx: don't load em28xx-alsa for em2870 based devices
V4L/DVB (10121): em28xx: remove worthless Pinnacle PCTV HD Mini 80e device profile
V4L/DVB (10120): em28xx: remove redundant Pinnacle Dazzle DVC 100 profile
V4L/DVB (10119): em28xx: fix corrupted XCLK value
V4L/DVB (10118): zoran: fix warning for a variable not used
V4L/DVB (10116): af9013: Fix gcc false warnings
V4L/DVB (10111a): usbvideo.h: remove an useless blank line
V4L/DVB (10111): quickcam_messenger.c: fix a warning
V4L/DVB (10110): v4l2-ioctl: Fix warnings when using .unlocked_ioctl = __video_ioctl2
V4L/DVB (10109): anysee: Fix usage of an unitialized function
V4L/DVB (10104): uvcvideo: Add support for video output devices
V4L/DVB (10102): uvcvideo: Ignore interrupt endpoint for built-in iSight webcams.
V4L/DVB (10101): uvcvideo: Fix bulk URB processing when the header is erroneous
...
Diffstat (limited to 'drivers/media')
299 files changed, 36754 insertions, 10977 deletions
diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index 4952aeb..d8229a0 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -2391,6 +2391,67 @@ IR_KEYTAB_TYPE ir_codes_powercolor_real_angel[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_powercolor_real_angel); +/* Kworld Plus TV Analog Lite PCI IR + Mauro Carvalho Chehab <mchehab@infradead.org> + */ +IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE] = { + [0x0c] = KEY_PROG1, /* Kworld key */ + [0x16] = KEY_CLOSECD, /* -> ) */ + [0x1d] = KEY_POWER2, + + [0x00] = KEY_1, + [0x01] = KEY_2, + [0x02] = KEY_3, /* Two keys have the same code: 3 and left */ + [0x03] = KEY_4, /* Two keys have the same code: 3 and right */ + [0x04] = KEY_5, + [0x05] = KEY_6, + [0x06] = KEY_7, + [0x07] = KEY_8, + [0x08] = KEY_9, + [0x0a] = KEY_0, + + [0x09] = KEY_AGAIN, + [0x14] = KEY_MUTE, + + [0x20] = KEY_UP, + [0x21] = KEY_DOWN, + [0x0b] = KEY_ENTER, + + [0x10] = KEY_CHANNELUP, + [0x11] = KEY_CHANNELDOWN, + + /* Couldn't map key left/key right since those + conflict with '3' and '4' scancodes + I dunno what the original driver does + */ + + [0x13] = KEY_VOLUMEUP, + [0x12] = KEY_VOLUMEDOWN, + + /* The lower part of the IR + There are several duplicated keycodes there. + Most of them conflict with digits. + Add mappings just to the unused scancodes. + Somehow, the original driver has a way to know, + but this doesn't seem to be on some GPIO. + Also, it is not related to the time between keyup + and keydown. + */ + [0x19] = KEY_PAUSE, /* Timeshift */ + [0x1a] = KEY_STOP, + [0x1b] = KEY_RECORD, + + [0x22] = KEY_TEXT, + + [0x15] = KEY_AUDIO, /* ((*)) */ + [0x0f] = KEY_ZOOM, + [0x1c] = KEY_SHUFFLE, /* snapshot */ + + [0x18] = KEY_RED, /* B */ + [0x23] = KEY_GREEN, /* C */ +}; +EXPORT_SYMBOL_GPL(ir_codes_kworld_plus_tv_analog); + IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE] = { [0x20] = KEY_LIST, [0x00] = KEY_POWER, @@ -2511,3 +2572,35 @@ IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_real_audio_220_32_keys); + +/* ATI TV Wonder HD 600 USB + Devin Heitmueller <devin.heitmueller@gmail.com> + */ +IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE] = { + [0x00] = KEY_RECORD, /* Row 1 */ + [0x01] = KEY_PLAYPAUSE, + [0x02] = KEY_STOP, + [0x03] = KEY_POWER, + [0x04] = KEY_PREVIOUS, /* Row 2 */ + [0x05] = KEY_REWIND, + [0x06] = KEY_FORWARD, + [0x07] = KEY_NEXT, + [0x08] = KEY_EPG, /* Row 3 */ + [0x09] = KEY_HOME, + [0x0a] = KEY_MENU, + [0x0b] = KEY_CHANNELUP, + [0x0c] = KEY_BACK, /* Row 4 */ + [0x0d] = KEY_UP, + [0x0e] = KEY_INFO, + [0x0f] = KEY_CHANNELDOWN, + [0x10] = KEY_LEFT, /* Row 5 */ + [0x11] = KEY_SELECT, + [0x12] = KEY_RIGHT, + [0x13] = KEY_VOLUMEUP, + [0x14] = KEY_LAST, /* Row 6 */ + [0x15] = KEY_DOWN, + [0x16] = KEY_MUTE, + [0x17] = KEY_VOLUMEDOWN, +}; + +EXPORT_SYMBOL_GPL(ir_codes_ati_tv_wonder_hd_600); diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 127b052..7d844af 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -313,7 +313,7 @@ static int fops_ioctl(struct inode *inode, struct file *file, unsigned int cmd, /* DEB_EE(("inode:%p, file:%p, cmd:%d, arg:%li\n",inode, file, cmd, arg)); */ - return video_usercopy(inode, file, cmd, arg, saa7146_video_do_ioctl); + return video_usercopy(file, cmd, arg, saa7146_video_do_ioctl); } static int fops_mmap(struct file *file, struct vm_area_struct * vma) diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index fe0bd55..101b01d 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -834,7 +834,7 @@ static int video_end(struct saa7146_fh *fh, struct file *file) * copying is done already, arg is a kernel pointer. */ -static int __saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg) +int saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct saa7146_fh *fh = file->private_data; struct saa7146_dev *dev = fh->dev; @@ -1216,17 +1216,11 @@ static int __saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *a #endif default: return v4l_compat_translate_ioctl(file, cmd, arg, - __saa7146_video_do_ioctl); + saa7146_video_do_ioctl); } return 0; } -int saa7146_video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - return __saa7146_video_do_ioctl(file, cmd, arg); -} - /*********************************************************************************/ /* buffer handling functions */ diff --git a/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c index a887824..31522d2 100644 --- a/drivers/media/common/tuners/mxl5005s.c +++ b/drivers/media/common/tuners/mxl5005s.c @@ -3598,7 +3598,7 @@ static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum, 76, 77, 91, 134, 135, 137, 147, 156, 166, 167, 168, 25 }; - *count = sizeof(RegAddr) / sizeof(u8); + *count = ARRAY_SIZE(RegAddr); status += MXL_BlockInit(fe); @@ -3630,7 +3630,7 @@ static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum, u8 *RegVal, */ #endif - *count = sizeof(RegAddr) / sizeof(u8); + *count = ARRAY_SIZE(RegAddr); for (i = 0 ; i < *count; i++) { RegNum[i] = RegAddr[i]; @@ -3648,7 +3648,7 @@ static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum, u8 RegAddr[] = {43, 136}; - *count = sizeof(RegAddr) / sizeof(u8); + *count = ARRAY_SIZE(RegAddr); for (i = 0; i < *count; i++) { RegNum[i] = RegAddr[i]; diff --git a/drivers/media/common/tuners/tda827x.c b/drivers/media/common/tuners/tda827x.c index 4a74f65..f4d931f 100644 --- a/drivers/media/common/tuners/tda827x.c +++ b/drivers/media/common/tuners/tda827x.c @@ -80,10 +80,11 @@ static void tda827x_set_std(struct dvb_frontend *fe, mode = "xx"; } - if (params->mode == V4L2_TUNER_RADIO) + if (params->mode == V4L2_TUNER_RADIO) { priv->sgIF = 88; /* if frequency is 5.5 MHz */ - - dprintk("setting tda827x to system %s\n", mode); + dprintk("setting tda827x to radio FM\n"); + } else + dprintk("setting tda827x to system %s\n", mode); } @@ -199,7 +200,7 @@ static int tda827xo_set_params(struct dvb_frontend *fe, fe->ops.i2c_gate_ctrl(fe, 1); i2c_transfer(priv->i2c_adap, &msg, 1); - priv->frequency = tuner_freq - if_freq; // FIXME + priv->frequency = params->frequency; priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; return 0; @@ -304,7 +305,7 @@ static int tda827xo_set_analog_params(struct dvb_frontend *fe, reg2[1] = 0x08; /* Vsync en */ i2c_transfer(priv->i2c_adap, &msg, 1); - priv->frequency = freq * 62500; + priv->frequency = params->frequency; return 0; } @@ -591,7 +592,7 @@ static int tda827xa_set_params(struct dvb_frontend *fe, fe->ops.i2c_gate_ctrl(fe, 1); i2c_transfer(priv->i2c_adap, &msg, 1); - priv->frequency = tuner_freq - if_freq; // FIXME + priv->frequency = params->frequency; priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; return 0; @@ -691,7 +692,7 @@ static int tda827xa_set_analog_params(struct dvb_frontend *fe, tuner_reg[1] = 0x19 + (priv->lpsel << 1); i2c_transfer(priv->i2c_adap, &msg, 1); - priv->frequency = freq * 62500; + priv->frequency = params->frequency; return 0; } diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c index c112bdd..0ee79fd 100644 --- a/drivers/media/common/tuners/tda8290.c +++ b/drivers/media/common/tuners/tda8290.c @@ -32,6 +32,9 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); +static int deemphasis_50; +MODULE_PARM_DESC(deemphasis_50, "0 - 75us deemphasis; 1 - 50us deemphasis"); + /* ---------------------------------------------------------------------- */ struct tda8290_priv { @@ -139,9 +142,34 @@ static void set_audio(struct dvb_frontend *fe, mode = "xx"; } - tuner_dbg("setting tda829x to system %s\n", mode); + if (params->mode == V4L2_TUNER_RADIO) { + priv->tda8290_easy_mode = 0x01; /* Start with MN values */ + tuner_dbg("setting to radio FM\n"); + } else { + tuner_dbg("setting tda829x to system %s\n", mode); + } } +struct { + unsigned char seq[2]; +} fm_mode[] = { + { { 0x01, 0x81} }, /* Put device into expert mode */ + { { 0x03, 0x48} }, /* Disable NOTCH and VIDEO filters */ + { { 0x04, 0x04} }, /* Disable color carrier filter (SSIF) */ + { { 0x05, 0x04} }, /* ADC headroom */ + { { 0x06, 0x10} }, /* group delay flat */ + + { { 0x07, 0x00} }, /* use the same radio DTO values as a tda8295 */ + { { 0x08, 0x00} }, + { { 0x09, 0x80} }, + { { 0x0a, 0xda} }, + { { 0x0b, 0x4b} }, + { { 0x0c, 0x68} }, + + { { 0x0d, 0x00} }, /* PLL off, no video carrier detect */ + { { 0x14, 0x00} }, /* disable auto mute if no video */ +}; + static void tda8290_set_params(struct dvb_frontend *fe, struct analog_parameters *params) { @@ -178,15 +206,30 @@ static void tda8290_set_params(struct dvb_frontend *fe, tuner_i2c_xfer_send(&priv->i2c_props, soft_reset, 2); msleep(1); - expert_mode[1] = priv->tda8290_easy_mode + 0x80; - tuner_i2c_xfer_send(&priv->i2c_props, expert_mode, 2); - tuner_i2c_xfer_send(&priv->i2c_props, gainset_off, 2); - tuner_i2c_xfer_send(&priv->i2c_props, if_agc_spd, 2); - if (priv->tda8290_easy_mode & 0x60) - tuner_i2c_xfer_send(&priv->i2c_props, adc_head_9, 2); - else - tuner_i2c_xfer_send(&priv->i2c_props, adc_head_6, 2); - tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2); + if (params->mode == V4L2_TUNER_RADIO) { + int i; + unsigned char deemphasis[] = { 0x13, 1 }; + + /* FIXME: allow using a different deemphasis */ + + if (deemphasis_50) + deemphasis[1] = 2; + + for (i = 0; i < ARRAY_SIZE(fm_mode); i++) + tuner_i2c_xfer_send(&priv->i2c_props, fm_mode[i].seq, 2); + + tuner_i2c_xfer_send(&priv->i2c_props, deemphasis, 2); + } else { + expert_mode[1] = priv->tda8290_easy_mode + 0x80; + tuner_i2c_xfer_send(&priv->i2c_props, expert_mode, 2); + tuner_i2c_xfer_send(&priv->i2c_props, gainset_off, 2); + tuner_i2c_xfer_send(&priv->i2c_props, if_agc_spd, 2); + if (priv->tda8290_easy_mode & 0x60) + tuner_i2c_xfer_send(&priv->i2c_props, adc_head_9, 2); + else + tuner_i2c_xfer_send(&priv->i2c_props, adc_head_6, 2); + tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2); + } tda8290_i2c_bridge(fe, 1); diff --git a/drivers/media/common/tuners/tda9887.c b/drivers/media/common/tuners/tda9887.c index ff1788c..544cdbe 100644 --- a/drivers/media/common/tuners/tda9887.c +++ b/drivers/media/common/tuners/tda9887.c @@ -180,11 +180,10 @@ static struct tvnorm tvnorms[] = { },{ .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, .name = "SECAM-BGH", - .b = ( cPositiveAmTV | + .b = ( cNegativeFmTV | cQSS ), .c = ( cTopDefault), - .e = ( cGating_36 | - cAudioIF_5_5 | + .e = ( cAudioIF_5_5 | cVideoIF_38_90 ), },{ .std = V4L2_STD_SECAM_L, diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index b65e680..1adce9f 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -28,6 +28,12 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); +static int no_poweroff; +module_param(no_poweroff, int, 0644); +MODULE_PARM_DESC(debug, "0 (default) powers device off when not used.\n" + "1 keep device energized and with tuner ready all the times.\n" + " Faster, but consumes more power and keeps the device hotter\n"); + static char audio_std[8]; module_param_string(audio_std, audio_std, sizeof(audio_std), 0); MODULE_PARM_DESC(audio_std, @@ -1091,6 +1097,34 @@ static int xc2028_set_params(struct dvb_frontend *fe, T_DIGITAL_TV, type, 0, demod); } +static int xc2028_sleep(struct dvb_frontend *fe) +{ + struct xc2028_data *priv = fe->tuner_priv; + int rc = 0; + + /* Avoid firmware reload on slow devices */ + if (no_poweroff) + return 0; + + tuner_dbg("Putting xc2028/3028 into poweroff mode.\n"); + if (debug > 1) { + tuner_dbg("Printing sleep stack trace:\n"); + dump_stack(); + } + + mutex_lock(&priv->lock); + + if (priv->firm_version < 0x0202) + rc = send_seq(priv, {0x00, 0x08, 0x00, 0x00}); + else + rc = send_seq(priv, {0x80, 0x08, 0x00, 0x00}); + + priv->cur_fw.type = 0; /* need firmware reload */ + + mutex_unlock(&priv->lock); + + return rc; +} static int xc2028_dvb_release(struct dvb_frontend *fe) { @@ -1171,6 +1205,7 @@ 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, + .sleep = xc2028_sleep, }; struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index e12d13e..493ce93 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -36,10 +36,6 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); -static int xc5000_load_fw_on_attach; -module_param_named(init_fw, xc5000_load_fw_on_attach, int, 0644); -MODULE_PARM_DESC(init_fw, "Load firmware during driver initialization."); - static DEFINE_MUTEX(xc5000_list_mutex); static LIST_HEAD(hybrid_tuner_instance_list); @@ -1017,9 +1013,6 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, memcpy(&fe->ops.tuner_ops, &xc5000_tuner_ops, sizeof(struct dvb_tuner_ops)); - if (xc5000_load_fw_on_attach) - xc5000_init(fe); - return fe; fail: mutex_unlock(&xc5000_list_mutex); diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index 0bcd852..40ebde5 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig @@ -2,6 +2,19 @@ # DVB device configuration # +config DVB_DYNAMIC_MINORS + bool "Dynamic DVB minor allocation" + depends on DVB_CORE + default n + help + If you say Y here, the DVB subsystem will use dynamic minor + allocation for any device that uses the DVB major number. + This means that you can have more than 4 of a single type + of device (like demuxes and frontends) per adapter, but udev + will be required to manage the device nodes. + + If you are unsure about this, say N here. + menuconfig DVB_CAPTURE_DRIVERS bool "DVB/ATSC adapters" depends on DVB_CORE diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig index b34301d..a8c6249 100644 --- a/drivers/media/dvb/b2c2/Kconfig +++ b/drivers/media/dvb/b2c2/Kconfig @@ -14,6 +14,7 @@ config DVB_B2C2_FLEXCOP select DVB_ISL6421 if !DVB_FE_CUSTOMISE select DVB_CX24123 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE + select DVB_TUNER_CX24113 if !DVB_FE_CUSTOMISE help Support for the digital TV receiver chip made by B2C2 Inc. included in Technisats PCI cards and USB boxes. diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index a21ce9e..f48f73a 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c @@ -19,7 +19,6 @@ * */ -#include <linux/version.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/kernel.h> @@ -368,7 +367,7 @@ static int __devinit dm1105dvb_dma_map(struct dm1105dvb *dm1105dvb) { dm1105dvb->ts_buf = pci_alloc_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, &dm1105dvb->dma_addr); - return pci_dma_mapping_error(dm1105dvb->pdev, dm1105dvb->dma_addr); + return !dm1105dvb->ts_buf; } static void dm1105dvb_dma_unmap(struct dm1105dvb *dm1105dvb) diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 7a421e9..171f9ca 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -128,6 +128,7 @@ struct dvb_frontend_private { unsigned int step_size; int quality; unsigned int check_wrapped; + enum dvbfe_search algo_status; }; static void dvb_frontend_wakeup(struct dvb_frontend *fe); @@ -516,6 +517,8 @@ static int dvb_frontend_thread(void *data) struct dvb_frontend_private *fepriv = fe->frontend_priv; unsigned long timeout; fe_status_t s; + enum dvbfe_algo algo; + struct dvb_frontend_parameters *params; dprintk("%s\n", __func__); @@ -562,23 +565,80 @@ restart: /* do an iteration of the tuning loop */ if (fe->ops.get_frontend_algo) { - if (fe->ops.get_frontend_algo(fe) == FE_ALGO_HW) { - /* have we been asked to retune? */ - params = NULL; + algo = fe->ops.get_frontend_algo(fe); + switch (algo) { + case DVBFE_ALGO_HW: + dprintk("%s: Frontend ALGO = DVBFE_ALGO_HW\n", __func__); + params = NULL; /* have we been asked to RETUNE ? */ + if (fepriv->state & FESTATE_RETUNE) { + dprintk("%s: Retune requested, FESTATE_RETUNE\n", __func__); params = &fepriv->parameters; fepriv->state = FESTATE_TUNED; } - fe->ops.tune(fe, params, fepriv->tune_mode_flags, &fepriv->delay, &s); - if (s != fepriv->status) { + if (fe->ops.tune) + fe->ops.tune(fe, params, fepriv->tune_mode_flags, &fepriv->delay, &s); + + if (s != fepriv->status && !(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) { + dprintk("%s: state changed, adding current state\n", __func__); dvb_frontend_add_event(fe, s); fepriv->status = s; } - } else + break; + case DVBFE_ALGO_SW: + dprintk("%s: Frontend ALGO = DVBFE_ALGO_SW\n", __func__); dvb_frontend_swzigzag(fe); - } else + break; + case DVBFE_ALGO_CUSTOM: + params = NULL; /* have we been asked to RETUNE ? */ + dprintk("%s: Frontend ALGO = DVBFE_ALGO_CUSTOM, state=%d\n", __func__, fepriv->state); + if (fepriv->state & FESTATE_RETUNE) { + dprintk("%s: Retune requested, FESTAT_RETUNE\n", __func__); + params = &fepriv->parameters; + fepriv->state = FESTATE_TUNED; + } + /* Case where we are going to search for a carrier + * User asked us to retune again for some reason, possibly + * requesting a search with a new set of parameters + */ + if (fepriv->algo_status & DVBFE_ALGO_SEARCH_AGAIN) { + if (fe->ops.search) { + fepriv->algo_status = fe->ops.search(fe, &fepriv->parameters); + /* We did do a search as was requested, the flags are + * now unset as well and has the flags wrt to search. + */ + } else { + fepriv->algo_status &= ~DVBFE_ALGO_SEARCH_AGAIN; + } + } + /* Track the carrier if the search was successful */ + if (fepriv->algo_status == DVBFE_ALGO_SEARCH_SUCCESS) { + if (fe->ops.track) + fe->ops.track(fe, &fepriv->parameters); + } else { + fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN; + fepriv->delay = HZ / 2; + } + fe->ops.read_status(fe, &s); + if (s != fepriv->status) { + dvb_frontend_add_event(fe, s); /* update event list */ + fepriv->status = s; + if (!(s & FE_HAS_LOCK)) { + fepriv->delay = HZ / 10; + fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN; + } else { + fepriv->delay = 60 * HZ; + } + } + break; + default: + dprintk("%s: UNDEFINED ALGO !\n", __func__); + break; + } + } else { dvb_frontend_swzigzag(fe); + } } if (dvb_powerdown_on_sleep) { @@ -1226,6 +1286,9 @@ int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp, dprintk("%s() Finalised property cache\n", __func__); dtv_property_cache_submit(fe); + /* Request the search algorithm to search */ + fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN; + r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND, &fepriv->parameters); break; diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index db4a63b..e176da4 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -69,6 +69,125 @@ struct analog_parameters { u64 std; }; +enum dvbfe_modcod { + DVBFE_MODCOD_DUMMY_PLFRAME = 0, + DVBFE_MODCOD_QPSK_1_4, + DVBFE_MODCOD_QPSK_1_3, + DVBFE_MODCOD_QPSK_2_5, + DVBFE_MODCOD_QPSK_1_2, + DVBFE_MODCOD_QPSK_3_5, + DVBFE_MODCOD_QPSK_2_3, + DVBFE_MODCOD_QPSK_3_4, + DVBFE_MODCOD_QPSK_4_5, + DVBFE_MODCOD_QPSK_5_6, + DVBFE_MODCOD_QPSK_8_9, + DVBFE_MODCOD_QPSK_9_10, + DVBFE_MODCOD_8PSK_3_5, + DVBFE_MODCOD_8PSK_2_3, + DVBFE_MODCOD_8PSK_3_4, + DVBFE_MODCOD_8PSK_5_6, + DVBFE_MODCOD_8PSK_8_9, + DVBFE_MODCOD_8PSK_9_10, + DVBFE_MODCOD_16APSK_2_3, + DVBFE_MODCOD_16APSK_3_4, + DVBFE_MODCOD_16APSK_4_5, + DVBFE_MODCOD_16APSK_5_6, + DVBFE_MODCOD_16APSK_8_9, + DVBFE_MODCOD_16APSK_9_10, + DVBFE_MODCOD_32APSK_3_4, + DVBFE_MODCOD_32APSK_4_5, + DVBFE_MODCOD_32APSK_5_6, + DVBFE_MODCOD_32APSK_8_9, + DVBFE_MODCOD_32APSK_9_10, + DVBFE_MODCOD_RESERVED_1, + DVBFE_MODCOD_BPSK_1_3, + DVBFE_MODCOD_BPSK_1_4, + DVBFE_MODCOD_RESERVED_2 +}; + +enum tuner_param { + DVBFE_TUNER_FREQUENCY = (1 << 0), + DVBFE_TUNER_TUNERSTEP = (1 << 1), + DVBFE_TUNER_IFFREQ = (1 << 2), + DVBFE_TUNER_BANDWIDTH = (1 << 3), + DVBFE_TUNER_REFCLOCK = (1 << 4), + DVBFE_TUNER_IQSENSE = (1 << 5), + DVBFE_TUNER_DUMMY = (1 << 31) +}; + +/* + * ALGO_HW: (Hardware Algorithm) + * ---------------------------------------------------------------- + * Devices that support this algorithm do everything in hardware + * and no software support is needed to handle them. + * Requesting these devices to LOCK is the only thing required, + * device is supposed to do everything in the hardware. + * + * ALGO_SW: (Software Algorithm) + * ---------------------------------------------------------------- + * These are dumb devices, that require software to do everything + * + * ALGO_CUSTOM: (Customizable Agorithm) + * ---------------------------------------------------------------- + * Devices having this algorithm can be customized to have specific + * algorithms in the frontend driver, rather than simply doing a + * software zig-zag. In this case the zigzag maybe hardware assisted + * or it maybe completely done in hardware. In all cases, usage of + * this algorithm, in conjunction with the search and track + * callbacks, utilizes the driver specific algorithm. + * + * ALGO_RECOVERY: (Recovery Algorithm) + * ---------------------------------------------------------------- + * These devices have AUTO recovery capabilities from LOCK failure + */ +enum dvbfe_algo { + DVBFE_ALGO_HW = (1 << 0), + DVBFE_ALGO_SW = (1 << 1), + DVBFE_ALGO_CUSTOM = (1 << 2), + DVBFE_ALGO_RECOVERY = (1 << 31) +}; + +struct tuner_state { + u32 frequency; + u32 tunerstep; + u32 ifreq; + u32 bandwidth; + u32 iqsense; + u32 refclock; +}; + +/* + * search callback possible return status + * + * DVBFE_ALGO_SEARCH_SUCCESS + * The frontend search algorithm completed and returned succesfully + * + * DVBFE_ALGO_SEARCH_ASLEEP + * The frontend search algorithm is sleeping + * + * DVBFE_ALGO_SEARCH_FAILED + * The frontend search for a signal failed + * + * DVBFE_ALGO_SEARCH_INVALID + * The frontend search algorith was probably supplied with invalid + * parameters and the search is an invalid one + * + * DVBFE_ALGO_SEARCH_ERROR + * The frontend search algorithm failed due to some error + * + * DVBFE_ALGO_SEARCH_AGAIN + * The frontend search algorithm was requested to search again + */ +enum dvbfe_search { + DVBFE_ALGO_SEARCH_SUCCESS = (1 << 0), + DVBFE_ALGO_SEARCH_ASLEEP = (1 << 1), + DVBFE_ALGO_SEARCH_FAILED = (1 << 2), + DVBFE_ALGO_SEARCH_INVALID = (1 << 3), + DVBFE_ALGO_SEARCH_AGAIN = (1 << 4), + DVBFE_ALGO_SEARCH_ERROR = (1 << 31), +}; + + struct dvb_tuner_ops { struct dvb_tuner_info info; @@ -99,6 +218,13 @@ struct dvb_tuner_ops { * tuners which require sophisticated tuning loops, controlling each parameter seperately. */ int (*set_frequency)(struct dvb_frontend *fe, u32 frequency); int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth); + + /* + * These are provided seperately from set_params in order to facilitate silicon + * tuners which require sophisticated tuning loops, controlling each parameter seperately. + */ + int (*set_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state); + int (*get_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state); }; struct analog_demod_info { @@ -142,7 +268,7 @@ struct dvb_frontend_ops { unsigned int *delay, fe_status_t *status); /* get frontend tuning algorithm from the module */ - int (*get_frontend_algo)(struct dvb_frontend *fe); + enum dvbfe_algo (*get_frontend_algo)(struct dvb_frontend *fe); /* these two are only used for the swzigzag code */ int (*set_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); @@ -167,6 +293,12 @@ struct dvb_frontend_ops { int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable); int (*ts_bus_ctrl)(struct dvb_frontend* fe, int acquire); + /* These callbacks are for devices that implement their own + * tuning algorithms, rather than a simple swzigzag + */ + enum dvbfe_search (*search)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p); + int (*track)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p); + struct dvb_tuner_ops tuner_ops; struct analog_demod_ops analog_ops; diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c index a113744..6c571d9 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.c +++ b/drivers/media/dvb/dvb-core/dvbdev.c @@ -50,33 +50,27 @@ static const char * const dnames[] = { "net", "osd" }; +#ifdef CONFIG_DVB_DYNAMIC_MINORS +#define MAX_DVB_MINORS 256 +#define DVB_MAX_IDS MAX_DVB_MINORS +#else #define DVB_MAX_IDS 4 #define nums2minor(num,type,id) ((num << 6) | (id << 4) | type) #define MAX_DVB_MINORS (DVB_MAX_ADAPTERS*64) +#endif static struct class *dvb_class; -static struct dvb_device* dvbdev_find_device (int minor) -{ - struct dvb_adapter *adap; - - list_for_each_entry(adap, &dvb_adapter_list, list_head) { - struct dvb_device *dev; - list_for_each_entry(dev, &adap->device_list, list_head) - if (nums2minor(adap->num, dev->type, dev->id) == minor) - return dev; - } - - return NULL; -} - +static struct dvb_device *dvb_minors[MAX_DVB_MINORS]; +static DECLARE_RWSEM(minor_rwsem); static int dvb_device_open(struct inode *inode, struct file *file) { struct dvb_device *dvbdev; lock_kernel(); - dvbdev = dvbdev_find_device (iminor(inode)); + down_read(&minor_rwsem); + dvbdev = dvb_minors[iminor(inode)]; if (dvbdev && dvbdev->fops) { int err = 0; @@ -92,9 +86,11 @@ static int dvb_device_open(struct inode *inode, struct file *file) file->f_op = fops_get(old_fops); } fops_put(old_fops); + up_read(&minor_rwsem); unlock_kernel(); return err; } + up_read(&minor_rwsem); unlock_kernel(); return -ENODEV; } @@ -192,6 +188,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, struct dvb_device *dvbdev; struct file_operations *dvbdevfops; struct device *clsdev; + int minor; int id; mutex_lock(&dvbdev_register_lock); @@ -231,11 +228,31 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, list_add_tail (&dvbdev->list_head, &adap->device_list); + down_write(&minor_rwsem); +#ifdef CONFIG_DVB_DYNAMIC_MINORS + for (minor = 0; minor < MAX_DVB_MINORS; minor++) + if (dvb_minors[minor] == NULL) + break; + + if (minor == MAX_DVB_MINORS) { + kfree(dvbdevfops); + kfree(dvbdev); + mutex_unlock(&dvbdev_register_lock); + return -EINVAL; + } +#else + minor = nums2minor(adap->num, type, id); +#endif + + dvbdev->minor = minor; + dvb_minors[minor] = dvbdev; + up_write(&minor_rwsem); + mutex_unlock(&dvbdev_register_lock); clsdev = device_create(dvb_class, adap->device, - MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)), - NULL, "dvb%d.%s%d", adap->num, dnames[type], id); + MKDEV(DVB_MAJOR, minor), + dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id); if (IS_ERR(clsdev)) { printk(KERN_ERR "%s: failed to create device dvb%d.%s%d (%ld)\n", __func__, adap->num, dnames[type], id, PTR_ERR(clsdev)); @@ -243,8 +260,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, } dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", - adap->num, dnames[type], id, nums2minor(adap->num, type, id), - nums2minor(adap->num, type, id)); + adap->num, dnames[type], id, minor, minor); return 0; } @@ -256,8 +272,11 @@ void dvb_unregister_device(struct dvb_device *dvbdev) if (!dvbdev) return; - device_destroy(dvb_class, MKDEV(DVB_MAJOR, nums2minor(dvbdev->adapter->num, - dvbdev->type, dvbdev->id))); + down_write(&minor_rwsem); + dvb_minors[dvbdev->minor] = NULL; + up_write(&minor_rwsem); + + device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor)); list_del (&dvbdev->list_head); kfree (dvbdev->fops); @@ -413,6 +432,15 @@ out: return err; } +static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct dvb_device *dvbdev = dev_get_drvdata(dev); + + add_uevent_var(env, "DVB_DEVICE_NUM=%d", dvbdev->id); + add_uevent_var(env, "DVB_ADAPTER_NUM=%d", dvbdev->adapter->num); + return 0; +} + static int __init init_dvbdev(void) { int retval; @@ -434,6 +462,7 @@ static int __init init_dvbdev(void) retval = PTR_ERR(dvb_class); goto error; } + dvb_class->dev_uevent = dvb_uevent; return 0; error: diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h index 574e336..dca49cf 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.h +++ b/drivers/media/dvb/dvb-core/dvbdev.h @@ -74,6 +74,7 @@ struct dvb_device { struct file_operations *fops; struct dvb_adapter *adapter; int type; + int minor; u32 id; /* in theory, 'users' can vanish now, diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index e9ab024..e1e9aa5 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -733,9 +733,19 @@ static int af9015_read_config(struct usb_device *udev) af9015_config.ir_table_size = ARRAY_SIZE(af9015_ir_table_mygictv); break; + case AF9015_REMOTE_DIGITTRADE_DVB_T: + af9015_properties[i].rc_key_map = + af9015_rc_keys_digittrade; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_digittrade); + af9015_config.ir_table = + af9015_ir_table_digittrade; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_digittrade); + break; } } else { - switch (udev->descriptor.idVendor) { + switch (le16_to_cpu(udev->descriptor.idVendor)) { case USB_VID_LEADTEK: af9015_properties[i].rc_key_map = af9015_rc_keys_leadtek; @@ -748,7 +758,7 @@ static int af9015_read_config(struct usb_device *udev) break; case USB_VID_VISIONPLUS: if (udev->descriptor.idProduct == - USB_PID_AZUREWAVE_AD_TU700) { + cpu_to_le16(USB_PID_AZUREWAVE_AD_TU700)) { af9015_properties[i].rc_key_map = af9015_rc_keys_twinhan; af9015_properties[i].rc_key_map_size = @@ -800,6 +810,16 @@ static int af9015_read_config(struct usb_device *udev) ARRAY_SIZE(af9015_ir_table_msi); } break; + case USB_VID_AVERMEDIA: + af9015_properties[i].rc_key_map = + af9015_rc_keys_avermedia; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_avermedia); + af9015_config.ir_table = + af9015_ir_table_avermedia; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_avermedia); + break; } } } @@ -1191,6 +1211,7 @@ static struct usb_device_id af9015_usb_table[] = { {USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)}, {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)}, /* 15 */{USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III)}, + {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U)}, {0}, }; MODULE_DEVICE_TABLE(usb, af9015_usb_table); @@ -1343,7 +1364,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .i2c_algo = &af9015_i2c_algo, - .num_device_descs = 6, + .num_device_descs = 7, .devices = { { .name = "Xtensions XD-380", @@ -1375,6 +1396,12 @@ static struct dvb_usb_device_properties af9015_properties[] = { .cold_ids = {&af9015_usb_table[15], NULL}, .warm_ids = {NULL}, }, + { + .name = "KWorld USB DVB-T TV Stick II " \ + "(VS-DVB-T 395U)", + .cold_ids = {&af9015_usb_table[16], NULL}, + .warm_ids = {NULL}, + }, } } }; diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h index 6c3c972..21c7782 100644 --- a/drivers/media/dvb/dvb-usb/af9015.h +++ b/drivers/media/dvb/dvb-usb/af9015.h @@ -123,6 +123,7 @@ enum af9015_remote { AF9015_REMOTE_A_LINK_DTU_M, AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, AF9015_REMOTE_MYGICTV_U718, + AF9015_REMOTE_DIGITTRADE_DVB_T, }; /* Leadtek WinFast DTV Dongle Gold */ @@ -520,4 +521,143 @@ static u8 af9015_ir_table_kworld[] = { 0x86, 0x6b, 0x23, 0xdc, 0x45, 0x07, 0x00, }; +/* AverMedia Volar X */ +static struct dvb_usb_rc_key af9015_rc_keys_avermedia[] = { + { 0x05, 0x3d, KEY_PROG1 }, /* SOURCE */ + { 0x05, 0x12, KEY_POWER }, /* POWER */ + { 0x05, 0x1e, KEY_1 }, /* 1 */ + { 0x05, 0x1f, KEY_2 }, /* 2 */ + { 0x05, 0x20, KEY_3 }, /* 3 */ + { 0x05, 0x21, KEY_4 }, /* 4 */ + { 0x05, 0x22, KEY_5 }, /* 5 */ + { 0x05, 0x23, KEY_6 }, /* 6 */ + { 0x05, 0x24, KEY_7 }, /* 7 */ + { 0x05, 0x25, KEY_8 }, /* 8 */ + { 0x05, 0x26, KEY_9 }, /* 9 */ + { 0x05, 0x3f, KEY_LEFT }, /* L / DISPLAY */ + { 0x05, 0x27, KEY_0 }, /* 0 */ + { 0x05, 0x0f, KEY_RIGHT }, /* R / CH RTN */ + { 0x05, 0x18, KEY_PROG2 }, /* SNAP SHOT */ + { 0x05, 0x1c, KEY_PROG3 }, /* 16-CH PREV */ + { 0x05, 0x2d, KEY_VOLUMEDOWN }, /* VOL DOWN */ + { 0x05, 0x3e, KEY_ZOOM }, /* FULL SCREEN */ + { 0x05, 0x2e, KEY_VOLUMEUP }, /* VOL UP */ + { 0x05, 0x10, KEY_MUTE }, /* MUTE */ + { 0x05, 0x04, KEY_AUDIO }, /* AUDIO */ + { 0x05, 0x15, KEY_RECORD }, /* RECORD */ + { 0x05, 0x11, KEY_PLAY }, /* PLAY */ + { 0x05, 0x16, KEY_STOP }, /* STOP */ + { 0x05, 0x0c, KEY_PLAYPAUSE }, /* TIMESHIFT / PAUSE */ + { 0x05, 0x05, KEY_BACK }, /* << / RED */ + { 0x05, 0x09, KEY_FORWARD }, /* >> / YELLOW */ + { 0x05, 0x17, KEY_TEXT }, /* TELETEXT */ + { 0x05, 0x0a, KEY_EPG }, /* EPG */ + { 0x05, 0x13, KEY_MENU }, /* MENU */ + + { 0x05, 0x0e, KEY_CHANNELUP }, /* CH UP */ + { 0x05, 0x0d, KEY_CHANNELDOWN }, /* CH DOWN */ + { 0x05, 0x19, KEY_FIRST }, /* |<< / GREEN */ + { 0x05, 0x08, KEY_LAST }, /* >>| / BLUE */ +}; + +static u8 af9015_ir_table_avermedia[] = { + 0x02, 0xfd, 0x00, 0xff, 0x12, 0x05, 0x00, + 0x02, 0xfd, 0x01, 0xfe, 0x3d, 0x05, 0x00, + 0x02, 0xfd, 0x03, 0xfc, 0x17, 0x05, 0x00, + 0x02, 0xfd, 0x04, 0xfb, 0x0a, 0x05, 0x00, + 0x02, 0xfd, 0x05, 0xfa, 0x1e, 0x05, 0x00, + 0x02, 0xfd, 0x06, 0xf9, 0x1f, 0x05, 0x00, + 0x02, 0xfd, 0x07, 0xf8, 0x20, 0x05, 0x00, + 0x02, 0xfd, 0x09, 0xf6, 0x21, 0x05, 0x00, + 0x02, 0xfd, 0x0a, 0xf5, 0x22, 0x05, 0x00, + 0x02, 0xfd, 0x0b, 0xf4, 0x23, 0x05, 0x00, + 0x02, 0xfd, 0x0d, 0xf2, 0x24, 0x05, 0x00, + 0x02, 0xfd, 0x0e, 0xf1, 0x25, 0x05, 0x00, + 0x02, 0xfd, 0x0f, 0xf0, 0x26, 0x05, 0x00, + 0x02, 0xfd, 0x11, 0xee, 0x27, 0x05, 0x00, + 0x02, 0xfd, 0x08, 0xf7, 0x04, 0x05, 0x00, + 0x02, 0xfd, 0x0c, 0xf3, 0x3e, 0x05, 0x00, + 0x02, 0xfd, 0x10, 0xef, 0x1c, 0x05, 0x00, + 0x02, 0xfd, 0x12, 0xed, 0x3f, 0x05, 0x00, + 0x02, 0xfd, 0x13, 0xec, 0x0f, 0x05, 0x00, + 0x02, 0xfd, 0x14, 0xeb, 0x10, 0x05, 0x00, + 0x02, 0xfd, 0x15, 0xea, 0x13, 0x05, 0x00, + 0x02, 0xfd, 0x17, 0xe8, 0x18, 0x05, 0x00, + 0x02, 0xfd, 0x18, 0xe7, 0x11, 0x05, 0x00, + 0x02, 0xfd, 0x19, 0xe6, 0x15, 0x05, 0x00, + 0x02, 0xfd, 0x1a, 0xe5, 0x0c, 0x05, 0x00, + 0x02, 0xfd, 0x1b, 0xe4, 0x16, 0x05, 0x00, + 0x02, 0xfd, 0x1c, 0xe3, 0x09, 0x05, 0x00, + 0x02, 0xfd, 0x1d, 0xe2, 0x05, 0x05, 0x00, + 0x02, 0xfd, 0x1e, 0xe1, 0x2d, 0x05, 0x00, + 0x02, 0xfd, 0x1f, 0xe0, 0x2e, 0x05, 0x00, + 0x03, 0xfc, 0x00, 0xff, 0x08, 0x05, 0x00, + 0x03, 0xfc, 0x01, 0xfe, 0x19, 0x05, 0x00, + 0x03, 0xfc, 0x02, 0xfd, 0x0d, 0x05, 0x00, + 0x03, 0xfc, 0x03, 0xfc, 0x0e, 0x05, 0x00, +}; + +/* Digittrade DVB-T USB Stick */ +static struct dvb_usb_rc_key af9015_rc_keys_digittrade[] = { + { 0x01, 0x0f, KEY_LAST }, /* RETURN */ + { 0x05, 0x17, KEY_TEXT }, /* TELETEXT */ + { 0x01, 0x08, KEY_EPG }, /* EPG */ + { 0x05, 0x13, KEY_POWER }, /* POWER */ + { 0x01, 0x09, KEY_ZOOM }, /* FULLSCREEN */ + { 0x00, 0x40, KEY_AUDIO }, /* DUAL SOUND */ + { 0x00, 0x2c, KEY_PRINT }, /* SNAPSHOT */ + { 0x05, 0x16, KEY_SUBTITLE }, /* SUBTITLE */ + { 0x00, 0x52, KEY_CHANNELUP }, /* CH Up */ + { 0x00, 0x51, KEY_CHANNELDOWN },/* Ch Dn */ + { 0x00, 0x57, KEY_VOLUMEUP }, /* Vol Up */ + { 0x00, 0x56, KEY_VOLUMEDOWN }, /* Vol Dn */ + { 0x01, 0x10, KEY_MUTE }, /* MUTE */ + { 0x00, 0x27, KEY_0 }, + { 0x00, 0x1e, KEY_1 }, + { 0x00, 0x1f, KEY_2 }, + { 0x00, 0x20, KEY_3 }, + { 0x00, 0x21, KEY_4 }, + { 0x00, 0x22, KEY_5 }, + { 0x00, 0x23, KEY_6 }, + { 0x00, 0x24, KEY_7 }, + { 0x00, 0x25, KEY_8 }, + { 0x00, 0x26, KEY_9 }, + { 0x01, 0x17, KEY_PLAYPAUSE }, /* TIMESHIFT */ + { 0x01, 0x15, KEY_RECORD }, /* RECORD */ + { 0x03, 0x13, KEY_PLAY }, /* PLAY */ + { 0x01, 0x16, KEY_STOP }, /* STOP */ + { 0x01, 0x13, KEY_PAUSE }, /* PAUSE */ +}; + +static u8 af9015_ir_table_digittrade[] = { + 0x00, 0xff, 0x06, 0xf9, 0x13, 0x05, 0x00, + 0x00, 0xff, 0x4d, 0xb2, 0x17, 0x01, 0x00, + 0x00, 0xff, 0x1f, 0xe0, 0x2c, 0x00, 0x00, + 0x00, 0xff, 0x0a, 0xf5, 0x15, 0x01, 0x00, + 0x00, 0xff, 0x0e, 0xf1, 0x16, 0x01, 0x00, + 0x00, 0xff, 0x09, 0xf6, 0x09, 0x01, 0x00, + 0x00, 0xff, 0x01, 0xfe, 0x08, 0x01, 0x00, + 0x00, 0xff, 0x05, 0xfa, 0x10, 0x01, 0x00, + 0x00, 0xff, 0x02, 0xfd, 0x56, 0x00, 0x00, + 0x00, 0xff, 0x40, 0xbf, 0x57, 0x00, 0x00, + 0x00, 0xff, 0x19, 0xe6, 0x52, 0x00, 0x00, + 0x00, 0xff, 0x17, 0xe8, 0x51, 0x00, 0x00, + 0x00, 0xff, 0x10, 0xef, 0x0f, 0x01, 0x00, + 0x00, 0xff, 0x54, 0xab, 0x27, 0x00, 0x00, + 0x00, 0xff, 0x1b, 0xe4, 0x1e, 0x00, 0x00, + 0x00, 0xff, 0x11, 0xee, 0x1f, 0x00, 0x00, + 0x00, 0xff, 0x15, 0xea, 0x20, 0x00, 0x00, + 0x00, 0xff, 0x12, 0xed, 0x21, 0x00, 0x00, + 0x00, 0xff, 0x16, 0xe9, 0x22, 0x00, 0x00, + 0x00, 0xff, 0x4c, 0xb3, 0x23, 0x00, 0x00, + 0x00, 0xff, 0x48, 0xb7, 0x24, 0x00, 0x00, + 0x00, 0xff, 0x04, 0xfb, 0x25, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x26, 0x00, 0x00, + 0x00, 0xff, 0x1e, 0xe1, 0x13, 0x03, 0x00, + 0x00, 0xff, 0x1a, 0xe5, 0x13, 0x01, 0x00, + 0x00, 0xff, 0x03, 0xfc, 0x17, 0x05, 0x00, + 0x00, 0xff, 0x0d, 0xf2, 0x16, 0x05, 0x00, + 0x00, 0xff, 0x1d, 0xe2, 0x40, 0x00, 0x00, +}; + #endif diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index cd2edbc..5017f08 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -153,7 +153,7 @@ static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); - int ret, inc, i = 0; + int ret = 0, inc, i = 0; if (mutex_lock_interruptible(&d->i2c_mutex) < 0) return -EAGAIN; diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-core.c b/drivers/media/dvb/dvb-usb/cinergyT2-core.c index 3ac9f74..80e37a0 100644 --- a/drivers/media/dvb/dvb-usb/cinergyT2-core.c +++ b/drivers/media/dvb/dvb-usb/cinergyT2-core.c @@ -32,7 +32,6 @@ /* debug */ int dvb_usb_cinergyt2_debug; -int disable_remote; module_param_named(debug, dvb_usb_cinergyt2_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=info, xfer=2, rc=4 " @@ -45,7 +44,7 @@ struct cinergyt2_state { }; /* We are missing a release hook with usb_device data */ -struct dvb_usb_device *cinergyt2_usb_device; +static struct dvb_usb_device *cinergyt2_usb_device; static struct dvb_usb_device_properties cinergyt2_properties; diff --git a/drivers/media/dvb/dvb-usb/cinergyT2.h b/drivers/media/dvb/dvb-usb/cinergyT2.h index 11d79eb..84efe03 100644 --- a/drivers/media/dvb/dvb-usb/cinergyT2.h +++ b/drivers/media/dvb/dvb-usb/cinergyT2.h @@ -70,11 +70,11 @@ struct dvbt_get_status_msg { uint8_t bandwidth; uint16_t tps; uint8_t flags; - uint16_t gain; + __le16 gain; uint8_t snr; - uint32_t viterbi_error_rate; + __le32 viterbi_error_rate; uint32_t rs_error_rate; - uint32_t uncorrected_block_count; + __le32 uncorrected_block_count; uint8_t lock_bits; uint8_t prev_lock_bits; } __attribute__((packed)); @@ -82,9 +82,9 @@ struct dvbt_get_status_msg { struct dvbt_set_parameters_msg { uint8_t cmd; - uint32_t freq; + __le32 freq; uint8_t bandwidth; - uint16_t tps; + __le16 tps; uint8_t flags; } __attribute__((packed)); diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 7380b94..a4fca3f 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -96,6 +96,7 @@ #define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 #define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 #define USB_PID_KWORLD_399U 0xe399 +#define USB_PID_KWORLD_395U 0xe396 #define USB_PID_KWORLD_PC160_2T 0xc160 #define USB_PID_KWORLD_VSTREAM_COLD 0x17de #define USB_PID_KWORLD_VSTREAM_WARM 0x17df diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 6286fbb..c65f273 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -9,7 +9,6 @@ * * see Documentation/dvb/README.dvb-usb for more information */ -#include <linux/version.h> #include "dw2102.h" #include "si21xx.h" #include "stv0299.h" @@ -27,6 +26,10 @@ #define USB_PID_DW2104 0x2104 #endif +#ifndef USB_PID_CINERGY_S +#define USB_PID_CINERGY_S 0x0064 +#endif + #define DW210X_READ_MSG 0 #define DW210X_WRITE_MSG 1 @@ -578,6 +581,7 @@ static struct usb_device_id dw2102_table[] = { {USB_DEVICE(USB_VID_CYPRESS, 0x2101)}, {USB_DEVICE(USB_VID_CYPRESS, 0x2104)}, {USB_DEVICE(0x9022, 0xd650)}, + {USB_DEVICE(USB_VID_TERRATEC, USB_PID_CINERGY_S)}, { } }; @@ -647,6 +651,7 @@ static int dw2102_load_firmware(struct usb_device *dev, dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, DW210X_WRITE_MSG); break; + case USB_PID_CINERGY_S: case USB_PID_DW2102: dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, DW210X_WRITE_MSG); @@ -655,7 +660,7 @@ static int dw2102_load_firmware(struct usb_device *dev, /* check STV0299 frontend */ dw210x_op_rw(dev, 0xb5, 0, 0, &reset16[0], 2, DW210X_READ_MSG); - if (reset16[0] == 0xa1) { + if ((reset16[0] == 0xa1) || (reset16[0] == 0x80)) { dw2102_properties.i2c_algo = &dw2102_i2c_algo; dw2102_properties.adapter->tuner_attach = &dw2102_tuner_attach; break; @@ -726,7 +731,7 @@ static struct dvb_usb_device_properties dw2102_properties = { }, } }, - .num_device_descs = 2, + .num_device_descs = 3, .devices = { {"DVBWorld DVB-S 2102 USB2.0", {&dw2102_table[0], NULL}, @@ -736,6 +741,10 @@ static struct dvb_usb_device_properties dw2102_properties = { {&dw2102_table[1], NULL}, {NULL}, }, + {"TerraTec Cinergy S USB", + {&dw2102_table[4], NULL}, + {NULL}, + }, } }; diff --git a/drivers/media/dvb/dvb-usb/gp8psk-fe.c b/drivers/media/dvb/dvb-usb/gp8psk-fe.c index 262a858..20eadf9 100644 --- a/drivers/media/dvb/dvb-usb/gp8psk-fe.c +++ b/drivers/media/dvb/dvb-usb/gp8psk-fe.c @@ -25,6 +25,20 @@ struct gp8psk_fe_state { unsigned long status_check_interval; }; +static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + u8 status; + gp8psk_usb_in_op(st->d, GET_8PSK_CONFIG, 0, 0, &status, 1); + return status & bmDCtuned; +} + +static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode) +{ + struct gp8psk_fe_state *state = fe->demodulator_priv; + return gp8psk_usb_out_op(state->d, SET_8PSK_CONFIG, mode, 0, NULL, 0); +} + static int gp8psk_fe_update_status(struct gp8psk_fe_state *st) { u8 buf[6]; @@ -99,39 +113,114 @@ static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_front return 0; } +static int gp8psk_fe_set_property(struct dvb_frontend *fe, + struct dtv_property *tvp) +{ + deb_fe("%s(..)\n", __func__); + return 0; +} + +static int gp8psk_fe_get_property(struct dvb_frontend *fe, + struct dtv_property *tvp) +{ + deb_fe("%s(..)\n", __func__); + return 0; +} + + static int gp8psk_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep) { struct gp8psk_fe_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; u8 cmd[10]; u32 freq = fep->frequency * 1000; + int gp_product_id = le16_to_cpu(state->d->udev->descriptor.idProduct); + + deb_fe("%s()\n", __func__); cmd[4] = freq & 0xff; cmd[5] = (freq >> 8) & 0xff; cmd[6] = (freq >> 16) & 0xff; cmd[7] = (freq >> 24) & 0xff; - switch(fe->ops.info.type) { - case FE_QPSK: - cmd[0] = fep->u.qpsk.symbol_rate & 0xff; - cmd[1] = (fep->u.qpsk.symbol_rate >> 8) & 0xff; - cmd[2] = (fep->u.qpsk.symbol_rate >> 16) & 0xff; - cmd[3] = (fep->u.qpsk.symbol_rate >> 24) & 0xff; - cmd[8] = ADV_MOD_DVB_QPSK; - cmd[9] = 0x03; /*ADV_MOD_FEC_XXX*/ + switch (c->delivery_system) { + case SYS_DVBS: + /* Only QPSK is supported for DVB-S */ + if (c->modulation != QPSK) { + deb_fe("%s: unsupported modulation selected (%d)\n", + __func__, c->modulation); + return -EOPNOTSUPP; + } + c->fec_inner = FEC_AUTO; break; + case SYS_DVBS2: + deb_fe("%s: DVB-S2 delivery system selected\n", __func__); + break; + default: - // other modes are unsuported right now - cmd[0] = 0; - cmd[1] = 0; - cmd[2] = 0; - cmd[3] = 0; - cmd[8] = 0; + deb_fe("%s: unsupported delivery system selected (%d)\n", + __func__, c->delivery_system); + return -EOPNOTSUPP; + } + + cmd[0] = c->symbol_rate & 0xff; + cmd[1] = (c->symbol_rate >> 8) & 0xff; + cmd[2] = (c->symbol_rate >> 16) & 0xff; + cmd[3] = (c->symbol_rate >> 24) & 0xff; + switch (c->modulation) { + case QPSK: + if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) + if (gp8psk_tuned_to_DCII(fe)) + gp8psk_bcm4500_reload(state->d); + switch (c->fec_inner) { + case FEC_1_2: + cmd[9] = 0; break; + case FEC_2_3: + cmd[9] = 1; break; + case FEC_3_4: + cmd[9] = 2; break; + case FEC_5_6: + cmd[9] = 3; break; + case FEC_7_8: + cmd[9] = 4; break; + case FEC_AUTO: + cmd[9] = 5; break; + default: + cmd[9] = 5; break; + } + cmd[8] = ADV_MOD_DVB_QPSK; + break; + case PSK_8: /* PSK_8 is for compatibility with DN */ + cmd[8] = ADV_MOD_TURBO_8PSK; + switch (c->fec_inner) { + case FEC_2_3: + cmd[9] = 0; break; + case FEC_3_4: + cmd[9] = 1; break; + case FEC_3_5: + cmd[9] = 2; break; + case FEC_5_6: + cmd[9] = 3; break; + case FEC_8_9: + cmd[9] = 4; break; + default: + cmd[9] = 0; break; + } + break; + case QAM_16: /* QAM_16 is for compatibility with DN */ + cmd[8] = ADV_MOD_TURBO_16QAM; cmd[9] = 0; break; + default: /* Unknown modulation */ + deb_fe("%s: unsupported modulation selected (%d)\n", + __func__, c->modulation); + return -EOPNOTSUPP; } - gp8psk_usb_out_op(state->d,TUNE_8PSK,0,0,cmd,10); + if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) + gp8psk_set_tuner_mode(fe, 0); + gp8psk_usb_out_op(state->d, TUNE_8PSK, 0, 0, cmd, 10); state->lock = 0; state->next_status_check = jiffies; @@ -140,13 +229,6 @@ static int gp8psk_fe_set_frontend(struct dvb_frontend* fe, return 0; } -static int gp8psk_fe_get_frontend(struct dvb_frontend* fe, - struct dvb_frontend_parameters *fep) -{ - return 0; -} - - static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *m) { @@ -261,9 +343,13 @@ static struct dvb_frontend_ops gp8psk_fe_ops = { .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, /* ppm */ .caps = FE_CAN_INVERSION_AUTO | - 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_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_QAM_16 is for compatibility + * (Myth incorrectly detects Turbo-QPSK as plain QAM-16) + */ + FE_CAN_QPSK | FE_CAN_QAM_16 }, .release = gp8psk_fe_release, @@ -271,8 +357,10 @@ static struct dvb_frontend_ops gp8psk_fe_ops = { .init = NULL, .sleep = NULL, + .set_property = gp8psk_fe_set_property, + .get_property = gp8psk_fe_get_property, .set_frontend = gp8psk_fe_set_frontend, - .get_frontend = gp8psk_fe_get_frontend, + .get_tune_settings = gp8psk_fe_get_tune_settings, .read_status = gp8psk_fe_read_status, diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c index d965a92..c1da962 100644 --- a/drivers/media/dvb/dvb-usb/gp8psk.c +++ b/drivers/media/dvb/dvb-usb/gp8psk.c @@ -174,6 +174,22 @@ static int gp8psk_power_ctrl(struct dvb_usb_device *d, int onoff) return 0; } +int gp8psk_bcm4500_reload(struct dvb_usb_device *d) +{ + u8 buf; + int gp_product_id = le16_to_cpu(d->udev->descriptor.idProduct); + /* Turn off 8psk power */ + if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1)) + return -EINVAL; + /* Turn On 8psk power */ + if (gp8psk_usb_in_op(d, BOOT_8PSK, 1, 0, &buf, 1)) + return -EINVAL; + /* load BCM4500 firmware */ + if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) + if (gp8psk_load_bcm4500fw(d)) + return EINVAL; + return 0; +} static int gp8psk_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) { diff --git a/drivers/media/dvb/dvb-usb/gp8psk.h b/drivers/media/dvb/dvb-usb/gp8psk.h index e5cd814..e83a575 100644 --- a/drivers/media/dvb/dvb-usb/gp8psk.h +++ b/drivers/media/dvb/dvb-usb/gp8psk.h @@ -92,5 +92,6 @@ extern struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d); extern int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen); extern int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen); +extern int gp8psk_bcm4500_reload(struct dvb_usb_device *d); #endif diff --git a/drivers/media/dvb/dvb-usb/usb-urb.c b/drivers/media/dvb/dvb-usb/usb-urb.c index da93b9e..9da2cc9 100644 --- a/drivers/media/dvb/dvb-usb/usb-urb.c +++ b/drivers/media/dvb/dvb-usb/usb-urb.c @@ -156,7 +156,8 @@ static int usb_bulk_urb_init(struct usb_data_stream *stream) stream->props.u.bulk.buffersize, usb_urb_complete, stream); - stream->urb_list[i]->transfer_flags = 0; + stream->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + stream->urb_list[i]->transfer_dma = stream->dma_addr[i]; stream->urbs_initialized++; } return 0; diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 96b93e2..0026956 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -12,6 +12,25 @@ config DVB_FE_CUSTOMISE If unsure say N. +comment "Multistandard (satellite) frontends" + depends on DVB_CORE + +config DVB_STB0899 + tristate "STB0899 based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S/S2/DSS Multistandard demodulator. Say Y when you want + to support this demodulator based frontends + +config DVB_STB6100 + tristate "STB6100 based tuners" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A Silicon tuner from ST used in conjunction with the STB0899 + demodulator. Say Y when you want to support this tuner. + comment "DVB-S (satellite) frontends" depends on DVB_CORE @@ -78,6 +97,13 @@ config DVB_TDA10086 help A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_TDA8261 + tristate "Philips TDA8261 based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S tuner module. Say Y when you want to support this frontend. + config DVB_VES1X93 tristate "VLSI VES1893 or VES1993 based" depends on DVB_CORE && I2C @@ -92,6 +118,14 @@ config DVB_TUNER_ITD1000 help A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_TUNER_CX24113 + tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S tuner module. Say Y when you want to support this frontend. + + config DVB_TDA826X tristate "Philips TDA826X silicon tuner" depends on DVB_CORE && I2C @@ -345,6 +379,14 @@ config DVB_LGDT330X An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. +config DVB_LGDT3304 + tristate "LG Electronics LGDT3304" + 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. + config DVB_S5H1409 tristate "Samsung S5H1409 based" depends on DVB_CORE && I2C @@ -369,6 +411,17 @@ config DVB_S5H1411 An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. +comment "ISDB-T (terrestrial) frontends" + depends on DVB_CORE + +config DVB_S921 + tristate "Sharp S921 tuner" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module. + Say Y when you want to support this frontend. + comment "Digital terrestrial only tuners/PLL" depends on DVB_CORE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index aba79f4..af7bdf0 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -5,8 +5,13 @@ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ EXTRA_CFLAGS += -Idrivers/media/common/tuners/ +s921-objs := s921_module.o s921_core.o +stb0899-objs = stb0899_drv.o stb0899_algo.o + obj-$(CONFIG_DVB_PLL) += dvb-pll.o obj-$(CONFIG_DVB_STV0299) += stv0299.o +obj-$(CONFIG_DVB_STB0899) += stb0899.o +obj-$(CONFIG_DVB_STB6100) += stb6100.o obj-$(CONFIG_DVB_SP8870) += sp8870.o obj-$(CONFIG_DVB_CX22700) += cx22700.o obj-$(CONFIG_DVB_CX24110) += cx24110.o @@ -35,18 +40,21 @@ obj-$(CONFIG_DVB_OR51132) += or51132.o obj-$(CONFIG_DVB_BCM3510) += bcm3510.o obj-$(CONFIG_DVB_S5H1420) += s5h1420.o obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o +obj-$(CONFIG_DVB_LGDT3304) += lgdt3304.o obj-$(CONFIG_DVB_CX24123) += cx24123.o obj-$(CONFIG_DVB_LNBP21) += lnbp21.o obj-$(CONFIG_DVB_ISL6405) += isl6405.o obj-$(CONFIG_DVB_ISL6421) += isl6421.o obj-$(CONFIG_DVB_TDA10086) += tda10086.o obj-$(CONFIG_DVB_TDA826X) += tda826x.o +obj-$(CONFIG_DVB_TDA8261) += tda8261.o obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o obj-$(CONFIG_DVB_TUA6100) += tua6100.o obj-$(CONFIG_DVB_S5H1409) += s5h1409.o obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o obj-$(CONFIG_DVB_AU8522) += au8522.o obj-$(CONFIG_DVB_TDA10048) += tda10048.o +obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o obj-$(CONFIG_DVB_S5H1411) += s5h1411.o obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o @@ -55,3 +63,5 @@ obj-$(CONFIG_DVB_CX24116) += cx24116.o obj-$(CONFIG_DVB_SI21XX) += si21xx.o obj-$(CONFIG_DVB_STV0288) += stv0288.o obj-$(CONFIG_DVB_STB6000) += stb6000.o +obj-$(CONFIG_DVB_S921) += s921.o + diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c index 692b68a..b2b50fb 100644 --- a/drivers/media/dvb/frontends/af9013.c +++ b/drivers/media/dvb/frontends/af9013.c @@ -223,12 +223,12 @@ static int af9013_set_coeff(struct af9013_state *state, fe_bandwidth_t bw) int ret = 0; u8 i = 0; u8 buf[24]; - u32 ns_coeff1_2048nu; - u32 ns_coeff1_8191nu; - u32 ns_coeff1_8192nu; - u32 ns_coeff1_8193nu; - u32 ns_coeff2_2k; - u32 ns_coeff2_8k; + u32 uninitialized_var(ns_coeff1_2048nu); + u32 uninitialized_var(ns_coeff1_8191nu); + u32 uninitialized_var(ns_coeff1_8192nu); + u32 uninitialized_var(ns_coeff1_8193nu); + u32 uninitialized_var(ns_coeff2_2k); + u32 uninitialized_var(ns_coeff2_8k); deb_info("%s: adc_clock:%d bw:%d\n", __func__, state->config.adc_clock, bw); @@ -1009,7 +1009,7 @@ static int af9013_update_snr(struct dvb_frontend *fe) int ret; u8 buf[3], i, len; u32 quant = 0; - struct snr_table *snr_table; + struct snr_table *uninitialized_var(snr_table); /* check if quantizer ready (for snr) */ ret = af9013_read_reg_bits(state, 0xd2e1, 3, 1, &buf[0]); diff --git a/drivers/media/dvb/frontends/cx24113.c b/drivers/media/dvb/frontends/cx24113.c new file mode 100644 index 0000000..f6e7b03 --- /dev/null +++ b/drivers/media/dvb/frontends/cx24113.c @@ -0,0 +1,616 @@ +/* + * Driver for Conexant CX24113/CX24128 Tuner (Satellite) + * + * Copyright (C) 2007-8 Patrick Boettcher <pb@linuxtv.org> + * + * Developed for BBTI / Technisat + * + * 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/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> + +#include "dvb_frontend.h" +#include "cx24113.h" + +static int debug; + +#define info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0) +#define err(args...) do { printk(KERN_ERR "CX24113: " args); } while (0) + +#define dprintk(args...) \ + do { \ + if (debug) { \ + printk(KERN_DEBUG "CX24113: %s: ", __func__); \ + printk(args); \ + } \ + } while (0) + +struct cx24113_state { + struct i2c_adapter *i2c; + const struct cx24113_config *config; + +#define REV_CX24113 0x23 + u8 rev; + u8 ver; + + u8 icp_mode:1; + +#define ICP_LEVEL1 0 +#define ICP_LEVEL2 1 +#define ICP_LEVEL3 2 +#define ICP_LEVEL4 3 + u8 icp_man:2; + u8 icp_auto_low:2; + u8 icp_auto_mlow:2; + u8 icp_auto_mhi:2; + u8 icp_auto_hi:2; + u8 icp_dig; + +#define LNA_MIN_GAIN 0 +#define LNA_MID_GAIN 1 +#define LNA_MAX_GAIN 2 + u8 lna_gain:2; + + u8 acp_on:1; + + u8 vco_mode:2; + u8 vco_shift:1; +#define VCOBANDSEL_6 0x80 +#define VCOBANDSEL_5 0x01 +#define VCOBANDSEL_4 0x02 +#define VCOBANDSEL_3 0x04 +#define VCOBANDSEL_2 0x08 +#define VCOBANDSEL_1 0x10 + u8 vco_band; + +#define VCODIV4 4 +#define VCODIV2 2 + u8 vcodiv; + + u8 bs_delay:4; + u16 bs_freqcnt:13; + u16 bs_rdiv; + u8 prescaler_mode:1; + + u8 rfvga_bias_ctrl; + + s16 tuner_gain_thres; + u8 gain_level; + + u32 frequency; + + u8 refdiv; + + u8 Fwindow_enabled; +}; + +static int cx24113_writereg(struct cx24113_state *state, int reg, int data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->i2c_addr, + .flags = 0, .buf = buf, .len = 2 }; + int err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x," + " data == 0x%02x)\n", __func__, err, reg, data); + return err; + } + + return 0; +} + +static int cx24113_readreg(struct cx24113_state *state, u8 reg) +{ + int ret; + u8 b; + struct i2c_msg msg[] = { + { .addr = state->config->i2c_addr, + .flags = 0, .buf = ®, .len = 1 }, + { .addr = state->config->i2c_addr, + .flags = I2C_M_RD, .buf = &b, .len = 1 } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + printk(KERN_DEBUG "%s: reg=0x%x (error=%d)\n", + __func__, reg, ret); + return ret; + } + + return b; +} + +static void cx24113_set_parameters(struct cx24113_state *state) +{ + u8 r; + + r = cx24113_readreg(state, 0x10) & 0x82; + r |= state->icp_mode; + r |= state->icp_man << 4; + r |= state->icp_dig << 2; + r |= state->prescaler_mode << 5; + cx24113_writereg(state, 0x10, r); + + r = (state->icp_auto_low << 0) | (state->icp_auto_mlow << 2) + | (state->icp_auto_mhi << 4) | (state->icp_auto_hi << 6); + cx24113_writereg(state, 0x11, r); + + if (state->rev == REV_CX24113) { + r = cx24113_readreg(state, 0x20) & 0xec; + r |= state->lna_gain; + r |= state->rfvga_bias_ctrl << 4; + cx24113_writereg(state, 0x20, r); + } + + r = cx24113_readreg(state, 0x12) & 0x03; + r |= state->acp_on << 2; + r |= state->bs_delay << 4; + cx24113_writereg(state, 0x12, r); + + r = cx24113_readreg(state, 0x18) & 0x40; + r |= state->vco_shift; + if (state->vco_band == VCOBANDSEL_6) + r |= (1 << 7); + else + r |= (state->vco_band << 1); + cx24113_writereg(state, 0x18, r); + + r = cx24113_readreg(state, 0x14) & 0x20; + r |= (state->vco_mode << 6) | ((state->bs_freqcnt >> 8) & 0x1f); + cx24113_writereg(state, 0x14, r); + cx24113_writereg(state, 0x15, (state->bs_freqcnt & 0xff)); + + cx24113_writereg(state, 0x16, (state->bs_rdiv >> 4) & 0xff); + r = (cx24113_readreg(state, 0x17) & 0x0f) | + ((state->bs_rdiv & 0x0f) << 4); + cx24113_writereg(state, 0x17, r); +} + +#define VGA_0 0x00 +#define VGA_1 0x04 +#define VGA_2 0x02 +#define VGA_3 0x06 +#define VGA_4 0x01 +#define VGA_5 0x05 +#define VGA_6 0x03 +#define VGA_7 0x07 + +#define RFVGA_0 0x00 +#define RFVGA_1 0x01 +#define RFVGA_2 0x02 +#define RFVGA_3 0x03 + +static int cx24113_set_gain_settings(struct cx24113_state *state, + s16 power_estimation) +{ + u8 ampout = cx24113_readreg(state, 0x1d) & 0xf0, + vga = cx24113_readreg(state, 0x1f) & 0x3f, + rfvga = cx24113_readreg(state, 0x20) & 0xf3; + u8 gain_level = power_estimation >= state->tuner_gain_thres; + + dprintk("power estimation: %d, thres: %d, gain_level: %d/%d\n", + power_estimation, state->tuner_gain_thres, + state->gain_level, gain_level); + + if (gain_level == state->gain_level) + return 0; /* nothing to be done */ + + ampout |= 0xf; + + if (gain_level) { + rfvga |= RFVGA_0 << 2; + vga |= (VGA_7 << 3) | VGA_7; + } else { + rfvga |= RFVGA_2 << 2; + vga |= (VGA_6 << 3) | VGA_2; + } + state->gain_level = gain_level; + + cx24113_writereg(state, 0x1d, ampout); + cx24113_writereg(state, 0x1f, vga); + cx24113_writereg(state, 0x20, rfvga); + + return 1; /* did something */ +} + +static int cx24113_set_Fref(struct cx24113_state *state, u8 high) +{ + u8 xtal = cx24113_readreg(state, 0x02); + if (state->rev == 0x43 && state->vcodiv == VCODIV4) + high = 1; + + xtal &= ~0x2; + if (high) + xtal |= high << 1; + return cx24113_writereg(state, 0x02, xtal); +} + +static int cx24113_enable(struct cx24113_state *state, u8 enable) +{ + u8 r21 = (cx24113_readreg(state, 0x21) & 0xc0) | enable; + if (state->rev == REV_CX24113) + r21 |= (1 << 1); + return cx24113_writereg(state, 0x21, r21); +} + +static int cx24113_set_bandwidth(struct cx24113_state *state, u32 bandwidth_khz) +{ + u8 r; + + if (bandwidth_khz <= 19000) + r = 0x03 << 6; + else if (bandwidth_khz <= 25000) + r = 0x02 << 6; + else + r = 0x01 << 6; + + dprintk("bandwidth to be set: %d\n", bandwidth_khz); + bandwidth_khz *= 10; + bandwidth_khz -= 10000; + bandwidth_khz /= 1000; + bandwidth_khz += 5; + bandwidth_khz /= 10; + + dprintk("bandwidth: %d %d\n", r >> 6, bandwidth_khz); + + r |= bandwidth_khz & 0x3f; + + return cx24113_writereg(state, 0x1e, r); +} + +static int cx24113_set_clk_inversion(struct cx24113_state *state, u8 on) +{ + u8 r = (cx24113_readreg(state, 0x10) & 0x7f) | ((on & 0x1) << 7); + return cx24113_writereg(state, 0x10, r); +} + +static int cx24113_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct cx24113_state *state = fe->tuner_priv; + u8 r = (cx24113_readreg(state, 0x10) & 0x02) >> 1; + if (r) + *status |= TUNER_STATUS_LOCKED; + dprintk("PLL locked: %d\n", r); + return 0; +} + +static u8 cx24113_set_ref_div(struct cx24113_state *state, u8 refdiv) +{ + if (state->rev == 0x43 && state->vcodiv == VCODIV4) + refdiv = 2; + return state->refdiv = refdiv; +} + +static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f) +{ + s32 N; + s64 F; + u8 R, r; + u8 vcodiv; + u8 factor; + s32 freq_hz = state->frequency * 1000; + + if (state->config->xtal_khz < 20000) + factor = 1; + else + factor = 2; + + if (state->rev == REV_CX24113) { + if (state->frequency >= 1100000) + vcodiv = VCODIV2; + else + vcodiv = VCODIV4; + } else { + if (state->frequency >= 1165000) + vcodiv = VCODIV2; + else + vcodiv = VCODIV4; + } + state->vcodiv = vcodiv; + + dprintk("calculating N/F for %dHz with vcodiv %d\n", freq_hz, vcodiv); + R = 0; + do { + R = cx24113_set_ref_div(state, R + 1); + + /* calculate tuner PLL settings: */ + N = (freq_hz / 100 * vcodiv) * R; + N /= (state->config->xtal_khz) * factor * 2; + N += 5; /* For round up. */ + N /= 10; + N -= 32; + } while (N < 6 && R < 3); + + if (N < 6) { + err("strange frequency: N < 6\n"); + return; + } + F = freq_hz; + F *= (u64) (R * vcodiv * 262144); + dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R); + do_div(F, state->config->xtal_khz*1000 * factor * 2); + dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R); + F -= (N + 32) * 262144; + + dprintk("3 N: %d, F: %lld, R: %d\n", N, (long long)F, R); + + if (state->Fwindow_enabled) { + if (F > (262144 / 2 - 1638)) + F = 262144 / 2 - 1638; + if (F < (-262144 / 2 + 1638)) + F = -262144 / 2 + 1638; + if ((F < 3277 && F > 0) || (F > -3277 && F < 0)) { + F = 0; + r = cx24113_readreg(state, 0x10); + cx24113_writereg(state, 0x10, r | (1 << 6)); + } + } + dprintk("4 N: %d, F: %lld, R: %d\n", N, (long long)F, R); + + *n = (u16) N; + *f = (s32) F; +} + + +static void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r) +{ + u8 reg; + cx24113_writereg(state, 0x19, (n >> 1) & 0xff); + + reg = ((n & 0x1) << 7) | ((f >> 11) & 0x7f); + cx24113_writereg(state, 0x1a, reg); + + cx24113_writereg(state, 0x1b, (f >> 3) & 0xff); + + reg = cx24113_readreg(state, 0x1c) & 0x1f; + cx24113_writereg(state, 0x1c, reg | ((f & 0x7) << 5)); + + cx24113_set_Fref(state, r - 1); +} + +static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency) +{ + u8 r = 1; /* or 2 */ + u16 n = 6; + s32 f = 0; + + r = cx24113_readreg(state, 0x14); + cx24113_writereg(state, 0x14, r & 0x3f); + + r = cx24113_readreg(state, 0x10); + cx24113_writereg(state, 0x10, r & 0xbf); + + state->frequency = frequency; + + dprintk("tuning to frequency: %d\n", frequency); + + cx24113_calc_pll_nf(state, &n, &f); + cx24113_set_nfr(state, n, f, state->refdiv); + + r = cx24113_readreg(state, 0x18) & 0xbf; + if (state->vcodiv != VCODIV2) + r |= 1 << 6; + cx24113_writereg(state, 0x18, r); + + /* The need for this sleep is not clear. But helps in some cases */ + msleep(5); + + r = cx24113_readreg(state, 0x1c) & 0xef; + cx24113_writereg(state, 0x1c, r | (1 << 4)); + return 0; +} + +static int cx24113_init(struct dvb_frontend *fe) +{ + struct cx24113_state *state = fe->tuner_priv; + int ret; + + state->tuner_gain_thres = -50; + state->gain_level = 255; /* to force a gain-setting initialization */ + state->icp_mode = 0; + + if (state->config->xtal_khz < 11000) { + state->icp_auto_hi = ICP_LEVEL4; + state->icp_auto_mhi = ICP_LEVEL4; + state->icp_auto_mlow = ICP_LEVEL3; + state->icp_auto_low = ICP_LEVEL3; + } else { + state->icp_auto_hi = ICP_LEVEL4; + state->icp_auto_mhi = ICP_LEVEL4; + state->icp_auto_mlow = ICP_LEVEL3; + state->icp_auto_low = ICP_LEVEL2; + } + + state->icp_dig = ICP_LEVEL3; + state->icp_man = ICP_LEVEL1; + state->acp_on = 1; + state->vco_mode = 0; + state->vco_shift = 0; + state->vco_band = VCOBANDSEL_1; + state->bs_delay = 8; + state->bs_freqcnt = 0x0fff; + state->bs_rdiv = 0x0fff; + state->prescaler_mode = 0; + state->lna_gain = LNA_MAX_GAIN; + state->rfvga_bias_ctrl = 1; + state->Fwindow_enabled = 1; + + cx24113_set_Fref(state, 0); + cx24113_enable(state, 0x3d); + cx24113_set_parameters(state); + + cx24113_set_gain_settings(state, -30); + + cx24113_set_bandwidth(state, 18025); + cx24113_set_clk_inversion(state, 1); + + if (state->config->xtal_khz >= 40000) + ret = cx24113_writereg(state, 0x02, + (cx24113_readreg(state, 0x02) & 0xfb) | (1 << 2)); + else + ret = cx24113_writereg(state, 0x02, + (cx24113_readreg(state, 0x02) & 0xfb) | (0 << 2)); + + return ret; +} + +static int cx24113_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct cx24113_state *state = fe->tuner_priv; + /* for a ROLL-OFF factor of 0.35, 0.2: 600, 0.25: 625 */ + u32 roll_off = 675; + u32 bw; + + bw = ((p->u.qpsk.symbol_rate/100) * roll_off) / 1000; + bw += (10000000/100) + 5; + bw /= 10; + bw += 1000; + cx24113_set_bandwidth(state, bw); + + cx24113_set_frequency(state, p->frequency); + msleep(5); + return cx24113_get_status(fe, &bw); +} + +static s8 cx24113_agc_table[2][10] = { + {-54, -41, -35, -30, -25, -21, -16, -10, -6, -2}, + {-39, -35, -30, -25, -19, -15, -11, -5, 1, 9}, +}; + +void cx24113_agc_callback(struct dvb_frontend *fe) +{ + struct cx24113_state *state = fe->tuner_priv; + s16 s, i; + if (!fe->ops.read_signal_strength) + return; + + do { + /* this only works with the current CX24123 implementation */ + fe->ops.read_signal_strength(fe, (u16 *) &s); + s >>= 8; + dprintk("signal strength: %d\n", s); + for (i = 0; i < sizeof(cx24113_agc_table[0]); i++) + if (cx24113_agc_table[state->gain_level][i] > s) + break; + s = -25 - i*5; + } while (cx24113_set_gain_settings(state, s)); +} +EXPORT_SYMBOL(cx24113_agc_callback); + +static int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct cx24113_state *state = fe->tuner_priv; + *frequency = state->frequency; + return 0; +} + +static int cx24113_release(struct dvb_frontend *fe) +{ + struct cx24113_state *state = fe->tuner_priv; + dprintk("\n"); + fe->tuner_priv = NULL; + kfree(state); + return 0; +} + +static const struct dvb_tuner_ops cx24113_tuner_ops = { + .info = { + .name = "Conexant CX24113", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_step = 125, + }, + + .release = cx24113_release, + + .init = cx24113_init, + .sleep = NULL, + + .set_params = cx24113_set_params, + .get_frequency = cx24113_get_frequency, + .get_bandwidth = NULL, + .get_status = cx24113_get_status, +}; + +struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, + const struct cx24113_config *config, struct i2c_adapter *i2c) +{ + /* allocate memory for the internal state */ + struct cx24113_state *state = + kzalloc(sizeof(struct cx24113_state), GFP_KERNEL); + int rc; + if (state == NULL) { + err("Unable to kmalloc\n"); + goto error; + } + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + info("trying to detect myself\n"); + + /* making a dummy read, because of some expected troubles + * after power on */ + cx24113_readreg(state, 0x00); + + rc = cx24113_readreg(state, 0x00); + if (rc < 0) { + info("CX24113 not found.\n"); + goto error; + } + state->rev = rc; + + switch (rc) { + case 0x43: + info("detected CX24113 variant\n"); + break; + case REV_CX24113: + info("sucessfully detected\n"); + break; + default: + err("unsupported device id: %x\n", state->rev); + goto error; + } + state->ver = cx24113_readreg(state, 0x01); + info("version: %x\n", state->ver); + + /* create dvb_frontend */ + memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = state; + return fe; + +error: + kfree(state); + + return NULL; +} +EXPORT_SYMBOL(cx24113_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); + +MODULE_AUTHOR("Patrick Boettcher <pb@linuxtv.org>"); +MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/dvb/frontends/cx24113.h b/drivers/media/dvb/frontends/cx24113.h index 5ab3dd1..5de0f7f 100644 --- a/drivers/media/dvb/frontends/cx24113.h +++ b/drivers/media/dvb/frontends/cx24113.h @@ -16,7 +16,7 @@ * * 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.= + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef CX24113_H @@ -30,9 +30,13 @@ struct cx24113_config { u32 xtal_khz; }; -/* TODO: #if defined(CONFIG_DVB_TUNER_CX24113) || \ - * (defined(CONFIG_DVB_TUNER_CX24113_MODULE) && defined(MODULE)) */ +#if defined(CONFIG_DVB_TUNER_CX24113) || \ + (defined(CONFIG_DVB_TUNER_CX24113_MODULE) && defined(MODULE)) +extern struct dvb_frontend *cx24113_attach(struct dvb_frontend *, + const struct cx24113_config *config, struct i2c_adapter *i2c); +extern void cx24113_agc_callback(struct dvb_frontend *fe); +#else static inline struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, const struct cx24113_config *config, struct i2c_adapter *i2c) { @@ -44,5 +48,6 @@ static inline void cx24113_agc_callback(struct dvb_frontend *fe) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); } +#endif #endif /* CX24113_H */ diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb/frontends/cx24116.c index b144b30..9b6c89e 100644 --- a/drivers/media/dvb/frontends/cx24116.c +++ b/drivers/media/dvb/frontends/cx24116.c @@ -106,7 +106,7 @@ MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); #define CX24116_HAS_SYNCLOCK (0x08) #define CX24116_HAS_UNKNOWN1 (0x10) #define CX24116_HAS_UNKNOWN2 (0x20) -#define CX24116_STATUS_MASK (0x3f) +#define CX24116_STATUS_MASK (0x0f) #define CX24116_SIGNAL_MASK (0xc0) #define CX24116_DISEQC_TONEOFF (0) /* toneburst never sent */ @@ -160,6 +160,7 @@ struct cx24116_tuning { fe_spectral_inversion_t inversion; fe_code_rate_t fec; + fe_delivery_system_t delsys; fe_modulation_t modulation; fe_pilot_t pilot; fe_rolloff_t rolloff; @@ -411,14 +412,15 @@ struct cx24116_modfec { }; static int cx24116_lookup_fecmod(struct cx24116_state *state, - fe_modulation_t m, fe_code_rate_t f) + fe_delivery_system_t d, fe_modulation_t m, fe_code_rate_t f) { int i, ret = -EOPNOTSUPP; dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f); for (i = 0; i < ARRAY_SIZE(CX24116_MODFEC_MODES); i++) { - if ((m == CX24116_MODFEC_MODES[i].modulation) && + if ((d == CX24116_MODFEC_MODES[i].delivery_system) && + (m == CX24116_MODFEC_MODES[i].modulation) && (f == CX24116_MODFEC_MODES[i].fec)) { ret = i; break; @@ -429,13 +431,13 @@ static int cx24116_lookup_fecmod(struct cx24116_state *state, } static int cx24116_set_fec(struct cx24116_state *state, - fe_modulation_t mod, fe_code_rate_t fec) + fe_delivery_system_t delsys, fe_modulation_t mod, fe_code_rate_t fec) { int ret = 0; dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec); - ret = cx24116_lookup_fecmod(state, mod, fec); + ret = cx24116_lookup_fecmod(state, delsys, mod, fec); if (ret < 0) return ret; @@ -679,7 +681,8 @@ static int cx24116_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct cx24116_state *state = fe->demodulator_priv; - int lock = cx24116_readreg(state, CX24116_REG_SSTATUS); + int lock = cx24116_readreg(state, CX24116_REG_SSTATUS) & + CX24116_STATUS_MASK; dprintk("%s: status = 0x%02x\n", __func__, lock); @@ -1205,7 +1208,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe, struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct cx24116_cmd cmd; fe_status_t tunerstat; - int i, status, ret, retune; + int i, status, ret, retune = 1; dprintk("%s()\n", __func__); @@ -1222,7 +1225,6 @@ static int cx24116_set_frontend(struct dvb_frontend *fe, /* Pilot doesn't exist in DVB-S, turn bit off */ state->dnxt.pilot_val = CX24116_PILOT_OFF; - retune = 1; /* DVB-S only supports 0.35 */ if (c->rolloff != ROLLOFF_35) { @@ -1250,7 +1252,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe, case PILOT_AUTO: /* Not supported but emulated */ state->dnxt.pilot_val = (c->modulation == QPSK) ? CX24116_PILOT_OFF : CX24116_PILOT_ON; - retune = 2; + retune++; break; case PILOT_OFF: state->dnxt.pilot_val = CX24116_PILOT_OFF; @@ -1287,6 +1289,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe, __func__, c->delivery_system); return -EOPNOTSUPP; } + state->dnxt.delsys = c->delivery_system; state->dnxt.modulation = c->modulation; state->dnxt.frequency = c->frequency; state->dnxt.pilot = c->pilot; @@ -1297,7 +1300,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe, return ret; /* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */ - ret = cx24116_set_fec(state, c->modulation, c->fec_inner); + ret = cx24116_set_fec(state, c->delivery_system, c->modulation, c->fec_inner); if (ret != 0) return ret; @@ -1308,6 +1311,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe, /* discard the 'current' tuning parameters and prepare to tune */ cx24116_clone_params(fe); + dprintk("%s: delsys = %d\n", __func__, state->dcur.delsys); dprintk("%s: modulation = %d\n", __func__, state->dcur.modulation); dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency); dprintk("%s: pilot = %d (val = 0x%02x)\n", __func__, @@ -1427,6 +1431,23 @@ tuned: /* Set/Reset B/W */ return ret; } +static int cx24116_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, + unsigned int mode_flags, unsigned int *delay, fe_status_t *status) +{ + *delay = HZ / 5; + if (params) { + int ret = cx24116_set_frontend(fe, params); + if (ret) + return ret; + } + return cx24116_read_status(fe, status); +} + +static int cx24116_get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + static struct dvb_frontend_ops cx24116_ops = { .info = { @@ -1458,6 +1479,8 @@ static struct dvb_frontend_ops cx24116_ops = { .set_voltage = cx24116_set_voltage, .diseqc_send_master_cmd = cx24116_send_diseqc_msg, .diseqc_send_burst = cx24116_diseqc_send_burst, + .get_frontend_algo = cx24116_get_algo, + .tune = cx24116_tune, .set_property = cx24116_set_property, .get_property = cx24116_get_property, diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h index 3e81268..aab8112 100644 --- a/drivers/media/dvb/frontends/dib7000p.h +++ b/drivers/media/dvb/frontends/dib7000p.h @@ -66,7 +66,8 @@ struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe, return NULL; } -extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, +static inline +int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) { @@ -74,13 +75,15 @@ extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, return -ENODEV; } -extern int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +static inline +int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -ENODEV; } -extern int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value) +static inline +int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -ENODEV; diff --git a/drivers/media/dvb/frontends/drx397xD.c b/drivers/media/dvb/frontends/drx397xD.c index b9ca5c8..ec4e08d 100644 --- a/drivers/media/dvb/frontends/drx397xD.c +++ b/drivers/media/dvb/frontends/drx397xD.c @@ -39,7 +39,7 @@ static const char mod_name[] = "drx397xD"; #define F_SET_0D4h 2 enum fw_ix { -#define _FW_ENTRY(a, b) b +#define _FW_ENTRY(a, b, c) b #include "drx397xD_fw.h" }; @@ -72,11 +72,11 @@ static struct { int refcnt; const u8 *data[ARRAY_SIZE(blob_name)]; } fw[] = { -#define _FW_ENTRY(a, b) { \ - .name = a, \ - .file = 0, \ - .lock = RW_LOCK_UNLOCKED, \ - .refcnt = 0, \ +#define _FW_ENTRY(a, b, c) { \ + .name = a, \ + .file = 0, \ + .lock = __RW_LOCK_UNLOCKED(fw[c].lock), \ + .refcnt = 0, \ .data = { } } #include "drx397xD_fw.h" }; diff --git a/drivers/media/dvb/frontends/drx397xD_fw.h b/drivers/media/dvb/frontends/drx397xD_fw.h index 01de02a..c8b44c1 100644 --- a/drivers/media/dvb/frontends/drx397xD_fw.h +++ b/drivers/media/dvb/frontends/drx397xD_fw.h @@ -18,8 +18,8 @@ */ #ifdef _FW_ENTRY - _FW_ENTRY("drx397xD.A2.fw", DRXD_FW_A2 = 0 ), - _FW_ENTRY("drx397xD.B1.fw", DRXD_FW_B1 ), + _FW_ENTRY("drx397xD.A2.fw", DRXD_FW_A2 = 0, DRXD_FW_A2 ), + _FW_ENTRY("drx397xD.B1.fw", DRXD_FW_B1, DRXD_FW_B1 ), #undef _FW_ENTRY #endif /* _FW_ENTRY */ diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c index ea05815..9f63499 100644 --- a/drivers/media/dvb/frontends/dvb-pll.c +++ b/drivers/media/dvb/frontends/dvb-pll.c @@ -311,7 +311,7 @@ static struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = { .count = 4, .entries = { { 1250000, 500, 0xc4, 0x00}, - { 1550000, 500, 0xc4, 0x40}, + { 1450000, 500, 0xc4, 0x40}, { 2050000, 500, 0xc4, 0x80}, { 2150000, 500, 0xc4, 0xc0}, }, diff --git a/drivers/media/dvb/frontends/lgdt3304.c b/drivers/media/dvb/frontends/lgdt3304.c new file mode 100644 index 0000000..469ace5 --- /dev/null +++ b/drivers/media/dvb/frontends/lgdt3304.c @@ -0,0 +1,378 @@ +/* + * Driver for LG ATSC lgdt3304 driver + * + * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de> + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include "dvb_frontend.h" +#include "lgdt3304.h" + +static unsigned int debug = 0; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug,"lgdt3304 debugging (default off)"); + +#define dprintk(fmt, args...) if (debug) do {\ + printk("lgdt3304 debug: " fmt, ##args); } while (0) + +struct lgdt3304_state +{ + struct dvb_frontend frontend; + fe_modulation_t current_modulation; + __u32 snr; + __u32 current_frequency; + __u8 addr; + struct i2c_adapter *i2c; +}; + +static int i2c_write_demod_bytes (struct dvb_frontend *fe, __u8 *buf, int len) +{ + struct lgdt3304_state *state = fe->demodulator_priv; + struct i2c_msg i2cmsgs = { + .addr = state->addr, + .flags = 0, + .len = 3, + .buf = buf + }; + int i; + int err; + + for (i=0; i<len-1; i+=3){ + if((err = i2c_transfer(state->i2c, &i2cmsgs, 1))<0) { + printk("%s i2c_transfer error %d\n", __FUNCTION__, err); + if (err < 0) + return err; + else + return -EREMOTEIO; + } + i2cmsgs.buf += 3; + } + return 0; +} + +static int lgdt3304_i2c_read_reg(struct dvb_frontend *fe, unsigned int reg) +{ + struct lgdt3304_state *state = fe->demodulator_priv; + struct i2c_msg i2cmsgs[2]; + int ret; + __u8 buf; + + __u8 regbuf[2] = { reg>>8, reg&0xff }; + + i2cmsgs[0].addr = state->addr; + i2cmsgs[0].flags = 0; + i2cmsgs[0].len = 2; + i2cmsgs[0].buf = regbuf; + + i2cmsgs[1].addr = state->addr; + i2cmsgs[1].flags = I2C_M_RD; + i2cmsgs[1].len = 1; + i2cmsgs[1].buf = &buf; + + if((ret = i2c_transfer(state->i2c, i2cmsgs, 2))<0) { + printk("%s i2c_transfer error %d\n", __FUNCTION__, ret); + return ret; + } + + return buf; +} + +static int lgdt3304_i2c_write_reg(struct dvb_frontend *fe, int reg, int val) +{ + struct lgdt3304_state *state = fe->demodulator_priv; + char buffer[3] = { reg>>8, reg&0xff, val }; + int ret; + + struct i2c_msg i2cmsgs = { + .addr = state->addr, + .flags = 0, + .len = 3, + .buf=buffer + }; + ret = i2c_transfer(state->i2c, &i2cmsgs, 1); + if (ret != 1) { + printk("%s i2c_transfer error %d\n", __FUNCTION__, ret); + return ret; + } + + return 0; +} + + +static int lgdt3304_soft_Reset(struct dvb_frontend *fe) +{ + lgdt3304_i2c_write_reg(fe, 0x0002, 0x9a); + lgdt3304_i2c_write_reg(fe, 0x0002, 0x9b); + mdelay(200); + return 0; +} + +static int lgdt3304_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) { + int err = 0; + + static __u8 lgdt3304_vsb8_data[] = { + /* 16bit , 8bit */ + /* regs , val */ + 0x00, 0x00, 0x02, + 0x00, 0x00, 0x13, + 0x00, 0x0d, 0x02, + 0x00, 0x0e, 0x02, + 0x00, 0x12, 0x32, + 0x00, 0x13, 0xc4, + 0x01, 0x12, 0x17, + 0x01, 0x13, 0x15, + 0x01, 0x14, 0x18, + 0x01, 0x15, 0xff, + 0x01, 0x16, 0x2c, + 0x02, 0x14, 0x67, + 0x02, 0x24, 0x8d, + 0x04, 0x27, 0x12, + 0x04, 0x28, 0x4f, + 0x03, 0x08, 0x80, + 0x03, 0x09, 0x00, + 0x03, 0x0d, 0x00, + 0x03, 0x0e, 0x1c, + 0x03, 0x14, 0xe1, + 0x05, 0x0e, 0x5b, + }; + + /* not yet tested .. */ + static __u8 lgdt3304_qam64_data[] = { + /* 16bit , 8bit */ + /* regs , val */ + 0x00, 0x00, 0x18, + 0x00, 0x0d, 0x02, + //0x00, 0x0e, 0x02, + 0x00, 0x12, 0x2a, + 0x00, 0x13, 0x00, + 0x03, 0x14, 0xe3, + 0x03, 0x0e, 0x1c, + 0x03, 0x08, 0x66, + 0x03, 0x09, 0x66, + 0x03, 0x0a, 0x08, + 0x03, 0x0b, 0x9b, + 0x05, 0x0e, 0x5b, + }; + + + /* tested with KWorld a340 */ + static __u8 lgdt3304_qam256_data[] = { + /* 16bit , 8bit */ + /* regs , val */ + 0x00, 0x00, 0x01, //0x19, + 0x00, 0x12, 0x2a, + 0x00, 0x13, 0x80, + 0x00, 0x0d, 0x02, + 0x03, 0x14, 0xe3, + + 0x03, 0x0e, 0x1c, + 0x03, 0x08, 0x66, + 0x03, 0x09, 0x66, + 0x03, 0x0a, 0x08, + 0x03, 0x0b, 0x9b, + + 0x03, 0x0d, 0x14, + //0x05, 0x0e, 0x5b, + 0x01, 0x06, 0x4a, + 0x01, 0x07, 0x3d, + 0x01, 0x08, 0x70, + 0x01, 0x09, 0xa3, + + 0x05, 0x04, 0xfd, + + 0x00, 0x0d, 0x82, + + 0x05, 0x0e, 0x5b, + + 0x05, 0x0e, 0x5b, + + 0x00, 0x02, 0x9a, + + 0x00, 0x02, 0x9b, + + 0x00, 0x00, 0x01, + 0x00, 0x12, 0x2a, + 0x00, 0x13, 0x80, + 0x00, 0x0d, 0x02, + 0x03, 0x14, 0xe3, + + 0x03, 0x0e, 0x1c, + 0x03, 0x08, 0x66, + 0x03, 0x09, 0x66, + 0x03, 0x0a, 0x08, + 0x03, 0x0b, 0x9b, + + 0x03, 0x0d, 0x14, + 0x01, 0x06, 0x4a, + 0x01, 0x07, 0x3d, + 0x01, 0x08, 0x70, + 0x01, 0x09, 0xa3, + + 0x05, 0x04, 0xfd, + + 0x00, 0x0d, 0x82, + + 0x05, 0x0e, 0x5b, + }; + + struct lgdt3304_state *state = fe->demodulator_priv; + if (state->current_modulation != param->u.vsb.modulation) { + switch(param->u.vsb.modulation) { + case VSB_8: + err = i2c_write_demod_bytes(fe, lgdt3304_vsb8_data, + sizeof(lgdt3304_vsb8_data)); + break; + case QAM_64: + err = i2c_write_demod_bytes(fe, lgdt3304_qam64_data, + sizeof(lgdt3304_qam64_data)); + break; + case QAM_256: + err = i2c_write_demod_bytes(fe, lgdt3304_qam256_data, + sizeof(lgdt3304_qam256_data)); + break; + default: + break; + } + + if (err) { + printk("%s error setting modulation\n", __FUNCTION__); + } else { + state->current_modulation = param->u.vsb.modulation; + } + } + state->current_frequency = param->frequency; + + lgdt3304_soft_Reset(fe); + + + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe, param); + + return 0; +} + +static int lgdt3304_init(struct dvb_frontend *fe) { + return 0; +} + +static int lgdt3304_sleep(struct dvb_frontend *fe) { + return 0; +} + + +static int lgdt3304_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct lgdt3304_state *state = fe->demodulator_priv; + int r011d; + int qam_lck; + + *status = 0; + dprintk("lgdt read status\n"); + + r011d = lgdt3304_i2c_read_reg(fe, 0x011d); + + dprintk("%02x\n", r011d); + + switch(state->current_modulation) { + case VSB_8: + if (r011d & 0x80) { + dprintk("VSB Locked\n"); + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_LOCK; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_SIGNAL; + } + break; + case QAM_64: + case QAM_256: + qam_lck = r011d & 0x7; + switch(qam_lck) { + case 0x0: dprintk("Unlock\n"); + break; + case 0x4: dprintk("1st Lock in acquisition state\n"); + break; + case 0x6: dprintk("2nd Lock in acquisition state\n"); + break; + case 0x7: dprintk("Final Lock in good reception state\n"); + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_LOCK; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_SIGNAL; + break; + } + break; + default: + printk("%s unhandled modulation\n", __FUNCTION__); + } + + + return 0; +} + +static int lgdt3304_read_ber(struct dvb_frontend *fe, __u32 *ber) +{ + dprintk("read ber\n"); + return 0; +} + +static int lgdt3304_read_snr(struct dvb_frontend *fe, __u16 *snr) +{ + dprintk("read snr\n"); + return 0; +} + +static int lgdt3304_read_ucblocks(struct dvb_frontend *fe, __u32 *ucblocks) +{ + dprintk("read ucblocks\n"); + return 0; +} + +static void lgdt3304_release(struct dvb_frontend *fe) +{ + struct lgdt3304_state *state = (struct lgdt3304_state *)fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops demod_lgdt3304={ + .info = { + .name = "LG 3304", + .type = FE_ATSC, + .frequency_min = 54000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, + .symbol_rate_min = 5056941, + .symbol_rate_max = 10762000, + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + .init = lgdt3304_init, + .sleep = lgdt3304_sleep, + .set_frontend = lgdt3304_set_parameters, + .read_snr = lgdt3304_read_snr, + .read_ber = lgdt3304_read_ber, + .read_status = lgdt3304_read_status, + .read_ucblocks = lgdt3304_read_ucblocks, + .release = lgdt3304_release, +}; + +struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config, + struct i2c_adapter *i2c) +{ + + struct lgdt3304_state *state; + state = kzalloc(sizeof(struct lgdt3304_state), GFP_KERNEL); + memset(state, 0x0, sizeof(struct lgdt3304_state)); + state->addr = config->i2c_address; + state->i2c = i2c; + + memcpy(&state->frontend.ops, &demod_lgdt3304, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; +} + +EXPORT_SYMBOL_GPL(lgdt3304_attach); +MODULE_AUTHOR("Markus Rechberger <mrechberger@empiatech.com>"); +MODULE_DESCRIPTION("LGE LGDT3304 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/lgdt3304.h b/drivers/media/dvb/frontends/lgdt3304.h new file mode 100644 index 0000000..fc409fe --- /dev/null +++ b/drivers/media/dvb/frontends/lgdt3304.h @@ -0,0 +1,45 @@ +/* + * Driver for DVB-T lgdt3304 demodulator + * + * Copyright (C) 2008 Markus Rechberger <mrechberger@gmail.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 LGDT3304_H +#define LGDT3304_H + +#include <linux/dvb/frontend.h> + +struct lgdt3304_config +{ + /* demodulator's I2C address */ + u8 i2c_address; +}; + +#if defined(CONFIG_DVB_LGDT3304) || (defined(CONFIG_DVB_LGDT3304_MODULE) && defined(MODULE)) +extern struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_LGDT */ + +#endif /* LGDT3304_H */ diff --git a/drivers/media/dvb/frontends/s5h1411.c b/drivers/media/dvb/frontends/s5h1411.c index 40644aa..66e2dd6 100644 --- a/drivers/media/dvb/frontends/s5h1411.c +++ b/drivers/media/dvb/frontends/s5h1411.c @@ -874,6 +874,9 @@ struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, /* Note: Leaving the I2C gate open here. */ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 1); + /* Put the device into low-power mode until first use */ + s5h1411_set_powerstate(&state->frontend, 1); + return &state->frontend; error: diff --git a/drivers/media/dvb/frontends/s921_core.c b/drivers/media/dvb/frontends/s921_core.c new file mode 100644 index 0000000..974b52b --- /dev/null +++ b/drivers/media/dvb/frontends/s921_core.c @@ -0,0 +1,216 @@ +/* + * Driver for Sharp s921 driver + * + * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de> + * + */ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include "s921_core.h" + +static int s921_isdb_init(struct s921_isdb_t *dev); +static int s921_isdb_set_parameters(struct s921_isdb_t *dev, struct s921_isdb_t_transmission_mode_params *params); +static int s921_isdb_tune(struct s921_isdb_t *dev, struct s921_isdb_t_tune_params *params); +static int s921_isdb_get_status(struct s921_isdb_t *dev, void *data); + +static u8 init_table[]={ 0x01, 0x40, 0x02, 0x00, 0x03, 0x40, 0x04, 0x01, + 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x5a, 0x0c, 0x00, + 0x0d, 0x00, 0x0f, 0x00, 0x13, 0x1b, 0x14, 0x80, + 0x15, 0x40, 0x17, 0x70, 0x18, 0x01, 0x19, 0x12, + 0x1a, 0x01, 0x1b, 0x12, 0x1c, 0xa0, 0x1d, 0x00, + 0x1e, 0x0a, 0x1f, 0x08, 0x20, 0x40, 0x21, 0xff, + 0x22, 0x4c, 0x23, 0x4e, 0x24, 0x4c, 0x25, 0x00, + 0x26, 0x00, 0x27, 0xf4, 0x28, 0x60, 0x29, 0x88, + 0x2a, 0x40, 0x2b, 0x40, 0x2c, 0xff, 0x2d, 0x00, + 0x2e, 0xff, 0x2f, 0x00, 0x30, 0x20, 0x31, 0x06, + 0x32, 0x0c, 0x34, 0x0f, 0x37, 0xfe, 0x38, 0x00, + 0x39, 0x63, 0x3a, 0x10, 0x3b, 0x10, 0x47, 0x00, + 0x49, 0xe5, 0x4b, 0x00, 0x50, 0xc0, 0x52, 0x20, + 0x54, 0x5a, 0x55, 0x5b, 0x56, 0x40, 0x57, 0x70, + 0x5c, 0x50, 0x5d, 0x00, 0x62, 0x17, 0x63, 0x2f, + 0x64, 0x6f, 0x68, 0x00, 0x69, 0x89, 0x6a, 0x00, + 0x6b, 0x00, 0x6c, 0x00, 0x6d, 0x00, 0x6e, 0x00, + 0x70, 0x00, 0x71, 0x00, 0x75, 0x00, 0x76, 0x30, + 0x77, 0x01, 0xaf, 0x00, 0xb0, 0xa0, 0xb2, 0x3d, + 0xb3, 0x25, 0xb4, 0x8b, 0xb5, 0x4b, 0xb6, 0x3f, + 0xb7, 0xff, 0xb8, 0xff, 0xb9, 0xfc, 0xba, 0x00, + 0xbb, 0x00, 0xbc, 0x00, 0xd0, 0x30, 0xe4, 0x84, + 0xf0, 0x48, 0xf1, 0x19, 0xf2, 0x5a, 0xf3, 0x8e, + 0xf4, 0x2d, 0xf5, 0x07, 0xf6, 0x5a, 0xf7, 0xba, + 0xf8, 0xd7 }; + +static u8 c_table[]={ 0x58, 0x8a, 0x7b, 0x59, 0x8c, 0x7b, 0x5a, 0x8e, 0x5b, + 0x5b, 0x90, 0x5b, 0x5c, 0x92, 0x5b, 0x5d, 0x94, 0x5b, + 0x5e, 0x96, 0x5b, 0x5f, 0x98, 0x3b, 0x60, 0x9a, 0x3b, + 0x61, 0x9c, 0x3b, 0x62, 0x9e, 0x3b, 0x63, 0xa0, 0x3b, + 0x64, 0xa2, 0x1b, 0x65, 0xa4, 0x1b, 0x66, 0xa6, 0x1b, + 0x67, 0xa8, 0x1b, 0x68, 0xaa, 0x1b, 0x69, 0xac, 0x1b, + 0x6a, 0xae, 0x1b, 0x6b, 0xb0, 0x1b, 0x6c, 0xb2, 0x1b, + 0x6d, 0xb4, 0xfb, 0x6e, 0xb6, 0xfb, 0x6f, 0xb8, 0xfb, + 0x70, 0xba, 0xfb, 0x71, 0xbc, 0xdb, 0x72, 0xbe, 0xdb, + 0x73, 0xc0, 0xdb, 0x74, 0xc2, 0xdb, 0x75, 0xc4, 0xdb, + 0x76, 0xc6, 0xdb, 0x77, 0xc8, 0xbb, 0x78, 0xca, 0xbb, + 0x79, 0xcc, 0xbb, 0x7a, 0xce, 0xbb, 0x7b, 0xd0, 0xbb, + 0x7c, 0xd2, 0xbb, 0x7d, 0xd4, 0xbb, 0x7e, 0xd6, 0xbb, + 0x7f, 0xd8, 0xbb, 0x80, 0xda, 0x9b, 0x81, 0xdc, 0x9b, + 0x82, 0xde, 0x9b, 0x83, 0xe0, 0x9b, 0x84, 0xe2, 0x9b, + 0x85, 0xe4, 0x9b, 0x86, 0xe6, 0x9b, 0x87, 0xe8, 0x9b, + 0x88, 0xea, 0x9b, 0x89, 0xec, 0x9b }; + +int s921_isdb_cmd(struct s921_isdb_t *dev, u32 cmd, void *data) { + switch(cmd) { + case ISDB_T_CMD_INIT: + s921_isdb_init(dev); + break; + case ISDB_T_CMD_SET_PARAM: + s921_isdb_set_parameters(dev, data); + break; + case ISDB_T_CMD_TUNE: + s921_isdb_tune(dev, data); + break; + case ISDB_T_CMD_GET_STATUS: + s921_isdb_get_status(dev, data); + break; + default: + printk("unhandled command\n"); + return -EINVAL; + } + return 0; +} + +static int s921_isdb_init(struct s921_isdb_t *dev) { + unsigned int i; + unsigned int ret; + printk("isdb_init\n"); + for (i = 0; i < sizeof(init_table); i+=2) { + ret = dev->i2c_write(dev->priv_dev, init_table[i], init_table[i+1]); + if (ret != 0) { + printk("i2c write failed\n"); + return ret; + } + } + return 0; +} + +static int s921_isdb_set_parameters(struct s921_isdb_t *dev, struct s921_isdb_t_transmission_mode_params *params) { + + int ret; + /* auto is sufficient for now, lateron this should be reflected in an extra interface */ + + + + ret = dev->i2c_write(dev->priv_dev, 0xb0, 0xa0); //mod_b2); + ret = dev->i2c_write(dev->priv_dev, 0xb2, 0x3d); //mod_b2); + + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0xb3, 0x25); //mod_b3); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0xb4, 0x8b); //mod_b4); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0xb5, 0x4b); //mod_b5); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0xb6, 0x3f); //mod_b6); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0xb7, 0x3f); //mod_b7); + if (ret < 0) + return -EINVAL; + + return E_OK; +} + +static int s921_isdb_tune(struct s921_isdb_t *dev, struct s921_isdb_t_tune_params *params) { + + int ret; + int index; + + index = (params->frequency - 473143000)/6000000; + + if (index > 48) { + return -EINVAL; + } + + dev->i2c_write(dev->priv_dev, 0x47, 0x60); + + ret = dev->i2c_write(dev->priv_dev, 0x68, 0x00); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0x69, 0x89); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0xf0, 0x48); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0xf1, 0x19); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0xf2, c_table[index*3]); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0xf3, c_table[index*3+1]); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0xf4, c_table[index*3+2]); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0xf5, 0xae); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0xf6, 0xb7); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0xf7, 0xba); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0xf8, 0xd7); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0x68, 0x0a); + if (ret < 0) + return -EINVAL; + + ret = dev->i2c_write(dev->priv_dev, 0x69, 0x09); + if (ret < 0) + return -EINVAL; + + dev->i2c_write(dev->priv_dev, 0x01, 0x40); + return 0; +} + +static int s921_isdb_get_status(struct s921_isdb_t *dev, void *data) { + unsigned int *ret = (unsigned int*)data; + u8 ifagc_dt; + u8 rfagc_dt; + + mdelay(10); + ifagc_dt = dev->i2c_read(dev->priv_dev, 0x81); + rfagc_dt = dev->i2c_read(dev->priv_dev, 0x82); + if (rfagc_dt == 0x40) { + *ret = 1; + } + return 0; +} diff --git a/drivers/media/dvb/frontends/s921_core.h b/drivers/media/dvb/frontends/s921_core.h new file mode 100644 index 0000000..de2f10a --- /dev/null +++ b/drivers/media/dvb/frontends/s921_core.h @@ -0,0 +1,114 @@ +#ifndef _S921_CORE_H +#define _S921_CORE_H +//#define u8 unsigned int +//#define u32 unsigned int + + + +//#define EINVAL -1 +#define E_OK 0 + +struct s921_isdb_t { + void *priv_dev; + int (*i2c_write)(void *dev, u8 reg, u8 val); + int (*i2c_read)(void *dev, u8 reg); +}; + +#define ISDB_T_CMD_INIT 0 +#define ISDB_T_CMD_SET_PARAM 1 +#define ISDB_T_CMD_TUNE 2 +#define ISDB_T_CMD_GET_STATUS 3 + +struct s921_isdb_t_tune_params { + u32 frequency; +}; + +struct s921_isdb_t_status { +}; + +struct s921_isdb_t_transmission_mode_params { + u8 mode; + u8 layer_a_mode; +#define ISDB_T_LA_MODE_1 0 +#define ISDB_T_LA_MODE_2 1 +#define ISDB_T_LA_MODE_3 2 + u8 layer_a_carrier_modulation; +#define ISDB_T_LA_CM_DQPSK 0 +#define ISDB_T_LA_CM_QPSK 1 +#define ISDB_T_LA_CM_16QAM 2 +#define ISDB_T_LA_CM_64QAM 3 +#define ISDB_T_LA_CM_NOLAYER 4 + u8 layer_a_code_rate; +#define ISDB_T_LA_CR_1_2 0 +#define ISDB_T_LA_CR_2_3 1 +#define ISDB_T_LA_CR_3_4 2 +#define ISDB_T_LA_CR_5_6 4 +#define ISDB_T_LA_CR_7_8 8 +#define ISDB_T_LA_CR_NOLAYER 16 + u8 layer_a_time_interleave; +#define ISDB_T_LA_TI_0 0 +#define ISDB_T_LA_TI_1 1 +#define ISDB_T_LA_TI_2 2 +#define ISDB_T_LA_TI_4 4 +#define ISDB_T_LA_TI_8 8 +#define ISDB_T_LA_TI_16 16 +#define ISDB_T_LA_TI_32 32 + u8 layer_a_nseg; + + u8 layer_b_mode; +#define ISDB_T_LB_MODE_1 0 +#define ISDB_T_LB_MODE_2 1 +#define ISDB_T_LB_MODE_3 2 + u8 layer_b_carrier_modulation; +#define ISDB_T_LB_CM_DQPSK 0 +#define ISDB_T_LB_CM_QPSK 1 +#define ISDB_T_LB_CM_16QAM 2 +#define ISDB_T_LB_CM_64QAM 3 +#define ISDB_T_LB_CM_NOLAYER 4 + u8 layer_b_code_rate; +#define ISDB_T_LB_CR_1_2 0 +#define ISDB_T_LB_CR_2_3 1 +#define ISDB_T_LB_CR_3_4 2 +#define ISDB_T_LB_CR_5_6 4 +#define ISDB_T_LB_CR_7_8 8 +#define ISDB_T_LB_CR_NOLAYER 16 + u8 layer_b_time_interleave; +#define ISDB_T_LB_TI_0 0 +#define ISDB_T_LB_TI_1 1 +#define ISDB_T_LB_TI_2 2 +#define ISDB_T_LB_TI_4 4 +#define ISDB_T_LB_TI_8 8 +#define ISDB_T_LB_TI_16 16 +#define ISDB_T_LB_TI_32 32 + u8 layer_b_nseg; + + u8 layer_c_mode; +#define ISDB_T_LC_MODE_1 0 +#define ISDB_T_LC_MODE_2 1 +#define ISDB_T_LC_MODE_3 2 + u8 layer_c_carrier_modulation; +#define ISDB_T_LC_CM_DQPSK 0 +#define ISDB_T_LC_CM_QPSK 1 +#define ISDB_T_LC_CM_16QAM 2 +#define ISDB_T_LC_CM_64QAM 3 +#define ISDB_T_LC_CM_NOLAYER 4 + u8 layer_c_code_rate; +#define ISDB_T_LC_CR_1_2 0 +#define ISDB_T_LC_CR_2_3 1 +#define ISDB_T_LC_CR_3_4 2 +#define ISDB_T_LC_CR_5_6 4 +#define ISDB_T_LC_CR_7_8 8 +#define ISDB_T_LC_CR_NOLAYER 16 + u8 layer_c_time_interleave; +#define ISDB_T_LC_TI_0 0 +#define ISDB_T_LC_TI_1 1 +#define ISDB_T_LC_TI_2 2 +#define ISDB_T_LC_TI_4 4 +#define ISDB_T_LC_TI_8 8 +#define ISDB_T_LC_TI_16 16 +#define ISDB_T_LC_TI_32 32 + u8 layer_c_nseg; +}; + +int s921_isdb_cmd(struct s921_isdb_t *dev, u32 cmd, void *data); +#endif diff --git a/drivers/media/dvb/frontends/s921_module.c b/drivers/media/dvb/frontends/s921_module.c new file mode 100644 index 0000000..3cbb9cb --- /dev/null +++ b/drivers/media/dvb/frontends/s921_module.c @@ -0,0 +1,190 @@ +/* + * Driver for Sharp s921 driver + * + * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de> + * + * All rights reserved. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include "dvb_frontend.h" +#include "s921_module.h" +#include "s921_core.h" + +static unsigned int debug = 0; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug,"s921 debugging (default off)"); + +#define dprintk(fmt, args...) if (debug) do {\ + printk("s921 debug: " fmt, ##args); } while (0) + +struct s921_state +{ + struct dvb_frontend frontend; + fe_modulation_t current_modulation; + __u32 snr; + __u32 current_frequency; + __u8 addr; + struct s921_isdb_t dev; + struct i2c_adapter *i2c; +}; + +static int s921_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) { + struct s921_state *state = (struct s921_state *)fe->demodulator_priv; + struct s921_isdb_t_transmission_mode_params params; + struct s921_isdb_t_tune_params tune_params; + + tune_params.frequency = param->frequency; + s921_isdb_cmd(&state->dev, ISDB_T_CMD_SET_PARAM, ¶ms); + s921_isdb_cmd(&state->dev, ISDB_T_CMD_TUNE, &tune_params); + mdelay(100); + return 0; +} + +static int s921_init(struct dvb_frontend *fe) { + printk("s921 init\n"); + return 0; +} + +static int s921_sleep(struct dvb_frontend *fe) { + printk("s921 sleep\n"); + return 0; +} + +static int s921_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct s921_state *state = (struct s921_state *)fe->demodulator_priv; + unsigned int ret; + mdelay(5); + s921_isdb_cmd(&state->dev, ISDB_T_CMD_GET_STATUS, &ret); + *status = 0; + + printk("status: %02x\n", ret); + if (ret == 1) { + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_LOCK; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_SIGNAL; + } + + return 0; +} + +static int s921_read_ber(struct dvb_frontend *fe, __u32 *ber) +{ + dprintk("read ber\n"); + return 0; +} + +static int s921_read_snr(struct dvb_frontend *fe, __u16 *snr) +{ + dprintk("read snr\n"); + return 0; +} + +static int s921_read_ucblocks(struct dvb_frontend *fe, __u32 *ucblocks) +{ + dprintk("read ucblocks\n"); + return 0; +} + +static void s921_release(struct dvb_frontend *fe) +{ + struct s921_state *state = (struct s921_state *)fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops demod_s921={ + .info = { + .name = "SHARP S921", + .type = FE_OFDM, + .frequency_min = 473143000, + .frequency_max = 767143000, + .frequency_stepsize = 6000000, + .frequency_tolerance = 0, + .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_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, + .init = s921_init, + .sleep = s921_sleep, + .set_frontend = s921_set_parameters, + .read_snr = s921_read_snr, + .read_ber = s921_read_ber, + .read_status = s921_read_status, + .read_ucblocks = s921_read_ucblocks, + .release = s921_release, +}; + +static int s921_write(void *dev, u8 reg, u8 val) { + struct s921_state *state = dev; + char buf[2]={reg,val}; + int err; + struct i2c_msg i2cmsgs = { + .addr = state->addr, + .flags = 0, + .len = 2, + .buf = buf + }; + + if((err = i2c_transfer(state->i2c, &i2cmsgs, 1))<0) { + printk("%s i2c_transfer error %d\n", __FUNCTION__, err); + if (err < 0) + return err; + else + return -EREMOTEIO; + } + + return 0; +} + +static int s921_read(void *dev, u8 reg) { + struct s921_state *state = dev; + u8 b1; + int ret; + struct i2c_msg msg[2] = { { .addr = state->addr, + .flags = 0, + .buf = ®, .len = 1 }, + { .addr = state->addr, + .flags = I2C_M_RD, + .buf = &b1, .len = 1 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret != 2) + return ret; + return b1; +} + +struct dvb_frontend* s921_attach(const struct s921_config *config, + struct i2c_adapter *i2c) +{ + + struct s921_state *state; + state = kzalloc(sizeof(struct s921_state), GFP_KERNEL); + memset(state, 0x0, sizeof(struct s921_state)); + + state->addr = config->i2c_address; + state->i2c = i2c; + state->dev.i2c_write = &s921_write; + state->dev.i2c_read = &s921_read; + state->dev.priv_dev = state; + + s921_isdb_cmd(&state->dev, ISDB_T_CMD_INIT, NULL); + + memcpy(&state->frontend.ops, &demod_s921, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; +} + +EXPORT_SYMBOL_GPL(s921_attach); +MODULE_AUTHOR("Markus Rechberger <mrechberger@empiatech.com>"); +MODULE_DESCRIPTION("Sharp S921 ISDB-T 1Seg"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/s921_module.h b/drivers/media/dvb/frontends/s921_module.h new file mode 100644 index 0000000..7866042 --- /dev/null +++ b/drivers/media/dvb/frontends/s921_module.h @@ -0,0 +1,49 @@ +/* + * Driver for DVB-T s921 demodulator + * + * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de> + * + * 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 S921_MODULE_H +#define S921_MODULE_H + +#include <linux/dvb/frontend.h> +#include "s921_core.h" + +int s921_isdb_init(struct s921_isdb_t *dev); +int s921_isdb_cmd(struct s921_isdb_t *dev, u32 cmd, void *data); + +struct s921_config +{ + /* demodulator's I2C address */ + u8 i2c_address; +}; + +#if defined(CONFIG_DVB_S921) || (defined(CONFIG_DVB_S921_MODULE) && defined(MODULE)) +extern struct dvb_frontend* s921_attach(const struct s921_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend* s921_attach(const struct s921_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_S921 */ + +#endif /* S921_H */ diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb/frontends/si21xx.c index 3ddbe69..0bd16af 100644 --- a/drivers/media/dvb/frontends/si21xx.c +++ b/drivers/media/dvb/frontends/si21xx.c @@ -8,7 +8,6 @@ * (at your option) any later version. * */ -#include <linux/version.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> diff --git a/drivers/media/dvb/frontends/stb0899_algo.c b/drivers/media/dvb/frontends/stb0899_algo.c new file mode 100644 index 0000000..ced9b7a --- /dev/null +++ b/drivers/media/dvb/frontends/stb0899_algo.c @@ -0,0 +1,1519 @@ +/* + STB0899 Multistandard Frontend driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + + 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 "stb0899_drv.h" +#include "stb0899_priv.h" +#include "stb0899_reg.h" + +inline u32 stb0899_do_div(u64 n, u32 d) +{ + /* wrap do_div() for ease of use */ + + do_div(n, d); + return n; +} + +/* + * stb0899_calc_srate + * Compute symbol rate + */ +static u32 stb0899_calc_srate(u32 master_clk, u8 *sfr) +{ + u64 tmp; + + /* srate = (SFR * master_clk) >> 20 */ + + /* sfr is of size 20 bit, stored with an offset of 4 bit */ + tmp = (((u32)sfr[0]) << 16) | (((u32)sfr[1]) << 8) | sfr[2]; + tmp &= ~0xf; + tmp *= master_clk; + tmp >>= 24; + + return tmp; +} + +/* + * stb0899_get_srate + * Get the current symbol rate + */ +u32 stb0899_get_srate(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + u8 sfr[3]; + + stb0899_read_regs(state, STB0899_SFRH, sfr, 3); + + return stb0899_calc_srate(internal->master_clk, sfr); +} + +/* + * stb0899_set_srate + * Set symbol frequency + * MasterClock: master clock frequency (hz) + * SymbolRate: symbol rate (bauds) + * return symbol frequency + */ +static u32 stb0899_set_srate(struct stb0899_state *state, u32 master_clk, u32 srate) +{ + u32 tmp; + u8 sfr[3]; + + dprintk(state->verbose, FE_DEBUG, 1, "-->"); + /* + * in order to have the maximum precision, the symbol rate entered into + * the chip is computed as the closest value of the "true value". + * In this purpose, the symbol rate value is rounded (1 is added on the bit + * below the LSB ) + * + * srate = (SFR * master_clk) >> 20 + * <=> + * SFR = srate << 20 / master_clk + * + * rounded: + * SFR = (srate << 21 + master_clk) / (2 * master_clk) + * + * stored as 20 bit number with an offset of 4 bit: + * sfr = SFR << 4; + */ + + tmp = stb0899_do_div((((u64)srate) << 21) + master_clk, 2 * master_clk); + tmp <<= 4; + + sfr[0] = tmp >> 16; + sfr[1] = tmp >> 8; + sfr[2] = tmp; + + stb0899_write_regs(state, STB0899_SFRH, sfr, 3); + + return srate; +} + +/* + * stb0899_calc_derot_time + * Compute the amount of time needed by the derotator to lock + * SymbolRate: Symbol rate + * return: derotator time constant (ms) + */ +static long stb0899_calc_derot_time(long srate) +{ + if (srate > 0) + return (100000 / (srate / 1000)); + else + return 0; +} + +/* + * stb0899_carr_width + * Compute the width of the carrier + * return: width of carrier (kHz or Mhz) + */ +long stb0899_carr_width(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + + return (internal->srate + (internal->srate * internal->rolloff) / 100); +} + +/* + * stb0899_first_subrange + * Compute the first subrange of the search + */ +static void stb0899_first_subrange(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_params *params = &state->params; + struct stb0899_config *config = state->config; + + int range = 0; + u32 bandwidth = 0; + + if (config->tuner_get_bandwidth) { + stb0899_i2c_gate_ctrl(&state->frontend, 1); + config->tuner_get_bandwidth(&state->frontend, &bandwidth); + stb0899_i2c_gate_ctrl(&state->frontend, 0); + range = bandwidth - stb0899_carr_width(state) / 2; + } + + if (range > 0) + internal->sub_range = MIN(internal->srch_range, range); + else + internal->sub_range = 0; + + internal->freq = params->freq; + internal->tuner_offst = 0L; + internal->sub_dir = 1; +} + +/* + * stb0899_check_tmg + * check for timing lock + * internal.Ttiming: time to wait for loop lock + */ +static enum stb0899_status stb0899_check_tmg(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + int lock; + u8 reg; + s8 timing; + + msleep(internal->t_derot); + + stb0899_write_reg(state, STB0899_RTF, 0xf2); + reg = stb0899_read_reg(state, STB0899_TLIR); + lock = STB0899_GETFIELD(TLIR_TMG_LOCK_IND, reg); + timing = stb0899_read_reg(state, STB0899_RTF); + + if (lock >= 42) { + if ((lock > 48) && (ABS(timing) >= 110)) { + internal->status = ANALOGCARRIER; + dprintk(state->verbose, FE_DEBUG, 1, "-->ANALOG Carrier !"); + } else { + internal->status = TIMINGOK; + dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK !"); + } + } else { + internal->status = NOTIMING; + dprintk(state->verbose, FE_DEBUG, 1, "-->NO TIMING !"); + } + return internal->status; +} + +/* + * stb0899_search_tmg + * perform a fs/2 zig-zag to find timing + */ +static enum stb0899_status stb0899_search_tmg(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_params *params = &state->params; + + short int derot_step, derot_freq = 0, derot_limit, next_loop = 3; + int index = 0; + u8 cfr[2]; + + internal->status = NOTIMING; + + /* timing loop computation & symbol rate optimisation */ + derot_limit = (internal->sub_range / 2L) / internal->mclk; + derot_step = (params->srate / 2L) / internal->mclk; + + while ((stb0899_check_tmg(state) != TIMINGOK) && next_loop) { + index++; + derot_freq += index * internal->direction * derot_step; /* next derot zig zag position */ + + if (ABS(derot_freq) > derot_limit) + next_loop--; + + if (next_loop) { + STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq)); + STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq)); + stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ + } + internal->direction = -internal->direction; /* Change zigzag direction */ + } + + if (internal->status == TIMINGOK) { + stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ + internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]); + dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK ! Derot Freq = %d", internal->derot_freq); + } + + return internal->status; +} + +/* + * stb0899_check_carrier + * Check for carrier found + */ +static enum stb0899_status stb0899_check_carrier(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + u8 reg; + + msleep(internal->t_derot); /* wait for derotator ok */ + + reg = stb0899_read_reg(state, STB0899_CFD); + STB0899_SETFIELD_VAL(CFD_ON, reg, 1); + stb0899_write_reg(state, STB0899_CFD, reg); + + reg = stb0899_read_reg(state, STB0899_DSTATUS); + dprintk(state->verbose, FE_DEBUG, 1, "--------------------> STB0899_DSTATUS=[0x%02x]", reg); + if (STB0899_GETFIELD(CARRIER_FOUND, reg)) { + internal->status = CARRIEROK; + dprintk(state->verbose, FE_DEBUG, 1, "-------------> CARRIEROK !"); + } else { + internal->status = NOCARRIER; + dprintk(state->verbose, FE_DEBUG, 1, "-------------> NOCARRIER !"); + } + + return internal->status; +} + +/* + * stb0899_search_carrier + * Search for a QPSK carrier with the derotator + */ +static enum stb0899_status stb0899_search_carrier(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + + short int derot_freq = 0, last_derot_freq = 0, derot_limit, next_loop = 3; + int index = 0; + u8 cfr[2]; + u8 reg; + + internal->status = NOCARRIER; + derot_limit = (internal->sub_range / 2L) / internal->mclk; + derot_freq = internal->derot_freq; + + reg = stb0899_read_reg(state, STB0899_CFD); + STB0899_SETFIELD_VAL(CFD_ON, reg, 1); + stb0899_write_reg(state, STB0899_CFD, reg); + + do { + dprintk(state->verbose, FE_DEBUG, 1, "Derot Freq=%d, mclk=%d", derot_freq, internal->mclk); + if (stb0899_check_carrier(state) == NOCARRIER) { + index++; + last_derot_freq = derot_freq; + derot_freq += index * internal->direction * internal->derot_step; /* next zig zag derotator position */ + + if(ABS(derot_freq) > derot_limit) + next_loop--; + + if (next_loop) { + reg = stb0899_read_reg(state, STB0899_CFD); + STB0899_SETFIELD_VAL(CFD_ON, reg, 1); + stb0899_write_reg(state, STB0899_CFD, reg); + + STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq)); + STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq)); + stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ + } + } + + internal->direction = -internal->direction; /* Change zigzag direction */ + } while ((internal->status != CARRIEROK) && next_loop); + + if (internal->status == CARRIEROK) { + stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ + internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]); + dprintk(state->verbose, FE_DEBUG, 1, "----> CARRIER OK !, Derot Freq=%d", internal->derot_freq); + } else { + internal->derot_freq = last_derot_freq; + } + + return internal->status; +} + +/* + * stb0899_check_data + * Check for data found + */ +static enum stb0899_status stb0899_check_data(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_params *params = &state->params; + + int lock = 0, index = 0, dataTime = 500, loop; + u8 reg; + + internal->status = NODATA; + + /* RESET FEC */ + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESACS, reg, 1); + stb0899_write_reg(state, STB0899_TSTRES, reg); + msleep(1); + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESACS, reg, 0); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + if (params->srate <= 2000000) + dataTime = 2000; + else if (params->srate <= 5000000) + dataTime = 1500; + else if (params->srate <= 15000000) + dataTime = 1000; + else + dataTime = 500; + + stb0899_write_reg(state, STB0899_DSTATUS2, 0x00); /* force search loop */ + while (1) { + /* WARNING! VIT LOCKED has to be tested before VIT_END_LOOOP */ + reg = stb0899_read_reg(state, STB0899_VSTATUS); + lock = STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg); + loop = STB0899_GETFIELD(VSTATUS_END_LOOPVIT, reg); + + if (lock || loop || (index > dataTime)) + break; + index++; + } + + if (lock) { /* DATA LOCK indicator */ + internal->status = DATAOK; + dprintk(state->verbose, FE_DEBUG, 1, "-----------------> DATA OK !"); + } + + return internal->status; +} + +/* + * stb0899_search_data + * Search for a QPSK carrier with the derotator + */ +static enum stb0899_status stb0899_search_data(struct stb0899_state *state) +{ + short int derot_freq, derot_step, derot_limit, next_loop = 3; + u8 cfr[2]; + u8 reg; + int index = 1; + + struct stb0899_internal *internal = &state->internal; + struct stb0899_params *params = &state->params; + + derot_step = (params->srate / 4L) / internal->mclk; + derot_limit = (internal->sub_range / 2L) / internal->mclk; + derot_freq = internal->derot_freq; + + do { + if ((internal->status != CARRIEROK) || (stb0899_check_data(state) != DATAOK)) { + + derot_freq += index * internal->direction * derot_step; /* next zig zag derotator position */ + if (ABS(derot_freq) > derot_limit) + next_loop--; + + if (next_loop) { + dprintk(state->verbose, FE_DEBUG, 1, "Derot freq=%d, mclk=%d", derot_freq, internal->mclk); + reg = stb0899_read_reg(state, STB0899_CFD); + STB0899_SETFIELD_VAL(CFD_ON, reg, 1); + stb0899_write_reg(state, STB0899_CFD, reg); + + STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq)); + STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq)); + stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ + + stb0899_check_carrier(state); + index++; + } + } + internal->direction = -internal->direction; /* change zig zag direction */ + } while ((internal->status != DATAOK) && next_loop); + + if (internal->status == DATAOK) { + stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ + internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]); + dprintk(state->verbose, FE_DEBUG, 1, "------> DATAOK ! Derot Freq=%d", internal->derot_freq); + } + + return internal->status; +} + +/* + * stb0899_check_range + * check if the found frequency is in the correct range + */ +static enum stb0899_status stb0899_check_range(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_params *params = &state->params; + + int range_offst, tp_freq; + + range_offst = internal->srch_range / 2000; + tp_freq = internal->freq + (internal->derot_freq * internal->mclk) / 1000; + + if ((tp_freq >= params->freq - range_offst) && (tp_freq <= params->freq + range_offst)) { + internal->status = RANGEOK; + dprintk(state->verbose, FE_DEBUG, 1, "----> RANGEOK !"); + } else { + internal->status = OUTOFRANGE; + dprintk(state->verbose, FE_DEBUG, 1, "----> OUT OF RANGE !"); + } + + return internal->status; +} + +/* + * NextSubRange + * Compute the next subrange of the search + */ +static void next_sub_range(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_params *params = &state->params; + + long old_sub_range; + + if (internal->sub_dir > 0) { + old_sub_range = internal->sub_range; + internal->sub_range = MIN((internal->srch_range / 2) - + (internal->tuner_offst + internal->sub_range / 2), + internal->sub_range); + + if (internal->sub_range < 0) + internal->sub_range = 0; + + internal->tuner_offst += (old_sub_range + internal->sub_range) / 2; + } + + internal->freq = params->freq + (internal->sub_dir * internal->tuner_offst) / 1000; + internal->sub_dir = -internal->sub_dir; +} + +/* + * stb0899_dvbs_algo + * Search for a signal, timing, carrier and data for a + * given frequency in a given range + */ +enum stb0899_status stb0899_dvbs_algo(struct stb0899_state *state) +{ + struct stb0899_params *params = &state->params; + struct stb0899_internal *internal = &state->internal; + struct stb0899_config *config = state->config; + + u8 bclc, reg; + u8 cfr[2]; + u8 eq_const[10]; + s32 clnI = 3; + u32 bandwidth = 0; + + /* BETA values rated @ 99MHz */ + s32 betaTab[5][4] = { + /* 5 10 20 30MBps */ + { 37, 34, 32, 31 }, /* QPSK 1/2 */ + { 37, 35, 33, 31 }, /* QPSK 2/3 */ + { 37, 35, 33, 31 }, /* QPSK 3/4 */ + { 37, 36, 33, 32 }, /* QPSK 5/6 */ + { 37, 36, 33, 32 } /* QPSK 7/8 */ + }; + + internal->direction = 1; + + stb0899_set_srate(state, internal->master_clk, params->srate); + /* Carrier loop optimization versus symbol rate for acquisition*/ + if (params->srate <= 5000000) { + stb0899_write_reg(state, STB0899_ACLC, 0x89); + bclc = stb0899_read_reg(state, STB0899_BCLC); + STB0899_SETFIELD_VAL(BETA, bclc, 0x1c); + stb0899_write_reg(state, STB0899_BCLC, bclc); + clnI = 0; + } else if (params->srate <= 15000000) { + stb0899_write_reg(state, STB0899_ACLC, 0xc9); + bclc = stb0899_read_reg(state, STB0899_BCLC); + STB0899_SETFIELD_VAL(BETA, bclc, 0x22); + stb0899_write_reg(state, STB0899_BCLC, bclc); + clnI = 1; + } else if(params->srate <= 25000000) { + stb0899_write_reg(state, STB0899_ACLC, 0x89); + bclc = stb0899_read_reg(state, STB0899_BCLC); + STB0899_SETFIELD_VAL(BETA, bclc, 0x27); + stb0899_write_reg(state, STB0899_BCLC, bclc); + clnI = 2; + } else { + stb0899_write_reg(state, STB0899_ACLC, 0xc8); + bclc = stb0899_read_reg(state, STB0899_BCLC); + STB0899_SETFIELD_VAL(BETA, bclc, 0x29); + stb0899_write_reg(state, STB0899_BCLC, bclc); + clnI = 3; + } + + dprintk(state->verbose, FE_DEBUG, 1, "Set the timing loop to acquisition"); + /* Set the timing loop to acquisition */ + stb0899_write_reg(state, STB0899_RTC, 0x46); + stb0899_write_reg(state, STB0899_CFD, 0xee); + + /* !! WARNING !! + * Do not read any status variables while acquisition, + * If any needed, read before the acquisition starts + * querying status while acquiring causes the + * acquisition to go bad and hence no locks. + */ + dprintk(state->verbose, FE_DEBUG, 1, "Derot Percent=%d Srate=%d mclk=%d", + internal->derot_percent, params->srate, internal->mclk); + + /* Initial calculations */ + internal->derot_step = internal->derot_percent * (params->srate / 1000L) / internal->mclk; /* DerotStep/1000 * Fsymbol */ + internal->t_derot = stb0899_calc_derot_time(params->srate); + internal->t_data = 500; + + dprintk(state->verbose, FE_DEBUG, 1, "RESET stream merger"); + /* RESET Stream merger */ + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESRS, reg, 1); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + /* + * Set KDIVIDER to an intermediate value between + * 1/2 and 7/8 for acquisition + */ + reg = stb0899_read_reg(state, STB0899_DEMAPVIT); + STB0899_SETFIELD_VAL(DEMAPVIT_KDIVIDER, reg, 60); + stb0899_write_reg(state, STB0899_DEMAPVIT, reg); + + stb0899_write_reg(state, STB0899_EQON, 0x01); /* Equalizer OFF while acquiring */ + stb0899_write_reg(state, STB0899_VITSYNC, 0x19); + + stb0899_first_subrange(state); + do { + /* Initialisations */ + cfr[0] = cfr[1] = 0; + stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* RESET derotator frequency */ + + stb0899_write_reg(state, STB0899_RTF, 0); + reg = stb0899_read_reg(state, STB0899_CFD); + STB0899_SETFIELD_VAL(CFD_ON, reg, 1); + stb0899_write_reg(state, STB0899_CFD, reg); + + internal->derot_freq = 0; + internal->status = NOAGC1; + + /* enable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 1); + + /* Move tuner to frequency */ + dprintk(state->verbose, FE_DEBUG, 1, "Tuner set frequency"); + if (state->config->tuner_set_frequency) + state->config->tuner_set_frequency(&state->frontend, internal->freq); + + if (state->config->tuner_get_frequency) + state->config->tuner_get_frequency(&state->frontend, &internal->freq); + + msleep(internal->t_agc1 + internal->t_agc2 + internal->t_derot); /* AGC1, AGC2 and timing loop */ + dprintk(state->verbose, FE_DEBUG, 1, "current derot freq=%d", internal->derot_freq); + internal->status = AGC1OK; + + /* There is signal in the band */ + if (config->tuner_get_bandwidth) + config->tuner_get_bandwidth(&state->frontend, &bandwidth); + + /* disable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 0); + + if (params->srate <= bandwidth / 2) + stb0899_search_tmg(state); /* For low rates (SCPC) */ + else + stb0899_check_tmg(state); /* For high rates (MCPC) */ + + if (internal->status == TIMINGOK) { + dprintk(state->verbose, FE_DEBUG, 1, + "TIMING OK ! Derot freq=%d, mclk=%d", + internal->derot_freq, internal->mclk); + + if (stb0899_search_carrier(state) == CARRIEROK) { /* Search for carrier */ + dprintk(state->verbose, FE_DEBUG, 1, + "CARRIER OK ! Derot freq=%d, mclk=%d", + internal->derot_freq, internal->mclk); + + if (stb0899_search_data(state) == DATAOK) { /* Check for data */ + dprintk(state->verbose, FE_DEBUG, 1, + "DATA OK ! Derot freq=%d, mclk=%d", + internal->derot_freq, internal->mclk); + + if (stb0899_check_range(state) == RANGEOK) { + dprintk(state->verbose, FE_DEBUG, 1, + "RANGE OK ! derot freq=%d, mclk=%d", + internal->derot_freq, internal->mclk); + + internal->freq = params->freq + ((internal->derot_freq * internal->mclk) / 1000); + reg = stb0899_read_reg(state, STB0899_PLPARM); + internal->fecrate = STB0899_GETFIELD(VITCURPUN, reg); + dprintk(state->verbose, FE_DEBUG, 1, + "freq=%d, internal resultant freq=%d", + params->freq, internal->freq); + + dprintk(state->verbose, FE_DEBUG, 1, + "internal puncture rate=%d", + internal->fecrate); + } + } + } + } + if (internal->status != RANGEOK) + next_sub_range(state); + + } while (internal->sub_range && internal->status != RANGEOK); + + /* Set the timing loop to tracking */ + stb0899_write_reg(state, STB0899_RTC, 0x33); + stb0899_write_reg(state, STB0899_CFD, 0xf7); + /* if locked and range ok, set Kdiv */ + if (internal->status == RANGEOK) { + dprintk(state->verbose, FE_DEBUG, 1, "Locked & Range OK !"); + stb0899_write_reg(state, STB0899_EQON, 0x41); /* Equalizer OFF while acquiring */ + stb0899_write_reg(state, STB0899_VITSYNC, 0x39); /* SN to b'11 for acquisition */ + + /* + * Carrier loop optimization versus + * symbol Rate/Puncture Rate for Tracking + */ + reg = stb0899_read_reg(state, STB0899_BCLC); + switch (internal->fecrate) { + case STB0899_FEC_1_2: /* 13 */ + stb0899_write_reg(state, STB0899_DEMAPVIT, 0x1a); + STB0899_SETFIELD_VAL(BETA, reg, betaTab[0][clnI]); + stb0899_write_reg(state, STB0899_BCLC, reg); + break; + case STB0899_FEC_2_3: /* 18 */ + stb0899_write_reg(state, STB0899_DEMAPVIT, 44); + STB0899_SETFIELD_VAL(BETA, reg, betaTab[1][clnI]); + stb0899_write_reg(state, STB0899_BCLC, reg); + break; + case STB0899_FEC_3_4: /* 21 */ + stb0899_write_reg(state, STB0899_DEMAPVIT, 60); + STB0899_SETFIELD_VAL(BETA, reg, betaTab[2][clnI]); + stb0899_write_reg(state, STB0899_BCLC, reg); + break; + case STB0899_FEC_5_6: /* 24 */ + stb0899_write_reg(state, STB0899_DEMAPVIT, 75); + STB0899_SETFIELD_VAL(BETA, reg, betaTab[3][clnI]); + stb0899_write_reg(state, STB0899_BCLC, reg); + break; + case STB0899_FEC_6_7: /* 25 */ + stb0899_write_reg(state, STB0899_DEMAPVIT, 88); + stb0899_write_reg(state, STB0899_ACLC, 0x88); + stb0899_write_reg(state, STB0899_BCLC, 0x9a); + break; + case STB0899_FEC_7_8: /* 26 */ + stb0899_write_reg(state, STB0899_DEMAPVIT, 94); + STB0899_SETFIELD_VAL(BETA, reg, betaTab[4][clnI]); + stb0899_write_reg(state, STB0899_BCLC, reg); + break; + default: + dprintk(state->verbose, FE_DEBUG, 1, "Unsupported Puncture Rate"); + break; + } + /* release stream merger RESET */ + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESRS, reg, 0); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + /* disable carrier detector */ + reg = stb0899_read_reg(state, STB0899_CFD); + STB0899_SETFIELD_VAL(CFD_ON, reg, 0); + stb0899_write_reg(state, STB0899_CFD, reg); + + stb0899_read_regs(state, STB0899_EQUAI1, eq_const, 10); + } + + return internal->status; +} + +/* + * stb0899_dvbs2_config_uwp + * Configure UWP state machine + */ +static void stb0899_dvbs2_config_uwp(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_config *config = state->config; + u32 uwp1, uwp2, uwp3, reg; + + uwp1 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1); + uwp2 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL2); + uwp3 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL3); + + STB0899_SETFIELD_VAL(UWP_ESN0_AVE, uwp1, config->esno_ave); + STB0899_SETFIELD_VAL(UWP_ESN0_QUANT, uwp1, config->esno_quant); + STB0899_SETFIELD_VAL(UWP_TH_SOF, uwp1, config->uwp_threshold_sof); + + STB0899_SETFIELD_VAL(FE_COARSE_TRK, uwp2, internal->av_frame_coarse); + STB0899_SETFIELD_VAL(FE_FINE_TRK, uwp2, internal->av_frame_fine); + STB0899_SETFIELD_VAL(UWP_MISS_TH, uwp2, config->miss_threshold); + + STB0899_SETFIELD_VAL(UWP_TH_ACQ, uwp3, config->uwp_threshold_acq); + STB0899_SETFIELD_VAL(UWP_TH_TRACK, uwp3, config->uwp_threshold_track); + + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL1, STB0899_OFF0_UWP_CNTRL1, uwp1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL2, STB0899_OFF0_UWP_CNTRL2, uwp2); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL3, STB0899_OFF0_UWP_CNTRL3, uwp3); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, SOF_SRCH_TO); + STB0899_SETFIELD_VAL(SOF_SEARCH_TIMEOUT, reg, config->sof_search_timeout); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_SOF_SRCH_TO, STB0899_OFF0_SOF_SRCH_TO, reg); +} + +/* + * stb0899_dvbs2_config_csm_auto + * Set CSM to AUTO mode + */ +static void stb0899_dvbs2_config_csm_auto(struct stb0899_state *state) +{ + u32 reg; + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); + STB0899_SETFIELD_VAL(CSM_AUTO_PARAM, reg, 1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, reg); +} + +long Log2Int(int number) +{ + int i; + + i = 0; + while ((1 << i) <= ABS(number)) + i++; + + if (number == 0) + i = 1; + + return i - 1; +} + +/* + * stb0899_dvbs2_calc_srate + * compute BTR_NOM_FREQ for the symbol rate + */ +static u32 stb0899_dvbs2_calc_srate(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_config *config = state->config; + + u32 dec_ratio, dec_rate, decim, remain, intval, btr_nom_freq; + u32 master_clk, srate; + + dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); + dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; + dec_rate = Log2Int(dec_ratio); + decim = 1 << dec_rate; + master_clk = internal->master_clk / 1000; + srate = internal->srate / 1000; + + if (decim <= 4) { + intval = (decim * (1 << (config->btr_nco_bits - 1))) / master_clk; + remain = (decim * (1 << (config->btr_nco_bits - 1))) % master_clk; + } else { + intval = (1 << (config->btr_nco_bits - 1)) / (master_clk / 100) * decim / 100; + remain = (decim * (1 << (config->btr_nco_bits - 1))) % master_clk; + } + btr_nom_freq = (intval * srate) + ((remain * srate) / master_clk); + + return btr_nom_freq; +} + +/* + * stb0899_dvbs2_calc_dev + * compute the correction to be applied to symbol rate + */ +static u32 stb0899_dvbs2_calc_dev(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + u32 dec_ratio, correction, master_clk, srate; + + dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); + dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; + + master_clk = internal->master_clk / 1000; /* for integer Caculation*/ + srate = internal->srate / 1000; /* for integer Caculation*/ + correction = (512 * master_clk) / (2 * dec_ratio * srate); + + return correction; +} + +/* + * stb0899_dvbs2_set_srate + * Set DVBS2 symbol rate + */ +static void stb0899_dvbs2_set_srate(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + + u32 dec_ratio, dec_rate, win_sel, decim, f_sym, btr_nom_freq; + u32 correction, freq_adj, band_lim, decim_cntrl, reg; + u8 anti_alias; + + /*set decimation to 1*/ + dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); + dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; + dec_rate = Log2Int(dec_ratio); + + win_sel = 0; + if (dec_rate >= 5) + win_sel = dec_rate - 4; + + decim = (1 << dec_rate); + /* (FSamp/Fsymbol *100) for integer Caculation */ + f_sym = internal->master_clk / ((decim * internal->srate) / 1000); + + if (f_sym <= 2250) /* don't band limit signal going into btr block*/ + band_lim = 1; + else + band_lim = 0; /* band limit signal going into btr block*/ + + decim_cntrl = ((win_sel << 3) & 0x18) + ((band_lim << 5) & 0x20) + (dec_rate & 0x7); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DECIM_CNTRL, STB0899_OFF0_DECIM_CNTRL, decim_cntrl); + + if (f_sym <= 3450) + anti_alias = 0; + else if (f_sym <= 4250) + anti_alias = 1; + else + anti_alias = 2; + + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ANTI_ALIAS_SEL, STB0899_OFF0_ANTI_ALIAS_SEL, anti_alias); + btr_nom_freq = stb0899_dvbs2_calc_srate(state); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_NOM_FREQ, STB0899_OFF0_BTR_NOM_FREQ, btr_nom_freq); + + correction = stb0899_dvbs2_calc_dev(state); + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_CNTRL); + STB0899_SETFIELD_VAL(BTR_FREQ_CORR, reg, correction); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_CNTRL, STB0899_OFF0_BTR_CNTRL, reg); + + /* scale UWP+CSM frequency to sample rate*/ + freq_adj = internal->srate / (internal->master_clk / 4096); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_FREQ_ADJ_SCALE, STB0899_OFF0_FREQ_ADJ_SCALE, freq_adj); +} + +/* + * stb0899_dvbs2_set_btr_loopbw + * set bit timing loop bandwidth as a percentage of the symbol rate + */ +static void stb0899_dvbs2_set_btr_loopbw(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_config *config = state->config; + + u32 sym_peak = 23, zeta = 707, loopbw_percent = 60; + s32 dec_ratio, dec_rate, k_btr1_rshft, k_btr1, k_btr0_rshft; + s32 k_btr0, k_btr2_rshft, k_direct_shift, k_indirect_shift; + u32 decim, K, wn, k_direct, k_indirect; + u32 reg; + + dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); + dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; + dec_rate = Log2Int(dec_ratio); + decim = (1 << dec_rate); + + sym_peak *= 576000; + K = (1 << config->btr_nco_bits) / (internal->master_clk / 1000); + K *= (internal->srate / 1000000) * decim; /*k=k 10^-8*/ + + if (K != 0) { + K = sym_peak / K; + wn = (4 * zeta * zeta) + 1000000; + wn = (2 * (loopbw_percent * 1000) * 40 * zeta) /wn; /*wn =wn 10^-8*/ + + k_indirect = (wn * wn) / K; + k_indirect = k_indirect; /*kindirect = kindirect 10^-6*/ + k_direct = (2 * wn * zeta) / K; /*kDirect = kDirect 10^-2*/ + k_direct *= 100; + + k_direct_shift = Log2Int(k_direct) - Log2Int(10000) - 2; + k_btr1_rshft = (-1 * k_direct_shift) + config->btr_gain_shift_offset; + k_btr1 = k_direct / (1 << k_direct_shift); + k_btr1 /= 10000; + + k_indirect_shift = Log2Int(k_indirect + 15) - 20 /*- 2*/; + k_btr0_rshft = (-1 * k_indirect_shift) + config->btr_gain_shift_offset; + k_btr0 = k_indirect * (1 << (-k_indirect_shift)); + k_btr0 /= 1000000; + + k_btr2_rshft = 0; + if (k_btr0_rshft > 15) { + k_btr2_rshft = k_btr0_rshft - 15; + k_btr0_rshft = 15; + } + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_LOOP_GAIN); + STB0899_SETFIELD_VAL(KBTR0_RSHFT, reg, k_btr0_rshft); + STB0899_SETFIELD_VAL(KBTR0, reg, k_btr0); + STB0899_SETFIELD_VAL(KBTR1_RSHFT, reg, k_btr1_rshft); + STB0899_SETFIELD_VAL(KBTR1, reg, k_btr1); + STB0899_SETFIELD_VAL(KBTR2_RSHFT, reg, k_btr2_rshft); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_LOOP_GAIN, STB0899_OFF0_BTR_LOOP_GAIN, reg); + } else + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_LOOP_GAIN, STB0899_OFF0_BTR_LOOP_GAIN, 0xc4c4f); +} + +/* + * stb0899_dvbs2_set_carr_freq + * set nominal frequency for carrier search + */ +static void stb0899_dvbs2_set_carr_freq(struct stb0899_state *state, s32 carr_freq, u32 master_clk) +{ + struct stb0899_config *config = state->config; + s32 crl_nom_freq; + u32 reg; + + crl_nom_freq = (1 << config->crl_nco_bits) / master_clk; + crl_nom_freq *= carr_freq; + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); + STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, crl_nom_freq); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); +} + +/* + * stb0899_dvbs2_init_calc + * Initialize DVBS2 UWP, CSM, carrier and timing loops + */ +static void stb0899_dvbs2_init_calc(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + s32 steps, step_size; + u32 range, reg; + + /* config uwp and csm */ + stb0899_dvbs2_config_uwp(state); + stb0899_dvbs2_config_csm_auto(state); + + /* initialize BTR */ + stb0899_dvbs2_set_srate(state); + stb0899_dvbs2_set_btr_loopbw(state); + + if (internal->srate / 1000000 >= 15) + step_size = (1 << 17) / 5; + else if (internal->srate / 1000000 >= 10) + step_size = (1 << 17) / 7; + else if (internal->srate / 1000000 >= 5) + step_size = (1 << 17) / 10; + else + step_size = (1 << 17) / 4; + + range = internal->srch_range / 1000000; + steps = (10 * range * (1 << 17)) / (step_size * (internal->srate / 1000000)); + steps = (steps + 6) / 10; + steps = (steps == 0) ? 1 : steps; + if (steps % 2 == 0) + stb0899_dvbs2_set_carr_freq(state, internal->center_freq - + (internal->step_size * (internal->srate / 20000000)), + (internal->master_clk) / 1000000); + else + stb0899_dvbs2_set_carr_freq(state, internal->center_freq, (internal->master_clk) / 1000000); + + /*Set Carrier Search params (zigzag, num steps and freq step size*/ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, ACQ_CNTRL2); + STB0899_SETFIELD_VAL(ZIGZAG, reg, 1); + STB0899_SETFIELD_VAL(NUM_STEPS, reg, steps); + STB0899_SETFIELD_VAL(FREQ_STEPSIZE, reg, step_size); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ACQ_CNTRL2, STB0899_OFF0_ACQ_CNTRL2, reg); +} + +/* + * stb0899_dvbs2_btr_init + * initialize the timing loop + */ +static void stb0899_dvbs2_btr_init(struct stb0899_state *state) +{ + u32 reg; + + /* set enable BTR loopback */ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_CNTRL); + STB0899_SETFIELD_VAL(INTRP_PHS_SENSE, reg, 1); + STB0899_SETFIELD_VAL(BTR_ERR_ENA, reg, 1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_CNTRL, STB0899_OFF0_BTR_CNTRL, reg); + + /* fix btr freq accum at 0 */ + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_FREQ_INIT, STB0899_OFF0_BTR_FREQ_INIT, 0x10000000); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_FREQ_INIT, STB0899_OFF0_BTR_FREQ_INIT, 0x00000000); + + /* fix btr freq accum at 0 */ + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_PHS_INIT, STB0899_OFF0_BTR_PHS_INIT, 0x10000000); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_PHS_INIT, STB0899_OFF0_BTR_PHS_INIT, 0x00000000); +} + +/* + * stb0899_dvbs2_reacquire + * trigger a DVB-S2 acquisition + */ +static void stb0899_dvbs2_reacquire(struct stb0899_state *state) +{ + u32 reg = 0; + + /* demod soft reset */ + STB0899_SETFIELD_VAL(DVBS2_RESET, reg, 1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_RESET_CNTRL, STB0899_OFF0_RESET_CNTRL, reg); + + /*Reset Timing Loop */ + stb0899_dvbs2_btr_init(state); + + /* reset Carrier loop */ + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_FREQ_INIT, STB0899_OFF0_CRL_FREQ_INIT, (1 << 30)); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_FREQ_INIT, STB0899_OFF0_CRL_FREQ_INIT, 0); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_LOOP_GAIN, STB0899_OFF0_CRL_LOOP_GAIN, 0); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_PHS_INIT, STB0899_OFF0_CRL_PHS_INIT, (1 << 30)); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_PHS_INIT, STB0899_OFF0_CRL_PHS_INIT, 0); + + /*release demod soft reset */ + reg = 0; + STB0899_SETFIELD_VAL(DVBS2_RESET, reg, 0); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_RESET_CNTRL, STB0899_OFF0_RESET_CNTRL, reg); + + /* start acquisition process */ + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ACQUIRE_TRIG, STB0899_OFF0_ACQUIRE_TRIG, 1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_LOCK_LOST, STB0899_OFF0_LOCK_LOST, 0); + + /* equalizer Init */ + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQUALIZER_INIT, STB0899_OFF0_EQUALIZER_INIT, 1); + + /*Start equilizer */ + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQUALIZER_INIT, STB0899_OFF0_EQUALIZER_INIT, 0); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); + STB0899_SETFIELD_VAL(EQ_SHIFT, reg, 0); + STB0899_SETFIELD_VAL(EQ_DISABLE_UPDATE, reg, 0); + STB0899_SETFIELD_VAL(EQ_DELAY, reg, 0x05); + STB0899_SETFIELD_VAL(EQ_ADAPT_MODE, reg, 0x01); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); + + /* RESET Packet delineator */ + stb0899_write_reg(state, STB0899_PDELCTRL, 0x4a); +} + +/* + * stb0899_dvbs2_get_dmd_status + * get DVB-S2 Demod LOCK status + */ +static enum stb0899_status stb0899_dvbs2_get_dmd_status(struct stb0899_state *state, int timeout) +{ + int time = -10, lock = 0, uwp, csm; + u32 reg; + + do { + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STATUS); + dprintk(state->verbose, FE_DEBUG, 1, "DMD_STATUS=[0x%02x]", reg); + if (STB0899_GETFIELD(IF_AGC_LOCK, reg)) + dprintk(state->verbose, FE_DEBUG, 1, "------------->IF AGC LOCKED !"); + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2); + dprintk(state->verbose, FE_DEBUG, 1, "----------->DMD STAT2=[0x%02x]", reg); + uwp = STB0899_GETFIELD(UWP_LOCK, reg); + csm = STB0899_GETFIELD(CSM_LOCK, reg); + if (uwp && csm) + lock = 1; + + time += 10; + msleep(10); + + } while ((!lock) && (time <= timeout)); + + if (lock) { + dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 LOCK !"); + return DVBS2_DEMOD_LOCK; + } else { + return DVBS2_DEMOD_NOLOCK; + } +} + +/* + * stb0899_dvbs2_get_data_lock + * get FEC status + */ +static int stb0899_dvbs2_get_data_lock(struct stb0899_state *state, int timeout) +{ + int time = 0, lock = 0; + u8 reg; + + while ((!lock) && (time < timeout)) { + reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1); + dprintk(state->verbose, FE_DEBUG, 1, "---------> CFGPDELSTATUS=[0x%02x]", reg); + lock = STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg); + time++; + } + + return lock; +} + +/* + * stb0899_dvbs2_get_fec_status + * get DVB-S2 FEC LOCK status + */ +static enum stb0899_status stb0899_dvbs2_get_fec_status(struct stb0899_state *state, int timeout) +{ + int time = 0, Locked; + + do { + Locked = stb0899_dvbs2_get_data_lock(state, 1); + time++; + msleep(1); + + } while ((!Locked) && (time < timeout)); + + if (Locked) { + dprintk(state->verbose, FE_DEBUG, 1, "---------->DVB-S2 FEC LOCK !"); + return DVBS2_FEC_LOCK; + } else { + return DVBS2_FEC_NOLOCK; + } +} + + +/* + * stb0899_dvbs2_init_csm + * set parameters for manual mode + */ +static void stb0899_dvbs2_init_csm(struct stb0899_state *state, int pilots, enum stb0899_modcod modcod) +{ + struct stb0899_internal *internal = &state->internal; + + s32 dvt_tbl = 1, two_pass = 0, agc_gain = 6, agc_shift = 0, loop_shift = 0, phs_diff_thr = 0x80; + s32 gamma_acq, gamma_rho_acq, gamma_trk, gamma_rho_trk, lock_count_thr; + u32 csm1, csm2, csm3, csm4; + + if (((internal->master_clk / internal->srate) <= 4) && (modcod <= 11) && (pilots == 1)) { + switch (modcod) { + case STB0899_QPSK_12: + gamma_acq = 25; + gamma_rho_acq = 2700; + gamma_trk = 12; + gamma_rho_trk = 180; + lock_count_thr = 8; + break; + case STB0899_QPSK_35: + gamma_acq = 38; + gamma_rho_acq = 7182; + gamma_trk = 14; + gamma_rho_trk = 308; + lock_count_thr = 8; + break; + case STB0899_QPSK_23: + gamma_acq = 42; + gamma_rho_acq = 9408; + gamma_trk = 17; + gamma_rho_trk = 476; + lock_count_thr = 8; + break; + case STB0899_QPSK_34: + gamma_acq = 53; + gamma_rho_acq = 16642; + gamma_trk = 19; + gamma_rho_trk = 646; + lock_count_thr = 8; + break; + case STB0899_QPSK_45: + gamma_acq = 53; + gamma_rho_acq = 17119; + gamma_trk = 22; + gamma_rho_trk = 880; + lock_count_thr = 8; + break; + case STB0899_QPSK_56: + gamma_acq = 55; + gamma_rho_acq = 19250; + gamma_trk = 23; + gamma_rho_trk = 989; + lock_count_thr = 8; + break; + case STB0899_QPSK_89: + gamma_acq = 60; + gamma_rho_acq = 24240; + gamma_trk = 24; + gamma_rho_trk = 1176; + lock_count_thr = 8; + break; + case STB0899_QPSK_910: + gamma_acq = 66; + gamma_rho_acq = 29634; + gamma_trk = 24; + gamma_rho_trk = 1176; + lock_count_thr = 8; + break; + default: + gamma_acq = 66; + gamma_rho_acq = 29634; + gamma_trk = 24; + gamma_rho_trk = 1176; + lock_count_thr = 8; + break; + } + + csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); + STB0899_SETFIELD_VAL(CSM_AUTO_PARAM, csm1, 0); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); + + csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); + csm2 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL2); + csm3 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL3); + csm4 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL4); + + STB0899_SETFIELD_VAL(CSM_DVT_TABLE, csm1, dvt_tbl); + STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, two_pass); + STB0899_SETFIELD_VAL(CSM_AGC_GAIN, csm1, agc_gain); + STB0899_SETFIELD_VAL(CSM_AGC_SHIFT, csm1, agc_shift); + STB0899_SETFIELD_VAL(FE_LOOP_SHIFT, csm1, loop_shift); + STB0899_SETFIELD_VAL(CSM_GAMMA_ACQ, csm2, gamma_acq); + STB0899_SETFIELD_VAL(CSM_GAMMA_RHOACQ, csm2, gamma_rho_acq); + STB0899_SETFIELD_VAL(CSM_GAMMA_TRACK, csm3, gamma_trk); + STB0899_SETFIELD_VAL(CSM_GAMMA_RHOTRACK, csm3, gamma_rho_trk); + STB0899_SETFIELD_VAL(CSM_LOCKCOUNT_THRESH, csm4, lock_count_thr); + STB0899_SETFIELD_VAL(CSM_PHASEDIFF_THRESH, csm4, phs_diff_thr); + + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL2, STB0899_OFF0_CSM_CNTRL2, csm2); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL3, STB0899_OFF0_CSM_CNTRL3, csm3); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL4, STB0899_OFF0_CSM_CNTRL4, csm4); + } +} + +/* + * stb0899_dvbs2_get_srate + * get DVB-S2 Symbol Rate + */ +static u32 stb0899_dvbs2_get_srate(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_config *config = state->config; + + u32 bTrNomFreq, srate, decimRate, intval1, intval2, reg; + int div1, div2, rem1, rem2; + + div1 = config->btr_nco_bits / 2; + div2 = config->btr_nco_bits - div1 - 1; + + bTrNomFreq = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_NOM_FREQ); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DECIM_CNTRL); + decimRate = STB0899_GETFIELD(DECIM_RATE, reg); + decimRate = (1 << decimRate); + + intval1 = internal->master_clk / (1 << div1); + intval2 = bTrNomFreq / (1 << div2); + + rem1 = internal->master_clk % (1 << div1); + rem2 = bTrNomFreq % (1 << div2); + /* only for integer calculation */ + srate = (intval1 * intval2) + ((intval1 * rem2) / (1 << div2)) + ((intval2 * rem1) / (1 << div1)); + srate /= decimRate; /*symbrate = (btrnomfreq_register_val*MasterClock)/2^(27+decim_rate_field) */ + + return srate; +} + +/* + * stb0899_dvbs2_algo + * Search for signal, timing, carrier and data for a given + * frequency in a given range + */ +enum stb0899_status stb0899_dvbs2_algo(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + enum stb0899_modcod modcod; + + s32 offsetfreq, searchTime, FecLockTime, pilots, iqSpectrum; + int i = 0; + u32 reg, csm1; + + if (internal->srate <= 2000000) { + searchTime = 5000; /* 5000 ms max time to lock UWP and CSM, SYMB <= 2Mbs */ + FecLockTime = 350; /* 350 ms max time to lock FEC, SYMB <= 2Mbs */ + } else if (internal->srate <= 5000000) { + searchTime = 2500; /* 2500 ms max time to lock UWP and CSM, 2Mbs < SYMB <= 5Mbs */ + FecLockTime = 170; /* 170 ms max time to lock FEC, 2Mbs< SYMB <= 5Mbs */ + } else if (internal->srate <= 10000000) { + searchTime = 1500; /* 1500 ms max time to lock UWP and CSM, 5Mbs <SYMB <= 10Mbs */ + FecLockTime = 80; /* 80 ms max time to lock FEC, 5Mbs< SYMB <= 10Mbs */ + } else if (internal->srate <= 15000000) { + searchTime = 500; /* 500 ms max time to lock UWP and CSM, 10Mbs <SYMB <= 15Mbs */ + FecLockTime = 50; /* 50 ms max time to lock FEC, 10Mbs< SYMB <= 15Mbs */ + } else if (internal->srate <= 20000000) { + searchTime = 300; /* 300 ms max time to lock UWP and CSM, 15Mbs < SYMB <= 20Mbs */ + FecLockTime = 30; /* 50 ms max time to lock FEC, 15Mbs< SYMB <= 20Mbs */ + } else if (internal->srate <= 25000000) { + searchTime = 250; /* 250 ms max time to lock UWP and CSM, 20 Mbs < SYMB <= 25Mbs */ + FecLockTime = 25; /* 25 ms max time to lock FEC, 20Mbs< SYMB <= 25Mbs */ + } else { + searchTime = 150; /* 150 ms max time to lock UWP and CSM, SYMB > 25Mbs */ + FecLockTime = 20; /* 20 ms max time to lock FEC, 20Mbs< SYMB <= 25Mbs */ + } + + /* Maintain Stream Merger in reset during acquisition */ + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESRS, reg, 1); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + /* enable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 1); + + /* Move tuner to frequency */ + if (state->config->tuner_set_frequency) + state->config->tuner_set_frequency(&state->frontend, internal->freq); + if (state->config->tuner_get_frequency) + state->config->tuner_get_frequency(&state->frontend, &internal->freq); + + /* disable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 0); + + /* Set IF AGC to acquisition */ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); + STB0899_SETFIELD_VAL(IF_LOOP_GAIN, reg, 4); + STB0899_SETFIELD_VAL(IF_AGC_REF, reg, 32); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL2); + STB0899_SETFIELD_VAL(IF_AGC_DUMP_PER, reg, 0); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL2, STB0899_OFF0_IF_AGC_CNTRL2, reg); + + /* Initialisation */ + stb0899_dvbs2_init_calc(state); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); + switch (internal->inversion) { + case IQ_SWAP_OFF: + STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 0); + break; + case IQ_SWAP_ON: + STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 1); + break; + case IQ_SWAP_AUTO: /* use last successful search first */ + STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 1); + break; + } + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg); + stb0899_dvbs2_reacquire(state); + + /* Wait for demod lock (UWP and CSM) */ + internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime); + + if (internal->status == DVBS2_DEMOD_LOCK) { + dprintk(state->verbose, FE_DEBUG, 1, "------------> DVB-S2 DEMOD LOCK !"); + i = 0; + /* Demod Locked, check FEC status */ + internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); + + /*If false lock (UWP and CSM Locked but no FEC) try 3 time max*/ + while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { + /* Read the frequency offset*/ + offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); + + /* Set the Nominal frequency to the found frequency offset for the next reacquire*/ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); + STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); + stb0899_dvbs2_reacquire(state); + internal->status = stb0899_dvbs2_get_fec_status(state, searchTime); + i++; + } + } + + if (internal->status != DVBS2_FEC_LOCK) { + if (internal->inversion == IQ_SWAP_AUTO) { + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); + iqSpectrum = STB0899_GETFIELD(SPECTRUM_INVERT, reg); + /* IQ Spectrum Inversion */ + STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, !iqSpectrum); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg); + /* start acquistion process */ + stb0899_dvbs2_reacquire(state); + + /* Wait for demod lock (UWP and CSM) */ + internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime); + if (internal->status == DVBS2_DEMOD_LOCK) { + i = 0; + /* Demod Locked, check FEC */ + internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); + /*try thrice for false locks, (UWP and CSM Locked but no FEC) */ + while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { + /* Read the frequency offset*/ + offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); + + /* Set the Nominal frequency to the found frequency offset for the next reacquire*/ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); + STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); + + stb0899_dvbs2_reacquire(state); + internal->status = stb0899_dvbs2_get_fec_status(state, searchTime); + i++; + } + } +/* + if (pParams->DVBS2State == FE_DVBS2_FEC_LOCKED) + pParams->IQLocked = !iqSpectrum; +*/ + } + } + if (internal->status == DVBS2_FEC_LOCK) { + dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 FEC Lock !"); + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); + modcod = STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 2; + pilots = STB0899_GETFIELD(UWP_DECODE_MOD, reg) & 0x01; + + if ((((10 * internal->master_clk) / (internal->srate / 10)) <= 410) && + (INRANGE(STB0899_QPSK_23, modcod, STB0899_QPSK_910)) && + (pilots == 1)) { + + stb0899_dvbs2_init_csm(state, pilots, modcod); + /* Wait for UWP,CSM and data LOCK 20ms max */ + internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); + + i = 0; + while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { + csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); + STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, 1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); + csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); + STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, 0); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); + + internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); + i++; + } + } + + if ((((10 * internal->master_clk) / (internal->srate / 10)) <= 410) && + (INRANGE(STB0899_QPSK_12, modcod, STB0899_QPSK_35)) && + (pilots == 1)) { + + /* Equalizer Disable update */ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); + STB0899_SETFIELD_VAL(EQ_DISABLE_UPDATE, reg, 1); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); + } + + /* slow down the Equalizer once locked */ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); + STB0899_SETFIELD_VAL(EQ_SHIFT, reg, 0x02); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); + + /* Store signal parameters */ + offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); + + offsetfreq = offsetfreq / ((1 << 30) / 1000); + offsetfreq *= (internal->master_clk / 1000000); + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); + if (STB0899_GETFIELD(SPECTRUM_INVERT, reg)) + offsetfreq *= -1; + + internal->freq = internal->freq - offsetfreq; + internal->srate = stb0899_dvbs2_get_srate(state); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); + internal->modcod = STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 2; + internal->pilots = STB0899_GETFIELD(UWP_DECODE_MOD, reg) & 0x01; + internal->frame_length = (STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 1) & 0x01; + + /* Set IF AGC to tracking */ + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); + STB0899_SETFIELD_VAL(IF_LOOP_GAIN, reg, 3); + + /* if QPSK 1/2,QPSK 3/5 or QPSK 2/3 set IF AGC reference to 16 otherwise 32*/ + if (INRANGE(STB0899_QPSK_12, internal->modcod, STB0899_QPSK_23)) + STB0899_SETFIELD_VAL(IF_AGC_REF, reg, 16); + + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL2); + STB0899_SETFIELD_VAL(IF_AGC_DUMP_PER, reg, 7); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL2, STB0899_OFF0_IF_AGC_CNTRL2, reg); + } + + /* Release Stream Merger Reset */ + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESRS, reg, 0); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + return internal->status; +} diff --git a/drivers/media/dvb/frontends/stb0899_cfg.h b/drivers/media/dvb/frontends/stb0899_cfg.h new file mode 100644 index 0000000..0867906 --- /dev/null +++ b/drivers/media/dvb/frontends/stb0899_cfg.h @@ -0,0 +1,287 @@ +/* + STB0899 Multistandard Frontend driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + + 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 __STB0899_CFG_H +#define __STB0899_CFG_H + +static const struct stb0899_s2_reg stb0899_s2_init_2[] = { + + { STB0899_OFF0_DMD_STATUS , STB0899_BASE_DMD_STATUS , 0x00000103 }, /* DMDSTATUS */ + { STB0899_OFF0_CRL_FREQ , STB0899_BASE_CRL_FREQ , 0x3ed1da56 }, /* CRLFREQ */ + { STB0899_OFF0_BTR_FREQ , STB0899_BASE_BTR_FREQ , 0x00004000 }, /* BTRFREQ */ + { STB0899_OFF0_IF_AGC_GAIN , STB0899_BASE_IF_AGC_GAIN , 0x00002ade }, /* IFAGCGAIN */ + { STB0899_OFF0_BB_AGC_GAIN , STB0899_BASE_BB_AGC_GAIN , 0x000001bc }, /* BBAGCGAIN */ + { STB0899_OFF0_DC_OFFSET , STB0899_BASE_DC_OFFSET , 0x00000200 }, /* DCOFFSET */ + { STB0899_OFF0_DMD_CNTRL , STB0899_BASE_DMD_CNTRL , 0x0000000f }, /* DMDCNTRL */ + + { STB0899_OFF0_IF_AGC_CNTRL , STB0899_BASE_IF_AGC_CNTRL , 0x03fb4a20 }, /* IFAGCCNTRL */ + { STB0899_OFF0_BB_AGC_CNTRL , STB0899_BASE_BB_AGC_CNTRL , 0x00200c97 }, /* BBAGCCNTRL */ + + { STB0899_OFF0_CRL_CNTRL , STB0899_BASE_CRL_CNTRL , 0x00000016 }, /* CRLCNTRL */ + { STB0899_OFF0_CRL_PHS_INIT , STB0899_BASE_CRL_PHS_INIT , 0x00000000 }, /* CRLPHSINIT */ + { STB0899_OFF0_CRL_FREQ_INIT , STB0899_BASE_CRL_FREQ_INIT , 0x00000000 }, /* CRLFREQINIT */ + { STB0899_OFF0_CRL_LOOP_GAIN , STB0899_BASE_CRL_LOOP_GAIN , 0x00000000 }, /* CRLLOOPGAIN */ + { STB0899_OFF0_CRL_NOM_FREQ , STB0899_BASE_CRL_NOM_FREQ , 0x3ed097b6 }, /* CRLNOMFREQ */ + { STB0899_OFF0_CRL_SWP_RATE , STB0899_BASE_CRL_SWP_RATE , 0x00000000 }, /* CRLSWPRATE */ + { STB0899_OFF0_CRL_MAX_SWP , STB0899_BASE_CRL_MAX_SWP , 0x00000000 }, /* CRLMAXSWP */ + { STB0899_OFF0_CRL_LK_CNTRL , STB0899_BASE_CRL_LK_CNTRL , 0x0f6cdc01 }, /* CRLLKCNTRL */ + { STB0899_OFF0_DECIM_CNTRL , STB0899_BASE_DECIM_CNTRL , 0x00000000 }, /* DECIMCNTRL */ + { STB0899_OFF0_BTR_CNTRL , STB0899_BASE_BTR_CNTRL , 0x00003993 }, /* BTRCNTRL */ + { STB0899_OFF0_BTR_LOOP_GAIN , STB0899_BASE_BTR_LOOP_GAIN , 0x000d3c6f }, /* BTRLOOPGAIN */ + { STB0899_OFF0_BTR_PHS_INIT , STB0899_BASE_BTR_PHS_INIT , 0x00000000 }, /* BTRPHSINIT */ + { STB0899_OFF0_BTR_FREQ_INIT , STB0899_BASE_BTR_FREQ_INIT , 0x00000000 }, /* BTRFREQINIT */ + { STB0899_OFF0_BTR_NOM_FREQ , STB0899_BASE_BTR_NOM_FREQ , 0x0238e38e }, /* BTRNOMFREQ */ + { STB0899_OFF0_BTR_LK_CNTRL , STB0899_BASE_BTR_LK_CNTRL , 0x00000000 }, /* BTRLKCNTRL */ + { STB0899_OFF0_DECN_CNTRL , STB0899_BASE_DECN_CNTRL , 0x00000000 }, /* DECNCNTRL */ + { STB0899_OFF0_TP_CNTRL , STB0899_BASE_TP_CNTRL , 0x00000000 }, /* TPCNTRL */ + { STB0899_OFF0_TP_BUF_STATUS , STB0899_BASE_TP_BUF_STATUS , 0x00000000 }, /* TPBUFSTATUS */ + { STB0899_OFF0_DC_ESTIM , STB0899_BASE_DC_ESTIM , 0x00000000 }, /* DCESTIM */ + { STB0899_OFF0_FLL_CNTRL , STB0899_BASE_FLL_CNTRL , 0x00000000 }, /* FLLCNTRL */ + { STB0899_OFF0_FLL_FREQ_WD , STB0899_BASE_FLL_FREQ_WD , 0x40070000 }, /* FLLFREQWD */ + { STB0899_OFF0_ANTI_ALIAS_SEL , STB0899_BASE_ANTI_ALIAS_SEL , 0x00000001 }, /* ANTIALIASSEL */ + { STB0899_OFF0_RRC_ALPHA , STB0899_BASE_RRC_ALPHA , 0x00000002 }, /* RRCALPHA */ + { STB0899_OFF0_DC_ADAPT_LSHFT , STB0899_BASE_DC_ADAPT_LSHFT , 0x00000000 }, /* DCADAPTISHFT */ + { STB0899_OFF0_IMB_OFFSET , STB0899_BASE_IMB_OFFSET , 0x0000fe01 }, /* IMBOFFSET */ + { STB0899_OFF0_IMB_ESTIMATE , STB0899_BASE_IMB_ESTIMATE , 0x00000000 }, /* IMBESTIMATE */ + { STB0899_OFF0_IMB_CNTRL , STB0899_BASE_IMB_CNTRL , 0x00000001 }, /* IMBCNTRL */ + { STB0899_OFF0_IF_AGC_CNTRL2 , STB0899_BASE_IF_AGC_CNTRL2 , 0x00005007 }, /* IFAGCCNTRL2 */ + { STB0899_OFF0_DMD_CNTRL2 , STB0899_BASE_DMD_CNTRL2 , 0x00000002 }, /* DMDCNTRL2 */ + { STB0899_OFF0_TP_BUFFER , STB0899_BASE_TP_BUFFER , 0x00000000 }, /* TPBUFFER */ + { STB0899_OFF0_TP_BUFFER1 , STB0899_BASE_TP_BUFFER1 , 0x00000000 }, /* TPBUFFER1 */ + { STB0899_OFF0_TP_BUFFER2 , STB0899_BASE_TP_BUFFER2 , 0x00000000 }, /* TPBUFFER2 */ + { STB0899_OFF0_TP_BUFFER3 , STB0899_BASE_TP_BUFFER3 , 0x00000000 }, /* TPBUFFER3 */ + { STB0899_OFF0_TP_BUFFER4 , STB0899_BASE_TP_BUFFER4 , 0x00000000 }, /* TPBUFFER4 */ + { STB0899_OFF0_TP_BUFFER5 , STB0899_BASE_TP_BUFFER5 , 0x00000000 }, /* TPBUFFER5 */ + { STB0899_OFF0_TP_BUFFER6 , STB0899_BASE_TP_BUFFER6 , 0x00000000 }, /* TPBUFFER6 */ + { STB0899_OFF0_TP_BUFFER7 , STB0899_BASE_TP_BUFFER7 , 0x00000000 }, /* TPBUFFER7 */ + { STB0899_OFF0_TP_BUFFER8 , STB0899_BASE_TP_BUFFER8 , 0x00000000 }, /* TPBUFFER8 */ + { STB0899_OFF0_TP_BUFFER9 , STB0899_BASE_TP_BUFFER9 , 0x00000000 }, /* TPBUFFER9 */ + { STB0899_OFF0_TP_BUFFER10 , STB0899_BASE_TP_BUFFER10 , 0x00000000 }, /* TPBUFFER10 */ + { STB0899_OFF0_TP_BUFFER11 , STB0899_BASE_TP_BUFFER11 , 0x00000000 }, /* TPBUFFER11 */ + { STB0899_OFF0_TP_BUFFER12 , STB0899_BASE_TP_BUFFER12 , 0x00000000 }, /* TPBUFFER12 */ + { STB0899_OFF0_TP_BUFFER13 , STB0899_BASE_TP_BUFFER13 , 0x00000000 }, /* TPBUFFER13 */ + { STB0899_OFF0_TP_BUFFER14 , STB0899_BASE_TP_BUFFER14 , 0x00000000 }, /* TPBUFFER14 */ + { STB0899_OFF0_TP_BUFFER15 , STB0899_BASE_TP_BUFFER15 , 0x00000000 }, /* TPBUFFER15 */ + { STB0899_OFF0_TP_BUFFER16 , STB0899_BASE_TP_BUFFER16 , 0x0000ff00 }, /* TPBUFFER16 */ + { STB0899_OFF0_TP_BUFFER17 , STB0899_BASE_TP_BUFFER17 , 0x00000100 }, /* TPBUFFER17 */ + { STB0899_OFF0_TP_BUFFER18 , STB0899_BASE_TP_BUFFER18 , 0x0000fe01 }, /* TPBUFFER18 */ + { STB0899_OFF0_TP_BUFFER19 , STB0899_BASE_TP_BUFFER19 , 0x000004fe }, /* TPBUFFER19 */ + { STB0899_OFF0_TP_BUFFER20 , STB0899_BASE_TP_BUFFER20 , 0x0000cfe7 }, /* TPBUFFER20 */ + { STB0899_OFF0_TP_BUFFER21 , STB0899_BASE_TP_BUFFER21 , 0x0000bec6 }, /* TPBUFFER21 */ + { STB0899_OFF0_TP_BUFFER22 , STB0899_BASE_TP_BUFFER22 , 0x0000c2bf }, /* TPBUFFER22 */ + { STB0899_OFF0_TP_BUFFER23 , STB0899_BASE_TP_BUFFER23 , 0x0000c1c1 }, /* TPBUFFER23 */ + { STB0899_OFF0_TP_BUFFER24 , STB0899_BASE_TP_BUFFER24 , 0x0000c1c1 }, /* TPBUFFER24 */ + { STB0899_OFF0_TP_BUFFER25 , STB0899_BASE_TP_BUFFER25 , 0x0000c1c1 }, /* TPBUFFER25 */ + { STB0899_OFF0_TP_BUFFER26 , STB0899_BASE_TP_BUFFER26 , 0x0000c1c1 }, /* TPBUFFER26 */ + { STB0899_OFF0_TP_BUFFER27 , STB0899_BASE_TP_BUFFER27 , 0x0000c1c0 }, /* TPBUFFER27 */ + { STB0899_OFF0_TP_BUFFER28 , STB0899_BASE_TP_BUFFER28 , 0x0000c0c0 }, /* TPBUFFER28 */ + { STB0899_OFF0_TP_BUFFER29 , STB0899_BASE_TP_BUFFER29 , 0x0000c1c1 }, /* TPBUFFER29 */ + { STB0899_OFF0_TP_BUFFER30 , STB0899_BASE_TP_BUFFER30 , 0x0000c1c1 }, /* TPBUFFER30 */ + { STB0899_OFF0_TP_BUFFER31 , STB0899_BASE_TP_BUFFER31 , 0x0000c0c1 }, /* TPBUFFER31 */ + { STB0899_OFF0_TP_BUFFER32 , STB0899_BASE_TP_BUFFER32 , 0x0000c0c1 }, /* TPBUFFER32 */ + { STB0899_OFF0_TP_BUFFER33 , STB0899_BASE_TP_BUFFER33 , 0x0000c1c1 }, /* TPBUFFER33 */ + { STB0899_OFF0_TP_BUFFER34 , STB0899_BASE_TP_BUFFER34 , 0x0000c1c1 }, /* TPBUFFER34 */ + { STB0899_OFF0_TP_BUFFER35 , STB0899_BASE_TP_BUFFER35 , 0x0000c0c1 }, /* TPBUFFER35 */ + { STB0899_OFF0_TP_BUFFER36 , STB0899_BASE_TP_BUFFER36 , 0x0000c1c1 }, /* TPBUFFER36 */ + { STB0899_OFF0_TP_BUFFER37 , STB0899_BASE_TP_BUFFER37 , 0x0000c0c1 }, /* TPBUFFER37 */ + { STB0899_OFF0_TP_BUFFER38 , STB0899_BASE_TP_BUFFER38 , 0x0000c1c1 }, /* TPBUFFER38 */ + { STB0899_OFF0_TP_BUFFER39 , STB0899_BASE_TP_BUFFER39 , 0x0000c0c0 }, /* TPBUFFER39 */ + { STB0899_OFF0_TP_BUFFER40 , STB0899_BASE_TP_BUFFER40 , 0x0000c1c0 }, /* TPBUFFER40 */ + { STB0899_OFF0_TP_BUFFER41 , STB0899_BASE_TP_BUFFER41 , 0x0000c1c1 }, /* TPBUFFER41 */ + { STB0899_OFF0_TP_BUFFER42 , STB0899_BASE_TP_BUFFER42 , 0x0000c0c0 }, /* TPBUFFER42 */ + { STB0899_OFF0_TP_BUFFER43 , STB0899_BASE_TP_BUFFER43 , 0x0000c1c0 }, /* TPBUFFER43 */ + { STB0899_OFF0_TP_BUFFER44 , STB0899_BASE_TP_BUFFER44 , 0x0000c0c1 }, /* TPBUFFER44 */ + { STB0899_OFF0_TP_BUFFER45 , STB0899_BASE_TP_BUFFER45 , 0x0000c1be }, /* TPBUFFER45 */ + { STB0899_OFF0_TP_BUFFER46 , STB0899_BASE_TP_BUFFER46 , 0x0000c1c9 }, /* TPBUFFER46 */ + { STB0899_OFF0_TP_BUFFER47 , STB0899_BASE_TP_BUFFER47 , 0x0000c0da }, /* TPBUFFER47 */ + { STB0899_OFF0_TP_BUFFER48 , STB0899_BASE_TP_BUFFER48 , 0x0000c0ba }, /* TPBUFFER48 */ + { STB0899_OFF0_TP_BUFFER49 , STB0899_BASE_TP_BUFFER49 , 0x0000c1c4 }, /* TPBUFFER49 */ + { STB0899_OFF0_TP_BUFFER50 , STB0899_BASE_TP_BUFFER50 , 0x0000c1bf }, /* TPBUFFER50 */ + { STB0899_OFF0_TP_BUFFER51 , STB0899_BASE_TP_BUFFER51 , 0x0000c0c1 }, /* TPBUFFER51 */ + { STB0899_OFF0_TP_BUFFER52 , STB0899_BASE_TP_BUFFER52 , 0x0000c1c0 }, /* TPBUFFER52 */ + { STB0899_OFF0_TP_BUFFER53 , STB0899_BASE_TP_BUFFER53 , 0x0000c0c1 }, /* TPBUFFER53 */ + { STB0899_OFF0_TP_BUFFER54 , STB0899_BASE_TP_BUFFER54 , 0x0000c1c1 }, /* TPBUFFER54 */ + { STB0899_OFF0_TP_BUFFER55 , STB0899_BASE_TP_BUFFER55 , 0x0000c1c1 }, /* TPBUFFER55 */ + { STB0899_OFF0_TP_BUFFER56 , STB0899_BASE_TP_BUFFER56 , 0x0000c1c1 }, /* TPBUFFER56 */ + { STB0899_OFF0_TP_BUFFER57 , STB0899_BASE_TP_BUFFER57 , 0x0000c1c1 }, /* TPBUFFER57 */ + { STB0899_OFF0_TP_BUFFER58 , STB0899_BASE_TP_BUFFER58 , 0x0000c1c1 }, /* TPBUFFER58 */ + { STB0899_OFF0_TP_BUFFER59 , STB0899_BASE_TP_BUFFER59 , 0x0000c1c1 }, /* TPBUFFER59 */ + { STB0899_OFF0_TP_BUFFER60 , STB0899_BASE_TP_BUFFER60 , 0x0000c1c1 }, /* TPBUFFER60 */ + { STB0899_OFF0_TP_BUFFER61 , STB0899_BASE_TP_BUFFER61 , 0x0000c1c1 }, /* TPBUFFER61 */ + { STB0899_OFF0_TP_BUFFER62 , STB0899_BASE_TP_BUFFER62 , 0x0000c1c1 }, /* TPBUFFER62 */ + { STB0899_OFF0_TP_BUFFER63 , STB0899_BASE_TP_BUFFER63 , 0x0000c1c0 }, /* TPBUFFER63 */ + { STB0899_OFF0_RESET_CNTRL , STB0899_BASE_RESET_CNTRL , 0x00000001 }, /* RESETCNTRL */ + { STB0899_OFF0_ACM_ENABLE , STB0899_BASE_ACM_ENABLE , 0x00005654 }, /* ACMENABLE */ + { STB0899_OFF0_DESCR_CNTRL , STB0899_BASE_DESCR_CNTRL , 0x00000000 }, /* DESCRCNTRL */ + { STB0899_OFF0_CSM_CNTRL1 , STB0899_BASE_CSM_CNTRL1 , 0x00020019 }, /* CSMCNTRL1 */ + { STB0899_OFF0_CSM_CNTRL2 , STB0899_BASE_CSM_CNTRL2 , 0x004b3237 }, /* CSMCNTRL2 */ + { STB0899_OFF0_CSM_CNTRL3 , STB0899_BASE_CSM_CNTRL3 , 0x0003dd17 }, /* CSMCNTRL3 */ + { STB0899_OFF0_CSM_CNTRL4 , STB0899_BASE_CSM_CNTRL4 , 0x00008008 }, /* CSMCNTRL4 */ + { STB0899_OFF0_UWP_CNTRL1 , STB0899_BASE_UWP_CNTRL1 , 0x002a3106 }, /* UWPCNTRL1 */ + { STB0899_OFF0_UWP_CNTRL2 , STB0899_BASE_UWP_CNTRL2 , 0x0006140a }, /* UWPCNTRL2 */ + { STB0899_OFF0_UWP_STAT1 , STB0899_BASE_UWP_STAT1 , 0x00008000 }, /* UWPSTAT1 */ + { STB0899_OFF0_UWP_STAT2 , STB0899_BASE_UWP_STAT2 , 0x00000000 }, /* UWPSTAT2 */ + { STB0899_OFF0_DMD_STAT2 , STB0899_BASE_DMD_STAT2 , 0x00000000 }, /* DMDSTAT2 */ + { STB0899_OFF0_FREQ_ADJ_SCALE , STB0899_BASE_FREQ_ADJ_SCALE , 0x00000471 }, /* FREQADJSCALE */ + { STB0899_OFF0_UWP_CNTRL3 , STB0899_BASE_UWP_CNTRL3 , 0x017b0465 }, /* UWPCNTRL3 */ + { STB0899_OFF0_SYM_CLK_SEL , STB0899_BASE_SYM_CLK_SEL , 0x00000002 }, /* SYMCLKSEL */ + { STB0899_OFF0_SOF_SRCH_TO , STB0899_BASE_SOF_SRCH_TO , 0x00196464 }, /* SOFSRCHTO */ + { STB0899_OFF0_ACQ_CNTRL1 , STB0899_BASE_ACQ_CNTRL1 , 0x00000603 }, /* ACQCNTRL1 */ + { STB0899_OFF0_ACQ_CNTRL2 , STB0899_BASE_ACQ_CNTRL2 , 0x02046666 }, /* ACQCNTRL2 */ + { STB0899_OFF0_ACQ_CNTRL3 , STB0899_BASE_ACQ_CNTRL3 , 0x10046583 }, /* ACQCNTRL3 */ + { STB0899_OFF0_FE_SETTLE , STB0899_BASE_FE_SETTLE , 0x00010404 }, /* FESETTLE */ + { STB0899_OFF0_AC_DWELL , STB0899_BASE_AC_DWELL , 0x0002aa8a }, /* ACDWELL */ + { STB0899_OFF0_ACQUIRE_TRIG , STB0899_BASE_ACQUIRE_TRIG , 0x00000000 }, /* ACQUIRETRIG */ + { STB0899_OFF0_LOCK_LOST , STB0899_BASE_LOCK_LOST , 0x00000001 }, /* LOCKLOST */ + { STB0899_OFF0_ACQ_STAT1 , STB0899_BASE_ACQ_STAT1 , 0x00000500 }, /* ACQSTAT1 */ + { STB0899_OFF0_ACQ_TIMEOUT , STB0899_BASE_ACQ_TIMEOUT , 0x0028a0a0 }, /* ACQTIMEOUT */ + { STB0899_OFF0_ACQ_TIME , STB0899_BASE_ACQ_TIME , 0x00000000 }, /* ACQTIME */ + { STB0899_OFF0_FINAL_AGC_CNTRL , STB0899_BASE_FINAL_AGC_CNTRL , 0x00800c17 }, /* FINALAGCCNTRL*/ + { STB0899_OFF0_FINAL_AGC_GAIN , STB0899_BASE_FINAL_AGC_GAIN , 0x00000000 }, /* FINALAGCCGAIN*/ + { STB0899_OFF0_EQUALIZER_INIT , STB0899_BASE_EQUALIZER_INIT , 0x00000000 }, /* EQUILIZERINIT*/ + { STB0899_OFF0_EQ_CNTRL , STB0899_BASE_EQ_CNTRL , 0x00054802 }, /* EQCNTL */ + { STB0899_OFF0_EQ_I_INIT_COEFF_0, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF0 */ + { STB0899_OFF1_EQ_I_INIT_COEFF_1, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF1 */ + { STB0899_OFF2_EQ_I_INIT_COEFF_2, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF2 */ + { STB0899_OFF3_EQ_I_INIT_COEFF_3, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF3 */ + { STB0899_OFF4_EQ_I_INIT_COEFF_4, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF4 */ + { STB0899_OFF5_EQ_I_INIT_COEFF_5, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000400 }, /* EQIINITCOEFF5 */ + { STB0899_OFF6_EQ_I_INIT_COEFF_6, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF6 */ + { STB0899_OFF7_EQ_I_INIT_COEFF_7, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF7 */ + { STB0899_OFF8_EQ_I_INIT_COEFF_8, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF8 */ + { STB0899_OFF9_EQ_I_INIT_COEFF_9, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF9 */ + { STB0899_OFFa_EQ_I_INIT_COEFF_10,STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF10*/ + { STB0899_OFF0_EQ_Q_INIT_COEFF_0, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF0 */ + { STB0899_OFF1_EQ_Q_INIT_COEFF_1, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF1 */ + { STB0899_OFF2_EQ_Q_INIT_COEFF_2, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF2 */ + { STB0899_OFF3_EQ_Q_INIT_COEFF_3, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF3 */ + { STB0899_OFF4_EQ_Q_INIT_COEFF_4, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF4 */ + { STB0899_OFF5_EQ_Q_INIT_COEFF_5, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF5 */ + { STB0899_OFF6_EQ_Q_INIT_COEFF_6, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF6 */ + { STB0899_OFF7_EQ_Q_INIT_COEFF_7, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF7 */ + { STB0899_OFF8_EQ_Q_INIT_COEFF_8, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF8 */ + { STB0899_OFF9_EQ_Q_INIT_COEFF_9, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF9 */ + { STB0899_OFFa_EQ_Q_INIT_COEFF_10,STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF10*/ + { STB0899_OFF0_EQ_I_OUT_COEFF_0 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT0 */ + { STB0899_OFF1_EQ_I_OUT_COEFF_1 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT1 */ + { STB0899_OFF2_EQ_I_OUT_COEFF_2 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT2 */ + { STB0899_OFF3_EQ_I_OUT_COEFF_3 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT3 */ + { STB0899_OFF4_EQ_I_OUT_COEFF_4 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT4 */ + { STB0899_OFF5_EQ_I_OUT_COEFF_5 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT5 */ + { STB0899_OFF6_EQ_I_OUT_COEFF_6 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT6 */ + { STB0899_OFF7_EQ_I_OUT_COEFF_7 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT7 */ + { STB0899_OFF8_EQ_I_OUT_COEFF_8 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT8 */ + { STB0899_OFF9_EQ_I_OUT_COEFF_9 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT9 */ + { STB0899_OFFa_EQ_I_OUT_COEFF_10,STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT10*/ + { STB0899_OFF0_EQ_Q_OUT_COEFF_0 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT0 */ + { STB0899_OFF1_EQ_Q_OUT_COEFF_1 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT1 */ + { STB0899_OFF2_EQ_Q_OUT_COEFF_2 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT2 */ + { STB0899_OFF3_EQ_Q_OUT_COEFF_3 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT3 */ + { STB0899_OFF4_EQ_Q_OUT_COEFF_4 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT4 */ + { STB0899_OFF5_EQ_Q_OUT_COEFF_5 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT5 */ + { STB0899_OFF6_EQ_Q_OUT_COEFF_6 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT6 */ + { STB0899_OFF7_EQ_Q_OUT_COEFF_7 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT7 */ + { STB0899_OFF8_EQ_Q_OUT_COEFF_8 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT8 */ + { STB0899_OFF9_EQ_Q_OUT_COEFF_9 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT9 */ + { STB0899_OFFa_EQ_Q_OUT_COEFF_10, STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT10*/ + { 0xffff , 0xffffffff , 0xffffffff }, +}; +static const struct stb0899_s2_reg stb0899_s2_init_4[] = { + { STB0899_OFF0_BLOCK_LNGTH , STB0899_BASE_BLOCK_LNGTH , 0x00000008 }, /* BLOCKLNGTH */ + { STB0899_OFF0_ROW_STR , STB0899_BASE_ROW_STR , 0x000000b4 }, /* ROWSTR */ + { STB0899_OFF0_BN_END_ADDR , STB0899_BASE_BN_END_ADDR , 0x000004b5 }, /* BNANDADDR */ + { STB0899_OFF0_CN_END_ADDR , STB0899_BASE_CN_END_ADDR , 0x00000b4b }, /* CNANDADDR */ + { STB0899_OFF0_INFO_LENGTH , STB0899_BASE_INFO_LENGTH , 0x00000078 }, /* INFOLENGTH */ + { STB0899_OFF0_BOT_ADDR , STB0899_BASE_BOT_ADDR , 0x000001e0 }, /* BOT_ADDR */ + { STB0899_OFF0_BCH_BLK_LN , STB0899_BASE_BCH_BLK_LN , 0x0000a8c0 }, /* BCHBLKLN */ + { STB0899_OFF0_BCH_T , STB0899_BASE_BCH_T , 0x0000000c }, /* BCHT */ + { STB0899_OFF0_CNFG_MODE , STB0899_BASE_CNFG_MODE , 0x00000001 }, /* CNFGMODE */ + { STB0899_OFF0_LDPC_STAT , STB0899_BASE_LDPC_STAT , 0x0000000d }, /* LDPCSTAT */ + { STB0899_OFF0_ITER_SCALE , STB0899_BASE_ITER_SCALE , 0x00000040 }, /* ITERSCALE */ + { STB0899_OFF0_INPUT_MODE , STB0899_BASE_INPUT_MODE , 0x00000000 }, /* INPUTMODE */ + { STB0899_OFF0_LDPCDECRST , STB0899_BASE_LDPCDECRST , 0x00000000 }, /* LDPCDECRST */ + { STB0899_OFF0_CLK_PER_BYTE_RW , STB0899_BASE_CLK_PER_BYTE_RW , 0x00000008 }, /* CLKPERBYTE */ + { STB0899_OFF0_BCH_ERRORS , STB0899_BASE_BCH_ERRORS , 0x00000000 }, /* BCHERRORS */ + { STB0899_OFF0_LDPC_ERRORS , STB0899_BASE_LDPC_ERRORS , 0x00000000 }, /* LDPCERRORS */ + { STB0899_OFF0_BCH_MODE , STB0899_BASE_BCH_MODE , 0x00000000 }, /* BCHMODE */ + { STB0899_OFF0_ERR_ACC_PER , STB0899_BASE_ERR_ACC_PER , 0x00000008 }, /* ERRACCPER */ + { STB0899_OFF0_BCH_ERR_ACC , STB0899_BASE_BCH_ERR_ACC , 0x00000000 }, /* BCHERRACC */ + { STB0899_OFF0_FEC_TP_SEL , STB0899_BASE_FEC_TP_SEL , 0x00000000 }, /* FECTPSEL */ + { 0xffff , 0xffffffff , 0xffffffff }, +}; + +static const struct stb0899_s1_reg stb0899_s1_init_5[] = { + { STB0899_TSTCK , 0x00 }, + { STB0899_TSTRES , 0x00 }, + { STB0899_TSTOUT , 0x00 }, + { STB0899_TSTIN , 0x00 }, + { STB0899_TSTSYS , 0x00 }, + { STB0899_TSTCHIP , 0x00 }, + { STB0899_TSTFREE , 0x00 }, + { STB0899_TSTI2C , 0x00 }, + { STB0899_BITSPEEDM , 0x00 }, + { STB0899_BITSPEEDL , 0x00 }, + { STB0899_TBUSBIT , 0x00 }, + { STB0899_TSTDIS , 0x00 }, + { STB0899_TSTDISRX , 0x00 }, + { STB0899_TSTJETON , 0x00 }, + { STB0899_TSTDCADJ , 0x00 }, + { STB0899_TSTAGC1 , 0x00 }, + { STB0899_TSTAGC1N , 0x00 }, + { STB0899_TSTPOLYPH , 0x00 }, + { STB0899_TSTR , 0x00 }, + { STB0899_TSTAGC2 , 0x00 }, + { STB0899_TSTCTL1 , 0x00 }, + { STB0899_TSTCTL2 , 0x00 }, + { STB0899_TSTCTL3 , 0x00 }, + { STB0899_TSTDEMAP , 0x00 }, + { STB0899_TSTDEMAP2 , 0x00 }, + { STB0899_TSTDEMMON , 0x00 }, + { STB0899_TSTRATE , 0x00 }, + { STB0899_TSTSELOUT , 0x00 }, + { STB0899_TSYNC , 0x00 }, + { STB0899_TSTERR , 0x00 }, + { STB0899_TSTRAM1 , 0x00 }, + { STB0899_TSTVSELOUT , 0x00 }, + { STB0899_TSTFORCEIN , 0x00 }, + { STB0899_TSTRS1 , 0x00 }, + { STB0899_TSTRS2 , 0x00 }, + { STB0899_TSTRS3 , 0x00 }, + { STB0899_GHOSTREG , 0x81 }, + { 0xffff , 0xff }, +}; + +#define STB0899_DVBS2_ESNO_AVE 3 +#define STB0899_DVBS2_ESNO_QUANT 32 +#define STB0899_DVBS2_AVFRAMES_COARSE 10 +#define STB0899_DVBS2_AVFRAMES_FINE 20 +#define STB0899_DVBS2_MISS_THRESHOLD 6 +#define STB0899_DVBS2_UWP_THRESHOLD_ACQ 1125 +#define STB0899_DVBS2_UWP_THRESHOLD_TRACK 758 +#define STB0899_DVBS2_UWP_THRESHOLD_SOF 1350 +#define STB0899_DVBS2_SOF_SEARCH_TIMEOUT 1664100 + +#define STB0899_DVBS2_BTR_NCO_BITS 28 +#define STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET 15 +#define STB0899_DVBS2_CRL_NCO_BITS 30 +#define STB0899_DVBS2_LDPC_MAX_ITER 70 + +#endif //__STB0899_CFG_H diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c new file mode 100644 index 0000000..5288201 --- /dev/null +++ b/drivers/media/dvb/frontends/stb0899_drv.c @@ -0,0 +1,1684 @@ +/* + STB0899 Multistandard Frontend driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + + 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/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> + +#include <linux/dvb/frontend.h> +#include "dvb_frontend.h" + +#include "stb0899_drv.h" +#include "stb0899_priv.h" +#include "stb0899_reg.h" + +static unsigned int verbose = 0;//1; +module_param(verbose, int, 0644); + +/* C/N in dB/10, NIRM/NIRL */ +static const struct stb0899_tab stb0899_cn_tab[] = { + { 200, 2600 }, + { 190, 2700 }, + { 180, 2860 }, + { 170, 3020 }, + { 160, 3210 }, + { 150, 3440 }, + { 140, 3710 }, + { 130, 4010 }, + { 120, 4360 }, + { 110, 4740 }, + { 100, 5190 }, + { 90, 5670 }, + { 80, 6200 }, + { 70, 6770 }, + { 60, 7360 }, + { 50, 7970 }, + { 40, 8250 }, + { 30, 9000 }, + { 20, 9450 }, + { 15, 9600 }, +}; + +/* DVB-S AGCIQ_VALUE vs. signal level in dBm/10. + * As measured, connected to a modulator. + * -8.0 to -50.0 dBm directly connected, + * -52.0 to -74.8 with extra attenuation. + * Cut-off to AGCIQ_VALUE = 0x80 below -74.8dBm. + * Crude linear extrapolation below -84.8dBm and above -8.0dBm. + */ +static const struct stb0899_tab stb0899_dvbsrf_tab[] = { + { -950, -128 }, + { -748, -94 }, + { -745, -92 }, + { -735, -90 }, + { -720, -87 }, + { -670, -77 }, + { -640, -70 }, + { -610, -62 }, + { -600, -60 }, + { -590, -56 }, + { -560, -41 }, + { -540, -25 }, + { -530, -17 }, + { -520, -11 }, + { -500, 1 }, + { -490, 6 }, + { -480, 10 }, + { -440, 22 }, + { -420, 27 }, + { -400, 31 }, + { -380, 34 }, + { -340, 40 }, + { -320, 43 }, + { -280, 48 }, + { -250, 52 }, + { -230, 55 }, + { -180, 61 }, + { -140, 66 }, + { -90, 73 }, + { -80, 74 }, + { 500, 127 } +}; + +/* DVB-S2 IF_AGC_GAIN vs. signal level in dBm/10. + * As measured, connected to a modulator. + * -8.0 to -50.1 dBm directly connected, + * -53.0 to -76.6 with extra attenuation. + * Cut-off to IF_AGC_GAIN = 0x3fff below -76.6dBm. + * Crude linear extrapolation below -76.6dBm and above -8.0dBm. + */ +static const struct stb0899_tab stb0899_dvbs2rf_tab[] = { + { 700, 0 }, + { -80, 3217 }, + { -150, 3893 }, + { -190, 4217 }, + { -240, 4621 }, + { -280, 4945 }, + { -320, 5273 }, + { -350, 5545 }, + { -370, 5741 }, + { -410, 6147 }, + { -450, 6671 }, + { -490, 7413 }, + { -501, 7665 }, + { -530, 8767 }, + { -560, 10219 }, + { -580, 10939 }, + { -590, 11518 }, + { -600, 11723 }, + { -650, 12659 }, + { -690, 13219 }, + { -730, 13645 }, + { -750, 13909 }, + { -766, 14153 }, + { -999, 16383 } +}; + +/* DVB-S2 Es/N0 quant in dB/100 vs read value * 100*/ +struct stb0899_tab stb0899_quant_tab[] = { + { 0, 0 }, + { 0, 100 }, + { 600, 200 }, + { 950, 299 }, + { 1200, 398 }, + { 1400, 501 }, + { 1560, 603 }, + { 1690, 700 }, + { 1810, 804 }, + { 1910, 902 }, + { 2000, 1000 }, + { 2080, 1096 }, + { 2160, 1202 }, + { 2230, 1303 }, + { 2350, 1496 }, + { 2410, 1603 }, + { 2460, 1698 }, + { 2510, 1799 }, + { 2600, 1995 }, + { 2650, 2113 }, + { 2690, 2213 }, + { 2720, 2291 }, + { 2760, 2399 }, + { 2800, 2512 }, + { 2860, 2692 }, + { 2930, 2917 }, + { 2960, 3020 }, + { 3010, 3199 }, + { 3040, 3311 }, + { 3060, 3388 }, + { 3120, 3631 }, + { 3190, 3936 }, + { 3400, 5012 }, + { 3610, 6383 }, + { 3800, 7943 }, + { 4210, 12735 }, + { 4500, 17783 }, + { 4690, 22131 }, + { 4810, 25410 } +}; + +/* DVB-S2 Es/N0 estimate in dB/100 vs read value */ +struct stb0899_tab stb0899_est_tab[] = { + { 0, 0 }, + { 0, 1 }, + { 301, 2 }, + { 1204, 16 }, + { 1806, 64 }, + { 2408, 256 }, + { 2709, 512 }, + { 3010, 1023 }, + { 3311, 2046 }, + { 3612, 4093 }, + { 3823, 6653 }, + { 3913, 8185 }, + { 4010, 10233 }, + { 4107, 12794 }, + { 4214, 16368 }, + { 4266, 18450 }, + { 4311, 20464 }, + { 4353, 22542 }, + { 4391, 24604 }, + { 4425, 26607 }, + { 4457, 28642 }, + { 4487, 30690 }, + { 4515, 32734 }, + { 4612, 40926 }, + { 4692, 49204 }, + { 4816, 65464 }, + { 4913, 81846 }, + { 4993, 98401 }, + { 5060, 114815 }, + { 5118, 131220 }, + { 5200, 158489 }, + { 5300, 199526 }, + { 5400, 251189 }, + { 5500, 316228 }, + { 5600, 398107 }, + { 5720, 524807 }, + { 5721, 526017 }, +}; + +int _stb0899_read_reg(struct stb0899_state *state, unsigned int reg) +{ + int ret; + + u8 b0[] = { reg >> 8, reg & 0xff }; + u8 buf; + + 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 = &buf, + .len = 1 + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret != 2) { + if (ret != -ERESTARTSYS) + dprintk(state->verbose, FE_ERROR, 1, + "Read error, Reg=[0x%02x], Status=%d", + reg, ret); + + return ret < 0 ? ret : -EREMOTEIO; + } + if (unlikely(*state->verbose >= FE_DEBUGREG)) + dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%02x], data=%02x", + reg, buf); + + return (unsigned int)buf; +} + +int stb0899_read_reg(struct stb0899_state *state, unsigned int reg) +{ + int result; + + result = _stb0899_read_reg(state, reg); + /* + * Bug ID 9: + * access to 0xf2xx/0xf6xx + * must be followed by read from 0xf2ff/0xf6ff. + */ + if ((reg != 0xf2ff) && (reg != 0xf6ff) && + (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) + _stb0899_read_reg(state, (reg | 0x00ff)); + + return result; +} + +u32 _stb0899_read_s2reg(struct stb0899_state *state, + u32 stb0899_i2cdev, + u32 stb0899_base_addr, + u16 stb0899_reg_offset) +{ + int status; + u32 data; + u8 buf[7] = { 0 }; + u16 tmpaddr; + + u8 buf_0[] = { + GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */ + GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */ + GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */ + GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */ + GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */ + GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */ + }; + u8 buf_1[] = { + 0x00, /* 0xf3 Reg Offset */ + 0x00, /* 0x44 Reg Offset */ + }; + + struct i2c_msg msg_0 = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf_0, + .len = 6 + }; + + struct i2c_msg msg_1 = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf_1, + .len = 2 + }; + + struct i2c_msg msg_r = { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = buf, + .len = 4 + }; + + tmpaddr = stb0899_reg_offset & 0xff00; + if (!(stb0899_reg_offset & 0x8)) + tmpaddr = stb0899_reg_offset | 0x20; + + buf_1[0] = GETBYTE(tmpaddr, BYTE1); + buf_1[1] = GETBYTE(tmpaddr, BYTE0); + + status = i2c_transfer(state->i2c, &msg_0, 1); + if (status < 1) { + if (status != -ERESTARTSYS) + printk(KERN_ERR "%s ERR(1), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", + __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); + + goto err; + } + + /* Dummy */ + status = i2c_transfer(state->i2c, &msg_1, 1); + if (status < 1) + goto err; + + status = i2c_transfer(state->i2c, &msg_r, 1); + if (status < 1) + goto err; + + buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1); + buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0); + + /* Actual */ + status = i2c_transfer(state->i2c, &msg_1, 1); + if (status < 1) { + if (status != -ERESTARTSYS) + printk(KERN_ERR "%s ERR(2), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", + __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); + goto err; + } + + status = i2c_transfer(state->i2c, &msg_r, 1); + if (status < 1) { + if (status != -ERESTARTSYS) + printk(KERN_ERR "%s ERR(3), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", + __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); + return status < 0 ? status : -EREMOTEIO; + } + + data = MAKEWORD32(buf[3], buf[2], buf[1], buf[0]); + if (unlikely(*state->verbose >= FE_DEBUGREG)) + printk(KERN_DEBUG "%s Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n", + __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, data); + + return data; + +err: + return status < 0 ? status : -EREMOTEIO; +} + +int stb0899_write_s2reg(struct stb0899_state *state, + u32 stb0899_i2cdev, + u32 stb0899_base_addr, + u16 stb0899_reg_offset, + u32 stb0899_data) +{ + int status; + + /* Base Address Setup */ + u8 buf_0[] = { + GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */ + GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */ + GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */ + GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */ + GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */ + GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */ + }; + u8 buf_1[] = { + 0x00, /* 0xf3 Reg Offset */ + 0x00, /* 0x44 Reg Offset */ + 0x00, /* data */ + 0x00, /* data */ + 0x00, /* data */ + 0x00, /* data */ + }; + + struct i2c_msg msg_0 = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf_0, + .len = 6 + }; + + struct i2c_msg msg_1 = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf_1, + .len = 6 + }; + + buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1); + buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0); + buf_1[2] = GETBYTE(stb0899_data, BYTE0); + buf_1[3] = GETBYTE(stb0899_data, BYTE1); + buf_1[4] = GETBYTE(stb0899_data, BYTE2); + buf_1[5] = GETBYTE(stb0899_data, BYTE3); + + if (unlikely(*state->verbose >= FE_DEBUGREG)) + printk(KERN_DEBUG "%s Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n", + __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data); + + status = i2c_transfer(state->i2c, &msg_0, 1); + if (unlikely(status < 1)) { + if (status != -ERESTARTSYS) + printk(KERN_ERR "%s ERR (1), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n", + __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status); + goto err; + } + status = i2c_transfer(state->i2c, &msg_1, 1); + if (unlikely(status < 1)) { + if (status != -ERESTARTSYS) + printk(KERN_ERR "%s ERR (2), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n", + __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status); + + return status < 0 ? status : -EREMOTEIO; + } + + return 0; + +err: + return status < 0 ? status : -EREMOTEIO; +} + +int stb0899_read_regs(struct stb0899_state *state, unsigned int reg, u8 *buf, u32 count) +{ + int status; + + u8 b0[] = { reg >> 8, reg & 0xff }; + + 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 = buf, + .len = count + } + }; + + status = i2c_transfer(state->i2c, msg, 2); + if (status != 2) { + if (status != -ERESTARTSYS) + printk(KERN_ERR "%s Read error, Reg=[0x%04x], Count=%u, Status=%d\n", + __func__, reg, count, status); + goto err; + } + /* + * Bug ID 9: + * access to 0xf2xx/0xf6xx + * must be followed by read from 0xf2ff/0xf6ff. + */ + if ((reg != 0xf2ff) && (reg != 0xf6ff) && + (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) + _stb0899_read_reg(state, (reg | 0x00ff)); + + if (unlikely(*state->verbose >= FE_DEBUGREG)) { + int i; + + printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg); + for (i = 0; i < count; i++) { + printk(" %02x", buf[i]); + } + printk("\n"); + } + + return 0; +err: + return status < 0 ? status : -EREMOTEIO; +} + +int stb0899_write_regs(struct stb0899_state *state, unsigned int reg, u8 *data, u32 count) +{ + int ret; + u8 buf[2 + count]; + struct i2c_msg i2c_msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf, + .len = 2 + count + }; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + memcpy(&buf[2], data, count); + + if (unlikely(*state->verbose >= FE_DEBUGREG)) { + int i; + + printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg); + for (i = 0; i < count; i++) + printk(" %02x", data[i]); + printk("\n"); + } + ret = i2c_transfer(state->i2c, &i2c_msg, 1); + + /* + * Bug ID 9: + * access to 0xf2xx/0xf6xx + * must be followed by read from 0xf2ff/0xf6ff. + */ + if ((((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) + stb0899_read_reg(state, (reg | 0x00ff)); + + if (ret != 1) { + if (ret != -ERESTARTSYS) + dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%04x], Data=[0x%02x ...], Count=%u, Status=%d", + reg, data[0], count, ret); + return ret < 0 ? ret : -EREMOTEIO; + } + + return 0; +} + +int stb0899_write_reg(struct stb0899_state *state, unsigned int reg, u8 data) +{ + return stb0899_write_regs(state, reg, &data, 1); +} + +/* + * stb0899_get_mclk + * Get STB0899 master clock frequency + * ExtClk: external clock frequency (Hz) + */ +static u32 stb0899_get_mclk(struct stb0899_state *state) +{ + u32 mclk = 0, div = 0; + + div = stb0899_read_reg(state, STB0899_NCOARSE); + mclk = (div + 1) * state->config->xtal_freq / 6; + dprintk(state->verbose, FE_DEBUG, 1, "div=%d, mclk=%d", div, mclk); + + return mclk; +} + +/* + * stb0899_set_mclk + * Set STB0899 master Clock frequency + * Mclk: demodulator master clock + * ExtClk: external clock frequency (Hz) + */ +static void stb0899_set_mclk(struct stb0899_state *state, u32 Mclk) +{ + struct stb0899_internal *internal = &state->internal; + u8 mdiv = 0; + + dprintk(state->verbose, FE_DEBUG, 1, "state->config=%p", state->config); + mdiv = ((6 * Mclk) / state->config->xtal_freq) - 1; + dprintk(state->verbose, FE_DEBUG, 1, "mdiv=%d", mdiv); + + stb0899_write_reg(state, STB0899_NCOARSE, mdiv); + internal->master_clk = stb0899_get_mclk(state); + + dprintk(state->verbose, FE_DEBUG, 1, "MasterCLOCK=%d", internal->master_clk); +} + +static int stb0899_postproc(struct stb0899_state *state, u8 ctl, int enable) +{ + struct stb0899_config *config = state->config; + const struct stb0899_postproc *postproc = config->postproc; + + /* post process event */ + if (postproc) { + if (enable) { + if (postproc[ctl].level == STB0899_GPIOPULLUP) + stb0899_write_reg(state, postproc[ctl].gpio, 0x02); + else + stb0899_write_reg(state, postproc[ctl].gpio, 0x82); + } else { + if (postproc[ctl].level == STB0899_GPIOPULLUP) + stb0899_write_reg(state, postproc[ctl].gpio, 0x82); + else + stb0899_write_reg(state, postproc[ctl].gpio, 0x02); + } + } + return 0; +} + +static void stb0899_release(struct dvb_frontend *fe) +{ + struct stb0899_state *state = fe->demodulator_priv; + + dprintk(state->verbose, FE_DEBUG, 1, "Release Frontend"); + /* post process event */ + stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0); + kfree(state); +} + +/* + * stb0899_get_alpha + * return: rolloff + */ +static int stb0899_get_alpha(struct stb0899_state *state) +{ + u8 mode_coeff; + + mode_coeff = stb0899_read_reg(state, STB0899_DEMOD); + + if (STB0899_GETFIELD(MODECOEFF, mode_coeff) == 1) + return 20; + else + return 35; +} + +/* + * stb0899_init_calc + */ +static void stb0899_init_calc(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + int master_clk; + u8 agc[2]; + u8 agc1cn; + u32 reg; + + /* Read registers (in burst mode) */ + agc1cn = stb0899_read_reg(state, STB0899_AGC1CN); + stb0899_read_regs(state, STB0899_AGC1REF, agc, 2); /* AGC1R and AGC2O */ + + /* Initial calculations */ + master_clk = stb0899_get_mclk(state); + internal->t_agc1 = 0; + internal->t_agc2 = 0; + internal->master_clk = master_clk; + internal->mclk = master_clk / 65536L; + internal->rolloff = stb0899_get_alpha(state); + + /* DVBS2 Initial calculations */ + /* Set AGC value to the middle */ + internal->agc_gain = 8154; + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); + STB0899_SETFIELD_VAL(IF_GAIN_INIT, reg, internal->agc_gain); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, RRC_ALPHA); + internal->rrc_alpha = STB0899_GETFIELD(RRC_ALPHA, reg); + + internal->center_freq = 0; + internal->av_frame_coarse = 10; + internal->av_frame_fine = 20; + internal->step_size = 2; +/* + if ((pParams->SpectralInv == FE_IQ_NORMAL) || (pParams->SpectralInv == FE_IQ_AUTO)) + pParams->IQLocked = 0; + else + pParams->IQLocked = 1; +*/ +} + +static int stb0899_wait_diseqc_fifo_empty(struct stb0899_state *state, int timeout) +{ + u8 reg = 0; + unsigned long start = jiffies; + + while (1) { + reg = stb0899_read_reg(state, STB0899_DISSTATUS); + if (!STB0899_GETFIELD(FIFOFULL, reg)) + break; + if ((jiffies - start) > timeout) { + dprintk(state->verbose, FE_ERROR, 1, "timed out !!"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int stb0899_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd) +{ + struct stb0899_state *state = fe->demodulator_priv; + u8 reg, i; + + if (cmd->msg_len > 8) + return -EINVAL; + + /* enable FIFO precharge */ + reg = stb0899_read_reg(state, STB0899_DISCNTRL1); + STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 1); + stb0899_write_reg(state, STB0899_DISCNTRL1, reg); + for (i = 0; i < cmd->msg_len; i++) { + /* wait for FIFO empty */ + if (stb0899_wait_diseqc_fifo_empty(state, 10) < 0) + return -ETIMEDOUT; + + stb0899_write_reg(state, STB0899_DISFIFO, cmd->msg[i]); + } + reg = stb0899_read_reg(state, STB0899_DISCNTRL1); + STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0); + stb0899_write_reg(state, STB0899_DISCNTRL1, reg); + + return 0; +} + +static int stb0899_wait_diseqc_rxidle(struct stb0899_state *state, int timeout) +{ + u8 reg = 0; + unsigned long start = jiffies; + + while (!STB0899_GETFIELD(RXEND, reg)) { + reg = stb0899_read_reg(state, STB0899_DISRX_ST0); + if (jiffies - start > timeout) { + dprintk(state->verbose, FE_ERROR, 1, "timed out!!"); + return -ETIMEDOUT; + } + msleep(10); + } + + return 0; +} + +static int stb0899_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply) +{ + struct stb0899_state *state = fe->demodulator_priv; + u8 reg, length = 0, i; + int result; + + if (stb0899_wait_diseqc_rxidle(state, 100) < 0) + return -ETIMEDOUT; + + reg = stb0899_read_reg(state, STB0899_DISRX_ST0); + if (STB0899_GETFIELD(RXEND, reg)) { + + reg = stb0899_read_reg(state, STB0899_DISRX_ST1); + length = STB0899_GETFIELD(FIFOBYTENBR, reg); + + if (length > sizeof (reply->msg)) { + result = -EOVERFLOW; + goto exit; + } + reply->msg_len = length; + + /* extract data */ + for (i = 0; i < length; i++) + reply->msg[i] = stb0899_read_reg(state, STB0899_DISFIFO); + } + + return 0; +exit: + + return result; +} + +static int stb0899_wait_diseqc_txidle(struct stb0899_state *state, int timeout) +{ + u8 reg = 0; + unsigned long start = jiffies; + + while (!STB0899_GETFIELD(TXIDLE, reg)) { + reg = stb0899_read_reg(state, STB0899_DISSTATUS); + if (jiffies - start > timeout) { + dprintk(state->verbose, FE_ERROR, 1, "timed out!!"); + return -ETIMEDOUT; + } + msleep(10); + } + return 0; +} + +static int stb0899_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) +{ + struct stb0899_state *state = fe->demodulator_priv; + u8 reg, old_state; + + /* wait for diseqc idle */ + if (stb0899_wait_diseqc_txidle(state, 100) < 0) + return -ETIMEDOUT; + + reg = stb0899_read_reg(state, STB0899_DISCNTRL1); + old_state = reg; + /* set to burst mode */ + STB0899_SETFIELD_VAL(DISEQCMODE, reg, 0x02); + STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x01); + stb0899_write_reg(state, STB0899_DISCNTRL1, reg); + switch (burst) { + case SEC_MINI_A: + /* unmodulated */ + stb0899_write_reg(state, STB0899_DISFIFO, 0x00); + break; + case SEC_MINI_B: + /* modulated */ + stb0899_write_reg(state, STB0899_DISFIFO, 0xff); + break; + } + reg = stb0899_read_reg(state, STB0899_DISCNTRL1); + STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x00); + stb0899_write_reg(state, STB0899_DISCNTRL1, reg); + /* wait for diseqc idle */ + if (stb0899_wait_diseqc_txidle(state, 100) < 0) + return -ETIMEDOUT; + + /* restore state */ + stb0899_write_reg(state, STB0899_DISCNTRL1, old_state); + + return 0; +} + +static int stb0899_diseqc_init(struct stb0899_state *state) +{ + struct dvb_diseqc_master_cmd tx_data; +/* + struct dvb_diseqc_slave_reply rx_data; +*/ + u8 f22_tx, f22_rx, reg; + + u32 mclk, tx_freq = 22000;/* count = 0, i; */ + tx_data.msg[0] = 0xe2; + tx_data.msg_len = 3; + reg = stb0899_read_reg(state, STB0899_DISCNTRL2); + STB0899_SETFIELD_VAL(ONECHIP_TRX, reg, 0); + stb0899_write_reg(state, STB0899_DISCNTRL2, reg); + + /* disable Tx spy */ + reg = stb0899_read_reg(state, STB0899_DISCNTRL1); + STB0899_SETFIELD_VAL(DISEQCRESET, reg, 1); + stb0899_write_reg(state, STB0899_DISCNTRL1, reg); + + reg = stb0899_read_reg(state, STB0899_DISCNTRL1); + STB0899_SETFIELD_VAL(DISEQCRESET, reg, 0); + stb0899_write_reg(state, STB0899_DISCNTRL1, reg); + + mclk = stb0899_get_mclk(state); + f22_tx = mclk / (tx_freq * 32); + stb0899_write_reg(state, STB0899_DISF22, f22_tx); /* DiSEqC Tx freq */ + state->rx_freq = 20000; + f22_rx = mclk / (state->rx_freq * 32); + + return 0; +} + +static int stb0899_sleep(struct dvb_frontend *fe) +{ + struct stb0899_state *state = fe->demodulator_priv; +/* + u8 reg; +*/ + dprintk(state->verbose, FE_DEBUG, 1, "Going to Sleep .. (Really tired .. :-))"); + /* post process event */ + stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0); + + return 0; +} + +static int stb0899_wakeup(struct dvb_frontend *fe) +{ + int rc; + struct stb0899_state *state = fe->demodulator_priv; + + if ((rc = stb0899_write_reg(state, STB0899_SYNTCTRL, STB0899_SELOSCI))) + return rc; + /* Activate all clocks; DVB-S2 registers are inaccessible otherwise. */ + if ((rc = stb0899_write_reg(state, STB0899_STOPCLK1, 0x00))) + return rc; + if ((rc = stb0899_write_reg(state, STB0899_STOPCLK2, 0x00))) + return rc; + + /* post process event */ + stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 1); + + return 0; +} + +static int stb0899_init(struct dvb_frontend *fe) +{ + int i; + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_config *config = state->config; + + dprintk(state->verbose, FE_DEBUG, 1, "Initializing STB0899 ... "); + + /* init device */ + dprintk(state->verbose, FE_DEBUG, 1, "init device"); + for (i = 0; config->init_dev[i].address != 0xffff; i++) + stb0899_write_reg(state, config->init_dev[i].address, config->init_dev[i].data); + + dprintk(state->verbose, FE_DEBUG, 1, "init S2 demod"); + /* init S2 demod */ + for (i = 0; config->init_s2_demod[i].offset != 0xffff; i++) + stb0899_write_s2reg(state, STB0899_S2DEMOD, + config->init_s2_demod[i].base_address, + config->init_s2_demod[i].offset, + config->init_s2_demod[i].data); + + dprintk(state->verbose, FE_DEBUG, 1, "init S1 demod"); + /* init S1 demod */ + for (i = 0; config->init_s1_demod[i].address != 0xffff; i++) + stb0899_write_reg(state, config->init_s1_demod[i].address, config->init_s1_demod[i].data); + + dprintk(state->verbose, FE_DEBUG, 1, "init S2 FEC"); + /* init S2 fec */ + for (i = 0; config->init_s2_fec[i].offset != 0xffff; i++) + stb0899_write_s2reg(state, STB0899_S2FEC, + config->init_s2_fec[i].base_address, + config->init_s2_fec[i].offset, + config->init_s2_fec[i].data); + + dprintk(state->verbose, FE_DEBUG, 1, "init TST"); + /* init test */ + for (i = 0; config->init_tst[i].address != 0xffff; i++) + stb0899_write_reg(state, config->init_tst[i].address, config->init_tst[i].data); + + stb0899_init_calc(state); + stb0899_diseqc_init(state); + + return 0; +} + +static int stb0899_table_lookup(const struct stb0899_tab *tab, int max, int val) +{ + int res = 0; + int min = 0, med; + + if (val < tab[min].read) + res = tab[min].real; + else if (val >= tab[max].read) + res = tab[max].real; + else { + while ((max - min) > 1) { + med = (max + min) / 2; + if (val >= tab[min].read && val < tab[med].read) + max = med; + else + min = med; + } + res = ((val - tab[min].read) * + (tab[max].real - tab[min].real) / + (tab[max].read - tab[min].read)) + + tab[min].real; + } + + return res; +} + +static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_internal *internal = &state->internal; + + int val; + u32 reg; + switch (state->delsys) { + case SYS_DVBS: + case SYS_DSS: + if (internal->lock) { + reg = stb0899_read_reg(state, STB0899_VSTATUS); + if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { + + reg = stb0899_read_reg(state, STB0899_AGCIQIN); + val = (s32)(s8)STB0899_GETFIELD(AGCIQVALUE, reg); + + *strength = stb0899_table_lookup(stb0899_dvbsrf_tab, ARRAY_SIZE(stb0899_dvbsrf_tab) - 1, val); + *strength += 750; + dprintk(state->verbose, FE_DEBUG, 1, "AGCIQVALUE = 0x%02x, C = %d * 0.1 dBm", + val & 0xff, *strength); + } + } + break; + case SYS_DVBS2: + if (internal->lock) { + reg = STB0899_READ_S2REG(STB0899_DEMOD, IF_AGC_GAIN); + val = STB0899_GETFIELD(IF_AGC_GAIN, reg); + + *strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val); + *strength += 750; + dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm", + val & 0x3fff, *strength); + } + break; + default: + dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); + return -EINVAL; + } + + return 0; +} + +static int stb0899_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_internal *internal = &state->internal; + + unsigned int val, quant, quantn = -1, est, estn = -1; + u8 buf[2]; + u32 reg; + + reg = stb0899_read_reg(state, STB0899_VSTATUS); + switch (state->delsys) { + case SYS_DVBS: + case SYS_DSS: + if (internal->lock) { + if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { + + stb0899_read_regs(state, STB0899_NIRM, buf, 2); + val = MAKEWORD16(buf[0], buf[1]); + + *snr = stb0899_table_lookup(stb0899_cn_tab, ARRAY_SIZE(stb0899_cn_tab) - 1, val); + dprintk(state->verbose, FE_DEBUG, 1, "NIR = 0x%02x%02x = %u, C/N = %d * 0.1 dBm\n", + buf[0], buf[1], val, *snr); + } + } + break; + case SYS_DVBS2: + if (internal->lock) { + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1); + quant = STB0899_GETFIELD(UWP_ESN0_QUANT, reg); + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); + est = STB0899_GETFIELD(ESN0_EST, reg); + if (est == 1) + val = 301; /* C/N = 30.1 dB */ + else if (est == 2) + val = 270; /* C/N = 27.0 dB */ + else { + /* quantn = 100 * log(quant^2) */ + quantn = stb0899_table_lookup(stb0899_quant_tab, ARRAY_SIZE(stb0899_quant_tab) - 1, quant * 100); + /* estn = 100 * log(est) */ + estn = stb0899_table_lookup(stb0899_est_tab, ARRAY_SIZE(stb0899_est_tab) - 1, est); + /* snr(dBm/10) = -10*(log(est)-log(quant^2)) => snr(dBm/10) = (100*log(quant^2)-100*log(est))/10 */ + val = (quantn - estn) / 10; + } + *snr = val; + dprintk(state->verbose, FE_DEBUG, 1, "Es/N0 quant = %d (%d) estimate = %u (%d), C/N = %d * 0.1 dBm", + quant, quantn, est, estn, val); + } + break; + default: + dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); + return -EINVAL; + } + + return 0; +} + +static int stb0899_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_internal *internal = &state->internal; + u8 reg; + *status = 0; + + switch (state->delsys) { + case SYS_DVBS: + case SYS_DSS: + dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S/DSS"); + if (internal->lock) { + reg = stb0899_read_reg(state, STB0899_VSTATUS); + if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { + dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_CARRIER | FE_HAS_LOCK"); + *status |= FE_HAS_CARRIER | FE_HAS_LOCK; + + reg = stb0899_read_reg(state, STB0899_PLPARM); + if (STB0899_GETFIELD(VITCURPUN, reg)) { + dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_VITERBI | FE_HAS_SYNC"); + *status |= FE_HAS_VITERBI | FE_HAS_SYNC; + /* post process event */ + stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1); + } + } + } + break; + case SYS_DVBS2: + dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S2"); + if (internal->lock) { + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2); + if (STB0899_GETFIELD(UWP_LOCK, reg) && STB0899_GETFIELD(CSM_LOCK, reg)) { + *status |= FE_HAS_CARRIER; + dprintk(state->verbose, FE_DEBUG, 1, + "UWP & CSM Lock ! ---> DVB-S2 FE_HAS_CARRIER"); + + reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1); + if (STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg)) { + *status |= FE_HAS_LOCK; + dprintk(state->verbose, FE_DEBUG, 1, + "Packet Delineator Locked ! -----> DVB-S2 FE_HAS_LOCK"); + + } + if (STB0899_GETFIELD(CONTINUOUS_STREAM, reg)) { + *status |= FE_HAS_VITERBI; + dprintk(state->verbose, FE_DEBUG, 1, + "Packet Delineator found VITERBI ! -----> DVB-S2 FE_HAS_VITERBI"); + } + if (STB0899_GETFIELD(ACCEPTED_STREAM, reg)) { + *status |= FE_HAS_SYNC; + dprintk(state->verbose, FE_DEBUG, 1, + "Packet Delineator found SYNC ! -----> DVB-S2 FE_HAS_SYNC"); + /* post process event */ + stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1); + } + } + } + break; + default: + dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); + return -EINVAL; + } + return 0; +} + +/* + * stb0899_get_error + * viterbi error for DVB-S/DSS + * packet error for DVB-S2 + * Bit Error Rate or Packet Error Rate * 10 ^ 7 + */ +static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_internal *internal = &state->internal; + + u8 lsb, msb; + u32 i; + + *ber = 0; + + switch (state->delsys) { + case SYS_DVBS: + case SYS_DSS: + if (internal->lock) { + /* average 5 BER values */ + for (i = 0; i < 5; i++) { + msleep(100); + lsb = stb0899_read_reg(state, STB0899_ECNT1L); + msb = stb0899_read_reg(state, STB0899_ECNT1M); + *ber += MAKEWORD16(msb, lsb); + } + *ber /= 5; + /* Viterbi Check */ + if (STB0899_GETFIELD(VSTATUS_PRFVIT, internal->v_status)) { + /* Error Rate */ + *ber *= 9766; + /* ber = ber * 10 ^ 7 */ + *ber /= (-1 + (1 << (2 * STB0899_GETFIELD(NOE, internal->err_ctrl)))); + *ber /= 8; + } + } + break; + case SYS_DVBS2: + if (internal->lock) { + /* Average 5 PER values */ + for (i = 0; i < 5; i++) { + msleep(100); + lsb = stb0899_read_reg(state, STB0899_ECNT1L); + msb = stb0899_read_reg(state, STB0899_ECNT1M); + *ber += MAKEWORD16(msb, lsb); + } + /* ber = ber * 10 ^ 7 */ + *ber *= 10000000; + *ber /= (-1 + (1 << (4 + 2 * STB0899_GETFIELD(NOE, internal->err_ctrl)))); + } + break; + default: + dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); + return -EINVAL; + } + + return 0; +} + +static int stb0899_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct stb0899_state *state = fe->demodulator_priv; + + switch (voltage) { + case SEC_VOLTAGE_13: + stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82); + stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02); + stb0899_write_reg(state, STB0899_GPIO02CFG, 0x00); + break; + case SEC_VOLTAGE_18: + stb0899_write_reg(state, STB0899_GPIO00CFG, 0x02); + stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02); + stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82); + break; + case SEC_VOLTAGE_OFF: + stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82); + stb0899_write_reg(state, STB0899_GPIO01CFG, 0x82); + stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int stb0899_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +{ + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_internal *internal = &state->internal; + + u8 div, reg; + + /* wait for diseqc idle */ + if (stb0899_wait_diseqc_txidle(state, 100) < 0) + return -ETIMEDOUT; + + switch (tone) { + case SEC_TONE_ON: + div = (internal->master_clk / 100) / 5632; + div = (div + 5) / 10; + stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x66); + reg = stb0899_read_reg(state, STB0899_ACRPRESC); + STB0899_SETFIELD_VAL(ACRPRESC, reg, 0x03); + stb0899_write_reg(state, STB0899_ACRPRESC, reg); + stb0899_write_reg(state, STB0899_ACRDIV1, div); + break; + case SEC_TONE_OFF: + stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x20); + break; + default: + return -EINVAL; + } + return 0; +} + +int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + int i2c_stat; + struct stb0899_state *state = fe->demodulator_priv; + + i2c_stat = stb0899_read_reg(state, STB0899_I2CRPT); + if (i2c_stat < 0) + goto err; + + if (enable) { + dprintk(state->verbose, FE_DEBUG, 1, "Enabling I2C Repeater ..."); + i2c_stat |= STB0899_I2CTON; + if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0) + goto err; + } else { + dprintk(state->verbose, FE_DEBUG, 1, "Disabling I2C Repeater ..."); + i2c_stat &= ~STB0899_I2CTON; + if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0) + goto err; + } + return 0; +err: + dprintk(state->verbose, FE_ERROR, 1, "I2C Repeater control failed"); + return -EREMOTEIO; +} + + +static inline void CONVERT32(u32 x, char *str) +{ + *str++ = (x >> 24) & 0xff; + *str++ = (x >> 16) & 0xff; + *str++ = (x >> 8) & 0xff; + *str++ = (x >> 0) & 0xff; + *str = '\0'; +} + +int stb0899_get_dev_id(struct stb0899_state *state) +{ + u8 chip_id, release; + u16 id; + u32 demod_ver = 0, fec_ver = 0; + char demod_str[5] = { 0 }; + char fec_str[5] = { 0 }; + + id = stb0899_read_reg(state, STB0899_DEV_ID); + dprintk(state->verbose, FE_DEBUG, 1, "ID reg=[0x%02x]", id); + chip_id = STB0899_GETFIELD(CHIP_ID, id); + release = STB0899_GETFIELD(CHIP_REL, id); + + dprintk(state->verbose, FE_ERROR, 1, "Device ID=[%d], Release=[%d]", + chip_id, release); + + CONVERT32(STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CORE_ID), (char *)&demod_str); + + demod_ver = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_VERSION_ID); + dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver); + CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str); + fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG); + if (! (chip_id > 0)) { + dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899"); + + return -ENODEV; + } + dprintk(state->verbose, FE_ERROR, 1, "FEC Core ID=[%s], Version=[%d]", (char*) &fec_str, fec_ver); + + return 0; +} + +static void stb0899_set_delivery(struct stb0899_state *state) +{ + u8 reg; + u8 stop_clk[2]; + + stop_clk[0] = stb0899_read_reg(state, STB0899_STOPCLK1); + stop_clk[1] = stb0899_read_reg(state, STB0899_STOPCLK2); + + switch (state->delsys) { + case SYS_DVBS: + dprintk(state->verbose, FE_DEBUG, 1, "Delivery System -- DVB-S"); + /* FECM/Viterbi ON */ + reg = stb0899_read_reg(state, STB0899_FECM); + STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0); + STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1); + stb0899_write_reg(state, STB0899_FECM, reg); + + stb0899_write_reg(state, STB0899_RSULC, 0xb1); + stb0899_write_reg(state, STB0899_TSULC, 0x40); + stb0899_write_reg(state, STB0899_RSLLC, 0x42); + stb0899_write_reg(state, STB0899_TSLPL, 0x12); + + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESLDPC, reg, 1); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); + STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1); + STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1); + + STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1); + STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1); + + STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 1); + STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); + + STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1); + break; + case SYS_DVBS2: + /* FECM/Viterbi OFF */ + reg = stb0899_read_reg(state, STB0899_FECM); + STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0); + STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 0); + stb0899_write_reg(state, STB0899_FECM, reg); + + stb0899_write_reg(state, STB0899_RSULC, 0xb1); + stb0899_write_reg(state, STB0899_TSULC, 0x42); + stb0899_write_reg(state, STB0899_RSLLC, 0x40); + stb0899_write_reg(state, STB0899_TSLPL, 0x02); + + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESLDPC, reg, 0); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); + STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 0); + STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 0); + + STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 0); + STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 0); + + STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 0); + STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); + + STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 0); + break; + case SYS_DSS: + /* FECM/Viterbi ON */ + reg = stb0899_read_reg(state, STB0899_FECM); + STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 1); + STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1); + stb0899_write_reg(state, STB0899_FECM, reg); + + stb0899_write_reg(state, STB0899_RSULC, 0xa1); + stb0899_write_reg(state, STB0899_TSULC, 0x61); + stb0899_write_reg(state, STB0899_RSLLC, 0x42); + + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESLDPC, reg, 1); + stb0899_write_reg(state, STB0899_TSTRES, reg); + + STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); + STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1); + STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1); + + STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1); + STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1); + + STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); + + STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1); + break; + default: + dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system"); + break; + } + STB0899_SETFIELD_VAL(STOP_CKADCI108, stop_clk[0], 0); + stb0899_write_regs(state, STB0899_STOPCLK1, stop_clk, 2); +} + +/* + * stb0899_set_iterations + * set the LDPC iteration scale function + */ +static void stb0899_set_iterations(struct stb0899_state *state) +{ + struct stb0899_internal *internal = &state->internal; + struct stb0899_config *config = state->config; + + s32 iter_scale; + u32 reg; + + iter_scale = 17 * (internal->master_clk / 1000); + iter_scale += 410000; + iter_scale /= (internal->srate / 1000000); + iter_scale /= 1000; + + if (iter_scale > config->ldpc_max_iter) + iter_scale = config->ldpc_max_iter; + + reg = STB0899_READ_S2REG(STB0899_S2DEMOD, MAX_ITER); + STB0899_SETFIELD_VAL(MAX_ITERATIONS, reg, iter_scale); + stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg); +} + +static enum dvbfe_search stb0899_search(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) +{ + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_params *i_params = &state->params; + struct stb0899_internal *internal = &state->internal; + struct stb0899_config *config = state->config; + struct dtv_frontend_properties *props = &fe->dtv_property_cache; + + u32 SearchRange, gain; + + i_params->freq = p->frequency; + i_params->srate = p->u.qpsk.symbol_rate; + state->delsys = props->delivery_system; + dprintk(state->verbose, FE_DEBUG, 1, "delivery system=%d", state->delsys); + + SearchRange = 10000000; + dprintk(state->verbose, FE_DEBUG, 1, "Frequency=%d, Srate=%d", i_params->freq, i_params->srate); + /* checking Search Range is meaningless for a fixed 3 Mhz */ + if (INRANGE(i_params->srate, 1000000, 45000000)) { + dprintk(state->verbose, FE_DEBUG, 1, "Parameters IN RANGE"); + stb0899_set_delivery(state); + + if (state->config->tuner_set_rfsiggain) { + if (internal->srate > 15000000) + gain = 8; /* 15Mb < srate < 45Mb, gain = 8dB */ + else if (internal->srate > 5000000) + gain = 12; /* 5Mb < srate < 15Mb, gain = 12dB */ + else + gain = 14; /* 1Mb < srate < 5Mb, gain = 14db */ + state->config->tuner_set_rfsiggain(fe, gain); + } + + if (i_params->srate <= 5000000) + stb0899_set_mclk(state, config->lo_clk); + else + stb0899_set_mclk(state, config->hi_clk); + + switch (state->delsys) { + case SYS_DVBS: + case SYS_DSS: + dprintk(state->verbose, FE_DEBUG, 1, "DVB-S delivery system"); + internal->freq = i_params->freq; + internal->srate = i_params->srate; + /* + * search = user search range + + * 500Khz + + * 2 * Tuner_step_size + + * 10% of the symbol rate + */ + internal->srch_range = SearchRange + 1500000 + (i_params->srate / 5); + internal->derot_percent = 30; + + /* What to do for tuners having no bandwidth setup ? */ + /* enable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 1); + + if (state->config->tuner_set_bandwidth) + state->config->tuner_set_bandwidth(fe, (13 * (stb0899_carr_width(state) + SearchRange)) / 10); + if (state->config->tuner_get_bandwidth) + state->config->tuner_get_bandwidth(fe, &internal->tuner_bw); + + /* disable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 0); + + /* Set DVB-S1 AGC */ + stb0899_write_reg(state, STB0899_AGCRFCFG, 0x11); + + /* Run the search algorithm */ + dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S search algo .."); + if (stb0899_dvbs_algo(state) == RANGEOK) { + internal->lock = 1; + dprintk(state->verbose, FE_DEBUG, 1, + "-------------------------------------> DVB-S LOCK !"); + +// stb0899_write_reg(state, STB0899_ERRCTRL1, 0x3d); /* Viterbi Errors */ +// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS); +// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1); +// dprintk(state->verbose, FE_DEBUG, 1, "VSTATUS=0x%02x", internal->v_status); +// dprintk(state->verbose, FE_DEBUG, 1, "ERR_CTRL=0x%02x", internal->err_ctrl); + + return DVBFE_ALGO_SEARCH_SUCCESS; + } else { + internal->lock = 0; + + return DVBFE_ALGO_SEARCH_FAILED; + } + break; + case SYS_DVBS2: + internal->freq = i_params->freq; + internal->srate = i_params->srate; + internal->srch_range = SearchRange; + + /* enable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 1); + + if (state->config->tuner_set_bandwidth) + state->config->tuner_set_bandwidth(fe, (stb0899_carr_width(state) + SearchRange)); + if (state->config->tuner_get_bandwidth) + state->config->tuner_get_bandwidth(fe, &internal->tuner_bw); + + /* disable tuner I/O */ + stb0899_i2c_gate_ctrl(&state->frontend, 0); + +// pParams->SpectralInv = pSearch->IQ_Inversion; + + /* Set DVB-S2 AGC */ + stb0899_write_reg(state, STB0899_AGCRFCFG, 0x1c); + + /* Set IterScale =f(MCLK,SYMB) */ + stb0899_set_iterations(state); + + /* Run the search algorithm */ + dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S2 search algo .."); + if (stb0899_dvbs2_algo(state) == DVBS2_FEC_LOCK) { + internal->lock = 1; + dprintk(state->verbose, FE_DEBUG, 1, + "-------------------------------------> DVB-S2 LOCK !"); + +// stb0899_write_reg(state, STB0899_ERRCTRL1, 0xb6); /* Packet Errors */ +// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS); +// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1); + + return DVBFE_ALGO_SEARCH_SUCCESS; + } else { + internal->lock = 0; + + return DVBFE_ALGO_SEARCH_FAILED; + } + break; + default: + dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system"); + return DVBFE_ALGO_SEARCH_INVALID; + } + } + + return DVBFE_ALGO_SEARCH_ERROR; +} +/* + * stb0899_track + * periodically check the signal level against a specified + * threshold level and perform derotator centering. + * called once we have a lock from a succesful search + * event. + * + * Will be called periodically called to maintain the + * lock. + * + * Will be used to get parameters as well as info from + * the decoded baseband header + * + * Once a new lock has established, the internal state + * frequency (internal->freq) is updated + */ +static int stb0899_track(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) +{ + return 0; +} + +static int stb0899_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) +{ + struct stb0899_state *state = fe->demodulator_priv; + struct stb0899_internal *internal = &state->internal; + + dprintk(state->verbose, FE_DEBUG, 1, "Get params"); + p->u.qpsk.symbol_rate = internal->srate; + + return 0; +} + +static enum dvbfe_algo stb0899_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_CUSTOM; +} + +static struct dvb_frontend_ops stb0899_ops = { + + .info = { + .name = "STB0899 Multistandard", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 0, + .frequency_tolerance = 0, + .symbol_rate_min = 5000000, + .symbol_rate_max = 45000000, + + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK + }, + + .release = stb0899_release, + .init = stb0899_init, + .sleep = stb0899_sleep, +// .wakeup = stb0899_wakeup, + + .i2c_gate_ctrl = stb0899_i2c_gate_ctrl, + + .get_frontend_algo = stb0899_frontend_algo, + .search = stb0899_search, + .track = stb0899_track, + .get_frontend = stb0899_get_frontend, + + + .read_status = stb0899_read_status, + .read_snr = stb0899_read_snr, + .read_signal_strength = stb0899_read_signal_strength, + .read_ber = stb0899_read_ber, + + .set_voltage = stb0899_set_voltage, + .set_tone = stb0899_set_tone, + + .diseqc_send_master_cmd = stb0899_send_diseqc_msg, + .diseqc_recv_slave_reply = stb0899_recv_slave_reply, + .diseqc_send_burst = stb0899_send_diseqc_burst, +}; + +struct dvb_frontend *stb0899_attach(struct stb0899_config *config, struct i2c_adapter *i2c) +{ + struct stb0899_state *state = NULL; + enum stb0899_inversion inversion; + + state = kzalloc(sizeof (struct stb0899_state), GFP_KERNEL); + if (state == NULL) + goto error; + + inversion = config->inversion; + state->verbose = &verbose; + state->config = config; + state->i2c = i2c; + state->frontend.ops = stb0899_ops; + state->frontend.demodulator_priv = state; + state->internal.inversion = inversion; + + stb0899_wakeup(&state->frontend); + if (stb0899_get_dev_id(state) == -ENODEV) { + printk("%s: Exiting .. !\n", __func__); + goto error; + } + + printk("%s: Attaching STB0899 \n", __func__); + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(stb0899_attach); +MODULE_PARM_DESC(verbose, "Set Verbosity level"); +MODULE_AUTHOR("Manu Abraham"); +MODULE_DESCRIPTION("STB0899 Multi-Std frontend"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/stb0899_drv.h b/drivers/media/dvb/frontends/stb0899_drv.h new file mode 100644 index 0000000..98b200c --- /dev/null +++ b/drivers/media/dvb/frontends/stb0899_drv.h @@ -0,0 +1,162 @@ +/* + STB0899 Multistandard Frontend driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + + 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 __STB0899_DRV_H +#define __STB0899_DRV_H + +#include <linux/kernel.h> +#include <linux/module.h> + +#include "dvb_frontend.h" + +#define STB0899_TSMODE_SERIAL 1 +#define STB0899_CLKPOL_FALLING 2 +#define STB0899_CLKNULL_PARITY 3 +#define STB0899_SYNC_FORCED 4 +#define STB0899_FECMODE_DSS 5 + +struct stb0899_s1_reg { + u16 address; + u8 data; +}; + +struct stb0899_s2_reg { + u16 offset; + u32 base_address; + u32 data; +}; + +enum stb0899_inversion { + IQ_SWAP_OFF = 0, + IQ_SWAP_ON, + IQ_SWAP_AUTO +}; + +#define STB0899_GPIO00 0xf140 +#define STB0899_GPIO01 0xf141 +#define STB0899_GPIO02 0xf142 +#define STB0899_GPIO03 0xf143 +#define STB0899_GPIO04 0xf144 +#define STB0899_GPIO05 0xf145 +#define STB0899_GPIO06 0xf146 +#define STB0899_GPIO07 0xf147 +#define STB0899_GPIO08 0xf148 +#define STB0899_GPIO09 0xf149 +#define STB0899_GPIO10 0xf14a +#define STB0899_GPIO11 0xf14b +#define STB0899_GPIO12 0xf14c +#define STB0899_GPIO13 0xf14d +#define STB0899_GPIO14 0xf14e +#define STB0899_GPIO15 0xf14f +#define STB0899_GPIO16 0xf150 +#define STB0899_GPIO17 0xf151 +#define STB0899_GPIO18 0xf152 +#define STB0899_GPIO19 0xf153 +#define STB0899_GPIO20 0xf154 + +#define STB0899_GPIOPULLUP 0x01 /* Output device is connected to Vdd */ +#define STB0899_GPIOPULLDN 0x00 /* Output device is connected to Vss */ + +#define STB0899_POSTPROC_GPIO_POWER 0x00 +#define STB0899_POSTPROC_GPIO_LOCK 0x01 + +/* + * Post process output configuration control + * 1. POWER ON/OFF (index 0) + * 2. FE_HAS_LOCK/LOCK_LOSS (index 1) + * + * @gpio = one of the above listed GPIO's + * @level = output state: pulled up or low + */ +struct stb0899_postproc { + u16 gpio; + u8 level; +}; + +struct stb0899_config { + const struct stb0899_s1_reg *init_dev; + const struct stb0899_s2_reg *init_s2_demod; + const struct stb0899_s1_reg *init_s1_demod; + const struct stb0899_s2_reg *init_s2_fec; + const struct stb0899_s1_reg *init_tst; + + const struct stb0899_postproc *postproc; + + enum stb0899_inversion inversion; + + u32 xtal_freq; + + u8 demod_address; + u8 ts_output_mode; + u8 block_sync_mode; + u8 ts_pfbit_toggle; + + u8 clock_polarity; + u8 data_clk_parity; + u8 fec_mode; + u8 data_output_ctl; + u8 data_fifo_mode; + u8 out_rate_comp; + u8 i2c_repeater; +// int inversion; + int lo_clk; + int hi_clk; + + u32 esno_ave; + u32 esno_quant; + u32 avframes_coarse; + u32 avframes_fine; + u32 miss_threshold; + u32 uwp_threshold_acq; + u32 uwp_threshold_track; + u32 uwp_threshold_sof; + u32 sof_search_timeout; + + u32 btr_nco_bits; + u32 btr_gain_shift_offset; + u32 crl_nco_bits; + u32 ldpc_max_iter; + + int (*tuner_set_frequency)(struct dvb_frontend *fe, u32 frequency); + int (*tuner_get_frequency)(struct dvb_frontend *fe, u32 *frequency); + int (*tuner_set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth); + int (*tuner_get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); + int (*tuner_set_rfsiggain)(struct dvb_frontend *fe, u32 rf_gain); +}; + +#if defined(CONFIG_DVB_STB0899) || (defined(CONFIG_DVB_STB0899_MODULE) && defined(MODULE)) + +extern struct dvb_frontend *stb0899_attach(struct stb0899_config *config, + struct i2c_adapter *i2c); + +#else + +static inline struct dvb_frontend *stb0899_attach(struct stb0899_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif //CONFIG_DVB_STB0899 + + +#endif diff --git a/drivers/media/dvb/frontends/stb0899_priv.h b/drivers/media/dvb/frontends/stb0899_priv.h new file mode 100644 index 0000000..24619e3 --- /dev/null +++ b/drivers/media/dvb/frontends/stb0899_priv.h @@ -0,0 +1,267 @@ +/* + STB0899 Multistandard Frontend driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + + 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 __STB0899_PRIV_H +#define __STB0899_PRIV_H + +#include "dvb_frontend.h" +#include "stb0899_drv.h" + +#define FE_ERROR 0 +#define FE_NOTICE 1 +#define FE_INFO 2 +#define FE_DEBUG 3 +#define FE_DEBUGREG 4 + +#define dprintk(x, y, z, format, arg...) do { \ + if (z) { \ + if ((*x > FE_ERROR) && (*x > y)) \ + printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ + else if ((*x > FE_NOTICE) && (*x > y)) \ + printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ + else if ((*x > FE_INFO) && (*x > y)) \ + printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ + else if ((*x > FE_DEBUG) && (*x > y)) \ + printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ + } else { \ + if (*x > y) \ + printk(format, ##arg); \ + } \ +} while(0) + +#define INRANGE(val, x, y) (((x <= val) && (val <= y)) || \ + ((y <= val) && (val <= x)) ? 1 : 0) + +#define BYTE0 0 +#define BYTE1 8 +#define BYTE2 16 +#define BYTE3 24 + +#define GETBYTE(x, y) (((x) >> (y)) & 0xff) +#define MAKEWORD32(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) +#define MAKEWORD16(a, b) (((a) << 8) | (b)) + +#define MIN(x, y) ((x) <= (y) ? (x) : (y)) +#define MAX(x, y) ((x) >= (y) ? (x) : (y)) +#define ABS(x) ((x) >= 0 ? (x) : -(x)) + +#define LSB(x) ((x & 0xff)) +#define MSB(y) ((y >> 8) & 0xff) + + +#define STB0899_GETFIELD(bitf, val) ((val >> STB0899_OFFST_##bitf) & ((1 << STB0899_WIDTH_##bitf) - 1)) + + +#define STB0899_SETFIELD(mask, val, width, offset) (mask & (~(((1 << width) - 1) << \ + offset))) | ((val & \ + ((1 << width) - 1)) << offset) + +#define STB0899_SETFIELD_VAL(bitf, mask, val) (mask = (mask & (~(((1 << STB0899_WIDTH_##bitf) - 1) <<\ + STB0899_OFFST_##bitf))) | \ + (val << STB0899_OFFST_##bitf)) + + +enum stb0899_status { + NOAGC1 = 0, + AGC1OK, + NOTIMING, + ANALOGCARRIER, + TIMINGOK, + NOAGC2, + AGC2OK, + NOCARRIER, + CARRIEROK, + NODATA, + FALSELOCK, + DATAOK, + OUTOFRANGE, + RANGEOK, + DVBS2_DEMOD_LOCK, + DVBS2_DEMOD_NOLOCK, + DVBS2_FEC_LOCK, + DVBS2_FEC_NOLOCK +}; + +enum stb0899_modcod { + STB0899_DUMMY_PLF, + STB0899_QPSK_14, + STB0899_QPSK_13, + STB0899_QPSK_25, + STB0899_QPSK_12, + STB0899_QPSK_35, + STB0899_QPSK_23, + STB0899_QPSK_34, + STB0899_QPSK_45, + STB0899_QPSK_56, + STB0899_QPSK_89, + STB0899_QPSK_910, + STB0899_8PSK_35, + STB0899_8PSK_23, + STB0899_8PSK_34, + STB0899_8PSK_56, + STB0899_8PSK_89, + STB0899_8PSK_910, + STB0899_16APSK_23, + STB0899_16APSK_34, + STB0899_16APSK_45, + STB0899_16APSK_56, + STB0899_16APSK_89, + STB0899_16APSK_910, + STB0899_32APSK_34, + STB0899_32APSK_45, + STB0899_32APSK_56, + STB0899_32APSK_89, + STB0899_32APSK_910 +}; + +enum stb0899_frame { + STB0899_LONG_FRAME, + STB0899_SHORT_FRAME +}; + +enum stb0899_alpha { + RRC_20, + RRC_25, + RRC_35 +}; + +struct stb0899_tab { + s32 real; + s32 read; +}; + +enum stb0899_fec { + STB0899_FEC_1_2 = 13, + STB0899_FEC_2_3 = 18, + STB0899_FEC_3_4 = 21, + STB0899_FEC_5_6 = 24, + STB0899_FEC_6_7 = 25, + STB0899_FEC_7_8 = 26 +}; + +struct stb0899_params { + u32 freq; /* Frequency */ + u32 srate; /* Symbol rate */ + enum fe_code_rate fecrate; +}; + +struct stb0899_internal { + u32 master_clk; + u32 freq; /* Demod internal Frequency */ + u32 srate; /* Demod internal Symbol rate */ + enum stb0899_fec fecrate; /* Demod internal FEC rate */ + u32 srch_range; /* Demod internal Search Range */ + u32 sub_range; /* Demod current sub range (Hz) */ + u32 tuner_step; /* Tuner step (Hz) */ + u32 tuner_offst; /* Relative offset to carrier (Hz) */ + u32 tuner_bw; /* Current bandwidth of the tuner (Hz) */ + + s32 mclk; /* Masterclock Divider factor (binary) */ + s32 rolloff; /* Current RollOff of the filter (x100) */ + + s16 derot_freq; /* Current derotator frequency (Hz) */ + s16 derot_percent; + + s16 direction; /* Current derotator search direction */ + s16 derot_step; /* Derotator step (binary value) */ + s16 t_derot; /* Derotator time constant (ms) */ + s16 t_data; /* Data recovery time constant (ms) */ + s16 sub_dir; /* Direction of the next sub range */ + + s16 t_agc1; /* Agc1 time constant (ms) */ + s16 t_agc2; /* Agc2 time constant (ms) */ + + u32 lock; /* Demod internal lock state */ + enum stb0899_status status; /* Demod internal status */ + + /* DVB-S2 */ + s32 agc_gain; /* RF AGC Gain */ + s32 center_freq; /* Nominal carrier frequency */ + s32 av_frame_coarse; /* Coarse carrier freq search frames */ + s32 av_frame_fine; /* Fine carrier freq search frames */ + + s16 step_size; /* Carrier frequency search step size */ + + enum stb0899_alpha rrc_alpha; + enum stb0899_inversion inversion; + enum stb0899_modcod modcod; + u8 pilots; /* Pilots found */ + + enum stb0899_frame frame_length; + u8 v_status; /* VSTATUS */ + u8 err_ctrl; /* ERRCTRLn */ +}; + +struct stb0899_state { + struct i2c_adapter *i2c; + struct stb0899_config *config; + struct dvb_frontend frontend; + + u32 *verbose; /* Cached module verbosity level */ + + struct stb0899_internal internal; /* Device internal parameters */ + + /* cached params from API */ + enum fe_delivery_system delsys; + struct stb0899_params params; + + u32 rx_freq; /* DiSEqC 2.0 receiver freq */ + struct mutex search_lock; +}; +/* stb0899.c */ +extern int stb0899_read_reg(struct stb0899_state *state, + unsigned int reg); + +extern u32 _stb0899_read_s2reg(struct stb0899_state *state, + u32 stb0899_i2cdev, + u32 stb0899_base_addr, + u16 stb0899_reg_offset); + +extern int stb0899_read_regs(struct stb0899_state *state, + unsigned int reg, u8 *buf, + u32 count); + +extern int stb0899_write_regs(struct stb0899_state *state, + unsigned int reg, u8 *data, + u32 count); + +extern int stb0899_write_reg(struct stb0899_state *state, + unsigned int reg, + u8 data); + +extern int stb0899_write_s2reg(struct stb0899_state *state, + u32 stb0899_i2cdev, + u32 stb0899_base_addr, + u16 stb0899_reg_offset, + u32 stb0899_data); + +extern int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable); + + +#define STB0899_READ_S2REG(DEVICE, REG) (_stb0899_read_s2reg(state, DEVICE, STB0899_BASE_##REG, STB0899_OFF0_##REG)) +//#define STB0899_WRITE_S2REG(DEVICE, REG, DATA) (_stb0899_write_s2reg(state, DEVICE, STB0899_BASE_##REG, STB0899_OFF0_##REG, DATA)) + +/* stb0899_algo.c */ +extern enum stb0899_status stb0899_dvbs_algo(struct stb0899_state *state); +extern enum stb0899_status stb0899_dvbs2_algo(struct stb0899_state *state); +extern long stb0899_carr_width(struct stb0899_state *state); + +#endif //__STB0899_PRIV_H diff --git a/drivers/media/dvb/frontends/stb0899_reg.h b/drivers/media/dvb/frontends/stb0899_reg.h new file mode 100644 index 0000000..ba1ed56 --- /dev/null +++ b/drivers/media/dvb/frontends/stb0899_reg.h @@ -0,0 +1,2027 @@ +/* + STB0899 Multistandard Frontend driver + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + + 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 __STB0899_REG_H +#define __STB0899_REG_H + +/* S1 */ +#define STB0899_DEV_ID 0xf000 +#define STB0899_CHIP_ID (0x0f << 4) +#define STB0899_OFFST_CHIP_ID 4 +#define STB0899_WIDTH_CHIP_ID 4 +#define STB0899_CHIP_REL (0x0f << 0) +#define STB0899_OFFST_CHIP_REL 0 +#define STB0899_WIDTH_CHIP_REL 4 + +#define STB0899_DEMOD 0xf40e +#define STB0899_MODECOEFF (0x01 << 0) +#define STB0899_OFFST_MODECOEFF 0 +#define STB0899_WIDTH_MODECOEFF 1 + +#define STB0899_RCOMPC 0xf410 +#define STB0899_AGC1CN 0xf412 +#define STB0899_AGC1REF 0xf413 +#define STB0899_RTC 0xf417 +#define STB0899_TMGCFG 0xf418 +#define STB0899_AGC2REF 0xf419 +#define STB0899_TLSR 0xf41a + +#define STB0899_CFD 0xf41b +#define STB0899_CFD_ON (0x01 << 7) +#define STB0899_OFFST_CFD_ON 7 +#define STB0899_WIDTH_CFD_ON 1 + +#define STB0899_ACLC 0xf41c + +#define STB0899_BCLC 0xf41d +#define STB0899_OFFST_ALGO 6 +#define STB0899_WIDTH_ALGO_QPSK2 2 +#define STB0899_ALGO_QPSK2 (2 << 6) +#define STB0899_ALGO_QPSK1 (1 << 6) +#define STB0899_ALGO_BPSK (0 << 6) +#define STB0899_OFFST_BETA 0 +#define STB0899_WIDTH_BETA 6 + +#define STB0899_EQON 0xf41e +#define STB0899_LDT 0xf41f +#define STB0899_LDT2 0xf420 +#define STB0899_EQUALREF 0xf425 +#define STB0899_TMGRAMP 0xf426 +#define STB0899_TMGTHD 0xf427 +#define STB0899_IDCCOMP 0xf428 +#define STB0899_QDCCOMP 0xf429 +#define STB0899_POWERI 0xf42a +#define STB0899_POWERQ 0xf42b +#define STB0899_RCOMP 0xf42c + +#define STB0899_AGCIQIN 0xf42e +#define STB0899_AGCIQVALUE (0xff << 0) +#define STB0899_OFFST_AGCIQVALUE 0 +#define STB0899_WIDTH_AGCIQVALUE 8 + +#define STB0899_AGC2I1 0xf436 +#define STB0899_AGC2I2 0xf437 + +#define STB0899_TLIR 0xf438 +#define STB0899_TLIR_TMG_LOCK_IND (0xff << 0) +#define STB0899_OFFST_TLIR_TMG_LOCK_IND 0 +#define STB0899_WIDTH_TLIR_TMG_LOCK_IND 8 + +#define STB0899_RTF 0xf439 +#define STB0899_RTF_TIMING_LOOP_FREQ (0xff << 0) +#define STB0899_OFFST_RTF_TIMING_LOOP_FREQ 0 +#define STB0899_WIDTH_RTF_TIMING_LOOP_FREQ 8 + +#define STB0899_DSTATUS 0xf43a +#define STB0899_CARRIER_FOUND (0x01 << 7) +#define STB0899_OFFST_CARRIER_FOUND 7 +#define STB0899_WIDTH_CARRIER_FOUND 1 +#define STB0899_TMG_LOCK (0x01 << 6) +#define STB0899_OFFST_TMG_LOCK 6 +#define STB0899_WIDTH_TMG_LOCK 1 +#define STB0899_DEMOD_LOCK (0x01 << 5) +#define STB0899_OFFST_DEMOD_LOCK 5 +#define STB0899_WIDTH_DEMOD_LOCK 1 +#define STB0899_TMG_AUTO (0x01 << 4) +#define STB0899_OFFST_TMG_AUTO 4 +#define STB0899_WIDTH_TMG_AUTO 1 +#define STB0899_END_MAIN (0x01 << 3) +#define STB0899_OFFST_END_MAIN 3 +#define STB0899_WIDTH_END_MAIN 1 + +#define STB0899_LDI 0xf43b +#define STB0899_OFFST_LDI 0 +#define STB0899_WIDTH_LDI 8 + +#define STB0899_CFRM 0xf43e +#define STB0899_OFFST_CFRM 0 +#define STB0899_WIDTH_CFRM 8 + +#define STB0899_CFRL 0xf43f +#define STB0899_OFFST_CFRL 0 +#define STB0899_WIDTH_CFRL 8 + +#define STB0899_NIRM 0xf440 +#define STB0899_OFFST_NIRM 0 +#define STB0899_WIDTH_NIRM 8 + +#define STB0899_NIRL 0xf441 +#define STB0899_OFFST_NIRL 0 +#define STB0899_WIDTH_NIRL 8 + +#define STB0899_ISYMB 0xf444 +#define STB0899_QSYMB 0xf445 + +#define STB0899_SFRH 0xf446 +#define STB0899_OFFST_SFRH 0 +#define STB0899_WIDTH_SFRH 8 + +#define STB0899_SFRM 0xf447 +#define STB0899_OFFST_SFRM 0 +#define STB0899_WIDTH_SFRM 8 + +#define STB0899_SFRL 0xf448 +#define STB0899_OFFST_SFRL 4 +#define STB0899_WIDTH_SFRL 4 + +#define STB0899_SFRUPH 0xf44c +#define STB0899_SFRUPM 0xf44d +#define STB0899_SFRUPL 0xf44e + +#define STB0899_EQUAI1 0xf4e0 +#define STB0899_EQUAQ1 0xf4e1 +#define STB0899_EQUAI2 0xf4e2 +#define STB0899_EQUAQ2 0xf4e3 +#define STB0899_EQUAI3 0xf4e4 +#define STB0899_EQUAQ3 0xf4e5 +#define STB0899_EQUAI4 0xf4e6 +#define STB0899_EQUAQ4 0xf4e7 +#define STB0899_EQUAI5 0xf4e8 +#define STB0899_EQUAQ5 0xf4e9 + +#define STB0899_DSTATUS2 0xf50c +#define STB0899_DS2_TMG_AUTOSRCH (0x01 << 7) +#define STB8999_OFFST_DS2_TMG_AUTOSRCH 7 +#define STB0899_WIDTH_DS2_TMG_AUTOSRCH 1 +#define STB0899_DS2_END_MAINLOOP (0x01 << 6) +#define STB0899_OFFST_DS2_END_MAINLOOP 6 +#define STB0899_WIDTH_DS2_END_MAINLOOP 1 +#define STB0899_DS2_CFSYNC (0x01 << 5) +#define STB0899_OFFST_DS2_CFSYNC 5 +#define STB0899_WIDTH_DS2_CFSYNC 1 +#define STB0899_DS2_TMGLOCK (0x01 << 4) +#define STB0899_OFFST_DS2_TMGLOCK 4 +#define STB0899_WIDTH_DS2_TMGLOCK 1 +#define STB0899_DS2_DEMODWAIT (0x01 << 3) +#define STB0899_OFFST_DS2_DEMODWAIT 3 +#define STB0899_WIDTH_DS2_DEMODWAIT 1 +#define STB0899_DS2_FECON (0x01 << 1) +#define STB0899_OFFST_DS2_FECON 1 +#define STB0899_WIDTH_DS2_FECON 1 + +/* S1 FEC */ +#define STB0899_VSTATUS 0xf50d +#define STB0899_VSTATUS_VITERBI_ON (0x01 << 7) +#define STB0899_OFFST_VSTATUS_VITERBI_ON 7 +#define STB0899_WIDTH_VSTATUS_VITERBI_ON 1 +#define STB0899_VSTATUS_END_LOOPVIT (0x01 << 6) +#define STB0899_OFFST_VSTATUS_END_LOOPVIT 6 +#define STB0899_WIDTH_VSTATUS_END_LOOPVIT 1 +#define STB0899_VSTATUS_PRFVIT (0x01 << 4) +#define STB0899_OFFST_VSTATUS_PRFVIT 4 +#define STB0899_WIDTH_VSTATUS_PRFVIT 1 +#define STB0899_VSTATUS_LOCKEDVIT (0x01 << 3) +#define STB0899_OFFST_VSTATUS_LOCKEDVIT 3 +#define STB0899_WIDTH_VSTATUS_LOCKEDVIT 1 + +#define STB0899_VERROR 0xf50f + +#define STB0899_IQSWAP 0xf523 +#define STB0899_SYM (0x01 << 3) +#define STB0899_OFFST_SYM 3 +#define STB0899_WIDTH_SYM 1 + +#define STB0899_FECAUTO1 0xf530 +#define STB0899_DSSSRCH (0x01 << 3) +#define STB0899_OFFST_DSSSRCH 3 +#define STB0899_WIDTH_DSSSRCH 1 +#define STB0899_SYMSRCH (0x01 << 2) +#define STB0899_OFFST_SYMSRCH 2 +#define STB0899_WIDTH_SYMSRCH 1 +#define STB0899_QPSKSRCH (0x01 << 1) +#define STB0899_OFFST_QPSKSRCH 1 +#define STB0899_WIDTH_QPSKSRCH 1 +#define STB0899_BPSKSRCH (0x01 << 0) +#define STB0899_OFFST_BPSKSRCH 0 +#define STB0899_WIDTH_BPSKSRCH 1 + +#define STB0899_FECM 0xf533 +#define STB0899_FECM_NOT_DVB (0x01 << 7) +#define STB0899_OFFST_FECM_NOT_DVB 7 +#define STB0899_WIDTH_FECM_NOT_DVB 1 +#define STB0899_FECM_RSVD1 (0x07 << 4) +#define STB0899_OFFST_FECM_RSVD1 4 +#define STB0899_WIDTH_FECM_RSVD1 3 +#define STB0899_FECM_VITERBI_ON (0x01 << 3) +#define STB0899_OFFST_FECM_VITERBI_ON 3 +#define STB0899_WIDTH_FECM_VITERBI_ON 1 +#define STB0899_FECM_RSVD0 (0x01 << 2) +#define STB0899_OFFST_FECM_RSVD0 2 +#define STB0899_WIDTH_FECM_RSVD0 1 +#define STB0899_FECM_SYNCDIS (0x01 << 1) +#define STB0899_OFFST_FECM_SYNCDIS 1 +#define STB0899_WIDTH_FECM_SYNCDIS 1 +#define STB0899_FECM_SYMI (0x01 << 0) +#define STB0899_OFFST_FECM_SYMI 0 +#define STB0899_WIDTH_FECM_SYMI 1 + +#define STB0899_VTH12 0xf534 +#define STB0899_VTH23 0xf535 +#define STB0899_VTH34 0xf536 +#define STB0899_VTH56 0xf537 +#define STB0899_VTH67 0xf538 +#define STB0899_VTH78 0xf539 + +#define STB0899_PRVIT 0xf53c +#define STB0899_PR_7_8 (0x01 << 5) +#define STB0899_OFFST_PR_7_8 5 +#define STB0899_WIDTH_PR_7_8 1 +#define STB0899_PR_6_7 (0x01 << 4) +#define STB0899_OFFST_PR_6_7 4 +#define STB0899_WIDTH_PR_6_7 1 +#define STB0899_PR_5_6 (0x01 << 3) +#define STB0899_OFFST_PR_5_6 3 +#define STB0899_WIDTH_PR_5_6 1 +#define STB0899_PR_3_4 (0x01 << 2) +#define STB0899_OFFST_PR_3_4 2 +#define STB0899_WIDTH_PR_3_4 1 +#define STB0899_PR_2_3 (0x01 << 1) +#define STB0899_OFFST_PR_2_3 1 +#define STB0899_WIDTH_PR_2_3 1 +#define STB0899_PR_1_2 (0x01 << 0) +#define STB0899_OFFST_PR_1_2 0 +#define STB0899_WIDTH_PR_1_2 1 + +#define STB0899_VITSYNC 0xf53d +#define STB0899_AM (0x01 << 7) +#define STB0899_OFFST_AM 7 +#define STB0899_WIDTH_AM 1 +#define STB0899_FREEZE (0x01 << 6) +#define STB0899_OFFST_FREEZE 6 +#define STB0899_WIDTH_FREEZE 1 +#define STB0899_SN_65536 (0x03 << 4) +#define STB0899_OFFST_SN_65536 4 +#define STB0899_WIDTH_SN_65536 2 +#define STB0899_SN_16384 (0x01 << 5) +#define STB0899_OFFST_SN_16384 5 +#define STB0899_WIDTH_SN_16384 1 +#define STB0899_SN_4096 (0x01 << 4) +#define STB0899_OFFST_SN_4096 4 +#define STB0899_WIDTH_SN_4096 1 +#define STB0899_SN_1024 (0x00 << 4) +#define STB0899_OFFST_SN_1024 4 +#define STB0899_WIDTH_SN_1024 0 +#define STB0899_TO_128 (0x03 << 2) +#define STB0899_OFFST_TO_128 2 +#define STB0899_WIDTH_TO_128 2 +#define STB0899_TO_64 (0x01 << 3) +#define STB0899_OFFST_TO_64 3 +#define STB0899_WIDTH_TO_64 1 +#define STB0899_TO_32 (0x01 << 2) +#define STB0899_OFFST_TO_32 2 +#define STB0899_WIDTH_TO_32 1 +#define STB0899_TO_16 (0x00 << 2) +#define STB0899_OFFST_TO_16 2 +#define STB0899_WIDTH_TO_16 0 +#define STB0899_HYST_128 (0x03 << 1) +#define STB0899_OFFST_HYST_128 1 +#define STB0899_WIDTH_HYST_128 2 +#define STB0899_HYST_64 (0x01 << 1) +#define STB0899_OFFST_HYST_64 1 +#define STB0899_WIDTH_HYST_64 1 +#define STB0899_HYST_32 (0x01 << 0) +#define STB0899_OFFST_HYST_32 0 +#define STB0899_WIDTH_HYST_32 1 +#define STB0899_HYST_16 (0x00 << 0) +#define STB0899_OFFST_HYST_16 0 +#define STB0899_WIDTH_HYST_16 0 + +#define STB0899_RSULC 0xf548 +#define STB0899_ULDIL_ON (0x01 << 7) +#define STB0899_OFFST_ULDIL_ON 7 +#define STB0899_WIDTH_ULDIL_ON 1 +#define STB0899_ULAUTO_ON (0x01 << 6) +#define STB0899_OFFST_ULAUTO_ON 6 +#define STB0899_WIDTH_ULAUTO_ON 1 +#define STB0899_ULRS_ON (0x01 << 5) +#define STB0899_OFFST_ULRS_ON 5 +#define STB0899_WIDTH_ULRS_ON 1 +#define STB0899_ULDESCRAM_ON (0x01 << 4) +#define STB0899_OFFST_ULDESCRAM_ON 4 +#define STB0899_WIDTH_ULDESCRAM_ON 1 +#define STB0899_UL_DISABLE (0x01 << 2) +#define STB0899_OFFST_UL_DISABLE 2 +#define STB0899_WIDTH_UL_DISABLE 1 +#define STB0899_NOFTHRESHOLD (0x01 << 0) +#define STB0899_OFFST_NOFTHRESHOLD 0 +#define STB0899_WIDTH_NOFTHRESHOLD 1 + +#define STB0899_RSLLC 0xf54a +#define STB0899_DEMAPVIT 0xf583 +#define STB0899_DEMAPVIT_RSVD (0x01 << 7) +#define STB0899_OFFST_DEMAPVIT_RSVD 7 +#define STB0899_WIDTH_DEMAPVIT_RSVD 1 +#define STB0899_DEMAPVIT_KDIVIDER (0x7f << 0) +#define STB0899_OFFST_DEMAPVIT_KDIVIDER 0 +#define STB0899_WIDTH_DEMAPVIT_KDIVIDER 7 + +#define STB0899_PLPARM 0xf58c +#define STB0899_VITMAPPING (0x07 << 5) +#define STB0899_OFFST_VITMAPPING 5 +#define STB0899_WIDTH_VITMAPPING 3 +#define STB0899_VITMAPPING_BPSK (0x01 << 5) +#define STB0899_OFFST_VITMAPPING_BPSK 5 +#define STB0899_WIDTH_VITMAPPING_BPSK 1 +#define STB0899_VITMAPPING_QPSK (0x00 << 5) +#define STB0899_OFFST_VITMAPPING_QPSK 5 +#define STB0899_WIDTH_VITMAPPING_QPSK 0 +#define STB0899_VITCURPUN (0x1f << 0) +#define STB0899_OFFST_VITCURPUN 0 +#define STB0899_WIDTH_VITCURPUN 5 +#define STB0899_VITCURPUN_1_2 (0x0d << 0) +#define STB0899_VITCURPUN_2_3 (0x12 << 0) +#define STB0899_VITCURPUN_3_4 (0x15 << 0) +#define STB0899_VITCURPUN_5_6 (0x18 << 0) +#define STB0899_VITCURPUN_6_7 (0x19 << 0) +#define STB0899_VITCURPUN_7_8 (0x1a << 0) + +/* S2 DEMOD */ +#define STB0899_OFF0_DMD_STATUS 0xf300 +#define STB0899_BASE_DMD_STATUS 0x00000000 +#define STB0899_IF_AGC_LOCK (0x01 << 8) +#define STB0899_OFFST_IF_AGC_LOCK 0 +#define STB0899_WIDTH_IF_AGC_LOCK 1 + +#define STB0899_OFF0_CRL_FREQ 0xf304 +#define STB0899_BASE_CRL_FREQ 0x00000000 +#define STB0899_CARR_FREQ (0x3fffffff << 0) +#define STB0899_OFFST_CARR_FREQ 0 +#define STB0899_WIDTH_CARR_FREQ 30 + +#define STB0899_OFF0_BTR_FREQ 0xf308 +#define STB0899_BASE_BTR_FREQ 0x00000000 +#define STB0899_BTR_FREQ (0xfffffff << 0) +#define STB0899_OFFST_BTR_FREQ 0 +#define STB0899_WIDTH_BTR_FREQ 28 + +#define STB0899_OFF0_IF_AGC_GAIN 0xf30c +#define STB0899_BASE_IF_AGC_GAIN 0x00000000 +#define STB0899_IF_AGC_GAIN (0x3fff < 0) +#define STB0899_OFFST_IF_AGC_GAIN 0 +#define STB0899_WIDTH_IF_AGC_GAIN 14 + +#define STB0899_OFF0_BB_AGC_GAIN 0xf310 +#define STB0899_BASE_BB_AGC_GAIN 0x00000000 +#define STB0899_BB_AGC_GAIN (0x3fff < 0) +#define STB0899_OFFST_BB_AGC_GAIN 0 +#define STB0899_WIDTH_BB_AGC_GAIN 14 + +#define STB0899_OFF0_DC_OFFSET 0xf314 +#define STB0899_BASE_DC_OFFSET 0x00000000 +#define STB0899_I (0xff < 8) +#define STB0899_OFFST_I 8 +#define STB0899_WIDTH_I 8 +#define STB0899_Q (0xff < 0) +#define STB0899_OFFST_Q 8 +#define STB0899_WIDTH_Q 8 + +#define STB0899_OFF0_DMD_CNTRL 0xf31c +#define STB0899_BASE_DMD_CNTRL 0x00000000 +#define STB0899_ADC0_PINS1IN (0x01 << 6) +#define STB0899_OFFST_ADC0_PINS1IN 6 +#define STB0899_WIDTH_ADC0_PINS1IN 1 +#define STB0899_IN2COMP1_OFFBIN0 (0x01 << 3) +#define STB0899_OFFST_IN2COMP1_OFFBIN0 3 +#define STB0899_WIDTH_IN2COMP1_OFFBIN0 1 +#define STB0899_DC_COMP (0x01 << 2) +#define STB0899_OFFST_DC_COMP 2 +#define STB0899_WIDTH_DC_COMP 1 +#define STB0899_MODMODE (0x03 << 0) +#define STB0899_OFFST_MODMODE 0 +#define STB0899_WIDTH_MODMODE 2 + +#define STB0899_OFF0_IF_AGC_CNTRL 0xf320 +#define STB0899_BASE_IF_AGC_CNTRL 0x00000000 +#define STB0899_IF_GAIN_INIT (0x3fff << 13) +#define STB0899_OFFST_IF_GAIN_INIT 13 +#define STB0899_WIDTH_IF_GAIN_INIT 14 +#define STB0899_IF_GAIN_SENSE (0x01 << 12) +#define STB0899_OFFST_IF_GAIN_SENSE 12 +#define STB0899_WIDTH_IF_GAIN_SENSE 1 +#define STB0899_IF_LOOP_GAIN (0x0f << 8) +#define STB0899_OFFST_IF_LOOP_GAIN 8 +#define STB0899_WIDTH_IF_LOOP_GAIN 4 +#define STB0899_IF_LD_GAIN_INIT (0x01 << 7) +#define STB0899_OFFST_IF_LD_GAIN_INIT 7 +#define STB0899_WIDTH_IF_LD_GAIN_INIT 1 +#define STB0899_IF_AGC_REF (0x7f << 0) +#define STB0899_OFFST_IF_AGC_REF 0 +#define STB0899_WIDTH_IF_AGC_REF 7 + +#define STB0899_OFF0_BB_AGC_CNTRL 0xf324 +#define STB0899_BASE_BB_AGC_CNTRL 0x00000000 +#define STB0899_BB_GAIN_INIT (0x3fff << 12) +#define STB0899_OFFST_BB_GAIN_INIT 12 +#define STB0899_WIDTH_BB_GAIN_INIT 14 +#define STB0899_BB_LOOP_GAIN (0x0f << 8) +#define STB0899_OFFST_BB_LOOP_GAIN 8 +#define STB0899_WIDTH_BB_LOOP_GAIN 4 +#define STB0899_BB_LD_GAIN_INIT (0x01 << 7) +#define STB0899_OFFST_BB_LD_GAIN_INIT 7 +#define STB0899_WIDTH_BB_LD_GAIN_INIT 1 +#define STB0899_BB_AGC_REF (0x7f << 0) +#define STB0899_OFFST_BB_AGC_REF 0 +#define STB0899_WIDTH_BB_AGC_REF 7 + +#define STB0899_OFF0_CRL_CNTRL 0xf328 +#define STB0899_BASE_CRL_CNTRL 0x00000000 +#define STB0899_CRL_LOCK_CLEAR (0x01 << 5) +#define STB0899_OFFST_CRL_LOCK_CLEAR 5 +#define STB0899_WIDTH_CRL_LOCK_CLEAR 1 +#define STB0899_CRL_SWPR_CLEAR (0x01 << 4) +#define STB0899_OFFST_CRL_SWPR_CLEAR 4 +#define STB0899_WIDTH_CRL_SWPR_CLEAR 1 +#define STB0899_CRL_SWP_ENA (0x01 << 3) +#define STB0899_OFFST_CRL_SWP_ENA 3 +#define STB0899_WIDTH_CRL_SWP_ENA 1 +#define STB0899_CRL_DET_SEL (0x01 << 2) +#define STB0899_OFFST_CRL_DET_SEL 2 +#define STB0899_WIDTH_CRL_DET_SEL 1 +#define STB0899_CRL_SENSE (0x01 << 1) +#define STB0899_OFFST_CRL_SENSE 1 +#define STB0899_WIDTH_CRL_SENSE 1 +#define STB0899_CRL_PHSERR_CLEAR (0x01 << 0) +#define STB0899_OFFST_CRL_PHSERR_CLEAR 0 +#define STB0899_WIDTH_CRL_PHSERR_CLEAR 1 + +#define STB0899_OFF0_CRL_PHS_INIT 0xf32c +#define STB0899_BASE_CRL_PHS_INIT 0x00000000 +#define STB0899_CRL_PHS_INIT_31 (0x1 << 30) +#define STB0899_OFFST_CRL_PHS_INIT_31 30 +#define STB0899_WIDTH_CRL_PHS_INIT_31 1 +#define STB0899_CRL_LD_INIT_PHASE (0x1 << 24) +#define STB0899_OFFST_CRL_LD_INIT_PHASE 24 +#define STB0899_WIDTH_CRL_LD_INIT_PHASE 1 +#define STB0899_CRL_INIT_PHASE (0xffffff << 0) +#define STB0899_OFFST_CRL_INIT_PHASE 0 +#define STB0899_WIDTH_CRL_INIT_PHASE 24 + +#define STB0899_OFF0_CRL_FREQ_INIT 0xf330 +#define STB0899_BASE_CRL_FREQ_INIT 0x00000000 +#define STB0899_CRL_FREQ_INIT_31 (0x1 << 30) +#define STB0899_OFFST_CRL_FREQ_INIT_31 30 +#define STB0899_WIDTH_CRL_FREQ_INIT_31 1 +#define STB0899_CRL_LD_FREQ_INIT (0x1 << 24) +#define STB0899_OFFST_CRL_LD_FREQ_INIT 24 +#define STB0899_WIDTH_CRL_LD_FREQ_INIT 1 +#define STB0899_CRL_FREQ_INIT (0xffffff << 0) +#define STB0899_OFFST_CRL_FREQ_INIT 0 +#define STB0899_WIDTH_CRL_FREQ_INIT 24 + +#define STB0899_OFF0_CRL_LOOP_GAIN 0xf334 +#define STB0899_BASE_CRL_LOOP_GAIN 0x00000000 +#define STB0899_KCRL2_RSHFT (0xf << 16) +#define STB0899_OFFST_KCRL2_RSHFT 16 +#define STB0899_WIDTH_KCRL2_RSHFT 4 +#define STB0899_KCRL1 (0xf << 12) +#define STB0899_OFFST_KCRL1 12 +#define STB0899_WIDTH_KCRL1 4 +#define STB0899_KCRL1_RSHFT (0xf << 8) +#define STB0899_OFFST_KCRL1_RSHFT 8 +#define STB0899_WIDTH_KCRL1_RSHFT 4 +#define STB0899_KCRL0 (0xf << 4) +#define STB0899_OFFST_KCRL0 4 +#define STB0899_WIDTH_KCRL0 4 +#define STB0899_KCRL0_RSHFT (0xf << 0) +#define STB0899_OFFST_KCRL0_RSHFT 0 +#define STB0899_WIDTH_KCRL0_RSHFT 4 + +#define STB0899_OFF0_CRL_NOM_FREQ 0xf338 +#define STB0899_BASE_CRL_NOM_FREQ 0x00000000 +#define STB0899_CRL_NOM_FREQ (0x3fffffff << 0) +#define STB0899_OFFST_CRL_NOM_FREQ 0 +#define STB0899_WIDTH_CRL_NOM_FREQ 30 + +#define STB0899_OFF0_CRL_SWP_RATE 0xf33c +#define STB0899_BASE_CRL_SWP_RATE 0x00000000 +#define STB0899_CRL_SWP_RATE (0x3fffffff << 0) +#define STB0899_OFFST_CRL_SWP_RATE 0 +#define STB0899_WIDTH_CRL_SWP_RATE 30 + +#define STB0899_OFF0_CRL_MAX_SWP 0xf340 +#define STB0899_BASE_CRL_MAX_SWP 0x00000000 +#define STB0899_CRL_MAX_SWP (0x3fffffff << 0) +#define STB0899_OFFST_CRL_MAX_SWP 0 +#define STB0899_WIDTH_CRL_MAX_SWP 30 + +#define STB0899_OFF0_CRL_LK_CNTRL 0xf344 +#define STB0899_BASE_CRL_LK_CNTRL 0x00000000 + +#define STB0899_OFF0_DECIM_CNTRL 0xf348 +#define STB0899_BASE_DECIM_CNTRL 0x00000000 +#define STB0899_BAND_LIMIT_B (0x01 << 5) +#define STB0899_OFFST_BAND_LIMIT_B 5 +#define STB0899_WIDTH_BAND_LIMIT_B 1 +#define STB0899_WIN_SEL (0x03 << 3) +#define STB0899_OFFST_WIN_SEL 3 +#define STB0899_WIDTH_WIN_SEL 2 +#define STB0899_DECIM_RATE (0x07 << 0) +#define STB0899_OFFST_DECIM_RATE 0 +#define STB0899_WIDTH_DECIM_RATE 3 + +#define STB0899_OFF0_BTR_CNTRL 0xf34c +#define STB0899_BASE_BTR_CNTRL 0x00000000 +#define STB0899_BTR_FREQ_CORR (0x7ff << 4) +#define STB0899_OFFST_BTR_FREQ_CORR 4 +#define STB0899_WIDTH_BTR_FREQ_CORR 11 +#define STB0899_BTR_CLR_LOCK (0x01 << 3) +#define STB0899_OFFST_BTR_CLR_LOCK 3 +#define STB0899_WIDTH_BTR_CLR_LOCK 1 +#define STB0899_BTR_SENSE (0x01 << 2) +#define STB0899_OFFST_BTR_SENSE 2 +#define STB0899_WIDTH_BTR_SENSE 1 +#define STB0899_BTR_ERR_ENA (0x01 << 1) +#define STB0899_OFFST_BTR_ERR_ENA 1 +#define STB0899_WIDTH_BTR_ERR_ENA 1 +#define STB0899_INTRP_PHS_SENSE (0x01 << 0) +#define STB0899_OFFST_INTRP_PHS_SENSE 0 +#define STB0899_WIDTH_INTRP_PHS_SENSE 1 + +#define STB0899_OFF0_BTR_LOOP_GAIN 0xf350 +#define STB0899_BASE_BTR_LOOP_GAIN 0x00000000 +#define STB0899_KBTR2_RSHFT (0x0f << 16) +#define STB0899_OFFST_KBTR2_RSHFT 16 +#define STB0899_WIDTH_KBTR2_RSHFT 4 +#define STB0899_KBTR1 (0x0f << 12) +#define STB0899_OFFST_KBTR1 12 +#define STB0899_WIDTH_KBTR1 4 +#define STB0899_KBTR1_RSHFT (0x0f << 8) +#define STB0899_OFFST_KBTR1_RSHFT 8 +#define STB0899_WIDTH_KBTR1_RSHFT 4 +#define STB0899_KBTR0 (0x0f << 4) +#define STB0899_OFFST_KBTR0 4 +#define STB0899_WIDTH_KBTR0 4 +#define STB0899_KBTR0_RSHFT (0x0f << 0) +#define STB0899_OFFST_KBTR0_RSHFT 0 +#define STB0899_WIDTH_KBTR0_RSHFT 4 + +#define STB0899_OFF0_BTR_PHS_INIT 0xf354 +#define STB0899_BASE_BTR_PHS_INIT 0x00000000 +#define STB0899_BTR_LD_PHASE_INIT (0x01 << 28) +#define STB0899_OFFST_BTR_LD_PHASE_INIT 28 +#define STB0899_WIDTH_BTR_LD_PHASE_INIT 1 +#define STB0899_BTR_INIT_PHASE (0xfffffff << 0) +#define STB0899_OFFST_BTR_INIT_PHASE 0 +#define STB0899_WIDTH_BTR_INIT_PHASE 28 + +#define STB0899_OFF0_BTR_FREQ_INIT 0xf358 +#define STB0899_BASE_BTR_FREQ_INIT 0x00000000 +#define STB0899_BTR_LD_FREQ_INIT (1 << 28) +#define STB0899_OFFST_BTR_LD_FREQ_INIT 28 +#define STB0899_WIDTH_BTR_LD_FREQ_INIT 1 +#define STB0899_BTR_FREQ_INIT (0xfffffff << 0) +#define STB0899_OFFST_BTR_FREQ_INIT 0 +#define STB0899_WIDTH_BTR_FREQ_INIT 28 + +#define STB0899_OFF0_BTR_NOM_FREQ 0xf35c +#define STB0899_BASE_BTR_NOM_FREQ 0x00000000 +#define STB0899_BTR_NOM_FREQ (0xfffffff << 0) +#define STB0899_OFFST_BTR_NOM_FREQ 0 +#define STB0899_WIDTH_BTR_NOM_FREQ 28 + +#define STB0899_OFF0_BTR_LK_CNTRL 0xf360 +#define STB0899_BASE_BTR_LK_CNTRL 0x00000000 +#define STB0899_BTR_MIN_ENERGY (0x0f << 24) +#define STB0899_OFFST_BTR_MIN_ENERGY 24 +#define STB0899_WIDTH_BTR_MIN_ENERGY 4 +#define STB0899_BTR_LOCK_TH_LO (0xff << 16) +#define STB0899_OFFST_BTR_LOCK_TH_LO 16 +#define STB0899_WIDTH_BTR_LOCK_TH_LO 8 +#define STB0899_BTR_LOCK_TH_HI (0xff << 8) +#define STB0899_OFFST_BTR_LOCK_TH_HI 8 +#define STB0899_WIDTH_BTR_LOCK_TH_HI 8 +#define STB0899_BTR_LOCK_GAIN (0x03 << 6) +#define STB0899_OFFST_BTR_LOCK_GAIN 6 +#define STB0899_WIDTH_BTR_LOCK_GAIN 2 +#define STB0899_BTR_LOCK_LEAK (0x3f << 0) +#define STB0899_OFFST_BTR_LOCK_LEAK 0 +#define STB0899_WIDTH_BTR_LOCK_LEAK 6 + +#define STB0899_OFF0_DECN_CNTRL 0xf364 +#define STB0899_BASE_DECN_CNTRL 0x00000000 + +#define STB0899_OFF0_TP_CNTRL 0xf368 +#define STB0899_BASE_TP_CNTRL 0x00000000 + +#define STB0899_OFF0_TP_BUF_STATUS 0xf36c +#define STB0899_BASE_TP_BUF_STATUS 0x00000000 +#define STB0899_TP_BUFFER_FULL (1 << 0) + +#define STB0899_OFF0_DC_ESTIM 0xf37c +#define STB0899_BASE_DC_ESTIM 0x0000 +#define STB0899_I_DC_ESTIMATE (0xff << 8) +#define STB0899_OFFST_I_DC_ESTIMATE 8 +#define STB0899_WIDTH_I_DC_ESTIMATE 8 +#define STB0899_Q_DC_ESTIMATE (0xff << 0) +#define STB0899_OFFST_Q_DC_ESTIMATE 0 +#define STB0899_WIDTH_Q_DC_ESTIMATE 8 + +#define STB0899_OFF0_FLL_CNTRL 0xf310 +#define STB0899_BASE_FLL_CNTRL 0x00000020 +#define STB0899_CRL_FLL_ACC (0x01 << 4) +#define STB0899_OFFST_CRL_FLL_ACC 4 +#define STB0899_WIDTH_CRL_FLL_ACC 1 +#define STB0899_FLL_AVG_PERIOD (0x0f << 0) +#define STB0899_OFFST_FLL_AVG_PERIOD 0 +#define STB0899_WIDTH_FLL_AVG_PERIOD 4 + +#define STB0899_OFF0_FLL_FREQ_WD 0xf314 +#define STB0899_BASE_FLL_FREQ_WD 0x00000020 +#define STB0899_FLL_FREQ_WD (0xffffffff << 0) +#define STB0899_OFFST_FLL_FREQ_WD 0 +#define STB0899_WIDTH_FLL_FREQ_WD 32 + +#define STB0899_OFF0_ANTI_ALIAS_SEL 0xf358 +#define STB0899_BASE_ANTI_ALIAS_SEL 0x00000020 +#define STB0899_ANTI_ALIAS_SELB (0x03 << 0) +#define STB0899_OFFST_ANTI_ALIAS_SELB 0 +#define STB0899_WIDTH_ANTI_ALIAS_SELB 2 + +#define STB0899_OFF0_RRC_ALPHA 0xf35c +#define STB0899_BASE_RRC_ALPHA 0x00000020 +#define STB0899_RRC_ALPHA (0x03 << 0) +#define STB0899_OFFST_RRC_ALPHA 0 +#define STB0899_WIDTH_RRC_ALPHA 2 + +#define STB0899_OFF0_DC_ADAPT_LSHFT 0xf360 +#define STB0899_BASE_DC_ADAPT_LSHFT 0x00000020 +#define STB0899_DC_ADAPT_LSHFT (0x077 << 0) +#define STB0899_OFFST_DC_ADAPT_LSHFT 0 +#define STB0899_WIDTH_DC_ADAPT_LSHFT 3 + +#define STB0899_OFF0_IMB_OFFSET 0xf364 +#define STB0899_BASE_IMB_OFFSET 0x00000020 +#define STB0899_PHS_IMB_COMP (0xff << 8) +#define STB0899_OFFST_PHS_IMB_COMP 8 +#define STB0899_WIDTH_PHS_IMB_COMP 8 +#define STB0899_AMPL_IMB_COMP (0xff << 0) +#define STB0899_OFFST_AMPL_IMB_COMP 0 +#define STB0899_WIDTH_AMPL_IMB_COMP 8 + +#define STB0899_OFF0_IMB_ESTIMATE 0xf368 +#define STB0899_BASE_IMB_ESTIMATE 0x00000020 +#define STB0899_PHS_IMB_ESTIMATE (0xff << 8) +#define STB0899_OFFST_PHS_IMB_ESTIMATE 8 +#define STB0899_WIDTH_PHS_IMB_ESTIMATE 8 +#define STB0899_AMPL_IMB_ESTIMATE (0xff << 0) +#define STB0899_OFFST_AMPL_IMB_ESTIMATE 0 +#define STB0899_WIDTH_AMPL_IMB_ESTIMATE 8 + +#define STB0899_OFF0_IMB_CNTRL 0xf36c +#define STB0899_BASE_IMB_CNTRL 0x00000020 +#define STB0899_PHS_ADAPT_LSHFT (0x07 << 4) +#define STB0899_OFFST_PHS_ADAPT_LSHFT 4 +#define STB0899_WIDTH_PHS_ADAPT_LSHFT 3 +#define STB0899_AMPL_ADAPT_LSHFT (0x07 << 1) +#define STB0899_OFFST_AMPL_ADAPT_LSHFT 1 +#define STB0899_WIDTH_AMPL_ADAPT_LSHFT 3 +#define STB0899_IMB_COMP (0x01 << 0) +#define STB0899_OFFST_IMB_COMP 0 +#define STB0899_WIDTH_IMB_COMP 1 + +#define STB0899_OFF0_IF_AGC_CNTRL2 0xf374 +#define STB0899_BASE_IF_AGC_CNTRL2 0x00000020 +#define STB0899_IF_AGC_LOCK_TH (0xff << 11) +#define STB0899_OFFST_IF_AGC_LOCK_TH 11 +#define STB0899_WIDTH_IF_AGC_LOCK_TH 8 +#define STB0899_IF_AGC_SD_DIV (0xff << 3) +#define STB0899_OFFST_IF_AGC_SD_DIV 3 +#define STB0899_WIDTH_IF_AGC_SD_DIV 8 +#define STB0899_IF_AGC_DUMP_PER (0x07 << 0) +#define STB0899_OFFST_IF_AGC_DUMP_PER 0 +#define STB0899_WIDTH_IF_AGC_DUMP_PER 3 + +#define STB0899_OFF0_DMD_CNTRL2 0xf378 +#define STB0899_BASE_DMD_CNTRL2 0x00000020 +#define STB0899_SPECTRUM_INVERT (0x01 << 2) +#define STB0899_OFFST_SPECTRUM_INVERT 2 +#define STB0899_WIDTH_SPECTRUM_INVERT 1 +#define STB0899_AGC_MODE (0x01 << 1) +#define STB0899_OFFST_AGC_MODE 1 +#define STB0899_WIDTH_AGC_MODE 1 +#define STB0899_CRL_FREQ_ADJ (0x01 << 0) +#define STB0899_OFFST_CRL_FREQ_ADJ 0 +#define STB0899_WIDTH_CRL_FREQ_ADJ 1 + +#define STB0899_OFF0_TP_BUFFER 0xf300 +#define STB0899_BASE_TP_BUFFER 0x00000040 +#define STB0899_TP_BUFFER_IN (0xffff << 0) +#define STB0899_OFFST_TP_BUFFER_IN 0 +#define STB0899_WIDTH_TP_BUFFER_IN 16 + +#define STB0899_OFF0_TP_BUFFER1 0xf304 +#define STB0899_BASE_TP_BUFFER1 0x00000040 +#define STB0899_OFF0_TP_BUFFER2 0xf308 +#define STB0899_BASE_TP_BUFFER2 0x00000040 +#define STB0899_OFF0_TP_BUFFER3 0xf30c +#define STB0899_BASE_TP_BUFFER3 0x00000040 +#define STB0899_OFF0_TP_BUFFER4 0xf310 +#define STB0899_BASE_TP_BUFFER4 0x00000040 +#define STB0899_OFF0_TP_BUFFER5 0xf314 +#define STB0899_BASE_TP_BUFFER5 0x00000040 +#define STB0899_OFF0_TP_BUFFER6 0xf318 +#define STB0899_BASE_TP_BUFFER6 0x00000040 +#define STB0899_OFF0_TP_BUFFER7 0xf31c +#define STB0899_BASE_TP_BUFFER7 0x00000040 +#define STB0899_OFF0_TP_BUFFER8 0xf320 +#define STB0899_BASE_TP_BUFFER8 0x00000040 +#define STB0899_OFF0_TP_BUFFER9 0xf324 +#define STB0899_BASE_TP_BUFFER9 0x00000040 +#define STB0899_OFF0_TP_BUFFER10 0xf328 +#define STB0899_BASE_TP_BUFFER10 0x00000040 +#define STB0899_OFF0_TP_BUFFER11 0xf32c +#define STB0899_BASE_TP_BUFFER11 0x00000040 +#define STB0899_OFF0_TP_BUFFER12 0xf330 +#define STB0899_BASE_TP_BUFFER12 0x00000040 +#define STB0899_OFF0_TP_BUFFER13 0xf334 +#define STB0899_BASE_TP_BUFFER13 0x00000040 +#define STB0899_OFF0_TP_BUFFER14 0xf338 +#define STB0899_BASE_TP_BUFFER14 0x00000040 +#define STB0899_OFF0_TP_BUFFER15 0xf33c +#define STB0899_BASE_TP_BUFFER15 0x00000040 +#define STB0899_OFF0_TP_BUFFER16 0xf340 +#define STB0899_BASE_TP_BUFFER16 0x00000040 +#define STB0899_OFF0_TP_BUFFER17 0xf344 +#define STB0899_BASE_TP_BUFFER17 0x00000040 +#define STB0899_OFF0_TP_BUFFER18 0xf348 +#define STB0899_BASE_TP_BUFFER18 0x00000040 +#define STB0899_OFF0_TP_BUFFER19 0xf34c +#define STB0899_BASE_TP_BUFFER19 0x00000040 +#define STB0899_OFF0_TP_BUFFER20 0xf350 +#define STB0899_BASE_TP_BUFFER20 0x00000040 +#define STB0899_OFF0_TP_BUFFER21 0xf354 +#define STB0899_BASE_TP_BUFFER21 0x00000040 +#define STB0899_OFF0_TP_BUFFER22 0xf358 +#define STB0899_BASE_TP_BUFFER22 0x00000040 +#define STB0899_OFF0_TP_BUFFER23 0xf35c +#define STB0899_BASE_TP_BUFFER23 0x00000040 +#define STB0899_OFF0_TP_BUFFER24 0xf360 +#define STB0899_BASE_TP_BUFFER24 0x00000040 +#define STB0899_OFF0_TP_BUFFER25 0xf364 +#define STB0899_BASE_TP_BUFFER25 0x00000040 +#define STB0899_OFF0_TP_BUFFER26 0xf368 +#define STB0899_BASE_TP_BUFFER26 0x00000040 +#define STB0899_OFF0_TP_BUFFER27 0xf36c +#define STB0899_BASE_TP_BUFFER27 0x00000040 +#define STB0899_OFF0_TP_BUFFER28 0xf370 +#define STB0899_BASE_TP_BUFFER28 0x00000040 +#define STB0899_OFF0_TP_BUFFER29 0xf374 +#define STB0899_BASE_TP_BUFFER29 0x00000040 +#define STB0899_OFF0_TP_BUFFER30 0xf378 +#define STB0899_BASE_TP_BUFFER30 0x00000040 +#define STB0899_OFF0_TP_BUFFER31 0xf37c +#define STB0899_BASE_TP_BUFFER31 0x00000040 +#define STB0899_OFF0_TP_BUFFER32 0xf300 +#define STB0899_BASE_TP_BUFFER32 0x00000060 +#define STB0899_OFF0_TP_BUFFER33 0xf304 +#define STB0899_BASE_TP_BUFFER33 0x00000060 +#define STB0899_OFF0_TP_BUFFER34 0xf308 +#define STB0899_BASE_TP_BUFFER34 0x00000060 +#define STB0899_OFF0_TP_BUFFER35 0xf30c +#define STB0899_BASE_TP_BUFFER35 0x00000060 +#define STB0899_OFF0_TP_BUFFER36 0xf310 +#define STB0899_BASE_TP_BUFFER36 0x00000060 +#define STB0899_OFF0_TP_BUFFER37 0xf314 +#define STB0899_BASE_TP_BUFFER37 0x00000060 +#define STB0899_OFF0_TP_BUFFER38 0xf318 +#define STB0899_BASE_TP_BUFFER38 0x00000060 +#define STB0899_OFF0_TP_BUFFER39 0xf31c +#define STB0899_BASE_TP_BUFFER39 0x00000060 +#define STB0899_OFF0_TP_BUFFER40 0xf320 +#define STB0899_BASE_TP_BUFFER40 0x00000060 +#define STB0899_OFF0_TP_BUFFER41 0xf324 +#define STB0899_BASE_TP_BUFFER41 0x00000060 +#define STB0899_OFF0_TP_BUFFER42 0xf328 +#define STB0899_BASE_TP_BUFFER42 0x00000060 +#define STB0899_OFF0_TP_BUFFER43 0xf32c +#define STB0899_BASE_TP_BUFFER43 0x00000060 +#define STB0899_OFF0_TP_BUFFER44 0xf330 +#define STB0899_BASE_TP_BUFFER44 0x00000060 +#define STB0899_OFF0_TP_BUFFER45 0xf334 +#define STB0899_BASE_TP_BUFFER45 0x00000060 +#define STB0899_OFF0_TP_BUFFER46 0xf338 +#define STB0899_BASE_TP_BUFFER46 0x00000060 +#define STB0899_OFF0_TP_BUFFER47 0xf33c +#define STB0899_BASE_TP_BUFFER47 0x00000060 +#define STB0899_OFF0_TP_BUFFER48 0xf340 +#define STB0899_BASE_TP_BUFFER48 0x00000060 +#define STB0899_OFF0_TP_BUFFER49 0xf344 +#define STB0899_BASE_TP_BUFFER49 0x00000060 +#define STB0899_OFF0_TP_BUFFER50 0xf348 +#define STB0899_BASE_TP_BUFFER50 0x00000060 +#define STB0899_OFF0_TP_BUFFER51 0xf34c +#define STB0899_BASE_TP_BUFFER51 0x00000060 +#define STB0899_OFF0_TP_BUFFER52 0xf350 +#define STB0899_BASE_TP_BUFFER52 0x00000060 +#define STB0899_OFF0_TP_BUFFER53 0xf354 +#define STB0899_BASE_TP_BUFFER53 0x00000060 +#define STB0899_OFF0_TP_BUFFER54 0xf358 +#define STB0899_BASE_TP_BUFFER54 0x00000060 +#define STB0899_OFF0_TP_BUFFER55 0xf35c +#define STB0899_BASE_TP_BUFFER55 0x00000060 +#define STB0899_OFF0_TP_BUFFER56 0xf360 +#define STB0899_BASE_TP_BUFFER56 0x00000060 +#define STB0899_OFF0_TP_BUFFER57 0xf364 +#define STB0899_BASE_TP_BUFFER57 0x00000060 +#define STB0899_OFF0_TP_BUFFER58 0xf368 +#define STB0899_BASE_TP_BUFFER58 0x00000060 +#define STB0899_OFF0_TP_BUFFER59 0xf36c +#define STB0899_BASE_TP_BUFFER59 0x00000060 +#define STB0899_OFF0_TP_BUFFER60 0xf370 +#define STB0899_BASE_TP_BUFFER60 0x00000060 +#define STB0899_OFF0_TP_BUFFER61 0xf374 +#define STB0899_BASE_TP_BUFFER61 0x00000060 +#define STB0899_OFF0_TP_BUFFER62 0xf378 +#define STB0899_BASE_TP_BUFFER62 0x00000060 +#define STB0899_OFF0_TP_BUFFER63 0xf37c +#define STB0899_BASE_TP_BUFFER63 0x00000060 + +#define STB0899_OFF0_RESET_CNTRL 0xf300 +#define STB0899_BASE_RESET_CNTRL 0x00000400 +#define STB0899_DVBS2_RESET (0x01 << 0) +#define STB0899_OFFST_DVBS2_RESET 0 +#define STB0899_WIDTH_DVBS2_RESET 1 + +#define STB0899_OFF0_ACM_ENABLE 0xf304 +#define STB0899_BASE_ACM_ENABLE 0x00000400 +#define STB0899_ACM_ENABLE 1 + +#define STB0899_OFF0_DESCR_CNTRL 0xf30c +#define STB0899_BASE_DESCR_CNTRL 0x00000400 +#define STB0899_OFFST_DESCR_CNTRL 0 +#define STB0899_WIDTH_DESCR_CNTRL 16 + +#define STB0899_OFF0_UWP_CNTRL1 0xf320 +#define STB0899_BASE_UWP_CNTRL1 0x00000400 +#define STB0899_UWP_TH_SOF (0x7fff << 11) +#define STB0899_OFFST_UWP_TH_SOF 11 +#define STB0899_WIDTH_UWP_TH_SOF 15 +#define STB0899_UWP_ESN0_QUANT (0xff << 3) +#define STB0899_OFFST_UWP_ESN0_QUANT 3 +#define STB0899_WIDTH_UWP_ESN0_QUANT 8 +#define STB0899_UWP_ESN0_AVE (0x03 << 1) +#define STB0899_OFFST_UWP_ESN0_AVE 1 +#define STB0899_WIDTH_UWP_ESN0_AVE 2 +#define STB0899_UWP_START (0x01 << 0) +#define STB0899_OFFST_UWP_START 0 +#define STB0899_WIDTH_UWP_START 1 + +#define STB0899_OFF0_UWP_CNTRL2 0xf324 +#define STB0899_BASE_UWP_CNTRL2 0x00000400 +#define STB0899_UWP_MISS_TH (0xff << 16) +#define STB0899_OFFST_UWP_MISS_TH 16 +#define STB0899_WIDTH_UWP_MISS_TH 8 +#define STB0899_FE_FINE_TRK (0xff << 8) +#define STB0899_OFFST_FE_FINE_TRK 8 +#define STB0899_WIDTH_FE_FINE_TRK 8 +#define STB0899_FE_COARSE_TRK (0xff << 0) +#define STB0899_OFFST_FE_COARSE_TRK 0 +#define STB0899_WIDTH_FE_COARSE_TRK 8 + +#define STB0899_OFF0_UWP_STAT1 0xf328 +#define STB0899_BASE_UWP_STAT1 0x00000400 +#define STB0899_UWP_STATE (0x03ff << 15) +#define STB0899_OFFST_UWP_STATE 15 +#define STB0899_WIDTH_UWP_STATE 10 +#define STB0899_UW_MAX_PEAK (0x7fff << 0) +#define STB0899_OFFST_UW_MAX_PEAK 0 +#define STB0899_WIDTH_UW_MAX_PEAK 15 + +#define STB0899_OFF0_UWP_STAT2 0xf32c +#define STB0899_BASE_UWP_STAT2 0x00000400 +#define STB0899_ESNO_EST (0x07ffff << 7) +#define STB0899_OFFST_ESN0_EST 7 +#define STB0899_WIDTH_ESN0_EST 19 +#define STB0899_UWP_DECODE_MOD (0x7f << 0) +#define STB0899_OFFST_UWP_DECODE_MOD 0 +#define STB0899_WIDTH_UWP_DECODE_MOD 7 + +#define STB0899_OFF0_DMD_CORE_ID 0xf334 +#define STB0899_BASE_DMD_CORE_ID 0x00000400 +#define STB0899_CORE_ID (0xffffffff << 0) +#define STB0899_OFFST_CORE_ID 0 +#define STB0899_WIDTH_CORE_ID 32 + +#define STB0899_OFF0_DMD_VERSION_ID 0xf33c +#define STB0899_BASE_DMD_VERSION_ID 0x00000400 +#define STB0899_VERSION_ID (0xff << 0) +#define STB0899_OFFST_VERSION_ID 0 +#define STB0899_WIDTH_VERSION_ID 8 + +#define STB0899_OFF0_DMD_STAT2 0xf340 +#define STB0899_BASE_DMD_STAT2 0x00000400 +#define STB0899_CSM_LOCK (0x01 << 1) +#define STB0899_OFFST_CSM_LOCK 1 +#define STB0899_WIDTH_CSM_LOCK 1 +#define STB0899_UWP_LOCK (0x01 << 0) +#define STB0899_OFFST_UWP_LOCK 0 +#define STB0899_WIDTH_UWP_LOCK 1 + +#define STB0899_OFF0_FREQ_ADJ_SCALE 0xf344 +#define STB0899_BASE_FREQ_ADJ_SCALE 0x00000400 +#define STB0899_FREQ_ADJ_SCALE (0x0fff << 0) +#define STB0899_OFFST_FREQ_ADJ_SCALE 0 +#define STB0899_WIDTH_FREQ_ADJ_SCALE 12 + +#define STB0899_OFF0_UWP_CNTRL3 0xf34c +#define STB0899_BASE_UWP_CNTRL3 0x00000400 +#define STB0899_UWP_TH_TRACK (0x7fff << 15) +#define STB0899_OFFST_UWP_TH_TRACK 15 +#define STB0899_WIDTH_UWP_TH_TRACK 15 +#define STB0899_UWP_TH_ACQ (0x7fff << 0) +#define STB0899_OFFST_UWP_TH_ACQ 0 +#define STB0899_WIDTH_UWP_TH_ACQ 15 + +#define STB0899_OFF0_SYM_CLK_SEL 0xf350 +#define STB0899_BASE_SYM_CLK_SEL 0x00000400 +#define STB0899_SYM_CLK_SEL (0x03 << 0) +#define STB0899_OFFST_SYM_CLK_SEL 0 +#define STB0899_WIDTH_SYM_CLK_SEL 2 + +#define STB0899_OFF0_SOF_SRCH_TO 0xf354 +#define STB0899_BASE_SOF_SRCH_TO 0x00000400 +#define STB0899_SOF_SEARCH_TIMEOUT (0x3fffff << 0) +#define STB0899_OFFST_SOF_SEARCH_TIMEOUT 0 +#define STB0899_WIDTH_SOF_SEARCH_TIMEOUT 22 + +#define STB0899_OFF0_ACQ_CNTRL1 0xf358 +#define STB0899_BASE_ACQ_CNTRL1 0x00000400 +#define STB0899_FE_FINE_ACQ (0xff << 8) +#define STB0899_OFFST_FE_FINE_ACQ 8 +#define STB0899_WIDTH_FE_FINE_ACQ 8 +#define STB0899_FE_COARSE_ACQ (0xff << 0) +#define STB0899_OFFST_FE_COARSE_ACQ 0 +#define STB0899_WIDTH_FE_COARSE_ACQ 8 + +#define STB0899_OFF0_ACQ_CNTRL2 0xf35c +#define STB0899_BASE_ACQ_CNTRL2 0x00000400 +#define STB0899_ZIGZAG (0x01 << 25) +#define STB0899_OFFST_ZIGZAG 25 +#define STB0899_WIDTH_ZIGZAG 1 +#define STB0899_NUM_STEPS (0xff << 17) +#define STB0899_OFFST_NUM_STEPS 17 +#define STB0899_WIDTH_NUM_STEPS 8 +#define STB0899_FREQ_STEPSIZE (0x1ffff << 0) +#define STB0899_OFFST_FREQ_STEPSIZE 0 +#define STB0899_WIDTH_FREQ_STEPSIZE 17 + +#define STB0899_OFF0_ACQ_CNTRL3 0xf360 +#define STB0899_BASE_ACQ_CNTRL3 0x00000400 +#define STB0899_THRESHOLD_SCL (0x3f << 23) +#define STB0899_OFFST_THRESHOLD_SCL 23 +#define STB0899_WIDTH_THRESHOLD_SCL 6 +#define STB0899_UWP_TH_SRCH (0x7fff << 8) +#define STB0899_OFFST_UWP_TH_SRCH 8 +#define STB0899_WIDTH_UWP_TH_SRCH 15 +#define STB0899_AUTO_REACQUIRE (0x01 << 7) +#define STB0899_OFFST_AUTO_REACQUIRE 7 +#define STB0899_WIDTH_AUTO_REACQUIRE 1 +#define STB0899_TRACK_LOCK_SEL (0x01 << 6) +#define STB0899_OFFST_TRACK_LOCK_SEL 6 +#define STB0899_WIDTH_TRACK_LOCK_SEL 1 +#define STB0899_ACQ_SEARCH_MODE (0x03 << 4) +#define STB0899_OFFST_ACQ_SEARCH_MODE 4 +#define STB0899_WIDTH_ACQ_SEARCH_MODE 2 +#define STB0899_CONFIRM_FRAMES (0x0f << 0) +#define STB0899_OFFST_CONFIRM_FRAMES 0 +#define STB0899_WIDTH_CONFIRM_FRAMES 4 + +#define STB0899_OFF0_FE_SETTLE 0xf364 +#define STB0899_BASE_FE_SETTLE 0x00000400 +#define STB0899_SETTLING_TIME (0x3fffff << 0) +#define STB0899_OFFST_SETTLING_TIME 0 +#define STB0899_WIDTH_SETTLING_TIME 22 + +#define STB0899_OFF0_AC_DWELL 0xf368 +#define STB0899_BASE_AC_DWELL 0x00000400 +#define STB0899_DWELL_TIME (0x3fffff << 0) +#define STB0899_OFFST_DWELL_TIME 0 +#define STB0899_WIDTH_DWELL_TIME 22 + +#define STB0899_OFF0_ACQUIRE_TRIG 0xf36c +#define STB0899_BASE_ACQUIRE_TRIG 0x00000400 +#define STB0899_ACQUIRE (0x01 << 0) +#define STB0899_OFFST_ACQUIRE 0 +#define STB0899_WIDTH_ACQUIRE 1 + +#define STB0899_OFF0_LOCK_LOST 0xf370 +#define STB0899_BASE_LOCK_LOST 0x00000400 +#define STB0899_LOCK_LOST (0x01 << 0) +#define STB0899_OFFST_LOCK_LOST 0 +#define STB0899_WIDTH_LOCK_LOST 1 + +#define STB0899_OFF0_ACQ_STAT1 0xf374 +#define STB0899_BASE_ACQ_STAT1 0x00000400 +#define STB0899_STEP_FREQ (0x1fffff << 11) +#define STB0899_OFFST_STEP_FREQ 11 +#define STB0899_WIDTH_STEP_FREQ 21 +#define STB0899_ACQ_STATE (0x07 << 8) +#define STB0899_OFFST_ACQ_STATE 8 +#define STB0899_WIDTH_ACQ_STATE 3 +#define STB0899_UW_DETECT_COUNT (0xff << 0) +#define STB0899_OFFST_UW_DETECT_COUNT 0 +#define STB0899_WIDTH_UW_DETECT_COUNT 8 + +#define STB0899_OFF0_ACQ_TIMEOUT 0xf378 +#define STB0899_BASE_ACQ_TIMEOUT 0x00000400 +#define STB0899_ACQ_TIMEOUT (0x3fffff << 0) +#define STB0899_OFFST_ACQ_TIMEOUT 0 +#define STB0899_WIDTH_ACQ_TIMEOUT 22 + +#define STB0899_OFF0_ACQ_TIME 0xf37c +#define STB0899_BASE_ACQ_TIME 0x00000400 +#define STB0899_ACQ_TIME_SYM (0xffffff << 0) +#define STB0899_OFFST_ACQ_TIME_SYM 0 +#define STB0899_WIDTH_ACQ_TIME_SYM 24 + +#define STB0899_OFF0_FINAL_AGC_CNTRL 0xf308 +#define STB0899_BASE_FINAL_AGC_CNTRL 0x00000440 +#define STB0899_FINAL_GAIN_INIT (0x3fff << 12) +#define STB0899_OFFST_FINAL_GAIN_INIT 12 +#define STB0899_WIDTH_FINAL_GAIN_INIT 14 +#define STB0899_FINAL_LOOP_GAIN (0x0f << 8) +#define STB0899_OFFST_FINAL_LOOP_GAIN 8 +#define STB0899_WIDTH_FINAL_LOOP_GAIN 4 +#define STB0899_FINAL_LD_GAIN_INIT (0x01 << 7) +#define STB0899_OFFST_FINAL_LD_GAIN_INIT 7 +#define STB0899_WIDTH_FINAL_LD_GAIN_INIT 1 +#define STB0899_FINAL_AGC_REF (0x7f << 0) +#define STB0899_OFFST_FINAL_AGC_REF 0 +#define STB0899_WIDTH_FINAL_AGC_REF 7 + +#define STB0899_OFF0_FINAL_AGC_GAIN 0xf30c +#define STB0899_BASE_FINAL_AGC_GAIN 0x00000440 +#define STB0899_FINAL_AGC_GAIN (0x3fff << 0) +#define STB0899_OFFST_FINAL_AGC_GAIN 0 +#define STB0899_WIDTH_FINAL_AGC_GAIN 14 + +#define STB0899_OFF0_EQUALIZER_INIT 0xf310 +#define STB0899_BASE_EQUALIZER_INIT 0x00000440 +#define STB0899_EQ_SRST (0x01 << 1) +#define STB0899_OFFST_EQ_SRST 1 +#define STB0899_WIDTH_EQ_SRST 1 +#define STB0899_EQ_INIT (0x01 << 0) +#define STB0899_OFFST_EQ_INIT 0 +#define STB0899_WIDTH_EQ_INIT 1 + +#define STB0899_OFF0_EQ_CNTRL 0xf314 +#define STB0899_BASE_EQ_CNTRL 0x00000440 +#define STB0899_EQ_ADAPT_MODE (0x01 << 18) +#define STB0899_OFFST_EQ_ADAPT_MODE 18 +#define STB0899_WIDTH_EQ_ADAPT_MODE 1 +#define STB0899_EQ_DELAY (0x0f << 14) +#define STB0899_OFFST_EQ_DELAY 14 +#define STB0899_WIDTH_EQ_DELAY 4 +#define STB0899_EQ_QUANT_LEVEL (0xff << 6) +#define STB0899_OFFST_EQ_QUANT_LEVEL 6 +#define STB0899_WIDTH_EQ_QUANT_LEVEL 8 +#define STB0899_EQ_DISABLE_UPDATE (0x01 << 5) +#define STB0899_OFFST_EQ_DISABLE_UPDATE 5 +#define STB0899_WIDTH_EQ_DISABLE_UPDATE 1 +#define STB0899_EQ_BYPASS (0x01 << 4) +#define STB0899_OFFST_EQ_BYPASS 4 +#define STB0899_WIDTH_EQ_BYPASS 1 +#define STB0899_EQ_SHIFT (0x0f << 0) +#define STB0899_OFFST_EQ_SHIFT 0 +#define STB0899_WIDTH_EQ_SHIFT 4 + +#define STB0899_OFF0_EQ_I_INIT_COEFF_0 0xf320 +#define STB0899_OFF1_EQ_I_INIT_COEFF_1 0xf324 +#define STB0899_OFF2_EQ_I_INIT_COEFF_2 0xf328 +#define STB0899_OFF3_EQ_I_INIT_COEFF_3 0xf32c +#define STB0899_OFF4_EQ_I_INIT_COEFF_4 0xf330 +#define STB0899_OFF5_EQ_I_INIT_COEFF_5 0xf334 +#define STB0899_OFF6_EQ_I_INIT_COEFF_6 0xf338 +#define STB0899_OFF7_EQ_I_INIT_COEFF_7 0xf33c +#define STB0899_OFF8_EQ_I_INIT_COEFF_8 0xf340 +#define STB0899_OFF9_EQ_I_INIT_COEFF_9 0xf344 +#define STB0899_OFFa_EQ_I_INIT_COEFF_10 0xf348 +#define STB0899_BASE_EQ_I_INIT_COEFF_N 0x00000440 +#define STB0899_EQ_I_INIT_COEFF_N (0x0fff << 0) +#define STB0899_OFFST_EQ_I_INIT_COEFF_N 0 +#define STB0899_WIDTH_EQ_I_INIT_COEFF_N 12 + +#define STB0899_OFF0_EQ_Q_INIT_COEFF_0 0xf350 +#define STB0899_OFF1_EQ_Q_INIT_COEFF_1 0xf354 +#define STB0899_OFF2_EQ_Q_INIT_COEFF_2 0xf358 +#define STB0899_OFF3_EQ_Q_INIT_COEFF_3 0xf35c +#define STB0899_OFF4_EQ_Q_INIT_COEFF_4 0xf360 +#define STB0899_OFF5_EQ_Q_INIT_COEFF_5 0xf364 +#define STB0899_OFF6_EQ_Q_INIT_COEFF_6 0xf368 +#define STB0899_OFF7_EQ_Q_INIT_COEFF_7 0xf36c +#define STB0899_OFF8_EQ_Q_INIT_COEFF_8 0xf370 +#define STB0899_OFF9_EQ_Q_INIT_COEFF_9 0xf374 +#define STB0899_OFFa_EQ_Q_INIT_COEFF_10 0xf378 +#define STB0899_BASE_EQ_Q_INIT_COEFF_N 0x00000440 +#define STB0899_EQ_Q_INIT_COEFF_N (0x0fff << 0) +#define STB0899_OFFST_EQ_Q_INIT_COEFF_N 0 +#define STB0899_WIDTH_EQ_Q_INIT_COEFF_N 12 + +#define STB0899_OFF0_EQ_I_OUT_COEFF_0 0xf300 +#define STB0899_OFF1_EQ_I_OUT_COEFF_1 0xf304 +#define STB0899_OFF2_EQ_I_OUT_COEFF_2 0xf308 +#define STB0899_OFF3_EQ_I_OUT_COEFF_3 0xf30c +#define STB0899_OFF4_EQ_I_OUT_COEFF_4 0xf310 +#define STB0899_OFF5_EQ_I_OUT_COEFF_5 0xf314 +#define STB0899_OFF6_EQ_I_OUT_COEFF_6 0xf318 +#define STB0899_OFF7_EQ_I_OUT_COEFF_7 0xf31c +#define STB0899_OFF8_EQ_I_OUT_COEFF_8 0xf320 +#define STB0899_OFF9_EQ_I_OUT_COEFF_9 0xf324 +#define STB0899_OFFa_EQ_I_OUT_COEFF_10 0xf328 +#define STB0899_BASE_EQ_I_OUT_COEFF_N 0x00000460 +#define STB0899_EQ_I_OUT_COEFF_N (0x0fff << 0) +#define STB0899_OFFST_EQ_I_OUT_COEFF_N 0 +#define STB0899_WIDTH_EQ_I_OUT_COEFF_N 12 + +#define STB0899_OFF0_EQ_Q_OUT_COEFF_0 0xf330 +#define STB0899_OFF1_EQ_Q_OUT_COEFF_1 0xf334 +#define STB0899_OFF2_EQ_Q_OUT_COEFF_2 0xf338 +#define STB0899_OFF3_EQ_Q_OUT_COEFF_3 0xf33c +#define STB0899_OFF4_EQ_Q_OUT_COEFF_4 0xf340 +#define STB0899_OFF5_EQ_Q_OUT_COEFF_5 0xf344 +#define STB0899_OFF6_EQ_Q_OUT_COEFF_6 0xf348 +#define STB0899_OFF7_EQ_Q_OUT_COEFF_7 0xf34c +#define STB0899_OFF8_EQ_Q_OUT_COEFF_8 0xf350 +#define STB0899_OFF9_EQ_Q_OUT_COEFF_9 0xf354 +#define STB0899_OFFa_EQ_Q_OUT_COEFF_10 0xf358 +#define STB0899_BASE_EQ_Q_OUT_COEFF_N 0x00000460 +#define STB0899_EQ_Q_OUT_COEFF_N (0x0fff << 0) +#define STB0899_OFFST_EQ_Q_OUT_COEFF_N 0 +#define STB0899_WIDTH_EQ_Q_OUT_COEFF_N 12 + +/* S2 FEC */ +#define STB0899_OFF0_BLOCK_LNGTH 0xfa04 +#define STB0899_BASE_BLOCK_LNGTH 0x00000000 +#define STB0899_BLOCK_LENGTH (0xff << 0) +#define STB0899_OFFST_BLOCK_LENGTH 0 +#define STB0899_WIDTH_BLOCK_LENGTH 8 + +#define STB0899_OFF0_ROW_STR 0xfa08 +#define STB0899_BASE_ROW_STR 0x00000000 +#define STB0899_ROW_STRIDE (0xff << 0) +#define STB0899_OFFST_ROW_STRIDE 0 +#define STB0899_WIDTH_ROW_STRIDE 8 + +#define STB0899_OFF0_MAX_ITER 0xfa0c +#define STB0899_BASE_MAX_ITER 0x00000000 +#define STB0899_MAX_ITERATIONS (0xff << 0) +#define STB0899_OFFST_MAX_ITERATIONS 0 +#define STB0899_WIDTH_MAX_ITERATIONS 8 + +#define STB0899_OFF0_BN_END_ADDR 0xfa10 +#define STB0899_BASE_BN_END_ADDR 0x00000000 +#define STB0899_BN_END_ADDR (0x0fff << 0) +#define STB0899_OFFST_BN_END_ADDR 0 +#define STB0899_WIDTH_BN_END_ADDR 12 + +#define STB0899_OFF0_CN_END_ADDR 0xfa14 +#define STB0899_BASE_CN_END_ADDR 0x00000000 +#define STB0899_CN_END_ADDR (0x0fff << 0) +#define STB0899_OFFST_CN_END_ADDR 0 +#define STB0899_WIDTH_CN_END_ADDR 12 + +#define STB0899_OFF0_INFO_LENGTH 0xfa1c +#define STB0899_BASE_INFO_LENGTH 0x00000000 +#define STB0899_INFO_LENGTH (0xff << 0) +#define STB0899_OFFST_INFO_LENGTH 0 +#define STB0899_WIDTH_INFO_LENGTH 8 + +#define STB0899_OFF0_BOT_ADDR 0xfa20 +#define STB0899_BASE_BOT_ADDR 0x00000000 +#define STB0899_BOTTOM_BASE_ADDR (0x03ff << 0) +#define STB0899_OFFST_BOTTOM_BASE_ADDR 0 +#define STB0899_WIDTH_BOTTOM_BASE_ADDR 10 + +#define STB0899_OFF0_BCH_BLK_LN 0xfa24 +#define STB0899_BASE_BCH_BLK_LN 0x00000000 +#define STB0899_BCH_BLOCK_LENGTH (0xffff << 0) +#define STB0899_OFFST_BCH_BLOCK_LENGTH 0 +#define STB0899_WIDTH_BCH_BLOCK_LENGTH 16 + +#define STB0899_OFF0_BCH_T 0xfa28 +#define STB0899_BASE_BCH_T 0x00000000 +#define STB0899_BCH_T (0x0f << 0) +#define STB0899_OFFST_BCH_T 0 +#define STB0899_WIDTH_BCH_T 4 + +#define STB0899_OFF0_CNFG_MODE 0xfa00 +#define STB0899_BASE_CNFG_MODE 0x00000800 +#define STB0899_MODCOD (0x1f << 2) +#define STB0899_OFFST_MODCOD 2 +#define STB0899_WIDTH_MODCOD 5 +#define STB0899_MODCOD_SEL (0x01 << 1) +#define STB0899_OFFST_MODCOD_SEL 1 +#define STB0899_WIDTH_MODCOD_SEL 1 +#define STB0899_CONFIG_MODE (0x01 << 0) +#define STB0899_OFFST_CONFIG_MODE 0 +#define STB0899_WIDTH_CONFIG_MODE 1 + +#define STB0899_OFF0_LDPC_STAT 0xfa04 +#define STB0899_BASE_LDPC_STAT 0x00000800 +#define STB0899_ITERATION (0xff << 3) +#define STB0899_OFFST_ITERATION 3 +#define STB0899_WIDTH_ITERATION 8 +#define STB0899_LDPC_DEC_STATE (0x07 << 0) +#define STB0899_OFFST_LDPC_DEC_STATE 0 +#define STB0899_WIDTH_LDPC_DEC_STATE 3 + +#define STB0899_OFF0_ITER_SCALE 0xfa08 +#define STB0899_BASE_ITER_SCALE 0x00000800 +#define STB0899_ITERATION_SCALE (0xff << 0) +#define STB0899_OFFST_ITERATION_SCALE 0 +#define STB0899_WIDTH_ITERATION_SCALE 8 + +#define STB0899_OFF0_INPUT_MODE 0xfa0c +#define STB0899_BASE_INPUT_MODE 0x00000800 +#define STB0899_SD_BLOCK1_STREAM0 (0x01 << 0) +#define STB0899_OFFST_SD_BLOCK1_STREAM0 0 +#define STB0899_WIDTH_SD_BLOCK1_STREAM0 1 + +#define STB0899_OFF0_LDPCDECRST 0xfa10 +#define STB0899_BASE_LDPCDECRST 0x00000800 +#define STB0899_LDPC_DEC_RST (0x01 << 0) +#define STB0899_OFFST_LDPC_DEC_RST 0 +#define STB0899_WIDTH_LDPC_DEC_RST 1 + +#define STB0899_OFF0_CLK_PER_BYTE_RW 0xfa14 +#define STB0899_BASE_CLK_PER_BYTE_RW 0x00000800 +#define STB0899_CLKS_PER_BYTE (0x0f << 0) +#define STB0899_OFFST_CLKS_PER_BYTE 0 +#define STB0899_WIDTH_CLKS_PER_BYTE 5 + +#define STB0899_OFF0_BCH_ERRORS 0xfa18 +#define STB0899_BASE_BCH_ERRORS 0x00000800 +#define STB0899_BCH_ERRORS (0x0f << 0) +#define STB0899_OFFST_BCH_ERRORS 0 +#define STB0899_WIDTH_BCH_ERRORS 4 + +#define STB0899_OFF0_LDPC_ERRORS 0xfa1c +#define STB0899_BASE_LDPC_ERRORS 0x00000800 +#define STB0899_LDPC_ERRORS (0xffff << 0) +#define STB0899_OFFST_LDPC_ERRORS 0 +#define STB0899_WIDTH_LDPC_ERRORS 16 + +#define STB0899_OFF0_BCH_MODE 0xfa20 +#define STB0899_BASE_BCH_MODE 0x00000800 +#define STB0899_BCH_CORRECT_N (0x01 << 1) +#define STB0899_OFFST_BCH_CORRECT_N 1 +#define STB0899_WIDTH_BCH_CORRECT_N 1 +#define STB0899_FULL_BYPASS (0x01 << 0) +#define STB0899_OFFST_FULL_BYPASS 0 +#define STB0899_WIDTH_FULL_BYPASS 1 + +#define STB0899_OFF0_ERR_ACC_PER 0xfa24 +#define STB0899_BASE_ERR_ACC_PER 0x00000800 +#define STB0899_BCH_ERR_ACC_PERIOD (0x0f << 0) +#define STB0899_OFFST_BCH_ERR_ACC_PERIOD 0 +#define STB0899_WIDTH_BCH_ERR_ACC_PERIOD 4 + +#define STB0899_OFF0_BCH_ERR_ACC 0xfa28 +#define STB0899_BASE_BCH_ERR_ACC 0x00000800 +#define STB0899_BCH_ERR_ACCUM (0xff << 0) +#define STB0899_OFFST_BCH_ERR_ACCUM 0 +#define STB0899_WIDTH_BCH_ERR_ACCUM 8 + +#define STB0899_OFF0_FEC_CORE_ID_REG 0xfa2c +#define STB0899_BASE_FEC_CORE_ID_REG 0x00000800 +#define STB0899_FEC_CORE_ID (0xffffffff << 0) +#define STB0899_OFFST_FEC_CORE_ID 0 +#define STB0899_WIDTH_FEC_CORE_ID 32 + +#define STB0899_OFF0_FEC_VER_ID_REG 0xfa34 +#define STB0899_BASE_FEC_VER_ID_REG 0x00000800 +#define STB0899_FEC_VER_ID (0xff << 0) +#define STB0899_OFFST_FEC_VER_ID 0 +#define STB0899_WIDTH_FEC_VER_ID 8 + +#define STB0899_OFF0_FEC_TP_SEL 0xfa38 +#define STB0899_BASE_FEC_TP_SEL 0x00000800 + +#define STB0899_OFF0_CSM_CNTRL1 0xf310 +#define STB0899_BASE_CSM_CNTRL1 0x00000400 +#define STB0899_CSM_FORCE_FREQLOCK (0x01 << 19) +#define STB0899_OFFST_CSM_FORCE_FREQLOCK 19 +#define STB0899_WIDTH_CSM_FORCE_FREQLOCK 1 +#define STB0899_CSM_FREQ_LOCKSTATE (0x01 << 18) +#define STB0899_OFFST_CSM_FREQ_LOCKSTATE 18 +#define STB0899_WIDTH_CSM_FREQ_LOCKSTATE 1 +#define STB0899_CSM_AUTO_PARAM (0x01 << 17) +#define STB0899_OFFST_CSM_AUTO_PARAM 17 +#define STB0899_WIDTH_CSM_AUTO_PARAM 1 +#define STB0899_FE_LOOP_SHIFT (0x07 << 14) +#define STB0899_OFFST_FE_LOOP_SHIFT 14 +#define STB0899_WIDTH_FE_LOOP_SHIFT 3 +#define STB0899_CSM_AGC_SHIFT (0x07 << 11) +#define STB0899_OFFST_CSM_AGC_SHIFT 11 +#define STB0899_WIDTH_CSM_AGC_SHIFT 3 +#define STB0899_CSM_AGC_GAIN (0x1ff << 2) +#define STB0899_OFFST_CSM_AGC_GAIN 2 +#define STB0899_WIDTH_CSM_AGC_GAIN 9 +#define STB0899_CSM_TWO_PASS (0x01 << 1) +#define STB0899_OFFST_CSM_TWO_PASS 1 +#define STB0899_WIDTH_CSM_TWO_PASS 1 +#define STB0899_CSM_DVT_TABLE (0x01 << 0) +#define STB0899_OFFST_CSM_DVT_TABLE 0 +#define STB0899_WIDTH_CSM_DVT_TABLE 1 + +#define STB0899_OFF0_CSM_CNTRL2 0xf314 +#define STB0899_BASE_CSM_CNTRL2 0x00000400 +#define STB0899_CSM_GAMMA_RHO_ACQ (0x1ff << 9) +#define STB0899_OFFST_CSM_GAMMA_RHOACQ 9 +#define STB0899_WIDTH_CSM_GAMMA_RHOACQ 9 +#define STB0899_CSM_GAMMA_ACQ (0x1ff << 0) +#define STB0899_OFFST_CSM_GAMMA_ACQ 0 +#define STB0899_WIDTH_CSM_GAMMA_ACQ 9 + +#define STB0899_OFF0_CSM_CNTRL3 0xf318 +#define STB0899_BASE_CSM_CNTRL3 0x00000400 +#define STB0899_CSM_GAMMA_RHO_TRACK (0x1ff << 9) +#define STB0899_OFFST_CSM_GAMMA_RHOTRACK 9 +#define STB0899_WIDTH_CSM_GAMMA_RHOTRACK 9 +#define STB0899_CSM_GAMMA_TRACK (0x1ff << 0) +#define STB0899_OFFST_CSM_GAMMA_TRACK 0 +#define STB0899_WIDTH_CSM_GAMMA_TRACK 9 + +#define STB0899_OFF0_CSM_CNTRL4 0xf31c +#define STB0899_BASE_CSM_CNTRL4 0x00000400 +#define STB0899_CSM_PHASEDIFF_THRESH (0x0f << 8) +#define STB0899_OFFST_CSM_PHASEDIFF_THRESH 8 +#define STB0899_WIDTH_CSM_PHASEDIFF_THRESH 4 +#define STB0899_CSM_LOCKCOUNT_THRESH (0xff << 0) +#define STB0899_OFFST_CSM_LOCKCOUNT_THRESH 0 +#define STB0899_WIDTH_CSM_LOCKCOUNT_THRESH 8 + +/* Check on chapter 8 page 42 */ +#define STB0899_ERRCTRL1 0xf574 +#define STB0899_ERRCTRL2 0xf575 +#define STB0899_ERRCTRL3 0xf576 +#define STB0899_ERR_SRC_S1 (0x1f << 3) +#define STB0899_OFFST_ERR_SRC_S1 3 +#define STB0899_WIDTH_ERR_SRC_S1 5 +#define STB0899_ERR_SRC_S2 (0x0f << 0) +#define STB0899_OFFST_ERR_SRC_S2 0 +#define STB0899_WIDTH_ERR_SRC_S2 4 +#define STB0899_NOE (0x07 << 0) +#define STB0899_OFFST_NOE 0 +#define STB0899_WIDTH_NOE 3 + +#define STB0899_ECNT1M 0xf524 +#define STB0899_ECNT1L 0xf525 +#define STB0899_ECNT2M 0xf526 +#define STB0899_ECNT2L 0xf527 +#define STB0899_ECNT3M 0xf528 +#define STB0899_ECNT3L 0xf529 + +#define STB0899_DMONMSK1 0xf57b +#define STB0899_DMONMSK1_WAIT_1STEP (1 << 7) +#define STB0899_DMONMSK1_FREE_14 (1 << 6) +#define STB0899_DMONMSK1_AVRGVIT_CALC (1 << 5) +#define STB0899_DMONMSK1_FREE_12 (1 << 4) +#define STB0899_DMONMSK1_FREE_11 (1 << 3) +#define STB0899_DMONMSK1_B0DIV_CALC (1 << 2) +#define STB0899_DMONMSK1_KDIVB1_CALC (1 << 1) +#define STB0899_DMONMSK1_KDIVB2_CALC (1 << 0) + +#define STB0899_DMONMSK0 0xf57c +#define STB0899_DMONMSK0_SMOTTH_CALC (1 << 7) +#define STB0899_DMONMSK0_FREE_6 (1 << 6) +#define STB0899_DMONMSK0_SIGPOWER_CALC (1 << 5) +#define STB0899_DMONMSK0_QSEUIL_CALC (1 << 4) +#define STB0899_DMONMSK0_FREE_3 (1 << 3) +#define STB0899_DMONMSK0_FREE_2 (1 << 2) +#define STB0899_DMONMSK0_KVDIVB1_CALC (1 << 1) +#define STB0899_DMONMSK0_KVDIVB2_CALC (1 << 0) + +#define STB0899_TSULC 0xf549 +#define STB0899_ULNOSYNCBYTES (0x01 << 7) +#define STB0899_OFFST_ULNOSYNCBYTES 7 +#define STB0899_WIDTH_ULNOSYNCBYTES 1 +#define STB0899_ULPARITY_ON (0x01 << 6) +#define STB0899_OFFST_ULPARITY_ON 6 +#define STB0899_WIDTH_ULPARITY_ON 1 +#define STB0899_ULSYNCOUTRS (0x01 << 5) +#define STB0899_OFFST_ULSYNCOUTRS 5 +#define STB0899_WIDTH_ULSYNCOUTRS 1 +#define STB0899_ULDSS_PACKETS (0x01 << 0) +#define STB0899_OFFST_ULDSS_PACKETS 0 +#define STB0899_WIDTH_ULDSS_PACKETS 1 + +#define STB0899_TSLPL 0xf54b +#define STB0899_LLDVBS2_MODE (0x01 << 4) +#define STB0899_OFFST_LLDVBS2_MODE 4 +#define STB0899_WIDTH_LLDVBS2_MODE 1 +#define STB0899_LLISSYI_ON (0x01 << 3) +#define STB0899_OFFST_LLISSYI_ON 3 +#define STB0899_WIDTH_LLISSYI_ON 1 +#define STB0899_LLNPD_ON (0x01 << 2) +#define STB0899_OFFST_LLNPD_ON 2 +#define STB0899_WIDTH_LLNPD_ON 1 +#define STB0899_LLCRC8_ON (0x01 << 1) +#define STB0899_OFFST_LLCRC8_ON 1 +#define STB0899_WIDTH_LLCRC8_ON 1 + +#define STB0899_TSCFGH 0xf54c +#define STB0899_OUTRS_PS (0x01 << 6) +#define STB0899_OFFST_OUTRS_PS 6 +#define STB0899_WIDTH_OUTRS_PS 1 +#define STB0899_SYNCBYTE (0x01 << 5) +#define STB0899_OFFST_SYNCBYTE 5 +#define STB0899_WIDTH_SYNCBYTE 1 +#define STB0899_PFBIT (0x01 << 4) +#define STB0899_OFFST_PFBIT 4 +#define STB0899_WIDTH_PFBIT 1 +#define STB0899_ERR_BIT (0x01 << 3) +#define STB0899_OFFST_ERR_BIT 3 +#define STB0899_WIDTH_ERR_BIT 1 +#define STB0899_MPEG (0x01 << 2) +#define STB0899_OFFST_MPEG 2 +#define STB0899_WIDTH_MPEG 1 +#define STB0899_CLK_POL (0x01 << 1) +#define STB0899_OFFST_CLK_POL 1 +#define STB0899_WIDTH_CLK_POL 1 +#define STB0899_FORCE0 (0x01 << 0) +#define STB0899_OFFST_FORCE0 0 +#define STB0899_WIDTH_FORCE0 1 + +#define STB0899_TSCFGM 0xf54d +#define STB0899_LLPRIORITY (0x01 << 3) +#define STB0899_OFFST_LLPRIORIY 3 +#define STB0899_WIDTH_LLPRIORITY 1 +#define STB0899_EN188 (0x01 << 2) +#define STB0899_OFFST_EN188 2 +#define STB0899_WIDTH_EN188 1 + +#define STB0899_TSCFGL 0xf54e +#define STB0899_DEL_ERRPCK (0x01 << 7) +#define STB0899_OFFST_DEL_ERRPCK 7 +#define STB0899_WIDTH_DEL_ERRPCK 1 +#define STB0899_ERRFLAGSTD (0x01 << 5) +#define STB0899_OFFST_ERRFLAGSTD 5 +#define STB0899_WIDTH_ERRFLAGSTD 1 +#define STB0899_MPEGERR (0x01 << 4) +#define STB0899_OFFST_MPEGERR 4 +#define STB0899_WIDTH_MPEGERR 1 +#define STB0899_BCH_CHK (0x01 << 3) +#define STB0899_OFFST_BCH_CHK 5 +#define STB0899_WIDTH_BCH_CHK 1 +#define STB0899_CRC8CHK (0x01 << 2) +#define STB0899_OFFST_CRC8CHK 2 +#define STB0899_WIDTH_CRC8CHK 1 +#define STB0899_SPEC_INFO (0x01 << 1) +#define STB0899_OFFST_SPEC_INFO 1 +#define STB0899_WIDTH_SPEC_INFO 1 +#define STB0899_LOW_PRIO_CLK (0x01 << 0) +#define STB0899_OFFST_LOW_PRIO_CLK 0 +#define STB0899_WIDTH_LOW_PRIO_CLK 1 +#define STB0899_ERROR_NORM (0x00 << 0) +#define STB0899_OFFST_ERROR_NORM 0 +#define STB0899_WIDTH_ERROR_NORM 0 + +#define STB0899_TSOUT 0xf54f +#define STB0899_RSSYNCDEL 0xf550 +#define STB0899_TSINHDELH 0xf551 +#define STB0899_TSINHDELM 0xf552 +#define STB0899_TSINHDELL 0xf553 +#define STB0899_TSLLSTKM 0xf55a +#define STB0899_TSLLSTKL 0xf55b +#define STB0899_TSULSTKM 0xf55c +#define STB0899_TSULSTKL 0xf55d +#define STB0899_TSSTATUS 0xf561 + +#define STB0899_PDELCTRL 0xf600 +#define STB0899_INVERT_RES (0x01 << 7) +#define STB0899_OFFST_INVERT_RES 7 +#define STB0899_WIDTH_INVERT_RES 1 +#define STB0899_FORCE_ACCEPTED (0x01 << 6) +#define STB0899_OFFST_FORCE_ACCEPTED 6 +#define STB0899_WIDTH_FORCE_ACCEPTED 1 +#define STB0899_FILTER_EN (0x01 << 5) +#define STB0899_OFFST_FILTER_EN 5 +#define STB0899_WIDTH_FILTER_EN 1 +#define STB0899_LOCKFALL_THRESH (0x01 << 4) +#define STB0899_OFFST_LOCKFALL_THRESH 4 +#define STB0899_WIDTH_LOCKFALL_THRESH 1 +#define STB0899_HYST_EN (0x01 << 3) +#define STB0899_OFFST_HYST_EN 3 +#define STB0899_WIDTH_HYST_EN 1 +#define STB0899_HYST_SWRST (0x01 << 2) +#define STB0899_OFFST_HYST_SWRST 2 +#define STB0899_WIDTH_HYST_SWRST 1 +#define STB0899_ALGO_EN (0x01 << 1) +#define STB0899_OFFST_ALGO_EN 1 +#define STB0899_WIDTH_ALGO_EN 1 +#define STB0899_ALGO_SWRST (0x01 << 0) +#define STB0899_OFFST_ALGO_SWRST 0 +#define STB0899_WIDTH_ALGO_SWRST 1 + +#define STB0899_PDELCTRL2 0xf601 +#define STB0899_BBHCTRL1 0xf602 +#define STB0899_BBHCTRL2 0xf603 +#define STB0899_HYSTTHRESH 0xf604 + +#define STB0899_MATCSTM 0xf605 +#define STB0899_MATCSTL 0xf606 +#define STB0899_UPLCSTM 0xf607 +#define STB0899_UPLCSTL 0xf608 +#define STB0899_DFLCSTM 0xf609 +#define STB0899_DFLCSTL 0xf60a +#define STB0899_SYNCCST 0xf60b +#define STB0899_SYNCDCSTM 0xf60c +#define STB0899_SYNCDCSTL 0xf60d +#define STB0899_ISI_ENTRY 0xf60e +#define STB0899_ISI_BIT_EN 0xf60f +#define STB0899_MATSTRM 0xf610 +#define STB0899_MATSTRL 0xf611 +#define STB0899_UPLSTRM 0xf612 +#define STB0899_UPLSTRL 0xf613 +#define STB0899_DFLSTRM 0xf614 +#define STB0899_DFLSTRL 0xf615 +#define STB0899_SYNCSTR 0xf616 +#define STB0899_SYNCDSTRM 0xf617 +#define STB0899_SYNCDSTRL 0xf618 + +#define STB0899_CFGPDELSTATUS1 0xf619 +#define STB0899_BADDFL (0x01 << 6) +#define STB0899_OFFST_BADDFL 6 +#define STB0899_WIDTH_BADDFL 1 +#define STB0899_CONTINUOUS_STREAM (0x01 << 5) +#define STB0899_OFFST_CONTINUOUS_STREAM 5 +#define STB0899_WIDTH_CONTINUOUS_STREAM 1 +#define STB0899_ACCEPTED_STREAM (0x01 << 4) +#define STB0899_OFFST_ACCEPTED_STREAM 4 +#define STB0899_WIDTH_ACCEPTED_STREAM 1 +#define STB0899_BCH_ERRFLAG (0x01 << 3) +#define STB0899_OFFST_BCH_ERRFLAG 3 +#define STB0899_WIDTH_BCH_ERRFLAG 1 +#define STB0899_CRCRES (0x01 << 2) +#define STB0899_OFFST_CRCRES 2 +#define STB0899_WIDTH_CRCRES 1 +#define STB0899_CFGPDELSTATUS_LOCK (0x01 << 1) +#define STB0899_OFFST_CFGPDELSTATUS_LOCK 1 +#define STB0899_WIDTH_CFGPDELSTATUS_LOCK 1 +#define STB0899_1STLOCK (0x01 << 0) +#define STB0899_OFFST_1STLOCK 0 +#define STB0899_WIDTH_1STLOCK 1 + +#define STB0899_CFGPDELSTATUS2 0xf61a +#define STB0899_BBFERRORM 0xf61b +#define STB0899_BBFERRORL 0xf61c +#define STB0899_UPKTERRORM 0xf61d +#define STB0899_UPKTERRORL 0xf61e + +#define STB0899_TSTCK 0xff10 + +#define STB0899_TSTRES 0xff11 +#define STB0899_FRESLDPC (0x01 << 7) +#define STB0899_OFFST_FRESLDPC 7 +#define STB0899_WIDTH_FRESLDPC 1 +#define STB0899_FRESRS (0x01 << 6) +#define STB0899_OFFST_FRESRS 6 +#define STB0899_WIDTH_FRESRS 1 +#define STB0899_FRESVIT (0x01 << 5) +#define STB0899_OFFST_FRESVIT 5 +#define STB0899_WIDTH_FRESVIT 1 +#define STB0899_FRESMAS1_2 (0x01 << 4) +#define STB0899_OFFST_FRESMAS1_2 4 +#define STB0899_WIDTH_FRESMAS1_2 1 +#define STB0899_FRESACS (0x01 << 3) +#define STB0899_OFFST_FRESACS 3 +#define STB0899_WIDTH_FRESACS 1 +#define STB0899_FRESSYM (0x01 << 2) +#define STB0899_OFFST_FRESSYM 2 +#define STB0899_WIDTH_FRESSYM 1 +#define STB0899_FRESMAS (0x01 << 1) +#define STB0899_OFFST_FRESMAS 1 +#define STB0899_WIDTH_FRESMAS 1 +#define STB0899_FRESINT (0x01 << 0) +#define STB0899_OFFST_FRESINIT 0 +#define STB0899_WIDTH_FRESINIT 1 + +#define STB0899_TSTOUT 0xff12 +#define STB0899_EN_SIGNATURE (0x01 << 7) +#define STB0899_OFFST_EN_SIGNATURE 7 +#define STB0899_WIDTH_EN_SIGNATURE 1 +#define STB0899_BCLK_CLK (0x01 << 6) +#define STB0899_OFFST_BCLK_CLK 6 +#define STB0899_WIDTH_BCLK_CLK 1 +#define STB0899_SGNL_OUT (0x01 << 5) +#define STB0899_OFFST_SGNL_OUT 5 +#define STB0899_WIDTH_SGNL_OUT 1 +#define STB0899_TS (0x01 << 4) +#define STB0899_OFFST_TS 4 +#define STB0899_WIDTH_TS 1 +#define STB0899_CTEST (0x01 << 0) +#define STB0899_OFFST_CTEST 0 +#define STB0899_WIDTH_CTEST 1 + +#define STB0899_TSTIN 0xff13 +#define STB0899_TEST_IN (0x01 << 7) +#define STB0899_OFFST_TEST_IN 7 +#define STB0899_WIDTH_TEST_IN 1 +#define STB0899_EN_ADC (0x01 << 6) +#define STB0899_OFFST_EN_ADC 6 +#define STB0899_WIDTH_ENADC 1 +#define STB0899_SGN_ADC (0x01 << 5) +#define STB0899_OFFST_SGN_ADC 5 +#define STB0899_WIDTH_SGN_ADC 1 +#define STB0899_BCLK_IN (0x01 << 4) +#define STB0899_OFFST_BCLK_IN 4 +#define STB0899_WIDTH_BCLK_IN 1 +#define STB0899_JETONIN_MODE (0x01 << 3) +#define STB0899_OFFST_JETONIN_MODE 3 +#define STB0899_WIDTH_JETONIN_MODE 1 +#define STB0899_BCLK_VALUE (0x01 << 2) +#define STB0899_OFFST_BCLK_VALUE 2 +#define STB0899_WIDTH_BCLK_VALUE 1 +#define STB0899_SGNRST_T12 (0x01 << 1) +#define STB0899_OFFST_SGNRST_T12 1 +#define STB0899_WIDTH_SGNRST_T12 1 +#define STB0899_LOWSP_ENAX (0x01 << 0) +#define STB0899_OFFST_LOWSP_ENAX 0 +#define STB0899_WIDTH_LOWSP_ENAX 1 + +#define STB0899_TSTSYS 0xff14 +#define STB0899_TSTCHIP 0xff15 +#define STB0899_TSTFREE 0xff16 +#define STB0899_TSTI2C 0xff17 +#define STB0899_BITSPEEDM 0xff1c +#define STB0899_BITSPEEDL 0xff1d +#define STB0899_TBUSBIT 0xff1e +#define STB0899_TSTDIS 0xff24 +#define STB0899_TSTDISRX 0xff25 +#define STB0899_TSTJETON 0xff28 +#define STB0899_TSTDCADJ 0xff40 +#define STB0899_TSTAGC1 0xff41 +#define STB0899_TSTAGC1N 0xff42 +#define STB0899_TSTPOLYPH 0xff48 +#define STB0899_TSTR 0xff49 +#define STB0899_TSTAGC2 0xff4a +#define STB0899_TSTCTL1 0xff4b +#define STB0899_TSTCTL2 0xff4c +#define STB0899_TSTCTL3 0xff4d +#define STB0899_TSTDEMAP 0xff50 +#define STB0899_TSTDEMAP2 0xff51 +#define STB0899_TSTDEMMON 0xff52 +#define STB0899_TSTRATE 0xff53 +#define STB0899_TSTSELOUT 0xff54 +#define STB0899_TSYNC 0xff55 +#define STB0899_TSTERR 0xff56 +#define STB0899_TSTRAM1 0xff58 +#define STB0899_TSTVSELOUT 0xff59 +#define STB0899_TSTFORCEIN 0xff5a +#define STB0899_TSTRS1 0xff5c +#define STB0899_TSTRS2 0xff5d +#define STB0899_TSTRS3 0xff53 + +#define STB0899_INTBUFSTATUS 0xf200 +#define STB0899_INTBUFCTRL 0xf201 +#define STB0899_PCKLENUL 0xf55e +#define STB0899_PCKLENLL 0xf55f +#define STB0899_RSPCKLEN 0xf560 + +/* 2 registers */ +#define STB0899_SYNCDCST 0xf60c + +/* DiSEqC */ +#define STB0899_DISCNTRL1 0xf0a0 +#define STB0899_TIMOFF (0x01 << 7) +#define STB0899_OFFST_TIMOFF 7 +#define STB0899_WIDTH_TIMOFF 1 +#define STB0899_DISEQCRESET (0x01 << 6) +#define STB0899_OFFST_DISEQCRESET 6 +#define STB0899_WIDTH_DISEQCRESET 1 +#define STB0899_TIMCMD (0x03 << 4) +#define STB0899_OFFST_TIMCMD 4 +#define STB0899_WIDTH_TIMCMD 2 +#define STB0899_DISPRECHARGE (0x01 << 2) +#define STB0899_OFFST_DISPRECHARGE 2 +#define STB0899_WIDTH_DISPRECHARGE 1 +#define STB0899_DISEQCMODE (0x03 << 0) +#define STB0899_OFFST_DISEQCMODE 0 +#define STB0899_WIDTH_DISEQCMODE 2 + +#define STB0899_DISCNTRL2 0xf0a1 +#define STB0899_RECEIVER_ON (0x01 << 7) +#define STB0899_OFFST_RECEIVER_ON 7 +#define STB0899_WIDTH_RECEIVER_ON 1 +#define STB0899_IGNO_SHORT_22K (0x01 << 6) +#define STB0899_OFFST_IGNO_SHORT_22K 6 +#define STB0899_WIDTH_IGNO_SHORT_22K 1 +#define STB0899_ONECHIP_TRX (0x01 << 5) +#define STB0899_OFFST_ONECHIP_TRX 5 +#define STB0899_WIDTH_ONECHIP_TRX 1 +#define STB0899_EXT_ENVELOP (0x01 << 4) +#define STB0899_OFFST_EXT_ENVELOP 4 +#define STB0899_WIDTH_EXT_ENVELOP 1 +#define STB0899_PIN_SELECT (0x03 << 2) +#define STB0899_OFFST_PIN_SELCT 2 +#define STB0899_WIDTH_PIN_SELCT 2 +#define STB0899_IRQ_RXEND (0x01 << 1) +#define STB0899_OFFST_IRQ_RXEND 1 +#define STB0899_WIDTH_IRQ_RXEND 1 +#define STB0899_IRQ_4NBYTES (0x01 << 0) +#define STB0899_OFFST_IRQ_4NBYTES 0 +#define STB0899_WIDTH_IRQ_4NBYTES 1 + +#define STB0899_DISRX_ST0 0xf0a4 +#define STB0899_RXEND (0x01 << 7) +#define STB0899_OFFST_RXEND 7 +#define STB0899_WIDTH_RXEND 1 +#define STB0899_RXACTIVE (0x01 << 6) +#define STB0899_OFFST_RXACTIVE 6 +#define STB0899_WIDTH_RXACTIVE 1 +#define STB0899_SHORT22K (0x01 << 5) +#define STB0899_OFFST_SHORT22K 5 +#define STB0899_WIDTH_SHORT22K 1 +#define STB0899_CONTTONE (0x01 << 4) +#define STB0899_OFFST_CONTTONE 4 +#define STB0899_WIDTH_CONTONE 1 +#define STB0899_4BFIFOREDY (0x01 << 3) +#define STB0899_OFFST_4BFIFOREDY 3 +#define STB0899_WIDTH_4BFIFOREDY 1 +#define STB0899_FIFOEMPTY (0x01 << 2) +#define STB0899_OFFST_FIFOEMPTY 2 +#define STB0899_WIDTH_FIFOEMPTY 1 +#define STB0899_ABORTTRX (0x01 << 0) +#define STB0899_OFFST_ABORTTRX 0 +#define STB0899_WIDTH_ABORTTRX 1 + +#define STB0899_DISRX_ST1 0xf0a5 +#define STB0899_RXFAIL (0x01 << 7) +#define STB0899_OFFST_RXFAIL 7 +#define STB0899_WIDTH_RXFAIL 1 +#define STB0899_FIFOPFAIL (0x01 << 6) +#define STB0899_OFFST_FIFOPFAIL 6 +#define STB0899_WIDTH_FIFOPFAIL 1 +#define STB0899_RXNONBYTES (0x01 << 5) +#define STB0899_OFFST_RXNONBYTES 5 +#define STB0899_WIDTH_RXNONBYTES 1 +#define STB0899_FIFOOVF (0x01 << 4) +#define STB0899_OFFST_FIFOOVF 4 +#define STB0899_WIDTH_FIFOOVF 1 +#define STB0899_FIFOBYTENBR (0x0f << 0) +#define STB0899_OFFST_FIFOBYTENBR 0 +#define STB0899_WIDTH_FIFOBYTENBR 4 + +#define STB0899_DISPARITY 0xf0a6 + +#define STB0899_DISFIFO 0xf0a7 + +#define STB0899_DISSTATUS 0xf0a8 +#define STB0899_FIFOFULL (0x01 << 6) +#define STB0899_OFFST_FIFOFULL 6 +#define STB0899_WIDTH_FIFOFULL 1 +#define STB0899_TXIDLE (0x01 << 5) +#define STB0899_OFFST_TXIDLE 5 +#define STB0899_WIDTH_TXIDLE 1 +#define STB0899_GAPBURST (0x01 << 4) +#define STB0899_OFFST_GAPBURST 4 +#define STB0899_WIDTH_GAPBURST 1 +#define STB0899_TXFIFOBYTES (0x0f << 0) +#define STB0899_OFFST_TXFIFOBYTES 0 +#define STB0899_WIDTH_TXFIFOBYTES 4 +#define STB0899_DISF22 0xf0a9 + +#define STB0899_DISF22RX 0xf0aa + +/* General Purpose */ +#define STB0899_SYSREG 0xf101 +#define STB0899_ACRPRESC 0xf110 +#define STB0899_OFFST_RSVD2 7 +#define STB0899_WIDTH_RSVD2 1 +#define STB0899_OFFST_ACRPRESC 4 +#define STB0899_WIDTH_ACRPRESC 3 +#define STB0899_OFFST_RSVD1 3 +#define STB0899_WIDTH_RSVD1 1 +#define STB0899_OFFST_ACRPRESC2 0 +#define STB0899_WIDTH_ACRPRESC2 3 + +#define STB0899_ACRDIV1 0xf111 +#define STB0899_ACRDIV2 0xf112 +#define STB0899_DACR1 0xf113 +#define STB0899_DACR2 0xf114 +#define STB0899_OUTCFG 0xf11c +#define STB0899_MODECFG 0xf11d +#define STB0899_NCOARSE 0xf1b3 + +#define STB0899_SYNTCTRL 0xf1b6 +#define STB0899_STANDBY (0x01 << 7) +#define STB0899_OFFST_STANDBY 7 +#define STB0899_WIDTH_STANDBY 1 +#define STB0899_BYPASSPLL (0x01 << 6) +#define STB0899_OFFST_BYPASSPLL 6 +#define STB0899_WIDTH_BYPASSPLL 1 +#define STB0899_SEL1XRATIO (0x01 << 5) +#define STB0899_OFFST_SEL1XRATIO 5 +#define STB0899_WIDTH_SEL1XRATIO 1 +#define STB0899_SELOSCI (0x01 << 1) +#define STB0899_OFFST_SELOSCI 1 +#define STB0899_WIDTH_SELOSCI 1 + +#define STB0899_FILTCTRL 0xf1b7 +#define STB0899_SYSCTRL 0xf1b8 + +#define STB0899_STOPCLK1 0xf1c2 +#define STB0899_STOP_CKINTBUF108 (0x01 << 7) +#define STB0899_OFFST_STOP_CKINTBUF108 7 +#define STB0899_WIDTH_STOP_CKINTBUF108 1 +#define STB0899_STOP_CKINTBUF216 (0x01 << 6) +#define STB0899_OFFST_STOP_CKINTBUF216 6 +#define STB0899_WIDTH_STOP_CKINTBUF216 1 +#define STB0899_STOP_CHK8PSK (0x01 << 5) +#define STB0899_OFFST_STOP_CHK8PSK 5 +#define STB0899_WIDTH_STOP_CHK8PSK 1 +#define STB0899_STOP_CKFEC108 (0x01 << 4) +#define STB0899_OFFST_STOP_CKFEC108 4 +#define STB0899_WIDTH_STOP_CKFEC108 1 +#define STB0899_STOP_CKFEC216 (0x01 << 3) +#define STB0899_OFFST_STOP_CKFEC216 3 +#define STB0899_WIDTH_STOP_CKFEC216 1 +#define STB0899_STOP_CKCORE216 (0x01 << 2) +#define STB0899_OFFST_STOP_CKCORE216 2 +#define STB0899_WIDTH_STOP_CKCORE216 1 +#define STB0899_STOP_CKADCI108 (0x01 << 1) +#define STB0899_OFFST_STOP_CKADCI108 1 +#define STB0899_WIDTH_STOP_CKADCI108 1 +#define STB0899_STOP_INVCKADCI108 (0x01 << 0) +#define STB0899_OFFST_STOP_INVCKADCI108 0 +#define STB0899_WIDTH_STOP_INVCKADCI108 1 + +#define STB0899_STOPCLK2 0xf1c3 +#define STB0899_STOP_CKS2DMD108 (0x01 << 2) +#define STB0899_OFFST_STOP_CKS2DMD108 2 +#define STB0899_WIDTH_STOP_CKS2DMD108 1 +#define STB0899_STOP_CKPKDLIN108 (0x01 << 1) +#define STB0899_OFFST_STOP_CKPKDLIN108 1 +#define STB0899_WIDTH_STOP_CKPKDLIN108 1 +#define STB0899_STOP_CKPKDLIN216 (0x01 << 0) +#define STB0899_OFFST_STOP_CKPKDLIN216 0 +#define STB0899_WIDTH_STOP_CKPKDLIN216 1 + +#define STB0899_TSTTNR1 0xf1e0 +#define STB0899_BYPASS_ADC (0x01 << 7) +#define STB0899_OFFST_BYPASS_ADC 7 +#define STB0899_WIDTH_BYPASS_ADC 1 +#define STB0899_INVADCICKOUT (0x01 << 6) +#define STB0899_OFFST_INVADCICKOUT 6 +#define STB0899_WIDTH_INVADCICKOUT 1 +#define STB0899_ADCTEST_VOLTAGE (0x03 << 4) +#define STB0899_OFFST_ADCTEST_VOLTAGE 4 +#define STB0899_WIDTH_ADCTEST_VOLTAGE 1 +#define STB0899_ADC_RESET (0x01 << 3) +#define STB0899_OFFST_ADC_RESET 3 +#define STB0899_WIDTH_ADC_RESET 1 +#define STB0899_TSTTNR1_2 (0x01 << 2) +#define STB0899_OFFST_TSTTNR1_2 2 +#define STB0899_WIDTH_TSTTNR1_2 1 +#define STB0899_ADCPON (0x01 << 1) +#define STB0899_OFFST_ADCPON 1 +#define STB0899_WIDTH_ADCPON 1 +#define STB0899_ADCIN_MODE (0x01 << 0) +#define STB0899_OFFST_ADCIN_MODE 0 +#define STB0899_WIDTH_ADCIN_MODE 1 + +#define STB0899_TSTTNR2 0xf1e1 +#define STB0899_TSTTNR2_7 (0x01 << 7) +#define STB0899_OFFST_TSTTNR2_7 7 +#define STB0899_WIDTH_TSTTNR2_7 1 +#define STB0899_NOT_DISRX_WIRED (0x01 << 6) +#define STB0899_OFFST_NOT_DISRX_WIRED 6 +#define STB0899_WIDTH_NOT_DISRX_WIRED 1 +#define STB0899_DISEQC_DCURRENT (0x01 << 5) +#define STB0899_OFFST_DISEQC_DCURRENT 5 +#define STB0899_WIDTH_DISEQC_DCURRENT 1 +#define STB0899_DISEQC_ZCURRENT (0x01 << 4) +#define STB0899_OFFST_DISEQC_ZCURRENT 4 +#define STB0899_WIDTH_DISEQC_ZCURRENT 1 +#define STB0899_DISEQC_SINC_SOURCE (0x03 << 2) +#define STB0899_OFFST_DISEQC_SINC_SOURCE 2 +#define STB0899_WIDTH_DISEQC_SINC_SOURCE 2 +#define STB0899_SELIQSRC (0x03 << 0) +#define STB0899_OFFST_SELIQSRC 0 +#define STB0899_WIDTH_SELIQSRC 2 + +#define STB0899_TSTTNR3 0xf1e2 + +#define STB0899_I2CCFG 0xf129 +#define STB0899_I2CCFGRSVD (0x0f << 4) +#define STB0899_OFFST_I2CCFGRSVD 4 +#define STB0899_WIDTH_I2CCFGRSVD 4 +#define STB0899_I2CFASTMODE (0x01 << 3) +#define STB0899_OFFST_I2CFASTMODE 3 +#define STB0899_WIDTH_I2CFASTMODE 1 +#define STB0899_STATUSWR (0x01 << 2) +#define STB0899_OFFST_STATUSWR 2 +#define STB0899_WIDTH_STATUSWR 1 +#define STB0899_I2CADDRINC (0x03 << 0) +#define STB0899_OFFST_I2CADDRINC 0 +#define STB0899_WIDTH_I2CADDRINC 2 + +#define STB0899_I2CRPT 0xf12a +#define STB0899_I2CTON (0x01 << 7) +#define STB0899_OFFST_I2CTON 7 +#define STB0899_WIDTH_I2CTON 1 +#define STB0899_ENARPTLEVEL (0x01 << 6) +#define STB0899_OFFST_ENARPTLEVEL 6 +#define STB0899_WIDTH_ENARPTLEVEL 2 +#define STB0899_SCLTDELAY (0x01 << 3) +#define STB0899_OFFST_SCLTDELAY 3 +#define STB0899_WIDTH_SCLTDELAY 1 +#define STB0899_STOPENA (0x01 << 2) +#define STB0899_OFFST_STOPENA 2 +#define STB0899_WIDTH_STOPENA 1 +#define STB0899_STOPSDAT2SDA (0x01 << 1) +#define STB0899_OFFST_STOPSDAT2SDA 1 +#define STB0899_WIDTH_STOPSDAT2SDA 1 + +#define STB0899_IOPVALUE8 0xf136 +#define STB0899_IOPVALUE7 0xf137 +#define STB0899_IOPVALUE6 0xf138 +#define STB0899_IOPVALUE5 0xf139 +#define STB0899_IOPVALUE4 0xf13a +#define STB0899_IOPVALUE3 0xf13b +#define STB0899_IOPVALUE2 0xf13c +#define STB0899_IOPVALUE1 0xf13d +#define STB0899_IOPVALUE0 0xf13e + +#define STB0899_GPIO00CFG 0xf140 + +#define STB0899_GPIO01CFG 0xf141 +#define STB0899_GPIO02CFG 0xf142 +#define STB0899_GPIO03CFG 0xf143 +#define STB0899_GPIO04CFG 0xf144 +#define STB0899_GPIO05CFG 0xf145 +#define STB0899_GPIO06CFG 0xf146 +#define STB0899_GPIO07CFG 0xf147 +#define STB0899_GPIO08CFG 0xf148 +#define STB0899_GPIO09CFG 0xf149 +#define STB0899_GPIO10CFG 0xf14a +#define STB0899_GPIO11CFG 0xf14b +#define STB0899_GPIO12CFG 0xf14c +#define STB0899_GPIO13CFG 0xf14d +#define STB0899_GPIO14CFG 0xf14e +#define STB0899_GPIO15CFG 0xf14f +#define STB0899_GPIO16CFG 0xf150 +#define STB0899_GPIO17CFG 0xf151 +#define STB0899_GPIO18CFG 0xf152 +#define STB0899_GPIO19CFG 0xf153 +#define STB0899_GPIO20CFG 0xf154 + +#define STB0899_SDATCFG 0xf155 +#define STB0899_SCLTCFG 0xf156 +#define STB0899_AGCRFCFG 0xf157 +#define STB0899_GPIO22 0xf158 /* AGCBB2CFG */ +#define STB0899_GPIO21 0xf159 /* AGCBB1CFG */ +#define STB0899_DIRCLKCFG 0xf15a +#define STB0899_CLKOUT27CFG 0xf15b +#define STB0899_STDBYCFG 0xf15c +#define STB0899_CS0CFG 0xf15d +#define STB0899_CS1CFG 0xf15e +#define STB0899_DISEQCOCFG 0xf15f + +#define STB0899_GPIO32CFG 0xf160 +#define STB0899_GPIO33CFG 0xf161 +#define STB0899_GPIO34CFG 0xf162 +#define STB0899_GPIO35CFG 0xf163 +#define STB0899_GPIO36CFG 0xf164 +#define STB0899_GPIO37CFG 0xf165 +#define STB0899_GPIO38CFG 0xf166 +#define STB0899_GPIO39CFG 0xf167 + +#define STB0899_IRQSTATUS_3 0xf120 +#define STB0899_IRQSTATUS_2 0xf121 +#define STB0899_IRQSTATUS_1 0xf122 +#define STB0899_IRQSTATUS_0 0xf123 + +#define STB0899_IRQMSK_3 0xf124 +#define STB0899_IRQMSK_2 0xf125 +#define STB0899_IRQMSK_1 0xf126 +#define STB0899_IRQMSK_0 0xf127 + +#define STB0899_IRQCFG 0xf128 + +#define STB0899_GHOSTREG 0xf000 + +#define STB0899_S2DEMOD 0xf3fc +#define STB0899_S2FEC 0xfafc + + +#endif diff --git a/drivers/media/dvb/frontends/stb6100.c b/drivers/media/dvb/frontends/stb6100.c new file mode 100644 index 0000000..ff39275 --- /dev/null +++ b/drivers/media/dvb/frontends/stb6100.c @@ -0,0 +1,545 @@ +/* + STB6100 Silicon Tuner + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + + 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/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> + +#include "dvb_frontend.h" +#include "stb6100.h" + +static unsigned int verbose; +module_param(verbose, int, 0644); + + +#define FE_ERROR 0 +#define FE_NOTICE 1 +#define FE_INFO 2 +#define FE_DEBUG 3 + +#define dprintk(x, y, z, format, arg...) do { \ + if (z) { \ + if ((x > FE_ERROR) && (x > y)) \ + printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ + else if ((x > FE_NOTICE) && (x > y)) \ + printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ + else if ((x > FE_INFO) && (x > y)) \ + printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ + else if ((x > FE_DEBUG) && (x > y)) \ + printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ + } else { \ + if (x > y) \ + printk(format, ##arg); \ + } \ +} while(0) + +struct stb6100_lkup { + u32 val_low; + u32 val_high; + u8 reg; +}; + +static int stb6100_release(struct dvb_frontend *fe); + +static const struct stb6100_lkup lkup[] = { + { 0, 950000, 0x0a }, + { 950000, 1000000, 0x0a }, + { 1000000, 1075000, 0x0c }, + { 1075000, 1200000, 0x00 }, + { 1200000, 1300000, 0x01 }, + { 1300000, 1370000, 0x02 }, + { 1370000, 1470000, 0x04 }, + { 1470000, 1530000, 0x05 }, + { 1530000, 1650000, 0x06 }, + { 1650000, 1800000, 0x08 }, + { 1800000, 1950000, 0x0a }, + { 1950000, 2150000, 0x0c }, + { 2150000, 9999999, 0x0c }, + { 0, 0, 0x00 } +}; + +/* Register names for easy debugging. */ +static const char *stb6100_regnames[] = { + [STB6100_LD] = "LD", + [STB6100_VCO] = "VCO", + [STB6100_NI] = "NI", + [STB6100_NF_LSB] = "NF", + [STB6100_K] = "K", + [STB6100_G] = "G", + [STB6100_F] = "F", + [STB6100_DLB] = "DLB", + [STB6100_TEST1] = "TEST1", + [STB6100_FCCK] = "FCCK", + [STB6100_LPEN] = "LPEN", + [STB6100_TEST3] = "TEST3", +}; + +/* Template for normalisation, i.e. setting unused or undocumented + * bits as required according to the documentation. + */ +struct stb6100_regmask { + u8 mask; + u8 set; +}; + +static const struct stb6100_regmask stb6100_template[] = { + [STB6100_LD] = { 0xff, 0x00 }, + [STB6100_VCO] = { 0xff, 0x00 }, + [STB6100_NI] = { 0xff, 0x00 }, + [STB6100_NF_LSB] = { 0xff, 0x00 }, + [STB6100_K] = { 0xc7, 0x38 }, + [STB6100_G] = { 0xef, 0x10 }, + [STB6100_F] = { 0x1f, 0xc0 }, + [STB6100_DLB] = { 0x38, 0xc4 }, + [STB6100_TEST1] = { 0x00, 0x8f }, + [STB6100_FCCK] = { 0x40, 0x0d }, + [STB6100_LPEN] = { 0xf0, 0x0b }, + [STB6100_TEST3] = { 0x00, 0xde }, +}; + +static void stb6100_normalise_regs(u8 regs[]) +{ + int i; + + for (i = 0; i < STB6100_NUMREGS; i++) + regs[i] = (regs[i] & stb6100_template[i].mask) | stb6100_template[i].set; +} + +static int stb6100_read_regs(struct stb6100_state *state, u8 regs[]) +{ + int rc; + struct i2c_msg msg = { + .addr = state->config->tuner_address, + .flags = I2C_M_RD, + .buf = regs, + .len = STB6100_NUMREGS + }; + + rc = i2c_transfer(state->i2c, &msg, 1); + if (unlikely(rc != 1)) { + dprintk(verbose, FE_ERROR, 1, "Read (0x%x) err, rc=[%d]", + state->config->tuner_address, rc); + + return -EREMOTEIO; + } + if (unlikely(verbose > FE_DEBUG)) { + int i; + + dprintk(verbose, FE_DEBUG, 1, " Read from 0x%02x", state->config->tuner_address); + for (i = 0; i < STB6100_NUMREGS; i++) + dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[i], regs[i]); + } + return 0; +} + +static int stb6100_read_reg(struct stb6100_state *state, u8 reg) +{ + u8 regs[STB6100_NUMREGS]; + int rc; + + if (unlikely(reg >= STB6100_NUMREGS)) { + dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg); + return -EINVAL; + } + if ((rc = stb6100_read_regs(state, regs)) < 0) + return rc; + return (unsigned int)regs[reg]; +} + +static int stb6100_write_reg_range(struct stb6100_state *state, u8 buf[], int start, int len) +{ + int rc; + u8 cmdbuf[len + 1]; + struct i2c_msg msg = { + .addr = state->config->tuner_address, + .flags = 0, + .buf = cmdbuf, + .len = len + 1 + }; + + if (unlikely(start < 1 || start + len > STB6100_NUMREGS)) { + dprintk(verbose, FE_ERROR, 1, "Invalid register range %d:%d", + start, len); + return -EINVAL; + } + memcpy(&cmdbuf[1], buf, len); + cmdbuf[0] = start; + + if (unlikely(verbose > FE_DEBUG)) { + int i; + + dprintk(verbose, FE_DEBUG, 1, " Write @ 0x%02x: [%d:%d]", state->config->tuner_address, start, len); + for (i = 0; i < len; i++) + dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[start + i], buf[i]); + } + rc = i2c_transfer(state->i2c, &msg, 1); + if (unlikely(rc != 1)) { + dprintk(verbose, FE_ERROR, 1, "(0x%x) write err [%d:%d], rc=[%d]", + (unsigned int)state->config->tuner_address, start, len, rc); + return -EREMOTEIO; + } + return 0; +} + +static int stb6100_write_reg(struct stb6100_state *state, u8 reg, u8 data) +{ + if (unlikely(reg >= STB6100_NUMREGS)) { + dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg); + return -EREMOTEIO; + } + data = (data & stb6100_template[reg].mask) | stb6100_template[reg].set; + return stb6100_write_reg_range(state, &data, reg, 1); +} + +static int stb6100_write_regs(struct stb6100_state *state, u8 regs[]) +{ + stb6100_normalise_regs(regs); + return stb6100_write_reg_range(state, ®s[1], 1, STB6100_NUMREGS - 1); +} + +static int stb6100_get_status(struct dvb_frontend *fe, u32 *status) +{ + int rc; + struct stb6100_state *state = fe->tuner_priv; + + if ((rc = stb6100_read_reg(state, STB6100_LD)) < 0) + return rc; + + return (rc & STB6100_LD_LOCK) ? TUNER_STATUS_LOCKED : 0; +} + +static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + int rc; + u8 f; + struct stb6100_state *state = fe->tuner_priv; + + if ((rc = stb6100_read_reg(state, STB6100_F)) < 0) + return rc; + f = rc & STB6100_F_F; + + state->status.bandwidth = (f + 5) * 2000; /* x2 for ZIF */ + + *bandwidth = state->bandwidth = state->status.bandwidth * 1000; + dprintk(verbose, FE_DEBUG, 1, "bandwidth = %u Hz", state->bandwidth); + return 0; +} + +static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) +{ + u32 tmp; + int rc; + struct stb6100_state *state = fe->tuner_priv; + + dprintk(verbose, FE_DEBUG, 1, "set bandwidth to %u Hz", bandwidth); + + bandwidth /= 2; /* ZIF */ + + if (bandwidth >= 36000000) /* F[4:0] BW/2 max =31+5=36 mhz for F=31 */ + tmp = 31; + else if (bandwidth <= 5000000) /* bw/2 min = 5Mhz for F=0 */ + tmp = 0; + else /* if 5 < bw/2 < 36 */ + tmp = (bandwidth + 500000) / 1000000 - 5; + + /* Turn on LPF bandwidth setting clock control, + * set bandwidth, wait 10ms, turn off. + */ + if ((rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d | STB6100_FCCK_FCCK)) < 0) + return rc; + if ((rc = stb6100_write_reg(state, STB6100_F, 0xc0 | tmp)) < 0) + return rc; + msleep(1); + if ((rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d)) < 0) + return rc; + + return 0; +} + +static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + int rc; + u32 nint, nfrac, fvco; + int psd2, odiv; + struct stb6100_state *state = fe->tuner_priv; + u8 regs[STB6100_NUMREGS]; + + if ((rc = stb6100_read_regs(state, regs)) < 0) + return rc; + + odiv = (regs[STB6100_VCO] & STB6100_VCO_ODIV) >> STB6100_VCO_ODIV_SHIFT; + psd2 = (regs[STB6100_K] & STB6100_K_PSD2) >> STB6100_K_PSD2_SHIFT; + nint = regs[STB6100_NI]; + nfrac = ((regs[STB6100_K] & STB6100_K_NF_MSB) << 8) | regs[STB6100_NF_LSB]; + fvco = (nfrac * state->reference >> (9 - psd2)) + (nint * state->reference << psd2); + *frequency = state->frequency = fvco >> (odiv + 1); + + dprintk(verbose, FE_DEBUG, 1, + "frequency = %u kHz, odiv = %u, psd2 = %u, fxtal = %u kHz, fvco = %u kHz, N(I) = %u, N(F) = %u", + state->frequency, odiv, psd2, state->reference, fvco, nint, nfrac); + return 0; +} + + +static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency) +{ + int rc; + const struct stb6100_lkup *ptr; + struct stb6100_state *state = fe->tuner_priv; + struct dvb_frontend_parameters p; + + u32 srate = 0, fvco, nint, nfrac; + u8 regs[STB6100_NUMREGS]; + u8 g, psd2, odiv; + + if ((rc = stb6100_read_regs(state, regs)) < 0) + return rc; + + if (fe->ops.get_frontend) { + dprintk(verbose, FE_DEBUG, 1, "Get frontend parameters"); + fe->ops.get_frontend(fe, &p); + } + srate = p.u.qpsk.symbol_rate; + + regs[STB6100_DLB] = 0xdc; + /* Disable LPEN */ + regs[STB6100_LPEN] &= ~STB6100_LPEN_LPEN; /* PLL Loop disabled */ + + if ((rc = stb6100_write_regs(state, regs)) < 0) + return rc; + + /* Baseband gain. */ + if (srate >= 15000000) + g = 9; // +4 dB + else if (srate >= 5000000) + g = 11; // +8 dB + else + g = 14; // +14 dB + + regs[STB6100_G] = (regs[STB6100_G] & ~STB6100_G_G) | g; + regs[STB6100_G] &= ~STB6100_G_GCT; /* mask GCT */ + regs[STB6100_G] |= (1 << 5); /* 2Vp-p Mode */ + + /* VCO divide ratio (LO divide ratio, VCO prescaler enable). */ + if (frequency <= 1075000) + odiv = 1; + else + odiv = 0; + regs[STB6100_VCO] = (regs[STB6100_VCO] & ~STB6100_VCO_ODIV) | (odiv << STB6100_VCO_ODIV_SHIFT); + + if ((frequency > 1075000) && (frequency <= 1325000)) + psd2 = 0; + else + psd2 = 1; + regs[STB6100_K] = (regs[STB6100_K] & ~STB6100_K_PSD2) | (psd2 << STB6100_K_PSD2_SHIFT); + + /* OSM */ + for (ptr = lkup; + (ptr->val_high != 0) && !CHKRANGE(frequency, ptr->val_low, ptr->val_high); + ptr++); + if (ptr->val_high == 0) { + printk(KERN_ERR "%s: frequency out of range: %u kHz\n", __func__, frequency); + return -EINVAL; + } + regs[STB6100_VCO] = (regs[STB6100_VCO] & ~STB6100_VCO_OSM) | ptr->reg; + + /* F(VCO) = F(LO) * (ODIV == 0 ? 2 : 4) */ + fvco = frequency << (1 + odiv); + /* N(I) = floor(f(VCO) / (f(XTAL) * (PSD2 ? 2 : 1))) */ + nint = fvco / (state->reference << psd2); + /* N(F) = round(f(VCO) / f(XTAL) * (PSD2 ? 2 : 1) - N(I)) * 2 ^ 9 */ + nfrac = (((fvco - (nint * state->reference << psd2)) << (9 - psd2)) + state->reference / 2) / state->reference; + dprintk(verbose, FE_DEBUG, 1, + "frequency = %u, srate = %u, g = %u, odiv = %u, psd2 = %u, fxtal = %u, osm = %u, fvco = %u, N(I) = %u, N(F) = %u", + frequency, srate, (unsigned int)g, (unsigned int)odiv, + (unsigned int)psd2, state->reference, + ptr->reg, fvco, nint, nfrac); + regs[STB6100_NI] = nint; + regs[STB6100_NF_LSB] = nfrac; + regs[STB6100_K] = (regs[STB6100_K] & ~STB6100_K_NF_MSB) | ((nfrac >> 8) & STB6100_K_NF_MSB); + regs[STB6100_VCO] |= STB6100_VCO_OSCH; /* VCO search enabled */ + regs[STB6100_VCO] |= STB6100_VCO_OCK; /* VCO search clock off */ + regs[STB6100_FCCK] |= STB6100_FCCK_FCCK; /* LPF BW setting clock enabled */ + regs[STB6100_LPEN] &= ~STB6100_LPEN_LPEN; /* PLL loop disabled */ + /* Power up. */ + regs[STB6100_LPEN] |= STB6100_LPEN_SYNP | STB6100_LPEN_OSCP | STB6100_LPEN_BEN; + + msleep(2); + if ((rc = stb6100_write_regs(state, regs)) < 0) + return rc; + + msleep(2); + regs[STB6100_LPEN] |= STB6100_LPEN_LPEN; /* PLL loop enabled */ + if ((rc = stb6100_write_reg(state, STB6100_LPEN, regs[STB6100_LPEN])) < 0) + return rc; + + regs[STB6100_VCO] &= ~STB6100_VCO_OCK; /* VCO fast search */ + if ((rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO])) < 0) + return rc; + + msleep(10); /* wait for LO to lock */ + regs[STB6100_VCO] &= ~STB6100_VCO_OSCH; /* vco search disabled */ + regs[STB6100_VCO] |= STB6100_VCO_OCK; /* search clock off */ + if ((rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO])) < 0) + return rc; + regs[STB6100_FCCK] &= ~STB6100_FCCK_FCCK; /* LPF BW clock disabled */ + stb6100_normalise_regs(regs); + if ((rc = stb6100_write_reg_range(state, ®s[1], 1, STB6100_NUMREGS - 3)) < 0) + return rc; + + msleep(100); + + return 0; +} + +static int stb6100_sleep(struct dvb_frontend *fe) +{ + /* TODO: power down */ + return 0; +} + +static int stb6100_init(struct dvb_frontend *fe) +{ + struct stb6100_state *state = fe->tuner_priv; + struct tuner_state *status = &state->status; + + status->tunerstep = 125000; + status->ifreq = 0; + status->refclock = 27000000; /* Hz */ + status->iqsense = 1; + status->bandwidth = 36000; /* kHz */ + state->bandwidth = status->bandwidth * 1000; /* MHz */ + state->reference = status->refclock / 1000; /* kHz */ + + /* Set default bandwidth. */ + return stb6100_set_bandwidth(fe, status->bandwidth); +} + +static int stb6100_get_state(struct dvb_frontend *fe, + enum tuner_param param, + struct tuner_state *state) +{ + switch (param) { + case DVBFE_TUNER_FREQUENCY: + stb6100_get_frequency(fe, &state->frequency); + break; + case DVBFE_TUNER_TUNERSTEP: + break; + case DVBFE_TUNER_IFFREQ: + break; + case DVBFE_TUNER_BANDWIDTH: + stb6100_get_bandwidth(fe, &state->bandwidth); + break; + case DVBFE_TUNER_REFCLOCK: + break; + default: + break; + } + + return 0; +} + +static int stb6100_set_state(struct dvb_frontend *fe, + enum tuner_param param, + struct tuner_state *state) +{ + struct stb6100_state *tstate = fe->tuner_priv; + + switch (param) { + case DVBFE_TUNER_FREQUENCY: + stb6100_set_frequency(fe, state->frequency); + tstate->frequency = state->frequency; + break; + case DVBFE_TUNER_TUNERSTEP: + break; + case DVBFE_TUNER_IFFREQ: + break; + case DVBFE_TUNER_BANDWIDTH: + stb6100_set_bandwidth(fe, state->bandwidth); + tstate->bandwidth = state->bandwidth; + break; + case DVBFE_TUNER_REFCLOCK: + break; + default: + break; + } + + return 0; +} + +static struct dvb_tuner_ops stb6100_ops = { + .info = { + .name = "STB6100 Silicon Tuner", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_step = 0, + }, + + .init = stb6100_init, + .sleep = stb6100_sleep, + .get_status = stb6100_get_status, + .get_state = stb6100_get_state, + .set_state = stb6100_set_state, + .release = stb6100_release +}; + +struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe, + struct stb6100_config *config, + struct i2c_adapter *i2c) +{ + struct stb6100_state *state = NULL; + + state = kzalloc(sizeof (struct stb6100_state), GFP_KERNEL); + if (state == NULL) + goto error; + + state->config = config; + state->i2c = i2c; + state->frontend = fe; + state->reference = config->refclock / 1000; /* kHz */ + fe->tuner_priv = state; + fe->ops.tuner_ops = stb6100_ops; + + printk("%s: Attaching STB6100 \n", __func__); + return fe; + +error: + kfree(state); + return NULL; +} + +static int stb6100_release(struct dvb_frontend *fe) +{ + struct stb6100_state *state = fe->tuner_priv; + + fe->tuner_priv = NULL; + kfree(state); + + return 0; +} + +EXPORT_SYMBOL(stb6100_attach); +MODULE_PARM_DESC(verbose, "Set Verbosity level"); + +MODULE_AUTHOR("Manu Abraham"); +MODULE_DESCRIPTION("STB6100 Silicon tuner"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/stb6100.h b/drivers/media/dvb/frontends/stb6100.h new file mode 100644 index 0000000..395d056 --- /dev/null +++ b/drivers/media/dvb/frontends/stb6100.h @@ -0,0 +1,115 @@ +/* + STB6100 Silicon Tuner + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + + 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 __STB_6100_REG_H +#define __STB_6100_REG_H + +#include <linux/dvb/frontend.h> +#include "dvb_frontend.h" + +#define STB6100_LD 0x00 +#define STB6100_LD_LOCK (1 << 0) + +#define STB6100_VCO 0x01 +#define STB6100_VCO_OSCH (0x01 << 7) +#define STB6100_VCO_OSCH_SHIFT 7 +#define STB6100_VCO_OCK (0x03 << 5) +#define STB6100_VCO_OCK_SHIFT 5 +#define STB6100_VCO_ODIV (0x01 << 4) +#define STB6100_VCO_ODIV_SHIFT 4 +#define STB6100_VCO_OSM (0x0f << 0) + +#define STB6100_NI 0x02 +#define STB6100_NF_LSB 0x03 + +#define STB6100_K 0x04 +#define STB6100_K_PSD2 (0x01 << 2) +#define STB6100_K_PSD2_SHIFT 2 +#define STB6100_K_NF_MSB (0x03 << 0) + +#define STB6100_G 0x05 +#define STB6100_G_G (0x0f << 0) +#define STB6100_G_GCT (0x07 << 5) + +#define STB6100_F 0x06 +#define STB6100_F_F (0x1f << 0) + +#define STB6100_DLB 0x07 + +#define STB6100_TEST1 0x08 + +#define STB6100_FCCK 0x09 +#define STB6100_FCCK_FCCK (0x01 << 6) + +#define STB6100_LPEN 0x0a +#define STB6100_LPEN_LPEN (0x01 << 4) +#define STB6100_LPEN_SYNP (0x01 << 5) +#define STB6100_LPEN_OSCP (0x01 << 6) +#define STB6100_LPEN_BEN (0x01 << 7) + +#define STB6100_TEST3 0x0b + +#define STB6100_NUMREGS 0x0c + + +#define INRANGE(val, x, y) (((x <= val) && (val <= y)) || \ + ((y <= val) && (val <= x)) ? 1 : 0) + +#define CHKRANGE(val, x, y) (((val >= x) && (val < y)) ? 1 : 0) + +struct stb6100_config { + u8 tuner_address; + u32 refclock; +}; + +struct stb6100_state { + struct i2c_adapter *i2c; + + const struct stb6100_config *config; + struct dvb_tuner_ops ops; + struct dvb_frontend *frontend; + struct tuner_state status; + + u32 frequency; + u32 srate; + u32 bandwidth; + u32 reference; +}; + +#if defined(CONFIG_DVB_STB6100) || (defined(CONFIG_DVB_STB6100_MODULE) && defined(MODULE)) + +extern struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe, + struct stb6100_config *config, + struct i2c_adapter *i2c); + +#else + +static inline struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe, + struct stb6100_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif //CONFIG_DVB_STB6100 + +#endif diff --git a/drivers/media/dvb/frontends/stb6100_cfg.h b/drivers/media/dvb/frontends/stb6100_cfg.h new file mode 100644 index 0000000..d313340 --- /dev/null +++ b/drivers/media/dvb/frontends/stb6100_cfg.h @@ -0,0 +1,108 @@ +/* + STB6100 Silicon Tuner + Copyright (C) Manu Abraham (abraham.manu@gmail.com) + + Copyright (C) ST Microelectronics + + 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. +*/ + +static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + struct tuner_state t_state; + int err = 0; + + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->get_state) { + if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { + printk("%s: Invalid parameter\n", __func__); + return err; + } + *frequency = t_state.frequency; + printk("%s: Frequency=%d\n", __func__, t_state.frequency); + } + return 0; +} + +static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + struct tuner_state t_state; + int err = 0; + + t_state.frequency = frequency; + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->set_state) { + if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { + printk("%s: Invalid parameter\n", __func__); + return err; + } + } + printk("%s: Frequency=%d\n", __func__, t_state.frequency); + return 0; +} + +static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; + struct tuner_state t_state; + int err = 0; + + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->get_state) { + if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { + printk("%s: Invalid parameter\n", __func__); + return err; + } + *bandwidth = t_state.bandwidth; + } + printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth); + return 0; +} + +static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + struct tuner_state t_state; + int err = 0; + + t_state.bandwidth = bandwidth; + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->set_state) { + if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { + printk("%s: Invalid parameter\n", __func__); + return err; + } + } + printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth); + return 0; +} diff --git a/drivers/media/dvb/frontends/tda8261.c b/drivers/media/dvb/frontends/tda8261.c new file mode 100644 index 0000000..b6d1777 --- /dev/null +++ b/drivers/media/dvb/frontends/tda8261.c @@ -0,0 +1,230 @@ +/* + TDA8261 8PSK/QPSK tuner driver + Copyright (C) Manu Abraham (abraham.manu@gmail.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/init.h> +#include <linux/kernel.h> +#include <linux/module.h> + +#include "dvb_frontend.h" +#include "tda8261.h" + +struct tda8261_state { + struct dvb_frontend *fe; + struct i2c_adapter *i2c; + const struct tda8261_config *config; + + /* state cache */ + u32 frequency; + u32 bandwidth; +}; + +static int tda8261_read(struct tda8261_state *state, u8 *buf) +{ + const struct tda8261_config *config = state->config; + int err = 0; + struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD,.buf = buf, .len = 2 }; + + if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) + printk("%s: read error, err=%d\n", __func__, err); + + return err; +} + +static int tda8261_write(struct tda8261_state *state, u8 *buf) +{ + const struct tda8261_config *config = state->config; + int err = 0; + struct i2c_msg msg = { .addr = config->addr, .flags = 0, .buf = buf, .len = 4 }; + + if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) + printk("%s: write error, err=%d\n", __func__, err); + + return err; +} + +static int tda8261_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct tda8261_state *state = fe->tuner_priv; + u8 result = 0; + int err = 0; + + *status = 0; + + if ((err = tda8261_read(state, &result)) < 0) { + printk("%s: I/O Error\n", __func__); + return err; + } + if ((result >> 6) & 0x01) { + printk("%s: Tuner Phase Locked\n", __func__); + *status = 1; + } + + return err; +} + +static const u32 div_tab[] = { 2000, 1000, 500, 250, 125 }; /* kHz */ +static const u8 ref_div[] = { 0x00, 0x01, 0x02, 0x05, 0x07 }; + +static int tda8261_get_state(struct dvb_frontend *fe, + enum tuner_param param, + struct tuner_state *tstate) +{ + struct tda8261_state *state = fe->tuner_priv; + int err = 0; + + switch (param) { + case DVBFE_TUNER_FREQUENCY: + tstate->frequency = state->frequency; + break; + case DVBFE_TUNER_BANDWIDTH: + tstate->bandwidth = 40000000; /* FIXME! need to calculate Bandwidth */ + break; + default: + printk("%s: Unknown parameter (param=%d)\n", __func__, param); + err = -EINVAL; + break; + } + + return err; +} + +static int tda8261_set_state(struct dvb_frontend *fe, + enum tuner_param param, + struct tuner_state *tstate) +{ + struct tda8261_state *state = fe->tuner_priv; + const struct tda8261_config *config = state->config; + u32 frequency, N, status = 0; + u8 buf[4]; + int err = 0; + + if (param & DVBFE_TUNER_FREQUENCY) { + /** + * N = Max VCO Frequency / Channel Spacing + * Max VCO Frequency = VCO frequency + (channel spacing - 1) + * (to account for half channel spacing on either side) + */ + frequency = tstate->frequency; + if ((frequency < 950000) || (frequency > 2150000)) { + printk("%s: Frequency beyond limits, frequency=%d\n", __func__, frequency); + return -EINVAL; + } + N = (frequency + (div_tab[config->step_size] - 1)) / div_tab[config->step_size]; + printk("%s: Step size=%d, Divider=%d, PG=0x%02x (%d)\n", + __func__, config->step_size, div_tab[config->step_size], N, N); + + buf[0] = (N >> 8) & 0xff; + buf[1] = N & 0xff; + buf[2] = (0x01 << 7) | ((ref_div[config->step_size] & 0x07) << 1); + + if (frequency < 1450000) + buf[3] = 0x00; + if (frequency < 2000000) + buf[3] = 0x40; + if (frequency < 2150000) + buf[3] = 0x80; + + /* Set params */ + if ((err = tda8261_write(state, buf)) < 0) { + printk("%s: I/O Error\n", __func__); + return err; + } + /* sleep for some time */ + printk("%s: Waiting to Phase LOCK\n", __func__); + msleep(20); + /* check status */ + if ((err = tda8261_get_status(fe, &status)) < 0) { + printk("%s: I/O Error\n", __func__); + return err; + } + if (status == 1) { + printk("%s: Tuner Phase locked: status=%d\n", __func__, status); + state->frequency = frequency; /* cache successful state */ + } else { + printk("%s: No Phase lock: status=%d\n", __func__, status); + } + } else { + printk("%s: Unknown parameter (param=%d)\n", __func__, param); + return -EINVAL; + } + + return 0; +} + +static int tda8261_release(struct dvb_frontend *fe) +{ + struct tda8261_state *state = fe->tuner_priv; + + fe->tuner_priv = NULL; + kfree(state); + return 0; +} + +static struct dvb_tuner_ops tda8261_ops = { + + .info = { + .name = "TDA8261", +// .tuner_name = NULL, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_step = 0 + }, + + .set_state = tda8261_set_state, + .get_state = tda8261_get_state, + .get_status = tda8261_get_status, + .release = tda8261_release +}; + +struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, + const struct tda8261_config *config, + struct i2c_adapter *i2c) +{ + struct tda8261_state *state = NULL; + + if ((state = kzalloc(sizeof (struct tda8261_state), GFP_KERNEL)) == NULL) + goto exit; + + state->config = config; + state->i2c = i2c; + state->fe = fe; + fe->tuner_priv = state; + fe->ops.tuner_ops = tda8261_ops; + + fe->ops.tuner_ops.info.frequency_step = div_tab[config->step_size]; +// fe->ops.tuner_ops.tuner_name = &config->buf; + +// printk("%s: Attaching %s TDA8261 8PSK/QPSK tuner\n", +// __func__, fe->ops.tuner_ops.tuner_name); + printk("%s: Attaching TDA8261 8PSK/QPSK tuner\n", __func__); + + return fe; + +exit: + kfree(state); + return NULL; +} + +EXPORT_SYMBOL(tda8261_attach); +MODULE_PARM_DESC(verbose, "Set verbosity level"); + +MODULE_AUTHOR("Manu Abraham"); +MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/tda8261.h b/drivers/media/dvb/frontends/tda8261.h new file mode 100644 index 0000000..006e453 --- /dev/null +++ b/drivers/media/dvb/frontends/tda8261.h @@ -0,0 +1,55 @@ +/* + TDA8261 8PSK/QPSK tuner driver + Copyright (C) Manu Abraham (abraham.manu@gmail.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 __TDA8261_H +#define __TDA8261_H + +enum tda8261_step { + TDA8261_STEP_2000 = 0, /* 2000 kHz */ + TDA8261_STEP_1000, /* 1000 kHz */ + TDA8261_STEP_500, /* 500 kHz */ + TDA8261_STEP_250, /* 250 kHz */ + TDA8261_STEP_125 /* 125 kHz */ +}; + +struct tda8261_config { +// u8 buf[16]; + u8 addr; + enum tda8261_step step_size; +}; + +#if defined(CONFIG_DVB_TDA8261) || (defined(CONFIG_DVB_TDA8261_MODULE) && defined(MODULE)) + +extern struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, + const struct tda8261_config *config, + struct i2c_adapter *i2c); + +#else + +static inline struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, + const struct tda8261_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); + return NULL; +} + +#endif //CONFIG_DVB_TDA8261 + +#endif// __TDA8261_H diff --git a/drivers/media/dvb/frontends/tda8261_cfg.h b/drivers/media/dvb/frontends/tda8261_cfg.h new file mode 100644 index 0000000..1af1ee4 --- /dev/null +++ b/drivers/media/dvb/frontends/tda8261_cfg.h @@ -0,0 +1,84 @@ +/* + TDA8261 8PSK/QPSK tuner driver + Copyright (C) Manu Abraham (abraham.manu@gmail.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. +*/ + +static int tda8261_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + struct tuner_state t_state; + int err = 0; + + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->get_state) { + if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { + printk("%s: Invalid parameter\n", __func__); + return err; + } + *frequency = t_state.frequency; + printk("%s: Frequency=%d\n", __func__, t_state.frequency); + } + return 0; +} + +static int tda8261_set_frequency(struct dvb_frontend *fe, u32 frequency) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + struct tuner_state t_state; + int err = 0; + + t_state.frequency = frequency; + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->set_state) { + if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { + printk("%s: Invalid parameter\n", __func__); + return err; + } + } + printk("%s: Frequency=%d\n", __func__, t_state.frequency); + return 0; +} + +static int tda8261_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; + struct tuner_state t_state; + int err = 0; + + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->get_state) { + if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { + printk("%s: Invalid parameter\n", __func__); + return err; + } + *bandwidth = t_state.bandwidth; + } + printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth); + return 0; +} diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c index 36a5a1c..5506f80 100644 --- a/drivers/media/dvb/frontends/zl10353.c +++ b/drivers/media/dvb/frontends/zl10353.c @@ -220,15 +220,18 @@ static int zl10353_set_parameters(struct dvb_frontend *fe, /* These are extrapolated from the 7 and 8MHz values */ zl10353_single_write(fe, MCLK_RATIO, 0x97); zl10353_single_write(fe, 0x64, 0x34); + zl10353_single_write(fe, 0xcc, 0xdd); break; case BANDWIDTH_7_MHZ: zl10353_single_write(fe, MCLK_RATIO, 0x86); zl10353_single_write(fe, 0x64, 0x35); + zl10353_single_write(fe, 0xcc, 0x73); break; case BANDWIDTH_8_MHZ: default: zl10353_single_write(fe, MCLK_RATIO, 0x75); zl10353_single_write(fe, 0x64, 0x36); + zl10353_single_write(fe, 0xcc, 0x73); } zl10353_calc_nominal_rate(fe, op->bandwidth, &nominal_rate); diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c index e98d6ca..fd62e0b 100644 --- a/drivers/media/dvb/siano/sms-cards.c +++ b/drivers/media/dvb/siano/sms-cards.c @@ -38,6 +38,16 @@ struct usb_device_id smsusb_id_table[] = { .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A }, { USB_DEVICE(0x2040, 0x1801), .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B }, + { USB_DEVICE(0x2040, 0x2000), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, + { USB_DEVICE(0x2040, 0x2009), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 }, + { USB_DEVICE(0x2040, 0x200a), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, + { USB_DEVICE(0x2040, 0x2010), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, + { USB_DEVICE(0x2040, 0x2019), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, { USB_DEVICE(0x2040, 0x5500), .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, { USB_DEVICE(0x2040, 0x5510), @@ -96,6 +106,21 @@ static struct sms_board sms_boards[] = { .name = "Hauppauge WinTV MiniStick", .type = SMS_NOVA_B0, .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", + .led_power = 26, + .led_lo = 27, + .led_hi = 28, + }, + [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD] = { + .name = "Hauppauge WinTV MiniCard", + .type = SMS_NOVA_B0, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", + .lna_ctrl = 29, + }, + [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2] = { + .name = "Hauppauge WinTV MiniCard", + .type = SMS_NOVA_B0, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", + .lna_ctrl = 1, }, }; @@ -106,3 +131,88 @@ struct sms_board *sms_get_board(int id) return &sms_boards[id]; } +static int sms_set_gpio(struct smscore_device_t *coredev, u32 pin, int enable) +{ + int ret; + struct smscore_gpio_config gpioconfig = { + .direction = SMS_GPIO_DIRECTION_OUTPUT, + .pullupdown = SMS_GPIO_PULLUPDOWN_NONE, + .inputcharacteristics = SMS_GPIO_INPUTCHARACTERISTICS_NORMAL, + .outputslewrate = SMS_GPIO_OUTPUTSLEWRATE_FAST, + .outputdriving = SMS_GPIO_OUTPUTDRIVING_4mA, + }; + + if (pin == 0) + return -EINVAL; + + ret = smscore_configure_gpio(coredev, pin, &gpioconfig); + + if (ret < 0) + return ret; + + return smscore_set_gpio(coredev, pin, enable); +} + +int sms_board_setup(struct smscore_device_t *coredev) +{ + int board_id = smscore_get_board_id(coredev); + struct sms_board *board = sms_get_board(board_id); + + switch (board_id) { + case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: + /* turn off all LEDs */ + sms_set_gpio(coredev, board->led_power, 0); + sms_set_gpio(coredev, board->led_hi, 0); + sms_set_gpio(coredev, board->led_lo, 0); + break; + case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: + case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: + /* turn off LNA */ + sms_set_gpio(coredev, board->lna_ctrl, 0); + break; + } + return 0; +} + +int sms_board_power(struct smscore_device_t *coredev, int onoff) +{ + int board_id = smscore_get_board_id(coredev); + struct sms_board *board = sms_get_board(board_id); + + switch (board_id) { + case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: + /* power LED */ + sms_set_gpio(coredev, + board->led_power, onoff ? 1 : 0); + break; + case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: + case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: + /* LNA */ + sms_set_gpio(coredev, + board->lna_ctrl, onoff ? 1 : 0); + break; + } + return 0; +} + +int sms_board_led_feedback(struct smscore_device_t *coredev, int led) +{ + int board_id = smscore_get_board_id(coredev); + struct sms_board *board = sms_get_board(board_id); + + /* dont touch GPIO if LEDs are already set */ + if (smscore_led_state(coredev, -1) == led) + return 0; + + switch (board_id) { + case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: + sms_set_gpio(coredev, + board->led_lo, (led & SMS_LED_LO) ? 1 : 0); + sms_set_gpio(coredev, + board->led_hi, (led & SMS_LED_HI) ? 1 : 0); + + smscore_led_state(coredev, led); + break; + } + return 0; +} diff --git a/drivers/media/dvb/siano/sms-cards.h b/drivers/media/dvb/siano/sms-cards.h index c8f3da6..8e0fe9f 100644 --- a/drivers/media/dvb/siano/sms-cards.h +++ b/drivers/media/dvb/siano/sms-cards.h @@ -32,14 +32,27 @@ #define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A 6 #define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B 7 #define SMS1XXX_BOARD_HAUPPAUGE_WINDHAM 8 +#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD 9 +#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 10 struct sms_board { enum sms_device_type_st type; char *name, *fw[DEVICE_MODE_MAX]; + + /* gpios */ + int led_power, led_hi, led_lo, lna_ctrl; }; struct sms_board *sms_get_board(int id); +int sms_board_setup(struct smscore_device_t *coredev); + +#define SMS_LED_OFF 0 +#define SMS_LED_LO 1 +#define SMS_LED_HI 2 +int sms_board_led_feedback(struct smscore_device_t *coredev, int led); +int sms_board_power(struct smscore_device_t *coredev, int onoff); + extern struct usb_device_id smsusb_id_table[]; #endif /* __SMS_CARDS_H__ */ diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c index 6576fbb..cf613f2 100644 --- a/drivers/media/dvb/siano/smscoreapi.c +++ b/drivers/media/dvb/siano/smscoreapi.c @@ -91,6 +91,7 @@ struct smscore_device_t { struct completion init_device_done, reload_start_done, resume_done; int board_id; + int led_state; }; void smscore_set_board_id(struct smscore_device_t *core, int id) @@ -98,6 +99,13 @@ void smscore_set_board_id(struct smscore_device_t *core, int id) core->board_id = id; } +int smscore_led_state(struct smscore_device_t *core, int led) +{ + if (led >= 0) + core->led_state = led; + return core->led_state; +} + int smscore_get_board_id(struct smscore_device_t *core) { return core->board_id; @@ -1187,6 +1195,76 @@ int smsclient_sendrequest(struct smscore_client_t *client, } +int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin, + struct smscore_gpio_config *pinconfig) +{ + struct { + struct SmsMsgHdr_ST hdr; + u32 data[6]; + } msg; + + if (coredev->device_flags & SMS_DEVICE_FAMILY2) { + msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + msg.hdr.msgDstId = HIF_TASK; + msg.hdr.msgFlags = 0; + msg.hdr.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; + msg.hdr.msgLength = sizeof(msg); + + msg.data[0] = pin; + msg.data[1] = pinconfig->pullupdown; + + /* Convert slew rate for Nova: Fast(0) = 3 / Slow(1) = 0; */ + msg.data[2] = pinconfig->outputslewrate == 0 ? 3 : 0; + + switch (pinconfig->outputdriving) { + case SMS_GPIO_OUTPUTDRIVING_16mA: + msg.data[3] = 7; /* Nova - 16mA */ + break; + case SMS_GPIO_OUTPUTDRIVING_12mA: + msg.data[3] = 5; /* Nova - 11mA */ + break; + case SMS_GPIO_OUTPUTDRIVING_8mA: + msg.data[3] = 3; /* Nova - 7mA */ + break; + case SMS_GPIO_OUTPUTDRIVING_4mA: + default: + msg.data[3] = 2; /* Nova - 4mA */ + break; + } + + msg.data[4] = pinconfig->direction; + msg.data[5] = 0; + } else /* TODO: SMS_DEVICE_FAMILY1 */ + return -EINVAL; + + return coredev->sendrequest_handler(coredev->context, + &msg, sizeof(msg)); +} + +int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level) +{ + struct { + struct SmsMsgHdr_ST hdr; + u32 data[3]; + } msg; + + if (pin > MAX_GPIO_PIN_NUMBER) + return -EINVAL; + + msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + msg.hdr.msgDstId = HIF_TASK; + msg.hdr.msgFlags = 0; + msg.hdr.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; + msg.hdr.msgLength = sizeof(msg); + + msg.data[0] = pin; + msg.data[1] = level ? 1 : 0; + msg.data[2] = 0; + + return coredev->sendrequest_handler(coredev->context, + &msg, sizeof(msg)); +} + static int __init smscore_module_init(void) { int rc = 0; diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/dvb/siano/smscoreapi.h index 8d973f7..760e233 100644 --- a/drivers/media/dvb/siano/smscoreapi.h +++ b/drivers/media/dvb/siano/smscoreapi.h @@ -186,6 +186,8 @@ struct smsclient_params_t { #define MSG_SW_RELOAD_EXEC_REQ 704 #define MSG_SW_RELOAD_EXEC_RES 705 #define MSG_SMS_SPI_INT_LINE_SET_REQ 710 +#define MSG_SMS_GPIO_CONFIG_EX_REQ 712 +#define MSG_SMS_GPIO_CONFIG_EX_RES 713 #define MSG_SMS_ISDBT_TUNE_REQ 776 #define MSG_SMS_ISDBT_TUNE_RES 777 @@ -341,6 +343,32 @@ struct SmsMsgStatisticsInfo_ST { }; +struct smscore_gpio_config { +#define SMS_GPIO_DIRECTION_INPUT 0 +#define SMS_GPIO_DIRECTION_OUTPUT 1 + u8 direction; + +#define SMS_GPIO_PULLUPDOWN_NONE 0 +#define SMS_GPIO_PULLUPDOWN_PULLDOWN 1 +#define SMS_GPIO_PULLUPDOWN_PULLUP 2 +#define SMS_GPIO_PULLUPDOWN_KEEPER 3 + u8 pullupdown; + +#define SMS_GPIO_INPUTCHARACTERISTICS_NORMAL 0 +#define SMS_GPIO_INPUTCHARACTERISTICS_SCHMITT 1 + u8 inputcharacteristics; + +#define SMS_GPIO_OUTPUTSLEWRATE_FAST 0 +#define SMS_GPIO_OUTPUTSLEWRATE_SLOW 1 + u8 outputslewrate; + +#define SMS_GPIO_OUTPUTDRIVING_4mA 0 +#define SMS_GPIO_OUTPUTDRIVING_8mA 1 +#define SMS_GPIO_OUTPUTDRIVING_12mA 2 +#define SMS_GPIO_OUTPUTDRIVING_16mA 3 + u8 outputdriving; +}; + struct smsdvb_client_t { struct list_head entry; @@ -353,7 +381,7 @@ struct smsdvb_client_t { struct dvb_frontend frontend; fe_status_t fe_status; - int fe_ber, fe_snr, fe_signal_strength; + int fe_ber, fe_snr, fe_unc, fe_signal_strength; struct completion tune_done, stat_done; @@ -396,9 +424,15 @@ struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev); extern void smscore_putbuffer(struct smscore_device_t *coredev, struct smscore_buffer_t *cb); +int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin, + struct smscore_gpio_config *pinconfig); +int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level); + void smscore_set_board_id(struct smscore_device_t *core, int id); int smscore_get_board_id(struct smscore_device_t *core); +int smscore_led_state(struct smscore_device_t *core, int led); + /* smsdvb.c */ int smsdvb_register(void); void smsdvb_unregister(void); diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c index 8d490e1..2da953a 100644 --- a/drivers/media/dvb/siano/smsdvb.c +++ b/drivers/media/dvb/siano/smsdvb.c @@ -60,6 +60,7 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) client->fe_snr = p->Stat.SNR; client->fe_ber = p->Stat.BER; + client->fe_unc = p->Stat.BERErrorCount; if (p->Stat.InBandPwr < -95) client->fe_signal_strength = 0; @@ -72,6 +73,7 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) client->fe_status = 0; client->fe_snr = client->fe_ber = + client->fe_unc = client->fe_signal_strength = 0; } @@ -165,8 +167,18 @@ static int smsdvb_send_statistics_request(struct smsdvb_client_t *client) struct SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ, DVBT_BDA_CONTROL_MSG_ID, HIF_TASK, sizeof(struct SmsMsgHdr_ST), 0 }; - return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), - &client->stat_done); + int ret = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), + &client->stat_done); + if (ret < 0) + return ret; + + if (client->fe_status & FE_HAS_LOCK) + sms_board_led_feedback(client->coredev, + (client->fe_unc == 0) ? + SMS_LED_HI : SMS_LED_LO); + else + sms_board_led_feedback(client->coredev, SMS_LED_OFF); + return ret; } static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat) @@ -217,6 +229,18 @@ static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) return rc; } +static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + int rc = smsdvb_send_statistics_request(client); + + if (!rc) + *ucblocks = client->fe_unc; + + return rc; +} + static int smsdvb_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) { @@ -273,6 +297,28 @@ static int smsdvb_get_frontend(struct dvb_frontend *fe, /* todo: */ memcpy(fep, &client->fe_params, sizeof(struct dvb_frontend_parameters)); + + return 0; +} + +static int smsdvb_init(struct dvb_frontend *fe) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + + sms_board_power(client->coredev, 1); + + return 0; +} + +static int smsdvb_sleep(struct dvb_frontend *fe) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + + sms_board_led_feedback(client->coredev, SMS_LED_OFF); + sms_board_power(client->coredev, 0); + return 0; } @@ -308,6 +354,10 @@ static struct dvb_frontend_ops smsdvb_fe_ops = { .read_ber = smsdvb_read_ber, .read_signal_strength = smsdvb_read_signal_strength, .read_snr = smsdvb_read_snr, + .read_ucblocks = smsdvb_read_ucblocks, + + .init = smsdvb_init, + .sleep = smsdvb_sleep, }; static int smsdvb_hotplug(struct smscore_device_t *coredev, @@ -402,6 +452,8 @@ static int smsdvb_hotplug(struct smscore_device_t *coredev, sms_info("success"); + sms_board_setup(coredev); + return 0; client_error: diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c index 87a3c24..5d7ca34 100644 --- a/drivers/media/dvb/siano/smsusb.c +++ b/drivers/media/dvb/siano/smsusb.c @@ -432,11 +432,56 @@ static void smsusb_disconnect(struct usb_interface *intf) smsusb_term_device(intf); } +static int smsusb_suspend(struct usb_interface *intf, pm_message_t msg) +{ + struct smsusb_device_t *dev = + (struct smsusb_device_t *)usb_get_intfdata(intf); + printk(KERN_INFO "%s Entering status %d.\n", __func__, msg.event); + smsusb_stop_streaming(dev); + return 0; +} + +static int smsusb_resume(struct usb_interface *intf) +{ + int rc, i; + struct smsusb_device_t *dev = + (struct smsusb_device_t *)usb_get_intfdata(intf); + struct usb_device *udev = interface_to_usbdev(intf); + + printk(KERN_INFO "%s Entering.\n", __func__); + usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81)); + usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02)); + + for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) + printk(KERN_INFO "endpoint %d %02x %02x %d\n", i, + intf->cur_altsetting->endpoint[i].desc.bEndpointAddress, + intf->cur_altsetting->endpoint[i].desc.bmAttributes, + intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize); + + if (intf->num_altsetting > 0) { + rc = usb_set_interface(udev, + intf->cur_altsetting->desc. + bInterfaceNumber, 0); + if (rc < 0) { + printk(KERN_INFO + "%s usb_set_interface failed, rc %d\n", + __func__, rc); + return rc; + } + } + + smsusb_start_streaming(dev); + return 0; +} + static struct usb_driver smsusb_driver = { .name = "sms1xxx", .probe = smsusb_probe, .disconnect = smsusb_disconnect, .id_table = smsusb_id_table, + + .suspend = smsusb_suspend, + .resume = smsusb_resume, }; int smsusb_register(void) diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index 401a04e..ab0bcd2 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -104,6 +104,8 @@ config DVB_BUDGET_CI select DVB_STV0297 if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE + select DVB_STB0899 if !DVB_FE_CUSTOMISE + select DVB_STB6100 if !DVB_FE_CUSTOMISE select DVB_LNBP21 if !DVB_FE_CUSTOMISE select DVB_TDA10023 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE @@ -131,6 +133,8 @@ config DVB_BUDGET_AV select DVB_TDA1004X if !DVB_FE_CUSTOMISE select DVB_TDA10021 if !DVB_FE_CUSTOMISE select DVB_TDA10023 if !DVB_FE_CUSTOMISE + select DVB_STB0899 if !DVB_FE_CUSTOMISE + select DVB_TDA8261 if !DVB_FE_CUSTOMISE select DVB_TUA6100 if !DVB_FE_CUSTOMISE help Support for simple SAA7146 based DVB cards diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 1032ea7..f996cef 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -35,6 +35,11 @@ #include "budget.h" #include "stv0299.h" +#include "stb0899_drv.h" +#include "stb0899_reg.h" +#include "stb0899_cfg.h" +#include "tda8261.h" +#include "tda8261_cfg.h" #include "tda1002x.h" #include "tda1004x.h" #include "tua6100.h" @@ -882,6 +887,281 @@ static struct stv0299_config philips_sd1878_config = { .set_symbol_rate = philips_sd1878_ci_set_symbol_rate, }; +/* KNC1 DVB-S (STB0899) Inittab */ +static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = { + + { STB0899_DEV_ID , 0x81 }, + { STB0899_DISCNTRL1 , 0x32 }, + { STB0899_DISCNTRL2 , 0x80 }, + { STB0899_DISRX_ST0 , 0x04 }, + { STB0899_DISRX_ST1 , 0x00 }, + { STB0899_DISPARITY , 0x00 }, + { STB0899_DISFIFO , 0x00 }, + { STB0899_DISSTATUS , 0x20 }, + { STB0899_DISF22 , 0x8c }, + { STB0899_DISF22RX , 0x9a }, + { STB0899_SYSREG , 0x0b }, + { STB0899_ACRPRESC , 0x11 }, + { STB0899_ACRDIV1 , 0x0a }, + { STB0899_ACRDIV2 , 0x05 }, + { STB0899_DACR1 , 0x00 }, + { STB0899_DACR2 , 0x00 }, + { STB0899_OUTCFG , 0x00 }, + { STB0899_MODECFG , 0x00 }, + { STB0899_IRQSTATUS_3 , 0x30 }, + { STB0899_IRQSTATUS_2 , 0x00 }, + { STB0899_IRQSTATUS_1 , 0x00 }, + { STB0899_IRQSTATUS_0 , 0x00 }, + { STB0899_IRQMSK_3 , 0xf3 }, + { STB0899_IRQMSK_2 , 0xfc }, + { STB0899_IRQMSK_1 , 0xff }, + { STB0899_IRQMSK_0 , 0xff }, + { STB0899_IRQCFG , 0x00 }, + { STB0899_I2CCFG , 0x88 }, + { STB0899_I2CRPT , 0x58 }, /* Repeater=8, Stop=disabled */ + { STB0899_IOPVALUE5 , 0x00 }, + { STB0899_IOPVALUE4 , 0x20 }, + { STB0899_IOPVALUE3 , 0xc9 }, + { STB0899_IOPVALUE2 , 0x90 }, + { STB0899_IOPVALUE1 , 0x40 }, + { STB0899_IOPVALUE0 , 0x00 }, + { STB0899_GPIO00CFG , 0x82 }, + { STB0899_GPIO01CFG , 0x82 }, + { STB0899_GPIO02CFG , 0x82 }, + { STB0899_GPIO03CFG , 0x82 }, + { STB0899_GPIO04CFG , 0x82 }, + { STB0899_GPIO05CFG , 0x82 }, + { STB0899_GPIO06CFG , 0x82 }, + { STB0899_GPIO07CFG , 0x82 }, + { STB0899_GPIO08CFG , 0x82 }, + { STB0899_GPIO09CFG , 0x82 }, + { STB0899_GPIO10CFG , 0x82 }, + { STB0899_GPIO11CFG , 0x82 }, + { STB0899_GPIO12CFG , 0x82 }, + { STB0899_GPIO13CFG , 0x82 }, + { STB0899_GPIO14CFG , 0x82 }, + { STB0899_GPIO15CFG , 0x82 }, + { STB0899_GPIO16CFG , 0x82 }, + { STB0899_GPIO17CFG , 0x82 }, + { STB0899_GPIO18CFG , 0x82 }, + { STB0899_GPIO19CFG , 0x82 }, + { STB0899_GPIO20CFG , 0x82 }, + { STB0899_SDATCFG , 0xb8 }, + { STB0899_SCLTCFG , 0xba }, + { STB0899_AGCRFCFG , 0x08 }, /* 0x1c */ + { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ + { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ + { STB0899_DIRCLKCFG , 0x82 }, + { STB0899_CLKOUT27CFG , 0x7e }, + { STB0899_STDBYCFG , 0x82 }, + { STB0899_CS0CFG , 0x82 }, + { STB0899_CS1CFG , 0x82 }, + { STB0899_DISEQCOCFG , 0x20 }, + { STB0899_GPIO32CFG , 0x82 }, + { STB0899_GPIO33CFG , 0x82 }, + { STB0899_GPIO34CFG , 0x82 }, + { STB0899_GPIO35CFG , 0x82 }, + { STB0899_GPIO36CFG , 0x82 }, + { STB0899_GPIO37CFG , 0x82 }, + { STB0899_GPIO38CFG , 0x82 }, + { STB0899_GPIO39CFG , 0x82 }, + { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ + { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ + { STB0899_FILTCTRL , 0x00 }, + { STB0899_SYSCTRL , 0x00 }, + { STB0899_STOPCLK1 , 0x20 }, + { STB0899_STOPCLK2 , 0x00 }, + { STB0899_INTBUFSTATUS , 0x00 }, + { STB0899_INTBUFCTRL , 0x0a }, + { 0xffff , 0xff }, +}; + +static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = { + { STB0899_DEMOD , 0x00 }, + { STB0899_RCOMPC , 0xc9 }, + { STB0899_AGC1CN , 0x41 }, + { STB0899_AGC1REF , 0x08 }, + { STB0899_RTC , 0x7a }, + { STB0899_TMGCFG , 0x4e }, + { STB0899_AGC2REF , 0x33 }, + { STB0899_TLSR , 0x84 }, + { STB0899_CFD , 0xee }, + { STB0899_ACLC , 0x87 }, + { STB0899_BCLC , 0x94 }, + { STB0899_EQON , 0x41 }, + { STB0899_LDT , 0xdd }, + { STB0899_LDT2 , 0xc9 }, + { STB0899_EQUALREF , 0xb4 }, + { STB0899_TMGRAMP , 0x10 }, + { STB0899_TMGTHD , 0x30 }, + { STB0899_IDCCOMP , 0xfb }, + { STB0899_QDCCOMP , 0x03 }, + { STB0899_POWERI , 0x3b }, + { STB0899_POWERQ , 0x3d }, + { STB0899_RCOMP , 0x81 }, + { STB0899_AGCIQIN , 0x80 }, + { STB0899_AGC2I1 , 0x04 }, + { STB0899_AGC2I2 , 0xf5 }, + { STB0899_TLIR , 0x25 }, + { STB0899_RTF , 0x80 }, + { STB0899_DSTATUS , 0x00 }, + { STB0899_LDI , 0xca }, + { STB0899_CFRM , 0xf1 }, + { STB0899_CFRL , 0xf3 }, + { STB0899_NIRM , 0x2a }, + { STB0899_NIRL , 0x05 }, + { STB0899_ISYMB , 0x17 }, + { STB0899_QSYMB , 0xfa }, + { STB0899_SFRH , 0x2f }, + { STB0899_SFRM , 0x68 }, + { STB0899_SFRL , 0x40 }, + { STB0899_SFRUPH , 0x2f }, + { STB0899_SFRUPM , 0x68 }, + { STB0899_SFRUPL , 0x40 }, + { STB0899_EQUAI1 , 0xfd }, + { STB0899_EQUAQ1 , 0x04 }, + { STB0899_EQUAI2 , 0x0f }, + { STB0899_EQUAQ2 , 0xff }, + { STB0899_EQUAI3 , 0xdf }, + { STB0899_EQUAQ3 , 0xfa }, + { STB0899_EQUAI4 , 0x37 }, + { STB0899_EQUAQ4 , 0x0d }, + { STB0899_EQUAI5 , 0xbd }, + { STB0899_EQUAQ5 , 0xf7 }, + { STB0899_DSTATUS2 , 0x00 }, + { STB0899_VSTATUS , 0x00 }, + { STB0899_VERROR , 0xff }, + { STB0899_IQSWAP , 0x2a }, + { STB0899_ECNT1M , 0x00 }, + { STB0899_ECNT1L , 0x00 }, + { STB0899_ECNT2M , 0x00 }, + { STB0899_ECNT2L , 0x00 }, + { STB0899_ECNT3M , 0x00 }, + { STB0899_ECNT3L , 0x00 }, + { STB0899_FECAUTO1 , 0x06 }, + { STB0899_FECM , 0x01 }, + { STB0899_VTH12 , 0xf0 }, + { STB0899_VTH23 , 0xa0 }, + { STB0899_VTH34 , 0x78 }, + { STB0899_VTH56 , 0x4e }, + { STB0899_VTH67 , 0x48 }, + { STB0899_VTH78 , 0x38 }, + { STB0899_PRVIT , 0xff }, + { STB0899_VITSYNC , 0x19 }, + { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ + { STB0899_TSULC , 0x42 }, + { STB0899_RSLLC , 0x40 }, + { STB0899_TSLPL , 0x12 }, + { STB0899_TSCFGH , 0x0c }, + { STB0899_TSCFGM , 0x00 }, + { STB0899_TSCFGL , 0x0c }, + { STB0899_TSOUT , 0x0d }, /* 0x0d for CAM */ + { STB0899_RSSYNCDEL , 0x00 }, + { STB0899_TSINHDELH , 0x02 }, + { STB0899_TSINHDELM , 0x00 }, + { STB0899_TSINHDELL , 0x00 }, + { STB0899_TSLLSTKM , 0x00 }, + { STB0899_TSLLSTKL , 0x00 }, + { STB0899_TSULSTKM , 0x00 }, + { STB0899_TSULSTKL , 0xab }, + { STB0899_PCKLENUL , 0x00 }, + { STB0899_PCKLENLL , 0xcc }, + { STB0899_RSPCKLEN , 0xcc }, + { STB0899_TSSTATUS , 0x80 }, + { STB0899_ERRCTRL1 , 0xb6 }, + { STB0899_ERRCTRL2 , 0x96 }, + { STB0899_ERRCTRL3 , 0x89 }, + { STB0899_DMONMSK1 , 0x27 }, + { STB0899_DMONMSK0 , 0x03 }, + { STB0899_DEMAPVIT , 0x5c }, + { STB0899_PLPARM , 0x1f }, + { STB0899_PDELCTRL , 0x48 }, + { STB0899_PDELCTRL2 , 0x00 }, + { STB0899_BBHCTRL1 , 0x00 }, + { STB0899_BBHCTRL2 , 0x00 }, + { STB0899_HYSTTHRESH , 0x77 }, + { STB0899_MATCSTM , 0x00 }, + { STB0899_MATCSTL , 0x00 }, + { STB0899_UPLCSTM , 0x00 }, + { STB0899_UPLCSTL , 0x00 }, + { STB0899_DFLCSTM , 0x00 }, + { STB0899_DFLCSTL , 0x00 }, + { STB0899_SYNCCST , 0x00 }, + { STB0899_SYNCDCSTM , 0x00 }, + { STB0899_SYNCDCSTL , 0x00 }, + { STB0899_ISI_ENTRY , 0x00 }, + { STB0899_ISI_BIT_EN , 0x00 }, + { STB0899_MATSTRM , 0x00 }, + { STB0899_MATSTRL , 0x00 }, + { STB0899_UPLSTRM , 0x00 }, + { STB0899_UPLSTRL , 0x00 }, + { STB0899_DFLSTRM , 0x00 }, + { STB0899_DFLSTRL , 0x00 }, + { STB0899_SYNCSTR , 0x00 }, + { STB0899_SYNCDSTRM , 0x00 }, + { STB0899_SYNCDSTRL , 0x00 }, + { STB0899_CFGPDELSTATUS1 , 0x10 }, + { STB0899_CFGPDELSTATUS2 , 0x00 }, + { STB0899_BBFERRORM , 0x00 }, + { STB0899_BBFERRORL , 0x00 }, + { STB0899_UPKTERRORM , 0x00 }, + { STB0899_UPKTERRORL , 0x00 }, + { 0xffff , 0xff }, +}; + +/* STB0899 demodulator config for the KNC1 and clones */ +static struct stb0899_config knc1_dvbs2_config = { + .init_dev = knc1_stb0899_s1_init_1, + .init_s2_demod = stb0899_s2_init_2, + .init_s1_demod = knc1_stb0899_s1_init_3, + .init_s2_fec = stb0899_s2_init_4, + .init_tst = stb0899_s1_init_5, + + .postproc = NULL, + + .demod_address = 0x68, +// .ts_output_mode = STB0899_OUT_PARALLEL, /* types = SERIAL/PARALLEL */ + .block_sync_mode = STB0899_SYNC_FORCED, /* DSS, SYNC_FORCED/UNSYNCED */ +// .ts_pfbit_toggle = STB0899_MPEG_NORMAL, /* DirecTV, MPEG toggling seq */ + + .xtal_freq = 27000000, + .inversion = IQ_SWAP_OFF, /* 1 */ + + .lo_clk = 76500000, + .hi_clk = 90000000, + + .esno_ave = STB0899_DVBS2_ESNO_AVE, + .esno_quant = STB0899_DVBS2_ESNO_QUANT, + .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, + .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, + .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, + .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, + .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, + .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, + .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, + + .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, + .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, + .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, + .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, + + .tuner_get_frequency = tda8261_get_frequency, + .tuner_set_frequency = tda8261_set_frequency, + .tuner_set_bandwidth = NULL, + .tuner_get_bandwidth = tda8261_get_bandwidth, + .tuner_set_rfsiggain = NULL +}; + +/* + * SD1878/SHA tuner config + * 1F, Single I/P, Horizontal mount, High Sensitivity + */ +static const struct tda8261_config sd1878c_config = { +// .name = "SD1878/SHA", + .addr = 0x60, + .step_size = TDA8261_STEP_1000 /* kHz */ +}; + static u8 read_pwm(struct budget_av *budget_av) { u8 b = 0xff; @@ -905,8 +1185,11 @@ static u8 read_pwm(struct budget_av *budget_av) #define SUBID_DVBS_TV_STAR 0x0014 #define SUBID_DVBS_TV_STAR_PLUS_X4 0x0015 #define SUBID_DVBS_TV_STAR_CI 0x0016 +#define SUBID_DVBS2_KNC1 0x0018 +#define SUBID_DVBS2_KNC1_OEM 0x0019 #define SUBID_DVBS_EASYWATCH_1 0x001a #define SUBID_DVBS_EASYWATCH_2 0x001b +#define SUBID_DVBS2_EASYWATCH 0x001d #define SUBID_DVBS_EASYWATCH 0x001e #define SUBID_DVBC_EASYWATCH 0x002a @@ -941,6 +1224,9 @@ static void frontend_init(struct budget_av *budget_av) case SUBID_DVBT_KNC1_PLUS: case SUBID_DVBC_EASYWATCH: case SUBID_DVBC_KNC1_PLUS_MK3: + case SUBID_DVBS2_KNC1: + case SUBID_DVBS2_KNC1_OEM: + case SUBID_DVBS2_EASYWATCH: saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI); break; } @@ -993,7 +1279,14 @@ static void frontend_init(struct budget_av *budget_av) fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; } break; + case SUBID_DVBS2_KNC1: + case SUBID_DVBS2_KNC1_OEM: + case SUBID_DVBS2_EASYWATCH: + budget_av->reinitialise_demod = 1; + if ((fe = dvb_attach(stb0899_attach, &knc1_dvbs2_config, &budget_av->budget.i2c_adap))) + dvb_attach(tda8261_attach, fe, &sd1878c_config, &budget_av->budget.i2c_adap); + break; case SUBID_DVBS_CINERGY1200: fe = dvb_attach(stv0299_attach, &cinergy_1200s_config, &budget_av->budget.i2c_adap); @@ -1260,6 +1553,8 @@ static struct saa7146_ext_vv vv_data = { static struct saa7146_extension budget_extension; MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S); +MAKE_BUDGET_INFO(knc1s2,"KNC1 DVB-S2", BUDGET_KNC1S2); +MAKE_BUDGET_INFO(sates2,"Satelco EasyWatch DVB-S2", BUDGET_KNC1S2); MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C); MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T); MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR); @@ -1290,6 +1585,9 @@ static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014), MAKE_EXTENSION_PCI(knc1spx4, 0x1894, 0x0015), MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016), + MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0018), + MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0019), + MAKE_EXTENSION_PCI(sates2, 0x1894, 0x001d), MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e), MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a), MAKE_EXTENSION_PCI(satewps, 0x1894, 0x001b), diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 0a5aad4..3507463 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -43,6 +43,11 @@ #include "stv0299.h" #include "stv0297.h" #include "tda1004x.h" +#include "stb0899_drv.h" +#include "stb0899_reg.h" +#include "stb0899_cfg.h" +#include "stb6100.h" +#include "stb6100_cfg.h" #include "lnbp21.h" #include "bsbe1.h" #include "bsru6.h" @@ -1071,7 +1076,271 @@ static struct tda10023_config tda10023_config = { .deltaf = 0xa511, }; +/* TT S2-3200 DVB-S (STB0899) Inittab */ +static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = { + + { STB0899_DEV_ID , 0x81 }, + { STB0899_DISCNTRL1 , 0x32 }, + { STB0899_DISCNTRL2 , 0x80 }, + { STB0899_DISRX_ST0 , 0x04 }, + { STB0899_DISRX_ST1 , 0x00 }, + { STB0899_DISPARITY , 0x00 }, + { STB0899_DISFIFO , 0x00 }, + { STB0899_DISSTATUS , 0x20 }, + { STB0899_DISF22 , 0x8c }, + { STB0899_DISF22RX , 0x9a }, + { STB0899_SYSREG , 0x0b }, + { STB0899_ACRPRESC , 0x11 }, + { STB0899_ACRDIV1 , 0x0a }, + { STB0899_ACRDIV2 , 0x05 }, + { STB0899_DACR1 , 0x00 }, + { STB0899_DACR2 , 0x00 }, + { STB0899_OUTCFG , 0x00 }, + { STB0899_MODECFG , 0x00 }, + { STB0899_IRQSTATUS_3 , 0x30 }, + { STB0899_IRQSTATUS_2 , 0x00 }, + { STB0899_IRQSTATUS_1 , 0x00 }, + { STB0899_IRQSTATUS_0 , 0x00 }, + { STB0899_IRQMSK_3 , 0xf3 }, + { STB0899_IRQMSK_2 , 0xfc }, + { STB0899_IRQMSK_1 , 0xff }, + { STB0899_IRQMSK_0 , 0xff }, + { STB0899_IRQCFG , 0x00 }, + { STB0899_I2CCFG , 0x88 }, + { STB0899_I2CRPT , 0x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */ + { STB0899_IOPVALUE5 , 0x00 }, + { STB0899_IOPVALUE4 , 0x20 }, + { STB0899_IOPVALUE3 , 0xc9 }, + { STB0899_IOPVALUE2 , 0x90 }, + { STB0899_IOPVALUE1 , 0x40 }, + { STB0899_IOPVALUE0 , 0x00 }, + { STB0899_GPIO00CFG , 0x82 }, + { STB0899_GPIO01CFG , 0x82 }, + { STB0899_GPIO02CFG , 0x82 }, + { STB0899_GPIO03CFG , 0x82 }, + { STB0899_GPIO04CFG , 0x82 }, + { STB0899_GPIO05CFG , 0x82 }, + { STB0899_GPIO06CFG , 0x82 }, + { STB0899_GPIO07CFG , 0x82 }, + { STB0899_GPIO08CFG , 0x82 }, + { STB0899_GPIO09CFG , 0x82 }, + { STB0899_GPIO10CFG , 0x82 }, + { STB0899_GPIO11CFG , 0x82 }, + { STB0899_GPIO12CFG , 0x82 }, + { STB0899_GPIO13CFG , 0x82 }, + { STB0899_GPIO14CFG , 0x82 }, + { STB0899_GPIO15CFG , 0x82 }, + { STB0899_GPIO16CFG , 0x82 }, + { STB0899_GPIO17CFG , 0x82 }, + { STB0899_GPIO18CFG , 0x82 }, + { STB0899_GPIO19CFG , 0x82 }, + { STB0899_GPIO20CFG , 0x82 }, + { STB0899_SDATCFG , 0xb8 }, + { STB0899_SCLTCFG , 0xba }, + { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ + { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ + { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ + { STB0899_DIRCLKCFG , 0x82 }, + { STB0899_CLKOUT27CFG , 0x7e }, + { STB0899_STDBYCFG , 0x82 }, + { STB0899_CS0CFG , 0x82 }, + { STB0899_CS1CFG , 0x82 }, + { STB0899_DISEQCOCFG , 0x20 }, + { STB0899_GPIO32CFG , 0x82 }, + { STB0899_GPIO33CFG , 0x82 }, + { STB0899_GPIO34CFG , 0x82 }, + { STB0899_GPIO35CFG , 0x82 }, + { STB0899_GPIO36CFG , 0x82 }, + { STB0899_GPIO37CFG , 0x82 }, + { STB0899_GPIO38CFG , 0x82 }, + { STB0899_GPIO39CFG , 0x82 }, + { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ + { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ + { STB0899_FILTCTRL , 0x00 }, + { STB0899_SYSCTRL , 0x00 }, + { STB0899_STOPCLK1 , 0x20 }, + { STB0899_STOPCLK2 , 0x00 }, + { STB0899_INTBUFSTATUS , 0x00 }, + { STB0899_INTBUFCTRL , 0x0a }, + { 0xffff , 0xff }, +}; + +static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = { + { STB0899_DEMOD , 0x00 }, + { STB0899_RCOMPC , 0xc9 }, + { STB0899_AGC1CN , 0x41 }, + { STB0899_AGC1REF , 0x10 }, + { STB0899_RTC , 0x7a }, + { STB0899_TMGCFG , 0x4e }, + { STB0899_AGC2REF , 0x34 }, + { STB0899_TLSR , 0x84 }, + { STB0899_CFD , 0xc7 }, + { STB0899_ACLC , 0x87 }, + { STB0899_BCLC , 0x94 }, + { STB0899_EQON , 0x41 }, + { STB0899_LDT , 0xdd }, + { STB0899_LDT2 , 0xc9 }, + { STB0899_EQUALREF , 0xb4 }, + { STB0899_TMGRAMP , 0x10 }, + { STB0899_TMGTHD , 0x30 }, + { STB0899_IDCCOMP , 0xfb }, + { STB0899_QDCCOMP , 0x03 }, + { STB0899_POWERI , 0x3b }, + { STB0899_POWERQ , 0x3d }, + { STB0899_RCOMP , 0x81 }, + { STB0899_AGCIQIN , 0x80 }, + { STB0899_AGC2I1 , 0x04 }, + { STB0899_AGC2I2 , 0xf5 }, + { STB0899_TLIR , 0x25 }, + { STB0899_RTF , 0x80 }, + { STB0899_DSTATUS , 0x00 }, + { STB0899_LDI , 0xca }, + { STB0899_CFRM , 0xf1 }, + { STB0899_CFRL , 0xf3 }, + { STB0899_NIRM , 0x2a }, + { STB0899_NIRL , 0x05 }, + { STB0899_ISYMB , 0x17 }, + { STB0899_QSYMB , 0xfa }, + { STB0899_SFRH , 0x2f }, + { STB0899_SFRM , 0x68 }, + { STB0899_SFRL , 0x40 }, + { STB0899_SFRUPH , 0x2f }, + { STB0899_SFRUPM , 0x68 }, + { STB0899_SFRUPL , 0x40 }, + { STB0899_EQUAI1 , 0xfd }, + { STB0899_EQUAQ1 , 0x04 }, + { STB0899_EQUAI2 , 0x0f }, + { STB0899_EQUAQ2 , 0xff }, + { STB0899_EQUAI3 , 0xdf }, + { STB0899_EQUAQ3 , 0xfa }, + { STB0899_EQUAI4 , 0x37 }, + { STB0899_EQUAQ4 , 0x0d }, + { STB0899_EQUAI5 , 0xbd }, + { STB0899_EQUAQ5 , 0xf7 }, + { STB0899_DSTATUS2 , 0x00 }, + { STB0899_VSTATUS , 0x00 }, + { STB0899_VERROR , 0xff }, + { STB0899_IQSWAP , 0x2a }, + { STB0899_ECNT1M , 0x00 }, + { STB0899_ECNT1L , 0x00 }, + { STB0899_ECNT2M , 0x00 }, + { STB0899_ECNT2L , 0x00 }, + { STB0899_ECNT3M , 0x00 }, + { STB0899_ECNT3L , 0x00 }, + { STB0899_FECAUTO1 , 0x06 }, + { STB0899_FECM , 0x01 }, + { STB0899_VTH12 , 0xf0 }, + { STB0899_VTH23 , 0xa0 }, + { STB0899_VTH34 , 0x78 }, + { STB0899_VTH56 , 0x4e }, + { STB0899_VTH67 , 0x48 }, + { STB0899_VTH78 , 0x38 }, + { STB0899_PRVIT , 0xff }, + { STB0899_VITSYNC , 0x19 }, + { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ + { STB0899_TSULC , 0x42 }, + { STB0899_RSLLC , 0x40 }, + { STB0899_TSLPL , 0x12 }, + { STB0899_TSCFGH , 0x0c }, + { STB0899_TSCFGM , 0x00 }, + { STB0899_TSCFGL , 0x0c }, + { STB0899_TSOUT , 0x0d }, /* 0x0d for CAM */ + { STB0899_RSSYNCDEL , 0x00 }, + { STB0899_TSINHDELH , 0x02 }, + { STB0899_TSINHDELM , 0x00 }, + { STB0899_TSINHDELL , 0x00 }, + { STB0899_TSLLSTKM , 0x00 }, + { STB0899_TSLLSTKL , 0x00 }, + { STB0899_TSULSTKM , 0x00 }, + { STB0899_TSULSTKL , 0xab }, + { STB0899_PCKLENUL , 0x00 }, + { STB0899_PCKLENLL , 0xcc }, + { STB0899_RSPCKLEN , 0xcc }, + { STB0899_TSSTATUS , 0x80 }, + { STB0899_ERRCTRL1 , 0xb6 }, + { STB0899_ERRCTRL2 , 0x96 }, + { STB0899_ERRCTRL3 , 0x89 }, + { STB0899_DMONMSK1 , 0x27 }, + { STB0899_DMONMSK0 , 0x03 }, + { STB0899_DEMAPVIT , 0x5c }, + { STB0899_PLPARM , 0x1f }, + { STB0899_PDELCTRL , 0x48 }, + { STB0899_PDELCTRL2 , 0x00 }, + { STB0899_BBHCTRL1 , 0x00 }, + { STB0899_BBHCTRL2 , 0x00 }, + { STB0899_HYSTTHRESH , 0x77 }, + { STB0899_MATCSTM , 0x00 }, + { STB0899_MATCSTL , 0x00 }, + { STB0899_UPLCSTM , 0x00 }, + { STB0899_UPLCSTL , 0x00 }, + { STB0899_DFLCSTM , 0x00 }, + { STB0899_DFLCSTL , 0x00 }, + { STB0899_SYNCCST , 0x00 }, + { STB0899_SYNCDCSTM , 0x00 }, + { STB0899_SYNCDCSTL , 0x00 }, + { STB0899_ISI_ENTRY , 0x00 }, + { STB0899_ISI_BIT_EN , 0x00 }, + { STB0899_MATSTRM , 0x00 }, + { STB0899_MATSTRL , 0x00 }, + { STB0899_UPLSTRM , 0x00 }, + { STB0899_UPLSTRL , 0x00 }, + { STB0899_DFLSTRM , 0x00 }, + { STB0899_DFLSTRL , 0x00 }, + { STB0899_SYNCSTR , 0x00 }, + { STB0899_SYNCDSTRM , 0x00 }, + { STB0899_SYNCDSTRL , 0x00 }, + { STB0899_CFGPDELSTATUS1 , 0x10 }, + { STB0899_CFGPDELSTATUS2 , 0x00 }, + { STB0899_BBFERRORM , 0x00 }, + { STB0899_BBFERRORL , 0x00 }, + { STB0899_UPKTERRORM , 0x00 }, + { STB0899_UPKTERRORL , 0x00 }, + { 0xffff , 0xff }, +}; +static struct stb0899_config tt3200_config = { + .init_dev = tt3200_stb0899_s1_init_1, + .init_s2_demod = stb0899_s2_init_2, + .init_s1_demod = tt3200_stb0899_s1_init_3, + .init_s2_fec = stb0899_s2_init_4, + .init_tst = stb0899_s1_init_5, + + .postproc = NULL, + + .demod_address = 0x68, + + .xtal_freq = 27000000, + .inversion = IQ_SWAP_ON, /* 1 */ + + .lo_clk = 76500000, + .hi_clk = 99000000, + + .esno_ave = STB0899_DVBS2_ESNO_AVE, + .esno_quant = STB0899_DVBS2_ESNO_QUANT, + .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, + .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, + .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, + .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, + .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, + .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, + .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, + + .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, + .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, + .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, + .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, + + .tuner_get_frequency = stb6100_get_frequency, + .tuner_set_frequency = stb6100_set_frequency, + .tuner_set_bandwidth = stb6100_set_bandwidth, + .tuner_get_bandwidth = stb6100_get_bandwidth, + .tuner_set_rfsiggain = NULL +}; + +struct stb6100_config tt3200_stb6100_config = { + .tuner_address = 0x60, + .refclock = 27000000, +}; static void frontend_init(struct budget_ci *budget_ci) { @@ -1152,6 +1421,46 @@ static void frontend_init(struct budget_ci *budget_ci) } } break; + + case 0x1019: // TT S2-3200 PCI + /* + * NOTE! on some STB0899 versions, the internal PLL takes a longer time + * to settle, aka LOCK. On the older revisions of the chip, we don't see + * this, as a result on the newer chips the entire clock tree, will not + * be stable after a freshly POWER 'ed up situation. + * In this case, we should RESET the STB0899 (Active LOW) and wait for + * PLL stabilization. + * + * On the TT S2 3200 and clones, the STB0899 demodulator's RESETB is + * connected to the SAA7146 GPIO, GPIO2, Pin 142 + */ + /* Reset Demodulator */ + saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTLO); + /* Wait for everything to die */ + msleep(50); + /* Pull it up out of Reset state */ + saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTHI); + /* Wait for PLL to stabilize */ + msleep(250); + /* + * PLL state should be stable now. Ideally, we should check + * for PLL LOCK status. But well, never mind! + */ + budget_ci->budget.dvb_frontend = dvb_attach(stb0899_attach, &tt3200_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) { + if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { + printk("%s: No LNBP21 found!\n", __FUNCTION__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } else { + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } + break; + } if (budget_ci->budget.dvb_frontend == NULL) { @@ -1242,6 +1551,7 @@ MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT); MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT); MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(tt3200, "TT-Budget S2-3200 PCI", BUDGET_TT); static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c), @@ -1251,6 +1561,7 @@ static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012), MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017), MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a), + MAKE_EXTENSION_PCI(tt3200, 0x13c2, 0x1019), { .vendor = 0, } diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h index 86435bf..3ad0c67 100644 --- a/drivers/media/dvb/ttpci/budget.h +++ b/drivers/media/dvb/ttpci/budget.h @@ -103,6 +103,7 @@ static struct saa7146_pci_extension_data x_var = { \ #define BUDGET_CIN1200C_MK3 15 #define BUDGET_KNC1C_MK3 16 #define BUDGET_KNC1CP_MK3 17 +#define BUDGET_KNC1S2 18 #define BUDGET_VIDEO_PORTA 0 #define BUDGET_VIDEO_PORTB 1 diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c index a5ca176..5474a22 100644 --- a/drivers/media/radio/dsbr100.c +++ b/drivers/media/radio/dsbr100.c @@ -1,5 +1,5 @@ -/* A driver for the D-Link DSB-R100 USB radio. The R100 plugs - into both the USB and an analog audio input, so this thing +/* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21. + The device plugs into both the USB and an analog audio input, so this thing only deals with initialisation and frequency setting, the audio data has to be handled by a sound driver. @@ -33,6 +33,10 @@ History: + Version 0.44: + Add suspend/resume functions, fix unplug of device, + a lot of cleanups and fixes by Alexey Klimov <klimov.linux@gmail.com> + Version 0.43: Oliver Neukum: avoided DMA coherency issue @@ -93,8 +97,8 @@ */ #include <linux/version.h> /* for KERNEL_VERSION MACRO */ -#define DRIVER_VERSION "v0.41" -#define RADIO_VERSION KERNEL_VERSION(0,4,1) +#define DRIVER_VERSION "v0.44" +#define RADIO_VERSION KERNEL_VERSION(0, 4, 4) static struct v4l2_queryctrl radio_qctrl[] = { { @@ -104,7 +108,27 @@ static struct v4l2_queryctrl radio_qctrl[] = { .maximum = 1, .default_value = 1, .type = V4L2_CTRL_TYPE_BOOLEAN, - } + }, +/* HINT: the disabled controls are only here to satify kradio and such apps */ + { .id = V4L2_CID_AUDIO_VOLUME, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_BALANCE, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_BASS, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_TREBLE, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_LOUDNESS, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, }; #define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>" @@ -125,12 +149,16 @@ devices, that would be 76 and 91. */ #define FREQ_MAX 108.0 #define FREQ_MUL 16000 +#define videodev_to_radio(d) container_of(d, struct dsbr100_device, videodev) static int usb_dsbr100_probe(struct usb_interface *intf, const struct usb_device_id *id); static void usb_dsbr100_disconnect(struct usb_interface *intf); static int usb_dsbr100_open(struct inode *inode, struct file *file); static int usb_dsbr100_close(struct inode *inode, struct file *file); +static int usb_dsbr100_suspend(struct usb_interface *intf, + pm_message_t message); +static int usb_dsbr100_resume(struct usb_interface *intf); static int radio_nr = -1; module_param(radio_nr, int, 0); @@ -138,8 +166,9 @@ module_param(radio_nr, int, 0); /* Data for one (physical) device */ struct dsbr100_device { struct usb_device *usbdev; - struct video_device *videodev; + struct video_device videodev; u8 *transfer_buffer; + struct mutex lock; /* buffer locking */ int curfreq; int stereo; int users; @@ -147,7 +176,6 @@ struct dsbr100_device { int muted; }; - static struct usb_device_id usb_dsbr100_device_table [] = { { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, { } /* Terminating entry */ @@ -157,10 +185,14 @@ MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table); /* USB subsystem interface */ static struct usb_driver usb_dsbr100_driver = { - .name = "dsbr100", - .probe = usb_dsbr100_probe, - .disconnect = usb_dsbr100_disconnect, - .id_table = usb_dsbr100_device_table, + .name = "dsbr100", + .probe = usb_dsbr100_probe, + .disconnect = usb_dsbr100_disconnect, + .id_table = usb_dsbr100_device_table, + .suspend = usb_dsbr100_suspend, + .resume = usb_dsbr100_resume, + .reset_resume = usb_dsbr100_resume, + .supports_autosuspend = 0, }; /* Low-level device interface begins here */ @@ -168,95 +200,190 @@ static struct usb_driver usb_dsbr100_driver = { /* switch on radio */ static int dsbr100_start(struct dsbr100_device *radio) { - if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), - USB_REQ_GET_STATUS, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x00, 0xC7, radio->transfer_buffer, 8, 300) < 0 || - usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), - DSB100_ONOFF, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x01, 0x00, radio->transfer_buffer, 8, 300) < 0) - return -1; - radio->muted=0; + int retval; + int request; + + mutex_lock(&radio->lock); + + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00, 0xC7, radio->transfer_buffer, 8, 300); + + if (retval < 0) { + request = USB_REQ_GET_STATUS; + goto usb_control_msg_failed; + } + + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_ONOFF, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x01, 0x00, radio->transfer_buffer, 8, 300); + + if (retval < 0) { + request = DSB100_ONOFF; + goto usb_control_msg_failed; + } + + radio->muted = 0; + mutex_unlock(&radio->lock); return (radio->transfer_buffer)[0]; -} +usb_control_msg_failed: + mutex_unlock(&radio->lock); + dev_err(&radio->usbdev->dev, + "%s - usb_control_msg returned %i, request %i\n", + __func__, retval, request); + return retval; + +} /* switch off radio */ static int dsbr100_stop(struct dsbr100_device *radio) { - if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), - USB_REQ_GET_STATUS, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x16, 0x1C, radio->transfer_buffer, 8, 300) < 0 || - usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), - DSB100_ONOFF, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x00, 0x00, radio->transfer_buffer, 8, 300) < 0) - return -1; - radio->muted=1; + int retval; + int request; + + mutex_lock(&radio->lock); + + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x16, 0x1C, radio->transfer_buffer, 8, 300); + + if (retval < 0) { + request = USB_REQ_GET_STATUS; + goto usb_control_msg_failed; + } + + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_ONOFF, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00, 0x00, radio->transfer_buffer, 8, 300); + + if (retval < 0) { + request = DSB100_ONOFF; + goto usb_control_msg_failed; + } + + radio->muted = 1; + mutex_unlock(&radio->lock); return (radio->transfer_buffer)[0]; + +usb_control_msg_failed: + mutex_unlock(&radio->lock); + dev_err(&radio->usbdev->dev, + "%s - usb_control_msg returned %i, request %i\n", + __func__, retval, request); + return retval; + } /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ static int dsbr100_setfreq(struct dsbr100_device *radio, int freq) { + int retval; + int request; + freq = (freq / 16 * 80) / 1000 + 856; - if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), - DSB100_TUNE, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - (freq >> 8) & 0x00ff, freq & 0xff, - radio->transfer_buffer, 8, 300) < 0 || - usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), - USB_REQ_GET_STATUS, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x96, 0xB7, radio->transfer_buffer, 8, 300) < 0 || - usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), - USB_REQ_GET_STATUS, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x00, 0x24, radio->transfer_buffer, 8, 300) < 0) { - radio->stereo = -1; - return -1; + mutex_lock(&radio->lock); + + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_TUNE, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + (freq >> 8) & 0x00ff, freq & 0xff, + radio->transfer_buffer, 8, 300); + + if (retval < 0) { + request = DSB100_TUNE; + goto usb_control_msg_failed; } + + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x96, 0xB7, radio->transfer_buffer, 8, 300); + + if (retval < 0) { + request = USB_REQ_GET_STATUS; + goto usb_control_msg_failed; + } + + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00, 0x24, radio->transfer_buffer, 8, 300); + + if (retval < 0) { + request = USB_REQ_GET_STATUS; + goto usb_control_msg_failed; + } + radio->stereo = !((radio->transfer_buffer)[0] & 0x01); + mutex_unlock(&radio->lock); return (radio->transfer_buffer)[0]; + +usb_control_msg_failed: + radio->stereo = -1; + mutex_unlock(&radio->lock); + dev_err(&radio->usbdev->dev, + "%s - usb_control_msg returned %i, request %i\n", + __func__, retval, request); + return retval; } /* return the device status. This is, in effect, just whether it sees a stereo signal or not. Pity. */ static void dsbr100_getstat(struct dsbr100_device *radio) { - if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + int retval; + + mutex_lock(&radio->lock); + + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), USB_REQ_GET_STATUS, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x00 , 0x24, radio->transfer_buffer, 8, 300) < 0) + 0x00 , 0x24, radio->transfer_buffer, 8, 300); + + if (retval < 0) { radio->stereo = -1; - else + dev_err(&radio->usbdev->dev, + "%s - usb_control_msg returned %i, request %i\n", + __func__, retval, USB_REQ_GET_STATUS); + } else { radio->stereo = !(radio->transfer_buffer[0] & 0x01); -} + } + mutex_unlock(&radio->lock); +} /* USB subsystem interface begins here */ -/* handle unplugging of the device, release data structures -if nothing keeps us from doing it. If something is still -keeping us busy, the release callback of v4l will take care -of releasing it. */ +/* + * Handle unplugging of the device. + * We call video_unregister_device in any case. + * The last function called in this procedure is + * usb_dsbr100_video_device_release + */ static void usb_dsbr100_disconnect(struct usb_interface *intf) { struct dsbr100_device *radio = usb_get_intfdata(intf); usb_set_intfdata (intf, NULL); - if (radio) { - video_unregister_device(radio->videodev); - radio->videodev = NULL; - if (radio->users) { - kfree(radio->transfer_buffer); - kfree(radio); - } else { - radio->removed = 1; - } - } + + mutex_lock(&radio->lock); + radio->removed = 1; + mutex_unlock(&radio->lock); + + video_unregister_device(&radio->videodev); } @@ -276,6 +403,10 @@ static int vidioc_g_tuner(struct file *file, void *priv, { struct dsbr100_device *radio = video_drvdata(file); + /* safety check */ + if (radio->removed) + return -EIO; + if (v->index > 0) return -EINVAL; @@ -297,6 +428,12 @@ static int vidioc_g_tuner(struct file *file, void *priv, static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { + struct dsbr100_device *radio = video_drvdata(file); + + /* safety check */ + if (radio->removed) + return -EIO; + if (v->index > 0) return -EINVAL; @@ -307,9 +444,15 @@ static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { struct dsbr100_device *radio = video_drvdata(file); + int retval; + + /* safety check */ + if (radio->removed) + return -EIO; radio->curfreq = f->frequency; - if (dsbr100_setfreq(radio, radio->curfreq) == -1) + retval = dsbr100_setfreq(radio, radio->curfreq); + if (retval < 0) dev_warn(&radio->usbdev->dev, "Set frequency failed\n"); return 0; } @@ -319,6 +462,10 @@ static int vidioc_g_frequency(struct file *file, void *priv, { struct dsbr100_device *radio = video_drvdata(file); + /* safety check */ + if (radio->removed) + return -EIO; + f->type = V4L2_TUNER_RADIO; f->frequency = radio->curfreq; return 0; @@ -343,6 +490,10 @@ static int vidioc_g_ctrl(struct file *file, void *priv, { struct dsbr100_device *radio = video_drvdata(file); + /* safety check */ + if (radio->removed) + return -EIO; + switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: ctrl->value = radio->muted; @@ -355,17 +506,24 @@ static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct dsbr100_device *radio = video_drvdata(file); + int retval; + + /* safety check */ + if (radio->removed) + return -EIO; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: if (ctrl->value) { - if (dsbr100_stop(radio) == -1) { + retval = dsbr100_stop(radio); + if (retval < 0) { dev_warn(&radio->usbdev->dev, "Radio did not respond properly\n"); return -EBUSY; } } else { - if (dsbr100_start(radio) == -1) { + retval = dsbr100_start(radio); + if (retval < 0) { dev_warn(&radio->usbdev->dev, "Radio did not respond properly\n"); return -EBUSY; @@ -417,7 +575,8 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file) radio->users = 1; radio->muted = 1; - if (dsbr100_start(radio) < 0) { + retval = dsbr100_start(radio); + if (retval < 0) { dev_warn(&radio->usbdev->dev, "Radio did not start up properly\n"); radio->users = 0; @@ -426,9 +585,9 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file) } retval = dsbr100_setfreq(radio, radio->curfreq); - - if (retval == -1) - printk(KERN_WARNING KBUILD_MODNAME ": Set frequency failed\n"); + if (retval < 0) + dev_warn(&radio->usbdev->dev, + "set frequency failed\n"); unlock_kernel(); return 0; @@ -437,17 +596,62 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file) static int usb_dsbr100_close(struct inode *inode, struct file *file) { struct dsbr100_device *radio = video_drvdata(file); + int retval; if (!radio) return -ENODEV; + radio->users = 0; - if (radio->removed) { - kfree(radio->transfer_buffer); - kfree(radio); + if (!radio->removed) { + retval = dsbr100_stop(radio); + if (retval < 0) { + dev_warn(&radio->usbdev->dev, + "dsbr100_stop failed\n"); + } + } return 0; } +/* Suspend device - stop device. */ +static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct dsbr100_device *radio = usb_get_intfdata(intf); + int retval; + + retval = dsbr100_stop(radio); + if (retval < 0) + dev_warn(&intf->dev, "dsbr100_stop failed\n"); + + dev_info(&intf->dev, "going into suspend..\n"); + + return 0; +} + +/* Resume device - start device. */ +static int usb_dsbr100_resume(struct usb_interface *intf) +{ + struct dsbr100_device *radio = usb_get_intfdata(intf); + int retval; + + retval = dsbr100_start(radio); + if (retval < 0) + dev_warn(&intf->dev, "dsbr100_start failed\n"); + + dev_info(&intf->dev, "coming out of suspend..\n"); + + return 0; +} + +/* free data structures */ +static void usb_dsbr100_video_device_release(struct video_device *videodev) +{ + struct dsbr100_device *radio = videodev_to_radio(videodev); + + kfree(radio->transfer_buffer); + kfree(radio); +} + /* File system interface */ static const struct file_operations usb_dsbr100_fops = { .owner = THIS_MODULE, @@ -476,19 +680,19 @@ static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { }; /* V4L2 interface */ -static struct video_device dsbr100_videodev_template = { +static struct video_device dsbr100_videodev_data = { .name = "D-Link DSB-R 100", .fops = &usb_dsbr100_fops, .ioctl_ops = &usb_dsbr100_ioctl_ops, - .release = video_device_release, + .release = usb_dsbr100_video_device_release, }; -/* check if the device is present and register with v4l and -usb if it is */ +/* check if the device is present and register with v4l and usb if it is */ static int usb_dsbr100_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct dsbr100_device *radio; + int retval; radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL); @@ -501,23 +705,18 @@ static int usb_dsbr100_probe(struct usb_interface *intf, kfree(radio); return -ENOMEM; } - radio->videodev = video_device_alloc(); - if (!(radio->videodev)) { - kfree(radio->transfer_buffer); - kfree(radio); - return -ENOMEM; - } - memcpy(radio->videodev, &dsbr100_videodev_template, - sizeof(dsbr100_videodev_template)); + mutex_init(&radio->lock); + radio->videodev = dsbr100_videodev_data; + radio->removed = 0; radio->users = 0; radio->usbdev = interface_to_usbdev(intf); radio->curfreq = FREQ_MIN * FREQ_MUL; - video_set_drvdata(radio->videodev, radio); - if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr) < 0) { - dev_warn(&intf->dev, "Could not register video device\n"); - video_device_release(radio->videodev); + video_set_drvdata(&radio->videodev, radio); + retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr); + if (retval < 0) { + dev_err(&intf->dev, "couldn't register video device\n"); kfree(radio->transfer_buffer); kfree(radio); return -EIO; diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c index 9305e95..dd6d3df 100644 --- a/drivers/media/radio/radio-aimslab.c +++ b/drivers/media/radio/radio-aimslab.c @@ -1,7 +1,7 @@ /* radiotrack (radioreveal) driver for Linux radio support * (c) 1997 M. Kirkwood * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> - * Converted to new API by Alan Cox <Alan.Cox@linux.org> + * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> * * History: diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c index 0490a1f..bfd37f3 100644 --- a/drivers/media/radio/radio-cadet.c +++ b/drivers/media/radio/radio-cadet.c @@ -23,7 +23,7 @@ * 2002-01-17 Adam Belay <ambx1@neo.rr.com> * Updated to latest pnp code * - * 2003-01-31 Alan Cox <alan@redhat.com> + * 2003-01-31 Alan Cox <alan@lxorguk.ukuu.org.uk> * Cleaned up locking, delay code, general odds and ends * * 2006-07-30 Hans J. Koch <koch@hjk-az.de> diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index d131a5d..e13118d 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -8,7 +8,7 @@ * RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff * * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood - * Converted to new API by Alan Cox <Alan.Cox@linux.org> + * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> * * TODO: Allow for more than one of these foolish entities :-) diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index 256cbef..e730edd 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -72,6 +72,11 @@ MODULE_LICENSE("GPL"); #define USB_AMRADIO_VENDOR 0x07ca #define USB_AMRADIO_PRODUCT 0xb800 +/* dev_warn macro with driver name */ +#define MR800_DRIVER_NAME "radio-mr800" +#define amradio_dev_warn(dev, fmt, arg...) \ + dev_warn(dev, MR800_DRIVER_NAME " - " fmt, ##arg) + /* Probably USB_TIMEOUT should be modified in module parameter */ #define BUFFER_LENGTH 8 #define USB_TIMEOUT 500 @@ -154,14 +159,14 @@ MODULE_DEVICE_TABLE(usb, usb_amradio_device_table); /* USB subsystem interface */ static struct usb_driver usb_amradio_driver = { - .name = "radio-mr800", + .name = MR800_DRIVER_NAME, .probe = usb_amradio_probe, .disconnect = usb_amradio_disconnect, .suspend = usb_amradio_suspend, .resume = usb_amradio_resume, .reset_resume = usb_amradio_resume, .id_table = usb_amradio_device_table, - .supports_autosuspend = 1, + .supports_autosuspend = 0, }; /* switch on radio. Send 8 bytes to device. */ @@ -202,6 +207,10 @@ static int amradio_stop(struct amradio_device *radio) int retval; int size; + /* safety check */ + if (radio->removed) + return -EIO; + mutex_lock(&radio->lock); radio->buffer[0] = 0x00; @@ -235,6 +244,10 @@ static int amradio_setfreq(struct amradio_device *radio, int freq) int size; unsigned short freq_send = 0x13 + (freq >> 3) / 25; + /* safety check */ + if (radio->removed) + return -EIO; + mutex_lock(&radio->lock); radio->buffer[0] = 0x00; @@ -288,18 +301,12 @@ static void usb_amradio_disconnect(struct usb_interface *intf) { struct amradio_device *radio = usb_get_intfdata(intf); - usb_set_intfdata(intf, NULL); + mutex_lock(&radio->lock); + radio->removed = 1; + mutex_unlock(&radio->lock); - if (radio) { - video_unregister_device(radio->videodev); - radio->videodev = NULL; - if (radio->users) { - kfree(radio->buffer); - kfree(radio); - } else { - radio->removed = 1; - } - } + usb_set_intfdata(intf, NULL); + video_unregister_device(radio->videodev); } /* vidioc_querycap - query device capabilities */ @@ -320,6 +327,10 @@ static int vidioc_g_tuner(struct file *file, void *priv, { struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + /* safety check */ + if (radio->removed) + return -EIO; + if (v->index > 0) return -EINVAL; @@ -346,6 +357,12 @@ static int vidioc_g_tuner(struct file *file, void *priv, static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { + struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + + /* safety check */ + if (radio->removed) + return -EIO; + if (v->index > 0) return -EINVAL; return 0; @@ -357,9 +374,14 @@ static int vidioc_s_frequency(struct file *file, void *priv, { struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + /* safety check */ + if (radio->removed) + return -EIO; + radio->curfreq = f->frequency; if (amradio_setfreq(radio, radio->curfreq) < 0) - warn("Set frequency failed"); + amradio_dev_warn(&radio->videodev->dev, + "set frequency failed\n"); return 0; } @@ -369,6 +391,10 @@ static int vidioc_g_frequency(struct file *file, void *priv, { struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + /* safety check */ + if (radio->removed) + return -EIO; + f->type = V4L2_TUNER_RADIO; f->frequency = radio->curfreq; return 0; @@ -382,8 +408,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); + memcpy(qc, &(radio_qctrl[i]), sizeof(*qc)); return 0; } } @@ -396,6 +421,10 @@ static int vidioc_g_ctrl(struct file *file, void *priv, { struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + /* safety check */ + if (radio->removed) + return -EIO; + switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: ctrl->value = radio->muted; @@ -410,16 +439,22 @@ static int vidioc_s_ctrl(struct file *file, void *priv, { struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + /* safety check */ + if (radio->removed) + return -EIO; + switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: if (ctrl->value) { if (amradio_stop(radio) < 0) { - warn("amradio_stop() failed"); + amradio_dev_warn(&radio->videodev->dev, + "amradio_stop failed\n"); return -1; } } else { if (amradio_start(radio) < 0) { - warn("amradio_start() failed"); + amradio_dev_warn(&radio->videodev->dev, + "amradio_start failed\n"); return -1; } } @@ -475,30 +510,38 @@ static int usb_amradio_open(struct inode *inode, struct file *file) radio->muted = 1; if (amradio_start(radio) < 0) { - warn("Radio did not start up properly"); + amradio_dev_warn(&radio->videodev->dev, + "radio did not start up properly\n"); radio->users = 0; unlock_kernel(); return -EIO; } if (amradio_setfreq(radio, radio->curfreq) < 0) - warn("Set frequency failed"); + amradio_dev_warn(&radio->videodev->dev, + "set frequency failed\n"); unlock_kernel(); return 0; } -/*close device - free driver structures */ +/*close device */ static int usb_amradio_close(struct inode *inode, struct file *file) { struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + int retval; if (!radio) return -ENODEV; + radio->users = 0; - if (radio->removed) { - kfree(radio->buffer); - kfree(radio); + + if (!radio->removed) { + retval = amradio_stop(radio); + if (retval < 0) + amradio_dev_warn(&radio->videodev->dev, + "amradio_stop failed\n"); } + return 0; } @@ -508,9 +551,9 @@ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message) struct amradio_device *radio = usb_get_intfdata(intf); if (amradio_stop(radio) < 0) - warn("amradio_stop() failed"); + dev_warn(&intf->dev, "amradio_stop failed\n"); - info("radio-mr800: Going into suspend.."); + dev_info(&intf->dev, "going into suspend..\n"); return 0; } @@ -521,9 +564,9 @@ static int usb_amradio_resume(struct usb_interface *intf) struct amradio_device *radio = usb_get_intfdata(intf); if (amradio_start(radio) < 0) - warn("amradio_start() failed"); + dev_warn(&intf->dev, "amradio_start failed\n"); - info("radio-mr800: Coming out of suspend.."); + dev_info(&intf->dev, "coming out of suspend..\n"); return 0; } @@ -555,12 +598,24 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = { .vidioc_s_input = vidioc_s_input, }; +static void usb_amradio_device_release(struct video_device *videodev) +{ + struct amradio_device *radio = video_get_drvdata(videodev); + + /* we call v4l to free radio->videodev */ + video_device_release(videodev); + + /* free rest memory */ + kfree(radio->buffer); + kfree(radio); +} + /* V4L2 interface */ static struct video_device amradio_videodev_template = { .name = "AverMedia MR 800 USB FM Radio", .fops = &usb_amradio_fops, .ioctl_ops = &usb_amradio_ioctl_ops, - .release = video_device_release, + .release = usb_amradio_device_release, }; /* check if the device is present and register with v4l and @@ -602,7 +657,7 @@ static int usb_amradio_probe(struct usb_interface *intf, video_set_drvdata(radio->videodev, radio); if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) { - warn("Could not register video device"); + dev_warn(&intf->dev, "could not register video device\n"); video_device_release(radio->videodev); kfree(radio->buffer); kfree(radio); @@ -617,9 +672,13 @@ static int __init amradio_init(void) { int retval = usb_register(&usb_amradio_driver); - info(DRIVER_VERSION " " DRIVER_DESC); + pr_info(KBUILD_MODNAME + ": version " DRIVER_VERSION " " DRIVER_DESC "\n"); + if (retval) - err("usb_register failed. Error number %d", retval); + pr_err(KBUILD_MODNAME + ": usb_register failed. Error number %d\n", retval); + return retval; } diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c index a670797..7704f24 100644 --- a/drivers/media/radio/radio-rtrack2.c +++ b/drivers/media/radio/radio-rtrack2.c @@ -1,7 +1,7 @@ /* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff * * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood - * Converted to new API by Alan Cox <Alan.Cox@linux.org> + * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> * * TODO: Allow for more than one of these foolish entities :-) diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index 329c90b..834d436 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -3,7 +3,7 @@ * (c) 1997 M. Kirkwood * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz * - * Fitted to new interface by Alan Cox <alan.cox@linux.org> + * Fitted to new interface by Alan Cox <alan@lxorguk.ukuu.org.uk> * Made working and cleaned up functions <mikael.hedin@irf.se> * Support for ISAPnP by Ladislav Michl <ladis@psi.cz> * diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 057fd7e1..19cf3b8 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -184,7 +184,7 @@ config VIDEO_MSP3400 config VIDEO_CS5345 tristate "Cirrus Logic CS5345 audio ADC" - depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C ---help--- Support for the Cirrus Logic CS5345 24-bit, 192 kHz stereo A/D converter. @@ -204,7 +204,7 @@ config VIDEO_CS53L32A config VIDEO_M52790 tristate "Mitsubishi M52790 A/V switch" - depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C ---help--- Support for the Mitsubishi M52790 A/V switch. @@ -242,7 +242,7 @@ config VIDEO_WM8739 config VIDEO_VP27SMPX tristate "Panasonic VP27s internal MPX" - depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C ---help--- Support for the internal MPX of the Panasonic VP27s tuner. @@ -361,6 +361,17 @@ config VIDEO_SAA7191 To compile this driver as a module, choose M here: the module will be called saa7191. +config VIDEO_TVP514X + tristate "Texas Instruments TVP514x video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + This is a Video4Linux2 sensor-level driver for the TI TVP5146/47 + decoder. It is currently working with the TI OMAP3 camera + controller. + + To compile this driver as a module, choose M here: the + module will be called tvp514x. + config VIDEO_TVP5150 tristate "Texas Instruments TVP5150 video decoder" depends on VIDEO_V4L2 && I2C @@ -387,7 +398,7 @@ comment "MPEG video encoders" config VIDEO_CX2341X tristate "Conexant CX2341x MPEG encoders" - depends on VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_COMMON + depends on VIDEO_V4L2 && VIDEO_V4L2_COMMON ---help--- Support for the Conexant CX23416 MPEG encoders and CX23415 MPEG encoder/decoders. @@ -725,10 +736,16 @@ config MT9M001_PCA9536_SWITCH extender to switch between 8 and 10 bit datawidth modes config SOC_CAMERA_MT9M111 - tristate "mt9m111 support" + tristate "mt9m111 and mt9m112 support" + depends on SOC_CAMERA && I2C + help + This driver supports MT9M111 and MT9M112 cameras from Micron + +config SOC_CAMERA_MT9T031 + tristate "mt9t031 support" depends on SOC_CAMERA && I2C help - This driver supports MT9M111 cameras from Micron + This driver supports MT9T031 cameras from Micron. config SOC_CAMERA_MT9V022 tristate "mt9v022 support" @@ -744,12 +761,24 @@ config MT9V022_PCA9536_SWITCH Select this if your MT9V022 camera uses a PCA9536 I2C GPIO extender to switch between 8 and 10 bit datawidth modes +config SOC_CAMERA_TW9910 + tristate "tw9910 support" + depends on SOC_CAMERA && I2C + help + This is a tw9910 video driver + config SOC_CAMERA_PLATFORM tristate "platform camera support" depends on SOC_CAMERA help This is a generic SoC camera platform driver, useful for testing +config SOC_CAMERA_OV772X + tristate "ov772x camera support" + depends on SOC_CAMERA && I2C + help + This is a ov772x camera driver + config VIDEO_PXA27x tristate "PXA27x Quick Capture Interface driver" depends on VIDEO_DEV && PXA27x && SOC_CAMERA @@ -764,6 +793,13 @@ config VIDEO_SH_MOBILE_CEU ---help--- This is a v4l2 driver for the SuperH Mobile CEU Interface +config VIDEO_OMAP2 + tristate "OMAP2 Camera Capture Interface driver" + depends on VIDEO_DEV && ARCH_OMAP2 + select VIDEOBUF_DMA_SG + ---help--- + This is a v4l2 driver for the TI OMAP2 camera capture interface + # # USB Multimedia device configuration # diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 16962f3..1611c33 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -8,9 +8,11 @@ msp3400-objs := msp3400-driver.o msp3400-kthreads.o stkwebcam-objs := stk-webcam.o stk-sensor.o -videodev-objs := v4l2-dev.o v4l2-ioctl.o +omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o -obj-$(CONFIG_VIDEO_DEV) += videodev.o compat_ioctl32.o v4l2-int-device.o +videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-subdev.o + +obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-compat-ioctl32.o v4l2-int-device.o obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o @@ -25,6 +27,7 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o +obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o @@ -66,6 +69,7 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o +obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o obj-$(CONFIG_VIDEO_CS5345) += cs5345.o @@ -129,11 +133,15 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885/ obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o +obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o obj-$(CONFIG_SOC_CAMERA) += soc_camera.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o +obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o +obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o +obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o obj-$(CONFIG_VIDEO_AU0828) += au0828/ diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c index e09b006..2ba6abd 100644 --- a/drivers/media/video/arv.c +++ b/drivers/media/video/arv.c @@ -396,8 +396,7 @@ out_up: return ret; } -static int ar_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int ar_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); struct ar_device *ar = video_get_drvdata(dev); @@ -543,7 +542,7 @@ static int ar_do_ioctl(struct inode *inode, struct file *file, static int ar_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, ar_do_ioctl); + return video_usercopy(file, cmd, arg, ar_do_ioctl); } #if USE_INT diff --git a/drivers/media/video/bt8xx/bt832.c b/drivers/media/video/bt8xx/bt832.c deleted file mode 100644 index 216fc96..0000000 --- a/drivers/media/video/bt8xx/bt832.c +++ /dev/null @@ -1,274 +0,0 @@ -/* Driver for Bt832 CMOS Camera Video Processor - i2c-addresses: 0x88 or 0x8a - - The BT832 interfaces to a Quartzsight Digital Camera (352x288, 25 or 30 fps) - via a 9 pin connector ( 4-wire SDATA, 2-wire i2c, SCLK, VCC, GND). - It outputs an 8-bit 4:2:2 YUV or YCrCb video signal which can be directly - connected to bt848/bt878 GPIO pins on this purpose. - (see: VLSI Vision Ltd. www.vvl.co.uk for camera datasheets) - - Supported Cards: - - Pixelview Rev.4E: 0x8a - GPIO 0x400000 toggles Bt832 RESET, and the chip changes to i2c 0x88 ! - - (c) Gunther Mayer, 2002 - - STATUS: - - detect chip and hexdump - - reset chip and leave low power mode - - detect camera present - - TODO: - - make it work (find correct setup for Bt832 and Bt878) -*/ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/i2c.h> -#include <linux/types.h> -#include <linux/videodev.h> -#include <linux/init.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <media/v4l2-common.h> - -#include "bttv.h" -#include "bt832.h" - -MODULE_LICENSE("GPL"); - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { I2C_ADDR_BT832_ALT1>>1, I2C_ADDR_BT832_ALT2>>1, - I2C_CLIENT_END }; -I2C_CLIENT_INSMOD; - -int debug; /* debug output */ -module_param(debug, int, 0644); - -/* ---------------------------------------------------------------------- */ - -static int bt832_detach(struct i2c_client *client); - - -static struct i2c_driver driver; -static struct i2c_client client_template; - -struct bt832 { - struct i2c_client client; -}; - -int bt832_hexdump(struct i2c_client *i2c_client_s, unsigned char *buf) -{ - int i,rc; - buf[0]=0x80; // start at register 0 with auto-increment - if (1 != (rc = i2c_master_send(i2c_client_s,buf,1))) - v4l_err(i2c_client_s,"i2c i/o error: rc == %d (should be 1)\n",rc); - - for(i=0;i<65;i++) - buf[i]=0; - if (65 != (rc=i2c_master_recv(i2c_client_s,buf,65))) - v4l_err(i2c_client_s,"i2c i/o error: rc == %d (should be 65)\n",rc); - - // Note: On READ the first byte is the current index - // (e.g. 0x80, what we just wrote) - - if(debug>1) { - int i; - v4l_dbg(2, debug,i2c_client_s,"hexdump:"); - for(i=1;i<65;i++) { - if(i!=1) { - if(((i-1)%8)==0) printk(" "); - if(((i-1)%16)==0) { - printk("\n"); - v4l_dbg(2, debug,i2c_client_s,"hexdump:"); - } - } - printk(" %02x",buf[i]); - } - printk("\n"); - } - return 0; -} - -// Return: 1 (is a bt832), 0 (No bt832 here) -int bt832_init(struct i2c_client *i2c_client_s) -{ - unsigned char *buf; - int rc; - - buf=kmalloc(65,GFP_KERNEL); - if (!buf) { - v4l_err(&t->client, - "Unable to allocate memory. Detaching.\n"); - return 0; - } - bt832_hexdump(i2c_client_s,buf); - - if(buf[0x40] != 0x31) { - v4l_err(i2c_client_s,"This i2c chip is no bt832 (id=%02x). Detaching.\n",buf[0x40]); - kfree(buf); - return 0; - } - - v4l_err(i2c_client_s,"Write 0 tp VPSTATUS\n"); - buf[0]=BT832_VP_STATUS; // Reg.52 - buf[1]= 0x00; - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_err(i2c_client_s,"i2c i/o error VPS: rc == %d (should be 2)\n",rc); - - bt832_hexdump(i2c_client_s,buf); - - - // Leave low power mode: - v4l_err(i2c_client_s,"leave low power mode.\n"); - buf[0]=BT832_CAM_SETUP0; //0x39 57 - buf[1]=0x08; - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_err(i2c_client_s,"i2c i/o error LLPM: rc == %d (should be 2)\n",rc); - - bt832_hexdump(i2c_client_s,buf); - - v4l_info(i2c_client_s,"Write 0 tp VPSTATUS\n"); - buf[0]=BT832_VP_STATUS; // Reg.52 - buf[1]= 0x00; - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_err(i2c_client_s,"i2c i/o error VPS: rc == %d (should be 2)\n",rc); - - bt832_hexdump(i2c_client_s,buf); - - - // Enable Output - v4l_info(i2c_client_s,"Enable Output\n"); - buf[0]=BT832_VP_CONTROL1; // Reg.40 - buf[1]= 0x27 & (~0x01); // Default | !skip - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_err(i2c_client_s,"i2c i/o error EO: rc == %d (should be 2)\n",rc); - - bt832_hexdump(i2c_client_s,buf); - - - // for testing (even works when no camera attached) - v4l_info(i2c_client_s,"*** Generate NTSC M Bars *****\n"); - buf[0]=BT832_VP_TESTCONTROL0; // Reg. 42 - buf[1]=3; // Generate NTSC System M bars, Generate Frame timing internally - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_info(i2c_client_s,"i2c i/o error MBAR: rc == %d (should be 2)\n",rc); - - v4l_info(i2c_client_s,"Camera Present: %s\n", - (buf[1+BT832_CAM_STATUS] & BT832_56_CAMERA_PRESENT) ? "yes":"no"); - - bt832_hexdump(i2c_client_s,buf); - kfree(buf); - return 1; -} - - - -static int bt832_attach(struct i2c_adapter *adap, int addr, int kind) -{ - struct bt832 *t; - - client_template.adapter = adap; - client_template.addr = addr; - - if (NULL == (t = kzalloc(sizeof(*t), GFP_KERNEL))) - return -ENOMEM; - t->client = client_template; - i2c_set_clientdata(&t->client, t); - i2c_attach_client(&t->client); - - v4l_info(&t->client,"chip found @ 0x%x\n", addr<<1); - - if(! bt832_init(&t->client)) { - bt832_detach(&t->client); - return -1; - } - - return 0; -} - -static int bt832_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, bt832_attach); - return 0; -} - -static int bt832_detach(struct i2c_client *client) -{ - struct bt832 *t = i2c_get_clientdata(client); - - v4l_info(&t->client,"dettach\n"); - i2c_detach_client(client); - kfree(t); - return 0; -} - -static int -bt832_command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct bt832 *t = i2c_get_clientdata(client); - - if (debug>1) - v4l_i2c_print_ioctl(&t->client,cmd); - - switch (cmd) { - case BT832_HEXDUMP: { - unsigned char *buf; - buf = kmalloc(65, GFP_KERNEL); - if (!buf) { - v4l_err(&t->client, - "Unable to allocate memory\n"); - break; - } - bt832_hexdump(&t->client,buf); - kfree(buf); - } - break; - case BT832_REATTACH: - v4l_info(&t->client,"re-attach\n"); - i2c_del_driver(&driver); - i2c_add_driver(&driver); - break; - } - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static struct i2c_driver driver = { - .driver = { - .name = "bt832", - }, - .id = 0, /* FIXME */ - .attach_adapter = bt832_probe, - .detach_client = bt832_detach, - .command = bt832_command, -}; -static struct i2c_client client_template = -{ - .name = "bt832", - .driver = &driver, -}; - - -static int __init bt832_init_module(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit bt832_cleanup_module(void) -{ - i2c_del_driver(&driver); -} - -module_init(bt832_init_module); -module_exit(bt832_cleanup_module); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/bt8xx/bt832.h b/drivers/media/video/bt8xx/bt832.h deleted file mode 100644 index 1ce8fa71f..0000000 --- a/drivers/media/video/bt8xx/bt832.h +++ /dev/null @@ -1,305 +0,0 @@ -/* Bt832 CMOS Camera Video Processor (VP) - - The Bt832 CMOS Camera Video Processor chip connects a Quartsight CMOS - color digital camera directly to video capture devices via an 8-bit, - 4:2:2 YUV or YCrCb video interface. - - i2c addresses: 0x88 or 0x8a - */ - -/* The 64 registers: */ - -// Input Processor -#define BT832_OFFSET 0 -#define BT832_RCOMP 1 -#define BT832_G1COMP 2 -#define BT832_G2COMP 3 -#define BT832_BCOMP 4 -// Exposures: -#define BT832_FINEH 5 -#define BT832_FINEL 6 -#define BT832_COARSEH 7 -#define BT832_COARSEL 8 -#define BT832_CAMGAIN 9 -// Main Processor: -#define BT832_M00 10 -#define BT832_M01 11 -#define BT832_M02 12 -#define BT832_M10 13 -#define BT832_M11 14 -#define BT832_M12 15 -#define BT832_M20 16 -#define BT832_M21 17 -#define BT832_M22 18 -#define BT832_APCOR 19 -#define BT832_GAMCOR 20 -// Level Accumulator Inputs -#define BT832_VPCONTROL2 21 -#define BT832_ZONECODE0 22 -#define BT832_ZONECODE1 23 -#define BT832_ZONECODE2 24 -#define BT832_ZONECODE3 25 -// Level Accumulator Outputs: -#define BT832_RACC 26 -#define BT832_GACC 27 -#define BT832_BACC 28 -#define BT832_BLACKACC 29 -#define BT832_EXP_AGC 30 -#define BT832_LACC0 31 -#define BT832_LACC1 32 -#define BT832_LACC2 33 -#define BT832_LACC3 34 -#define BT832_LACC4 35 -#define BT832_LACC5 36 -#define BT832_LACC6 37 -#define BT832_LACC7 38 -// System: -#define BT832_VP_CONTROL0 39 -#define BT832_VP_CONTROL1 40 -#define BT832_THRESH 41 -#define BT832_VP_TESTCONTROL0 42 -#define BT832_VP_DMCODE 43 -#define BT832_ACB_CONFIG 44 -#define BT832_ACB_GNBASE 45 -#define BT832_ACB_MU 46 -#define BT832_CAM_TEST0 47 -#define BT832_AEC_CONFIG 48 -#define BT832_AEC_TL 49 -#define BT832_AEC_TC 50 -#define BT832_AEC_TH 51 -// Status: -#define BT832_VP_STATUS 52 -#define BT832_VP_LINECOUNT 53 -#define BT832_CAM_DEVICEL 54 // e.g. 0x19 -#define BT832_CAM_DEVICEH 55 // e.g. 0x40 == 0x194 Mask0, 0x194 = 404 decimal (VVL-404 camera) -#define BT832_CAM_STATUS 56 - #define BT832_56_CAMERA_PRESENT 0x20 -//Camera Setups: -#define BT832_CAM_SETUP0 57 -#define BT832_CAM_SETUP1 58 -#define BT832_CAM_SETUP2 59 -#define BT832_CAM_SETUP3 60 -// System: -#define BT832_DEFCOR 61 -#define BT832_VP_TESTCONTROL1 62 -#define BT832_DEVICE_ID 63 -# define BT832_DEVICE_ID__31 0x31 // Bt832 has ID 0x31 - -/* STMicroelectronivcs VV5404 camera module - i2c: 0x20: sensor address - i2c: 0xa0: eeprom for ccd defect map - */ -#define VV5404_device_h 0x00 // 0x19 -#define VV5404_device_l 0x01 // 0x40 -#define VV5404_status0 0x02 -#define VV5404_linecountc 0x03 // current line counter -#define VV5404_linecountl 0x04 -#define VV5404_setup0 0x10 -#define VV5404_setup1 0x11 -#define VV5404_setup2 0x12 -#define VV5404_setup4 0x14 -#define VV5404_setup5 0x15 -#define VV5404_fine_h 0x20 // fine exposure -#define VV5404_fine_l 0x21 -#define VV5404_coarse_h 0x22 //coarse exposure -#define VV5404_coarse_l 0x23 -#define VV5404_gain 0x24 // ADC pre-amp gain setting -#define VV5404_clk_div 0x25 -#define VV5404_cr 0x76 // control register -#define VV5404_as0 0x77 // ADC setup register - - -// IOCTL -#define BT832_HEXDUMP _IOR('b',1,int) -#define BT832_REATTACH _IOR('b',2,int) - -/* from BT8x8VXD/capdrv/dialogs.cpp */ - -/* -typedef enum { SVI, Logitech, Rockwell } CAMERA; - -static COMBOBOX_ENTRY gwCameraOptions[] = -{ - { SVI, "Silicon Vision 512N" }, - { Logitech, "Logitech VideoMan 1.3" }, - { Rockwell, "Rockwell QuartzSight PCI 1.0" } -}; - -// SRAM table values -//=========================================================================== -typedef enum { TGB_NTSC624, TGB_NTSC780, TGB_NTSC858, TGB_NTSC392 } TimeGenByte; - -BYTE SRAMTable[][ 60 ] = -{ - // TGB_NTSC624 - { - 0x33, // size of table = 51 - 0x0E, 0xC0, 0x00, 0x00, 0x90, 0x02, 0x03, 0x10, 0x03, 0x06, - 0x10, 0x04, 0x12, 0x12, 0x05, 0x02, 0x13, 0x04, 0x19, 0x00, - 0x04, 0x39, 0x00, 0x06, 0x59, 0x08, 0x03, 0x85, 0x08, 0x07, - 0x03, 0x50, 0x00, 0x91, 0x40, 0x00, 0x11, 0x01, 0x01, 0x4D, - 0x0D, 0x02, 0x03, 0x11, 0x01, 0x05, 0x37, 0x00, 0x37, 0x21, 0x00 - }, - // TGB_NTSC780 - { - 0x33, // size of table = 51 - 0x0e, 0xc0, 0x00, 0x00, 0x90, 0xe2, 0x03, 0x10, 0x03, 0x06, - 0x10, 0x34, 0x12, 0x12, 0x65, 0x02, 0x13, 0x24, 0x19, 0x00, - 0x24, 0x39, 0x00, 0x96, 0x59, 0x08, 0x93, 0x85, 0x08, 0x97, - 0x03, 0x50, 0x50, 0xaf, 0x40, 0x30, 0x5f, 0x01, 0xf1, 0x7f, - 0x0d, 0xf2, 0x03, 0x11, 0xf1, 0x05, 0x37, 0x30, 0x85, 0x21, 0x50 - }, - // TGB_NTSC858 - { - 0x33, // size of table = 51 - 0x0c, 0xc0, 0x00, 0x00, 0x90, 0xc2, 0x03, 0x10, 0x03, 0x06, - 0x10, 0x34, 0x12, 0x12, 0x65, 0x02, 0x13, 0x24, 0x19, 0x00, - 0x24, 0x39, 0x00, 0x96, 0x59, 0x08, 0x93, 0x83, 0x08, 0x97, - 0x03, 0x50, 0x30, 0xc0, 0x40, 0x30, 0x86, 0x01, 0x01, 0xa6, - 0x0d, 0x62, 0x03, 0x11, 0x61, 0x05, 0x37, 0x30, 0xac, 0x21, 0x50 - }, - // TGB_NTSC392 - // This table has been modified to be used for Fusion Rev D - { - 0x2A, // size of table = 42 - 0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24, - 0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10, - 0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00, - 0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3, - 0x20, 0x00 - } -}; - -//=========================================================================== -// This is the structure of the camera specifications -//=========================================================================== -typedef struct tag_cameraSpec -{ - SignalFormat signal; // which digital signal format the camera has - VideoFormat vidFormat; // video standard - SyncVideoRef syncRef; // which sync video reference is used - State syncOutput; // enable sync output for sync video input? - DecInputClk iClk; // which input clock is used - TimeGenByte tgb; // which timing generator byte does the camera use - int HReset; // select 64, 48, 32, or 16 CLKx1 for HReset - PLLFreq pllFreq; // what synthesized frequency to set PLL to - VSIZEPARMS vSize; // video size the camera produces - int lineCount; // expected total number of half-line per frame - 1 - BOOL interlace; // interlace signal? -} CameraSpec; - -//=========================================================================== -// <UPDATE REQUIRED> -// Camera specifications database. Update this table whenever camera spec -// has been changed or added/deleted supported camera models -//=========================================================================== -static CameraSpec dbCameraSpec[ N_CAMERAOPTIONS ] = -{ // Silicon Vision 512N - { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC624, 64, KHz19636, - // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace - { 512, 0x64, 480, 0x13, 240 }, 0, TRUE - }, - // Logitech VideoMan 1.3 - { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC780, 64, KHz24545, - // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace - { 640, 0x80, 480, 0x1A, 240 }, 0, TRUE - }, - // Rockwell QuartzSight - // Note: Fusion Rev D (rev ID 0x02) and later supports 16 pixels for HReset which is preferable. - // Use 32 for earlier version of hardware. Clkx1_HDELAY also changed from 0x27 to 0x20. - { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC392, 16, KHz28636, - // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace - { 352, 0x20, 576, 0x08, 288 }, 607, FALSE - } -}; -*/ - -/* -The corresponding APIs required to be invoked are: -SetConnector( ConCamera, TRUE/FALSE ); -SetSignalFormat( spec.signal ); -SetVideoFormat( spec.vidFormat ); -SetSyncVideoRef( spec.syncRef ); -SetEnableSyncOutput( spec.syncOutput ); -SetTimGenByte( SRAMTable[ spec.tgb ], SRAMTableSize[ spec.tgb ] ); -SetHReset( spec.HReset ); -SetPLL( spec.pllFreq ); -SetDecInputClock( spec.iClk ); -SetVideoInfo( spec.vSize ); -SetTotalLineCount( spec.lineCount ); -SetInterlaceMode( spec.interlace ); -*/ - -/* from web: - Video Sampling -Digital video is a sampled form of analog video. The most common sampling schemes in use today are: - Pixel Clock Horiz Horiz Vert - Rate Total Active -NTSC square pixel 12.27 MHz 780 640 525 -NTSC CCIR-601 13.5 MHz 858 720 525 -NTSC 4FSc 14.32 MHz 910 768 525 -PAL square pixel 14.75 MHz 944 768 625 -PAL CCIR-601 13.5 MHz 864 720 625 -PAL 4FSc 17.72 MHz 1135 948 625 - -For the CCIR-601 standards, the sampling is based on a static orthogonal sampling grid. The luminance component (Y) is sampled at 13.5 MHz, while the two color difference signals, Cr and Cb are sampled at half that, or 6.75 MHz. The Cr and Cb samples are colocated with alternate Y samples, and they are taken at the same position on each line, such that one sample is coincident with the 50% point of the falling edge of analog sync. The samples are coded to either 8 or 10 bits per component. -*/ - -/* from DScaler:*/ -/* -//=========================================================================== -// CCIR656 Digital Input Support: The tables were taken from DScaler proyect -// -// 13 Dec 2000 - Michael Eskin, Conexant Systems - Initial version -// - -//=========================================================================== -// Timing generator SRAM table values for CCIR601 720x480 NTSC -//=========================================================================== -// For NTSC CCIR656 -BYTE BtCard::SRAMTable_NTSC[] = -{ - // SRAM Timing Table for NTSC - 0x0c, 0xc0, 0x00, - 0x00, 0x90, 0xc2, - 0x03, 0x10, 0x03, - 0x06, 0x10, 0x34, - 0x12, 0x12, 0x65, - 0x02, 0x13, 0x24, - 0x19, 0x00, 0x24, - 0x39, 0x00, 0x96, - 0x59, 0x08, 0x93, - 0x83, 0x08, 0x97, - 0x03, 0x50, 0x30, - 0xc0, 0x40, 0x30, - 0x86, 0x01, 0x01, - 0xa6, 0x0d, 0x62, - 0x03, 0x11, 0x61, - 0x05, 0x37, 0x30, - 0xac, 0x21, 0x50 -}; - -//=========================================================================== -// Timing generator SRAM table values for CCIR601 720x576 NTSC -//=========================================================================== -// For PAL CCIR656 -BYTE BtCard::SRAMTable_PAL[] = -{ - // SRAM Timing Table for PAL - 0x36, 0x11, 0x01, - 0x00, 0x90, 0x02, - 0x05, 0x10, 0x04, - 0x16, 0x14, 0x05, - 0x11, 0x00, 0x04, - 0x12, 0xc0, 0x00, - 0x31, 0x00, 0x06, - 0x51, 0x08, 0x03, - 0x89, 0x08, 0x07, - 0xc0, 0x44, 0x00, - 0x81, 0x01, 0x01, - 0xa9, 0x0d, 0x02, - 0x02, 0x50, 0x03, - 0x37, 0x3d, 0x00, - 0xaf, 0x21, 0x00, -}; -*/ diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 13742b0..d24dcc0 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -44,7 +44,6 @@ /* fwd decl */ static void boot_msp34xx(struct bttv *btv, int pin); -static void boot_bt832(struct bttv *btv); static void hauppauge_eeprom(struct bttv *btv); static void avermedia_eeprom(struct bttv *btv); static void osprey_eeprom(struct bttv *btv, const u8 ee[256]); @@ -2217,9 +2216,9 @@ struct tvcard bttv_tvcards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, - [BTTV_BOARD_VD009X1_MINIDIN] = { + [BTTV_BOARD_VD009X1_VD011_MINIDIN] = { /* M.Klahr@phytec.de */ - .name = "PHYTEC VD-009-X1 MiniDIN (bt878)", + .name = "PHYTEC VD-009-X1 VD-011 MiniDIN (bt878)", .video_inputs = 4, .audio_inputs = 0, .tuner = UNSET, /* card has no tuner */ @@ -2227,14 +2226,14 @@ struct tvcard bttv_tvcards[] = { .gpiomask = 0x00, .muxsel = { 2, 3, 1, 0 }, .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ - .needs_tvaudio = 1, + .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, - [BTTV_BOARD_VD009X1_COMBI] = { - .name = "PHYTEC VD-009-X1 Combi (bt878)", + [BTTV_BOARD_VD009X1_VD011_COMBI] = { + .name = "PHYTEC VD-009-X1 VD-011 Combi (bt878)", .video_inputs = 4, .audio_inputs = 0, .tuner = UNSET, /* card has no tuner */ @@ -2242,7 +2241,7 @@ struct tvcard bttv_tvcards[] = { .gpiomask = 0x00, .muxsel = { 2, 3, 1, 1 }, .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ - .needs_tvaudio = 1, + .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, @@ -3061,6 +3060,54 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .has_radio = 1, .has_remote = 1, + }, + [BTTV_BOARD_VD012] = { + /* D.Heer@Phytec.de */ + .name = "PHYTEC VD-012 (bt878)", + .video_inputs = 4, + .audio_inputs = 0, + .tuner = UNSET, /* card has no tuner */ + .svhs = UNSET, /* card has no s-video */ + .gpiomask = 0x00, + .muxsel = { 0, 2, 3, 1 }, + .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ + .needs_tvaudio = 0, + .pll = PLL_28, + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + }, + [BTTV_BOARD_VD012_X1] = { + /* D.Heer@Phytec.de */ + .name = "PHYTEC VD-012-X1 (bt878)", + .video_inputs = 4, + .audio_inputs = 0, + .tuner = UNSET, /* card has no tuner */ + .svhs = 3, + .gpiomask = 0x00, + .muxsel = { 2, 3, 1 }, + .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ + .needs_tvaudio = 0, + .pll = PLL_28, + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + }, + [BTTV_BOARD_VD012_X2] = { + /* D.Heer@Phytec.de */ + .name = "PHYTEC VD-012-X2 (bt878)", + .video_inputs = 4, + .audio_inputs = 0, + .tuner = UNSET, /* card has no tuner */ + .svhs = 3, + .gpiomask = 0x00, + .muxsel = { 3, 2, 1 }, + .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ + .needs_tvaudio = 0, + .pll = PLL_28, + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, } }; @@ -3673,13 +3720,6 @@ void __devinit bttv_init_card2(struct bttv *btv) if (bttv_tvcards[btv->c.type].audio_mode_gpio) btv->audio_mode_gpio=bttv_tvcards[btv->c.type].audio_mode_gpio; - if (bttv_tvcards[btv->c.type].digital_mode == DIGITAL_MODE_CAMERA) { - /* detect Bt832 chip for quartzsight digital camera */ - if ((bttv_I2CRead(btv, I2C_ADDR_BT832_ALT1, "Bt832") >=0) || - (bttv_I2CRead(btv, I2C_ADDR_BT832_ALT2, "Bt832") >=0)) - boot_bt832(btv); - } - if (!autoload) return; @@ -4075,10 +4115,6 @@ static void __devinit boot_msp34xx(struct bttv *btv, int pin) "init [%d]\n", btv->c.nr, pin); } -static void __devinit boot_bt832(struct bttv *btv) -{ -} - /* ----------------------------------------------------------------------- */ /* Imagenation L-Model PXC200 Framegrabber */ /* This is basically the same procedure as diff --git a/drivers/media/video/bt8xx/bttv-gpio.c b/drivers/media/video/bt8xx/bttv-gpio.c index dce6dae..74c325e 100644 --- a/drivers/media/video/bt8xx/bttv-gpio.c +++ b/drivers/media/video/bt8xx/bttv-gpio.c @@ -42,7 +42,7 @@ static int bttv_sub_bus_match(struct device *dev, struct device_driver *drv) struct bttv_sub_driver *sub = to_bttv_sub_drv(drv); int len = strlen(sub->wanted); - if (0 == strncmp(dev->bus_id, sub->wanted, len)) + if (0 == strncmp(dev_name(dev), sub->wanted, len)) return 1; return 0; } @@ -91,15 +91,14 @@ int bttv_sub_add_device(struct bttv_core *core, char *name) sub->dev.parent = &core->pci->dev; sub->dev.bus = &bttv_sub_bus_type; sub->dev.release = release_sub_device; - snprintf(sub->dev.bus_id,sizeof(sub->dev.bus_id),"%s%d", - name, core->nr); + dev_set_name(&sub->dev, "%s%d", name, core->nr); err = device_register(&sub->dev); if (0 != err) { kfree(sub); return err; } - printk("bttv%d: add subdevice \"%s\"\n", core->nr, sub->dev.bus_id); + printk("bttv%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev)); list_add_tail(&sub->list,&core->subs); return 0; } diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index 46cb90e..529bf6c 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -130,8 +130,8 @@ #define BTTV_BOARD_XGUARD 0x67 #define BTTV_BOARD_NEBULA_DIGITV 0x68 #define BTTV_BOARD_PV143 0x69 -#define BTTV_BOARD_VD009X1_MINIDIN 0x6a -#define BTTV_BOARD_VD009X1_COMBI 0x6b +#define BTTV_BOARD_VD009X1_VD011_MINIDIN 0x6a +#define BTTV_BOARD_VD009X1_VD011_COMBI 0x6b #define BTTV_BOARD_VD009_MINIDIN 0x6c #define BTTV_BOARD_VD009_COMBI 0x6d #define BTTV_BOARD_IVC100 0x6e @@ -177,6 +177,10 @@ #define BTTV_BOARD_GEOVISION_GV600 0x96 #define BTTV_BOARD_KOZUMI_KTV_01C 0x97 #define BTTV_BOARD_ENLTV_FM_2 0x98 +#define BTTV_BOARD_VD012 0x99 +#define BTTV_BOARD_VD012_X1 0x9a +#define BTTV_BOARD_VD012_X2 0x9b + /* more card-specific defines */ #define PT2254_L_CHANNEL 0x10 @@ -308,7 +312,7 @@ struct bttv_sub_device { struct bttv_sub_driver { struct device_driver drv; - char wanted[BUS_ID_SIZE]; + char wanted[20]; int (*probe)(struct bttv_sub_device *sub); void (*remove)(struct bttv_sub_device *sub); }; diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h index b4d940b..199a4d2 100644 --- a/drivers/media/video/bt8xx/bttvp.h +++ b/drivers/media/video/bt8xx/bttvp.h @@ -459,7 +459,7 @@ struct bttv { }; /* our devices */ -#define BTTV_MAX 16 +#define BTTV_MAX 32 extern unsigned int bttv_num; extern struct bttv bttvs[BTTV_MAX]; diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c index ace4ff9..17f80d0 100644 --- a/drivers/media/video/bw-qcam.c +++ b/drivers/media/video/bw-qcam.c @@ -706,8 +706,7 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l * Video4linux interfacing */ -static int qcam_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); struct qcam_device *qcam=(struct qcam_device *)dev; @@ -867,7 +866,7 @@ static int qcam_do_ioctl(struct inode *inode, struct file *file, static int qcam_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, qcam_do_ioctl); + return video_usercopy(file, cmd, arg, qcam_do_ioctl); } static ssize_t qcam_read(struct file *file, char __user *buf, diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c index 0f930d3..21c71eb 100644 --- a/drivers/media/video/c-qcam.c +++ b/drivers/media/video/c-qcam.c @@ -500,8 +500,7 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le * Video4linux interfacing */ -static int qcam_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); struct qcam_device *qcam=(struct qcam_device *)dev; @@ -667,9 +666,9 @@ static int qcam_do_ioctl(struct inode *inode, struct file *file, } static int qcam_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, qcam_do_ioctl); + return video_usercopy(file, cmd, arg, qcam_do_ioctl); } static ssize_t qcam_read(struct file *file, char __user *buf, diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c index 16c094f..028a400 100644 --- a/drivers/media/video/cpia.c +++ b/drivers/media/video/cpia.c @@ -3333,8 +3333,7 @@ static ssize_t cpia_read(struct file *file, char __user *buf, return cam->decompressed_frame.count; } -static int cpia_do_ioctl(struct inode *inode, struct file *file, - unsigned int ioctlnr, void *arg) +static int cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = file->private_data; struct cam_data *cam = video_get_drvdata(dev); @@ -3347,9 +3346,9 @@ static int cpia_do_ioctl(struct inode *inode, struct file *file, if (mutex_lock_interruptible(&cam->busy_lock)) return -EINTR; - //DBG("cpia_ioctl: %u\n", ioctlnr); + /* DBG("cpia_ioctl: %u\n", cmd); */ - switch (ioctlnr) { + switch (cmd) { /* query capabilities */ case VIDIOCGCAP: { @@ -3724,7 +3723,7 @@ static int cpia_do_ioctl(struct inode *inode, struct file *file, static int cpia_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, cpia_do_ioctl); + return video_usercopy(file, cmd, arg, cpia_do_ioctl); } diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c index 7e791b6..1cc0df8 100644 --- a/drivers/media/video/cpia2/cpia2_core.c +++ b/drivers/media/video/cpia2/cpia2_core.c @@ -25,7 +25,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Stripped of 2.4 stuff ready for main kernel submit by - * Alan Cox <alan@redhat.com> + * Alan Cox <alan@lxorguk.ukuu.org.uk> * ****************************************************************************/ diff --git a/drivers/media/video/cpia2/cpia2_usb.c b/drivers/media/video/cpia2/cpia2_usb.c index 73511a5..dc5b07a 100644 --- a/drivers/media/video/cpia2/cpia2_usb.c +++ b/drivers/media/video/cpia2/cpia2_usb.c @@ -25,7 +25,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Stripped of 2.4 stuff ready for main kernel submit by - * Alan Cox <alan@redhat.com> + * Alan Cox <alan@lxorguk.ukuu.org.uk> ****************************************************************************/ #include <linux/kernel.h> diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 1c6bd63..3c2d7ea 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -26,7 +26,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Stripped of 2.4 stuff ready for main kernel submit by - * Alan Cox <alan@redhat.com> + * Alan Cox <alan@lxorguk.ukuu.org.uk> ****************************************************************************/ #include <linux/version.h> @@ -1572,8 +1572,7 @@ static int ioctl_dqbuf(void *arg,struct camera_data *cam, struct file *file) * cpia2_ioctl * *****************************************************************************/ -static int cpia2_do_ioctl(struct inode *inode, struct file *file, - unsigned int ioctl_nr, void *arg) +static int cpia2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct camera_data *cam = video_drvdata(file); int retval = 0; @@ -1591,7 +1590,7 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file, } /* Priority check */ - switch (ioctl_nr) { + switch (cmd) { case VIDIOCSWIN: case VIDIOCMCAPTURE: case VIDIOC_S_FMT: @@ -1618,7 +1617,7 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file, break; } - switch (ioctl_nr) { + switch (cmd) { case VIDIOCGCAP: /* query capabilities */ retval = ioctl_cap_query(arg, cam); break; @@ -1683,7 +1682,7 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_ENUMINPUT: case VIDIOC_G_INPUT: case VIDIOC_S_INPUT: - retval = ioctl_input(ioctl_nr, arg,cam); + retval = ioctl_input(cmd, arg, cam); break; case VIDIOC_ENUM_FMT: @@ -1843,9 +1842,9 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file, } static int cpia2_ioctl(struct inode *inode, struct file *file, - unsigned int ioctl_nr, unsigned long iarg) + unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, ioctl_nr, iarg, cpia2_do_ioctl); + return video_usercopy(file, cmd, arg, cpia2_do_ioctl); } /****************************************************************************** diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c index a662b15..70fcd0d 100644 --- a/drivers/media/video/cs5345.c +++ b/drivers/media/video/cs5345.c @@ -23,9 +23,9 @@ #include <linux/kernel.h> #include <linux/i2c.h> #include <linux/videodev2.h> -#include <media/v4l2-i2c-drv.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC"); MODULE_AUTHOR("Hans Verkuil"); @@ -40,111 +40,143 @@ MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); /* ----------------------------------------------------------------------- */ -static inline int cs5345_write(struct i2c_client *client, u8 reg, u8 value) +static inline int cs5345_write(struct v4l2_subdev *sd, u8 reg, u8 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_write_byte_data(client, reg, value); } -static inline int cs5345_read(struct i2c_client *client, u8 reg) +static inline int cs5345_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte_data(client, reg); } -static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg) +static int cs5345_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct v4l2_routing *route = arg; - struct v4l2_control *ctrl = arg; - - switch (cmd) { - case VIDIOC_INT_G_AUDIO_ROUTING: - route->input = cs5345_read(client, 0x09) & 7; - route->input |= cs5345_read(client, 0x05) & 0x70; - route->output = 0; - break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - if ((route->input & 0xf) > 6) { - v4l_err(client, "Invalid input %d.\n", route->input); - return -EINVAL; - } - cs5345_write(client, 0x09, route->input & 0xf); - cs5345_write(client, 0x05, route->input & 0xf0); - break; - - case VIDIOC_G_CTRL: - if (ctrl->id == V4L2_CID_AUDIO_MUTE) { - ctrl->value = (cs5345_read(client, 0x04) & 0x08) != 0; - break; - } - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - ctrl->value = cs5345_read(client, 0x07) & 0x3f; - if (ctrl->value >= 32) - ctrl->value = ctrl->value - 64; - break; - - case VIDIOC_S_CTRL: - break; - if (ctrl->id == V4L2_CID_AUDIO_MUTE) { - cs5345_write(client, 0x04, ctrl->value ? 0x80 : 0); - break; - } - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - if (ctrl->value > 24 || ctrl->value < -24) - return -EINVAL; - cs5345_write(client, 0x07, ((u8)ctrl->value) & 0x3f); - cs5345_write(client, 0x08, ((u8)ctrl->value) & 0x3f); - break; - -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = cs5345_read(client, reg->reg & 0x1f); - else - cs5345_write(client, reg->reg & 0x1f, reg->val & 0xff); - break; + if ((route->input & 0xf) > 6) { + v4l2_err(sd, "Invalid input %d.\n", route->input); + return -EINVAL; } -#endif + cs5345_write(sd, 0x09, route->input & 0xf); + cs5345_write(sd, 0x05, route->input & 0xf0); + return 0; +} - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, - arg, V4L2_IDENT_CS5345, 0); - - case VIDIOC_LOG_STATUS: - { - u8 v = cs5345_read(client, 0x09) & 7; - u8 m = cs5345_read(client, 0x04); - int vol = cs5345_read(client, 0x08) & 0x3f; - - v4l_info(client, "Input: %d%s\n", v, - (m & 0x80) ? " (muted)" : ""); - if (vol >= 32) - vol = vol - 64; - v4l_info(client, "Volume: %d dB\n", vol); - break; - } - - default: +static int cs5345_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + if (ctrl->id == V4L2_CID_AUDIO_MUTE) { + ctrl->value = (cs5345_read(sd, 0x04) & 0x08) != 0; + return 0; + } + if (ctrl->id != V4L2_CID_AUDIO_VOLUME) return -EINVAL; + ctrl->value = cs5345_read(sd, 0x07) & 0x3f; + if (ctrl->value >= 32) + ctrl->value = ctrl->value - 64; + return 0; +} + +static int cs5345_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + if (ctrl->id == V4L2_CID_AUDIO_MUTE) { + cs5345_write(sd, 0x04, ctrl->value ? 0x80 : 0); + return 0; } + if (ctrl->id != V4L2_CID_AUDIO_VOLUME) + return -EINVAL; + if (ctrl->value > 24 || ctrl->value < -24) + return -EINVAL; + cs5345_write(sd, 0x07, ((u8)ctrl->value) & 0x3f); + cs5345_write(sd, 0x08, ((u8)ctrl->value) & 0x3f); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = cs5345_read(sd, reg->reg & 0x1f); + return 0; +} + +static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cs5345_write(sd, reg->reg & 0x1f, reg->val & 0xff); return 0; } +#endif + +static int cs5345_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_CS5345, 0); +} + +static int cs5345_log_status(struct v4l2_subdev *sd) +{ + u8 v = cs5345_read(sd, 0x09) & 7; + u8 m = cs5345_read(sd, 0x04); + int vol = cs5345_read(sd, 0x08) & 0x3f; + + v4l2_info(sd, "Input: %d%s\n", v, + (m & 0x80) ? " (muted)" : ""); + if (vol >= 32) + vol = vol - 64; + v4l2_info(sd, "Volume: %d dB\n", vol); + return 0; +} + +static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops cs5345_core_ops = { + .log_status = cs5345_log_status, + .g_chip_ident = cs5345_g_chip_ident, + .g_ctrl = cs5345_g_ctrl, + .s_ctrl = cs5345_s_ctrl, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = cs5345_g_register, + .s_register = cs5345_s_register, +#endif +}; + +static const struct v4l2_subdev_audio_ops cs5345_audio_ops = { + .s_routing = cs5345_s_routing, +}; + +static const struct v4l2_subdev_ops cs5345_ops = { + .core = &cs5345_core_ops, + .audio = &cs5345_audio_ops, +}; /* ----------------------------------------------------------------------- */ static int cs5345_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_subdev *sd; + /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; @@ -152,9 +184,25 @@ static int cs5345_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - cs5345_write(client, 0x02, 0x00); - cs5345_write(client, 0x04, 0x01); - cs5345_write(client, 0x09, 0x01); + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &cs5345_ops); + + cs5345_write(sd, 0x02, 0x00); + cs5345_write(sd, 0x04, 0x01); + cs5345_write(sd, 0x09, 0x01); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int cs5345_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); return 0; } @@ -171,5 +219,6 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .driverid = I2C_DRIVERID_CS5345, .command = cs5345_command, .probe = cs5345_probe, + .remove = cs5345_remove, .id_table = cs5345_id, }; diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index c444450..cb65d51 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -27,7 +27,7 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv-legacy.h> @@ -47,84 +47,104 @@ I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ -static int cs53l32a_write(struct i2c_client *client, u8 reg, u8 value) +static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_write_byte_data(client, reg, value); } -static int cs53l32a_read(struct i2c_client *client, u8 reg) +static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte_data(client, reg); } -static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg) +static int cs53l32a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct v4l2_routing *route = arg; - struct v4l2_control *ctrl = arg; - - switch (cmd) { - case VIDIOC_INT_G_AUDIO_ROUTING: - route->input = (cs53l32a_read(client, 0x01) >> 4) & 3; - route->output = 0; - break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - /* There are 2 physical inputs, but the second input can be - placed in two modes, the first mode bypasses the PGA (gain), - the second goes through the PGA. Hence there are three - possible inputs to choose from. */ - if (route->input > 2) { - v4l_err(client, "Invalid input %d.\n", route->input); - return -EINVAL; - } - cs53l32a_write(client, 0x01, 0x01 + (route->input << 4)); - break; - - case VIDIOC_G_CTRL: - if (ctrl->id == V4L2_CID_AUDIO_MUTE) { - ctrl->value = (cs53l32a_read(client, 0x03) & 0xc0) != 0; - break; - } - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - ctrl->value = (s8)cs53l32a_read(client, 0x04); - break; - - case VIDIOC_S_CTRL: - if (ctrl->id == V4L2_CID_AUDIO_MUTE) { - cs53l32a_write(client, 0x03, ctrl->value ? 0xf0 : 0x30); - break; - } - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - if (ctrl->value > 12 || ctrl->value < -96) - return -EINVAL; - cs53l32a_write(client, 0x04, (u8) ctrl->value); - cs53l32a_write(client, 0x05, (u8) ctrl->value); - break; - - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, - arg, V4L2_IDENT_CS53l32A, 0); - - case VIDIOC_LOG_STATUS: - { - u8 v = cs53l32a_read(client, 0x01); - u8 m = cs53l32a_read(client, 0x03); - s8 vol = cs53l32a_read(client, 0x04); - - v4l_info(client, "Input: %d%s\n", (v >> 4) & 3, - (m & 0xC0) ? " (muted)" : ""); - v4l_info(client, "Volume: %d dB\n", vol); - break; - } - - default: + /* There are 2 physical inputs, but the second input can be + placed in two modes, the first mode bypasses the PGA (gain), + the second goes through the PGA. Hence there are three + possible inputs to choose from. */ + if (route->input > 2) { + v4l2_err(sd, "Invalid input %d.\n", route->input); return -EINVAL; } + cs53l32a_write(sd, 0x01, 0x01 + (route->input << 4)); + return 0; +} + +static int cs53l32a_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + if (ctrl->id == V4L2_CID_AUDIO_MUTE) { + ctrl->value = (cs53l32a_read(sd, 0x03) & 0xc0) != 0; + return 0; + } + if (ctrl->id != V4L2_CID_AUDIO_VOLUME) + return -EINVAL; + ctrl->value = (s8)cs53l32a_read(sd, 0x04); + return 0; +} + +static int cs53l32a_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + if (ctrl->id == V4L2_CID_AUDIO_MUTE) { + cs53l32a_write(sd, 0x03, ctrl->value ? 0xf0 : 0x30); + return 0; + } + if (ctrl->id != V4L2_CID_AUDIO_VOLUME) + return -EINVAL; + if (ctrl->value > 12 || ctrl->value < -96) + return -EINVAL; + cs53l32a_write(sd, 0x04, (u8) ctrl->value); + cs53l32a_write(sd, 0x05, (u8) ctrl->value); return 0; } +static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, + chip, V4L2_IDENT_CS53l32A, 0); +} + +static int cs53l32a_log_status(struct v4l2_subdev *sd) +{ + u8 v = cs53l32a_read(sd, 0x01); + u8 m = cs53l32a_read(sd, 0x03); + s8 vol = cs53l32a_read(sd, 0x04); + + v4l2_info(sd, "Input: %d%s\n", (v >> 4) & 3, + (m & 0xC0) ? " (muted)" : ""); + v4l2_info(sd, "Volume: %d dB\n", vol); + return 0; +} + +static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops cs53l32a_core_ops = { + .log_status = cs53l32a_log_status, + .g_chip_ident = cs53l32a_g_chip_ident, + .g_ctrl = cs53l32a_g_ctrl, + .s_ctrl = cs53l32a_s_ctrl, +}; + +static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = { + .s_routing = cs53l32a_s_routing, +}; + +static const struct v4l2_subdev_ops cs53l32a_ops = { + .core = &cs53l32a_core_ops, + .audio = &cs53l32a_audio_ops, +}; + /* ----------------------------------------------------------------------- */ /* i2c implementation */ @@ -137,6 +157,7 @@ static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg) static int cs53l32a_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_subdev *sd; int i; /* Check if the adapter supports the needed features */ @@ -149,32 +170,46 @@ static int cs53l32a_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); + sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops); + for (i = 1; i <= 7; i++) { - u8 v = cs53l32a_read(client, i); + u8 v = cs53l32a_read(sd, i); - v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v); + v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); } /* Set cs53l32a internal register for Adaptec 2010/2410 setup */ - cs53l32a_write(client, 0x01, (u8) 0x21); - cs53l32a_write(client, 0x02, (u8) 0x29); - cs53l32a_write(client, 0x03, (u8) 0x30); - cs53l32a_write(client, 0x04, (u8) 0x00); - cs53l32a_write(client, 0x05, (u8) 0x00); - cs53l32a_write(client, 0x06, (u8) 0x00); - cs53l32a_write(client, 0x07, (u8) 0x00); + cs53l32a_write(sd, 0x01, (u8) 0x21); + cs53l32a_write(sd, 0x02, (u8) 0x29); + cs53l32a_write(sd, 0x03, (u8) 0x30); + cs53l32a_write(sd, 0x04, (u8) 0x00); + cs53l32a_write(sd, 0x05, (u8) 0x00); + cs53l32a_write(sd, 0x06, (u8) 0x00); + cs53l32a_write(sd, 0x07, (u8) 0x00); /* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */ for (i = 1; i <= 7; i++) { - u8 v = cs53l32a_read(client, i); + u8 v = cs53l32a_read(sd, i); - v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v); + v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); } return 0; } +static int cs53l32a_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + return 0; +} + static const struct i2c_device_id cs53l32a_id[] = { { "cs53l32a", 0 }, { } @@ -185,6 +220,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "cs53l32a", .driverid = I2C_DRIVERID_CS53L32A, .command = cs53l32a_command, + .remove = cs53l32a_remove, .probe = cs53l32a_probe, .id_table = cs53l32a_id, }; diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c index 0b55837..a2f0ad5 100644 --- a/drivers/media/video/cx18/cx18-av-audio.c +++ b/drivers/media/video/cx18/cx18-av-audio.c @@ -4,6 +4,7 @@ * Derived from cx25840-audio.c * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.net> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,98 +31,165 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) if (freq != 32000 && freq != 44100 && freq != 48000) return -EINVAL; - /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */ - cx18_av_write(cx, 0x127, 0x50); + /* + * The PLL parameters are based on the external crystal frequency that + * would ideally be: + * + * NTSC Color subcarrier freq * 8 = + * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz + * + * The accidents of history and rationale that explain from where this + * combination of magic numbers originate can be found in: + * + * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in + * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80 + * + * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the + * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83 + * + * As Mike Bradley has rightly pointed out, it's not the exact crystal + * frequency that matters, only that all parts of the driver and + * firmware are using the same value (close to the ideal value). + * + * Since I have a strong suspicion that, if the firmware ever assumes a + * crystal value at all, it will assume 28.636360 MHz, the crystal + * freq used in calculations in this driver will be: + * + * xtal_freq = 28.636360 MHz + * + * an error of less than 0.13 ppm which is way, way better than any off + * the shelf crystal will have for accuracy anyway. + * + * Below I aim to run the PLLs' VCOs near 400 MHz to minimze error. + * + * Many thanks to Jeff Campbell and Mike Bradley for their extensive + * investigation, experimentation, testing, and suggested solutions of + * of audio/video sync problems with SVideo and CVBS captures. + */ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { switch (freq) { case 32000: - /* VID_PLL and AUX_PLL */ - cx18_av_write4(cx, 0x108, 0x1408040f); + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20 + */ + cx18_av_write4(cx, 0x108, 0x200d040f); - /* AUX_PLL_FRAC */ - /* 0x8.9504318a * 28,636,363.636 / 0x14 = 32000 * 384 */ - cx18_av_write4(cx, 0x110, 0x012a0863); + /* VID_PLL Fraction = 0x2be2fe */ + /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ + cx18_av_write4(cx, 0x10c, 0x002be2fe); + + /* AUX_PLL Fraction = 0x176740c */ + /* xtal * 0xd.bb3a060/0x20 = 32000 * 384: 393 MHz p-pd*/ + cx18_av_write4(cx, 0x110, 0x0176740c); /* src3/4/6_ctl */ - /* 0x1.f77f = (4 * 15734.26) / 32000 */ + /* 0x1.f77f = (4 * xtal/8*2/455) / 32000 */ cx18_av_write4(cx, 0x900, 0x0801f77f); cx18_av_write4(cx, 0x904, 0x0801f77f); cx18_av_write4(cx, 0x90c, 0x0801f77f); - /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */ - cx18_av_write(cx, 0x127, 0x54); + /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */ + cx18_av_write(cx, 0x127, 0x60); /* AUD_COUNT = 0x2fff = 8 samples * 4 * 384 - 1 */ cx18_av_write4(cx, 0x12c, 0x11202fff); /* - * EN_AV_LOCK = 1 + * EN_AV_LOCK = 0 * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 = * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8 */ - cx18_av_write4(cx, 0x128, 0xa10d2ef8); + cx18_av_write4(cx, 0x128, 0xa00d2ef8); break; case 44100: - /* VID_PLL and AUX_PLL */ - cx18_av_write4(cx, 0x108, 0x1009040f); + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x18 + */ + cx18_av_write4(cx, 0x108, 0x180e040f); + + /* VID_PLL Fraction = 0x2be2fe */ + /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ + cx18_av_write4(cx, 0x10c, 0x002be2fe); - /* AUX_PLL_FRAC */ - /* 0x9.7635e7 * 28,636,363.63 / 0x10 = 44100 * 384 */ - cx18_av_write4(cx, 0x110, 0x00ec6bce); + /* AUX_PLL Fraction = 0x062a1f2 */ + /* xtal * 0xe.3150f90/0x18 = 44100 * 384: 406 MHz p-pd*/ + cx18_av_write4(cx, 0x110, 0x0062a1f2); /* src3/4/6_ctl */ - /* 0x1.6d59 = (4 * 15734.26) / 44100 */ + /* 0x1.6d59 = (4 * xtal/8*2/455) / 44100 */ cx18_av_write4(cx, 0x900, 0x08016d59); cx18_av_write4(cx, 0x904, 0x08016d59); cx18_av_write4(cx, 0x90c, 0x08016d59); + /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x18 */ + cx18_av_write(cx, 0x127, 0x58); + /* AUD_COUNT = 0x92ff = 49 samples * 2 * 384 - 1 */ cx18_av_write4(cx, 0x12c, 0x112092ff); /* - * EN_AV_LOCK = 1 + * EN_AV_LOCK = 0 * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 = * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8 */ - cx18_av_write4(cx, 0x128, 0xa11d4bf8); + cx18_av_write4(cx, 0x128, 0xa01d4bf8); break; case 48000: - /* VID_PLL and AUX_PLL */ - cx18_av_write4(cx, 0x108, 0x100a040f); + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x16 + */ + cx18_av_write4(cx, 0x108, 0x160e040f); - /* AUX_PLL_FRAC */ - /* 0xa.4c6b6ea * 28,636,363.63 / 0x10 = 48000 * 384 */ - cx18_av_write4(cx, 0x110, 0x0098d6dd); + /* VID_PLL Fraction = 0x2be2fe */ + /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ + cx18_av_write4(cx, 0x10c, 0x002be2fe); + + /* AUX_PLL Fraction = 0x05227ad */ + /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz p-pd*/ + cx18_av_write4(cx, 0x110, 0x005227ad); /* src3/4/6_ctl */ - /* 0x1.4faa = (4 * 15734.26) / 48000 */ + /* 0x1.4faa = (4 * xtal/8*2/455) / 48000 */ cx18_av_write4(cx, 0x900, 0x08014faa); cx18_av_write4(cx, 0x904, 0x08014faa); cx18_av_write4(cx, 0x90c, 0x08014faa); + /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ + cx18_av_write(cx, 0x127, 0x56); + /* AUD_COUNT = 0x5fff = 4 samples * 16 * 384 - 1 */ cx18_av_write4(cx, 0x12c, 0x11205fff); /* - * EN_AV_LOCK = 1 + * EN_AV_LOCK = 0 * VID_COUNT = 0x1193f8 = 143999.000 * 8 = * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8 */ - cx18_av_write4(cx, 0x128, 0xa11193f8); + cx18_av_write4(cx, 0x128, 0xa01193f8); break; } } else { switch (freq) { case 32000: - /* VID_PLL and AUX_PLL */ - cx18_av_write4(cx, 0x108, 0x1e08040f); + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x30 + */ + cx18_av_write4(cx, 0x108, 0x300d040f); + + /* VID_PLL Fraction = 0x2be2fe */ + /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ + cx18_av_write4(cx, 0x10c, 0x002be2fe); - /* AUX_PLL_FRAC */ - /* 0x8.9504318 * 28,636,363.63 / 0x1e = 32000 * 256 */ - cx18_av_write4(cx, 0x110, 0x012a0863); + /* AUX_PLL Fraction = 0x176740c */ + /* xtal * 0xd.bb3a060/0x30 = 32000 * 256: 393 MHz p-pd*/ + cx18_av_write4(cx, 0x110, 0x0176740c); /* src1_ctl */ /* 0x1.0000 = 32000/32000 */ @@ -133,27 +201,34 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) cx18_av_write4(cx, 0x904, 0x08020000); cx18_av_write4(cx, 0x90c, 0x08020000); - /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */ - cx18_av_write(cx, 0x127, 0x54); + /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x30 */ + cx18_av_write(cx, 0x127, 0x70); /* AUD_COUNT = 0x1fff = 8 samples * 4 * 256 - 1 */ cx18_av_write4(cx, 0x12c, 0x11201fff); /* - * EN_AV_LOCK = 1 + * EN_AV_LOCK = 0 * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 = * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8 */ - cx18_av_write4(cx, 0x128, 0xa10d2ef8); + cx18_av_write4(cx, 0x128, 0xa00d2ef8); break; case 44100: - /* VID_PLL and AUX_PLL */ - cx18_av_write4(cx, 0x108, 0x1809040f); + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x24 + */ + cx18_av_write4(cx, 0x108, 0x240e040f); - /* AUX_PLL_FRAC */ - /* 0x9.7635e74 * 28,636,363.63 / 0x18 = 44100 * 256 */ - cx18_av_write4(cx, 0x110, 0x00ec6bce); + /* VID_PLL Fraction = 0x2be2fe */ + /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ + cx18_av_write4(cx, 0x10c, 0x002be2fe); + + /* AUX_PLL Fraction = 0x062a1f2 */ + /* xtal * 0xe.3150f90/0x24 = 44100 * 256: 406 MHz p-pd*/ + cx18_av_write4(cx, 0x110, 0x0062a1f2); /* src1_ctl */ /* 0x1.60cd = 44100/32000 */ @@ -165,24 +240,34 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) cx18_av_write4(cx, 0x904, 0x08017385); cx18_av_write4(cx, 0x90c, 0x08017385); + /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x24 */ + cx18_av_write(cx, 0x127, 0x64); + /* AUD_COUNT = 0x61ff = 49 samples * 2 * 256 - 1 */ cx18_av_write4(cx, 0x12c, 0x112061ff); /* - * EN_AV_LOCK = 1 + * EN_AV_LOCK = 0 * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 = * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8 */ - cx18_av_write4(cx, 0x128, 0xa11d4bf8); + cx18_av_write4(cx, 0x128, 0xa01d4bf8); break; case 48000: - /* VID_PLL and AUX_PLL */ - cx18_av_write4(cx, 0x108, 0x180a040f); + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20 + */ + cx18_av_write4(cx, 0x108, 0x200d040f); + + /* VID_PLL Fraction = 0x2be2fe */ + /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ + cx18_av_write4(cx, 0x10c, 0x002be2fe); - /* AUX_PLL_FRAC */ - /* 0xa.4c6b6ea * 28,636,363.63 / 0x18 = 48000 * 256 */ - cx18_av_write4(cx, 0x110, 0x0098d6dd); + /* AUX_PLL Fraction = 0x176740c */ + /* xtal * 0xd.bb3a060/0x20 = 48000 * 256: 393 MHz p-pd*/ + cx18_av_write4(cx, 0x110, 0x0176740c); /* src1_ctl */ /* 0x1.8000 = 48000/32000 */ @@ -194,15 +279,18 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) cx18_av_write4(cx, 0x904, 0x08015555); cx18_av_write4(cx, 0x90c, 0x08015555); + /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */ + cx18_av_write(cx, 0x127, 0x60); + /* AUD_COUNT = 0x3fff = 4 samples * 16 * 256 - 1 */ cx18_av_write4(cx, 0x12c, 0x11203fff); /* - * EN_AV_LOCK = 1 + * EN_AV_LOCK = 0 * VID_COUNT = 0x1193f8 = 143999.000 * 8 = * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8 */ - cx18_av_write4(cx, 0x128, 0xa11193f8); + cx18_av_write4(cx, 0x128, 0xa01193f8); break; } } @@ -215,12 +303,15 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) void cx18_av_audio_set_path(struct cx18 *cx) { struct cx18_av_state *state = &cx->av_state; + u8 v; /* stop microcontroller */ - cx18_av_and_or(cx, 0x803, ~0x10, 0); + v = cx18_av_read(cx, 0x803) & ~0x10; + cx18_av_write_expect(cx, 0x803, v, v, 0x1f); /* assert soft reset */ - cx18_av_and_or(cx, 0x810, ~0x1, 0x01); + v = cx18_av_read(cx, 0x810) | 0x01; + cx18_av_write_expect(cx, 0x810, v, v, 0x0f); /* Mute everything to prevent the PFFT! */ cx18_av_write(cx, 0x8d3, 0x1f); @@ -240,12 +331,14 @@ void cx18_av_audio_set_path(struct cx18 *cx) set_audclk_freq(cx, state->audclk_freq); /* deassert soft reset */ - cx18_av_and_or(cx, 0x810, ~0x1, 0x00); + v = cx18_av_read(cx, 0x810) & ~0x01; + cx18_av_write_expect(cx, 0x810, v, v, 0x0f); if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { /* When the microcontroller detects the * audio format, it will unmute the lines */ - cx18_av_and_or(cx, 0x803, ~0x10, 0x10); + v = cx18_av_read(cx, 0x803) | 0x10; + cx18_av_write_expect(cx, 0x803, v, v, 0x1f); } } @@ -347,19 +440,23 @@ static int get_mute(struct cx18 *cx) static void set_mute(struct cx18 *cx, int mute) { struct cx18_av_state *state = &cx->av_state; + u8 v; if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { /* Must turn off microcontroller in order to mute sound. * Not sure if this is the best method, but it does work. * If the microcontroller is running, then it will undo any * changes to the mute register. */ + v = cx18_av_read(cx, 0x803); if (mute) { /* disable microcontroller */ - cx18_av_and_or(cx, 0x803, ~0x10, 0x00); + v &= ~0x10; + cx18_av_write_expect(cx, 0x803, v, v, 0x1f); cx18_av_write(cx, 0x8d3, 0x1f); } else { /* enable microcontroller */ - cx18_av_and_or(cx, 0x803, ~0x10, 0x10); + v |= 0x10; + cx18_av_write_expect(cx, 0x803, v, v, 0x1f); } } else { /* SRC1_MUTE_EN */ @@ -375,16 +472,26 @@ int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg) switch (cmd) { case VIDIOC_INT_AUDIO_CLOCK_FREQ: + { + u8 v; if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { - cx18_av_and_or(cx, 0x803, ~0x10, 0); + v = cx18_av_read(cx, 0x803) & ~0x10; + cx18_av_write_expect(cx, 0x803, v, v, 0x1f); cx18_av_write(cx, 0x8d3, 0x1f); } - cx18_av_and_or(cx, 0x810, ~0x1, 1); + v = cx18_av_read(cx, 0x810) | 0x1; + cx18_av_write_expect(cx, 0x810, v, v, 0x0f); + retval = set_audclk_freq(cx, *(u32 *)arg); - cx18_av_and_or(cx, 0x810, ~0x1, 0); - if (state->aud_input > CX18_AV_AUDIO_SERIAL2) - cx18_av_and_or(cx, 0x803, ~0x10, 0x10); + + v = cx18_av_read(cx, 0x810) & ~0x1; + cx18_av_write_expect(cx, 0x810, v, v, 0x0f); + if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { + v = cx18_av_read(cx, 0x803) | 0x10; + cx18_av_write_expect(cx, 0x803, v, v, 0x1f); + } return retval; + } case VIDIOC_G_CTRL: switch (ctrl->id) { diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 73f5141..0b1c84b 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -4,6 +4,7 @@ * Derived from cx25840-core.c * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.net> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -36,12 +37,31 @@ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value) return 0; } +int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask) +{ + u32 reg = 0xc40000 + (addr & ~3); + int shift = (addr & 3) * 8; + u32 x = cx18_read_reg(cx, reg); + + x = (x & ~((u32)0xff << shift)) | ((u32)value << shift); + cx18_write_reg_expect(cx, x, reg, + ((u32)eval << shift), ((u32)mask << shift)); + return 0; +} + int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value) { cx18_write_reg(cx, value, 0xc40000 + addr); return 0; } +int +cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval, u32 mask) +{ + cx18_write_reg_expect(cx, value, 0xc40000 + addr, eval, mask); + return 0; +} + int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value) { cx18_write_reg_noretry(cx, value, 0xc40000 + addr); @@ -61,11 +81,6 @@ u32 cx18_av_read4(struct cx18 *cx, u16 addr) return cx18_read_reg(cx, 0xc40000 + addr); } -u32 cx18_av_read4_noretry(struct cx18 *cx, u16 addr) -{ - return cx18_read_reg_noretry(cx, 0xc40000 + addr); -} - int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask, u8 or_value) { @@ -98,14 +113,16 @@ static void cx18_av_initialize(struct cx18 *cx) cx18_av_loadfw(cx); /* Stop 8051 code execution */ - cx18_av_write4(cx, CXADEC_DL_CTL, 0x03000000); + cx18_av_write4_expect(cx, CXADEC_DL_CTL, 0x03000000, + 0x03000000, 0x13000000); /* initallize the PLL by toggling sleep bit */ v = cx18_av_read4(cx, CXADEC_HOST_REG1); - /* enable sleep mode */ - cx18_av_write4(cx, CXADEC_HOST_REG1, v | 1); + /* enable sleep mode - register appears to be read only... */ + cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v | 1, v, 0xfffe); /* disable sleep mode */ - cx18_av_write4(cx, CXADEC_HOST_REG1, v & 0xfffe); + cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v & 0xfffe, + v & 0xfffe, 0xffff); /* initialize DLLs */ v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF; @@ -125,9 +142,10 @@ static void cx18_av_initialize(struct cx18 *cx) v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1; /* enable TUNE_FIL_RST */ - cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v); + cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3, v, v, 0x03009F0F); /* disable TUNE_FIL_RST */ - cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v & 0xFFFFFFFE); + cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3, + v & 0xFFFFFFFE, v & 0xFFFFFFFE, 0x03009F0F); /* enable 656 output */ cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00); @@ -251,10 +269,9 @@ void cx18_av_std_setup(struct cx18 *cx) pll_int, pll_frac, pll_post); if (pll_post) { - int fin, fsc; - int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac); + int fin, fsc, pll; - pll >>= 25; + pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25; pll /= pll_post; CX18_DEBUG_INFO("PLL = %d.%06d MHz\n", pll / 1000000, pll % 1000000); @@ -324,6 +341,7 @@ static void input_change(struct cx18 *cx) { struct cx18_av_state *state = &cx->av_state; v4l2_std_id std = state->std; + u8 v; /* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */ cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); @@ -333,31 +351,34 @@ static void input_change(struct cx18 *cx) if (std & V4L2_STD_525_60) { if (std == V4L2_STD_NTSC_M_JP) { /* Japan uses EIAJ audio standard */ - cx18_av_write(cx, 0x808, 0xf7); - cx18_av_write(cx, 0x80b, 0x02); + cx18_av_write_expect(cx, 0x808, 0xf7, 0xf7, 0xff); + cx18_av_write_expect(cx, 0x80b, 0x02, 0x02, 0x3f); } else if (std == V4L2_STD_NTSC_M_KR) { /* South Korea uses A2 audio standard */ - cx18_av_write(cx, 0x808, 0xf8); - cx18_av_write(cx, 0x80b, 0x03); + cx18_av_write_expect(cx, 0x808, 0xf8, 0xf8, 0xff); + cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f); } else { /* Others use the BTSC audio standard */ - cx18_av_write(cx, 0x808, 0xf6); - cx18_av_write(cx, 0x80b, 0x01); + cx18_av_write_expect(cx, 0x808, 0xf6, 0xf6, 0xff); + cx18_av_write_expect(cx, 0x80b, 0x01, 0x01, 0x3f); } } else if (std & V4L2_STD_PAL) { /* Follow tuner change procedure for PAL */ - cx18_av_write(cx, 0x808, 0xff); - cx18_av_write(cx, 0x80b, 0x03); + cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff); + cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f); } else if (std & V4L2_STD_SECAM) { /* Select autodetect for SECAM */ - cx18_av_write(cx, 0x808, 0xff); - cx18_av_write(cx, 0x80b, 0x03); + cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff); + cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f); } - if (cx18_av_read(cx, 0x803) & 0x10) { + v = cx18_av_read(cx, 0x803); + if (v & 0x10) { /* restart audio decoder microcontroller */ - cx18_av_and_or(cx, 0x803, ~0x10, 0x00); - cx18_av_and_or(cx, 0x803, ~0x10, 0x10); + v &= ~0x10; + cx18_av_write_expect(cx, 0x803, v, v, 0x1f); + v |= 0x10; + cx18_av_write_expect(cx, 0x803, v, v, 0x1f); } } @@ -368,6 +389,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 && vid_input <= CX18_AV_COMPOSITE8); u8 reg; + u8 v; CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n", vid_input, aud_input); @@ -413,16 +435,23 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, return -EINVAL; } - cx18_av_write(cx, 0x103, reg); + cx18_av_write_expect(cx, 0x103, reg, reg, 0xf7); /* Set INPUT_MODE to Composite (0) or S-Video (1) */ cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02); + /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ - cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); + v = cx18_av_read(cx, 0x102); + if (reg & 0x80) + v &= ~0x2; + else + v |= 0x2; /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */ if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) - cx18_av_and_or(cx, 0x102, ~0x4, 4); + v |= 0x4; else - cx18_av_and_or(cx, 0x102, ~0x4, 0); + v &= ~0x4; + cx18_av_write_expect(cx, 0x102, v, v, 0x17); + /*cx18_av_and_or4(cx, 0x104, ~0x001b4180, 0x00004180);*/ state->vid_input = vid_input; @@ -799,40 +828,47 @@ int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg) } case VIDIOC_S_TUNER: + { + u8 v; + if (state->radio) break; + v = cx18_av_read(cx, 0x809); + v &= ~0xf; + switch (vt->audmode) { case V4L2_TUNER_MODE_MONO: /* mono -> mono stereo -> mono bilingual -> lang1 */ - cx18_av_and_or(cx, 0x809, ~0xf, 0x00); break; case V4L2_TUNER_MODE_STEREO: case V4L2_TUNER_MODE_LANG1: /* mono -> mono stereo -> stereo bilingual -> lang1 */ - cx18_av_and_or(cx, 0x809, ~0xf, 0x04); + v |= 0x4; break; case V4L2_TUNER_MODE_LANG1_LANG2: /* mono -> mono stereo -> stereo bilingual -> lang1/lang2 */ - cx18_av_and_or(cx, 0x809, ~0xf, 0x07); + v |= 0x7; break; case V4L2_TUNER_MODE_LANG2: /* mono -> mono stereo -> stereo bilingual -> lang2 */ - cx18_av_and_or(cx, 0x809, ~0xf, 0x01); + v |= 0x1; break; default: return -EINVAL; } + cx18_av_write_expect(cx, 0x809, v, v, 0xff); state->audmode = vt->audmode; break; + } case VIDIOC_G_FMT: return get_v4lfmt(cx, (struct v4l2_format *)arg); diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h index b67d8df..cf68a60 100644 --- a/drivers/media/video/cx18/cx18-av-core.h +++ b/drivers/media/video/cx18/cx18-av-core.h @@ -4,6 +4,7 @@ * Derived from cx25840-core.h * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.net> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -302,9 +303,11 @@ struct cx18_av_state { int cx18_av_write(struct cx18 *cx, u16 addr, u8 value); int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value); int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value); +int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask); +int cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval, + u32 mask); u8 cx18_av_read(struct cx18 *cx, u16 addr); u32 cx18_av_read4(struct cx18 *cx, u16 addr); -u32 cx18_av_read4_noretry(struct cx18 *cx, u16 addr); int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value); int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value); int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg); diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c index 522a035..c64fd0a 100644 --- a/drivers/media/video/cx18/cx18-av-firmware.c +++ b/drivers/media/video/cx18/cx18-av-firmware.c @@ -2,6 +2,7 @@ * cx18 ADEC firmware functions * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.net> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -43,11 +44,13 @@ int cx18_av_loadfw(struct cx18 *cx) /* The firmware load often has byte errors, so allow for several retries, both at byte level and at the firmware load level. */ while (retries1 < 5) { - cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000); - cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); + cx18_av_write4_expect(cx, CXADEC_CHIP_CTRL, 0x00010000, + 0x00008430, 0xffffffff); /* cx25843 */ + cx18_av_write_expect(cx, CXADEC_STD_DET_CTL, 0xf6, 0xf6, 0xff); - /* Reset the Mako core (Register is undocumented.) */ - cx18_av_write4(cx, 0x8100, 0x00010000); + /* Reset the Mako core, Register is alias of CXADEC_CHIP_CTRL */ + cx18_av_write4_expect(cx, 0x8100, 0x00010000, + 0x00008430, 0xffffffff); /* cx25843 */ /* Put the 8051 in reset and enable firmware upload */ cx18_av_write4_noretry(cx, CXADEC_DL_CTL, 0x0F000000); @@ -61,13 +64,12 @@ int cx18_av_loadfw(struct cx18 *cx) int retries2; int unrec_err = 0; - for (retries2 = 0; retries2 < CX18_MAX_MMIO_RETRIES; + for (retries2 = 0; retries2 < CX18_MAX_MMIO_WR_RETRIES; retries2++) { cx18_av_write4_noretry(cx, CXADEC_DL_CTL, dl_control); udelay(10); - value = cx18_av_read4_noretry(cx, - CXADEC_DL_CTL); + value = cx18_av_read4(cx, CXADEC_DL_CTL); if (value == dl_control) break; /* Check if we can correct the byte by changing @@ -78,9 +80,7 @@ int cx18_av_loadfw(struct cx18 *cx) break; } } - cx18_log_write_retries(cx, retries2, - cx->reg_mem + 0xc40000 + CXADEC_DL_CTL); - if (unrec_err || retries2 >= CX18_MAX_MMIO_RETRIES) + if (unrec_err || retries2 >= CX18_MAX_MMIO_WR_RETRIES) break; } if (i == size) @@ -93,7 +93,8 @@ int cx18_av_loadfw(struct cx18 *cx) return -EIO; } - cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size); + cx18_av_write4_expect(cx, CXADEC_DL_CTL, + 0x13000000 | fw->size, 0x13000000, 0x13000000); /* Output to the 416 */ cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000); @@ -118,7 +119,8 @@ int cx18_av_loadfw(struct cx18 *cx) passthrough */ cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687); - cx18_av_write4(cx, CXADEC_STD_DET_CTL, 0x000000F6); + cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, 0x000000F6, 0x000000F6, + 0x3F00FFFF); /* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */ /* Set bit 0 in register 0x9CC to signify that this is MiniMe. */ @@ -136,7 +138,7 @@ int cx18_av_loadfw(struct cx18 *cx) v |= 0xFF; /* Auto by default */ v |= 0x400; /* Stereo by default */ v |= 0x14000000; - cx18_av_write4(cx, CXADEC_STD_DET_CTL, v); + cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, v, v, 0x3F00FFFF); release_firmware(fw); diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c index 02fdf57..1527ea4 100644 --- a/drivers/media/video/cx18/cx18-av-vbi.c +++ b/drivers/media/video/cx18/cx18-av-vbi.c @@ -141,10 +141,11 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) u8 lcr[24]; fmt = arg; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE && + fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE) return -EINVAL; svbi = &fmt->fmt.sliced; - if (svbi->service_set == 0) { + if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { /* raw VBI */ memset(svbi, 0, sizeof(*svbi)); diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index 5efe01e..e274043 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -4,6 +4,7 @@ * Derived from ivtv-cards.c * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -50,7 +51,7 @@ static struct cx18_card_tuner_i2c cx18_i2c_std = { static const struct cx18_card cx18_card_hvr1600_esmt = { .type = CX18_CARD_HVR_1600_ESMT, .name = "Hauppauge HVR-1600", - .comment = "VBI is not yet supported\n", + .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_muxer = CX18_HW_CS5345, @@ -96,7 +97,7 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { static const struct cx18_card cx18_card_hvr1600_samsung = { .type = CX18_CARD_HVR_1600_SAMSUNG, .name = "Hauppauge HVR-1600 (Preproduction)", - .comment = "VBI is not yet supported\n", + .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_muxer = CX18_HW_CS5345, @@ -151,7 +152,7 @@ static const struct cx18_card_pci_info cx18_pci_h900[] = { static const struct cx18_card cx18_card_h900 = { .type = CX18_CARD_COMPRO_H900, .name = "Compro VideoMate H900", - .comment = "VBI is not yet supported\n", + .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_all = CX18_HW_TUNER, @@ -248,7 +249,7 @@ static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = { static const struct cx18_card cx18_card_cnxt_raptor_pal = { .type = CX18_CARD_CNXT_RAPTOR_PAL, .name = "Conexant Raptor PAL/SECAM", - .comment = "VBI is not yet supported\n", + .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_muxer = CX18_HW_GPIO, diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index 32155f6..6fa7bcb 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -4,6 +4,7 @@ * Derived from ivtv-cards.c * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -47,8 +48,9 @@ /* V4L2 capability aliases */ #define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ - V4L2_CAP_AUDIO | V4L2_CAP_READWRITE) -/* | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */ + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | \ + V4L2_CAP_VBI_CAPTURE) +/* | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */ struct cx18_card_video_input { u8 video_type; /* video input type */ diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c index f46c7e5..17edf30 100644 --- a/drivers/media/video/cx18/cx18-controls.c +++ b/drivers/media/video/cx18/cx18-controls.c @@ -259,6 +259,7 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) return err; } if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + struct cx18_api_func_private priv; struct cx2341x_mpeg_params p = cx->params; int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing), c, VIDIOC_S_EXT_CTRLS); @@ -278,7 +279,9 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) fmt.fmt.pix.height = cx->params.height; cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt); } - err = cx2341x_update(cx, cx18_api_func, &cx->params, &p); + priv.cx = cx; + priv.s = &cx->streams[id->type]; + err = cx2341x_update(&priv, cx18_api_func, &cx->params, &p); if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt) err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt); cx->params = p; diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 7874d97..f50cf21 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -75,48 +75,76 @@ static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; -static int mmio_ndelay[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 }; static unsigned cardtype_c = 1; static unsigned tuner_c = 1; static unsigned radio_c = 1; -static unsigned mmio_ndelay_c = 1; static char pal[] = "--"; static char secam[] = "--"; static char ntsc[] = "-"; /* Buffers */ -static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS; static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS; +static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS; +static int enc_idx_buffers = CX18_DEFAULT_ENC_IDX_BUFFERS; static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS; static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS; static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS; +static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE; +static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE; +static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE; +static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE; +/* VBI bufsize based on standards supported by card tuner for now */ +static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE; + +static int enc_ts_bufs = -1; +static int enc_mpg_bufs = -1; +static int enc_idx_bufs = -1; +static int enc_yuv_bufs = -1; +static int enc_vbi_bufs = -1; +static int enc_pcm_bufs = -1; + + static int cx18_pci_latency = 1; -int cx18_retry_mmio = 1; +static int mmio_ndelay; +static int retry_mmio = 1; + int cx18_debug; module_param_array(tuner, int, &tuner_c, 0644); module_param_array(radio, bool, &radio_c, 0644); module_param_array(cardtype, int, &cardtype_c, 0644); -module_param_array(mmio_ndelay, int, &mmio_ndelay_c, 0644); module_param_string(pal, pal, sizeof(pal), 0644); module_param_string(secam, secam, sizeof(secam), 0644); module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); module_param_named(debug, cx18_debug, int, 0644); -module_param_named(retry_mmio, cx18_retry_mmio, int, 0644); +module_param(mmio_ndelay, int, 0644); +module_param(retry_mmio, int, 0644); module_param(cx18_pci_latency, int, 0644); module_param(cx18_first_minor, int, 0644); -module_param(enc_mpg_buffers, int, 0644); module_param(enc_ts_buffers, int, 0644); +module_param(enc_mpg_buffers, int, 0644); +module_param(enc_idx_buffers, int, 0644); module_param(enc_yuv_buffers, int, 0644); module_param(enc_vbi_buffers, int, 0644); module_param(enc_pcm_buffers, int, 0644); +module_param(enc_ts_bufsize, int, 0644); +module_param(enc_mpg_bufsize, int, 0644); +module_param(enc_idx_bufsize, int, 0644); +module_param(enc_yuv_bufsize, int, 0644); +/* VBI bufsize based on standards supported by card tuner for now */ +module_param(enc_pcm_bufsize, int, 0644); + +module_param(enc_ts_bufs, int, 0644); +module_param(enc_mpg_bufs, int, 0644); +module_param(enc_idx_bufs, int, 0644); +module_param(enc_yuv_bufs, int, 0644); +module_param(enc_vbi_bufs, int, 0644); +module_param(enc_pcm_bufs, int, 0644); + MODULE_PARM_DESC(tuner, "Tuner type selection,\n" "\t\t\tsee tuner.h for values"); MODULE_PARM_DESC(radio, @@ -152,28 +180,62 @@ MODULE_PARM_DESC(cx18_pci_latency, "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" "\t\t\tDefault: Yes"); MODULE_PARM_DESC(retry_mmio, - "Check and retry memory mapped IO accesses\n" - "\t\t\tDefault: 1 [Yes]"); + "(Deprecated) MMIO writes are now always checked and retried\n" + "\t\t\tEffectively: 1 [Yes]"); MODULE_PARM_DESC(mmio_ndelay, - "Delay (ns) for each CX23418 memory mapped IO access.\n" - "\t\t\tTry larger values that are close to a multiple of the\n" - "\t\t\tPCI clock period, 30.3 ns, if your card doesn't work.\n" - "\t\t\tDefault: " __stringify(CX18_DEFAULT_MMIO_NDELAY)); -MODULE_PARM_DESC(enc_mpg_buffers, - "Encoder MPG Buffers (in MB)\n" - "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS)); + "(Deprecated) MMIO accesses are now never purposely delayed\n" + "\t\t\tEffectively: 0 ns"); MODULE_PARM_DESC(enc_ts_buffers, - "Encoder TS Buffers (in MB)\n" + "Encoder TS buffer memory (MB). (enc_ts_bufs can override)\n" "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS)); +MODULE_PARM_DESC(enc_ts_bufsize, + "Size of an encoder TS buffer (kB)\n" + "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFSIZE)); +MODULE_PARM_DESC(enc_ts_bufs, + "Number of encoder TS buffers\n" + "\t\t\tDefault is computed from other enc_ts_* parameters"); +MODULE_PARM_DESC(enc_mpg_buffers, + "Encoder MPG buffer memory (MB). (enc_mpg_bufs can override)\n" + "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS)); +MODULE_PARM_DESC(enc_mpg_bufsize, + "Size of an encoder MPG buffer (kB)\n" + "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFSIZE)); +MODULE_PARM_DESC(enc_mpg_bufs, + "Number of encoder MPG buffers\n" + "\t\t\tDefault is computed from other enc_mpg_* parameters"); +MODULE_PARM_DESC(enc_idx_buffers, + "Encoder IDX buffer memory (MB). (enc_idx_bufs can override)\n" + "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFFERS)); +MODULE_PARM_DESC(enc_idx_bufsize, + "Size of an encoder IDX buffer (kB)\n" + "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFSIZE)); +MODULE_PARM_DESC(enc_idx_bufs, + "Number of encoder IDX buffers\n" + "\t\t\tDefault is computed from other enc_idx_* parameters"); MODULE_PARM_DESC(enc_yuv_buffers, - "Encoder YUV Buffers (in MB)\n" + "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n" "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS)); +MODULE_PARM_DESC(enc_yuv_bufsize, + "Size of an encoder YUV buffer (kB)\n" + "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFSIZE)); +MODULE_PARM_DESC(enc_yuv_bufs, + "Number of encoder YUV buffers\n" + "\t\t\tDefault is computed from other enc_yuv_* parameters"); MODULE_PARM_DESC(enc_vbi_buffers, - "Encoder VBI Buffers (in MB)\n" + "Encoder VBI buffer memory (MB). (enc_vbi_bufs can override)\n" "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS)); +MODULE_PARM_DESC(enc_vbi_bufs, + "Number of encoder VBI buffers\n" + "\t\t\tDefault is computed from enc_vbi_buffers & tuner std"); MODULE_PARM_DESC(enc_pcm_buffers, - "Encoder PCM buffers (in MB)\n" + "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n" "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS)); +MODULE_PARM_DESC(enc_pcm_bufsize, + "Size of an encoder PCM buffer (kB)\n" + "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFSIZE)); +MODULE_PARM_DESC(enc_pcm_bufs, + "Number of encoder PCM buffers\n" + "\t\t\tDefault is computed from other enc_pcm_* parameters"); MODULE_PARM_DESC(cx18_first_minor, "Set kernel number assigned to first card"); @@ -187,7 +249,7 @@ MODULE_VERSION(CX18_VERSION); /* Generic utility functions */ int cx18_msleep_timeout(unsigned int msecs, int intr) { - int timeout = msecs_to_jiffies(msecs); + long int timeout = msecs_to_jiffies(msecs); int sig; do { @@ -366,20 +428,69 @@ static void cx18_process_options(struct cx18 *cx) { int i, j; - cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers; cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers; + cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers; + cx->options.megabytes[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_buffers; cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers; cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers; cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers; + cx->options.megabytes[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control only */ + + cx->stream_buffers[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufs; + cx->stream_buffers[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufs; + cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufs; + cx->stream_buffers[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufs; + cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_bufs; + cx->stream_buffers[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufs; + cx->stream_buffers[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control, no data */ + + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufsize; + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize; + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize; + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize; + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = 0; /* computed later */ + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize; + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */ + + /* Except for VBI ensure stream_buffers & stream_buf_size are valid */ + for (i = 0; i < CX18_MAX_STREAMS; i++) { + /* User said to use 0 buffers */ + if (cx->stream_buffers[i] == 0) { + cx->options.megabytes[i] = 0; + cx->stream_buf_size[i] = 0; + continue; + } + /* User said to use 0 MB total */ + if (cx->options.megabytes[i] <= 0) { + cx->options.megabytes[i] = 0; + cx->stream_buffers[i] = 0; + cx->stream_buf_size[i] = 0; + continue; + } + /* VBI is computed later or user said buffer has size 0 */ + if (cx->stream_buf_size[i] <= 0) { + if (i != CX18_ENC_STREAM_TYPE_VBI) { + cx->options.megabytes[i] = 0; + cx->stream_buffers[i] = 0; + cx->stream_buf_size[i] = 0; + } + continue; + } + if (cx->stream_buffers[i] < 0) { + cx->stream_buffers[i] = cx->options.megabytes[i] * 1024 + / cx->stream_buf_size[i]; + } else { + /* N.B. This might round down to 0 */ + cx->options.megabytes[i] = + cx->stream_buffers[i] * cx->stream_buf_size[i] / 1024; + } + cx->stream_buf_size[i] *= 1024; /* convert from kB to bytes */ + } + cx->options.cardtype = cardtype[cx->num]; cx->options.tuner = tuner[cx->num]; cx->options.radio = radio[cx->num]; - if (mmio_ndelay[cx->num] < 0) - cx->options.mmio_ndelay = CX18_DEFAULT_MMIO_NDELAY; - else - cx->options.mmio_ndelay = mmio_ndelay[cx->num]; - cx->std = cx18_parse_std(cx); if (cx->options.cardtype == -1) { CX18_INFO("Ignore card\n"); @@ -440,22 +551,30 @@ done: */ static int __devinit cx18_init_struct1(struct cx18 *cx) { + int i; + cx->base_addr = pci_resource_start(cx->dev, 0); mutex_init(&cx->serialize_lock); mutex_init(&cx->i2c_bus_lock[0]); mutex_init(&cx->i2c_bus_lock[1]); mutex_init(&cx->gpio_lock); + mutex_init(&cx->epu2apu_mb_lock); + mutex_init(&cx->epu2cpu_mb_lock); spin_lock_init(&cx->lock); cx->work_queue = create_singlethread_workqueue(cx->name); if (cx->work_queue == NULL) { - CX18_ERR("Could not create work queue\n"); - return -1; + CX18_ERR("Unable to create work hander thread\n"); + return -ENOMEM; } - INIT_WORK(&cx->work, cx18_work_handler); + for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) { + cx->epu_work_order[i].cx = cx; + cx->epu_work_order[i].str = cx->epu_debug_str; + INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler); + } /* start counting open_id at 1 */ cx->open_id = 1; @@ -472,20 +591,55 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) init_waitqueue_head(&cx->cap_w); init_waitqueue_head(&cx->mb_apu_waitq); init_waitqueue_head(&cx->mb_cpu_waitq); - init_waitqueue_head(&cx->mb_epu_waitq); - init_waitqueue_head(&cx->mb_hpu_waitq); init_waitqueue_head(&cx->dma_waitq); /* VBI */ - cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced; - cx->vbi.raw_size = 1456; - cx->vbi.raw_decoder_line_size = 1456; - cx->vbi.raw_decoder_sav_odd_field = 0x20; - cx->vbi.raw_decoder_sav_even_field = 0x60; - cx->vbi.sliced_decoder_line_size = 272; - cx->vbi.sliced_decoder_sav_odd_field = 0xB0; - cx->vbi.sliced_decoder_sav_even_field = 0xF0; + + /* + * The VBI line sizes depend on the pixel clock and the horiz rate + * + * (1/Fh)*(2*Fp) = Samples/line + * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples + * + * Sliced VBI is sent as ancillary data during horizontal blanking + * Raw VBI is sent as active video samples during vertcal blanking + * + * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line + * length of 720 pixels @ 4:2:2 sampling. Thus... + * + * For systems that use a 15.734 kHz horizontal rate, such as + * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have: + * + * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line = + * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples + * + * For systems that use a 15.625 kHz horizontal rate, such as + * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have: + * + * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line = + * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples + * + */ + + /* FIXME: init these based on tuner std & modify when std changes */ + /* CX18-AV-Core number of VBI samples output per horizontal line */ + cx->vbi.raw_decoder_line_size = 1444; /* 4 byte SAV + 2 * 720 */ + cx->vbi.sliced_decoder_line_size = 272; /* 60 Hz: 268+4, 50 Hz: 280+4 */ + + /* CX18-AV-Core VBI samples/line possibly rounded up */ + cx->vbi.raw_size = 1444; /* Real max size is 1444 */ + cx->vbi.sliced_size = 284; /* Real max size is 284 */ + + /* + * CX18-AV-Core SAV/EAV RP codes in VIP 1.x mode + * Task Field VerticalBlank HorizontalBlank 0 0 0 0 + */ + cx->vbi.raw_decoder_sav_odd_field = 0x20; /* V */ + cx->vbi.raw_decoder_sav_even_field = 0x60; /* FV */ + cx->vbi.sliced_decoder_sav_odd_field = 0xB0; /* T VH - actually EAV */ + cx->vbi.sliced_decoder_sav_even_field = 0xF0; /* TFVH - actually EAV */ return 0; } @@ -518,6 +672,7 @@ static void __devinit cx18_init_struct2(struct cx18 *cx) cx->av_state.aud_input = CX18_AV_AUDIO8; cx->av_state.audclk_freq = 48000; cx->av_state.audmode = V4L2_TUNER_MODE_LANG1; + /* FIXME - 8 is NTSC value, investigate */ cx->av_state.vbi_line_offset = 8; } @@ -662,12 +817,9 @@ static int __devinit cx18_probe(struct pci_dev *dev, /* PCI Device Setup */ retval = cx18_setup_pci(cx, dev, pci_id); - if (retval != 0) { - if (retval == -EIO) - goto free_workqueue; - else if (retval == -ENXIO) - goto free_mem; - } + if (retval != 0) + goto free_workqueue; + /* save cx in the pci struct for later use */ pci_set_drvdata(dev, cx); @@ -726,6 +878,7 @@ static int __devinit cx18_probe(struct pci_dev *dev, goto free_i2c; } cx18_init_memory(cx); + cx18_init_scb(cx); /* Register IRQ */ retval = request_irq(cx->dev->irq, cx18_irq_handler, @@ -739,8 +892,6 @@ static int __devinit cx18_probe(struct pci_dev *dev, cx->std = V4L2_STD_NTSC_M; if (cx->options.tuner == -1) { - int i; - for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) { if ((cx->std & cx->card->tuners[i].std) == 0) continue; @@ -777,13 +928,23 @@ static int __devinit cx18_probe(struct pci_dev *dev, } cx->params.video_gop_size = cx->is_60hz ? 15 : 12; - cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = 0x08000; - cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = 0x08000; - cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = 0x01200; - cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = 0x20000; + /* + * FIXME: setting the buffer size based on the tuner standard is + * suboptimal, as the CVBS and SVideo inputs could use a different std + * and the buffer could end up being too small in that case. + */ vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size; + if (cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] < 0) + cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = + cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] * 1024 * 1024 + / vbi_buf_size; + else + cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = + cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] * vbi_buf_size + / (1024 * 1024); + if (cx->options.radio > 0) cx->v4l2_cap |= V4L2_CAP_RADIO; @@ -844,7 +1005,6 @@ err: if (retval == 0) retval = -ENODEV; CX18_ERR("Error %d on initialization\n", retval); - cx18_log_statistics(cx); i = cx->num; spin_lock(&cx18_cards_lock); @@ -923,6 +1083,13 @@ int cx18_init_on_first_open(struct cx18 *cx) return 0; } +static void cx18_cancel_epu_work_orders(struct cx18 *cx) +{ + int i; + for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) + cancel_work_sync(&cx->epu_work_order[i].work); +} + static void cx18_remove(struct pci_dev *pci_dev) { struct cx18 *cx = pci_get_drvdata(pci_dev); @@ -940,7 +1107,8 @@ static void cx18_remove(struct pci_dev *pci_dev) cx18_halt_firmware(cx); - flush_workqueue(cx->work_queue); + cx18_cancel_epu_work_orders(cx); + destroy_workqueue(cx->work_queue); cx18_streams_cleanup(cx, 1); @@ -955,7 +1123,6 @@ static void cx18_remove(struct pci_dev *pci_dev) pci_disable_device(cx->dev); - cx18_log_statistics(cx); CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num); } @@ -1004,6 +1171,7 @@ static void module_cleanup(void) continue; kfree(cx18_cards[i]); } + } module_init(module_start); diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index bbdd5f2..0d2edeb 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -4,6 +4,7 @@ * Derived from ivtv-driver.h * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -64,9 +65,6 @@ # error "This driver requires kernel PCI support." #endif -/* Default delay to throttle mmio access to the CX23418 */ -#define CX18_DEFAULT_MMIO_NDELAY 0 /* 0 ns = 0 PCI clock(s) / 33 MHz */ - #define CX18_MEM_OFFSET 0x00000000 #define CX18_MEM_SIZE 0x04000000 #define CX18_REG_OFFSET 0x02000000 @@ -117,6 +115,17 @@ #define CX18_DEFAULT_ENC_VBI_BUFFERS 1 #define CX18_DEFAULT_ENC_PCM_BUFFERS 1 +/* Maximum firmware DMA buffers per stream */ +#define CX18_MAX_FW_MDLS_PER_STREAM 63 + +/* DMA buffer, default size in kB allocated */ +#define CX18_DEFAULT_ENC_TS_BUFSIZE 32 +#define CX18_DEFAULT_ENC_MPG_BUFSIZE 32 +#define CX18_DEFAULT_ENC_IDX_BUFSIZE 32 +#define CX18_DEFAULT_ENC_YUV_BUFSIZE 128 +/* Default VBI bufsize based on standards supported by card tuner for now */ +#define CX18_DEFAULT_ENC_PCM_BUFSIZE 4 + /* i2c stuff */ #define I2C_CLIENTS_MAX 16 @@ -176,7 +185,6 @@ #define CX18_MAX_PGM_INDEX (400) -extern int cx18_retry_mmio; /* enable check & retry of mmio accesses */ extern int cx18_debug; @@ -185,7 +193,6 @@ struct cx18_options { int cardtype; /* force card type on load */ int tuner; /* set tuner on load */ int radio; /* enable/disable radio */ - unsigned long mmio_ndelay; /* delay in ns after every PCI mmio access */ }; /* per-buffer bit flags */ @@ -203,11 +210,8 @@ struct cx18_options { #define CX18_F_I_EOS 4 /* End of encoder stream */ #define CX18_F_I_RADIO_USER 5 /* radio tuner is selected */ #define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */ -#define CX18_F_I_HAVE_WORK 15 /* there is work to be done */ -#define CX18_F_I_WORK_HANDLER_DVB 18 /* work to be done for DVB */ #define CX18_F_I_INITED 21 /* set after first open */ #define CX18_F_I_FAILED 22 /* set if first open failed */ -#define CX18_F_I_WORK_INITED 23 /* worker thread initialized */ /* These are the VBI types as they appear in the embedded VBI private packets. */ #define CX18_SLICED_TYPE_TELETEXT_B (1) @@ -220,6 +224,7 @@ struct cx18_buffer { dma_addr_t dma_handle; u32 id; unsigned long b_flags; + unsigned skipped; char *buf; u32 bytesused; @@ -248,6 +253,27 @@ struct cx18_dvb { struct cx18; /* forward reference */ struct cx18_scb; /* forward reference */ + +#define CX18_MAX_MDL_ACKS 2 +#define CX18_MAX_EPU_WORK_ORDERS (CX18_MAX_FW_MDLS_PER_STREAM + 7) +/* CPU_DE_RELEASE_MDL can burst CX18_MAX_FW_MDLS_PER_STREAM orders in a group */ + +#define CX18_F_EWO_MB_STALE_UPON_RECEIPT 0x1 +#define CX18_F_EWO_MB_STALE_WHILE_PROC 0x2 +#define CX18_F_EWO_MB_STALE \ + (CX18_F_EWO_MB_STALE_UPON_RECEIPT | CX18_F_EWO_MB_STALE_WHILE_PROC) + +struct cx18_epu_work_order { + struct work_struct work; + atomic_t pending; + struct cx18 *cx; + unsigned long flags; + int rpu; + struct cx18_mailbox mb; + struct cx18_mdl_ack mdl_ack[CX18_MAX_MDL_ACKS]; + char *str; +}; + #define CX18_INVALID_TASK_HANDLE 0xffffffff struct cx18_stream { @@ -261,7 +287,7 @@ struct cx18_stream { unsigned mdl_offset; u32 id; - spinlock_t qlock; /* locks access to the queues */ + struct mutex qlock; /* locks access to the queues */ unsigned long s_flags; /* status flags, see above */ int dma; /* can be PCI_DMA_TODEVICE, PCI_DMA_FROMDEVICE or @@ -275,8 +301,8 @@ struct cx18_stream { /* Buffer Queues */ struct cx18_queue q_free; /* free buffers */ - struct cx18_queue q_full; /* full buffers */ - struct cx18_queue q_io; /* waiting for I/O */ + struct cx18_queue q_busy; /* busy buffers - in use by firmware */ + struct cx18_queue q_full; /* full buffers - data for user apps */ /* DVB / Digital Transport */ struct cx18_dvb dvb; @@ -353,12 +379,7 @@ struct cx18_i2c_algo_callback_data { int bus_index; /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */ }; -#define CX18_MAX_MMIO_RETRIES 10 - -struct cx18_mmio_stats { - atomic_t retried_write[CX18_MAX_MMIO_RETRIES+1]; - atomic_t retried_read[CX18_MAX_MMIO_RETRIES+1]; -}; +#define CX18_MAX_MMIO_WR_RETRIES 10 /* Struct to hold info about cx18 cards */ struct cx18 { @@ -378,7 +399,9 @@ struct cx18 { u32 v4l2_cap; /* V4L2 capabilities of card */ u32 hw_flags; /* Hardware description of the board */ unsigned mdl_offset; - struct cx18_scb __iomem *scb; /* pointer to SCB */ + struct cx18_scb __iomem *scb; /* pointer to SCB */ + struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/ + struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/ struct cx18_av_state av_state; @@ -397,6 +420,7 @@ struct cx18 { struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */ struct cx18_options options; /* User options */ + int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */ int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */ struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */ unsigned long i_flags; /* global cx18 flags */ @@ -428,14 +452,17 @@ struct cx18 { wait_queue_head_t mb_apu_waitq; wait_queue_head_t mb_cpu_waitq; - wait_queue_head_t mb_epu_waitq; - wait_queue_head_t mb_hpu_waitq; wait_queue_head_t cap_w; /* when the current DMA is finished this queue is woken up */ wait_queue_head_t dma_waitq; + u32 sw1_irq_mask; + u32 sw2_irq_mask; + u32 hw2_irq_mask; + struct workqueue_struct *work_queue; - struct work_struct work; + struct cx18_epu_work_order epu_work_order[CX18_MAX_EPU_WORK_ORDERS]; + char epu_debug_str[256]; /* CX18_EPU_DEBUG is rare: use shared space */ /* i2c */ struct i2c_adapter i2c_adap[2]; @@ -450,9 +477,6 @@ struct cx18 { u32 gpio_val; struct mutex gpio_lock; - /* Statistics */ - struct cx18_mmio_stats mmio_stats; - /* v4l2 and User settings */ /* codec settings */ @@ -481,4 +505,10 @@ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv); /* First-open initialization: load firmware, etc. */ int cx18_init_on_first_open(struct cx18 *cx); +/* Test if the current VBI mode is raw (1) or sliced (0) */ +static inline int cx18_raw_vbi(const struct cx18 *cx) +{ + return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE; +} + #endif /* CX18_DRIVER_H */ diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index 4542e2e..bd5e6f3 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/drivers/media/video/cx18/cx18-dvb.c @@ -2,6 +2,7 @@ * cx18 functions for DVB support * * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -23,8 +24,6 @@ #include "cx18-dvb.h" #include "cx18-io.h" #include "cx18-streams.h" -#include "cx18-queue.h" -#include "cx18-scb.h" #include "cx18-cards.h" #include "s5h1409.h" #include "mxl5005s.h" @@ -109,20 +108,23 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) if (!demux->dmx.frontend) return -EINVAL; - if (stream) { - mutex_lock(&stream->dvb.feedlock); - if (stream->dvb.feeding++ == 0) { - CX18_DEBUG_INFO("Starting Transport DMA\n"); - ret = cx18_start_v4l2_encode_stream(stream); - if (ret < 0) { - CX18_DEBUG_INFO( - "Failed to start Transport DMA\n"); - stream->dvb.feeding--; - } - } else - ret = 0; - mutex_unlock(&stream->dvb.feedlock); - } + if (!stream) + return -EINVAL; + + mutex_lock(&stream->dvb.feedlock); + if (stream->dvb.feeding++ == 0) { + CX18_DEBUG_INFO("Starting Transport DMA\n"); + set_bit(CX18_F_S_STREAMING, &stream->s_flags); + ret = cx18_start_v4l2_encode_stream(stream); + if (ret < 0) { + CX18_DEBUG_INFO("Failed to start Transport DMA\n"); + stream->dvb.feeding--; + if (stream->dvb.feeding == 0) + clear_bit(CX18_F_S_STREAMING, &stream->s_flags); + } + } else + ret = 0; + mutex_unlock(&stream->dvb.feedlock); return ret; } @@ -215,6 +217,10 @@ int cx18_dvb_register(struct cx18_stream *stream) dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx); CX18_INFO("DVB Frontend registered\n"); + CX18_INFO("Registered DVB adapter%d for %s (%d x %d kB)\n", + stream->dvb.dvb_adapter.num, stream->name, + stream->buffers, stream->buf_size/1024); + mutex_init(&dvb->feedlock); dvb->enabled = 1; return ret; @@ -302,24 +308,3 @@ static int dvb_register(struct cx18_stream *stream) return ret; } - -void cx18_dvb_work_handler(struct cx18 *cx) -{ - struct cx18_buffer *buf; - struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_TS]; - - while ((buf = cx18_dequeue(s, &s->q_full)) != NULL) { - if (s->dvb.enabled) - dvb_dmx_swfilter(&s->dvb.demux, buf->buf, - buf->bytesused); - - cx18_enqueue(s, buf, &s->q_free); - cx18_buf_sync_for_device(s, buf); - if (s->handle == CX18_INVALID_TASK_HANDLE) /* FIXME: improve */ - continue; - - cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, - (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, - 1, buf->id, s->buf_size); - } -} diff --git a/drivers/media/video/cx18/cx18-dvb.h b/drivers/media/video/cx18/cx18-dvb.h index bbdcefc..bf8d8f6 100644 --- a/drivers/media/video/cx18/cx18-dvb.h +++ b/drivers/media/video/cx18/cx18-dvb.h @@ -23,4 +23,3 @@ int cx18_dvb_register(struct cx18_stream *stream); void cx18_dvb_unregister(struct cx18_stream *stream); -void cx18_dvb_work_handler(struct cx18 *cx); diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 5f90899..425271a 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -4,6 +4,7 @@ * Derived from ivtv-fileops.c * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -66,12 +67,11 @@ static int cx18_claim_stream(struct cx18_open_id *id, int type) } s->id = id->open_id; - /* CX18_DEC_STREAM_TYPE_MPG needs to claim CX18_DEC_STREAM_TYPE_VBI, - CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI + /* CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI (provided VBI insertion is on and sliced VBI is selected), for all other streams we're done */ if (type == CX18_ENC_STREAM_TYPE_MPG && - cx->vbi.insert_mpeg && cx->vbi.sliced_in->service_set) { + cx->vbi.insert_mpeg && !cx18_raw_vbi(cx)) { vbi_type = CX18_ENC_STREAM_TYPE_VBI; } else { return 0; @@ -185,8 +185,10 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) { /* byteswap and process VBI data */ -/* cx18_process_vbi_data(cx, buf, s_vbi->dma_pts, s_vbi->type); */ - cx18_enqueue(s_vbi, buf, &s_vbi->q_free); + cx18_process_vbi_data(cx, buf, + s_vbi->dma_pts, + s_vbi->type); + cx18_stream_put_buf_fw(s_vbi, buf); } } buf = &cx->vbi.sliced_mpeg_buf; @@ -194,11 +196,6 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, return buf; } - /* do we have leftover data? */ - buf = cx18_dequeue(s, &s->q_io); - if (buf) - return buf; - /* do we have new data? */ buf = cx18_dequeue(s, &s->q_full); if (buf) { @@ -262,7 +259,7 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s, if (len > ucount) len = ucount; if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG && - cx->vbi.sliced_in->service_set && buf != &cx->vbi.sliced_mpeg_buf) { + !cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) { const char *start = buf->buf + buf->readpos; const char *p = start + 1; const u8 *q; @@ -337,8 +334,7 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, /* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should arrive one-by-one, so make sure we never output more than one VBI frame at a time */ - if (s->type == CX18_ENC_STREAM_TYPE_VBI && - cx->vbi.sliced_in->service_set) + if (s->type == CX18_ENC_STREAM_TYPE_VBI && !cx18_raw_vbi(cx)) single_frame = 1; for (;;) { @@ -365,16 +361,10 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, tot_count - tot_written); if (buf != &cx->vbi.sliced_mpeg_buf) { - if (buf->readpos == buf->bytesused) { - cx18_buf_sync_for_device(s, buf); - cx18_enqueue(s, buf, &s->q_free); - cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, - s->handle, - (void __iomem *)&cx->scb->cpu_mdl[buf->id] - - cx->enc_mem, - 1, buf->id, s->buf_size); - } else - cx18_enqueue(s, buf, &s->q_io); + if (buf->readpos == buf->bytesused) + cx18_stream_put_buf_fw(s, buf); + else + cx18_push(s, buf, &s->q_full); } else if (buf->readpos == buf->bytesused) { int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; @@ -518,7 +508,7 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait) CX18_DEBUG_HI_FILE("Encoder poll\n"); poll_wait(filp, &s->waitq, wait); - if (atomic_read(&s->q_full.buffers) || atomic_read(&s->q_io.buffers)) + if (atomic_read(&s->q_full.buffers)) return POLLIN | POLLRDNORM; if (eof) return POLLHUP; diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c index 5153442..1fa95da 100644 --- a/drivers/media/video/cx18/cx18-firmware.c +++ b/drivers/media/video/cx18/cx18-firmware.c @@ -2,6 +2,7 @@ * cx18 firmware functions * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -25,6 +26,7 @@ #include "cx18-irq.h" #include "cx18-firmware.h" #include "cx18-cards.h" +#include "cx18-av-core.h" #include <linux/firmware.h> #define CX18_PROC_SOFT_RESET 0xc70010 @@ -121,6 +123,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) if (cx18_raw_readl(cx, dst) != *src) { CX18_ERR("Mismatch at offset %x\n", i); release_firmware(fw); + cx18_setup_page(cx, 0); return -EIO; } dst++; @@ -131,10 +134,12 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); size = fw->size; release_firmware(fw); + cx18_setup_page(cx, SCB_OFFSET); return size; } -static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) +static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, + u32 *entry_addr) { const struct firmware *fw = NULL; int i, j; @@ -149,9 +154,11 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) if (request_firmware(&fw, fn, &cx->dev->dev)) { CX18_ERR("unable to open firmware %s\n", fn); CX18_ERR("did you put the firmware in the hotplug firmware directory?\n"); + cx18_setup_page(cx, 0); return -ENOMEM; } + *entry_addr = 0; src = (const u32 *)fw->data; vers = fw->data + sizeof(seghdr); sz = fw->size; @@ -168,10 +175,12 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) } CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr, seghdr.addr + seghdr.size - 1); + if (*entry_addr == 0) + *entry_addr = seghdr.addr; if (offset + seghdr.size > sz) break; for (i = 0; i < seghdr.size; i += 4096) { - cx18_setup_page(cx, offset + i); + cx18_setup_page(cx, seghdr.addr + i); for (j = i; j < seghdr.size && j < i + 4096; j += 4) { /* no need for endianness conversion on the ppc */ cx18_raw_writel(cx, src[(offset + j) / 4], @@ -181,6 +190,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) CX18_ERR("Mismatch at offset %x\n", offset + j); release_firmware(fw); + cx18_setup_page(cx, 0); return -EIO; } } @@ -192,16 +202,17 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) fn, apu_version, fw->size); size = fw->size; release_firmware(fw); - /* Clear bit0 for APU to start from 0 */ - cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030); + cx18_setup_page(cx, 0); return size; } void cx18_halt_firmware(struct cx18 *cx) { CX18_DEBUG_INFO("Preparing for firmware halt.\n"); - cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ - cx18_write_reg(cx, 0x00020002, CX18_ADEC_CONTROL); + cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET, + 0x0000000F, 0x000F000F); + cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL, + 0x00000002, 0x00020002); } void cx18_init_power(struct cx18 *cx, int lowpwr) @@ -211,9 +222,48 @@ void cx18_init_power(struct cx18 *cx, int lowpwr) cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN); /* ADEC out of sleep */ - cx18_write_reg(cx, 0x00020000, CX18_ADEC_CONTROL); - - /* The fast clock is at 200/245 MHz */ + cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL, + 0x00000000, 0x00020002); + + /* + * The PLL parameters are based on the external crystal frequency that + * would ideally be: + * + * NTSC Color subcarrier freq * 8 = + * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz + * + * The accidents of history and rationale that explain from where this + * combination of magic numbers originate can be found in: + * + * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in + * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80 + * + * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the + * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83 + * + * As Mike Bradley has rightly pointed out, it's not the exact crystal + * frequency that matters, only that all parts of the driver and + * firmware are using the same value (close to the ideal value). + * + * Since I have a strong suspicion that, if the firmware ever assumes a + * crystal value at all, it will assume 28.636360 MHz, the crystal + * freq used in calculations in this driver will be: + * + * xtal_freq = 28.636360 MHz + * + * an error of less than 0.13 ppm which is way, way better than any off + * the shelf crystal will have for accuracy anyway. + * + * Below I aim to run the PLLs' VCOs near 400 MHz to minimze errors. + * + * Many thanks to Jeff Campbell and Mike Bradley for their extensive + * investigation, experimentation, testing, and suggested solutions of + * of audio/video sync problems with SVideo and CVBS captures. + */ + + /* the fast clock is at 200/245 MHz */ + /* 1 * xtal_freq * 0x0d.f7df9b8 / 2 = 200 MHz: 400 MHz pre post-divide*/ + /* 1 * xtal_freq * 0x11.1c71eb8 / 2 = 245 MHz: 490 MHz pre post-divide*/ cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7, CX18_FAST_CLOCK_PLL_FRAC); @@ -223,16 +273,36 @@ void cx18_init_power(struct cx18 *cx, int lowpwr) cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH); /* set slow clock to 125/120 MHz */ - cx18_write_reg(cx, lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT); - cx18_write_reg(cx, lowpwr ? 0xEBAF05 : 0x18618A8, + /* xtal_freq * 0x0d.1861a20 / 3 = 125 MHz: 375 MHz before post-divide */ + /* xtal_freq * 0x0c.92493f8 / 3 = 120 MHz: 360 MHz before post-divide */ + cx18_write_reg(cx, lowpwr ? 0xD : 0xC, CX18_SLOW_CLOCK_PLL_INT); + cx18_write_reg(cx, lowpwr ? 0x30C344 : 0x124927F, CX18_SLOW_CLOCK_PLL_FRAC); - cx18_write_reg(cx, 4, CX18_SLOW_CLOCK_PLL_POST); + cx18_write_reg(cx, 3, CX18_SLOW_CLOCK_PLL_POST); /* mpeg clock pll 54MHz */ + /* xtal_freq * 0xf.15f17f0 / 8 = 54 MHz: 432 MHz before post-divide */ cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT); - cx18_write_reg(cx, 0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC); + cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC); cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST); + /* + * VDCLK Integer = 0x0f, Post Divider = 0x04 + * AIMCLK Integer = 0x0e, Post Divider = 0x16 + */ + cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f); + + /* VDCLK Fraction = 0x2be2fe */ + /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */ + cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe); + + /* AIMCLK Fraction = 0x05227ad */ + /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz before post-divide */ + cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad); + + /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ + cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56); + /* Defaults */ /* APU = SC or SC/2 = 125/62.5 */ /* EPU = SC = 125 */ @@ -248,22 +318,34 @@ void cx18_init_power(struct cx18 *cx, int lowpwr) /* VFC = disabled */ /* USB = disabled */ - cx18_write_reg(cx, lowpwr ? 0xFFFF0020 : 0x00060004, - CX18_CLOCK_SELECT1); - cx18_write_reg(cx, lowpwr ? 0xFFFF0004 : 0x00060006, - CX18_CLOCK_SELECT2); - - cx18_write_reg(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1); - cx18_write_reg(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2); + if (lowpwr) { + cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1, + 0x00000020, 0xFFFFFFFF); + cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2, + 0x00000004, 0xFFFFFFFF); + } else { + /* This doesn't explicitly set every clock select */ + cx18_write_reg_expect(cx, 0x00060004, CX18_CLOCK_SELECT1, + 0x00000004, 0x00060006); + cx18_write_reg_expect(cx, 0x00060006, CX18_CLOCK_SELECT2, + 0x00000006, 0x00060006); + } - cx18_write_reg(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1); - cx18_write_reg(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2); + cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1, + 0x00000002, 0xFFFFFFFF); + cx18_write_reg_expect(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2, + 0x00000104, 0xFFFFFFFF); + cx18_write_reg_expect(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1, + 0x00009026, 0xFFFFFFFF); + cx18_write_reg_expect(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2, + 0x00003105, 0xFFFFFFFF); } void cx18_init_memory(struct cx18 *cx) { cx18_msleep_timeout(10, 0); - cx18_write_reg(cx, 0x10000, CX18_DDR_SOFT_RESET); + cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET, + 0x00000000, 0x00010001); cx18_msleep_timeout(10, 0); cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); @@ -282,13 +364,15 @@ void cx18_init_memory(struct cx18 *cx) cx18_msleep_timeout(10, 0); - cx18_write_reg(cx, 0x20000, CX18_DDR_SOFT_RESET); + cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET, + 0x00000000, 0x00020002); cx18_msleep_timeout(10, 0); /* use power-down mode when idle */ cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG); - cx18_write_reg(cx, 0x10001, CX18_REG_BUS_TIMEOUT_EN); + cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN, + 0x00000001, 0x00010001); cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7); cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR); @@ -307,51 +391,76 @@ void cx18_init_memory(struct cx18 *cx) int cx18_firmware_init(struct cx18 *cx) { + u32 fw_entry_addr; + int sz, retries; + u32 api_args[MAX_MB_ARGUMENTS]; + /* Allow chip to control CLKRUN */ cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK); - cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ + /* Stop the firmware */ + cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET, + 0x0000000F, 0x000F000F); cx18_msleep_timeout(1, 0); + /* If the CPU is still running */ + if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) { + CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__); + return -EIO; + } + cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); - /* Only if the processor is not running */ - if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) { - int sz = load_apu_fw_direct("v4l-cx23418-apu.fw", - cx->enc_mem, cx); - - cx18_write_enc(cx, 0xE51FF004, 0); - cx18_write_enc(cx, 0xa00000, 4); /* todo: not hardcoded */ - /* Start APU */ - cx18_write_reg(cx, 0x00010000, CX18_PROC_SOFT_RESET); - cx18_msleep_timeout(500, 0); - - sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw", - cx->enc_mem, cx); - - if (sz > 0) { - int retries = 0; - - /* start the CPU */ - cx18_write_reg(cx, 0x00080000, CX18_PROC_SOFT_RESET); - while (retries++ < 50) { /* Loop for max 500mS */ - if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) - & 1) == 0) - break; - cx18_msleep_timeout(10, 0); - } - cx18_msleep_timeout(200, 0); - if (retries == 51) { - CX18_ERR("Could not start the CPU\n"); - return -EIO; - } - } - if (sz <= 0) - return -EIO; + sz = load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx); + if (sz <= 0) + return sz; + + /* The SCB & IPC area *must* be correct before starting the firmwares */ + cx18_init_scb(cx); + + fw_entry_addr = 0; + sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx, + &fw_entry_addr); + if (sz <= 0) + return sz; + + /* Start the CPU. The CPU will take care of the APU for us. */ + cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET, + 0x00000000, 0x00080008); + + /* Wait up to 500 ms for the APU to come out of reset */ + for (retries = 0; + retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1; + retries++) + cx18_msleep_timeout(10, 0); + + cx18_msleep_timeout(200, 0); + + if (retries == 50 && + (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) { + CX18_ERR("Could not start the CPU\n"); + return -EIO; } + + /* + * The CPU had once before set up to receive an interrupt for it's + * outgoing IRQ_CPU_TO_EPU_ACK to us. If it ever does this, we get an + * interrupt when it sends us an ack, but by the time we process it, + * that flag in the SW2 status register has been cleared by the CPU + * firmware. We'll prevent that not so useful condition from happening + * by clearing the CPU's interrupt enables for Ack IRQ's we want to + * process. + */ + cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); + + /* Try a benign command to see if the CPU is alive and well */ + sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0); + if (sz < 0) + return sz; + /* initialize GPIO */ - cx18_write_reg(cx, 0x14001400, 0xC78110); + cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400); return 0; } diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c index 0e56042..1a99329 100644 --- a/drivers/media/video/cx18/cx18-gpio.c +++ b/drivers/media/video/cx18/cx18-gpio.c @@ -4,6 +4,7 @@ * Derived from ivtv-gpio.c * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -47,15 +48,19 @@ static void gpio_write(struct cx18 *cx) { - u32 dir = cx->gpio_dir; - u32 val = cx->gpio_val; - - cx18_write_reg(cx, (dir & 0xffff) << 16, CX18_REG_GPIO_DIR1); - cx18_write_reg(cx, ((dir & 0xffff) << 16) | (val & 0xffff), - CX18_REG_GPIO_OUT1); - cx18_write_reg(cx, dir & 0xffff0000, CX18_REG_GPIO_DIR2); - cx18_write_reg_sync(cx, (dir & 0xffff0000) | ((val & 0xffff0000) >> 16), - CX18_REG_GPIO_OUT2); + u32 dir_lo = cx->gpio_dir & 0xffff; + u32 val_lo = cx->gpio_val & 0xffff; + u32 dir_hi = cx->gpio_dir >> 16; + u32 val_hi = cx->gpio_val >> 16; + + cx18_write_reg_expect(cx, dir_lo << 16, + CX18_REG_GPIO_DIR1, ~dir_lo, dir_lo); + cx18_write_reg_expect(cx, (dir_lo << 16) | val_lo, + CX18_REG_GPIO_OUT1, val_lo, dir_lo); + cx18_write_reg_expect(cx, dir_hi << 16, + CX18_REG_GPIO_DIR2, ~dir_hi, dir_hi); + cx18_write_reg_expect(cx, (dir_hi << 16) | val_hi, + CX18_REG_GPIO_OUT2, val_hi, dir_hi); } void cx18_reset_i2c_slaves_gpio(struct cx18 *cx) diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h index beb7424..39ffccc 100644 --- a/drivers/media/video/cx18/cx18-gpio.h +++ b/drivers/media/video/cx18/cx18-gpio.h @@ -4,6 +4,7 @@ * Derived from ivtv-gpio.h * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index aa09e55..8941f58 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -4,6 +4,7 @@ * Derived from ivtv-i2c.c * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -27,6 +28,7 @@ #include "cx18-gpio.h" #include "cx18-av-core.h" #include "cx18-i2c.h" +#include "cx18-irq.h" #define CX18_REG_I2C_1_WR 0xf15000 #define CX18_REG_I2C_1_RD 0xf15008 @@ -160,9 +162,9 @@ static void cx18_setscl(void *data, int state) u32 r = cx18_read_reg(cx, addr); if (state) - cx18_write_reg_sync(cx, r | SETSCL_BIT, addr); + cx18_write_reg(cx, r | SETSCL_BIT, addr); else - cx18_write_reg_sync(cx, r & ~SETSCL_BIT, addr); + cx18_write_reg(cx, r & ~SETSCL_BIT, addr); } static void cx18_setsda(void *data, int state) @@ -173,9 +175,9 @@ static void cx18_setsda(void *data, int state) u32 r = cx18_read_reg(cx, addr); if (state) - cx18_write_reg_sync(cx, r | SETSDL_BIT, addr); + cx18_write_reg(cx, r | SETSDL_BIT, addr); else - cx18_write_reg_sync(cx, r & ~SETSDL_BIT, addr); + cx18_write_reg(cx, r & ~SETSDL_BIT, addr); } static int cx18_getscl(void *data) @@ -396,30 +398,33 @@ int init_cx18_i2c(struct cx18 *cx) if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) { /* Reset/Unreset I2C hardware block */ /* Clock select 220MHz */ - cx18_write_reg(cx, 0x10000000, 0xc71004); + cx18_write_reg_expect(cx, 0x10000000, 0xc71004, + 0x00000000, 0x10001000); /* Clock Enable */ - cx18_write_reg_sync(cx, 0x10001000, 0xc71024); + cx18_write_reg_expect(cx, 0x10001000, 0xc71024, + 0x00001000, 0x10001000); } /* courtesy of Steven Toth <stoth@hauppauge.com> */ - cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c); + cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0); mdelay(10); - cx18_write_reg_sync(cx, 0x00c000c0, 0xc7001c); + cx18_write_reg_expect(cx, 0x00c000c0, 0xc7001c, 0x000000c0, 0x00c000c0); mdelay(10); - cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c); + cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0); mdelay(10); /* Set to edge-triggered intrs. */ - cx18_write_reg_sync(cx, 0x00c00000, 0xc730c8); + cx18_write_reg(cx, 0x00c00000, 0xc730c8); /* Clear any stale intrs */ - cx18_write_reg_sync(cx, 0x00c00000, 0xc730c4); + cx18_write_reg_expect(cx, HW2_I2C1_INT|HW2_I2C2_INT, HW2_INT_CLR_STATUS, + ~(HW2_I2C1_INT|HW2_I2C2_INT), HW2_I2C1_INT|HW2_I2C2_INT); /* Hw I2C1 Clock Freq ~100kHz */ - cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR); + cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR); cx18_setscl(&cx->i2c_algo_cb_data[0], 1); cx18_setsda(&cx->i2c_algo_cb_data[0], 1); /* Hw I2C2 Clock Freq ~100kHz */ - cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR); + cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR); cx18_setscl(&cx->i2c_algo_cb_data[1], 1); cx18_setsda(&cx->i2c_algo_cb_data[1], 1); diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c index 220fae8..ec5b3d7 100644 --- a/drivers/media/video/cx18/cx18-io.c +++ b/drivers/media/video/cx18/cx18-io.c @@ -24,179 +24,6 @@ #include "cx18-io.h" #include "cx18-irq.h" -void cx18_log_statistics(struct cx18 *cx) -{ - int i; - - if (!(cx18_debug & CX18_DBGFLG_INFO)) - return; - - for (i = 0; i <= CX18_MAX_MMIO_RETRIES; i++) - CX18_DEBUG_INFO("retried_write[%d] = %d\n", i, - atomic_read(&cx->mmio_stats.retried_write[i])); - for (i = 0; i <= CX18_MAX_MMIO_RETRIES; i++) - CX18_DEBUG_INFO("retried_read[%d] = %d\n", i, - atomic_read(&cx->mmio_stats.retried_read[i])); - return; -} - -void cx18_raw_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr) -{ - int i; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { - cx18_raw_writel_noretry(cx, val, addr); - if (val == cx18_raw_readl_noretry(cx, addr)) - break; - } - cx18_log_write_retries(cx, i, addr); -} - -u32 cx18_raw_readl_retry(struct cx18 *cx, const void __iomem *addr) -{ - int i; - u32 val; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { - val = cx18_raw_readl_noretry(cx, addr); - if (val != 0xffffffff) /* PCI bus read error */ - break; - } - cx18_log_read_retries(cx, i, addr); - return val; -} - -u16 cx18_raw_readw_retry(struct cx18 *cx, const void __iomem *addr) -{ - int i; - u16 val; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { - val = cx18_raw_readw_noretry(cx, addr); - if (val != 0xffff) /* PCI bus read error */ - break; - } - cx18_log_read_retries(cx, i, addr); - return val; -} - -void cx18_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr) -{ - int i; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { - cx18_writel_noretry(cx, val, addr); - if (val == cx18_readl_noretry(cx, addr)) - break; - } - cx18_log_write_retries(cx, i, addr); -} - -void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr, - u32 eval, u32 mask) -{ - int i; - eval &= mask; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { - cx18_writel_noretry(cx, val, addr); - if (eval == (cx18_readl_noretry(cx, addr) & mask)) - break; - } - cx18_log_write_retries(cx, i, addr); -} - -void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr) -{ - int i; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { - cx18_writew_noretry(cx, val, addr); - if (val == cx18_readw_noretry(cx, addr)) - break; - } - cx18_log_write_retries(cx, i, addr); -} - -void cx18_writeb_retry(struct cx18 *cx, u8 val, void __iomem *addr) -{ - int i; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { - cx18_writeb_noretry(cx, val, addr); - if (val == cx18_readb_noretry(cx, addr)) - break; - } - cx18_log_write_retries(cx, i, addr); -} - -u32 cx18_readl_retry(struct cx18 *cx, const void __iomem *addr) -{ - int i; - u32 val; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { - val = cx18_readl_noretry(cx, addr); - if (val != 0xffffffff) /* PCI bus read error */ - break; - } - cx18_log_read_retries(cx, i, addr); - return val; -} - -u16 cx18_readw_retry(struct cx18 *cx, const void __iomem *addr) -{ - int i; - u16 val; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { - val = cx18_readw_noretry(cx, addr); - if (val != 0xffff) /* PCI bus read error */ - break; - } - cx18_log_read_retries(cx, i, addr); - return val; -} - -u8 cx18_readb_retry(struct cx18 *cx, const void __iomem *addr) -{ - int i; - u8 val; - for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { - val = cx18_readb_noretry(cx, addr); - if (val != 0xff) /* PCI bus read error */ - break; - } - cx18_log_read_retries(cx, i, addr); - return val; -} - -void cx18_memcpy_fromio(struct cx18 *cx, void *to, - const void __iomem *from, unsigned int len) -{ - const u8 __iomem *src = from; - u8 *dst = to; - - /* Align reads on the CX23418's addresses */ - if ((len > 0) && ((unsigned long) src & 1)) { - *dst = cx18_readb(cx, src); - len--; - dst++; - src++; - } - if ((len > 1) && ((unsigned long) src & 2)) { - *((u16 *)dst) = cx18_raw_readw(cx, src); - len -= 2; - dst += 2; - src += 2; - } - while (len > 3) { - *((u32 *)dst) = cx18_raw_readl(cx, src); - len -= 4; - dst += 4; - src += 4; - } - if (len > 1) { - *((u16 *)dst) = cx18_raw_readw(cx, src); - len -= 2; - dst += 2; - src += 2; - } - if (len > 0) - *dst = cx18_readb(cx, src); -} - void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count) { u8 __iomem *dst = addr; @@ -230,32 +57,35 @@ void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count) void cx18_sw1_irq_enable(struct cx18 *cx, u32 val) { - u32 r; cx18_write_reg_expect(cx, val, SW1_INT_STATUS, ~val, val); - r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI); - cx18_write_reg(cx, r | val, SW1_INT_ENABLE_PCI); + cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | val; + cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI); } void cx18_sw1_irq_disable(struct cx18 *cx, u32 val) { - u32 r; - r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI); - cx18_write_reg(cx, r & ~val, SW1_INT_ENABLE_PCI); + cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) & ~val; + cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI); } void cx18_sw2_irq_enable(struct cx18 *cx, u32 val) { - u32 r; cx18_write_reg_expect(cx, val, SW2_INT_STATUS, ~val, val); - r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI); - cx18_write_reg(cx, r | val, SW2_INT_ENABLE_PCI); + cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | val; + cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI); } void cx18_sw2_irq_disable(struct cx18 *cx, u32 val) { + cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) & ~val; + cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI); +} + +void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val) +{ u32 r; - r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI); - cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_PCI); + r = cx18_read_reg(cx, SW2_INT_ENABLE_CPU); + cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_CPU); } void cx18_setup_page(struct cx18 *cx, u32 addr) diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h index 4252444..2635b3a 100644 --- a/drivers/media/video/cx18/cx18-io.h +++ b/drivers/media/video/cx18/cx18-io.h @@ -25,232 +25,125 @@ #include "cx18-driver.h" -static inline void cx18_io_delay(struct cx18 *cx) -{ - if (cx->options.mmio_ndelay) - ndelay(cx->options.mmio_ndelay); -} - /* * Readback and retry of MMIO access for reliability: * The concept was suggested by Steve Toth <stoth@linuxtv.org>. * The implmentation is the fault of Andy Walls <awalls@radix.net>. + * + * *write* functions are implied to retry the mmio unless suffixed with _noretry + * *read* functions never retry the mmio (it never helps to do so) */ -/* Statistics gathering */ -static inline -void cx18_log_write_retries(struct cx18 *cx, int i, const void __iomem *addr) -{ - if (i > CX18_MAX_MMIO_RETRIES) - i = CX18_MAX_MMIO_RETRIES; - atomic_inc(&cx->mmio_stats.retried_write[i]); - return; -} - -static inline -void cx18_log_read_retries(struct cx18 *cx, int i, const void __iomem *addr) +/* Non byteswapping memory mapped IO */ +static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr) { - if (i > CX18_MAX_MMIO_RETRIES) - i = CX18_MAX_MMIO_RETRIES; - atomic_inc(&cx->mmio_stats.retried_read[i]); - return; + return __raw_readl(addr); } -void cx18_log_statistics(struct cx18 *cx); - -/* Non byteswapping memory mapped IO */ static inline void cx18_raw_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr) { __raw_writel(val, addr); - cx18_io_delay(cx); } -void cx18_raw_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr); - static inline void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr) { - if (cx18_retry_mmio) - cx18_raw_writel_retry(cx, val, addr); - else + int i; + for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { cx18_raw_writel_noretry(cx, val, addr); + if (val == cx18_raw_readl(cx, addr)) + break; + } } - -static inline -u32 cx18_raw_readl_noretry(struct cx18 *cx, const void __iomem *addr) -{ - u32 ret = __raw_readl(addr); - cx18_io_delay(cx); - return ret; -} - -u32 cx18_raw_readl_retry(struct cx18 *cx, const void __iomem *addr); - -static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr) +/* Normal memory mapped IO */ +static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr) { - if (cx18_retry_mmio) - return cx18_raw_readl_retry(cx, addr); - - return cx18_raw_readl_noretry(cx, addr); + return readl(addr); } - static inline -u16 cx18_raw_readw_noretry(struct cx18 *cx, const void __iomem *addr) +void cx18_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr) { - u16 ret = __raw_readw(addr); - cx18_io_delay(cx); - return ret; + writel(val, addr); } -u16 cx18_raw_readw_retry(struct cx18 *cx, const void __iomem *addr); - -static inline u16 cx18_raw_readw(struct cx18 *cx, const void __iomem *addr) +static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr) { - if (cx18_retry_mmio) - return cx18_raw_readw_retry(cx, addr); - - return cx18_raw_readw_noretry(cx, addr); + int i; + for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { + cx18_writel_noretry(cx, val, addr); + if (val == cx18_readl(cx, addr)) + break; + } } - -/* Normal memory mapped IO */ static inline -void cx18_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr) +void cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr, + u32 eval, u32 mask) { - writel(val, addr); - cx18_io_delay(cx); + int i; + u32 r; + eval &= mask; + for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { + cx18_writel_noretry(cx, val, addr); + r = cx18_readl(cx, addr); + if (r == 0xffffffff && eval != 0xffffffff) + continue; + if (eval == (r & mask)) + break; + } } -void cx18_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr); - -static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr) +static inline u16 cx18_readw(struct cx18 *cx, const void __iomem *addr) { - if (cx18_retry_mmio) - cx18_writel_retry(cx, val, addr); - else - cx18_writel_noretry(cx, val, addr); + return readw(addr); } -void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr, - u32 eval, u32 mask); - static inline void cx18_writew_noretry(struct cx18 *cx, u16 val, void __iomem *addr) { writew(val, addr); - cx18_io_delay(cx); } -void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr); - static inline void cx18_writew(struct cx18 *cx, u16 val, void __iomem *addr) { - if (cx18_retry_mmio) - cx18_writew_retry(cx, val, addr); - else + int i; + for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { cx18_writew_noretry(cx, val, addr); + if (val == cx18_readw(cx, addr)) + break; + } } +static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr) +{ + return readb(addr); +} static inline void cx18_writeb_noretry(struct cx18 *cx, u8 val, void __iomem *addr) { writeb(val, addr); - cx18_io_delay(cx); } -void cx18_writeb_retry(struct cx18 *cx, u8 val, void __iomem *addr); - static inline void cx18_writeb(struct cx18 *cx, u8 val, void __iomem *addr) { - if (cx18_retry_mmio) - cx18_writeb_retry(cx, val, addr); - else + int i; + for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { cx18_writeb_noretry(cx, val, addr); -} - - -static inline u32 cx18_readl_noretry(struct cx18 *cx, const void __iomem *addr) -{ - u32 ret = readl(addr); - cx18_io_delay(cx); - return ret; -} - -u32 cx18_readl_retry(struct cx18 *cx, const void __iomem *addr); - -static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr) -{ - if (cx18_retry_mmio) - return cx18_readl_retry(cx, addr); - - return cx18_readl_noretry(cx, addr); -} - - -static inline u16 cx18_readw_noretry(struct cx18 *cx, const void __iomem *addr) -{ - u16 ret = readw(addr); - cx18_io_delay(cx); - return ret; -} - -u16 cx18_readw_retry(struct cx18 *cx, const void __iomem *addr); - -static inline u16 cx18_readw(struct cx18 *cx, const void __iomem *addr) -{ - if (cx18_retry_mmio) - return cx18_readw_retry(cx, addr); - - return cx18_readw_noretry(cx, addr); -} - - -static inline u8 cx18_readb_noretry(struct cx18 *cx, const void __iomem *addr) -{ - u8 ret = readb(addr); - cx18_io_delay(cx); - return ret; -} - -u8 cx18_readb_retry(struct cx18 *cx, const void __iomem *addr); - -static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr) -{ - if (cx18_retry_mmio) - return cx18_readb_retry(cx, addr); - - return cx18_readb_noretry(cx, addr); -} - - -static inline -u32 cx18_write_sync_noretry(struct cx18 *cx, u32 val, void __iomem *addr) -{ - cx18_writel_noretry(cx, val, addr); - return cx18_readl_noretry(cx, addr); + if (val == cx18_readb(cx, addr)) + break; + } } static inline -u32 cx18_write_sync_retry(struct cx18 *cx, u32 val, void __iomem *addr) -{ - cx18_writel_retry(cx, val, addr); - return cx18_readl_retry(cx, addr); -} - -static inline u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr) +void cx18_memcpy_fromio(struct cx18 *cx, void *to, + const void __iomem *from, unsigned int len) { - if (cx18_retry_mmio) - return cx18_write_sync_retry(cx, val, addr); - - return cx18_write_sync_noretry(cx, val, addr); + memcpy_fromio(to, from, len); } - -void cx18_memcpy_fromio(struct cx18 *cx, void *to, - const void __iomem *from, unsigned int len); void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count); @@ -260,136 +153,39 @@ static inline void cx18_write_reg_noretry(struct cx18 *cx, u32 val, u32 reg) cx18_writel_noretry(cx, val, cx->reg_mem + reg); } -static inline void cx18_write_reg_retry(struct cx18 *cx, u32 val, u32 reg) -{ - cx18_writel_retry(cx, val, cx->reg_mem + reg); -} - static inline void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg) { - if (cx18_retry_mmio) - cx18_write_reg_retry(cx, val, reg); - else - cx18_write_reg_noretry(cx, val, reg); -} - -static inline void _cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg, - u32 eval, u32 mask) -{ - _cx18_writel_expect(cx, val, cx->reg_mem + reg, eval, mask); + cx18_writel(cx, val, cx->reg_mem + reg); } static inline void cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg, u32 eval, u32 mask) { - if (cx18_retry_mmio) - _cx18_write_reg_expect(cx, val, reg, eval, mask); - else - cx18_write_reg_noretry(cx, val, reg); -} - - -static inline u32 cx18_read_reg_noretry(struct cx18 *cx, u32 reg) -{ - return cx18_readl_noretry(cx, cx->reg_mem + reg); -} - -static inline u32 cx18_read_reg_retry(struct cx18 *cx, u32 reg) -{ - return cx18_readl_retry(cx, cx->reg_mem + reg); + cx18_writel_expect(cx, val, cx->reg_mem + reg, eval, mask); } static inline u32 cx18_read_reg(struct cx18 *cx, u32 reg) { - if (cx18_retry_mmio) - return cx18_read_reg_retry(cx, reg); - - return cx18_read_reg_noretry(cx, reg); -} - - -static inline u32 cx18_write_reg_sync_noretry(struct cx18 *cx, u32 val, u32 reg) -{ - return cx18_write_sync_noretry(cx, val, cx->reg_mem + reg); -} - -static inline u32 cx18_write_reg_sync_retry(struct cx18 *cx, u32 val, u32 reg) -{ - return cx18_write_sync_retry(cx, val, cx->reg_mem + reg); -} - -static inline u32 cx18_write_reg_sync(struct cx18 *cx, u32 val, u32 reg) -{ - if (cx18_retry_mmio) - return cx18_write_reg_sync_retry(cx, val, reg); - - return cx18_write_reg_sync_noretry(cx, val, reg); + return cx18_readl(cx, cx->reg_mem + reg); } /* Access "encoder memory" region of CX23418 memory mapped I/O */ -static inline void cx18_write_enc_noretry(struct cx18 *cx, u32 val, u32 addr) -{ - cx18_writel_noretry(cx, val, cx->enc_mem + addr); -} - -static inline void cx18_write_enc_retry(struct cx18 *cx, u32 val, u32 addr) -{ - cx18_writel_retry(cx, val, cx->enc_mem + addr); -} - static inline void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr) { - if (cx18_retry_mmio) - cx18_write_enc_retry(cx, val, addr); - else - cx18_write_enc_noretry(cx, val, addr); -} - - -static inline u32 cx18_read_enc_noretry(struct cx18 *cx, u32 addr) -{ - return cx18_readl_noretry(cx, cx->enc_mem + addr); -} - -static inline u32 cx18_read_enc_retry(struct cx18 *cx, u32 addr) -{ - return cx18_readl_retry(cx, cx->enc_mem + addr); + cx18_writel(cx, val, cx->enc_mem + addr); } static inline u32 cx18_read_enc(struct cx18 *cx, u32 addr) { - if (cx18_retry_mmio) - return cx18_read_enc_retry(cx, addr); - - return cx18_read_enc_noretry(cx, addr); -} - -static inline -u32 cx18_write_enc_sync_noretry(struct cx18 *cx, u32 val, u32 addr) -{ - return cx18_write_sync_noretry(cx, val, cx->enc_mem + addr); -} - -static inline -u32 cx18_write_enc_sync_retry(struct cx18 *cx, u32 val, u32 addr) -{ - return cx18_write_sync_retry(cx, val, cx->enc_mem + addr); -} - -static inline -u32 cx18_write_enc_sync(struct cx18 *cx, u32 val, u32 addr) -{ - if (cx18_retry_mmio) - return cx18_write_enc_sync_retry(cx, val, addr); - - return cx18_write_enc_sync_noretry(cx, val, addr); + return cx18_readl(cx, cx->enc_mem + addr); } void cx18_sw1_irq_enable(struct cx18 *cx, u32 val); void cx18_sw1_irq_disable(struct cx18 *cx, u32 val); void cx18_sw2_irq_enable(struct cx18 *cx, u32 val); void cx18_sw2_irq_disable(struct cx18 *cx, u32 val); +void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val); void cx18_setup_page(struct cx18 *cx, u32 addr); #endif /* CX18_IO_H */ diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index f0ca50f..e608748 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -4,6 +4,7 @@ * Derived from ivtv-ioctl.c * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -237,13 +238,12 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, if (ret) return ret; - if (id->type == CX18_ENC_STREAM_TYPE_VBI && - cx->vbi.sliced_in->service_set && - atomic_read(&cx->ana_capturing) > 0) + if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0) return -EBUSY; cx->vbi.sliced_in->service_set = 0; - cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in); + cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; + cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); return cx18_g_fmt_vbi_cap(file, fh, fmt); } @@ -745,14 +745,12 @@ static int cx18_log_status(struct file *file, void *fh) continue; CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, - (s->buffers - atomic_read(&s->q_free.buffers)) - * 100 / s->buffers, + atomic_read(&s->q_full.buffers) * 100 / s->buffers, (s->buffers * s->buf_size) / 1024, s->buffers); } CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", (long long)cx->mpg_data_received, (long long)cx->vbi_data_inserted); - cx18_log_statistics(cx); CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num); return 0; } diff --git a/drivers/media/video/cx18/cx18-ioctl.h b/drivers/media/video/cx18/cx18-ioctl.h index 2222f67..08fe24e 100644 --- a/drivers/media/video/cx18/cx18-ioctl.h +++ b/drivers/media/video/cx18/cx18-ioctl.h @@ -4,6 +4,7 @@ * Derived from ivtv-ioctl.h * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c index 5fbfbd0..af2f504 100644 --- a/drivers/media/video/cx18/cx18-irq.c +++ b/drivers/media/video/cx18/cx18-irq.c @@ -2,6 +2,7 @@ * cx18 interrupt handling * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -21,132 +22,9 @@ #include "cx18-driver.h" #include "cx18-io.h" -#include "cx18-firmware.h" -#include "cx18-fileops.h" -#include "cx18-queue.h" #include "cx18-irq.h" -#include "cx18-ioctl.h" #include "cx18-mailbox.h" -#include "cx18-vbi.h" #include "cx18-scb.h" -#include "cx18-dvb.h" - -void cx18_work_handler(struct work_struct *work) -{ - struct cx18 *cx = container_of(work, struct cx18, work); - if (test_and_clear_bit(CX18_F_I_WORK_INITED, &cx->i_flags)) { - struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; - /* This thread must use the FIFO scheduler as it - * is realtime sensitive. */ - sched_setscheduler(current, SCHED_FIFO, ¶m); - } - if (test_and_clear_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags)) - cx18_dvb_work_handler(cx); -} - -static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) -{ - u32 handle = mb->args[0]; - struct cx18_stream *s = NULL; - struct cx18_buffer *buf; - u32 off; - int i; - int id; - - for (i = 0; i < CX18_MAX_STREAMS; i++) { - s = &cx->streams[i]; - if ((handle == s->handle) && (s->dvb.enabled)) - break; - if (s->v4l2dev && handle == s->handle) - break; - } - if (i == CX18_MAX_STREAMS) { - CX18_WARN("Got DMA done notification for unknown/inactive" - " handle %d\n", handle); - mb->error = CXERR_NOT_OPEN; - mb->cmd = 0; - cx18_mb_ack(cx, mb); - return; - } - - off = mb->args[1]; - if (mb->args[2] != 1) - CX18_WARN("Ack struct = %d for %s\n", - mb->args[2], s->name); - id = cx18_read_enc(cx, off); - buf = cx18_queue_get_buf_irq(s, id, cx18_read_enc(cx, off + 4)); - CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); - if (buf) { - cx18_buf_sync_for_cpu(s, buf); - if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { - CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n", - buf->bytesused); - - set_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags); - set_bit(CX18_F_I_HAVE_WORK, &cx->i_flags); - } else - set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); - } else { - CX18_WARN("Could not find buf %d for stream %s\n", - cx18_read_enc(cx, off), s->name); - } - mb->error = 0; - mb->cmd = 0; - cx18_mb_ack(cx, mb); - wake_up(&cx->dma_waitq); - if (s->id != -1) - wake_up(&s->waitq); -} - -static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb) -{ - char str[256] = { 0 }; - char *p; - - if (mb->args[1]) { - cx18_setup_page(cx, mb->args[1]); - cx18_memcpy_fromio(cx, str, cx->enc_mem + mb->args[1], 252); - str[252] = 0; - } - cx18_mb_ack(cx, mb); - CX18_DEBUG_INFO("%x %s\n", mb->args[0], str); - p = strchr(str, '.'); - if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str) - CX18_INFO("FW version: %s\n", p - 1); -} - -static void epu_cmd(struct cx18 *cx, u32 sw1) -{ - struct cx18_mailbox mb; - - if (sw1 & IRQ_CPU_TO_EPU) { - cx18_memcpy_fromio(cx, &mb, &cx->scb->cpu2epu_mb, sizeof(mb)); - mb.error = 0; - - switch (mb.cmd) { - case CX18_EPU_DMA_DONE: - epu_dma_done(cx, &mb); - break; - case CX18_EPU_DEBUG: - epu_debug(cx, &mb); - break; - default: - CX18_WARN("Unknown CPU_TO_EPU mailbox command %#08x\n", - mb.cmd); - break; - } - } - - if (sw1 & IRQ_APU_TO_EPU) { - cx18_memcpy_fromio(cx, &mb, &cx->scb->apu2epu_mb, sizeof(mb)); - CX18_WARN("Unknown APU_TO_EPU mailbox command %#08x\n", mb.cmd); - } - - if (sw1 & IRQ_HPU_TO_EPU) { - cx18_memcpy_fromio(cx, &mb, &cx->scb->hpu2epu_mb, sizeof(mb)); - CX18_WARN("Unknown HPU_TO_EPU mailbox command %#08x\n", mb.cmd); - } -} static void xpu_ack(struct cx18 *cx, u32 sw2) { @@ -154,23 +32,24 @@ static void xpu_ack(struct cx18 *cx, u32 sw2) wake_up(&cx->mb_cpu_waitq); if (sw2 & IRQ_APU_TO_EPU_ACK) wake_up(&cx->mb_apu_waitq); - if (sw2 & IRQ_HPU_TO_EPU_ACK) - wake_up(&cx->mb_hpu_waitq); +} + +static void epu_cmd(struct cx18 *cx, u32 sw1) +{ + if (sw1 & IRQ_CPU_TO_EPU) + cx18_api_epu_cmd_irq(cx, CPU); + if (sw1 & IRQ_APU_TO_EPU) + cx18_api_epu_cmd_irq(cx, APU); } irqreturn_t cx18_irq_handler(int irq, void *dev_id) { struct cx18 *cx = (struct cx18 *)dev_id; - u32 sw1, sw1_mask; - u32 sw2, sw2_mask; - u32 hw2, hw2_mask; + u32 sw1, sw2, hw2; - sw1_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI); - sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & sw1_mask; - sw2_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI); - sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & sw2_mask; - hw2_mask = cx18_read_reg(cx, HW2_INT_MASK5_PCI); - hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & hw2_mask; + sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & cx->sw1_irq_mask; + sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & cx->sw2_irq_mask; + hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & cx->hw2_irq_mask; if (sw1) cx18_write_reg_expect(cx, sw1, SW1_INT_STATUS, ~sw1, sw1); @@ -180,7 +59,15 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2); if (sw1 || sw2 || hw2) - CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); + CX18_DEBUG_HI_IRQ("received interrupts " + "SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); + + /* + * SW1 responses have to happen first. The sending XPU times out the + * incoming mailboxes on us rather rapidly. + */ + if (sw1) + epu_cmd(cx, sw1); /* To do: interrupt-based I2C handling if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) { @@ -190,11 +77,5 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) if (sw2) xpu_ack(cx, sw2); - if (sw1) - epu_cmd(cx, sw1); - - if (test_and_clear_bit(CX18_F_I_HAVE_WORK, &cx->i_flags)) - queue_work(cx->work_queue, &cx->work); - return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE; } diff --git a/drivers/media/video/cx18/cx18-irq.h b/drivers/media/video/cx18/cx18-irq.h index 6173ca3..91f0b52 100644 --- a/drivers/media/video/cx18/cx18-irq.h +++ b/drivers/media/video/cx18/cx18-irq.h @@ -2,6 +2,7 @@ * cx18 interrupt handling * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -28,8 +29,7 @@ #define SW1_INT_ENABLE_PCI 0xc7311c #define SW2_INT_SET 0xc73140 #define SW2_INT_STATUS 0xc73144 +#define SW2_INT_ENABLE_CPU 0xc73158 #define SW2_INT_ENABLE_PCI 0xc7315c irqreturn_t cx18_irq_handler(int irq, void *dev_id); - -void cx18_work_handler(struct work_struct *work); diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index acff7df..de5e723 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -2,6 +2,7 @@ * cx18 mailbox functions * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -26,15 +27,14 @@ #include "cx18-scb.h" #include "cx18-irq.h" #include "cx18-mailbox.h" +#include "cx18-queue.h" +#include "cx18-streams.h" + +static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" }; #define API_FAST (1 << 2) /* Short timeout */ #define API_SLOW (1 << 3) /* Additional 300ms timeout */ -#define APU 0 -#define CPU 1 -#define EPU 2 -#define HPU 3 - struct cx18_api_info { u32 cmd; u8 flags; /* Flags, see above */ @@ -82,8 +82,9 @@ static const struct cx18_api_info api_info[] = { API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0), API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), - API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST), API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW), + API_ENTRY(APU, CX18_APU_RESETAI, 0), + API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0), API_ENTRY(0, 0, 0), }; @@ -97,70 +98,175 @@ static const struct cx18_api_info *find_api_info(u32 cmd) return NULL; } -static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu, - u32 *state, u32 *irq, u32 *req) +static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) { - struct cx18_mailbox __iomem *mb = NULL; - int wait_count = 0; - u32 ack; - - switch (rpu) { - case APU: - mb = &cx->scb->epu2apu_mb; - *state = cx18_readl(cx, &cx->scb->apu_state); - *irq = cx18_readl(cx, &cx->scb->epu2apu_irq); - break; + char argstr[MAX_MB_ARGUMENTS*11+1]; + char *p; + int i; - case CPU: - mb = &cx->scb->epu2cpu_mb; - *state = cx18_readl(cx, &cx->scb->cpu_state); - *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq); - break; + if (!(cx18_debug & CX18_DBGFLG_API)) + return; - case HPU: - mb = &cx->scb->epu2hpu_mb; - *state = cx18_readl(cx, &cx->scb->hpu_state); - *irq = cx18_readl(cx, &cx->scb->epu2hpu_irq); - break; + for (i = 0, p = argstr; i < MAX_MB_ARGUMENTS; i++, p += 11) { + /* kernel snprintf() appends '\0' always */ + snprintf(p, 12, " %#010x", mb->args[i]); } + CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s" + "\n", name, mb->request, mb->ack, mb->cmd, mb->error, argstr); +} - if (mb == NULL) - return mb; - do { - *req = cx18_readl(cx, &mb->request); - ack = cx18_readl(cx, &mb->ack); - wait_count++; - } while (*req != ack && wait_count < 600); +/* + * Functions that run in a work_queue work handling context + */ - if (*req == ack) { - (*req)++; - if (*req == 0 || *req == 0xffffffff) - *req = 1; - return mb; +static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) +{ + u32 handle, mdl_ack_count, id; + struct cx18_mailbox *mb; + struct cx18_mdl_ack *mdl_ack; + struct cx18_stream *s; + struct cx18_buffer *buf; + int i; + + mb = &order->mb; + handle = mb->args[0]; + s = cx18_handle_to_stream(cx, handle); + + if (s == NULL) { + CX18_WARN("Got DMA done notification for unknown/inactive" + " handle %d, %s mailbox seq no %d\n", handle, + (order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) ? + "stale" : "good", mb->request); + return; } - return NULL; + + mdl_ack_count = mb->args[2]; + mdl_ack = order->mdl_ack; + for (i = 0; i < mdl_ack_count; i++, mdl_ack++) { + id = mdl_ack->id; + /* + * Simple integrity check for processing a stale (and possibly + * inconsistent mailbox): make sure the buffer id is in the + * valid range for the stream. + * + * We go through the trouble of dealing with stale mailboxes + * because most of the time, the mailbox data is still valid and + * unchanged (and in practice the firmware ping-pongs the + * two mdl_ack buffers so mdl_acks are not stale). + * + * There are occasions when we get a half changed mailbox, + * which this check catches for a handle & id mismatch. If the + * handle and id do correspond, the worst case is that we + * completely lost the old buffer, but pick up the new buffer + * early (but the new mdl_ack is guaranteed to be good in this + * case as the firmware wouldn't point us to a new mdl_ack until + * it's filled in). + * + * cx18_queue_get buf() will detect the lost buffers + * and send them back to q_free for fw rotation eventually. + */ + if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) && + !(id >= s->mdl_offset && + id < (s->mdl_offset + s->buffers))) { + CX18_WARN("Fell behind! Ignoring stale mailbox with " + " inconsistent data. Lost buffer for mailbox " + "seq no %d\n", mb->request); + break; + } + buf = cx18_queue_get_buf(s, id, mdl_ack->data_used); + + CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); + if (buf == NULL) { + CX18_WARN("Could not find buf %d for stream %s\n", + id, s->name); + /* Put as many buffers as possible back into fw use */ + cx18_stream_load_fw_queue(s); + continue; + } + + if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { + CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n", + buf->bytesused); + dvb_dmx_swfilter(&s->dvb.demux, buf->buf, + buf->bytesused); + } + /* Put as many buffers as possible back into fw use */ + cx18_stream_load_fw_queue(s); + /* Put back TS buffer, since it was removed from all queues */ + if (s->type == CX18_ENC_STREAM_TYPE_TS) + cx18_stream_put_buf_fw(s, buf); + } + wake_up(&cx->dma_waitq); + if (s->id != -1) + wake_up(&s->waitq); } -long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) +static void epu_debug(struct cx18 *cx, struct cx18_epu_work_order *order) { - const struct cx18_api_info *info = find_api_info(mb->cmd); - struct cx18_mailbox __iomem *ack_mb; - u32 ack_irq; - u8 rpu = CPU; + char *p; + char *str = order->str; - if (info == NULL && mb->cmd) { - CX18_WARN("Cannot ack unknown command %x\n", mb->cmd); - return -EINVAL; - } - if (info) - rpu = info->rpu; + CX18_DEBUG_INFO("%x %s\n", order->mb.args[0], str); + p = strchr(str, '.'); + if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str) + CX18_INFO("FW version: %s\n", p - 1); +} - switch (rpu) { - case HPU: - ack_irq = IRQ_EPU_TO_HPU_ACK; - ack_mb = &cx->scb->hpu2epu_mb; +static void epu_cmd(struct cx18 *cx, struct cx18_epu_work_order *order) +{ + switch (order->rpu) { + case CPU: + { + switch (order->mb.cmd) { + case CX18_EPU_DMA_DONE: + epu_dma_done(cx, order); + break; + case CX18_EPU_DEBUG: + epu_debug(cx, order); + break; + default: + CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n", + order->mb.cmd); + break; + } break; + } + case APU: + CX18_WARN("Unknown APU to EPU mailbox command %#0x\n", + order->mb.cmd); + break; + default: + break; + } +} + +static +void free_epu_work_order(struct cx18 *cx, struct cx18_epu_work_order *order) +{ + atomic_set(&order->pending, 0); +} + +void cx18_epu_work_handler(struct work_struct *work) +{ + struct cx18_epu_work_order *order = + container_of(work, struct cx18_epu_work_order, work); + struct cx18 *cx = order->cx; + epu_cmd(cx, order); + free_epu_work_order(cx, order); +} + + +/* + * Functions that run in an interrupt handling context + */ + +static void mb_ack_irq(struct cx18 *cx, struct cx18_epu_work_order *order) +{ + struct cx18_mailbox __iomem *ack_mb; + u32 ack_irq, req; + + switch (order->rpu) { case APU: ack_irq = IRQ_EPU_TO_APU_ACK; ack_mb = &cx->scb->apu2epu_mb; @@ -170,26 +276,197 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) ack_mb = &cx->scb->cpu2epu_mb; break; default: - CX18_WARN("Unknown RPU for command %x\n", mb->cmd); - return -EINVAL; + CX18_WARN("Unhandled RPU (%d) for command %x ack\n", + order->rpu, order->mb.cmd); + return; } - cx18_setup_page(cx, SCB_OFFSET); - cx18_write_sync(cx, mb->request, &ack_mb->ack); + req = order->mb.request; + /* Don't ack if the RPU has gotten impatient and timed us out */ + if (req != cx18_readl(cx, &ack_mb->request) || + req == cx18_readl(cx, &ack_mb->ack)) { + CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our " + "incoming %s to EPU mailbox (sequence no. %u) " + "while processing\n", + rpu_str[order->rpu], rpu_str[order->rpu], req); + order->flags |= CX18_F_EWO_MB_STALE_WHILE_PROC; + return; + } + cx18_writel(cx, req, &ack_mb->ack); cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq); - return 0; + return; +} + +static int epu_dma_done_irq(struct cx18 *cx, struct cx18_epu_work_order *order) +{ + u32 handle, mdl_ack_offset, mdl_ack_count; + struct cx18_mailbox *mb; + + mb = &order->mb; + handle = mb->args[0]; + mdl_ack_offset = mb->args[1]; + mdl_ack_count = mb->args[2]; + + if (handle == CX18_INVALID_TASK_HANDLE || + mdl_ack_count == 0 || mdl_ack_count > CX18_MAX_MDL_ACKS) { + if ((order->flags & CX18_F_EWO_MB_STALE) == 0) + mb_ack_irq(cx, order); + return -1; + } + + cx18_memcpy_fromio(cx, order->mdl_ack, cx->enc_mem + mdl_ack_offset, + sizeof(struct cx18_mdl_ack) * mdl_ack_count); + + if ((order->flags & CX18_F_EWO_MB_STALE) == 0) + mb_ack_irq(cx, order); + return 1; +} + +static +int epu_debug_irq(struct cx18 *cx, struct cx18_epu_work_order *order) +{ + u32 str_offset; + char *str = order->str; + + str[0] = '\0'; + str_offset = order->mb.args[1]; + if (str_offset) { + cx18_setup_page(cx, str_offset); + cx18_memcpy_fromio(cx, str, cx->enc_mem + str_offset, 252); + str[252] = '\0'; + cx18_setup_page(cx, SCB_OFFSET); + } + + if ((order->flags & CX18_F_EWO_MB_STALE) == 0) + mb_ack_irq(cx, order); + + return str_offset ? 1 : 0; } +static inline +int epu_cmd_irq(struct cx18 *cx, struct cx18_epu_work_order *order) +{ + int ret = -1; + + switch (order->rpu) { + case CPU: + { + switch (order->mb.cmd) { + case CX18_EPU_DMA_DONE: + ret = epu_dma_done_irq(cx, order); + break; + case CX18_EPU_DEBUG: + ret = epu_debug_irq(cx, order); + break; + default: + CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n", + order->mb.cmd); + break; + } + break; + } + case APU: + CX18_WARN("Unknown APU to EPU mailbox command %#0x\n", + order->mb.cmd); + break; + default: + break; + } + return ret; +} + +static inline +struct cx18_epu_work_order *alloc_epu_work_order_irq(struct cx18 *cx) +{ + int i; + struct cx18_epu_work_order *order = NULL; + + for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) { + /* + * We only need "pending" atomic to inspect its contents, + * and need not do a check and set because: + * 1. Any work handler thread only clears "pending" and only + * on one, particular work order at a time, per handler thread. + * 2. "pending" is only set here, and we're serialized because + * we're called in an IRQ handler context. + */ + if (atomic_read(&cx->epu_work_order[i].pending) == 0) { + order = &cx->epu_work_order[i]; + atomic_set(&order->pending, 1); + break; + } + } + return order; +} + +void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) +{ + struct cx18_mailbox __iomem *mb; + struct cx18_mailbox *order_mb; + struct cx18_epu_work_order *order; + int submit; + + switch (rpu) { + case CPU: + mb = &cx->scb->cpu2epu_mb; + break; + case APU: + mb = &cx->scb->apu2epu_mb; + break; + default: + return; + } + + order = alloc_epu_work_order_irq(cx); + if (order == NULL) { + CX18_WARN("Unable to find blank work order form to schedule " + "incoming mailbox command processing\n"); + return; + } + + order->flags = 0; + order->rpu = rpu; + order_mb = &order->mb; + + /* mb->cmd and mb->args[0] through mb->args[2] */ + cx18_memcpy_fromio(cx, &order_mb->cmd, &mb->cmd, 4 * sizeof(u32)); + /* mb->request and mb->ack. N.B. we want to read mb->ack last */ + cx18_memcpy_fromio(cx, &order_mb->request, &mb->request, + 2 * sizeof(u32)); + + if (order_mb->request == order_mb->ack) { + CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our " + "incoming %s to EPU mailbox (sequence no. %u)" + "\n", + rpu_str[rpu], rpu_str[rpu], order_mb->request); + dump_mb(cx, order_mb, "incoming"); + order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT; + } + + /* + * Individual EPU command processing is responsible for ack-ing + * a non-stale mailbox as soon as possible + */ + submit = epu_cmd_irq(cx, order); + if (submit > 0) { + queue_work(cx->work_queue, &order->work); + } +} + + +/* + * Functions called from a non-interrupt, non work_queue context + */ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) { const struct cx18_api_info *info = find_api_info(cmd); - u32 state = 0, irq = 0, req, oldreq, err; + u32 state, irq, req, ack, err; struct cx18_mailbox __iomem *mb; + u32 __iomem *xpu_state; wait_queue_head_t *waitq; - int timeout = 100; - int cnt = 0; - int sig = 0; + struct mutex *mb_lock; + long int timeout, ret; int i; if (info == NULL) { @@ -201,50 +478,104 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) CX18_DEBUG_HI_API("%s\n", info->name); else CX18_DEBUG_API("%s\n", info->name); - cx18_setup_page(cx, SCB_OFFSET); - mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req); - if (mb == NULL) { - CX18_ERR("mb %s busy\n", info->name); - return -EBUSY; + switch (info->rpu) { + case APU: + waitq = &cx->mb_apu_waitq; + mb_lock = &cx->epu2apu_mb_lock; + irq = IRQ_EPU_TO_APU; + mb = &cx->scb->epu2apu_mb; + xpu_state = &cx->scb->apu_state; + break; + case CPU: + waitq = &cx->mb_cpu_waitq; + mb_lock = &cx->epu2cpu_mb_lock; + irq = IRQ_EPU_TO_CPU; + mb = &cx->scb->epu2cpu_mb; + xpu_state = &cx->scb->cpu_state; + break; + default: + CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu); + return -EINVAL; } - oldreq = req - 1; + mutex_lock(mb_lock); + /* + * Wait for an in-use mailbox to complete + * + * If the XPU is responding with Ack's, the mailbox shouldn't be in + * a busy state, since we serialize access to it on our end. + * + * If the wait for ack after sending a previous command was interrupted + * by a signal, we may get here and find a busy mailbox. After waiting, + * mark it "not busy" from our end, if the XPU hasn't ack'ed it still. + */ + state = cx18_readl(cx, xpu_state); + req = cx18_readl(cx, &mb->request); + timeout = msecs_to_jiffies(10); + ret = wait_event_timeout(*waitq, + (ack = cx18_readl(cx, &mb->ack)) == req, + timeout); + if (req != ack) { + /* waited long enough, make the mbox "not busy" from our end */ + cx18_writel(cx, req, &mb->ack); + CX18_ERR("mbox was found stuck busy when setting up for %s; " + "clearing busy and trying to proceed\n", info->name); + } else if (ret != timeout) + CX18_DEBUG_API("waited %u msecs for busy mbox to be acked\n", + jiffies_to_msecs(timeout-ret)); + + /* Build the outgoing mailbox */ + req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1; + cx18_writel(cx, cmd, &mb->cmd); for (i = 0; i < args; i++) cx18_writel(cx, data[i], &mb->args[i]); cx18_writel(cx, 0, &mb->error); cx18_writel(cx, req, &mb->request); + cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */ - switch (info->rpu) { - case APU: waitq = &cx->mb_apu_waitq; break; - case CPU: waitq = &cx->mb_cpu_waitq; break; - case EPU: waitq = &cx->mb_epu_waitq; break; - case HPU: waitq = &cx->mb_hpu_waitq; break; - default: return -EINVAL; - } - if (info->flags & API_FAST) - timeout /= 2; + /* + * Notify the XPU and wait for it to send an Ack back + */ + timeout = msecs_to_jiffies((info->flags & API_FAST) ? 10 : 20); + + CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n", + irq, info->name); cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq); - while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request) - && cnt < 660) { - if (cnt > 200 && !in_atomic()) - sig = cx18_msleep_timeout(10, 1); - cnt++; - } - if (sig) - return -EINTR; - if (cnt == 660) { - cx18_writel(cx, oldreq, &mb->request); - CX18_ERR("mb %s failed\n", info->name); + ret = wait_event_timeout( + *waitq, + cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request), + timeout); + + if (ret == 0) { + /* Timed out */ + mutex_unlock(mb_lock); + CX18_DEBUG_WARN("sending %s timed out waiting %d msecs for RPU " + "acknowledgement\n", + info->name, jiffies_to_msecs(timeout)); return -EINVAL; } + + if (ret != timeout) + CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n", + jiffies_to_msecs(timeout-ret), info->name); + + /* Collect data returned by the XPU */ for (i = 0; i < MAX_MB_ARGUMENTS; i++) data[i] = cx18_readl(cx, &mb->args[i]); err = cx18_readl(cx, &mb->error); - if (!in_atomic() && (info->flags & API_SLOW)) + mutex_unlock(mb_lock); + + /* + * Wait for XPU to perform extra actions for the caller in some cases. + * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers + * back in a burst shortly thereafter + */ + if (info->flags & API_SLOW) cx18_msleep_timeout(300, 0); + if (err) CX18_DEBUG_API("mailbox error %08x for command %s\n", err, info->name); @@ -253,12 +584,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]) { - int res = cx18_api_call(cx, cmd, args, data); - - /* Allow a single retry, probably already too late though. - If there is no free mailbox then that is usually an indication - of a more serious problem. */ - return (res == -EBUSY) ? cx18_api_call(cx, cmd, args, data) : res; + return cx18_api_call(cx, cmd, args, data); } static int cx18_set_filter_param(struct cx18_stream *s) @@ -281,8 +607,9 @@ static int cx18_set_filter_param(struct cx18_stream *s) int cx18_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]) { - struct cx18 *cx = priv; - struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG]; + struct cx18_api_func_private *api_priv = priv; + struct cx18 *cx = api_priv->cx; + struct cx18_stream *s = api_priv->s; switch (cmd) { case CX2341X_ENC_SET_OUTPUT_PORT: diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h index d995641..ce2b668 100644 --- a/drivers/media/video/cx18/cx18-mailbox.h +++ b/drivers/media/video/cx18/cx18-mailbox.h @@ -2,6 +2,7 @@ * cx18 mailbox functions * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -30,8 +31,24 @@ #define MB_RESERVED_HANDLE_0 0 #define MB_RESERVED_HANDLE_1 0xFFFFFFFF +#define APU 0 +#define CPU 1 +#define EPU 2 +#define HPU 3 + struct cx18; +/* + * This structure is used by CPU to provide completed buffers information + * Its structure is dictrated by the layout of the SCB, required by the + * firmware, but its defintion needs to be here, instead of in cx18-scb.h, + * for mailbox work order scheduling + */ +struct cx18_mdl_ack { + u32 id; /* ID of a completed MDL */ + u32 data_used; /* Total data filled in the MDL for buffer 'id' */ +}; + /* The cx18_mailbox struct is the mailbox structure which is used for passing messages between processors */ struct cx18_mailbox { @@ -62,12 +79,22 @@ struct cx18_mailbox { u32 error; }; +struct cx18_stream; + +struct cx18_api_func_private { + struct cx18 *cx; + struct cx18_stream *s; +}; + int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]); int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd, int args, ...); int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...); int cx18_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]); -long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb); + +void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu); + +void cx18_epu_work_handler(struct work_struct *work); #endif diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index 174682c..8d9441e8 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -4,6 +4,7 @@ * Derived from ivtv-queue.c * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -41,91 +42,126 @@ void cx18_queue_init(struct cx18_queue *q) q->bytesused = 0; } -void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, - struct cx18_queue *q) +struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, + struct cx18_queue *q, int to_front) { - unsigned long flags = 0; - - /* clear the buffer if it is going to be enqueued to the free queue */ - if (q == &s->q_free) { + /* clear the buffer if it is not to be enqueued to the full queue */ + if (q != &s->q_full) { buf->bytesused = 0; buf->readpos = 0; buf->b_flags = 0; + buf->skipped = 0; } - spin_lock_irqsave(&s->qlock, flags); - list_add_tail(&buf->list, &q->list); - atomic_inc(&q->buffers); + + mutex_lock(&s->qlock); + + /* q_busy is restricted to a max buffer count imposed by firmware */ + if (q == &s->q_busy && + atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM) + q = &s->q_free; + + if (to_front) + list_add(&buf->list, &q->list); /* LIFO */ + else + list_add_tail(&buf->list, &q->list); /* FIFO */ q->bytesused += buf->bytesused - buf->readpos; - spin_unlock_irqrestore(&s->qlock, flags); + atomic_inc(&q->buffers); + + mutex_unlock(&s->qlock); + return q; } struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) { struct cx18_buffer *buf = NULL; - unsigned long flags = 0; - spin_lock_irqsave(&s->qlock, flags); + mutex_lock(&s->qlock); if (!list_empty(&q->list)) { - buf = list_entry(q->list.next, struct cx18_buffer, list); - list_del_init(q->list.next); - atomic_dec(&q->buffers); + buf = list_first_entry(&q->list, struct cx18_buffer, list); + list_del_init(&buf->list); q->bytesused -= buf->bytesused - buf->readpos; + buf->skipped = 0; + atomic_dec(&q->buffers); } - spin_unlock_irqrestore(&s->qlock, flags); + mutex_unlock(&s->qlock); return buf; } -struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, +struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, u32 bytesused) { struct cx18 *cx = s->cx; - struct list_head *p; - - spin_lock(&s->qlock); - list_for_each(p, &s->q_free.list) { - struct cx18_buffer *buf = - list_entry(p, struct cx18_buffer, list); - - if (buf->id != id) + struct cx18_buffer *buf; + struct cx18_buffer *tmp; + struct cx18_buffer *ret = NULL; + + mutex_lock(&s->qlock); + list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) { + if (buf->id != id) { + buf->skipped++; + if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) { + /* buffer must have fallen out of rotation */ + CX18_WARN("Skipped %s, buffer %d, %d " + "times - it must have dropped out of " + "rotation\n", s->name, buf->id, + buf->skipped); + /* move it to q_free */ + list_move_tail(&buf->list, &s->q_free.list); + buf->bytesused = buf->readpos = buf->b_flags = + buf->skipped = 0; + atomic_dec(&s->q_busy.buffers); + atomic_inc(&s->q_free.buffers); + } continue; + } buf->bytesused = bytesused; - atomic_dec(&s->q_free.buffers); - atomic_inc(&s->q_full.buffers); - s->q_full.bytesused += buf->bytesused; - list_move_tail(&buf->list, &s->q_full.list); + /* Sync the buffer before we release the qlock */ + cx18_buf_sync_for_cpu(s, buf); + if (s->type == CX18_ENC_STREAM_TYPE_TS) { + /* + * TS doesn't use q_full. As we pull the buffer off of + * the queue here, the caller will have to put it back. + */ + list_del_init(&buf->list); + } else { + /* Move buffer from q_busy to q_full */ + list_move_tail(&buf->list, &s->q_full.list); + set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); + s->q_full.bytesused += buf->bytesused; + atomic_inc(&s->q_full.buffers); + } + atomic_dec(&s->q_busy.buffers); - spin_unlock(&s->qlock); - return buf; + ret = buf; + break; } - spin_unlock(&s->qlock); - CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name); - return NULL; + mutex_unlock(&s->qlock); + return ret; } /* Move all buffers of a queue to q_free, while flushing the buffers */ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) { - unsigned long flags; struct cx18_buffer *buf; if (q == &s->q_free) return; - spin_lock_irqsave(&s->qlock, flags); + mutex_lock(&s->qlock); while (!list_empty(&q->list)) { - buf = list_entry(q->list.next, struct cx18_buffer, list); - list_move_tail(q->list.next, &s->q_free.list); - buf->bytesused = buf->readpos = buf->b_flags = 0; + buf = list_first_entry(&q->list, struct cx18_buffer, list); + list_move_tail(&buf->list, &s->q_free.list); + buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0; atomic_inc(&s->q_free.buffers); } cx18_queue_init(q); - spin_unlock_irqrestore(&s->qlock, flags); + mutex_unlock(&s->qlock); } void cx18_flush_queues(struct cx18_stream *s) { - cx18_queue_flush(s, &s->q_io); + cx18_queue_flush(s, &s->q_busy); cx18_queue_flush(s, &s->q_full); } diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h index 7f93bb1..456cec3 100644 --- a/drivers/media/video/cx18/cx18-queue.h +++ b/drivers/media/video/cx18/cx18-queue.h @@ -4,6 +4,7 @@ * Derived from ivtv-queue.h * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -42,11 +43,26 @@ static inline void cx18_buf_sync_for_device(struct cx18_stream *s, void cx18_buf_swap(struct cx18_buffer *buf); /* cx18_queue utility functions */ +struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, + struct cx18_queue *q, int to_front); + +static inline +struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, + struct cx18_queue *q) +{ + return _cx18_enqueue(s, buf, q, 0); /* FIFO */ +} + +static inline +struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_buffer *buf, + struct cx18_queue *q) +{ + return _cx18_enqueue(s, buf, q, 1); /* LIFO */ +} + void cx18_queue_init(struct cx18_queue *q); -void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, - struct cx18_queue *q); struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); -struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, +struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, u32 bytesused); void cx18_flush_queues(struct cx18_stream *s); diff --git a/drivers/media/video/cx18/cx18-scb.c b/drivers/media/video/cx18/cx18-scb.c index f56d377..34b4d03 100644 --- a/drivers/media/video/cx18/cx18-scb.c +++ b/drivers/media/video/cx18/cx18-scb.c @@ -2,6 +2,7 @@ * cx18 System Control Block initialization * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -117,6 +118,5 @@ void cx18_init_scb(struct cx18 *cx) cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state), &cx->scb->ipc_offset); - cx18_writel(cx, 1, &cx->scb->hpu_state); cx18_writel(cx, 1, &cx->scb->epu_state); } diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/video/cx18/cx18-scb.h index 594713bb..1dc1c43 100644 --- a/drivers/media/video/cx18/cx18-scb.h +++ b/drivers/media/video/cx18/cx18-scb.h @@ -2,6 +2,7 @@ * cx18 System Control Block initialization * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -85,12 +86,6 @@ struct cx18_mdl { u32 length; /* Length of the buffer segment */ }; -/* This structure is used by CPU to provide completed buffers information */ -struct cx18_mdl_ack { - u32 id; /* ID of a completed MDL */ - u32 data_used; /* Total data filled in the MDL for buffer 'id' */ -}; - struct cx18_scb { /* These fields form the System Control Block which is used at boot time for localizing the IPC data as well as the code positions for all @@ -276,7 +271,7 @@ struct cx18_scb { struct cx18_mailbox hpu2epu_mb; struct cx18_mailbox ppu2epu_mb; - struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][2]; + struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS]; struct cx18_mdl cpu_mdl[1]; }; diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index e5ff770..63c336c 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -4,6 +4,7 @@ * Derived from ivtv-streams.c * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -110,7 +111,6 @@ static void cx18_stream_init(struct cx18 *cx, int type) { struct cx18_stream *s = &cx->streams[type]; struct video_device *dev = s->v4l2dev; - u32 max_size = cx->options.megabytes[type] * 1024 * 1024; /* we need to keep v4l2dev, so restore it afterwards */ memset(s, 0, sizeof(*s)); @@ -123,21 +123,15 @@ static void cx18_stream_init(struct cx18 *cx, int type) s->handle = CX18_INVALID_TASK_HANDLE; s->dma = cx18_stream_info[type].dma; + s->buffers = cx->stream_buffers[type]; s->buf_size = cx->stream_buf_size[type]; - if (s->buf_size) - s->buffers = max_size / s->buf_size; - if (s->buffers > 63) { - /* Each stream has a maximum of 63 buffers, - ensure we do not exceed that. */ - s->buffers = 63; - s->buf_size = (max_size / s->buffers) & ~0xfff; - } - spin_lock_init(&s->qlock); + + mutex_init(&s->qlock); init_waitqueue_head(&s->waitq); s->id = -1; cx18_queue_init(&s->q_free); + cx18_queue_init(&s->q_busy); cx18_queue_init(&s->q_full); - cx18_queue_init(&s->q_io); } static int cx18_prep_dev(struct cx18 *cx, int type) @@ -167,7 +161,7 @@ static int cx18_prep_dev(struct cx18 *cx, int type) /* User explicitly selected 0 buffers for these streams, so don't create them. */ if (cx18_stream_info[type].dma != PCI_DMA_NONE && - cx->options.megabytes[type] == 0) { + cx->stream_buffers[type] == 0) { CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name); return 0; } @@ -267,8 +261,9 @@ static int cx18_reg_dev(struct cx18 *cx, int type) switch (vfl_type) { case VFL_TYPE_GRABBER: - CX18_INFO("Registered device video%d for %s (%d MB)\n", - num, s->name, cx->options.megabytes[type]); + CX18_INFO("Registered device video%d for %s (%d x %d kB)\n", + num, s->name, cx->stream_buffers[type], + cx->stream_buf_size[type]/1024); break; case VFL_TYPE_RADIO: @@ -277,10 +272,11 @@ static int cx18_reg_dev(struct cx18 *cx, int type) break; case VFL_TYPE_VBI: - if (cx->options.megabytes[type]) - CX18_INFO("Registered device vbi%d for %s (%d MB)\n", - num, - s->name, cx->options.megabytes[type]); + if (cx->stream_buffers[type]) + CX18_INFO("Registered device vbi%d for %s " + "(%d x %d bytes)\n", + num, s->name, cx->stream_buffers[type], + cx->stream_buf_size[type]); else CX18_INFO("Registered device vbi%d for %s\n", num, s->name); @@ -344,7 +340,7 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister) static void cx18_vbi_setup(struct cx18_stream *s) { struct cx18 *cx = s->cx; - int raw = cx->vbi.sliced_in->service_set == 0; + int raw = cx18_raw_vbi(cx); u32 data[CX2341X_MBOX_MAX_DATA]; int lines; @@ -362,8 +358,7 @@ static void cx18_vbi_setup(struct cx18_stream *s) cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in); /* determine number of lines and total number of VBI bytes. - A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1 - The '- 1' byte is probably an unused U or V byte. Or something... + A raw line takes 1444 bytes: 4 byte SAV code + 2 * 720 A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal header, 42 data bytes + checksum (to be confirmed) */ if (raw) { @@ -381,14 +376,15 @@ static void cx18_vbi_setup(struct cx18_stream *s) /* Lines per field */ data[1] = (lines / 2) | ((lines / 2) << 16); /* bytes per line */ - data[2] = (raw ? cx->vbi.raw_size : cx->vbi.sliced_size); + data[2] = (raw ? cx->vbi.raw_decoder_line_size + : cx->vbi.sliced_decoder_line_size); /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */ data[3] = 1; /* Setup VBI for the cx25840 digitizer */ if (raw) { data[4] = 0x20602060; - data[5] = 0x30703070; + data[5] = 0x307090d0; } else { data[4] = 0xB0F0B0F0; data[5] = 0xA0E0A0E0; @@ -401,11 +397,52 @@ static void cx18_vbi_setup(struct cx18_stream *s) cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); } +struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, + struct cx18_buffer *buf) +{ + struct cx18 *cx = s->cx; + struct cx18_queue *q; + + /* Don't give it to the firmware, if we're not running a capture */ + if (s->handle == CX18_INVALID_TASK_HANDLE || + !test_bit(CX18_F_S_STREAMING, &s->s_flags)) + return cx18_enqueue(s, buf, &s->q_free); + + q = cx18_enqueue(s, buf, &s->q_busy); + if (q != &s->q_busy) + return q; /* The firmware has the max buffers it can handle */ + + cx18_buf_sync_for_device(s, buf); + cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, + (void __iomem *) &cx->scb->cpu_mdl[buf->id] - cx->enc_mem, + 1, buf->id, s->buf_size); + return q; +} + +void cx18_stream_load_fw_queue(struct cx18_stream *s) +{ + struct cx18_queue *q; + struct cx18_buffer *buf; + + if (atomic_read(&s->q_free.buffers) == 0 || + atomic_read(&s->q_busy.buffers) >= CX18_MAX_FW_MDLS_PER_STREAM) + return; + + /* Move from q_free to q_busy notifying the firmware, until the limit */ + do { + buf = cx18_dequeue(s, &s->q_free); + if (buf == NULL) + break; + q = cx18_stream_put_buf_fw(s, buf); + } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM + && q == &s->q_busy); +} + int cx18_start_v4l2_encode_stream(struct cx18_stream *s) { u32 data[MAX_MB_ARGUMENTS]; struct cx18 *cx = s->cx; - struct list_head *p; + struct cx18_buffer *buf; int ts = 0; int captype = 0; @@ -434,8 +471,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) captype = CAPTURE_CHANNEL_TYPE_PCM; break; case CX18_ENC_STREAM_TYPE_VBI: - captype = cx->vbi.sliced_in->service_set ? - CAPTURE_CHANNEL_TYPE_SLICED_VBI : CAPTURE_CHANNEL_TYPE_VBI; + captype = cx18_raw_vbi(cx) ? + CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI; cx->vbi.frame = 0; cx->vbi.inserted_frame = 0; memset(cx->vbi.sliced_mpeg_size, @@ -457,6 +494,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype); if (atomic_read(&cx->ana_capturing) == 0 && !ts) { + struct cx18_api_func_private priv; + /* Stuff from Windows, we don't know what it is */ cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0); cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1); @@ -476,7 +515,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0); /* Setup API for Stream */ - cx2341x_update(cx, cx18_api_func, NULL, &cx->params); + priv.cx = cx; + priv.s = s; + cx2341x_update(&priv, cx18_api_func, NULL, &cx->params); } if (atomic_read(&cx->tot_capturing) == 0) { @@ -488,16 +529,17 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem, (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem); - list_for_each(p, &s->q_free.list) { - struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list); - + /* Init all the cpu_mdls for this stream */ + cx18_flush_queues(s); + mutex_lock(&s->qlock); + list_for_each_entry(buf, &s->q_free.list, list) { cx18_writel(cx, buf->dma_handle, &cx->scb->cpu_mdl[buf->id].paddr); cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); - cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, - (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, - 1, buf->id, s->buf_size); } + mutex_unlock(&s->qlock); + cx18_stream_load_fw_queue(s); + /* begin_capture */ if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { CX18_DEBUG_WARN("Error starting capture!\n"); @@ -506,9 +548,15 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); else cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); + clear_bit(CX18_F_S_STREAMING, &s->s_flags); + /* FIXME - CX18_F_S_STREAMOFF as well? */ cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); - /* FIXME - clean-up DSP0_INT mask, i_flags, s_flags, etc. */ + s->handle = CX18_INVALID_TASK_HANDLE; + if (atomic_read(&cx->tot_capturing) == 0) { + set_bit(CX18_F_I_EOS, &cx->i_flags); + cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); + } return -EINVAL; } @@ -560,9 +608,6 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n"); } - /* Tell the CX23418 it can't use our buffers anymore */ - cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); - if (s->type != CX18_ENC_STREAM_TYPE_TS) atomic_dec(&cx->ana_capturing); atomic_dec(&cx->tot_capturing); @@ -570,6 +615,9 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) /* Clear capture and no-read bits */ clear_bit(CX18_F_S_STREAMING, &s->s_flags); + /* Tell the CX23418 it can't use our buffers anymore */ + cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); + cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); s->handle = CX18_INVALID_TASK_HANDLE; @@ -595,3 +643,21 @@ u32 cx18_find_handle(struct cx18 *cx) } return CX18_INVALID_TASK_HANDLE; } + +struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle) +{ + int i; + struct cx18_stream *s; + + if (handle == CX18_INVALID_TASK_HANDLE) + return NULL; + + for (i = 0; i < CX18_MAX_STREAMS; i++) { + s = &cx->streams[i]; + if (s->handle != handle) + continue; + if (s->v4l2dev || s->dvb.enabled) + return s; + } + return NULL; +} diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h index f327e94..420e0a1 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/drivers/media/video/cx18/cx18-streams.h @@ -4,6 +4,7 @@ * Derived from ivtv-streams.h * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.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 @@ -22,11 +23,15 @@ */ u32 cx18_find_handle(struct cx18 *cx); +struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle); int cx18_streams_setup(struct cx18 *cx); int cx18_streams_register(struct cx18 *cx); void cx18_streams_cleanup(struct cx18 *cx, int unregister); /* Capture related */ +void cx18_stream_load_fw_queue(struct cx18_stream *s); +struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, + struct cx18_buffer *buf); int cx18_start_v4l2_encode_stream(struct cx18_stream *s); int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end); diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c index 22e76ee..fb595bd 100644 --- a/drivers/media/video/cx18/cx18-vbi.c +++ b/drivers/media/video/cx18/cx18-vbi.c @@ -160,11 +160,14 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, return; /* Raw VBI data */ - if (cx->vbi.sliced_in->service_set == 0) { + if (cx18_raw_vbi(cx)) { u8 type; cx18_buf_swap(buf); + /* Skip 12 bytes of header that gets stuffed in */ + size -= 12; + memcpy(p, &buf->buf[12], size); type = p[3]; size = buf->bytesused = compress_raw_buf(cx, p, size); diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h index 9f6be2d..84c0ff1 100644 --- a/drivers/media/video/cx18/cx18-version.h +++ b/drivers/media/video/cx18/cx18-version.h @@ -25,7 +25,7 @@ #define CX18_DRIVER_NAME "cx18" #define CX18_DRIVER_VERSION_MAJOR 1 #define CX18_DRIVER_VERSION_MINOR 0 -#define CX18_DRIVER_VERSION_PATCHLEVEL 1 +#define CX18_DRIVER_VERSION_PATCHLEVEL 4 #define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL) #define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \ diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h index 668f968..601f3a2 100644 --- a/drivers/media/video/cx18/cx23418.h +++ b/drivers/media/video/cx18/cx23418.h @@ -44,6 +44,7 @@ /* All commands for CPU have the following mask set */ #define CPU_CMD_MASK 0x20000000 +#define CPU_CMD_MASK_DEBUG (CPU_CMD_MASK | 0x00000000) #define CPU_CMD_MASK_ACK (CPU_CMD_MASK | 0x80000000) #define CPU_CMD_MASK_CAPTURE (CPU_CMD_MASK | 0x00020000) #define CPU_CMD_MASK_TS (CPU_CMD_MASK | 0x00040000) @@ -71,6 +72,11 @@ 0/zero/NULL means "I have nothing to say" */ #define CX18_EPU_DEBUG (EPU_CMD_MASK_DEBUG | 0x0003) +/* Reads memory/registers (32-bit) + IN[0] - Address + OUT[1] - Value */ +#define CX18_CPU_DEBUG_PEEK32 (CPU_CMD_MASK_DEBUG | 0x0003) + /* Description: This command starts streaming with the set channel type IN[0] - Task handle. Handle of the task to start ReturnCode - One of the ERR_CAPTURE_... */ diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index 00831f3..798d240 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -2,7 +2,7 @@ * * Support for a cx23417 mpeg encoder via cx23885 host port. * - * (c) 2004 Jelle Foks <jelle@foks.8m.com> + * (c) 2004 Jelle Foks <jelle@foks.us> * (c) 2004 Gerd Knorr <kraxel@bytesex.org> * (c) 2008 Steven Toth <stoth@linuxtv.org> * - CX23885/7/8 support diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index dac5ccc..caa098b 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -158,6 +158,10 @@ struct cx23885_board cx23885_boards[] = { .name = "Leadtek Winfast PxDVR3200 H", .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_COMPRO_VIDEOMATE_E650F] = { + .name = "Compro VideoMate E650F", + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -237,6 +241,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x107d, .subdevice = 0x6681, .card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H, + }, { + .subvendor = 0x185b, + .subdevice = 0xe800, + .card = CX23885_BOARD_COMPRO_VIDEOMATE_E650F, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -390,6 +398,7 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg) case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: /* Tuner Reset Command */ bitmask = 0x04; break; @@ -530,6 +539,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x000f000f); break; case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: /* GPIO-2 xc3028 tuner reset */ /* The following GPIO's are on the internal AVCore (cx25840) */ @@ -630,6 +640,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1700: case CX23885_BOARD_HAUPPAUGE_HVR1400: case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ @@ -644,6 +655,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1800lp: case CX23885_BOARD_HAUPPAUGE_HVR1700: case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: request_module("cx25840"); break; } diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index e1aac07..1c45412 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -502,6 +502,7 @@ static int dvb_register(struct cx23885_tsport *port) break; } case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: i2c_bus = &dev->i2c_bus[0]; fe0->dvb.frontend = dvb_attach(zl10353_attach, diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 1d53f54..6782802 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -66,6 +66,7 @@ #define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10 #define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11 #define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12 +#define CX23885_BOARD_COMPRO_VIDEOMATE_E650F 13 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ diff --git a/drivers/media/video/cx25840/Kconfig b/drivers/media/video/cx25840/Kconfig index de515da..451133a 100644 --- a/drivers/media/video/cx25840/Kconfig +++ b/drivers/media/video/cx25840/Kconfig @@ -1,6 +1,6 @@ config VIDEO_CX25840 tristate "Conexant CX2584x audio/video decoders" - depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C ---help--- Support for the Conexant CX2584x audio/video decoders. diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c index d6421e1..d199d80 100644 --- a/drivers/media/video/cx25840/cx25840-audio.c +++ b/drivers/media/video/cx25840/cx25840-audio.c @@ -25,7 +25,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); if (freq != 32000 && freq != 44100 && freq != 48000) return -EINVAL; @@ -193,7 +193,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) void cx25840_audio_set_path(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); /* assert soft reset */ cx25840_and_or(client, 0x810, ~0x1, 0x01); @@ -235,7 +235,7 @@ void cx25840_audio_set_path(struct i2c_client *client) static int get_volume(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); int vol; if (state->unmute_volume >= 0) @@ -252,7 +252,7 @@ static int get_volume(struct i2c_client *client) static void set_volume(struct i2c_client *client, int volume) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); int vol; if (state->unmute_volume >= 0) { @@ -340,14 +340,14 @@ static void set_balance(struct i2c_client *client, int balance) static int get_mute(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); return state->unmute_volume >= 0; } static void set_mute(struct i2c_client *client, int mute) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); if (mute && state->unmute_volume == -1) { int vol = get_volume(client); @@ -365,7 +365,7 @@ static void set_mute(struct i2c_client *client, int mute) int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct v4l2_control *ctrl = arg; int retval; diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 4da8cd7..2ad2771 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -191,7 +191,7 @@ static void cx25840_work_handler(struct work_struct *work) static void cx25840_initialize(struct i2c_client *client) { DEFINE_WAIT(wait); - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct workqueue_struct *q; /* datasheet startup in numbered steps, refer to page 3-77 */ @@ -259,7 +259,7 @@ static void cx25840_initialize(struct i2c_client *client) static void cx23885_initialize(struct i2c_client *client) { DEFINE_WAIT(wait); - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct workqueue_struct *q; /* Internal Reset */ @@ -350,7 +350,7 @@ static void cx23885_initialize(struct i2c_client *client) void cx25840_std_setup(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); v4l2_std_id std = state->std; int hblank, hactive, burst, vblank, vactive, sc; int vblank656, src_decimation; @@ -497,7 +497,7 @@ void cx25840_std_setup(struct i2c_client *client) static void input_change(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); v4l2_std_id std = state->std; /* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */ @@ -551,7 +551,7 @@ static void input_change(struct i2c_client *client) static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, enum cx25840_audio_input aud_input) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 is_composite = (vid_input >= CX25840_COMPOSITE1 && vid_input <= CX25840_COMPOSITE8); u8 reg; @@ -671,7 +671,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp static int set_v4lstd(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 fmt = 0; /* zero is autodetect */ u8 pal_m = 0; @@ -720,9 +720,10 @@ static int set_v4lstd(struct i2c_client *client) /* ----------------------------------------------------------------------- */ -static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); switch (ctrl->id) { case CX25840_CID_ENABLE_PVR150_WORKAROUND: @@ -786,9 +787,10 @@ static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) return 0; } -static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); switch (ctrl->id) { case CX25840_CID_ENABLE_PVR150_WORKAROUND: @@ -823,21 +825,23 @@ static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) /* ----------------------------------------------------------------------- */ -static int get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int cx25840_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + switch (fmt->type) { case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: return cx25840_vbi(client, VIDIOC_G_FMT, fmt); default: return -EINVAL; } - return 0; } -static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int cx25840_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); struct v4l2_pix_format *pix; int HSC, VSC, Vsrc, Hsrc, filter, Vlines; int is_50Hz = !(state->std & V4L2_STD_525_60); @@ -914,7 +918,7 @@ static void log_video_status(struct i2c_client *client) "0xD", "0xE", "0xF" }; - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf; u8 gen_stat1 = cx25840_read(client, 0x40d); u8 gen_stat2 = cx25840_read(client, 0x40e); @@ -944,7 +948,7 @@ static void log_video_status(struct i2c_client *client) static void log_audio_status(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 download_ctl = cx25840_read(client, 0x803); u8 mod_det_stat0 = cx25840_read(client, 0x804); u8 mod_det_stat1 = cx25840_read(client, 0x805); @@ -1097,21 +1101,12 @@ static void log_audio_status(struct i2c_client *client) /* ----------------------------------------------------------------------- */ -static int cx25840_command(struct i2c_client *client, unsigned int cmd, - void *arg) +static int cx25840_init(struct v4l2_subdev *sd, u32 val) { - struct cx25840_state *state = i2c_get_clientdata(client); - struct v4l2_tuner *vt = arg; - struct v4l2_routing *route = arg; - - /* ignore these commands */ - switch (cmd) { - case TUNER_SET_TYPE_ADDR: - return 0; - } + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); if (!state->is_initialized) { - v4l_dbg(1, cx25840_debug, client, "cmd %08x triggered fw load\n", cmd); /* initialize on first use */ state->is_initialized = 1; if (state->is_cx25836) @@ -1121,50 +1116,69 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, else cx25840_initialize(client); } + return 0; +} - switch (cmd) { #ifdef CONFIG_VIDEO_ADV_DEBUG - /* ioctls to allow direct access to the - * cx25840 registers for testing */ - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; +static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = cx25840_read(client, reg->reg & 0x0fff); - else - cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff); - break; - } + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = cx25840_read(client, reg->reg & 0x0fff); + return 0; +} + +static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff); + return 0; +} #endif - case VIDIOC_INT_DECODE_VBI_LINE: - return cx25840_vbi(client, cmd, arg); +static int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - return cx25840_audio(client, cmd, arg); + return cx25840_vbi(client, VIDIOC_INT_DECODE_VBI_LINE, vbi); +} + +static int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return cx25840_audio(client, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freq); +} + +static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_STREAMON: - v4l_dbg(1, cx25840_debug, client, "enable output\n"); + v4l_dbg(1, cx25840_debug, client, "%s output\n", + enable ? "enable" : "disable"); + if (enable) { if (state->is_cx23885) { u8 v = (cx25840_read(client, 0x421) | 0x0b); cx25840_write(client, 0x421, v); } else { cx25840_write(client, 0x115, - state->is_cx25836 ? 0x0c : 0x8c); + state->is_cx25836 ? 0x0c : 0x8c); cx25840_write(client, 0x116, - state->is_cx25836 ? 0x04 : 0x07); + state->is_cx25836 ? 0x04 : 0x07); } - break; - - case VIDIOC_STREAMOFF: - v4l_dbg(1, cx25840_debug, client, "disable output\n"); + } else { if (state->is_cx23885) { u8 v = cx25840_read(client, 0x421) & ~(0x0b); cx25840_write(client, 0x421, v); @@ -1172,133 +1186,136 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, cx25840_write(client, 0x115, 0x00); cx25840_write(client, 0x116, 0x00); } - break; + } + return 0; +} - case VIDIOC_LOG_STATUS: - log_video_status(client); - if (!state->is_cx25836) - log_audio_status(client); - break; +static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + struct cx25840_state *state = to_state(sd); - case VIDIOC_G_CTRL: - return get_v4lctrl(client, (struct v4l2_control *)arg); + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill_std(qc); + default: + break; + } + if (state->is_cx25836) + return -EINVAL; - case VIDIOC_S_CTRL: - return set_v4lctrl(client, (struct v4l2_control *)arg); + switch (qc->id) { + case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 65535, + 65535 / 100, state->default_volume); + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + return v4l2_ctrl_query_fill_std(qc); + default: + return -EINVAL; + } + return -EINVAL; +} - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; +static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill_std(qc); - default: - break; - } - if (state->is_cx25836) - return -EINVAL; + if (state->radio == 0 && state->std == std) + return 0; + state->radio = 0; + state->std = std; + return set_v4lstd(client); +} - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, - 65535 / 100, state->default_volume); - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill_std(qc); - default: - return -EINVAL; - } - return -EINVAL; - } +static int cx25840_s_radio(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); - case VIDIOC_G_STD: - *(v4l2_std_id *)arg = state->std; - break; + state->radio = 1; + return 0; +} - case VIDIOC_S_STD: - if (state->radio == 0 && state->std == *(v4l2_std_id *)arg) - return 0; - state->radio = 0; - state->std = *(v4l2_std_id *)arg; - return set_v4lstd(client); +static int cx25840_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case AUDC_SET_RADIO: - state->radio = 1; - break; + return set_input(client, route->input, state->aud_input); +} - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = state->vid_input; - route->output = 0; - break; +static int cx25840_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_S_VIDEO_ROUTING: - return set_input(client, route->input, state->aud_input); + if (state->is_cx25836) + return -EINVAL; + return set_input(client, state->vid_input, route->input); +} - case VIDIOC_INT_G_AUDIO_ROUTING: - if (state->is_cx25836) - return -EINVAL; - route->input = state->aud_input; - route->output = 0; - break; +static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_S_AUDIO_ROUTING: - if (state->is_cx25836) - return -EINVAL; - return set_input(client, state->vid_input, route->input); + if (!state->is_cx25836) + input_change(client); + return 0; +} - case VIDIOC_S_FREQUENCY: - if (!state->is_cx25836) { - input_change(client); - } - break; +static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 vpres = cx25840_read(client, 0x40e) & 0x20; + u8 mode; + int val = 0; - case VIDIOC_G_TUNER: - { - u8 vpres = cx25840_read(client, 0x40e) & 0x20; - u8 mode; - int val = 0; + if (state->radio) + return 0; - if (state->radio) - break; + vt->signal = vpres ? 0xffff : 0x0; + if (state->is_cx25836) + return 0; - vt->signal = vpres ? 0xffff : 0x0; - if (state->is_cx25836) - break; + vt->capability |= + V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; - vt->capability |= - V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | - V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + mode = cx25840_read(client, 0x804); - mode = cx25840_read(client, 0x804); + /* get rxsubchans and audmode */ + if ((mode & 0xf) == 1) + val |= V4L2_TUNER_SUB_STEREO; + else + val |= V4L2_TUNER_SUB_MONO; - /* get rxsubchans and audmode */ - if ((mode & 0xf) == 1) - val |= V4L2_TUNER_SUB_STEREO; - else - val |= V4L2_TUNER_SUB_MONO; + if (mode == 2 || mode == 4) + val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - if (mode == 2 || mode == 4) - val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + if (mode & 0x10) + val |= V4L2_TUNER_SUB_SAP; - if (mode & 0x10) - val |= V4L2_TUNER_SUB_SAP; + vt->rxsubchans = val; + vt->audmode = state->audmode; + return 0; +} - vt->rxsubchans = val; - vt->audmode = state->audmode; - break; - } +static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_S_TUNER: - if (state->radio || state->is_cx25836) - break; + if (state->radio || state->is_cx25836) + return 0; - switch (vt->audmode) { + switch (vt->audmode) { case V4L2_TUNER_MODE_MONO: /* mono -> mono stereo -> mono @@ -1326,41 +1343,100 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, break; default: return -EINVAL; - } - state->audmode = vt->audmode; - break; + } + state->audmode = vt->audmode; + return 0; +} - case VIDIOC_G_FMT: - return get_v4lfmt(client, (struct v4l2_format *)arg); +static int cx25840_reset(struct v4l2_subdev *sd, u32 val) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_S_FMT: - return set_v4lfmt(client, (struct v4l2_format *)arg); + if (state->is_cx25836) + cx25836_initialize(client); + else if (state->is_cx23885) + cx23885_initialize(client); + else + cx25840_initialize(client); + return 0; +} - case VIDIOC_INT_RESET: - if (state->is_cx25836) - cx25836_initialize(client); - else if (state->is_cx23885) - cx23885_initialize(client); - else - cx25840_initialize(client); - break; +static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, state->id, state->rev); + return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev); +} - default: - return -EINVAL; - } +static int cx25840_log_status(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + log_video_status(client); + if (!state->is_cx25836) + log_audio_status(client); return 0; } +static int cx25840_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops cx25840_core_ops = { + .log_status = cx25840_log_status, + .g_chip_ident = cx25840_g_chip_ident, + .g_ctrl = cx25840_g_ctrl, + .s_ctrl = cx25840_s_ctrl, + .queryctrl = cx25840_queryctrl, + .reset = cx25840_reset, + .init = cx25840_init, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = cx25840_g_register, + .s_register = cx25840_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = { + .s_frequency = cx25840_s_frequency, + .s_std = cx25840_s_std, + .s_radio = cx25840_s_radio, + .g_tuner = cx25840_g_tuner, + .s_tuner = cx25840_s_tuner, +}; + +static const struct v4l2_subdev_audio_ops cx25840_audio_ops = { + .s_clock_freq = cx25840_s_clock_freq, + .s_routing = cx25840_s_audio_routing, +}; + +static const struct v4l2_subdev_video_ops cx25840_video_ops = { + .s_routing = cx25840_s_video_routing, + .g_fmt = cx25840_g_fmt, + .s_fmt = cx25840_s_fmt, + .decode_vbi_line = cx25840_decode_vbi_line, + .s_stream = cx25840_s_stream, +}; + +static const struct v4l2_subdev_ops cx25840_ops = { + .core = &cx25840_core_ops, + .tuner = &cx25840_tuner_ops, + .audio = &cx25840_audio_ops, + .video = &cx25840_video_ops, +}; + /* ----------------------------------------------------------------------- */ static int cx25840_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct cx25840_state *state; + struct v4l2_subdev *sd; u32 id; u16 device_id; @@ -1392,10 +1468,11 @@ static int cx25840_probe(struct i2c_client *client, } state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL); - if (state == NULL) { + if (state == NULL) return -ENOMEM; - } + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &cx25840_ops); /* Note: revision '(device_id & 0x0f) == 2' was never built. The marking skips from 0x1 == 22 to 0x3 == 23. */ v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", @@ -1403,7 +1480,6 @@ static int cx25840_probe(struct i2c_client *client, (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f), client->addr << 1, client->adapter->name); - i2c_set_clientdata(client, state); state->c = client; state->is_cx25836 = ((device_id & 0xff00) == 0x8300); state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313); @@ -1430,7 +1506,10 @@ static int cx25840_probe(struct i2c_client *client, static int cx25840_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index b87337e..be05582 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -22,6 +22,7 @@ #include <linux/videodev2.h> +#include <media/v4l2-device.h> #include <linux/i2c.h> /* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is @@ -34,6 +35,7 @@ struct cx25840_state { struct i2c_client *c; + struct v4l2_subdev sd; int pvr150_workaround; int radio; v4l2_std_id std; @@ -53,6 +55,11 @@ struct cx25840_state { struct work_struct fw_work; /* work entry for fw load */ }; +static inline struct cx25840_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cx25840_state, sd); +} + /* ----------------------------------------------------------------------- */ /* cx25850-core.c */ int cx25840_write(struct i2c_client *client, u16 addr, u8 value); diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c index 8d489a4..0b2dceb 100644 --- a/drivers/media/video/cx25840/cx25840-firmware.c +++ b/drivers/media/video/cx25840/cx25840-firmware.c @@ -91,7 +91,7 @@ static int fw_write(struct i2c_client *client, const u8 *data, int size) int cx25840_loadfw(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); const struct firmware *fw = NULL; u8 buffer[FWSEND]; const u8 *ptr; diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c index 58e6ef1..03f09b2 100644 --- a/drivers/media/video/cx25840/cx25840-vbi.c +++ b/drivers/media/video/cx25840/cx25840-vbi.c @@ -84,7 +84,7 @@ static int decode_vps(u8 * dst, u8 * p) int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct v4l2_format *fmt; struct v4l2_sliced_vbi_format *svbi; diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index 06f171a..66c755c 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -742,7 +742,6 @@ static int __devinit snd_cx88_create(struct snd_card *card, core = cx88_core_get(pci); if (NULL == core) { err = -EINVAL; - kfree (chip); return err; } @@ -812,7 +811,7 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci, err = snd_cx88_create(card, pci, &chip); if (err < 0) - return (err); + goto error; err = snd_cx88_pcm(chip, 0, "CX88 Digital"); if (err < 0) diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index d3ae5b4..e162a70 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -3,7 +3,7 @@ * Support for a cx23416 mpeg encoder via cx2388x host port. * "blackbird" reference design. * - * (c) 2004 Jelle Foks <jelle@foks.8m.com> + * (c) 2004 Jelle Foks <jelle@foks.us> * (c) 2004 Gerd Knorr <kraxel@bytesex.org> * * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org> @@ -39,7 +39,7 @@ #include "cx88.h" MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards"); -MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); +MODULE_AUTHOR("Jelle Foks <jelle@foks.us>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); static unsigned int mpegbufs = 32; @@ -1244,8 +1244,16 @@ static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv) * We're being given access to re-arrange the GPIOs. * Take the bus off the cx22702 and put the cx23416 on it. */ - cx_clear(MO_GP0_IO, 0x00000080); /* cx22702 in reset */ - cx_set(MO_GP0_IO, 0x00000004); /* Disable the cx22702 */ + /* Toggle reset on cx22702 leaving i2c active */ + cx_set(MO_GP0_IO, 0x00000080); + udelay(1000); + cx_clear(MO_GP0_IO, 0x00000080); + udelay(50); + cx_set(MO_GP0_IO, 0x00000080); + udelay(1000); + /* tri-state the cx22702 pins */ + cx_set(MO_GP0_IO, 0x00000004); + udelay(1000); break; default: err = -ENODEV; diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 5bcbb4c..733ede3 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1237,7 +1237,6 @@ static const struct cx88_board cx88_boards[] = { }, }, [CX88_BOARD_WINFAST_DTV2000H] = { - /* video inputs and radio still in testing */ .name = "WinFast DTV2000 H", .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, .radio_type = UNSET, @@ -1251,7 +1250,35 @@ static const struct cx88_board cx88_boards[] = { .gpio1 = 0x00008203, .gpio2 = 0x00017304, .gpio3 = 0x02000000, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x0001d701, + .gpio1 = 0x0000b207, + .gpio2 = 0x0001d701, + .gpio3 = 0x02000000, + }, { + .type = CX88_VMUX_COMPOSITE2, + .vmux = 2, + .gpio0 = 0x0001d503, + .gpio1 = 0x0000b207, + .gpio2 = 0x0001d503, + .gpio3 = 0x02000000, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 3, + .gpio0 = 0x0001d701, + .gpio1 = 0x0000b207, + .gpio2 = 0x0001d701, + .gpio3 = 0x02000000, }}, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x00015702, + .gpio1 = 0x0000f207, + .gpio2 = 0x00015702, + .gpio3 = 0x02000000, + }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_GENIATECH_DVBS] = { @@ -1847,6 +1874,18 @@ static const struct cx88_board cx88_boards[] = { } }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_TBS_8910] = { + .name = "TBS 8910 DVB-S", + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_DVB, + .vmux = 0, + } }, + .mpeg = CX88_MPEG_DVB, + }, [CX88_BOARD_TBS_8920] = { .name = "TBS 8920 DVB-S/S2", .tuner_type = TUNER_ABSENT, @@ -1859,6 +1898,18 @@ static const struct cx88_board cx88_boards[] = { } }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_PROF_6200] = { + .name = "Prof 6200 DVB-S", + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_DVB, + .vmux = 0, + } }, + .mpeg = CX88_MPEG_DVB, + }, [CX88_BOARD_PROF_7300] = { .name = "PROF 7300 DVB-S/S2", .tuner_type = UNSET, @@ -1871,6 +1922,18 @@ static const struct cx88_board cx88_boards[] = { } }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_SATTRADE_ST4200] = { + .name = "SATTRADE ST4200 DVB-S/S2", + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_DVB, + .vmux = 0, + } }, + .mpeg = CX88_MPEG_DVB, + }, }; /* ------------------------------------------------------------------ */ @@ -1897,7 +1960,11 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = PCI_VENDOR_ID_ATI, .subdevice = 0x00f8, .card = CX88_BOARD_ATI_WONDER_PRO, - },{ + }, { + .subvendor = PCI_VENDOR_ID_ATI, + .subdevice = 0x00f9, + .card = CX88_BOARD_ATI_WONDER_PRO, + }, { .subvendor = 0x107d, .subdevice = 0x6611, .card = CX88_BOARD_WINFAST2000XP_EXPERT, @@ -2257,13 +2324,25 @@ static const struct cx88_subid cx88_subids[] = { .subdevice = 0x2011, .card = CX88_BOARD_OMICOM_SS4_PCI, }, { + .subvendor = 0x8910, + .subdevice = 0x8888, + .card = CX88_BOARD_TBS_8910, + }, { .subvendor = 0x8920, .subdevice = 0x8888, .card = CX88_BOARD_TBS_8920, }, { + .subvendor = 0xb022, + .subdevice = 0x3022, + .card = CX88_BOARD_PROF_6200, + }, { .subvendor = 0xB033, .subdevice = 0x3033, .card = CX88_BOARD_PROF_7300, + }, { + .subvendor = 0xb200, + .subdevice = 0x4200, + .card = CX88_BOARD_SATTRADE_ST4200, }, }; @@ -2874,8 +2953,11 @@ static void cx88_card_setup(struct cx88_core *core) case CX88_BOARD_TEVII_S420: case CX88_BOARD_TEVII_S460: case CX88_BOARD_OMICOM_SS4_PCI: + case CX88_BOARD_TBS_8910: case CX88_BOARD_TBS_8920: + case CX88_BOARD_PROF_6200: case CX88_BOARD_PROF_7300: + case CX88_BOARD_SATTRADE_ST4200: cx_write(MO_SRST_IO, 0); msleep(100); cx_write(MO_SRST_IO, 1); diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index 60705b0..b045874 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -844,6 +844,9 @@ static int set_tvaudio(struct cx88_core *core) } else if (V4L2_STD_SECAM_L & norm) { core->tvaudio = WW_L; + } else if ((V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H) & norm) { + core->tvaudio = WW_BG; + } else if (V4L2_STD_SECAM_DK & norm) { core->tvaudio = WW_DK; diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 309ca5e..da4dd49 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -406,7 +406,7 @@ static int tevii_dvbs_set_voltage(struct dvb_frontend *fe, cx_write(MO_GP0_IO, 0x00006060); break; case SEC_VOLTAGE_OFF: - printk("LNB Voltage SEC_VOLTAGE_off\n"); + printk("LNB Voltage SEC_VOLTAGE_off\n"); break; } @@ -606,7 +606,7 @@ static int dvb_register(struct cx8802_dev *dev) /* Get the first frontend */ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); if (!fe0) - return -EINVAL; + goto frontend_detach; /* multi-frontend gate control is undefined or defaults to fe0 */ dev->frontends.gate = 0; @@ -653,38 +653,35 @@ static int dvb_register(struct cx8802_dev *dev) } break; case CX88_BOARD_HAUPPAUGE_HVR3000: + /* MFE frontend 1 */ + mfe_shared = 1; + dev->frontends.gate = 2; /* DVB-S init */ fe0->dvb.frontend = dvb_attach(cx24123_attach, - &hauppauge_novas_config, - &dev->core->i2c_adap); + &hauppauge_novas_config, + &dev->core->i2c_adap); if (fe0->dvb.frontend) { - if (!dvb_attach(isl6421_attach, fe0->dvb.frontend, - &dev->core->i2c_adap, 0x08, ISL6421_DCL, 0x00)) { - dprintk( 1, "%s(): HVR3000 - DVB-S LNB Init: failed\n", __func__); - } - } else { - dprintk( 1, "%s(): HVR3000 - DVB-S Init: failed\n", __func__); + if (!dvb_attach(isl6421_attach, + fe0->dvb.frontend, + &dev->core->i2c_adap, + 0x08, ISL6421_DCL, 0x00)) + goto frontend_detach; } - /* DVB-T init */ + /* MFE frontend 2 */ fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2); - if (fe1) { - dev->frontends.gate = 2; - mfe_shared = 1; - fe1->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr_config, - &dev->core->i2c_adap); - if (fe1->dvb.frontend) { - fe1->dvb.frontend->id = 1; - if(!dvb_attach(simple_tuner_attach, fe1->dvb.frontend, - &dev->core->i2c_adap, 0x61, - TUNER_PHILIPS_FMD1216ME_MK3)) { - dprintk( 1, "%s(): HVR3000 - DVB-T misc Init: failed\n", __func__); - } - } else { - dprintk( 1, "%s(): HVR3000 - DVB-T Init: failed\n", __func__); - } - } else { - dprintk( 1, "%s(): HVR3000 - DVB-T Init: can't find frontend 2.\n", __func__); + if (!fe1) + goto frontend_detach; + /* DVB-T init */ + fe1->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr_config, + &dev->core->i2c_adap); + if (fe1->dvb.frontend) { + fe1->dvb.frontend->id = 1; + if (!dvb_attach(simple_tuner_attach, + fe1->dvb.frontend, + &dev->core->i2c_adap, + 0x61, TUNER_PHILIPS_FMD1216ME_MK3)) + goto frontend_detach; } break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: @@ -998,50 +995,51 @@ static int dvb_register(struct cx8802_dev *dev) } break; case CX88_BOARD_HAUPPAUGE_HVR4000: + /* MFE frontend 1 */ + mfe_shared = 1; + dev->frontends.gate = 2; /* DVB-S/S2 Init */ fe0->dvb.frontend = dvb_attach(cx24116_attach, - &hauppauge_hvr4000_config, - &dev->core->i2c_adap); + &hauppauge_hvr4000_config, + &dev->core->i2c_adap); if (fe0->dvb.frontend) { - if(!dvb_attach(isl6421_attach, fe0->dvb.frontend, - &dev->core->i2c_adap, 0x08, ISL6421_DCL, 0x00)) { - dprintk( 1, "%s(): HVR4000 - DVB-S LNB Init: failed\n", __func__); - } - } else { - dprintk( 1, "%s(): HVR4000 - DVB-S Init: failed\n", __func__); + if (!dvb_attach(isl6421_attach, + fe0->dvb.frontend, + &dev->core->i2c_adap, + 0x08, ISL6421_DCL, 0x00)) + goto frontend_detach; } - /* DVB-T Init */ + /* MFE frontend 2 */ fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2); - if (fe1) { - dev->frontends.gate = 2; - mfe_shared = 1; - fe1->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr_config, - &dev->core->i2c_adap); - if (fe1->dvb.frontend) { - fe1->dvb.frontend->id = 1; - if(!dvb_attach(simple_tuner_attach, fe1->dvb.frontend, - &dev->core->i2c_adap, 0x61, - TUNER_PHILIPS_FMD1216ME_MK3)) { - dprintk( 1, "%s(): HVR4000 - DVB-T misc Init: failed\n", __func__); - } - } else { - dprintk( 1, "%s(): HVR4000 - DVB-T Init: failed\n", __func__); - } - } else { - dprintk( 1, "%s(): HVR4000 - DVB-T Init: can't find frontend 2.\n", __func__); + if (!fe1) + goto frontend_detach; + /* DVB-T Init */ + fe1->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr_config, + &dev->core->i2c_adap); + if (fe1->dvb.frontend) { + fe1->dvb.frontend->id = 1; + if (!dvb_attach(simple_tuner_attach, + fe1->dvb.frontend, + &dev->core->i2c_adap, + 0x61, TUNER_PHILIPS_FMD1216ME_MK3)) + goto frontend_detach; } break; case CX88_BOARD_HAUPPAUGE_HVR4000LITE: fe0->dvb.frontend = dvb_attach(cx24116_attach, - &hauppauge_hvr4000_config, - &dev->core->i2c_adap); + &hauppauge_hvr4000_config, + &dev->core->i2c_adap); if (fe0->dvb.frontend) { - dvb_attach(isl6421_attach, fe0->dvb.frontend, - &dev->core->i2c_adap, - 0x08, ISL6421_DCL, 0x00); + if (!dvb_attach(isl6421_attach, + fe0->dvb.frontend, + &dev->core->i2c_adap, + 0x08, ISL6421_DCL, 0x00)) + goto frontend_detach; } break; + case CX88_BOARD_PROF_6200: + case CX88_BOARD_TBS_8910: case CX88_BOARD_TEVII_S420: fe0->dvb.frontend = dvb_attach(stv0299_attach, &tevii_tuner_sharp_config, @@ -1070,21 +1068,18 @@ static int dvb_register(struct cx8802_dev *dev) fe0->dvb.frontend = dvb_attach(cx24116_attach, &tevii_s460_config, &core->i2c_adap); - if (fe0->dvb.frontend != NULL) { - core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage; + if (fe0->dvb.frontend != NULL) fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; - } break; case CX88_BOARD_OMICOM_SS4_PCI: case CX88_BOARD_TBS_8920: case CX88_BOARD_PROF_7300: + case CX88_BOARD_SATTRADE_ST4200: fe0->dvb.frontend = dvb_attach(cx24116_attach, &hauppauge_hvr4000_config, &core->i2c_adap); - if (fe0->dvb.frontend != NULL) { - core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage; + if (fe0->dvb.frontend != NULL) fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; - } break; default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", @@ -1092,11 +1087,11 @@ static int dvb_register(struct cx8802_dev *dev) break; } - if ( (NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend) ) { + if ( (NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend) ) { printk(KERN_ERR "%s/2: frontend initialization failed\n", core->name); - return -EINVAL; + goto frontend_detach; } /* define general-purpose callback pointer */ fe0->dvb.frontend->callback = cx88_tuner_callback; @@ -1133,40 +1128,44 @@ static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv) * on the bus. Take the bus from the cx23416 and enable the * cx22702 demod */ - cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset and enable */ + /* Toggle reset on cx22702 leaving i2c active */ + cx_set(MO_GP0_IO, 0x00000080); + udelay(1000); + cx_clear(MO_GP0_IO, 0x00000080); + udelay(50); + cx_set(MO_GP0_IO, 0x00000080); + udelay(1000); + /* enable the cx22702 pins */ cx_clear(MO_GP0_IO, 0x00000004); udelay(1000); break; case CX88_BOARD_HAUPPAUGE_HVR3000: case CX88_BOARD_HAUPPAUGE_HVR4000: - if(core->dvbdev->frontends.active_fe_id == 1) { - /* DVB-S/S2 Enabled */ - - /* Toggle reset on cx22702 leaving i2c active */ - cx_write(MO_GP0_IO, (core->board.input[0].gpio0 & 0x0000ff00) | 0x00000080); - udelay(1000); - cx_clear(MO_GP0_IO, 0x00000080); - udelay(50); - cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset */ - cx_set(MO_GP0_IO, 0x00000004); /* tri-state the cx22702 pins */ - udelay(1000); - - cx_write(MO_SRST_IO, 1); /* Take the cx24116/cx24123 out of reset */ + /* Toggle reset on cx22702 leaving i2c active */ + cx_set(MO_GP0_IO, 0x00000080); + udelay(1000); + cx_clear(MO_GP0_IO, 0x00000080); + udelay(50); + cx_set(MO_GP0_IO, 0x00000080); + udelay(1000); + switch (core->dvbdev->frontends.active_fe_id) { + case 1: /* DVB-S/S2 Enabled */ + /* tri-state the cx22702 pins */ + cx_set(MO_GP0_IO, 0x00000004); + /* Take the cx24116/cx24123 out of reset */ + cx_write(MO_SRST_IO, 1); core->dvbdev->ts_gen_cntrl = 0x02; /* Parallel IO */ - } else - if (core->dvbdev->frontends.active_fe_id == 2) { - /* DVB-T Enabled */ - + break; + case 2: /* DVB-T Enabled */ /* Put the cx24116/cx24123 into reset */ cx_write(MO_SRST_IO, 0); - - /* cx22702 out of reset and enable it */ - cx_set(MO_GP0_IO, 0x00000080); + /* enable the cx22702 pins */ cx_clear(MO_GP0_IO, 0x00000004); core->dvbdev->ts_gen_cntrl = 0x0c; /* Serial IO */ - udelay(1000); + break; } + udelay(1000); break; default: @@ -1199,8 +1198,7 @@ static int cx8802_dvb_probe(struct cx8802_driver *drv) { struct cx88_core *core = drv->core; struct cx8802_dev *dev = drv->core->dvbdev; - int err, i; - struct videobuf_dvb_frontend *fe; + int err; dprintk( 1, "%s\n", __func__); dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", @@ -1216,31 +1214,47 @@ static int cx8802_dvb_probe(struct cx8802_driver *drv) /* If vp3054 isn't enabled, a stub will just return 0 */ err = vp3054_i2c_probe(dev); if (0 != err) - goto fail_core; + goto fail_probe; /* dvb stuff */ printk(KERN_INFO "%s/2: cx2388x based DVB/ATSC card\n", core->name); dev->ts_gen_cntrl = 0x0c; - for (i = 1; i <= core->board.num_frontends; i++) { - fe = videobuf_dvb_get_frontend(&core->dvbdev->frontends, i); - if (!fe) { - printk(KERN_ERR "%s() failed to get frontend(%d)\n", __func__, i); - continue; + err = -ENODEV; + if (core->board.num_frontends) { + struct videobuf_dvb_frontend *fe; + int i; + + for (i = 1; i <= core->board.num_frontends; i++) { + fe = videobuf_dvb_get_frontend(&core->dvbdev->frontends, i); + if (fe == NULL) { + printk(KERN_ERR "%s() failed to get frontend(%d)\n", + __func__, i); + goto fail_probe; + } + videobuf_queue_sg_init(&fe->dvb.dvbq, &dvb_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_TOP, + sizeof(struct cx88_buffer), + dev); + /* init struct videobuf_dvb */ + fe->dvb.name = dev->core->name; } - videobuf_queue_sg_init(&fe->dvb.dvbq, &dvb_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_TOP, - sizeof(struct cx88_buffer), - dev); - /* init struct videobuf_dvb */ - fe->dvb.name = dev->core->name; + } else { + /* no frontends allocated */ + printk(KERN_ERR "%s/2 .num_frontends should be non-zero\n", + core->name); + goto fail_core; } err = dvb_register(dev); - if (err != 0) + if (err) + /* frontends/adapter de-allocated in dvb_register */ printk(KERN_ERR "%s/2: dvb_register failed (err = %d)\n", core->name, err); + return err; +fail_probe: + videobuf_dvb_dealloc_frontends(&core->dvbdev->frontends); fail_core: return err; } diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 3ebdcd1..a04fee2 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -3,7 +3,7 @@ * Support for the mpeg transport stream transfers * PCI function #2 of the cx2388x. * - * (c) 2004 Jelle Foks <jelle@foks.8m.com> + * (c) 2004 Jelle Foks <jelle@foks.us> * (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au> * (c) 2004 Gerd Knorr <kraxel@bytesex.org> * @@ -34,7 +34,7 @@ /* ------------------------------------------------------------------ */ MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards"); -MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>"); +MODULE_AUTHOR("Jelle Foks <jelle@foks.us>"); MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index f424096..20649b2 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -53,12 +53,11 @@ /* ----------------------------------------------------------- */ /* defines and enums */ -/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ -#define CX88_NORMS (\ - V4L2_STD_NTSC_M| V4L2_STD_NTSC_M_JP| V4L2_STD_NTSC_443 | \ - V4L2_STD_PAL_BG| V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \ - V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \ - V4L2_STD_PAL_60| V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK ) +/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM/LC */ +#define CX88_NORMS (V4L2_STD_ALL \ + & ~V4L2_STD_PAL_H \ + & ~V4L2_STD_NTSC_M_KR \ + & ~V4L2_STD_SECAM_LC) #define FORMAT_FLAGS_PACKED 0x01 #define FORMAT_FLAGS_PLANAR 0x02 @@ -229,6 +228,9 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_TEVII_S420 73 #define CX88_BOARD_PROLINK_PV_GLOBAL_XTREME 74 #define CX88_BOARD_PROF_7300 75 +#define CX88_BOARD_SATTRADE_ST4200 76 +#define CX88_BOARD_TBS_8910 77 +#define CX88_BOARD_PROF_6200 78 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index 7a8d49e..15c03f0 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -424,11 +424,12 @@ static int em28xx_audio_init(struct em28xx *dev) struct snd_pcm *pcm; struct snd_card *card; static int devnr; - int ret, err; + int err; - if (dev->has_audio_class) { + if (dev->has_alsa_audio != 1) { /* This device does not support the extension (in this case - the device is expecting the snd-usb-audio module */ + the device is expecting the snd-usb-audio module or + doesn't have analog audio support at all) */ return 0; } @@ -449,7 +450,12 @@ static int em28xx_audio_init(struct em28xx *dev) } spin_lock_init(&adev->slock); - ret = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm); + err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm); + if (err < 0) { + snd_card_free(card); + return err; + } + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture); pcm->info_flags = 0; pcm->private_data = dev; @@ -461,7 +467,7 @@ static int em28xx_audio_init(struct em28xx *dev) err = snd_card_register(card); if (err < 0) { snd_card_free(card); - return -ENOMEM; + return err; } adev->sndcard = card; adev->udev = dev->udev; @@ -475,9 +481,10 @@ static int em28xx_audio_fini(struct em28xx *dev) if (dev == NULL) return 0; - if (dev->has_audio_class) { + if (dev->has_alsa_audio != 1) { /* This device does not support the extension (in this case - the device is expecting the snd-usb-audio module */ + the device is expecting the snd-usb-audio module or + doesn't have analog audio support at all) */ return 0; } diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index d65d057..e776699 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -37,6 +37,8 @@ #include "em28xx.h" +#define DRIVER_NAME "em28xx" + static int tuner = -1; module_param(tuner, int, 0444); MODULE_PARM_DESC(tuner, "tuner type"); @@ -45,122 +47,177 @@ static unsigned int disable_ir; module_param(disable_ir, int, 0444); MODULE_PARM_DESC(disable_ir, "disable infrared remote support"); +static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +module_param_array(card, int, NULL, 0444); +MODULE_PARM_DESC(card, "card type"); + +/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */ +static unsigned long em28xx_devused; + struct em28xx_hash_table { unsigned long hash; unsigned int model; unsigned int tuner; }; +/* + * Reset sequences for analog/digital modes + */ + +/* Reset for the most [analog] boards */ +static struct em28xx_reg_seq default_analog[] = { + {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Reset for the most [digital] boards */ +static struct em28xx_reg_seq default_digital[] = { + {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* 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}, +}; + +/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ +static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = { + {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ + +/* Board - EM2870 Kworld 355u + Analog - No input analog */ + +/* Callback for the most boards */ +static struct em28xx_reg_seq default_tuner_gpio[] = { + {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}, +}; + +/* + * Board definitions + */ struct em28xx_board em28xx_boards[] = { [EM2750_BOARD_UNKNOWN] = { .name = "Unknown EM2750/EM2751 webcam grabber", - .vchannels = 1, + .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, + .tuner_type = TUNER_ABSENT, /* This is a webcam */ .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, } }, }, [EM2800_BOARD_UNKNOWN] = { .name = "Unknown EM2800 video grabber", .is_em2800 = 1, - .vchannels = 2, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_UNKNOWN] = { - .name = "Unknown EM2750/28xx video grabber", - .is_em2800 = 0, - .tuner_type = TUNER_ABSENT, + .name = "Unknown EM2750/28xx video grabber", + .tuner_type = TUNER_ABSENT, }, [EM2750_BOARD_DLCW_130] = { /* Beijing Huaqi Information Digital Technology Co., Ltd */ .name = "Huaqi DLCW-130", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 1, + .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, + .tuner_type = TUNER_ABSENT, /* This is a webcam */ .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, } }, }, [EM2820_BOARD_KWORLD_PVRTV2800RF] = { .name = "Kworld PVR TV 2800 RF", - .is_em2800 = 0, - .vchannels = 2, .tuner_type = TUNER_TEMIC_PAL, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_TERRATEC_CINERGY_250] = { .name = "Terratec Cinergy 250 USB", - .vchannels = 3, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_PINNACLE_USB_2] = { .name = "Pinnacle PCTV USB 2", - .vchannels = 3, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_HAUPPAUGE_WINTV_USB_2] = { .name = "Hauppauge WinTV USB 2", - .vchannels = 3, .tuner_type = TUNER_PHILIPS_FM1236_MK3, .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE| TDA9887_PORT2_ACTIVE, .decoder = EM28XX_TVP5150, .has_msp34xx = 1, - /*FIXME: S-Video not tested */ - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = MSP_INPUT_DEFAULT, @@ -174,327 +231,305 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_DLINK_USB_TV] = { .name = "D-Link DUB-T210 TV Tuner", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, - .is_em2800 = 0, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_HERCULES_SMART_TV_USB2] = { .name = "Hercules Smart TV USB 2.0", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_PINNACLE_USB_2_FM1216ME] = { .name = "Pinnacle PCTV USB 2 (Philips FM1216ME)", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, - .is_em2800 = 0, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_GADMEI_UTV310] = { .name = "Gadmei UTV310", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_TNF_5335MF, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE] = { .name = "Leadtek Winfast USB II Deluxe", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7114, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_TELEVISION, - .vmux = 2, - .amux = 0, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, - .amux = 1, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, - .amux = 1, - } }, - }, - [EM2820_BOARD_PINNACLE_DVC_100] = { - .name = "Pinnacle Dazzle DVC 100", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, - .decoder = EM28XX_SAA7113, - .input = { { - .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = 1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = { - .name = "Videology 20K14XUSB USB2.0", + .name = "Videology 20K14XUSB USB2.0", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 1, - .input = { { + .tuner_type = TUNER_ABSENT, /* This is a webcam */ + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, } }, }, [EM2821_BOARD_PROLINK_PLAYTV_USB2] = { .name = "SIIG AVTuner-PVR/Prolink PlayTV USB 2.0", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, - .is_em2800 = 0, .tuner_type = TUNER_LG_PAL_NEW_TAPC, /* unknown? */ .tda9887_conf = TDA9887_PRESENT, /* unknown? */ - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2821_BOARD_SUPERCOMP_USB_2] = { .name = "Supercomp USB 2.0 TV", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, - .is_em2800 = 0, .tuner_type = TUNER_PHILIPS_FM1236_MK3, .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2821_BOARD_USBGEAR_VD204] = { - .name = "Usbgear VD204v9", + .name = "Usbgear VD204v9", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 2, - .decoder = EM28XX_SAA7113, - .input = { { + .tuner_type = TUNER_ABSENT, /* Capture only device */ + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2860_BOARD_NETGMBH_CAM] = { /* Beijing Huaqi Information Digital Technology Co., Ltd */ - .name = "NetGMBH Cam", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 1, - .input = { { + .name = "NetGMBH Cam", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, /* This is a webcam */ + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, } }, }, [EM2860_BOARD_TYPHOON_DVD_MAKER] = { - .name = "Typhoon DVD Maker", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 2, - .decoder = EM28XX_SAA7113, - .input = { { + .name = "Typhoon DVD Maker", + .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, /* Capture only device */ + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2860_BOARD_GADMEI_UTV330] = { .name = "Gadmei UTV330", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_TNF_5335MF, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2860_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Cinergy A Hybrid XS", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, - .input = { { + + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2861_BOARD_KWORLD_PVRTV_300U] = { .name = "KWorld PVRTV 300U", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2861_BOARD_YAKUMO_MOVIE_MIXER] = { .name = "Yakumo MovieMixer", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 1, + .tuner_type = TUNER_ABSENT, /* Capture only device */ .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2861_BOARD_PLEXTOR_PX_TV100U] = { .name = "Plextor ConvertX PX-TV100U", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_TNF_5335MF, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, + + /* Those boards with em2870 are DVB Only*/ + [EM2870_BOARD_TERRATEC_XS] = { .name = "Terratec Cinergy T XS", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, }, [EM2870_BOARD_TERRATEC_XS_MT2060] = { .name = "Terratec Cinergy T XS (MT2060)", @@ -505,6 +540,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Kworld 350 U DVB-T", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, }, [EM2870_BOARD_KWORLD_355U] = { .name = "Kworld 355 U DVB-T", @@ -514,164 +550,216 @@ struct em28xx_board em28xx_boards[] = { .name = "Pinnacle PCTV DVB-T", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_ABSENT, /* MT2060 */ + /* djh - I have serious doubts this is right... */ + .xclk = EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_10MHZ, }, [EM2870_BOARD_COMPRO_VIDEOMATE] = { .name = "Compro, VideoMate U3", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_ABSENT, /* MT2060 */ }, + [EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = { .name = "Terratec Hybrid XS Secam", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .has_msp34xx = 1, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, } }, }, [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = { .name = "Hauppauge WinTV HVR 900", - .vchannels = 3, .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, - .has_dvb = 1, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = { .name = "Hauppauge WinTV HVR 900 (R2)", - .vchannels = 3, .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + } }, + }, + [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850] = { + .name = "Hauppauge WinTV HVR 850", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = ir_codes_hauppauge_new, + .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 3, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = { .name = "Hauppauge WinTV HVR 950", - .vchannels = 3, - .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, - .has_12mhz_i2s = 1, .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = ir_codes_hauppauge_new, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = { .name = "Pinnacle PCTV HD Pro Stick", - .vchannels = 3, - .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, - .has_12mhz_i2s = 1, .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = ir_codes_pinnacle_pctv_hd, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = { .name = "AMD ATI TV Wonder HD 600", - .vchannels = 3, - .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, - .has_12mhz_i2s = 1, .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = ir_codes_ati_tv_wonder_hd_600, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2880_BOARD_TERRATEC_HYBRID_XS] = { - .name = "Terratec Hybrid XS", - .vchannels = 3, - .tda9887_conf = TDA9887_PRESENT, - .tuner_type = TUNER_XC2028, - .decoder = EM28XX_TVP5150, + .name = "Terratec Hybrid XS", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, .has_dvb = 1, + .dvb_gpio = default_analog, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, } }, }, /* maybe there's a reason behind it why Terratec sells the Hybrid XS @@ -679,172 +767,190 @@ struct em28xx_board em28xx_boards[] = { maybe we'll need it lateron */ [EM2880_BOARD_TERRATEC_PRODIGY_XS] = { .name = "Terratec Prodigy XS", - .vchannels = 3, - .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2820_BOARD_MSI_VOX_USB_2] = { .name = "MSI VOX USB 2.0", - .vchannels = 3, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE, .max_range_640_480 = 1, - - .decoder = EM28XX_SAA7114, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE4, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2800_BOARD_TERRATEC_CINERGY_200] = { .name = "Terratec Cinergy 200 USB", .is_em2800 = 1, - .vchannels = 3, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2800_BOARD_GRABBEEX_USB2800] = { .name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder", .is_em2800 = 1, - .vchannels = 2, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, /* capture only board */ + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2800_BOARD_LEADTEK_WINFAST_USBII] = { .name = "Leadtek Winfast USB II", .is_em2800 = 1, - .vchannels = 3, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2800_BOARD_KWORLD_USB2800] = { .name = "Kworld USB2800", .is_em2800 = 1, - .vchannels = 3, .tuner_type = TUNER_PHILIPS_FCV1236D, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_PINNACLE_DVC_90] = { .name = "Pinnacle Dazzle DVC 90/DVC 100", - .vchannels = 3, - .tuner_type = TUNER_ABSENT, - .decoder = EM28XX_SAA7113, - .input = { { + .tuner_type = TUNER_ABSENT, /* capture only board */ + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2800_BOARD_VGEAR_POCKETTV] = { .name = "V-Gear PocketTV", .is_em2800 = 1, - .vchannels = 3, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2] = { + .name = "Pixelview PlayTV Box 4 USB 2.0", + .tda9887_conf = TDA9887_PRESENT, + .tuner_type = TUNER_YMEC_TVF_5533MF, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + .aout = EM28XX_AOUT_MONO | /* I2S */ + EM28XX_AOUT_MASTER, /* Line out pin */ + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_PROLINK_PLAYTV_USB2] = { .name = "Pixelview Prolink PlayTV USB 2.0", - .vchannels = 3, + .has_snapshot_button = 1, .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_YMEC_TVF_5533MF, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_LINE_IN, + .amux = EM28XX_AMUX_VIDEO, + .aout = EM28XX_AOUT_MONO | /* I2S */ + EM28XX_AOUT_MASTER, /* Line out pin */ }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, @@ -856,228 +962,252 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA] = { - .name = "PointNix Intra-Oral Camera", + .name = "PointNix Intra-Oral Camera", .has_snapshot_button = 1, - .vchannels = 1, - .tda9887_conf = TDA9887_PRESENT, - .tuner_type = TUNER_ABSENT, - .decoder = EM28XX_SAA7113, - .input = { { + .tda9887_conf = TDA9887_PRESENT, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_SAA711X, + .input = { { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, } }, }, [EM2880_BOARD_MSI_DIGIVOX_AD] = { .name = "MSI DigiVox A/D", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = em2880_msi_digivox_ad_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2880_msi_digivox_ad_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2880_msi_digivox_ad_analog, } }, }, [EM2880_BOARD_MSI_DIGIVOX_AD_II] = { .name = "MSI DigiVox A/D II", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = em2880_msi_digivox_ad_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2880_msi_digivox_ad_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2880_msi_digivox_ad_analog, } }, }, [EM2880_BOARD_KWORLD_DVB_305U] = { .name = "KWorld DVB-T 305U", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2880_BOARD_KWORLD_DVB_310U] = { .name = "KWorld DVB-T 310U", - .vchannels = 3, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .has_dvb = 1, + .dvb_gpio = default_digital, .mts_firmware = 1, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_AC97_LINE_IN, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, }, { /* S-video has not been tested yet */ .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_AC97_LINE_IN, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, } }, }, [EM2881_BOARD_DNT_DA2_HYBRID] = { .name = "DNT DA2 Hybrid", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, } }, }, [EM2881_BOARD_PINNACLE_HYBRID_PRO] = { .name = "Pinnacle Hybrid Pro", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, } }, }, [EM2882_BOARD_PINNACLE_HYBRID_PRO] = { .name = "Pinnacle Hybrid Pro (2)", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2882_BOARD_KWORLD_VS_DVBT] = { .name = "Kworld VS-DVB-T 323UR", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2882_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Hybrid XS (em2882)", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2883_BOARD_KWORLD_HYBRID_A316] = { .name = "Kworld PlusTV HD Hybrid 330", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, - .is_em2800 = 0, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, - .input = { { + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = default_digital, + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = { .name = "Compro VideoMate ForYou/Stereo", - .vchannels = 2, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, @@ -1101,7 +1231,7 @@ struct usb_device_id em28xx_id_table [] = { { USB_DEVICE(0xeb1a, 0x2820), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2821), - .driver_info = EM2820_BOARD_PROLINK_PLAYTV_USB2 }, + .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2860), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2861), @@ -1164,8 +1294,8 @@ struct usb_device_id em28xx_id_table [] = { .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, { USB_DEVICE(0x2040, 0x651b), /* RP HVR-950 */ .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, - { USB_DEVICE(0x2040, 0x651f), /* HCW HVR-850 */ - .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, + { USB_DEVICE(0x2040, 0x651f), + .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 }, { USB_DEVICE(0x0438, 0xb002), .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 }, { USB_DEVICE(0x2001, 0xf112), @@ -1189,78 +1319,12 @@ struct usb_device_id em28xx_id_table [] = { MODULE_DEVICE_TABLE(usb, em28xx_id_table); /* - * Reset sequences for analog/digital modes - */ - -/* Reset for the most [analog] boards */ -static struct em28xx_reg_seq default_analog[] = { - {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -/* Reset for the most [digital] boards */ -static struct em28xx_reg_seq default_digital[] = { - {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -/* 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}, -}; - -/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ -static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = { - {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ -static struct em28xx_reg_seq em2880_msi_digivox_ad_digital[] = { - {EM28XX_R08_GPIO, 0x6a, ~EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -/* Board - EM2870 Kworld 355u - Analog - No input analog */ -static struct em28xx_reg_seq em2870_kworld_355u_digital[] = { - {EM2880_R04_GPO, 0x01, 0xff, 10}, - { -1, -1, -1, -1}, -}; - -/* Callback for the most boards */ -static struct em28xx_reg_seq default_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}, -}; - -/* Callback for EM2882 TERRATEC HYBRID XS */ -static struct em28xx_reg_seq em2882_terratec_hybrid_xs_digital[] = { - {EM28XX_R08_GPIO, 0x2e, 0xff, 6}, - {EM28XX_R08_GPIO, 0x3e, ~EM_GPIO_4, 6}, - {EM2880_R04_GPO, 0x04, 0xff, 10}, - {EM2880_R04_GPO, 0x0c, 0xff, 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}, + {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF}, {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, }; @@ -1282,27 +1346,26 @@ int em28xx_tuner_callback(void *ptr, int component, int command, int arg) 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); + rc = em28xx_gpio_set(dev, dev->board.tuner_gpio); return rc; } EXPORT_SYMBOL_GPL(em28xx_tuner_callback); -static void em28xx_set_model(struct em28xx *dev) +static void inline 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; - dev->has_snapshot_button = em28xx_boards[dev->model].has_snapshot_button; - dev->valid = em28xx_boards[dev->model].valid; + memcpy(&dev->board, &em28xx_boards[dev->model], sizeof(dev->board)); + + /* Those are the default values for the majority of boards + Use those values if not specified otherwise at boards entry + */ + if (!dev->board.xclk) + dev->board.xclk = EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ; + + if (!dev->board.i2c_speed) + dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ; } /* Since em28xx_pre_card_setup() requires a proper dev->model, @@ -1312,205 +1375,126 @@ 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; + em28xx_set_model(dev); + + em28xx_info("Identified as %s (card=%d)\n", + dev->board.name, dev->model); + + /* Set the default GPO/GPIO for legacy devices */ + dev->reg_gpo_num = EM2880_R04_GPO; + dev->reg_gpio_num = EM28XX_R08_GPIO; dev->wait_after_write = 5; + + /* Based on the Chip ID, set the device configuration */ rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); if (rc > 0) { - switch (rc) { + dev->chip_id = rc; + + switch (dev->chip_id) { + case CHIP_ID_EM2750: + em28xx_info("chip ID is em2750\n"); + break; + case CHIP_ID_EM2820: + em28xx_info("chip ID is em2820\n"); + break; + case CHIP_ID_EM2840: + em28xx_info("chip ID is em2840\n"); + break; case CHIP_ID_EM2860: em28xx_info("chip ID is em2860\n"); break; + case CHIP_ID_EM2870: + em28xx_info("chip ID is em2870\n"); + dev->wait_after_write = 0; + break; + case CHIP_ID_EM2874: + em28xx_info("chip ID is em2874\n"); + dev->reg_gpio_num = EM2874_R80_GPIO; + dev->wait_after_write = 0; + break; case CHIP_ID_EM2883: em28xx_info("chip ID is em2882/em2883\n"); dev->wait_after_write = 0; break; default: - em28xx_info("em28xx chip ID = %d\n", rc); + em28xx_info("em28xx chip ID = %d\n", dev->chip_id); } } - 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_900_R2: - case EM2860_BOARD_TERRATEC_HYBRID_XS: - case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: - case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: - case EM2882_BOARD_PINNACLE_HYBRID_PRO: - case EM2883_BOARD_KWORLD_HYBRID_A316: - case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); - msleep(50); - - /* 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 = default_callback; - dev->tun_digital_gpio = default_callback; - break; - - case EM2882_BOARD_TERRATEC_HYBRID_XS: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); - msleep(50); - - /* should be added ir_codes here */ - - /* 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 = default_callback; - dev->tun_digital_gpio = em2882_terratec_hybrid_xs_digital; - break; - - case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: - case EM2880_BOARD_TERRATEC_HYBRID_XS: - case EM2870_BOARD_TERRATEC_XS: - case EM2881_BOARD_PINNACLE_HYBRID_PRO: - case EM2880_BOARD_KWORLD_DVB_310U: - case EM2870_BOARD_KWORLD_350U: - case EM2881_BOARD_DNT_DA2_HYBRID: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); - msleep(50); - - /* NOTE: EM2881_DNT_DA2_HYBRID spend 140 msleep for digital - and analog commands. If this commands doesn't work, - add this timer. */ - - /* Sets GPO/GPIO sequences for this device */ - dev->analog_gpio = default_analog; - dev->digital_gpio = default_digital; - dev->tun_analog_gpio = default_callback; - dev->tun_digital_gpio = default_callback; - break; - case EM2880_BOARD_MSI_DIGIVOX_AD: - case EM2880_BOARD_MSI_DIGIVOX_AD_II: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); - msleep(50); - - /* Sets GPO/GPIO sequences for this device */ - dev->analog_gpio = em2880_msi_digivox_ad_analog; - dev->digital_gpio = em2880_msi_digivox_ad_digital; - dev->tun_analog_gpio = default_callback; - dev->tun_digital_gpio = default_callback; - break; + /* Prepopulate cached GPO register content */ + rc = em28xx_read_reg(dev, dev->reg_gpo_num); + if (rc >= 0) + dev->reg_gpo = rc; - case EM2750_BOARD_UNKNOWN: - case EM2750_BOARD_DLCW_130: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x0a", 1); - break; + /* Set the initial XCLK and I2C clock values based on the board + definition */ + em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); + msleep(50); + /* request some modules */ + switch (dev->model) { case EM2861_BOARD_PLEXTOR_PX_TV100U: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); /* FIXME guess */ /* Turn on analog audio output */ - em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); break; - case EM2861_BOARD_KWORLD_PVRTV_300U: case EM2880_BOARD_KWORLD_DVB_305U: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x4c", 1); - msleep(10); - em28xx_write_regs(dev, 0x08, "\x6d", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d); msleep(10); - em28xx_write_regs(dev, 0x08, "\x7d", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x7d); msleep(10); break; - - case EM2870_BOARD_KWORLD_355U: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); - msleep(50); - - /* Sets GPO/GPIO sequences for this device */ - dev->digital_gpio = em2870_kworld_355u_digital; - break; - case EM2870_BOARD_COMPRO_VIDEOMATE: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); /* TODO: someone can do some cleanup here... not everything's needed */ - em28xx_write_regs(dev, 0x04, "\x00", 1); + em28xx_write_reg(dev, EM2880_R04_GPO, 0x00); msleep(10); - em28xx_write_regs(dev, 0x04, "\x01", 1); + em28xx_write_reg(dev, EM2880_R04_GPO, 0x01); msleep(10); - em28xx_write_regs(dev, 0x08, "\xfd", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); mdelay(70); - em28xx_write_regs(dev, 0x08, "\xfc", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc); mdelay(70); - em28xx_write_regs(dev, 0x08, "\xdc", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xdc); mdelay(70); - em28xx_write_regs(dev, 0x08, "\xfc", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc); mdelay(70); break; - case EM2870_BOARD_TERRATEC_XS_MT2060: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); /* this device needs some gpio writes to get the DVB-T demod work */ - em28xx_write_regs(dev, 0x08, "\xfe", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); mdelay(70); - em28xx_write_regs(dev, 0x08, "\xde", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde); mdelay(70); - dev->em28xx_write_regs(dev, 0x08, "\xfe", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); mdelay(70); break; - case EM2870_BOARD_PINNACLE_PCTV_DVB: - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); /* this device needs some gpio writes to get the DVB-T demod work */ - em28xx_write_regs(dev, 0x08, "\xfe", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); mdelay(70); - em28xx_write_regs(dev, 0x08, "\xde", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde); mdelay(70); - em28xx_write_regs(dev, 0x08, "\xfe", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); mdelay(70); - /* switch em2880 rc protocol */ - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x22", 1); - /* should be added ir_codes here */ break; - case EM2820_BOARD_GADMEI_UTV310: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); - /* Turn on analog audio output */ - em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1); - break; - - case EM2860_BOARD_GADMEI_UTV330: - /* Turn on IR */ - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); - /* should be added ir_codes here */ - break; - case EM2820_BOARD_MSI_VOX_USB_2: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); - /* enables audio for that device */ - em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1); + /* enables audio for that devices */ + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); break; } - em28xx_gpio_set(dev, dev->tun_analog_gpio); + em28xx_gpio_set(dev, dev->board.tuner_gpio); em28xx_set_mode(dev, EM28XX_ANALOG_MODE); /* Unlock device */ - em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + em28xx_set_mode(dev, EM28XX_SUSPEND); } static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) @@ -1536,6 +1520,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) ctl->demod = XC3028_FE_DEFAULT; ctl->fname = XC3028L_DEFAULT_FIRMWARE; break; + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: /* FIXME: Better to specify the needed IF */ @@ -1712,12 +1697,15 @@ void em28xx_card_setup(struct em28xx *dev) em28xx_set_model(dev); dev->tuner_type = em28xx_boards[dev->model].tuner_type; + if (em28xx_boards[dev->model].tuner_addr) + dev->tuner_addr = em28xx_boards[dev->model].tuner_addr; /* request some modules */ switch (dev->model) { case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: { struct tveeprom tv; @@ -1733,7 +1721,7 @@ void em28xx_card_setup(struct em28xx *dev) if (tv.audio_processor == V4L2_IDENT_MSPX4XX) { dev->i2s_speed = 2048000; - dev->has_msp34xx = 1; + dev->board.has_msp34xx = 1; } #ifdef CONFIG_MODULES if (tv.has_ir) @@ -1743,7 +1731,7 @@ void em28xx_card_setup(struct em28xx *dev) } case EM2820_BOARD_KWORLD_PVRTV2800RF: /* GPIO enables sound on KWORLD PVR TV 2800RF */ - em28xx_write_regs_req(dev, 0x00, 0x08, "\xf9", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf9); break; case EM2820_BOARD_UNKNOWN: case EM2800_BOARD_UNKNOWN: @@ -1766,10 +1754,10 @@ void em28xx_card_setup(struct em28xx *dev) break; } - if (dev->has_snapshot_button) + if (dev->board.has_snapshot_button) em28xx_register_snapshot_button(dev); - if (dev->valid == EM28XX_BOARD_NOT_VALIDATED) { + if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) { em28xx_errdev("\n\n"); em28xx_errdev("The support for this board weren't " "valid yet.\n"); @@ -1784,15 +1772,433 @@ void em28xx_card_setup(struct em28xx *dev) #ifdef CONFIG_MODULES /* request some modules */ - if (dev->has_msp34xx) + if (dev->board.has_msp34xx) request_module("msp3400"); - if (dev->decoder == EM28XX_SAA7113 || dev->decoder == EM28XX_SAA7114) + if (dev->board.decoder == EM28XX_SAA711X) request_module("saa7115"); - if (dev->decoder == EM28XX_TVP5150) + if (dev->board.decoder == EM28XX_TVP5150) request_module("tvp5150"); - if (dev->tuner_type != TUNER_ABSENT) + if (dev->board.tuner_type != TUNER_ABSENT) request_module("tuner"); #endif em28xx_config_tuner(dev); + + em28xx_ir_init(dev); +} + + +#if defined(CONFIG_MODULES) && defined(MODULE) +static void request_module_async(struct work_struct *work) +{ + struct em28xx *dev = container_of(work, + struct em28xx, request_module_wk); + + if (dev->has_audio_class) + request_module("snd-usb-audio"); + else if (dev->has_alsa_audio) + request_module("em28xx-alsa"); + + if (dev->board.has_dvb) + request_module("em28xx-dvb"); +} + +static void request_modules(struct em28xx *dev) +{ + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); +} +#else +#define request_modules(dev) +#endif /* CONFIG_MODULES */ + +/* + * em28xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +void em28xx_release_resources(struct em28xx *dev) +{ + if (dev->sbutton_input_dev) + em28xx_deregister_snapshot_button(dev); + + if (dev->ir) + em28xx_ir_fini(dev); + + /*FIXME: I2C IR should be disconnected */ + + em28xx_release_analog_resources(dev); + + em28xx_remove_from_devlist(dev); + + em28xx_i2c_unregister(dev); + usb_put_dev(dev->udev); + + /* Mark device as unused */ + em28xx_devused &= ~(1 << dev->devno); +}; + +/* + * em28xx_init_dev() + * allocates and inits the device structs, registers i2c bus and v4l device + */ +int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, + int minor) +{ + struct em28xx *dev = *devhandle; + int retval = -ENOMEM; + int errCode; + + dev->udev = udev; + mutex_init(&dev->ctrl_urb_lock); + spin_lock_init(&dev->slock); + init_waitqueue_head(&dev->open); + init_waitqueue_head(&dev->wait_frame); + init_waitqueue_head(&dev->wait_stream); + + dev->em28xx_write_regs = em28xx_write_regs; + dev->em28xx_read_reg = em28xx_read_reg; + dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len; + dev->em28xx_write_regs_req = em28xx_write_regs_req; + dev->em28xx_read_reg_req = em28xx_read_reg_req; + dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800; + + em28xx_pre_card_setup(dev); + + if (!dev->board.is_em2800) { + /* Sets I2C speed to 100 KHz */ + retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); + if (retval < 0) { + em28xx_errdev("%s: em28xx_write_regs_req failed!" + " retval [%d]\n", + __func__, retval); + return retval; + } + } + + /* register i2c bus */ + errCode = em28xx_i2c_register(dev); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* Do board specific init and eeprom reading */ + em28xx_card_setup(dev); + + /* Configure audio */ + errCode = em28xx_audio_setup(dev); + if (errCode < 0) { + em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n", + __func__, errCode); + } + + /* wake i2c devices */ + em28xx_wake_i2c(dev); + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vidq.queued); + + + if (dev->board.has_msp34xx) { + /* Send a reset to other chips via gpio */ + errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_write_regs_req - " + "msp34xx(1) failed! errCode [%d]\n", + __func__, errCode); + return errCode; + } + msleep(3); + + errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_write_regs_req - " + "msp34xx(2) failed! errCode [%d]\n", + __func__, errCode); + return errCode; + } + msleep(3); + } + + em28xx_add_into_devlist(dev); + + retval = em28xx_register_analog_devices(dev); + if (retval < 0) { + em28xx_release_resources(dev); + goto fail_reg_devices; + } + + em28xx_init_extension(dev); + + /* Save some power by putting tuner to sleep */ + em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); + + return 0; + +fail_reg_devices: + return retval; +} + +/* + * em28xx_usb_probe() + * checks for supported devices + */ +static int em28xx_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + const struct usb_endpoint_descriptor *endpoint; + struct usb_device *udev; + struct usb_interface *uif; + struct em28xx *dev = NULL; + int retval = -ENODEV; + int i, nr, ifnum, isoc_pipe; + char *speed; + char descr[255] = ""; + + udev = usb_get_dev(interface_to_usbdev(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; + + /* 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", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + ifnum, + interface->altsetting[0].desc.bInterfaceClass); + + em28xx_devused &= ~(1<<nr); + return -ENODEV; + } + + endpoint = &interface->cur_altsetting->endpoint[0].desc; + + /* check if the device has the iso in endpoint at the correct place */ + if (usb_endpoint_xfer_isoc(endpoint) + && + (interface->altsetting[1].endpoint[0].desc.wMaxPacketSize == 940)) { + /* It's a newer em2874/em2875 device */ + isoc_pipe = 0; + } else { + int check_interface = 1; + isoc_pipe = 1; + endpoint = &interface->cur_altsetting->endpoint[1].desc; + if (usb_endpoint_type(endpoint) != + USB_ENDPOINT_XFER_ISOC) + check_interface = 0; + + if (usb_endpoint_dir_out(endpoint)) + check_interface = 0; + + if (!check_interface) { + em28xx_err(DRIVER_NAME " video device (%04x:%04x): " + "interface %i, class %i found.\n", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + ifnum, + interface->altsetting[0].desc.bInterfaceClass); + + em28xx_err(DRIVER_NAME " This is an anciliary " + "interface not used by the driver\n"); + + em28xx_devused &= ~(1<<nr); + return -ENODEV; + } + } + + switch (udev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; + } + + if (udev->manufacturer) + strlcpy(descr, udev->manufacturer, sizeof(descr)); + + if (udev->product) { + if (*descr) + strlcat(descr, " ", sizeof(descr)); + strlcat(descr, udev->product, sizeof(descr)); + } + if (*descr) + strlcat(descr, " ", sizeof(descr)); + + printk(DRIVER_NAME ": New device %s@ %s Mbps " + "(%04x:%04x, interface %d, class %d)\n", + descr, + speed, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + ifnum, + interface->altsetting->desc.bInterfaceNumber); + + if (nr >= EM28XX_MAXBOARDS) { + printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", + EM28XX_MAXBOARDS); + em28xx_devused &= ~(1<<nr); + return -ENOMEM; + } + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + em28xx_err(DRIVER_NAME ": out of memory!\n"); + em28xx_devused &= ~(1<<nr); + return -ENOMEM; + } + + snprintf(dev->name, 29, "em28xx #%d", nr); + dev->devno = nr; + dev->model = id->driver_info; + dev->alt = -1; + + /* Checks if audio is provided by some interface */ + for (i = 0; i < udev->config->desc.bNumInterfaces; i++) { + uif = udev->config->interface[i]; + if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { + dev->has_audio_class = 1; + break; + } + } + + /* compute alternate max packet sizes */ + uif = udev->actconfig->interface[0]; + + dev->num_alt = uif->num_altsetting; + 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); + kfree(dev); + return -ENOMEM; + } + + for (i = 0; i < dev->num_alt ; i++) { + u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize); + dev->alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + } + + if ((card[nr] >= 0) && (card[nr] < em28xx_bcount)) + dev->model = card[nr]; + + /* allocate device struct */ + mutex_init(&dev->lock); + mutex_lock(&dev->lock); + retval = em28xx_init_dev(&dev, udev, nr); + if (retval) { + em28xx_devused &= ~(1<<dev->devno); + kfree(dev); + + return retval; + } + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + request_modules(dev); + + /* Should be the last thing to do, to avoid newer udev's to + open the device before fully initializing it + */ + mutex_unlock(&dev->lock); + + return 0; +} + +/* + * em28xx_usb_disconnect() + * called when the device gets diconencted + * video device will be unregistered on v4l2_close in case it is still open + */ +static void em28xx_usb_disconnect(struct usb_interface *interface) +{ + struct em28xx *dev; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + if (!dev) + return; + + em28xx_info("disconnecting %s\n", dev->vdev->name); + + /* wait until all current v4l2 io is finished then deallocate + resources */ + mutex_lock(&dev->lock); + + wake_up_interruptible_all(&dev->open); + + if (dev->users) { + em28xx_warn + ("device /dev/video%d is open! Deregistration and memory " + "deallocation are deferred on close.\n", + dev->vdev->num); + + dev->state |= DEV_MISCONFIGURED; + em28xx_uninit_isoc(dev); + dev->state |= DEV_DISCONNECTED; + wake_up_interruptible(&dev->wait_frame); + wake_up_interruptible(&dev->wait_stream); + } else { + dev->state |= DEV_DISCONNECTED; + em28xx_release_resources(dev); + } + + em28xx_close_extension(dev); + + mutex_unlock(&dev->lock); + + if (!dev->users) { + kfree(dev->alt_max_pkt_size); + kfree(dev); + } } + +static struct usb_driver em28xx_usb_driver = { + .name = "em28xx", + .probe = em28xx_usb_probe, + .disconnect = em28xx_usb_disconnect, + .id_table = em28xx_id_table, +}; + +static int __init em28xx_module_init(void) +{ + int result; + + /* register this driver with the USB subsystem */ + result = usb_register(&em28xx_usb_driver); + if (result) + em28xx_err(DRIVER_NAME + " usb_register failed. Error number %d.\n", result); + + printk(KERN_INFO DRIVER_NAME " driver loaded\n"); + + return result; +} + +static void __exit em28xx_module_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&em28xx_usb_driver); +} + +module_init(em28xx_module_init); +module_exit(em28xx_module_exit); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 15e2b52..f850451 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -26,6 +26,7 @@ #include <linux/module.h> #include <linux/usb.h> #include <linux/vmalloc.h> +#include <media/v4l2-common.h> #include "em28xx.h" @@ -66,7 +67,8 @@ MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, char *buf, int len) { - int ret, byte; + int ret; + int pipe = usb_rcvctrlpipe(dev->udev, 0); if (dev->state & DEV_DISCONNECTED) return -ENODEV; @@ -74,10 +76,18 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, if (len > URB_MAX_CTRL_SIZE) return -EINVAL; - em28xx_regdbg("req=%02x, reg=%02x ", req, reg); + if (reg_debug) { + printk( KERN_DEBUG "(pipe 0x%08x): " + "IN: %02x %02x %02x %02x %02x %02x %02x %02x ", + pipe, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, 0, + reg & 0xff, reg >> 8, + len & 0xff, len >> 8); + } mutex_lock(&dev->ctrl_urb_lock); - ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), req, + ret = usb_control_msg(dev->udev, pipe, req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0000, reg, dev->urb_buf, len, HZ); if (ret < 0) { @@ -93,7 +103,9 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, mutex_unlock(&dev->ctrl_urb_lock); if (reg_debug) { - printk("%02x values: ", ret); + int byte; + + printk("<<<"); for (byte = 0; byte < len; byte++) printk(" %02x", (unsigned char)buf[byte]); printk("\n"); @@ -108,28 +120,12 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, */ int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg) { - u8 val; int ret; + u8 val; - if (dev->state & DEV_DISCONNECTED) - return(-ENODEV); - - em28xx_regdbg("req=%02x, reg=%02x:", req, reg); - - mutex_lock(&dev->ctrl_urb_lock); - ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0000, reg, dev->urb_buf, 1, HZ); - val = dev->urb_buf[0]; - mutex_unlock(&dev->ctrl_urb_lock); - - if (ret < 0) { - printk(" failed!\n"); + ret = em28xx_read_reg_req_len(dev, req, reg, &val, 1); + if (ret < 0) return ret; - } - - if (reg_debug) - printk("%02x\n", (unsigned char) val); return val; } @@ -147,6 +143,7 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, int len) { int ret; + int pipe = usb_sndctrlpipe(dev->udev, 0); if (dev->state & DEV_DISCONNECTED) return -ENODEV; @@ -154,17 +151,25 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, if ((len < 1) || (len > URB_MAX_CTRL_SIZE)) return -EINVAL; - em28xx_regdbg("req=%02x reg=%02x:", req, reg); if (reg_debug) { - int i; - for (i = 0; i < len; ++i) - printk(" %02x", (unsigned char)buf[i]); + int byte; + + printk( KERN_DEBUG "(pipe 0x%08x): " + "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>", + pipe, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, 0, + reg & 0xff, reg >> 8, + len & 0xff, len >> 8); + + for (byte = 0; byte < len; byte++) + printk(" %02x", (unsigned char)buf[byte]); printk("\n"); } mutex_lock(&dev->ctrl_urb_lock); memcpy(dev->urb_buf, buf, len); - ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), req, + ret = usb_control_msg(dev->udev, pipe, req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0000, reg, dev->urb_buf, len, HZ); mutex_unlock(&dev->ctrl_urb_lock); @@ -187,15 +192,21 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len) Not sure what happens on reading GPO register. */ if (rc >= 0) { - if (reg == EM2880_R04_GPO) + if (reg == dev->reg_gpo_num) dev->reg_gpo = buf[0]; - else if (reg == EM28XX_R08_GPIO) + else if (reg == dev->reg_gpio_num) dev->reg_gpio = buf[0]; } return rc; } +/* Write a single register */ +int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val) +{ + return em28xx_write_regs(dev, reg, &val, 1); +} + /* * em28xx_write_reg_bits() * sets only some bits (specified by bitmask) of a register, by first reading @@ -208,9 +219,9 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, u8 newval; /* Uses cache for gpo/gpio registers */ - if (reg == EM2880_R04_GPO) + if (reg == dev->reg_gpo_num) oldval = dev->reg_gpo; - else if (reg == EM28XX_R08_GPIO) + else if (reg == dev->reg_gpio_num) oldval = dev->reg_gpio; else oldval = em28xx_read_reg(dev, reg); @@ -224,15 +235,70 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, } /* + * em28xx_is_ac97_ready() + * Checks if ac97 is ready + */ +static int em28xx_is_ac97_ready(struct em28xx *dev) +{ + int ret, i; + + /* Wait up to 50 ms for AC97 command to complete */ + for (i = 0; i < 10; i++, msleep(5)) { + ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY); + if (ret < 0) + return ret; + + if (!(ret & 0x01)) + return 0; + } + + em28xx_warn("AC97 command still being executed: not handled properly!\n"); + return -EBUSY; +} + +/* + * em28xx_read_ac97() + * write a 16 bit value to the specified AC97 address (LSB first!) + */ +int em28xx_read_ac97(struct em28xx *dev, u8 reg) +{ + int ret; + u8 addr = (reg & 0x7f) | 0x80; + u16 val; + + ret = em28xx_is_ac97_ready(dev); + if (ret < 0) + return ret; + + ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1); + if (ret < 0) + return ret; + + ret = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R40_AC97LSB, + (u8 *)&val, sizeof(val)); + + if (ret < 0) + return ret; + return le16_to_cpu(val); +} + +/* * em28xx_write_ac97() * write a 16 bit value to the specified AC97 address (LSB first!) */ -static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val) +int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val) { - int ret, i; + int ret; u8 addr = reg & 0x7f; + __le16 value; - ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, val, 2); + value = cpu_to_le16(val); + + ret = em28xx_is_ac97_ready(dev); + if (ret < 0) + return ret; + + ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, (u8 *) &value, 2); if (ret < 0) return ret; @@ -240,58 +306,74 @@ static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val) if (ret < 0) return ret; - /* Wait up to 50 ms for AC97 command to complete */ - for (i = 0; i < 10; i++) { - ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY); - if (ret < 0) - return ret; + return 0; +} - if (!(ret & 0x01)) - return 0; - msleep(5); +struct em28xx_vol_table { + enum em28xx_amux mux; + u8 reg; +}; + +static struct em28xx_vol_table inputs[] = { + { EM28XX_AMUX_VIDEO, AC97_VIDEO_VOL }, + { EM28XX_AMUX_LINE_IN, AC97_LINEIN_VOL }, + { EM28XX_AMUX_PHONE, AC97_PHONE_VOL }, + { EM28XX_AMUX_MIC, AC97_MIC_VOL }, + { EM28XX_AMUX_CD, AC97_CD_VOL }, + { EM28XX_AMUX_AUX, AC97_AUX_VOL }, + { EM28XX_AMUX_PCM_OUT, AC97_PCM_OUT_VOL }, +}; + +static int set_ac97_input(struct em28xx *dev) +{ + int ret, i; + enum em28xx_amux amux = dev->ctl_ainput; + + /* EM28XX_AMUX_VIDEO2 is a special case used to indicate that + em28xx should point to LINE IN, while AC97 should use VIDEO + */ + if (amux == EM28XX_AMUX_VIDEO2) + amux = EM28XX_AMUX_VIDEO; + + /* Mute all entres but the one that were selected */ + for (i = 0; i < ARRAY_SIZE(inputs); i++) { + if (amux == inputs[i].mux) + ret = em28xx_write_ac97(dev, inputs[i].reg, 0x0808); + else + ret = em28xx_write_ac97(dev, inputs[i].reg, 0x8000); + + if (ret < 0) + em28xx_warn("couldn't setup AC97 register %d\n", + inputs[i].reg); } - em28xx_warn("AC97 command still being executed: not handled properly!\n"); return 0; } static int em28xx_set_audio_source(struct em28xx *dev) { - static char *enable = "\x08\x08"; - static char *disable = "\x08\x88"; - char *video = enable, *line = disable; int ret; u8 input; - if (dev->is_em2800) { - if (dev->ctl_ainput) - input = EM2800_AUDIO_SRC_LINE; - else + if (dev->board.is_em2800) { + if (dev->ctl_ainput == EM28XX_AMUX_VIDEO) input = EM2800_AUDIO_SRC_TUNER; + else + input = EM2800_AUDIO_SRC_LINE; ret = em28xx_write_regs(dev, EM2800_R08_AUDIOSRC, &input, 1); if (ret < 0) return ret; } - if (dev->has_msp34xx) + if (dev->board.has_msp34xx) input = EM28XX_AUDIO_SRC_TUNER; else { switch (dev->ctl_ainput) { case EM28XX_AMUX_VIDEO: input = EM28XX_AUDIO_SRC_TUNER; break; - case EM28XX_AMUX_LINE_IN: + default: input = EM28XX_AUDIO_SRC_LINE; - video = disable; - line = enable; - break; - case EM28XX_AMUX_AC97_VIDEO: - input = EM28XX_AUDIO_SRC_LINE; - break; - case EM28XX_AMUX_AC97_LINE_IN: - input = EM28XX_AUDIO_SRC_LINE; - video = disable; - line = enable; break; } } @@ -301,41 +383,50 @@ static int em28xx_set_audio_source(struct em28xx *dev) return ret; msleep(5); - /* Sets AC97 mixer registers - This is seems to be needed, even for non-ac97 configs - */ - ret = em28xx_write_ac97(dev, EM28XX_R14_VIDEO_AC97, video); - if (ret < 0) - return ret; - - ret = em28xx_write_ac97(dev, EM28XX_R10_LINE_IN_AC97, line); + switch (dev->audio_mode.ac97) { + case EM28XX_NO_AC97: + break; + default: + ret = set_ac97_input(dev); + } return ret; } +struct em28xx_vol_table outputs[] = { + { EM28XX_AOUT_MASTER, AC97_MASTER_VOL }, + { EM28XX_AOUT_LINE, AC97_LINE_LEVEL_VOL }, + { EM28XX_AOUT_MONO, AC97_MASTER_MONO_VOL }, + { EM28XX_AOUT_LFE, AC97_LFE_MASTER_VOL }, + { EM28XX_AOUT_SURR, AC97_SURR_MASTER_VOL }, +}; + int em28xx_audio_analog_set(struct em28xx *dev) { - int ret; - char s[2] = { 0x00, 0x00 }; - u8 xclk = 0x07; - - s[0] |= 0x1f - dev->volume; - s[1] |= 0x1f - dev->volume; - - /* Mute */ - s[1] |= 0x80; - ret = em28xx_write_ac97(dev, EM28XX_R02_MASTER_AC97, s); + int ret, i; + u8 xclk; - if (ret < 0) - return ret; + if (!dev->audio_mode.has_audio) + return 0; - if (dev->has_12mhz_i2s) - xclk |= 0x20; + /* It is assumed that all devices use master volume for output. + It would be possible to use also line output. + */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + /* Mute all outputs */ + for (i = 0; i < ARRAY_SIZE(outputs); i++) { + ret = em28xx_write_ac97(dev, outputs[i].reg, 0x8000); + if (ret < 0) + em28xx_warn("couldn't setup AC97 register %d\n", + outputs[i].reg); + } + } + xclk = dev->board.xclk & 0x7f; if (!dev->mute) xclk |= 0x80; - ret = em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, xclk, 0xa7); + ret = em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk); if (ret < 0) return ret; msleep(10); @@ -343,36 +434,169 @@ int em28xx_audio_analog_set(struct em28xx *dev) /* Selects the proper audio input */ ret = em28xx_set_audio_source(dev); - /* Unmute device */ - if (!dev->mute) - s[1] &= ~0x80; - ret = em28xx_write_ac97(dev, EM28XX_R02_MASTER_AC97, s); + /* Sets volume */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + int vol; + + /* LSB: left channel - both channels with the same level */ + vol = (0x1f - dev->volume) | ((0x1f - dev->volume) << 8); + + /* Mute device, if needed */ + if (dev->mute) + vol |= 0x8000; + + /* Sets volume */ + for (i = 0; i < ARRAY_SIZE(outputs); i++) { + if (dev->ctl_aoutput & outputs[i].mux) + ret = em28xx_write_ac97(dev, outputs[i].reg, + vol); + if (ret < 0) + em28xx_warn("couldn't setup AC97 register %d\n", + outputs[i].reg); + } + } return ret; } EXPORT_SYMBOL_GPL(em28xx_audio_analog_set); -int em28xx_colorlevels_set_default(struct em28xx *dev) +int em28xx_audio_setup(struct em28xx *dev) { - 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); + int vid1, vid2, feat, cfg; + u32 vid; + + if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874) { + /* Digital only device - don't load any alsa module */ + dev->audio_mode.has_audio = 0; + dev->has_audio_class = 0; + dev->has_alsa_audio = 0; + return 0; + } + + /* If device doesn't support Usb Audio Class, use vendor class */ + if (!dev->has_audio_class) + dev->has_alsa_audio = 1; + + dev->audio_mode.has_audio = 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); + /* See how this device is configured */ + cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG); + if (cfg < 0) + cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */ + else + em28xx_info("Config register raw data: 0x%02x\n", cfg); + + if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == + EM28XX_CHIPCFG_I2S_3_SAMPRATES) { + em28xx_info("I2S Audio (3 sample rates)\n"); + dev->audio_mode.i2s_3rates = 1; + } + if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == + EM28XX_CHIPCFG_I2S_5_SAMPRATES) { + em28xx_info("I2S Audio (5 sample rates)\n"); + dev->audio_mode.i2s_5rates = 1; + } + + if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) != EM28XX_CHIPCFG_AC97) { + /* Skip the code that does AC97 vendor detection */ + dev->audio_mode.ac97 = EM28XX_NO_AC97; + goto init_audio; + } + + dev->audio_mode.ac97 = EM28XX_AC97_OTHER; + + vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1); + if (vid1 < 0) { + /* Device likely doesn't support AC97 */ + em28xx_warn("AC97 chip type couldn't be determined\n"); + goto init_audio; + } + + vid2 = em28xx_read_ac97(dev, AC97_VENDOR_ID2); + if (vid2 < 0) + goto init_audio; + + vid = vid1 << 16 | vid2; + + dev->audio_mode.ac97_vendor_id = vid; + em28xx_warn("AC97 vendor ID = 0x%08x\n", vid); + + feat = em28xx_read_ac97(dev, AC97_RESET); + if (feat < 0) + goto init_audio; + + dev->audio_mode.ac97_feat = feat; + em28xx_warn("AC97 features = 0x%04x\n", feat); + + /* Try to identify what audio processor we have */ + if ((vid == 0xffffffff) && (feat == 0x6a90)) + dev->audio_mode.ac97 = EM28XX_AC97_EM202; + else if ((vid >> 8) == 0x838476) + dev->audio_mode.ac97 = EM28XX_AC97_SIGMATEL; + +init_audio: + /* Reports detected AC97 processor */ + switch (dev->audio_mode.ac97) { + case EM28XX_NO_AC97: + em28xx_info("No AC97 audio processor\n"); + break; + case EM28XX_AC97_EM202: + em28xx_info("Empia 202 AC97 audio processor detected\n"); + break; + case EM28XX_AC97_SIGMATEL: + em28xx_info("Sigmatel audio processor detected(stac 97%02x)\n", + dev->audio_mode.ac97_vendor_id & 0xff); + break; + case EM28XX_AC97_OTHER: + em28xx_warn("Unknown AC97 audio processor detected!\n"); + break; + default: + break; + } + + return em28xx_audio_analog_set(dev); +} +EXPORT_SYMBOL_GPL(em28xx_audio_setup); + +int em28xx_colorlevels_set_default(struct em28xx *dev) +{ + em28xx_write_reg(dev, EM28XX_R20_YGAIN, 0x10); /* contrast */ + em28xx_write_reg(dev, EM28XX_R21_YOFFSET, 0x00); /* brightness */ + em28xx_write_reg(dev, EM28XX_R22_UVGAIN, 0x10); /* saturation */ + em28xx_write_reg(dev, EM28XX_R23_UOFFSET, 0x00); + em28xx_write_reg(dev, EM28XX_R24_VOFFSET, 0x00); + em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, 0x00); + + em28xx_write_reg(dev, EM28XX_R14_GAMMA, 0x20); + em28xx_write_reg(dev, EM28XX_R15_RGAIN, 0x20); + em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20); + em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20); + em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00); + em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00); + return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00); } int em28xx_capture_start(struct em28xx *dev, int start) { int rc; + + if (dev->chip_id == CHIP_ID_EM2874) { + /* The Transport Stream Enable Register moved in em2874 */ + if (!start) { + rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, + 0x00, + EM2874_TS1_CAPTURE_ENABLE); + return rc; + } + + /* Enable Transport Stream */ + rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, + EM2874_TS1_CAPTURE_ENABLE, + EM2874_TS1_CAPTURE_ENABLE); + return rc; + } + + /* FIXME: which is the best order? */ /* video registers are sampled by VREF */ rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP, @@ -382,28 +606,37 @@ int em28xx_capture_start(struct em28xx *dev, int start) if (!start) { /* disable video capture */ - rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x27", 1); + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27); return rc; } /* enable video capture */ - rc = em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); + rc = em28xx_write_reg(dev, 0x48, 0x00); if (dev->mode == EM28XX_ANALOG_MODE) - rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x67", 1); + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67); else - rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x37", 1); + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37); msleep(6); return rc; } -int em28xx_outfmt_set_yuv422(struct em28xx *dev) +int em28xx_set_outfmt(struct em28xx *dev) { - 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); + int ret; + + ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT, + dev->format->reg | 0x20, 0x3f); + if (ret < 0) + return ret; + + ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, 0x10); + if (ret < 0) + return ret; + + return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x11); } static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, @@ -440,7 +673,7 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) { u8 mode; /* the em2800 scaler only supports scaling down to 50% */ - if (dev->is_em2800) + if (dev->board.is_em2800) mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00); else { u8 buf[2]; @@ -464,7 +697,7 @@ int em28xx_resolution_set(struct em28xx *dev) width = norm_maxw(dev); height = norm_maxh(dev) >> 1; - em28xx_outfmt_set_yuv422(dev); + em28xx_set_outfmt(dev); em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); return em28xx_scaler_set(dev, dev->hscale, dev->vscale); @@ -519,12 +752,14 @@ int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio) 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); + if (dev->mode != EM28XX_SUSPEND) { + em28xx_write_reg(dev, 0x48, 0x00); + if (dev->mode == EM28XX_ANALOG_MODE) + em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67); + else + em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37); + msleep(6); + } /* Send GPIO reset sequences specified at board entry */ while (gpio->sleep >= 0) { @@ -549,17 +784,20 @@ 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) { + if (set_mode == EM28XX_SUSPEND) { dev->mode = set_mode; - return 0; + + /* FIXME: add suspend support for ac97 */ + + return em28xx_gpio_set(dev, dev->board.suspend_gpio); } dev->mode = set_mode; if (dev->mode == EM28XX_DIGITAL_MODE) - return em28xx_gpio_set(dev, dev->digital_gpio); + return em28xx_gpio_set(dev, dev->board.dvb_gpio); else - return em28xx_gpio_set(dev, dev->analog_gpio); + return em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio); } EXPORT_SYMBOL_GPL(em28xx_set_mode); @@ -738,3 +976,145 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, return 0; } EXPORT_SYMBOL_GPL(em28xx_init_isoc); + +/* + * em28xx_wake_i2c() + * configure i2c attached devices + */ +void em28xx_wake_i2c(struct em28xx *dev) +{ + struct v4l2_routing route; + int zero = 0; + + route.input = INPUT(dev->ctl_input)->vmux; + route.output = 0; + em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, &zero); + em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); + em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL); +} + +/* + * Device control list + */ + +static LIST_HEAD(em28xx_devlist); +static DEFINE_MUTEX(em28xx_devlist_mutex); + +struct em28xx *em28xx_get_device(struct inode *inode, + enum v4l2_buf_type *fh_type, + int *has_radio) +{ + struct em28xx *h, *dev = NULL; + int minor = iminor(inode); + + *fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + *has_radio = 0; + + mutex_lock(&em28xx_devlist_mutex); + list_for_each_entry(h, &em28xx_devlist, devlist) { + if (h->vdev->minor == minor) + dev = h; + if (h->vbi_dev->minor == minor) { + dev = h; + *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; + } + if (h->radio_dev && + h->radio_dev->minor == minor) { + dev = h; + *has_radio = 1; + } + } + mutex_unlock(&em28xx_devlist_mutex); + + return dev; +} + +/* + * em28xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +void em28xx_remove_from_devlist(struct em28xx *dev) +{ + mutex_lock(&em28xx_devlist_mutex); + list_del(&dev->devlist); + mutex_unlock(&em28xx_devlist_mutex); +}; + +void em28xx_add_into_devlist(struct em28xx *dev) +{ + mutex_lock(&em28xx_devlist_mutex); + list_add_tail(&dev->devlist, &em28xx_devlist); + mutex_unlock(&em28xx_devlist_mutex); +}; + +/* + * Extension interface + */ + +static LIST_HEAD(em28xx_extension_devlist); +static DEFINE_MUTEX(em28xx_extension_devlist_lock); + +int em28xx_register_extension(struct em28xx_ops *ops) +{ + struct em28xx *dev = NULL; + + mutex_lock(&em28xx_devlist_mutex); + mutex_lock(&em28xx_extension_devlist_lock); + list_add_tail(&ops->next, &em28xx_extension_devlist); + list_for_each_entry(dev, &em28xx_devlist, devlist) { + if (dev) + ops->init(dev); + } + printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); + mutex_unlock(&em28xx_extension_devlist_lock); + mutex_unlock(&em28xx_devlist_mutex); + return 0; +} +EXPORT_SYMBOL(em28xx_register_extension); + +void em28xx_unregister_extension(struct em28xx_ops *ops) +{ + struct em28xx *dev = NULL; + + mutex_lock(&em28xx_devlist_mutex); + list_for_each_entry(dev, &em28xx_devlist, devlist) { + if (dev) + ops->fini(dev); + } + + mutex_lock(&em28xx_extension_devlist_lock); + printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); + list_del(&ops->next); + mutex_unlock(&em28xx_extension_devlist_lock); + mutex_unlock(&em28xx_devlist_mutex); +} +EXPORT_SYMBOL(em28xx_unregister_extension); + +void em28xx_init_extension(struct em28xx *dev) +{ + struct em28xx_ops *ops = NULL; + + mutex_lock(&em28xx_extension_devlist_lock); + if (!list_empty(&em28xx_extension_devlist)) { + list_for_each_entry(ops, &em28xx_extension_devlist, next) { + if (ops->init) + ops->init(dev); + } + } + mutex_unlock(&em28xx_extension_devlist_lock); +} + +void em28xx_close_extension(struct em28xx *dev) +{ + struct em28xx_ops *ops = NULL; + + mutex_lock(&em28xx_extension_devlist_lock); + if (!list_empty(&em28xx_extension_devlist)) { + list_for_each_entry(ops, &em28xx_extension_devlist, next) { + if (ops->fini) + ops->fini(dev); + } + } + mutex_unlock(&em28xx_extension_devlist_lock); +} diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index c99e238..d38cb21 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -161,7 +161,7 @@ static int stop_streaming(struct em28xx_dvb *dvb) em28xx_uninit_isoc(dev); - em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + em28xx_set_mode(dev, EM28XX_SUSPEND); return 0; } @@ -215,7 +215,7 @@ static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) if (acquire) return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); else - return em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + return em28xx_set_mode(dev, EM28XX_SUSPEND); } /* ------------------------------------------------------------------ */ @@ -393,7 +393,7 @@ static int dvb_init(struct em28xx *dev) int result = 0; struct em28xx_dvb *dvb; - if (!dev->has_dvb) { + if (!dev->board.has_dvb) { /* This device does not support the extension */ return 0; } @@ -409,8 +409,10 @@ static int dvb_init(struct em28xx *dev) em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); /* init frontend */ switch (dev->model) { + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: + case EM2883_BOARD_KWORLD_HYBRID_A316: case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: dvb->frontend = dvb_attach(lgdt330x_attach, &em2880_lgdt3303_dev, @@ -466,12 +468,12 @@ static int dvb_init(struct em28xx *dev) if (result < 0) goto out_free; - em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + em28xx_set_mode(dev, EM28XX_SUSPEND); printk(KERN_INFO "Successfully loaded em28xx-dvb\n"); return 0; out_free: - em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + em28xx_set_mode(dev, EM28XX_SUSPEND); kfree(dvb); dev->dvb = NULL; return result; @@ -479,7 +481,7 @@ out_free: static int dvb_fini(struct em28xx *dev) { - if (!dev->has_dvb) { + if (!dev->board.has_dvb) { /* This device does not support the extension */ return 0; } diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index 2360c61..d69f0ef 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -250,7 +250,7 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, (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 (dev->is_em2800) + if (dev->board.is_em2800) rc = em2800_i2c_check_for_device(dev, addr); else rc = em28xx_i2c_check_for_device(dev, addr); @@ -261,7 +261,7 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, } else if (msgs[i].flags & I2C_M_RD) { /* read bytes */ - if (dev->is_em2800) + if (dev->board.is_em2800) rc = em2800_i2c_recv_bytes(dev, addr, msgs[i].buf, msgs[i].len); @@ -279,7 +279,7 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, for (byte = 0; byte < msgs[i].len; byte++) printk(" %02x", msgs[i].buf[byte]); } - if (dev->is_em2800) + if (dev->board.is_em2800) rc = em2800_i2c_send_bytes(dev, addr, msgs[i].buf, msgs[i].len); @@ -332,6 +332,17 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) struct em28xx_eeprom *em_eeprom = (void *)eedata; int i, err, size = len, block; + if (dev->chip_id == CHIP_ID_EM2874) { + /* Empia switched to a 16-bit addressable eeprom in newer + devices. While we could certainly write a routine to read + the eeprom, there is nothing of use in there that cannot be + accessed through registers, and there is the risk that we + could corrupt the eeprom (since a 16-bit read call is + interpreted as a write call by 8-bit eeproms). + */ + return 0; + } + dev->i2c_client.addr = 0xa0 >> 1; /* Check if board has eeprom */ @@ -377,47 +388,49 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) if (em_eeprom->id == 0x9567eb1a) dev->hash = em28xx_hash_mem(eedata, len, 32); - printk(KERN_INFO "EEPROM ID= 0x%08x, hash = 0x%08lx\n", - em_eeprom->id, dev->hash); - printk(KERN_INFO "Vendor/Product ID= %04x:%04x\n", em_eeprom->vendor_ID, - em_eeprom->product_ID); + printk(KERN_INFO "%s: EEPROM ID= 0x%08x, EEPROM hash = 0x%08lx\n", + dev->name, em_eeprom->id, dev->hash); + + printk(KERN_INFO "%s: EEPROM info:\n", dev->name); switch (em_eeprom->chip_conf >> 4 & 0x3) { case 0: - printk(KERN_INFO "No audio on board.\n"); + printk(KERN_INFO "%s:\tNo audio on board.\n", dev->name); break; case 1: - printk(KERN_INFO "AC97 audio (5 sample rates)\n"); + printk(KERN_INFO "%s:\tAC97 audio (5 sample rates)\n", + dev->name); break; case 2: - printk(KERN_INFO "I2S audio, sample rate=32k\n"); + printk(KERN_INFO "%s:\tI2S audio, sample rate=32k\n", dev->name); break; case 3: - printk(KERN_INFO "I2S audio, 3 sample rates\n"); + printk(KERN_INFO "%s:\tI2S audio, 3 sample rates\n", dev->name); break; } if (em_eeprom->chip_conf & 1 << 3) - printk(KERN_INFO "USB Remote wakeup capable\n"); + printk(KERN_INFO "%s:\tUSB Remote wakeup capable\n", dev->name); if (em_eeprom->chip_conf & 1 << 2) - printk(KERN_INFO "USB Self power capable\n"); + printk(KERN_INFO "%s:\tUSB Self power capable\n", dev->name); switch (em_eeprom->chip_conf & 0x3) { case 0: - printk(KERN_INFO "500mA max power\n"); + printk(KERN_INFO "%s:\t500mA max power\n", dev->name); break; case 1: - printk(KERN_INFO "400mA max power\n"); + printk(KERN_INFO "%s:\t400mA max power\n", dev->name); break; case 2: - printk(KERN_INFO "300mA max power\n"); + printk(KERN_INFO "%s:\t300mA max power\n", dev->name); break; case 3: - printk(KERN_INFO "200mA max power\n"); + printk(KERN_INFO "%s:\t200mA max power\n", dev->name); break; } - printk(KERN_INFO "Table at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n", + printk(KERN_INFO "%s:\tTable at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n", + dev->name, em_eeprom->string_idx_table, em_eeprom->string1, em_eeprom->string2, diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index eab3d95..42bbaf6 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -38,12 +38,48 @@ static unsigned int ir_debug; module_param(ir_debug, int, 0644); MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); -#define dprintk(fmt, arg...) \ +#define i2cdprintk(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->name , ## arg); \ + } + +/********************************************************** + Polling structure used by em28xx IR's + **********************************************************/ + +struct em28xx_ir_poll_result { + unsigned int toggle_bit:1; + unsigned int read_count:7; + u8 rc_address; + u8 rc_data[4]; /* 1 byte on em2860/2880, 4 on em2874 */ +}; + +struct em28xx_IR { + struct em28xx *dev; + struct input_dev *input; + struct ir_input_state ir; + char name[32]; + char phys[32]; + + /* poll external decoder */ + int polling; + struct work_struct work; + struct timer_list timer; + unsigned int last_toggle:1; + unsigned int last_readcount; + unsigned int repeat_interval; + + int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); +}; + +/********************************************************** + I2C IR based get keycodes - should be used with ir-kbd-i2c + **********************************************************/ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { @@ -51,7 +87,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) /* poll IR chip */ if (1 != i2c_master_recv(&ir->c, &b, 1)) { - dprintk("read error\n"); + i2cdprintk("read error\n"); return -EIO; } @@ -59,7 +95,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) down, while 0xff indicates that no button is hold down. 0xfe sequences are sometimes interrupted by 0xFF */ - dprintk("key %02x\n", b); + i2cdprintk("key %02x\n", b); if (b == 0xff) return 0; @@ -73,7 +109,6 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } - int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char buf[2]; @@ -97,7 +132,7 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ((buf[0]&0x10)>>3) | /* 0000 0010 */ ((buf[0]&0x20)>>5); /* 0000 0001 */ - dprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n", + i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n", code, buf[0]); /* return key */ @@ -114,11 +149,11 @@ 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)) { - dprintk("read error\n"); + i2cdprintk("read error\n"); return -EIO; } - dprintk("key %02x\n", buf[2]&0x3f); + i2cdprintk("key %02x\n", buf[2]&0x3f); if (buf[0] != 0x00) return 0; @@ -128,6 +163,260 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, return 1; } +/********************************************************** + Poll based get keycode functions + **********************************************************/ + +/* This is for the em2860/em2880 */ +static int default_polling_getkey(struct em28xx_IR *ir, + struct em28xx_ir_poll_result *poll_result) +{ + struct em28xx *dev = ir->dev; + int rc; + u8 msg[3] = { 0, 0, 0 }; + + /* Read key toggle, brand, and key code + on registers 0x45, 0x46 and 0x47 + */ + rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR, + msg, sizeof(msg)); + if (rc < 0) + return rc; + + /* Infrared toggle (Reg 0x45[7]) */ + poll_result->toggle_bit = (msg[0] >> 7); + + /* Infrared read count (Reg 0x45[6:0] */ + poll_result->read_count = (msg[0] & 0x7f); + + /* Remote Control Address (Reg 0x46) */ + poll_result->rc_address = msg[1]; + + /* Remote Control Data (Reg 0x47) */ + poll_result->rc_data[0] = msg[2]; + + return 0; +} + +static int em2874_polling_getkey(struct em28xx_IR *ir, + struct em28xx_ir_poll_result *poll_result) +{ + struct em28xx *dev = ir->dev; + int rc; + u8 msg[5] = { 0, 0, 0, 0, 0 }; + + /* Read key toggle, brand, and key code + on registers 0x51-55 + */ + rc = dev->em28xx_read_reg_req_len(dev, 0, EM2874_R51_IR, + msg, sizeof(msg)); + if (rc < 0) + return rc; + + /* Infrared toggle (Reg 0x51[7]) */ + poll_result->toggle_bit = (msg[0] >> 7); + + /* Infrared read count (Reg 0x51[6:0] */ + poll_result->read_count = (msg[0] & 0x7f); + + /* Remote Control Address (Reg 0x52) */ + poll_result->rc_address = msg[1]; + + /* Remote Control Data (Reg 0x53-55) */ + poll_result->rc_data[0] = msg[2]; + poll_result->rc_data[1] = msg[3]; + poll_result->rc_data[2] = msg[4]; + + return 0; +} + +/********************************************************** + Polling code for em28xx + **********************************************************/ + +static void em28xx_ir_handle_key(struct em28xx_IR *ir) +{ + int result; + int do_sendkey = 0; + struct em28xx_ir_poll_result poll_result; + + /* read the registers containing the IR status */ + result = ir->get_key(ir, &poll_result); + if (result < 0) { + dprintk("ir->get_key() failed %d\n", result); + return; + } + + dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n", + poll_result.toggle_bit, poll_result.read_count, + ir->last_readcount, poll_result.rc_data[0]); + + if (ir->dev->chip_id == CHIP_ID_EM2874) { + /* The em2874 clears the readcount field every time the + register is read. The em2860/2880 datasheet says that it + is supposed to clear the readcount, but it doesn't. So with + the em2874, we are looking for a non-zero read count as + opposed to a readcount that is incrementing */ + ir->last_readcount = 0; + } + + if (poll_result.read_count == 0) { + /* The button has not been pressed since the last read */ + } else if (ir->last_toggle != poll_result.toggle_bit) { + /* A button has been pressed */ + dprintk("button has been pressed\n"); + ir->last_toggle = poll_result.toggle_bit; + ir->repeat_interval = 0; + do_sendkey = 1; + } else if (poll_result.toggle_bit == ir->last_toggle && + poll_result.read_count > 0 && + poll_result.read_count != ir->last_readcount) { + /* The button is still being held down */ + dprintk("button being held down\n"); + + /* Debouncer for first keypress */ + if (ir->repeat_interval++ > 9) { + /* Start repeating after 1 second */ + do_sendkey = 1; + } + } + + if (do_sendkey) { + dprintk("sending keypress\n"); + ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0], + poll_result.rc_data[0]); + ir_input_nokey(ir->input, &ir->ir); + } + + ir->last_readcount = poll_result.read_count; + return; +} + +static void ir_timer(unsigned long data) +{ + struct em28xx_IR *ir = (struct em28xx_IR *)data; + + schedule_work(&ir->work); +} + +static void em28xx_ir_work(struct work_struct *work) +{ + struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work); + + em28xx_ir_handle_key(ir); + mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); +} + +void em28xx_ir_start(struct em28xx_IR *ir) +{ + setup_timer(&ir->timer, ir_timer, (unsigned long)ir); + INIT_WORK(&ir->work, em28xx_ir_work); + schedule_work(&ir->work); +} + +static void em28xx_ir_stop(struct em28xx_IR *ir) +{ + del_timer_sync(&ir->timer); + flush_scheduled_work(); +} + +int em28xx_ir_init(struct em28xx *dev) +{ + struct em28xx_IR *ir; + struct input_dev *input_dev; + u8 ir_config; + int err = -ENOMEM; + + if (dev->board.ir_codes == NULL) { + /* No remote control support */ + return 0; + } + + ir = kzalloc(sizeof(*ir), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ir || !input_dev) + goto err_out_free; + + ir->input = input_dev; + + /* Setup the proper handler based on the chip */ + switch (dev->chip_id) { + case CHIP_ID_EM2860: + case CHIP_ID_EM2883: + ir->get_key = default_polling_getkey; + break; + case CHIP_ID_EM2874: + ir->get_key = em2874_polling_getkey; + /* For now we only support RC5, so enable it */ + ir_config = EM2874_IR_RC5; + em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1); + break; + default: + printk("Unrecognized em28xx chip id: IR not supported\n"); + goto err_out_free; + } + + /* This is how often we ask the chip for IR information */ + ir->polling = 100; /* ms */ + + /* init input device */ + snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)", + dev->name); + + usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); + strlcat(ir->phys, "/input0", sizeof(ir->phys)); + + ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes); + input_dev->name = ir->name; + input_dev->phys = ir->phys; + input_dev->id.bustype = BUS_USB; + input_dev->id.version = 1; + input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + + input_dev->dev.parent = &dev->udev->dev; + /* record handles to ourself */ + ir->dev = dev; + dev->ir = ir; + + em28xx_ir_start(ir); + + /* all done */ + err = input_register_device(ir->input); + if (err) + goto err_out_stop; + + return 0; + err_out_stop: + em28xx_ir_stop(ir); + dev->ir = NULL; + err_out_free: + input_free_device(input_dev); + kfree(ir); + return err; +} + +int em28xx_ir_fini(struct em28xx *dev) +{ + struct em28xx_IR *ir = dev->ir; + + /* skip detach on non attached boards */ + if (!ir) + return 0; + + em28xx_ir_stop(ir); + input_unregister_device(ir->input); + kfree(ir); + + /* done */ + dev->ir = NULL; + return 0; +} + +/********************************************************** + Handle Webcam snapshot button + **********************************************************/ + static void em28xx_query_sbutton(struct work_struct *work) { /* Poll the register and see if the button is depressed */ @@ -210,9 +499,3 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev) } return; } - -/* ---------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index fac1ab2..65dcb91 100644 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ b/drivers/media/video/em28xx/em28xx-reg.h @@ -17,17 +17,58 @@ /* em28xx registers */ +#define EM28XX_R00_CHIPCFG 0x00 + +/* em28xx Chip Configuration 0x00 */ +#define EM28XX_CHIPCFG_VENDOR_AUDIO 0x80 +#define EM28XX_CHIPCFG_I2S_VOLUME_CAPABLE 0x40 +#define EM28XX_CHIPCFG_I2S_5_SAMPRATES 0x30 +#define EM28XX_CHIPCFG_I2S_3_SAMPRATES 0x20 +#define EM28XX_CHIPCFG_AC97 0x10 +#define EM28XX_CHIPCFG_AUDIOMASK 0x30 + /* 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 + +/* em28xx I2C Clock Register (0x06) */ +#define EM28XX_I2C_CLK_ACK_LAST_READ 0x80 +#define EM28XX_I2C_CLK_WAIT_ENABLE 0x40 +#define EM28XX_I2C_EEPROM_ON_BOARD 0x08 +#define EM28XX_I2C_EEPROM_KEY_VALID 0x04 +#define EM2874_I2C_SECONDARY_BUS_SELECT 0x04 /* em2874 has two i2c busses */ +#define EM28XX_I2C_FREQ_1_5_MHZ 0x03 /* bus frequency (bits [1-0]) */ +#define EM28XX_I2C_FREQ_25_KHZ 0x02 +#define EM28XX_I2C_FREQ_400_KHZ 0x01 +#define EM28XX_I2C_FREQ_100_KHZ 0x00 + + #define EM28XX_R0A_CHIPID 0x0a #define EM28XX_R0C_USBSUSP 0x0c /* */ #define EM28XX_R0E_AUDIOSRC 0x0e #define EM28XX_R0F_XCLK 0x0f +/* em28xx XCLK Register (0x0f) */ +#define EM28XX_XCLK_AUDIO_UNMUTE 0x80 /* otherwise audio muted */ +#define EM28XX_XCLK_I2S_MSB_TIMING 0x40 /* otherwise standard timing */ +#define EM28XX_XCLK_IR_RC5_MODE 0x20 /* otherwise NEC mode */ +#define EM28XX_XCLK_IR_NEC_CHK_PARITY 0x10 +#define EM28XX_XCLK_FREQUENCY_30MHZ 0x00 /* Freq. select (bits [3-0]) */ +#define EM28XX_XCLK_FREQUENCY_15MHZ 0x01 +#define EM28XX_XCLK_FREQUENCY_10MHZ 0x02 +#define EM28XX_XCLK_FREQUENCY_7_5MHZ 0x03 +#define EM28XX_XCLK_FREQUENCY_6MHZ 0x04 +#define EM28XX_XCLK_FREQUENCY_5MHZ 0x05 +#define EM28XX_XCLK_FREQUENCY_4_3MHZ 0x06 +#define EM28XX_XCLK_FREQUENCY_12MHZ 0x07 +#define EM28XX_XCLK_FREQUENCY_20MHZ 0x08 +#define EM28XX_XCLK_FREQUENCY_20MHZ_2 0x09 +#define EM28XX_XCLK_FREQUENCY_48MHZ 0x0a +#define EM28XX_XCLK_FREQUENCY_24MHZ 0x0b + #define EM28XX_R10_VINMODE 0x10 #define EM28XX_R11_VINCTRL 0x11 #define EM28XX_R12_VINENABLE 0x12 /* */ @@ -56,6 +97,19 @@ #define EM28XX_R26_COMPR 0x26 #define EM28XX_R27_OUTFMT 0x27 +/* em28xx Output Format Register (0x27) */ +#define EM28XX_OUTFMT_RGB_8_RGRG 0x00 +#define EM28XX_OUTFMT_RGB_8_GRGR 0x01 +#define EM28XX_OUTFMT_RGB_8_GBGB 0x02 +#define EM28XX_OUTFMT_RGB_8_BGBG 0x03 +#define EM28XX_OUTFMT_RGB_16_656 0x04 +#define EM28XX_OUTFMT_RGB_8_BAYER 0x08 /* Pattern in Reg 0x10[1-0] */ +#define EM28XX_OUTFMT_YUV211 0x10 +#define EM28XX_OUTFMT_YUV422_Y0UY1V 0x14 +#define EM28XX_OUTFMT_YUV422_Y1UY0V 0x15 +#define EM28XX_OUTFMT_YUV411 0x18 + + #define EM28XX_R28_XMIN 0x28 #define EM28XX_R29_XMAX 0x29 #define EM28XX_R2A_YMIN 0x2a @@ -71,10 +125,32 @@ #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 +#define EM28XX_R45_IR 0x45 + /* 0x45 bit 7 - parity bit + bits 6-0 - count + 0x46 IR brand + 0x47 IR data + */ + +/* em2874 registers */ +#define EM2874_R50_IR_CONFIG 0x50 +#define EM2874_R51_IR 0x51 +#define EM2874_R5F_TS_ENABLE 0x5f +#define EM2874_R80_GPIO 0x80 + +/* em2874 IR config register (0x50) */ +#define EM2874_IR_NEC 0x00 +#define EM2874_IR_RC5 0x04 +#define EM2874_IR_RC5_MODE_0 0x08 +#define EM2874_IR_RC5_MODE_6A 0x0b + +/* em2874 Transport Stream Enable Register (0x5f) */ +#define EM2874_TS1_CAPTURE_ENABLE (1 << 0) +#define EM2874_TS1_FILTER_ENABLE (1 << 1) +#define EM2874_TS1_NULL_DISCARD (1 << 2) +#define EM2874_TS2_CAPTURE_ENABLE (1 << 4) +#define EM2874_TS2_FILTER_ENABLE (1 << 5) +#define EM2874_TS2_NULL_DISCARD (1 << 6) /* register settings */ #define EM2800_AUDIO_SRC_TUNER 0x0d @@ -84,6 +160,75 @@ /* FIXME: Need to be populated with the other chip ID's */ enum em28xx_chip_id { + CHIP_ID_EM2820 = 18, + CHIP_ID_EM2840 = 20, + CHIP_ID_EM2750 = 33, CHIP_ID_EM2860 = 34, + CHIP_ID_EM2870 = 35, CHIP_ID_EM2883 = 36, + CHIP_ID_EM2874 = 65, }; + +/* + * Registers used by em202 and other AC97 chips + */ + +/* Standard AC97 registers */ +#define AC97_RESET 0x00 + + /* Output volumes */ +#define AC97_MASTER_VOL 0x02 +#define AC97_LINE_LEVEL_VOL 0x04 /* Some devices use for headphones */ +#define AC97_MASTER_MONO_VOL 0x06 + + /* Input volumes */ +#define AC97_PC_BEEP_VOL 0x0a +#define AC97_PHONE_VOL 0x0c +#define AC97_MIC_VOL 0x0e +#define AC97_LINEIN_VOL 0x10 +#define AC97_CD_VOL 0x12 +#define AC97_VIDEO_VOL 0x14 +#define AC97_AUX_VOL 0x16 +#define AC97_PCM_OUT_VOL 0x18 + + /* capture registers */ +#define AC97_RECORD_SELECT 0x1a +#define AC97_RECORD_GAIN 0x1c + + /* control registers */ +#define AC97_GENERAL_PURPOSE 0x20 +#define AC97_3D_CTRL 0x22 +#define AC97_AUD_INT_AND_PAG 0x24 +#define AC97_POWER_DOWN_CTRL 0x26 +#define AC97_EXT_AUD_ID 0x28 +#define AC97_EXT_AUD_CTRL 0x2a + +/* Supported rate varies for each AC97 device + if write an unsupported value, it will return the closest one + */ +#define AC97_PCM_OUT_FRONT_SRATE 0x2c +#define AC97_PCM_OUT_SURR_SRATE 0x2e +#define AC97_PCM_OUT_LFE_SRATE 0x30 +#define AC97_PCM_IN_SRATE 0x32 + + /* For devices with more than 2 channels, extra output volumes */ +#define AC97_LFE_MASTER_VOL 0x36 +#define AC97_SURR_MASTER_VOL 0x38 + + /* Digital SPDIF output control */ +#define AC97_SPDIF_OUT_CTRL 0x3a + + /* Vendor ID identifier */ +#define AC97_VENDOR_ID1 0x7c +#define AC97_VENDOR_ID2 0x7e + +/* EMP202 vendor registers */ +#define EM202_EXT_MODEM_CTRL 0x3e +#define EM202_GPIO_CONF 0x4c +#define EM202_GPIO_POLARITY 0x4e +#define EM202_GPIO_STICKY 0x50 +#define EM202_GPIO_MASK 0x52 +#define EM202_GPIO_STATUS 0x54 +#define EM202_SPDIF_OUT_SEL 0x6a +#define EM202_ANTIPOP 0x72 +#define EM202_EAPD_GPIO_ACCESS 0x74 diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 4ea1f1e..5352753 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -39,6 +39,7 @@ #include "em28xx.h" #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-chip-ident.h> #include <media/msp3400.h> #include <media/tuner.h> @@ -47,9 +48,8 @@ "Mauro Carvalho Chehab <mchehab@infradead.org>, " \ "Sascha Sommer <saschasommer@freenet.de>" -#define DRIVER_NAME "em28xx" #define DRIVER_DESC "Empia em28xx based USB video device driver" -#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 0) +#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 1) #define em28xx_videodbg(fmt, arg...) do {\ if (video_debug) \ @@ -72,19 +72,13 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -static LIST_HEAD(em28xx_devlist); -static DEFINE_MUTEX(em28xx_devlist_mutex); - -static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; 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 }; -module_param_array(card, int, NULL, 0444); module_param_array(video_nr, int, NULL, 0444); module_param_array(vbi_nr, int, NULL, 0444); module_param_array(radio_nr, int, NULL, 0444); -MODULE_PARM_DESC(card, "card type"); MODULE_PARM_DESC(video_nr, "video device numbers"); MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); MODULE_PARM_DESC(radio_nr, "radio device numbers"); @@ -93,8 +87,15 @@ static unsigned int video_debug; 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; +/* supported video standards */ +static struct em28xx_fmt format[] = { + { + .name = "16bpp YUY2, 4:2:2, packed", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .reg = EM28XX_OUTFMT_YUV422_Y0UY1V, + }, +}; /* supported controls */ /* Common to all boards */ @@ -120,8 +121,6 @@ static struct v4l2_queryctrl em28xx_qctrl[] = { } }; -static struct usb_driver em28xx_usb_driver; - /* ------------------------------------------------------------------ DMA and thread functions ------------------------------------------------------------------*/ @@ -386,16 +385,18 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) struct em28xx *dev = fh->dev; struct v4l2_frequency f; - *size = 16 * fh->dev->width * fh->dev->height >> 3; + *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3; + if (0 == *count) *count = EM28XX_DEF_BUF; if (*count < EM28XX_MIN_BUF) *count = EM28XX_MIN_BUF; - /* Ask tuner to go to analog mode */ + /* Ask tuner to go to analog or radio mode */ memset(&f, 0, sizeof(f)); f.frequency = dev->ctl_freq; + f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f); @@ -438,9 +439,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, struct em28xx *dev = fh->dev; int rc = 0, urb_init = 0; - /* FIXME: It assumes depth = 16 */ - /* The only currently supported format is 16 bits/pixel */ - buf->vb.size = 16 * dev->width * dev->height >> 3; + buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; @@ -508,56 +507,6 @@ static struct videobuf_queue_ops em28xx_video_qops = { /********************* v4l2 interface **************************************/ -/* - * em28xx_config() - * inits registers with sane defaults - */ -static int em28xx_config(struct em28xx *dev) -{ - int retval; - - /* Sets I2C speed to 100 KHz */ - if (!dev->is_em2800) { - retval = em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1); - if (retval < 0) { - em28xx_errdev("%s: em28xx_write_regs_req failed! retval [%d]\n", - __func__, retval); - return retval; - } - } - - /* 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); - - dev->mute = 1; /* maybe not the right place... */ - dev->volume = 0x1f; - - em28xx_outfmt_set_yuv422(dev); - em28xx_colorlevels_set_default(dev); - em28xx_compression_disable(dev); - - return 0; -} - -/* - * em28xx_config_i2c() - * configure i2c attached devices - */ -static void em28xx_config_i2c(struct em28xx *dev) -{ - struct v4l2_routing route; - int zero = 0; - - route.input = INPUT(dev->ctl_input)->vmux; - route.output = 0; - em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, &zero); - em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); - em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL); -} - static void video_mux(struct em28xx *dev, int index) { struct v4l2_routing route; @@ -566,10 +515,14 @@ static void video_mux(struct em28xx *dev, int index) route.output = 0; dev->ctl_input = index; dev->ctl_ainput = INPUT(index)->amux; + dev->ctl_aoutput = INPUT(index)->aout; + + if (!dev->ctl_aoutput) + dev->ctl_aoutput = EM28XX_AOUT_MASTER; em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); - if (dev->has_msp34xx) { + if (dev->board.has_msp34xx) { if (dev->i2s_speed) { em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed); @@ -595,12 +548,10 @@ static int res_get(struct em28xx_fh *fh) return rc; if (dev->stream_on) - return -EINVAL; + return -EBUSY; - mutex_lock(&dev->lock); dev->stream_on = 1; fh->stream_on = 1; - mutex_unlock(&dev->lock); return rc; } @@ -613,10 +564,8 @@ static void res_free(struct em28xx_fh *fh) { struct em28xx *dev = fh->dev; - mutex_lock(&dev->lock); fh->stream_on = 0; dev->stream_on = 0; - mutex_unlock(&dev->lock); } /* @@ -703,8 +652,8 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; - f->fmt.pix.bytesperline = dev->width * 2; + f->fmt.pix.pixelformat = dev->format->fourcc; + f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; @@ -716,6 +665,17 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, return 0; } +static struct em28xx_fmt *format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format); i++) + if (format[i].fourcc == fourcc) + return &format[i]; + + return NULL; +} + static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { @@ -726,24 +686,30 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, unsigned int maxw = norm_maxw(dev); unsigned int maxh = norm_maxh(dev); unsigned int hscale, vscale; + struct em28xx_fmt *fmt; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) { + em28xx_videodbg("Fourcc format (%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } /* width must even because of the YUYV format height must be even because of interlacing */ height &= 0xfffe; - width &= 0xfffe; + width &= 0xfffe; - if (height < 32) + if (unlikely(height < 32)) height = 32; - if (height > maxh) + if (unlikely(height > maxh)) height = maxh; - if (width < 48) + if (unlikely(width < 48)) width = 48; - if (width > maxw) + if (unlikely(width > maxw)) width = maxw; - mutex_lock(&dev->lock); - - if (dev->is_em2800) { + if (dev->board.is_em2800) { /* the em2800 can only scale down to 50% */ if (height % (maxh / 2)) height = maxh; @@ -766,13 +732,12 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = width; f->fmt.pix.height = height; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; - f->fmt.pix.bytesperline = width * 2; - f->fmt.pix.sizeimage = width * 2 * height; + f->fmt.pix.pixelformat = fmt->fourcc; + f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; f->fmt.pix.field = V4L2_FIELD_INTERLACED; - mutex_unlock(&dev->lock); return 0; } @@ -782,14 +747,21 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; int rc; + struct em28xx_fmt *fmt; rc = check_dev(dev); if (rc < 0) return rc; + mutex_lock(&dev->lock); + vidioc_try_fmt_vid_cap(file, priv, f); - mutex_lock(&dev->lock); + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) { + rc = -EINVAL; + goto out; + } if (videobuf_queue_is_busy(&fh->vb_vidq)) { em28xx_errdev("%s queue busy\n", __func__); @@ -806,6 +778,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, /* set new image size */ dev->width = f->fmt.pix.width; dev->height = f->fmt.pix.height; + dev->format = fmt; get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); em28xx_set_alternate(dev); @@ -831,15 +804,12 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm) mutex_lock(&dev->lock); dev->norm = *norm; - mutex_unlock(&dev->lock); /* Adjusts width/height, if needed */ f.fmt.pix.width = dev->width; f.fmt.pix.height = dev->height; vidioc_try_fmt_vid_cap(file, priv, &f); - mutex_lock(&dev->lock); - /* set new image size */ dev->width = f.fmt.pix.width; dev->height = f.fmt.pix.height; @@ -928,20 +898,38 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - unsigned int index = a->index; - - if (a->index > 1) - return -EINVAL; - - index = dev->ctl_ainput; - if (index == 0) + switch (a->index) { + case EM28XX_AMUX_VIDEO: strcpy(a->name, "Television"); - else + break; + case EM28XX_AMUX_LINE_IN: strcpy(a->name, "Line In"); + break; + case EM28XX_AMUX_VIDEO2: + strcpy(a->name, "Television alt"); + break; + case EM28XX_AMUX_PHONE: + strcpy(a->name, "Phone"); + break; + case EM28XX_AMUX_MIC: + strcpy(a->name, "Mic"); + break; + case EM28XX_AMUX_CD: + strcpy(a->name, "CD"); + break; + case EM28XX_AMUX_AUX: + strcpy(a->name, "Aux"); + break; + case EM28XX_AMUX_PCM_OUT: + strcpy(a->name, "PCM"); + break; + default: + return -EINVAL; + } + a->index = dev->ctl_ainput; a->capability = V4L2_AUDCAP_STEREO; - a->index = index; return 0; } @@ -951,9 +939,15 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - if (a->index != dev->ctl_ainput) - return -EINVAL; + mutex_lock(&dev->lock); + + dev->ctl_ainput = INPUT(a->index)->amux; + dev->ctl_aoutput = INPUT(a->index)->aout; + if (!dev->ctl_aoutput) + dev->ctl_aoutput = EM28XX_AOUT_MASTER; + + mutex_unlock(&dev->lock); return 0; } @@ -974,7 +968,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, qc->id = id; - if (!dev->has_msp34xx) { + if (!dev->board.has_msp34xx) { for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { if (qc->id && qc->id == em28xx_qctrl[i].id) { memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc)); @@ -1002,17 +996,14 @@ static int vidioc_g_ctrl(struct file *file, void *priv, rc = check_dev(dev); if (rc < 0) return rc; - mutex_lock(&dev->lock); + rc = 0; - if (!dev->has_msp34xx) - rc = em28xx_get_ctrl(dev, ctrl); - else - rc = -EINVAL; + mutex_lock(&dev->lock); - if (rc == -EINVAL) { + if (dev->board.has_msp34xx) em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl); - rc = 0; - } + else + rc = em28xx_get_ctrl(dev, ctrl); mutex_unlock(&dev->lock); return rc; @@ -1032,7 +1023,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, mutex_lock(&dev->lock); - if (dev->has_msp34xx) + if (dev->board.has_msp34xx) em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl); else { rc = 1; @@ -1112,8 +1103,10 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; + mutex_lock(&dev->lock); f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = dev->ctl_freq; + mutex_unlock(&dev->lock); return 0; } @@ -1143,6 +1136,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f); mutex_unlock(&dev->lock); + return 0; } @@ -1159,6 +1153,21 @@ static int em28xx_reg_len(int reg) } } +static int vidioc_g_chip_ident(struct file *file, void *priv, + struct v4l2_chip_ident *chip) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + + em28xx_i2c_call_clients(dev, VIDIOC_G_CHIP_IDENT, chip); + + return 0; +} + + static int vidioc_g_register(struct file *file, void *priv, struct v4l2_register *reg) { @@ -1166,19 +1175,43 @@ static int vidioc_g_register(struct file *file, void *priv, struct em28xx *dev = fh->dev; int ret; - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + switch (reg->match_type) { + case V4L2_CHIP_MATCH_AC97: + mutex_lock(&dev->lock); + ret = em28xx_read_ac97(dev, reg->reg); + mutex_unlock(&dev->lock); + if (ret < 0) + return ret; + + reg->val = ret; + return 0; + case V4L2_CHIP_MATCH_I2C_DRIVER: + em28xx_i2c_call_clients(dev, VIDIOC_DBG_G_REGISTER, reg); + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + /* Not supported yet */ return -EINVAL; + default: + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return -EINVAL; + } + /* Match host */ if (em28xx_reg_len(reg->reg) == 1) { + mutex_lock(&dev->lock); ret = em28xx_read_reg(dev, reg->reg); + mutex_unlock(&dev->lock); + if (ret < 0) return ret; reg->val = ret; } else { __le64 val = 0; + mutex_lock(&dev->lock); ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS, reg->reg, (char *)&val, 2); + mutex_unlock(&dev->lock); if (ret < 0) return ret; @@ -1194,11 +1227,35 @@ static int vidioc_s_register(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; __le64 buf; + int rc; + switch (reg->match_type) { + case V4L2_CHIP_MATCH_AC97: + mutex_lock(&dev->lock); + rc = em28xx_write_ac97(dev, reg->reg, reg->val); + mutex_unlock(&dev->lock); + + return rc; + case V4L2_CHIP_MATCH_I2C_DRIVER: + em28xx_i2c_call_clients(dev, VIDIOC_DBG_S_REGISTER, reg); + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + /* Not supported yet */ + return -EINVAL; + default: + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return -EINVAL; + } + + /* Match host */ buf = cpu_to_le64(reg->val); - return em28xx_write_regs(dev, reg->reg, (char *)&buf, - em28xx_reg_len(reg->reg)); + mutex_lock(&dev->lock); + rc = em28xx_write_regs(dev, reg->reg, (char *)&buf, + em28xx_reg_len(reg->reg)); + mutex_unlock(&dev->lock); + + return rc; } #endif @@ -1235,10 +1292,15 @@ static int vidioc_streamon(struct file *file, void *priv, return rc; - if (unlikely(res_get(fh) < 0)) - return -EBUSY; + mutex_lock(&dev->lock); + rc = res_get(fh); + + if (likely(rc >= 0)) + rc = videobuf_streamon(&fh->vb_vidq); - return (videobuf_streamon(&fh->vb_vidq)); + mutex_unlock(&dev->lock); + + return rc; } static int vidioc_streamoff(struct file *file, void *priv, @@ -1257,9 +1319,13 @@ static int vidioc_streamoff(struct file *file, void *priv, if (type != fh->type) return -EINVAL; + mutex_lock(&dev->lock); + videobuf_streamoff(&fh->vb_vidq); res_free(fh); + mutex_unlock(&dev->lock); + return 0; } @@ -1271,7 +1337,7 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); - strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info)); + strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info)); cap->version = EM28XX_VERSION_CODE; @@ -1288,15 +1354,13 @@ static int vidioc_querycap(struct file *file, void *priv, } static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *fmtd) + struct v4l2_fmtdesc *f) { - if (fmtd->index != 0) + if (unlikely(f->index >= ARRAY_SIZE(format))) return -EINVAL; - fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - strcpy(fmtd->description, "Packed YUY2"); - fmtd->pixelformat = V4L2_PIX_FMT_YUYV; - memset(fmtd->reserved, 0, sizeof(fmtd->reserved)); + strlcpy(f->description, format[f->index].name, sizeof(f->description)); + f->pixelformat = format[f->index].fourcc; return 0; } @@ -1424,7 +1488,7 @@ static int radio_querycap(struct file *file, void *priv, strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); - strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info)); + strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info)); cap->version = EM28XX_VERSION_CODE; cap->capabilities = V4L2_CAP_TUNER; @@ -1442,7 +1506,10 @@ static int radio_g_tuner(struct file *file, void *priv, strcpy(t->name, "Radio"); t->type = V4L2_TUNER_RADIO; + mutex_lock(&dev->lock); em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t); + mutex_unlock(&dev->lock); + return 0; } @@ -1474,7 +1541,9 @@ static int radio_s_tuner(struct file *file, void *priv, if (0 != t->index) return -EINVAL; + mutex_lock(&dev->lock); em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t); + mutex_unlock(&dev->lock); return 0; } @@ -1516,28 +1585,13 @@ static int radio_queryctrl(struct file *file, void *priv, 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; + int errCode = 0, radio; + struct em28xx *dev; + enum v4l2_buf_type fh_type; struct em28xx_fh *fh; - enum v4l2_buf_type fh_type = 0; - mutex_lock(&em28xx_devlist_mutex); - list_for_each_entry(h, &em28xx_devlist, devlist) { - if (h->vdev->minor == minor) { - dev = h; - fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - } - if (h->vbi_dev->minor == minor) { - dev = h; - fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; - } - if (h->radio_dev && - h->radio_dev->minor == minor) { - radio = 1; - dev = h; - } - } - mutex_unlock(&em28xx_devlist_mutex); + dev = em28xx_get_device(inode, &fh_type, &radio); + if (NULL == dev) return -ENODEV; @@ -1571,7 +1625,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) /* Needed, since GPIO might have disabled power of some i2c device */ - em28xx_config_i2c(dev); + em28xx_wake_i2c(dev); } if (fh->radio) { @@ -1595,16 +1649,11 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) * unregisters the v4l2,i2c and usb devices * called when the device gets disconected or at module unload */ -static void em28xx_release_resources(struct em28xx *dev) +void em28xx_release_analog_resources(struct em28xx *dev) { /*FIXME: I2C IR should be disconnected */ - em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n", - dev->vdev->num, dev->vbi_dev->num); - list_del(&dev->devlist); - if (dev->sbutton_input_dev) - em28xx_deregister_snapshot_button(dev); if (dev->radio_dev) { if (-1 != dev->radio_dev->minor) video_unregister_device(dev->radio_dev); @@ -1613,6 +1662,8 @@ static void em28xx_release_resources(struct em28xx *dev) dev->radio_dev = NULL; } if (dev->vbi_dev) { + em28xx_info("V4L2 device /dev/vbi%d deregistered\n", + dev->vbi_dev->num); if (-1 != dev->vbi_dev->minor) video_unregister_device(dev->vbi_dev); else @@ -1620,17 +1671,14 @@ static void em28xx_release_resources(struct em28xx *dev) dev->vbi_dev = NULL; } if (dev->vdev) { + em28xx_info("V4L2 device /dev/video%d deregistered\n", + dev->vdev->num); if (-1 != dev->vdev->minor) video_unregister_device(dev->vdev); else video_device_release(dev->vdev); dev->vdev = NULL; } - em28xx_i2c_unregister(dev); - usb_put_dev(dev->udev); - - /* Mark device as unused */ - em28xx_devused &= ~(1<<dev->devno); } /* @@ -1647,11 +1695,10 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) em28xx_videodbg("users=%d\n", dev->users); + mutex_lock(&dev->lock); if (res_check(fh)) res_free(fh); - mutex_lock(&dev->lock); - if (dev->users == 1) { videobuf_stop(&fh->vb_vidq); videobuf_mmap_free(&fh->vb_vidq); @@ -1665,9 +1712,12 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) return 0; } + /* Save some power by putting tuner to sleep */ + em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); + /* do this before setting alternate! */ em28xx_uninit_isoc(dev); - em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + em28xx_set_mode(dev, EM28XX_SUSPEND); /* set alternate 0 */ dev->alt = 0; @@ -1706,8 +1756,12 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, */ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (unlikely(res_get(fh))) - return -EBUSY; + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return rc; return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, filp->f_flags & O_NONBLOCK); @@ -1729,7 +1783,11 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait) if (rc < 0) return rc; - if (unlikely(res_get(fh) < 0)) + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) return POLLERR; if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) @@ -1747,13 +1805,17 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) struct em28xx *dev = fh->dev; int rc; - if (unlikely(res_get(fh) < 0)) - return -EBUSY; - rc = check_dev(dev); if (rc < 0) return rc; + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return rc; + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", @@ -1810,6 +1872,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, + .vidioc_g_chip_ident = vidioc_g_chip_ident, #endif #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, @@ -1865,44 +1928,6 @@ static struct video_device em28xx_radio_template = { /******************************** usb interface ******************************/ -static LIST_HEAD(em28xx_extension_devlist); -static DEFINE_MUTEX(em28xx_extension_devlist_lock); - -int em28xx_register_extension(struct em28xx_ops *ops) -{ - struct em28xx *dev = NULL; - - mutex_lock(&em28xx_devlist_mutex); - mutex_lock(&em28xx_extension_devlist_lock); - list_add_tail(&ops->next, &em28xx_extension_devlist); - list_for_each_entry(dev, &em28xx_devlist, devlist) { - if (dev) - ops->init(dev); - } - printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); - mutex_unlock(&em28xx_extension_devlist_lock); - mutex_unlock(&em28xx_devlist_mutex); - return 0; -} -EXPORT_SYMBOL(em28xx_register_extension); - -void em28xx_unregister_extension(struct em28xx_ops *ops) -{ - struct em28xx *dev = NULL; - - mutex_lock(&em28xx_devlist_mutex); - list_for_each_entry(dev, &em28xx_devlist, devlist) { - if (dev) - ops->fini(dev); - } - - mutex_lock(&em28xx_extension_devlist_lock); - printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); - list_del(&ops->next); - mutex_unlock(&em28xx_extension_devlist_lock); - mutex_unlock(&em28xx_devlist_mutex); -} -EXPORT_SYMBOL(em28xx_unregister_extension); static struct video_device *em28xx_vdev_init(struct em28xx *dev, const struct video_device *template, @@ -1925,11 +1950,43 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, return vfd; } - -static int register_analog_devices(struct em28xx *dev) +int em28xx_register_analog_devices(struct em28xx *dev) { int ret; + printk(KERN_INFO "%s: v4l2 driver version %d.%d.%d\n", + dev->name, + (EM28XX_VERSION_CODE >> 16) & 0xff, + (EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff); + + /* Analog specific initialization */ + dev->format = &format[0]; + video_mux(dev, 0); + + /* enable vbi capturing */ + +/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */ +/* em28xx_write_reg(dev, EM28XX_R0F_XCLK, 0x80); clk register */ + em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51); + + dev->mute = 1; /* maybe not the right place... */ + dev->volume = 0x1f; + + em28xx_set_outfmt(dev); + em28xx_colorlevels_set_default(dev); + em28xx_compression_disable(dev); + + /* set default norm */ + dev->norm = em28xx_video_template.current_norm; + dev->width = norm_maxw(dev); + dev->height = norm_maxh(dev); + dev->interlaced = EM28XX_INTERLACED_DEFAULT; + dev->hscale = 0; + dev->vscale = 0; + + /* FIXME: This is a very bad hack! Not all devices have TV on input 2 */ + dev->ctl_input = 2; + /* allocate and fill video video_device struct */ dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video"); if (!dev->vdev) { @@ -1978,383 +2035,3 @@ static int register_analog_devices(struct em28xx *dev) return 0; } - - -/* - * em28xx_init_dev() - * allocates and inits the device structs, registers i2c bus and v4l device - */ -static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, - int minor) -{ - struct em28xx_ops *ops = NULL; - struct em28xx *dev = *devhandle; - int retval = -ENOMEM; - int errCode; - unsigned int maxh, maxw; - - dev->udev = udev; - mutex_init(&dev->lock); - mutex_init(&dev->ctrl_urb_lock); - spin_lock_init(&dev->slock); - init_waitqueue_head(&dev->open); - init_waitqueue_head(&dev->wait_frame); - init_waitqueue_head(&dev->wait_stream); - - dev->em28xx_write_regs = em28xx_write_regs; - dev->em28xx_read_reg = em28xx_read_reg; - dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len; - dev->em28xx_write_regs_req = em28xx_write_regs_req; - dev->em28xx_read_reg_req = em28xx_read_reg_req; - dev->is_em2800 = em28xx_boards[dev->model].is_em2800; - - em28xx_pre_card_setup(dev); - - errCode = em28xx_config(dev); - if (errCode) { - em28xx_errdev("error configuring device\n"); - return -ENOMEM; - } - - /* register i2c bus */ - errCode = em28xx_i2c_register(dev); - if (errCode < 0) { - em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - - /* Do board specific init and eeprom reading */ - em28xx_card_setup(dev); - - /* Configure audio */ - errCode = em28xx_audio_analog_set(dev); - if (errCode < 0) { - em28xx_errdev("%s: em28xx_audio_analog_set - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - - /* configure the device */ - em28xx_config_i2c(dev); - - /* set default norm */ - dev->norm = em28xx_video_template.current_norm; - - maxw = norm_maxw(dev); - maxh = norm_maxh(dev); - - /* set default image size */ - dev->width = maxw; - dev->height = maxh; - dev->interlaced = EM28XX_INTERLACED_DEFAULT; - dev->hscale = 0; - dev->vscale = 0; - dev->ctl_input = 2; - - errCode = em28xx_config(dev); - if (errCode < 0) { - em28xx_errdev("%s: em28xx_config - errCode [%d]!\n", - __func__, errCode); - return errCode; - } - - /* init video dma queues */ - INIT_LIST_HEAD(&dev->vidq.active); - INIT_LIST_HEAD(&dev->vidq.queued); - - - if (dev->has_msp34xx) { - /* Send a reset to other chips via gpio */ - errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1); - if (errCode < 0) { - em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(1) failed! errCode [%d]\n", - __func__, errCode); - return errCode; - } - msleep(3); - - errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1); - if (errCode < 0) { - em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(2) failed! errCode [%d]\n", - __func__, errCode); - return errCode; - } - msleep(3); - } - - video_mux(dev, 0); - - mutex_lock(&em28xx_devlist_mutex); - list_add_tail(&dev->devlist, &em28xx_devlist); - retval = register_analog_devices(dev); - if (retval < 0) { - em28xx_release_resources(dev); - mutex_unlock(&em28xx_devlist_mutex); - goto fail_reg_devices; - } - - mutex_lock(&em28xx_extension_devlist_lock); - if (!list_empty(&em28xx_extension_devlist)) { - list_for_each_entry(ops, &em28xx_extension_devlist, next) { - if (ops->id) - ops->init(dev); - } - } - mutex_unlock(&em28xx_extension_devlist_lock); - mutex_unlock(&em28xx_devlist_mutex); - - return 0; - -fail_reg_devices: - mutex_unlock(&dev->lock); - return retval; -} - -#if defined(CONFIG_MODULES) && defined(MODULE) -static void request_module_async(struct work_struct *work) -{ - struct em28xx *dev = container_of(work, - struct em28xx, request_module_wk); - - if (dev->has_audio_class) - request_module("snd-usb-audio"); - else - request_module("em28xx-alsa"); - - if (dev->has_dvb) - request_module("em28xx-dvb"); -} - -static void request_modules(struct em28xx *dev) -{ - INIT_WORK(&dev->request_module_wk, request_module_async); - schedule_work(&dev->request_module_wk); -} -#else -#define request_modules(dev) -#endif /* CONFIG_MODULES */ - -/* - * em28xx_usb_probe() - * checks for supported devices - */ -static int em28xx_usb_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - const struct usb_endpoint_descriptor *endpoint; - struct usb_device *udev; - struct usb_interface *uif; - struct em28xx *dev = NULL; - int retval = -ENODEV; - int i, nr, ifnum; - - udev = usb_get_dev(interface_to_usbdev(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; - - /* 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, - ifnum, - interface->altsetting[0].desc.bInterfaceClass); - - 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, - ifnum, - interface->altsetting[0].desc.bInterfaceClass); - - endpoint = &interface->cur_altsetting->endpoint[1].desc; - - /* check if the device has the iso in endpoint at the correct place */ - 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); - 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); - return -ENODEV; - } - - if (nr >= EM28XX_MAXBOARDS) { - printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", - EM28XX_MAXBOARDS); - em28xx_devused &= ~(1<<nr); - return -ENOMEM; - } - - /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { - em28xx_err(DRIVER_NAME ": out of memory!\n"); - em28xx_devused &= ~(1<<nr); - return -ENOMEM; - } - - snprintf(dev->name, 29, "em28xx #%d", nr); - dev->devno = nr; - dev->model = id->driver_info; - dev->alt = -1; - - /* Checks if audio is provided by some interface */ - for (i = 0; i < udev->config->desc.bNumInterfaces; i++) { - uif = udev->config->interface[i]; - if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { - dev->has_audio_class = 1; - break; - } - } - - printk(KERN_INFO DRIVER_NAME " %s usb audio class\n", - dev->has_audio_class ? "Has" : "Doesn't have"); - - /* 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); - - if (dev->alt_max_pkt_size == NULL) { - em28xx_errdev("out of memory!\n"); - em28xx_devused &= ~(1<<nr); - kfree(dev); - return -ENOMEM; - } - - for (i = 0; i < dev->num_alt ; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc. - 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]); - } - - if ((card[nr] >= 0) && (card[nr] < em28xx_bcount)) - dev->model = card[nr]; - - /* allocate device struct */ - retval = em28xx_init_dev(&dev, udev, nr); - if (retval) { - em28xx_devused &= ~(1<<dev->devno); - kfree(dev); - - return retval; - } - - em28xx_info("Found %s\n", em28xx_boards[dev->model].name); - - /* save our data pointer in this interface device */ - usb_set_intfdata(interface, dev); - - request_modules(dev); - - return 0; -} - -/* - * em28xx_usb_disconnect() - * called when the device gets diconencted - * video device will be unregistered on v4l2_close in case it is still open - */ -static void em28xx_usb_disconnect(struct usb_interface *interface) -{ - struct em28xx *dev; - struct em28xx_ops *ops = NULL; - - dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - - if (!dev) - return; - - em28xx_info("disconnecting %s\n", dev->vdev->name); - - /* wait until all current v4l2 io is finished then deallocate - resources */ - mutex_lock(&dev->lock); - - wake_up_interruptible_all(&dev->open); - - if (dev->users) { - em28xx_warn - ("device /dev/video%d is open! Deregistration and memory " - "deallocation are deferred on close.\n", - dev->vdev->num); - - dev->state |= DEV_MISCONFIGURED; - em28xx_uninit_isoc(dev); - dev->state |= DEV_DISCONNECTED; - wake_up_interruptible(&dev->wait_frame); - wake_up_interruptible(&dev->wait_stream); - } else { - dev->state |= DEV_DISCONNECTED; - em28xx_release_resources(dev); - } - mutex_unlock(&dev->lock); - - mutex_lock(&em28xx_extension_devlist_lock); - if (!list_empty(&em28xx_extension_devlist)) { - list_for_each_entry(ops, &em28xx_extension_devlist, next) { - ops->fini(dev); - } - } - mutex_unlock(&em28xx_extension_devlist_lock); - - if (!dev->users) { - kfree(dev->alt_max_pkt_size); - kfree(dev); - } -} - -static struct usb_driver em28xx_usb_driver = { - .name = "em28xx", - .probe = em28xx_usb_probe, - .disconnect = em28xx_usb_disconnect, - .id_table = em28xx_id_table, -}; - -static int __init em28xx_module_init(void) -{ - int result; - - printk(KERN_INFO DRIVER_NAME " v4l2 driver version %d.%d.%d loaded\n", - (EM28XX_VERSION_CODE >> 16) & 0xff, - (EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff); -#ifdef SNAPSHOT - printk(KERN_INFO DRIVER_NAME " snapshot date %04d-%02d-%02d\n", - SNAPSHOT / 10000, (SNAPSHOT / 100) % 100, SNAPSHOT % 100); -#endif - - /* register this driver with the USB subsystem */ - result = usb_register(&em28xx_usb_driver); - if (result) - em28xx_err(DRIVER_NAME - " usb_register failed. Error number %d.\n", result); - - return result; -} - -static void __exit em28xx_module_exit(void) -{ - /* deregister this driver with the USB subsystem */ - usb_deregister(&em28xx_usb_driver); -} - -module_init(em28xx_module_init); -module_exit(em28xx_module_exit); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 5956e9b..b5eddc2 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -67,7 +67,6 @@ #define EM2820_BOARD_HERCULES_SMART_TV_USB2 26 #define EM2820_BOARD_PINNACLE_USB_2_FM1216ME 27 #define EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE 28 -#define EM2820_BOARD_PINNACLE_DVC_100 29 #define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30 #define EM2821_BOARD_USBGEAR_VD204 31 #define EM2821_BOARD_SUPERCOMP_USB_2 32 @@ -97,6 +96,8 @@ #define EM2882_BOARD_PINNACLE_HYBRID_PRO 56 #define EM2883_BOARD_KWORLD_HYBRID_A316 57 #define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58 +#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60 +#define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -159,7 +160,7 @@ #define EM2800_I2C_WRITE_TIMEOUT 20 enum em28xx_mode { - EM28XX_MODE_UNDEFINED, + EM28XX_SUSPEND, EM28XX_ANALOG_MODE, EM28XX_DIGITAL_MODE, }; @@ -207,9 +208,12 @@ struct em28xx_usb_isoc_ctl { }; +/* Struct to enumberate video formats */ struct em28xx_fmt { char *name; u32 fourcc; /* v4l2 format id */ + int depth; + int reg; }; /* buffer for one video frame */ @@ -255,54 +259,105 @@ enum enum28xx_itype { EM28XX_RADIO, }; +enum em28xx_ac97_mode { + EM28XX_NO_AC97 = 0, + EM28XX_AC97_EM202, + EM28XX_AC97_SIGMATEL, + EM28XX_AC97_OTHER, +}; + +struct em28xx_audio_mode { + enum em28xx_ac97_mode ac97; + + u16 ac97_feat; + u32 ac97_vendor_id; + + unsigned int has_audio:1; + + unsigned int i2s_3rates:1; + unsigned int i2s_5rates:1; +}; + +/* em28xx has two audio inputs: tuner and line in. + However, on most devices, an auxiliary AC97 codec device is used. + The AC97 device may have several different inputs and outputs, + depending on their model. So, it is possible to use AC97 mixer to + address more than two different entries. + */ enum em28xx_amux { - EM28XX_AMUX_VIDEO, - EM28XX_AMUX_LINE_IN, - EM28XX_AMUX_AC97_VIDEO, - EM28XX_AMUX_AC97_LINE_IN, + /* This is the only entry for em28xx tuner input */ + EM28XX_AMUX_VIDEO, /* em28xx tuner, AC97 mixer Video */ + + EM28XX_AMUX_LINE_IN, /* AC97 mixer Line In */ + + /* Some less-common mixer setups */ + EM28XX_AMUX_VIDEO2, /* em28xx Line in, AC97 mixer Video */ + EM28XX_AMUX_PHONE, + EM28XX_AMUX_MIC, + EM28XX_AMUX_CD, + EM28XX_AMUX_AUX, + EM28XX_AMUX_PCM_OUT, +}; + +enum em28xx_aout { + EM28XX_AOUT_MASTER = 1 << 0, + EM28XX_AOUT_LINE = 1 << 1, + EM28XX_AOUT_MONO = 1 << 2, + EM28XX_AOUT_LFE = 1 << 3, + EM28XX_AOUT_SURR = 1 << 4, +}; + +struct em28xx_reg_seq { + int reg; + unsigned char val, mask; + int sleep; }; struct em28xx_input { enum enum28xx_itype type; unsigned int vmux; enum em28xx_amux amux; + enum em28xx_aout aout; + struct em28xx_reg_seq *gpio; }; #define INPUT(nr) (&em28xx_boards[dev->model].input[nr]) enum em28xx_decoder { + EM28XX_NODECODER, EM28XX_TVP5150, - EM28XX_SAA7113, - EM28XX_SAA7114 -}; - -struct em28xx_reg_seq { - int reg; - unsigned char val, mask; - int sleep; + EM28XX_SAA711X, }; struct em28xx_board { char *name; int vchannels; int tuner_type; + int tuner_addr; /* i2c flags */ unsigned int tda9887_conf; + /* GPIO sequences */ + struct em28xx_reg_seq *dvb_gpio; + struct em28xx_reg_seq *suspend_gpio; + struct em28xx_reg_seq *tuner_gpio; + unsigned int is_em2800:1; unsigned int has_msp34xx:1; unsigned int mts_firmware:1; - unsigned int has_12mhz_i2s:1; unsigned int max_range_640_480:1; unsigned int has_dvb:1; unsigned int has_snapshot_button:1; unsigned int valid:1; + unsigned char xclk, i2c_speed; + enum em28xx_decoder decoder; struct em28xx_input input[MAX_EM28XX_INPUT]; struct em28xx_input radio; + IR_KEYTAB_TYPE *ir_codes; }; struct em28xx_eeprom { @@ -369,32 +424,26 @@ struct em28xx { 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 is_em2800:1; - unsigned int has_msp34xx:1; - unsigned int has_tda9887:1; + enum em28xx_chip_id chip_id; + + struct em28xx_board board; + unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; - unsigned int has_12mhz_i2s:1; - unsigned int max_range_640_480:1; - unsigned int has_dvb:1; - unsigned int has_snapshot_button:1; - unsigned int valid:1; /* report for validated boards */ + unsigned int has_alsa_audio:1; - /* Some older em28xx chips needs a waiting time after writing */ - unsigned int wait_after_write; + struct em28xx_fmt *format; - /* GPIO sequences for analog and digital mode */ - struct em28xx_reg_seq *analog_gpio, *digital_gpio; + struct em28xx_IR *ir; - /* GPIO sequences for tuner callbacks */ - struct em28xx_reg_seq *tun_analog_gpio, *tun_digital_gpio; + /* Some older em28xx chips needs a waiting time after writing */ + unsigned int wait_after_write; - int video_inputs; /* number of video inputs */ struct list_head devlist; u32 i2s_speed; /* I2S speed for audio digital stream */ - enum em28xx_decoder decoder; + struct em28xx_audio_mode audio_mode; int tuner_type; /* type of the tuner */ int tuner_addr; /* tuner address */ @@ -409,6 +458,7 @@ struct em28xx { int ctl_freq; /* selected frequency */ unsigned int ctl_input; /* selected input */ unsigned int ctl_ainput;/* selected audio input */ + unsigned int ctl_aoutput;/* selected audio output */ int mute; int volume; /* frame properties */ @@ -469,6 +519,9 @@ struct em28xx { enum em28xx_mode mode; + /* register numbers for GPO/GPIO registers */ + u16 reg_gpo_num, reg_gpio_num; + /* Caches GPO and GPIO registers */ unsigned char reg_gpo, reg_gpio; @@ -508,11 +561,17 @@ int em28xx_read_reg(struct em28xx *dev, u16 reg); int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, int len); int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len); +int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val); + +int em28xx_read_ac97(struct em28xx *dev, u8 reg); +int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val); + int em28xx_audio_analog_set(struct em28xx *dev); +int em28xx_audio_setup(struct em28xx *dev); int em28xx_colorlevels_set_default(struct em28xx *dev); int em28xx_capture_start(struct em28xx *dev, int start); -int em28xx_outfmt_set_yuv422(struct em28xx *dev); +int em28xx_set_outfmt(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, @@ -521,10 +580,20 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, 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 */ +void em28xx_wake_i2c(struct em28xx *dev); +void em28xx_remove_from_devlist(struct em28xx *dev); +void em28xx_add_into_devlist(struct em28xx *dev); +struct em28xx *em28xx_get_device(struct inode *inode, + enum v4l2_buf_type *fh_type, + int *has_radio); int em28xx_register_extension(struct em28xx_ops *dev); void em28xx_unregister_extension(struct em28xx_ops *dev); +void em28xx_init_extension(struct em28xx *dev); +void em28xx_close_extension(struct em28xx *dev); + +/* Provided by em28xx-video.c */ +int em28xx_register_analog_devices(struct em28xx *dev); +void em28xx_release_analog_resources(struct em28xx *dev); /* Provided by em28xx-cards.c */ extern int em2800_variant_detect(struct usb_device *udev, int model); @@ -535,9 +604,9 @@ 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 component, int command, int arg); +void em28xx_release_resources(struct em28xx *dev); /* Provided by em28xx-input.c */ -/* TODO: Check if the standard get_key handlers on ir-common can be used */ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); 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, @@ -545,6 +614,9 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, void em28xx_register_snapshot_button(struct em28xx *dev); void em28xx_deregister_snapshot_button(struct em28xx *dev); +int em28xx_ir_init(struct em28xx *dev); +int em28xx_ir_fini(struct em28xx *dev); + /* printk macros */ #define em28xx_err(fmt, arg...) do {\ @@ -564,7 +636,7 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev); static inline int em28xx_compression_disable(struct em28xx *dev) { /* side effect of disabling scaler and mixer */ - return em28xx_write_regs(dev, EM28XX_R26_COMPR, "\x00", 1); + return em28xx_write_reg(dev, EM28XX_R26_COMPR, 0x00); } static inline int em28xx_contrast_get(struct em28xx *dev) @@ -636,7 +708,7 @@ static inline int em28xx_gamma_set(struct em28xx *dev, s32 val) /*FIXME: maxw should be dependent of alt mode */ static inline unsigned int norm_maxw(struct em28xx *dev) { - if (dev->max_range_640_480) + if (dev->board.max_range_640_480) return 640; else return 720; @@ -644,7 +716,7 @@ static inline unsigned int norm_maxw(struct em28xx *dev) static inline unsigned int norm_maxh(struct em28xx *dev) { - if (dev->max_range_640_480) + if (dev->board.max_range_640_480) return 480; else return (dev->norm & V4L2_STD_625_50) ? 576 : 480; diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c index 9d0ef96..83c0711 100644 --- a/drivers/media/video/et61x251/et61x251_core.c +++ b/drivers/media/video/et61x251/et61x251_core.c @@ -1581,7 +1581,7 @@ et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg) strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) - strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, + strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), sizeof(cap.bus_info)); if (copy_to_user(arg, &cap, sizeof(cap))) diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index 6b557c0..ee6a691 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -12,12 +12,13 @@ menuconfig USB_GSPCA "Video For Linux" to use this driver. To compile this driver as modules, choose M here: the - modules will be called gspca_main. + module will be called gspca_main. if USB_GSPCA && VIDEO_V4L2 source "drivers/media/video/gspca/m5602/Kconfig" +source "drivers/media/video/gspca/stv06xx/Kconfig" config USB_GSPCA_CONEX tristate "Conexant Camera Driver" @@ -64,6 +65,16 @@ config USB_GSPCA_OV519 To compile this driver as a module, choose M here: the module will be called gspca_ov519. +config USB_GSPCA_OV534 + tristate "OV534 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the OV534 chip. + (e.g. Sony Playstation EYE) + + To compile this driver as a module, choose M here: the + module will be called gspca_ov534. + config USB_GSPCA_PAC207 tristate "Pixart PAC207 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA @@ -83,10 +94,11 @@ config USB_GSPCA_PAC7311 module will be called gspca_pac7311. config USB_GSPCA_SONIXB - tristate "SN9C102 USB Camera Driver" + tristate "SONIX Bayer USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the SONIXB chip. + Say Y here if you want support for cameras based on the Sonix + chips with Bayer format (SN9C101, SN9C102 and SN9C103). To compile this driver as a module, choose M here: the module will be called gspca_sonixb. @@ -95,7 +107,8 @@ config USB_GSPCA_SONIXJ tristate "SONIX JPEG USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the SONIXJ chip. + Say Y here if you want support for cameras based on the Sonix + chips with JPEG format (SN9C102P, SN9C105 and >= SN9C110). To compile this driver as a module, choose M here: the module will be called gspca_sonixj @@ -171,7 +184,7 @@ config USB_GSPCA_SUNPLUS SPCA504(abc) SPCA533 SPCA536 chips. To compile this driver as a module, choose M here: the - module will be called gspca_spca5xx. + module will be called gspca_sunplus. config USB_GSPCA_T613 tristate "T613 (JPEG Compliance) USB Camera Driver" diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 22734f5..bd8d9ee 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o +obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o @@ -27,6 +28,7 @@ gspca_etoms-objs := etoms.o gspca_finepix-objs := finepix.o gspca_mars-objs := mars.o gspca_ov519-objs := ov519.o +gspca_ov534-objs := ov534.o gspca_pac207-objs := pac207.o gspca_pac7311-objs := pac7311.o gspca_sonixb-objs := sonixb.o @@ -45,4 +47,4 @@ gspca_vc032x-objs := vc032x.o gspca_zc3xx-objs := zc3xx.o obj-$(CONFIG_USB_M5602) += m5602/ - +obj-$(CONFIG_USB_STV06XX) += stv06xx/ diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index de28354..1753f5b 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -93,7 +93,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_mode[] = { {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144 * 3 / 8 + 590, diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c index 3be30b4..f3cd8ff 100644 --- a/drivers/media/video/gspca/etoms.c +++ b/drivers/media/video/gspca/etoms.c @@ -112,7 +112,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_mode[] = { {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240, @@ -125,7 +125,7 @@ static struct v4l2_pix_format vga_mode[] = { .priv = 0}, */ }; -static struct v4l2_pix_format sif_mode[] = { +static const struct v4l2_pix_format sif_mode[] = { {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144, diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c index 607942f..afc8b2d 100644 --- a/drivers/media/video/gspca/finepix.c +++ b/drivers/media/video/gspca/finepix.c @@ -72,7 +72,7 @@ struct usb_fpix { } /* These cameras only support 320x200. */ -static struct v4l2_pix_format fpix_mode[1] = { +static const struct v4l2_pix_format fpix_mode[1] = { { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, @@ -314,9 +314,6 @@ static int sd_start(struct gspca_dev *gspca_dev) int ret; int size_ret; - /* Reset bulk in endpoint */ - usb_clear_halt(gspca_dev->dev, gspca_dev->cam.epaddr); - /* Init the device */ memset(gspca_dev->usb_buf, 0, 12); gspca_dev->usb_buf[0] = 0xc6; diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 02a6e9e..8b9f3bd 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -30,7 +30,6 @@ #include <linux/string.h> #include <linux/pagemap.h> #include <linux/io.h> -#include <linux/kref.h> #include <asm/page.h> #include <linux/uaccess.h> #include <linux/jiffies.h> @@ -45,7 +44,7 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 3, 0) +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 4, 0) static int video_nr = -1; @@ -150,8 +149,11 @@ static void fill_frame(struct gspca_dev *gspca_dev, /* check the packet status and length */ len = urb->iso_frame_desc[i].actual_length; - if (len == 0) + if (len == 0) { + if (gspca_dev->empty_packet == 0) + gspca_dev->empty_packet = 1; continue; + } st = urb->iso_frame_desc[i].status; if (st) { PDEBUG(D_ERR, @@ -170,7 +172,6 @@ static void fill_frame(struct gspca_dev *gspca_dev, } /* resubmit the URB */ - urb->status = 0; st = usb_submit_urb(urb, GFP_ATOMIC); if (st < 0) PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st); @@ -200,11 +201,18 @@ static void bulk_irq(struct urb *urb { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; struct gspca_frame *frame; + int st; PDEBUG(D_PACK, "bulk irq"); if (!gspca_dev->streaming) return; - if (urb->status != 0 && urb->status != -ECONNRESET) { + switch (urb->status) { + case 0: + break; + case -ECONNRESET: + urb->status = 0; + break; + default: #ifdef CONFIG_PM if (!gspca_dev->frozen) #endif @@ -223,6 +231,13 @@ static void bulk_irq(struct urb *urb urb->transfer_buffer, urb->actual_length); } + + /* resubmit the URB */ + if (gspca_dev->cam.bulk_nurbs != 0) { + st = usb_submit_urb(urb, GFP_ATOMIC); + if (st < 0) + PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st); + } } /* @@ -285,7 +300,6 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, frame->v4l2_buf.bytesused = frame->data_end - frame->data; frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED; frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE; - atomic_inc(&gspca_dev->nevent); wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ i = (gspca_dev->fr_i + 1) % gspca_dev->nframes; gspca_dev->fr_i = i; @@ -379,7 +393,6 @@ static int frame_alloc(struct gspca_dev *gspca_dev, gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; gspca_dev->last_packet_type = DISCARD_PACKET; gspca_dev->sequence = 0; - atomic_set(&gspca_dev->nevent, 0); return 0; } @@ -520,11 +533,14 @@ static int create_urbs(struct gspca_dev *gspca_dev, nurbs = DEF_NURBS; } else { /* bulk */ npkt = 0; - bsize = gspca_dev->cam. bulk_size; + bsize = gspca_dev->cam.bulk_size; if (bsize == 0) bsize = psize; PDEBUG(D_STREAM, "bulk bsize:%d", bsize); - nurbs = 1; + if (gspca_dev->cam.bulk_nurbs != 0) + nurbs = gspca_dev->cam.bulk_nurbs; + else + nurbs = 1; } gspca_dev->nurbs = nurbs; @@ -597,6 +613,12 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) if (ret < 0) goto out; + /* clear the bulk endpoint */ + if (gspca_dev->alt == 0) /* if bulk transfer */ + usb_clear_halt(gspca_dev->dev, + usb_rcvintpipe(gspca_dev->dev, + gspca_dev->cam.epaddr)); + /* start the cam */ ret = gspca_dev->sd_desc->start(gspca_dev); if (ret < 0) { @@ -604,10 +626,9 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) goto out; } gspca_dev->streaming = 1; - atomic_set(&gspca_dev->nevent, 0); - /* bulk transfers are started by the subdriver */ - if (gspca_dev->alt == 0) + /* some bulk transfers are started by the subdriver */ + if (gspca_dev->alt == 0 && gspca_dev->cam.bulk_nurbs == 0) break; /* submit the URBs */ @@ -618,8 +639,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) "usb_submit_urb [%d] err %d", n, ret); gspca_dev->streaming = 0; destroy_urbs(gspca_dev); - if (ret == -ENOSPC) + if (ret == -ENOSPC) { + msleep(20); /* wait for kill + * complete */ break; /* try the previous alt */ + } goto out; } } @@ -637,7 +661,7 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0); if (ret < 0) - PDEBUG(D_ERR|D_STREAM, "set interface 0 err %d", ret); + PDEBUG(D_ERR|D_STREAM, "set alt 0 err %d", ret); return ret; } @@ -645,7 +669,6 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) static void gspca_stream_off(struct gspca_dev *gspca_dev) { gspca_dev->streaming = 0; - atomic_set(&gspca_dev->nevent, 0); if (gspca_dev->present && gspca_dev->sd_desc->stopN) gspca_dev->sd_desc->stopN(gspca_dev); @@ -727,7 +750,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, if (fmtdesc->index == index) break; /* new format */ index++; - if (index >= sizeof fmt_tb / sizeof fmt_tb[0]) + if (index >= ARRAY_SIZE(fmt_tb)) return -EINVAL; } } @@ -752,8 +775,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct gspca_dev *gspca_dev = priv; int mode; - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; mode = gspca_dev->curr_mode; memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode], sizeof fmt->fmt.pix); @@ -765,8 +786,6 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, { int w, h, mode, mode2; - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; w = fmt->fmt.pix.width; h = fmt->fmt.pix.height; @@ -846,11 +865,11 @@ out: return ret; } -static void gspca_delete(struct kref *kref) +static void gspca_release(struct video_device *vfd) { - struct gspca_dev *gspca_dev = container_of(kref, struct gspca_dev, kref); + struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev); - PDEBUG(D_STREAM, "device deleted"); + PDEBUG(D_STREAM, "device released"); kfree(gspca_dev->usb_buf); kfree(gspca_dev); @@ -862,7 +881,7 @@ static int dev_open(struct inode *inode, struct file *file) int ret; PDEBUG(D_STREAM, "%s open", current->comm); - gspca_dev = video_drvdata(file); + gspca_dev = (struct gspca_dev *) video_devdata(file); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; if (!gspca_dev->present) { @@ -883,17 +902,14 @@ static int dev_open(struct inode *inode, struct file *file) gspca_dev->users++; - /* one more user */ - kref_get(&gspca_dev->kref); - file->private_data = gspca_dev; #ifdef GSPCA_DEBUG /* activate the v4l2 debug */ if (gspca_debug & D_V4L2) - gspca_dev->vdev->debug |= V4L2_DEBUG_IOCTL + gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; else - gspca_dev->vdev->debug &= ~(V4L2_DEBUG_IOCTL + gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG); #endif ret = 0; @@ -932,8 +948,6 @@ static int dev_close(struct inode *inode, struct file *file) PDEBUG(D_STREAM, "close done"); - kref_put(&gspca_dev->kref, gspca_delete); - return 0; } @@ -1053,6 +1067,35 @@ static int vidioc_g_ctrl(struct file *file, void *priv, return -EINVAL; } +/*fixme: have an audio flag in gspca_dev?*/ +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *audio) +{ + if (audio->index != 0) + return -EINVAL; + return 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *audio) +{ + memset(audio, 0, sizeof *audio); + strcpy(audio->name, "Microphone"); + return 0; +} + +static int vidioc_enumaudio(struct file *file, void *priv, + struct v4l2_audio *audio) +{ + if (audio->index != 0) + return -EINVAL; + + strcpy(audio->name, "Microphone"); + audio->capability = 0; + audio->mode = 0; + return 0; +} + static int vidioc_querymenu(struct file *file, void *priv, struct v4l2_querymenu *qmenu) { @@ -1096,8 +1139,6 @@ static int vidioc_reqbufs(struct file *file, void *priv, struct gspca_dev *gspca_dev = priv; int i, ret = 0; - if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; switch (rb->memory) { case GSPCA_MEMORY_READ: /* (internal call) */ case V4L2_MEMORY_MMAP: @@ -1162,8 +1203,7 @@ static int vidioc_querybuf(struct file *file, void *priv, struct gspca_dev *gspca_dev = priv; struct gspca_frame *frame; - if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE - || v4l2_buf->index < 0 + if (v4l2_buf->index < 0 || v4l2_buf->index >= gspca_dev->nframes) return -EINVAL; @@ -1186,7 +1226,8 @@ static int vidioc_streamon(struct file *file, void *priv, ret = -ENODEV; goto out; } - if (gspca_dev->nframes == 0) { + if (gspca_dev->nframes == 0 + || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) { ret = -EINVAL; goto out; } @@ -1236,7 +1277,6 @@ static int vidioc_streamoff(struct file *file, void *priv, gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; gspca_dev->last_packet_type = DISCARD_PACKET; gspca_dev->sequence = 0; - atomic_set(&gspca_dev->nevent, 0); ret = 0; out: mutex_unlock(&gspca_dev->queue_lock); @@ -1281,6 +1321,17 @@ static int vidioc_g_parm(struct file *filp, void *priv, memset(parm, 0, sizeof *parm); parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; parm->parm.capture.readbuffers = gspca_dev->nbufread; + + if (gspca_dev->sd_desc->get_streamparm) { + int ret; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + ret = gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); + mutex_unlock(&gspca_dev->usb_lock); + return ret; + } + return 0; } @@ -1295,6 +1346,17 @@ static int vidioc_s_parm(struct file *filp, void *priv, parm->parm.capture.readbuffers = gspca_dev->nbufread; else gspca_dev->nbufread = n; + + if (gspca_dev->sd_desc->set_streamparm) { + int ret; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + ret = gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); + mutex_unlock(&gspca_dev->usb_lock); + return ret; + } + return 0; } @@ -1440,33 +1502,22 @@ static int frame_wait(struct gspca_dev *gspca_dev, i = gspca_dev->fr_o; j = gspca_dev->fr_queue[i]; frame = &gspca_dev->frame[j]; - if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) { - atomic_dec(&gspca_dev->nevent); - goto ok; - } - if (nonblock_ing) /* no frame yet */ - return -EAGAIN; - /* wait till a frame is ready */ - for (;;) { + if (!(frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE)) { + if (nonblock_ing) + return -EAGAIN; + + /* wait till a frame is ready */ ret = wait_event_interruptible_timeout(gspca_dev->wq, - atomic_read(&gspca_dev->nevent) > 0, - msecs_to_jiffies(3000)); - if (ret <= 0) { - if (ret < 0) - return ret; /* interrupt */ - return -EIO; /* timeout */ - } - atomic_dec(&gspca_dev->nevent); - if (!gspca_dev->streaming || !gspca_dev->present) + (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) || + !gspca_dev->streaming || !gspca_dev->present, + msecs_to_jiffies(3000)); + if (ret < 0) + return ret; + if (ret == 0 || !gspca_dev->streaming || !gspca_dev->present) return -EIO; - i = gspca_dev->fr_o; - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) - break; } -ok: + gspca_dev->fr_o = (i + 1) % gspca_dev->nframes; PDEBUG(D_FRAM, "frame wait q:%d i:%d o:%d", gspca_dev->fr_q, @@ -1494,8 +1545,6 @@ static int vidioc_dqbuf(struct file *file, void *priv, int i, ret; PDEBUG(D_FRAM, "dqbuf"); - if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; if (v4l2_buf->memory != gspca_dev->memory) return -EINVAL; @@ -1550,8 +1599,6 @@ static int vidioc_qbuf(struct file *file, void *priv, int i, index, ret; PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index); - if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; @@ -1761,7 +1808,7 @@ static struct file_operations dev_fops = { .release = dev_close, .read = dev_read, .mmap = dev_mmap, - .ioctl = video_ioctl2, + .unlocked_ioctl = __video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, #endif @@ -1781,6 +1828,9 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_enumaudio = vidioc_enumaudio, .vidioc_querymenu = vidioc_querymenu, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, @@ -1802,7 +1852,7 @@ static struct video_device gspca_template = { .name = "gspca main driver", .fops = &dev_fops, .ioctl_ops = &dev_ioctl_ops, - .release = video_device_release, + .release = gspca_release, .minor = -1, }; @@ -1840,7 +1890,6 @@ int gspca_dev_probe(struct usb_interface *intf, err("couldn't kzalloc gspca struct"); return -ENOMEM; } - kref_init(&gspca_dev->kref); gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); if (!gspca_dev->usb_buf) { err("out of memory"); @@ -1852,12 +1901,13 @@ int gspca_dev_probe(struct usb_interface *intf, gspca_dev->nbalt = intf->num_altsetting; gspca_dev->sd_desc = sd_desc; gspca_dev->nbufread = 2; + gspca_dev->empty_packet = -1; /* don't check the empty packets */ /* configure the subdriver and initialize the USB device */ - ret = gspca_dev->sd_desc->config(gspca_dev, id); + ret = sd_desc->config(gspca_dev, id); if (ret < 0) goto out; - ret = gspca_dev->sd_desc->init(gspca_dev); + ret = sd_desc->init(gspca_dev); if (ret < 0) goto out; ret = gspca_set_alt0(gspca_dev); @@ -1871,18 +1921,15 @@ int gspca_dev_probe(struct usb_interface *intf, init_waitqueue_head(&gspca_dev->wq); /* init video stuff */ - gspca_dev->vdev = video_device_alloc(); - memcpy(gspca_dev->vdev, &gspca_template, sizeof gspca_template); - gspca_dev->vdev->parent = &dev->dev; + memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template); + gspca_dev->vdev.parent = &dev->dev; gspca_dev->module = module; gspca_dev->present = 1; - video_set_drvdata(gspca_dev->vdev, gspca_dev); - ret = video_register_device(gspca_dev->vdev, + ret = video_register_device(&gspca_dev->vdev, VFL_TYPE_GRABBER, video_nr); if (ret < 0) { err("video_register_device err %d", ret); - video_device_release(gspca_dev->vdev); goto out; } @@ -1906,15 +1953,14 @@ void gspca_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); - usb_set_intfdata(intf, NULL); - -/* We don't want people trying to open up the device */ - video_unregister_device(gspca_dev->vdev); - gspca_dev->present = 0; gspca_dev->streaming = 0; - kref_put(&gspca_dev->kref, gspca_delete); + usb_set_intfdata(intf, NULL); + + /* release the device */ + /* (this will call gspca_release() immediatly or on last close) */ + video_unregister_device(&gspca_dev->vdev); PDEBUG(D_PROBE, "disconnect complete"); } @@ -1992,7 +2038,7 @@ int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, desired lumination fast (with the risc of a slight overshoot) */ steps = abs(desired_avg_lum - avg_lum) / deadzone; - PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n", + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", avg_lum, desired_avg_lum, steps); for (i = 0; i < steps; i++) { diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index d25e8d69..c90af9c 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -56,8 +56,12 @@ extern int gspca_debug; /* device information - set at probe time */ struct cam { int bulk_size; /* buffer size when image transfer by bulk */ - struct v4l2_pix_format *cam_mode; /* size nmodes */ + const struct v4l2_pix_format *cam_mode; /* size nmodes */ char nmodes; + __u8 bulk_nurbs; /* number of URBs in bulk mode + * - cannot be > MAX_NURBS + * - when 0 and bulk_size != 0 means + * 1 URB and submit done by subdriver */ __u8 epaddr; }; @@ -70,6 +74,8 @@ typedef void (*cam_v_op) (struct gspca_dev *); typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *); typedef int (*cam_jpg_op) (struct gspca_dev *, struct v4l2_jpegcompression *); +typedef int (*cam_streamparm_op) (struct gspca_dev *, + struct v4l2_streamparm *); typedef int (*cam_qmnu_op) (struct gspca_dev *, struct v4l2_querymenu *); typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, @@ -102,6 +108,8 @@ struct sd_desc { cam_jpg_op get_jcomp; cam_jpg_op set_jcomp; cam_qmnu_op querymenu; + cam_streamparm_op get_streamparm; + cam_streamparm_op set_streamparm; }; /* packet types when moving from iso buf to frame buf */ @@ -120,10 +128,9 @@ struct gspca_frame { }; struct gspca_dev { - struct video_device *vdev; + struct video_device vdev; /* !! must be the first item */ struct module *module; /* subdriver handling the device */ struct usb_device *dev; - struct kref kref; struct file *capt_file; /* file doing video capture */ struct cam cam; /* device information */ @@ -142,22 +149,20 @@ struct gspca_dev { char fr_q; /* next frame to queue */ char fr_o; /* next frame to dequeue */ signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */ - char last_packet_type; + __u8 last_packet_type; + __s8 empty_packet; /* if (-1) don't check empty packets */ + __u8 streaming; - __u8 iface; /* USB interface number */ - __u8 alt; /* USB alternate setting */ __u8 curr_mode; /* current camera mode */ __u32 pixfmt; /* current mode parameters */ __u16 width; __u16 height; + __u32 sequence; /* frame sequence number */ - atomic_t nevent; /* number of frames done */ wait_queue_head_t wq; /* wait queue */ struct mutex usb_lock; /* usb exchange protection */ struct mutex read_lock; /* read protection */ struct mutex queue_lock; /* ISOC queue protection */ - __u32 sequence; /* frame sequence number */ - char streaming; #ifdef CONFIG_PM char frozen; /* suspend - resume */ #endif @@ -166,6 +171,8 @@ struct gspca_dev { char nbufread; /* number of buffers for read() */ char nurbs; /* number of allocated URBs */ char memory; /* memory type (V4L2_MEMORY_xxx) */ + __u8 iface; /* USB interface number */ + __u8 alt; /* USB alternate setting */ __u8 nbalt; /* number of USB alternate settings */ }; diff --git a/drivers/media/video/gspca/m5602/m5602_bridge.h b/drivers/media/video/gspca/m5602/m5602_bridge.h index 1a37ae4..a3f3b7a 100644 --- a/drivers/media/video/gspca/m5602/m5602_bridge.h +++ b/drivers/media/video/gspca/m5602/m5602_bridge.h @@ -25,59 +25,59 @@ /*****************************************************************************/ -#define M5602_XB_SENSOR_TYPE 0x00 -#define M5602_XB_SENSOR_CTRL 0x01 -#define M5602_XB_LINE_OF_FRAME_H 0x02 -#define M5602_XB_LINE_OF_FRAME_L 0x03 -#define M5602_XB_PIX_OF_LINE_H 0x04 -#define M5602_XB_PIX_OF_LINE_L 0x05 -#define M5602_XB_VSYNC_PARA 0x06 -#define M5602_XB_HSYNC_PARA 0x07 -#define M5602_XB_TEST_MODE_1 0x08 -#define M5602_XB_TEST_MODE_2 0x09 -#define M5602_XB_SIG_INI 0x0a -#define M5602_XB_DS_PARA 0x0e -#define M5602_XB_TRIG_PARA 0x0f -#define M5602_XB_CLK_PD 0x10 -#define M5602_XB_MCU_CLK_CTRL 0x12 -#define M5602_XB_MCU_CLK_DIV 0x13 -#define M5602_XB_SEN_CLK_CTRL 0x14 -#define M5602_XB_SEN_CLK_DIV 0x15 -#define M5602_XB_AUD_CLK_CTRL 0x16 -#define M5602_XB_AUD_CLK_DIV 0x17 -#define M5602_XB_DEVCTR1 0x41 -#define M5602_XB_EPSETR0 0x42 -#define M5602_XB_EPAFCTR 0x47 -#define M5602_XB_EPBFCTR 0x49 -#define M5602_XB_EPEFCTR 0x4f -#define M5602_XB_TEST_REG 0x53 -#define M5602_XB_ALT2SIZE 0x54 -#define M5602_XB_ALT3SIZE 0x55 -#define M5602_XB_OBSFRAME 0x56 -#define M5602_XB_PWR_CTL 0x59 -#define M5602_XB_ADC_CTRL 0x60 -#define M5602_XB_ADC_DATA 0x61 -#define M5602_XB_MISC_CTRL 0x62 -#define M5602_XB_SNAPSHOT 0x63 -#define M5602_XB_SCRATCH_1 0x64 -#define M5602_XB_SCRATCH_2 0x65 -#define M5602_XB_SCRATCH_3 0x66 -#define M5602_XB_SCRATCH_4 0x67 -#define M5602_XB_I2C_CTRL 0x68 -#define M5602_XB_I2C_CLK_DIV 0x69 -#define M5602_XB_I2C_DEV_ADDR 0x6a -#define M5602_XB_I2C_REG_ADDR 0x6b -#define M5602_XB_I2C_DATA 0x6c -#define M5602_XB_I2C_STATUS 0x6d -#define M5602_XB_GPIO_DAT_H 0x70 -#define M5602_XB_GPIO_DAT_L 0x71 -#define M5602_XB_GPIO_DIR_H 0x72 -#define M5602_XB_GPIO_DIR_L 0x73 -#define M5602_XB_GPIO_EN_H 0x74 -#define M5602_XB_GPIO_EN_L 0x75 -#define M5602_XB_GPIO_DAT 0x76 -#define M5602_XB_GPIO_DIR 0x77 -#define M5602_XB_MISC_CTL 0x70 +#define M5602_XB_SENSOR_TYPE 0x00 +#define M5602_XB_SENSOR_CTRL 0x01 +#define M5602_XB_LINE_OF_FRAME_H 0x02 +#define M5602_XB_LINE_OF_FRAME_L 0x03 +#define M5602_XB_PIX_OF_LINE_H 0x04 +#define M5602_XB_PIX_OF_LINE_L 0x05 +#define M5602_XB_VSYNC_PARA 0x06 +#define M5602_XB_HSYNC_PARA 0x07 +#define M5602_XB_TEST_MODE_1 0x08 +#define M5602_XB_TEST_MODE_2 0x09 +#define M5602_XB_SIG_INI 0x0a +#define M5602_XB_DS_PARA 0x0e +#define M5602_XB_TRIG_PARA 0x0f +#define M5602_XB_CLK_PD 0x10 +#define M5602_XB_MCU_CLK_CTRL 0x12 +#define M5602_XB_MCU_CLK_DIV 0x13 +#define M5602_XB_SEN_CLK_CTRL 0x14 +#define M5602_XB_SEN_CLK_DIV 0x15 +#define M5602_XB_AUD_CLK_CTRL 0x16 +#define M5602_XB_AUD_CLK_DIV 0x17 +#define M5602_XB_DEVCTR1 0x41 +#define M5602_XB_EPSETR0 0x42 +#define M5602_XB_EPAFCTR 0x47 +#define M5602_XB_EPBFCTR 0x49 +#define M5602_XB_EPEFCTR 0x4f +#define M5602_XB_TEST_REG 0x53 +#define M5602_XB_ALT2SIZE 0x54 +#define M5602_XB_ALT3SIZE 0x55 +#define M5602_XB_OBSFRAME 0x56 +#define M5602_XB_PWR_CTL 0x59 +#define M5602_XB_ADC_CTRL 0x60 +#define M5602_XB_ADC_DATA 0x61 +#define M5602_XB_MISC_CTRL 0x62 +#define M5602_XB_SNAPSHOT 0x63 +#define M5602_XB_SCRATCH_1 0x64 +#define M5602_XB_SCRATCH_2 0x65 +#define M5602_XB_SCRATCH_3 0x66 +#define M5602_XB_SCRATCH_4 0x67 +#define M5602_XB_I2C_CTRL 0x68 +#define M5602_XB_I2C_CLK_DIV 0x69 +#define M5602_XB_I2C_DEV_ADDR 0x6a +#define M5602_XB_I2C_REG_ADDR 0x6b +#define M5602_XB_I2C_DATA 0x6c +#define M5602_XB_I2C_STATUS 0x6d +#define M5602_XB_GPIO_DAT_H 0x70 +#define M5602_XB_GPIO_DAT_L 0x71 +#define M5602_XB_GPIO_DIR_H 0x72 +#define M5602_XB_GPIO_DIR_L 0x73 +#define M5602_XB_GPIO_EN_H 0x74 +#define M5602_XB_GPIO_EN_L 0x75 +#define M5602_XB_GPIO_DAT 0x76 +#define M5602_XB_GPIO_DIR 0x77 +#define M5602_XB_MISC_CTL 0x70 #define I2C_BUSY 0x80 @@ -90,13 +90,7 @@ #define M5602_ISOC_ENDPOINT_ADDR 0x81 #define M5602_INTR_ENDPOINT_ADDR 0x82 -#define M5602_MAX_FRAMES 32 -#define M5602_URBS 2 -#define M5602_ISOC_PACKETS 14 - -#define M5602_URB_TIMEOUT msecs_to_jiffies(2 * M5602_ISOC_PACKETS) #define M5602_URB_MSG_TIMEOUT 5000 -#define M5602_FRAME_TIMEOUT 2 /*****************************************************************************/ @@ -115,7 +109,6 @@ static const unsigned char sensor_urb_skeleton[] = { 0x13, M5602_XB_I2C_CTRL, 0x81, 0x11 }; -/* m5602 device descriptor, currently it just wraps the m5602_camera struct */ struct sd { struct gspca_dev gspca_dev; @@ -140,4 +133,10 @@ int m5602_read_bridge( int m5602_write_bridge( struct sd *sd, u8 address, u8 i2c_data); +int m5602_write_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + +int m5602_read_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + #endif diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c index fd6ce38..ed906fe 100644 --- a/drivers/media/video/gspca/m5602/m5602_core.c +++ b/drivers/media/video/gspca/m5602/m5602_core.c @@ -24,7 +24,7 @@ /* Kernel module parameters */ int force_sensor; -int dump_bridge; +static int dump_bridge; int dump_sensor; static const __devinitdata struct usb_device_id m5602_table[] = { @@ -80,6 +80,97 @@ int m5602_write_bridge(struct sd *sd, u8 address, u8 i2c_data) return (err < 0) ? err : 0; } +int m5602_read_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len) +{ + int err, i; + + if (!len || len > sd->sensor->i2c_regW) + return -EINVAL; + + do { + err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); + } while ((*i2c_data & I2C_BUSY) && !err); + if (err < 0) + goto out; + + err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, + sd->sensor->i2c_slave_id); + if (err < 0) + goto out; + + err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); + if (err < 0) + goto out; + + if (sd->sensor->i2c_regW == 1) { + err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, len); + if (err < 0) + goto out; + + err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08); + if (err < 0) + goto out; + } else { + err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len); + if (err < 0) + goto out; + } + + for (i = 0; (i < len) && !err; i++) { + err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); + + PDEBUG(D_CONF, "Reading sensor register " + "0x%x containing 0x%x ", address, *i2c_data); + } +out: + return err; +} + +int m5602_write_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len) +{ + int err, i; + u8 *p; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + /* No sensor with a data width larger than 16 bits has yet been seen */ + if (len > sd->sensor->i2c_regW || !len) + return -EINVAL; + + memcpy(buf, sensor_urb_skeleton, + sizeof(sensor_urb_skeleton)); + + buf[11] = sd->sensor->i2c_slave_id; + buf[15] = address; + + /* Special case larger sensor writes */ + p = buf + 16; + + /* Copy a four byte write sequence for each byte to be written to */ + for (i = 0; i < len; i++) { + memcpy(p, sensor_urb_skeleton + 16, 4); + p[3] = i2c_data[i]; + p += 4; + PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x", + address, i2c_data[i]); + } + + /* Copy the tailer */ + memcpy(p, sensor_urb_skeleton + 20, 4); + + /* Set the total length */ + p[3] = 0x10 + len; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x19, + 0x0000, buf, + 20 + len * 4, M5602_URB_MSG_TIMEOUT); + + return (err < 0) ? err : 0; +} + /* Dump all the registers of the m5602 bridge, unfortunately this breaks the camera until it's power cycled */ static void m5602_dump_bridge(struct sd *sd) @@ -150,11 +241,15 @@ static int m5602_start_transfer(struct gspca_dev *gspca_dev) /* Send start command to the camera */ const u8 buffer[4] = {0x13, 0xf9, 0x0f, 0x01}; + + if (sd->sensor->start) + sd->sensor->start(sd); + memcpy(buf, buffer, sizeof(buffer)); err = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 0x04, 0x40, 0x19, 0x0000, buf, - 4, M5602_URB_MSG_TIMEOUT); + sizeof(buffer), M5602_URB_MSG_TIMEOUT); PDEBUG(D_STREAM, "Transfer started"); return (err < 0) ? err : 0; @@ -284,6 +379,7 @@ static int __init mod_m5602_init(void) PDEBUG(D_PROBE, "registered"); return 0; } + static void __exit mod_m5602_exit(void) { usb_deregister(&sd_driver); diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c index fb700c2..c0e71c3 100644 --- a/drivers/media/video/gspca/m5602/m5602_mt9m111.c +++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.c @@ -18,6 +18,8 @@ #include "m5602_mt9m111.h" +static void mt9m111_dump_registers(struct sd *sd); + int mt9m111_probe(struct sd *sd) { u8 data[2] = {0x00, 0x00}; @@ -44,12 +46,12 @@ int mt9m111_probe(struct sd *sd) } else { data[0] = preinit_mt9m111[i][2]; data[1] = preinit_mt9m111[i][3]; - mt9m111_write_sensor(sd, + m5602_write_sensor(sd, preinit_mt9m111[i][1], data, 2); } } - if (mt9m111_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2)) + if (m5602_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2)) return -ENODEV; if ((data[0] == 0x14) && (data[1] == 0x3a)) { @@ -72,7 +74,7 @@ int mt9m111_init(struct sd *sd) int i, err = 0; /* Init the sensor */ - for (i = 0; i < ARRAY_SIZE(init_mt9m111); i++) { + for (i = 0; i < ARRAY_SIZE(init_mt9m111) && !err; i++) { u8 data[2]; if (init_mt9m111[i][0] == BRIDGE) { @@ -82,7 +84,7 @@ int mt9m111_init(struct sd *sd) } else { data[0] = init_mt9m111[i][2]; data[1] = init_mt9m111[i][3]; - err = mt9m111_write_sensor(sd, + err = m5602_write_sensor(sd, init_mt9m111[i][1], data, 2); } } @@ -104,12 +106,12 @@ int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) u8 data[2] = {0x00, 0x00}; struct sd *sd = (struct sd *) gspca_dev; - err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, + err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); *val = data[0] & MT9M111_RMB_MIRROR_ROWS; PDEBUG(D_V4L2, "Read vertical flip %d", *val); - return (err < 0) ? err : 0; + return err; } int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val) @@ -121,19 +123,19 @@ int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val) PDEBUG(D_V4L2, "Set vertical flip to %d", val); /* Set the correct page map */ - err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); + err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); if (err < 0) goto out; - err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); + err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); if (err < 0) goto out; data[0] = (data[0] & 0xfe) | val; - err = mt9m111_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, + err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); out: - return (err < 0) ? err : 0; + return err; } int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -142,12 +144,12 @@ int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) u8 data[2] = {0x00, 0x00}; struct sd *sd = (struct sd *) gspca_dev; - err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, + err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); *val = data[0] & MT9M111_RMB_MIRROR_COLS; PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - return (err < 0) ? err : 0; + return err; } int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val) @@ -159,19 +161,19 @@ int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val) PDEBUG(D_V4L2, "Set horizontal flip to %d", val); /* Set the correct page map */ - err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); + err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); if (err < 0) goto out; - err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); + err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); if (err < 0) goto out; data[0] = (data[0] & 0xfd) | ((val << 1) & 0x02); - err = mt9m111_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, + err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); out: - return (err < 0) ? err : 0; + return err; } int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val) @@ -180,7 +182,7 @@ int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val) u8 data[2] = {0x00, 0x00}; struct sd *sd = (struct sd *) gspca_dev; - err = mt9m111_read_sensor(sd, MT9M111_SC_GLOBAL_GAIN, data, 2); + err = m5602_read_sensor(sd, MT9M111_SC_GLOBAL_GAIN, data, 2); tmp = ((data[1] << 8) | data[0]); *val = ((tmp & (1 << 10)) * 2) | @@ -190,7 +192,7 @@ int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val) PDEBUG(D_V4L2, "Read gain %d", *val); - return (err < 0) ? err : 0; + return err; } int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val) @@ -200,7 +202,7 @@ int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; /* Set the correct page map */ - err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); + err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); if (err < 0) goto out; @@ -225,90 +227,13 @@ int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val) PDEBUG(D_V4L2, "tmp=%d, data[1]=%d, data[0]=%d", tmp, data[1], data[0]); - err = mt9m111_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN, + err = m5602_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN, data, 2); out: - return (err < 0) ? err : 0; -} - -int mt9m111_read_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len) { - int err, i; - - do { - err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); - } while ((*i2c_data & I2C_BUSY) && !err); - if (err < 0) - goto out; - - err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, - sd->sensor->i2c_slave_id); - if (err < 0) - goto out; - - err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); - if (err < 0) - goto out; - - err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x1a); - if (err < 0) - goto out; - - for (i = 0; i < len && !err; i++) { - err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); - - PDEBUG(D_CONF, "Reading sensor register " - "0x%x contains 0x%x ", address, *i2c_data); - } -out: - return (err < 0) ? err : 0; -} - -int mt9m111_write_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len) -{ - int err, i; - u8 *p; - struct usb_device *udev = sd->gspca_dev.dev; - __u8 *buf = sd->gspca_dev.usb_buf; - - /* No sensor with a data width larger - than 16 bits has yet been seen, nor with 0 :p*/ - if (len > 2 || !len) - return -EINVAL; - - memcpy(buf, sensor_urb_skeleton, - sizeof(sensor_urb_skeleton)); - - buf[11] = sd->sensor->i2c_slave_id; - buf[15] = address; - - p = buf + 16; - - /* Copy a four byte write sequence for each byte to be written to */ - for (i = 0; i < len; i++) { - memcpy(p, sensor_urb_skeleton + 16, 4); - p[3] = i2c_data[i]; - p += 4; - PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x", - address, i2c_data[i]); - } - - /* Copy the tailer */ - memcpy(p, sensor_urb_skeleton + 20, 4); - - /* Set the total length */ - p[3] = 0x10 + len; - - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x04, 0x40, 0x19, - 0x0000, buf, - 20 + len * 4, M5602_URB_MSG_TIMEOUT); - - return (err < 0) ? err : 0; + return err; } -void mt9m111_dump_registers(struct sd *sd) +static void mt9m111_dump_registers(struct sd *sd) { u8 address, value[2] = {0x00, 0x00}; @@ -316,27 +241,27 @@ void mt9m111_dump_registers(struct sd *sd) info("Dumping the mt9m111 sensor core registers"); value[1] = MT9M111_SENSOR_CORE; - mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); + m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); for (address = 0; address < 0xff; address++) { - mt9m111_read_sensor(sd, address, value, 2); + m5602_read_sensor(sd, address, value, 2); info("register 0x%x contains 0x%x%x", address, value[0], value[1]); } info("Dumping the mt9m111 color pipeline registers"); value[1] = MT9M111_COLORPIPE; - mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); + m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); for (address = 0; address < 0xff; address++) { - mt9m111_read_sensor(sd, address, value, 2); + m5602_read_sensor(sd, address, value, 2); info("register 0x%x contains 0x%x%x", address, value[0], value[1]); } info("Dumping the mt9m111 camera control registers"); value[1] = MT9M111_CAMERA_CONTROL; - mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); + m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); for (address = 0; address < 0xff; address++) { - mt9m111_read_sensor(sd, address, value, 2); + m5602_read_sensor(sd, address, value, 2); info("register 0x%x contains 0x%x%x", address, value[0], value[1]); } diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.h b/drivers/media/video/gspca/m5602/m5602_mt9m111.h index 315209d5..e795ab7 100644 --- a/drivers/media/video/gspca/m5602/m5602_mt9m111.h +++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.h @@ -87,14 +87,6 @@ int mt9m111_probe(struct sd *sd); int mt9m111_init(struct sd *sd); int mt9m111_power_down(struct sd *sd); -int mt9m111_read_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); - -int mt9m111_write_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); - -void mt9m111_dump_registers(struct sd *sd); - int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val); int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); @@ -106,14 +98,12 @@ static struct m5602_sensor mt9m111 = { .name = "MT9M111", .i2c_slave_id = 0xba, + .i2c_regW = 2, .probe = mt9m111_probe, .init = mt9m111_init, .power_down = mt9m111_power_down, - .read_sensor = mt9m111_read_sensor, - .write_sensor = mt9m111_write_sensor, - .nctrls = 3, .ctrls = { { @@ -1003,7 +993,7 @@ static const unsigned char init_mt9m111[][4] = {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, /* 639*/ {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c index 837c7e47..c908a8d 100644 --- a/drivers/media/video/gspca/m5602/m5602_ov9650.c +++ b/drivers/media/video/gspca/m5602/m5602_ov9650.c @@ -18,77 +18,57 @@ #include "m5602_ov9650.h" -int ov9650_read_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len) -{ - int err, i; - - /* The ov9650 registers have a max depth of one byte */ - if (len > 1 || !len) - return -EINVAL; - - do { - err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); - } while ((*i2c_data & I2C_BUSY) && !err); - - m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, - ov9650.i2c_slave_id); - m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); - m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x10 + len); - m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08); - - for (i = 0; i < len; i++) { - err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); - - PDEBUG(D_CONF, "Reading sensor register " - "0x%x containing 0x%x ", address, *i2c_data); - } - return (err < 0) ? err : 0; -} - -int ov9650_write_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len) -{ - int err, i; - u8 *p; - struct usb_device *udev = sd->gspca_dev.dev; - __u8 *buf = sd->gspca_dev.usb_buf; - - /* The ov9650 only supports one byte writes */ - if (len > 1 || !len) - return -EINVAL; - - memcpy(buf, sensor_urb_skeleton, - sizeof(sensor_urb_skeleton)); - - buf[11] = sd->sensor->i2c_slave_id; - buf[15] = address; - - /* Special case larger sensor writes */ - p = buf + 16; - - /* Copy a four byte write sequence for each byte to be written to */ - for (i = 0; i < len; i++) { - memcpy(p, sensor_urb_skeleton + 16, 4); - p[3] = i2c_data[i]; - p += 4; - PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x", - address, i2c_data[i]); - } - - /* Copy the tailer */ - memcpy(p, sensor_urb_skeleton + 20, 4); - - /* Set the total length */ - p[3] = 0x10 + len; - - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x04, 0x40, 0x19, - 0x0000, buf, - 20 + len * 4, M5602_URB_MSG_TIMEOUT); +/* Vertically and horizontally flips the image if matched, needed for machines + where the sensor is mounted upside down */ +static + const + struct dmi_system_id ov9650_flip_dmi_table[] = { + { + .ident = "ASUS A6VC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6VC") + } + }, + { + .ident = "ASUS A6VM", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") + } + }, + { + .ident = "ASUS A6JC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6JC") + } + }, + { + .ident = "ASUS A6Ja", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6J") + } + }, + { + .ident = "ASUS A6Kt", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt") + } + }, + { + .ident = "Alienware Aurora m9700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700") + } + }, + { } +}; - return (err < 0) ? err : 0; -} +static void ov9650_dump_registers(struct sd *sd); int ov9650_probe(struct sd *sd) { @@ -110,16 +90,16 @@ int ov9650_probe(struct sd *sd) for (i = 0; i < ARRAY_SIZE(preinit_ov9650); i++) { u8 data = preinit_ov9650[i][2]; if (preinit_ov9650[i][0] == SENSOR) - ov9650_write_sensor(sd, + m5602_write_sensor(sd, preinit_ov9650[i][1], &data, 1); else m5602_write_bridge(sd, preinit_ov9650[i][1], data); } - if (ov9650_read_sensor(sd, OV9650_PID, &prod_id, 1)) + if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1)) return -ENODEV; - if (ov9650_read_sensor(sd, OV9650_VER, &ver_id, 1)) + if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1)) return -ENODEV; if ((prod_id == 0x96) && (ver_id == 0x52)) { @@ -148,34 +128,90 @@ int ov9650_init(struct sd *sd) for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) { data = init_ov9650[i][2]; if (init_ov9650[i][0] == SENSOR) - err = ov9650_write_sensor(sd, init_ov9650[i][1], + err = m5602_write_sensor(sd, init_ov9650[i][1], &data, 1); else err = m5602_write_bridge(sd, init_ov9650[i][1], data); } - if (!err && dmi_check_system(ov9650_flip_dmi_table)) { + if (dmi_check_system(ov9650_flip_dmi_table) && !err) { info("vflip quirk active"); data = 0x30; - err = ov9650_write_sensor(sd, OV9650_MVFP, &data, 1); + err = m5602_write_sensor(sd, OV9650_MVFP, &data, 1); } + return err; +} + +int ov9650_start(struct sd *sd) +{ + int i, err = 0; + struct cam *cam = &sd->gspca_dev.cam; - return (err < 0) ? err : 0; + for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) { + u8 data = res_init_ov9650[i][1]; + err = m5602_write_bridge(sd, res_init_ov9650[i][0], data); + } + if (err < 0) + return err; + + switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) + { + case 640: + PDEBUG(D_V4L2, "Configuring camera for VGA mode"); + + for (i = 0; i < ARRAY_SIZE(VGA_ov9650) && !err; i++) { + u8 data = VGA_ov9650[i][2]; + if (VGA_ov9650[i][0] == SENSOR) + err = m5602_write_sensor(sd, + VGA_ov9650[i][1], &data, 1); + else + err = m5602_write_bridge(sd, VGA_ov9650[i][1], data); + } + break; + + case 352: + PDEBUG(D_V4L2, "Configuring camera for CIF mode"); + + for (i = 0; i < ARRAY_SIZE(CIF_ov9650) && !err; i++) { + u8 data = CIF_ov9650[i][2]; + if (CIF_ov9650[i][0] == SENSOR) + err = m5602_write_sensor(sd, + CIF_ov9650[i][1], &data, 1); + else + err = m5602_write_bridge(sd, CIF_ov9650[i][1], data); + } + break; + + case 320: + PDEBUG(D_V4L2, "Configuring camera for QVGA mode"); + + for (i = 0; i < ARRAY_SIZE(QVGA_ov9650) && !err; i++) { + u8 data = QVGA_ov9650[i][2]; + if (QVGA_ov9650[i][0] == SENSOR) + err = m5602_write_sensor(sd, + QVGA_ov9650[i][1], &data, 1); + else + err = m5602_write_bridge(sd, QVGA_ov9650[i][1], data); + } + break; + } + return err; } int ov9650_power_down(struct sd *sd) { - int i; - for (i = 0; i < ARRAY_SIZE(power_down_ov9650); i++) { + int i, err = 0; + for (i = 0; i < ARRAY_SIZE(power_down_ov9650) && !err; i++) { u8 data = power_down_ov9650[i][2]; if (power_down_ov9650[i][0] == SENSOR) - ov9650_write_sensor(sd, + err = m5602_write_sensor(sd, power_down_ov9650[i][1], &data, 1); else - m5602_write_bridge(sd, power_down_ov9650[i][1], data); + err = m5602_write_bridge(sd, power_down_ov9650[i][1], + data); } - return 0; + return err; } int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) @@ -184,24 +220,24 @@ int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; int err; - err = ov9650_read_sensor(sd, OV9650_COM1, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_COM1, &i2c_data, 1); if (err < 0) goto out; *val = i2c_data & 0x03; - err = ov9650_read_sensor(sd, OV9650_AECH, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_AECH, &i2c_data, 1); if (err < 0) goto out; *val |= (i2c_data << 2); - err = ov9650_read_sensor(sd, OV9650_AECHM, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_AECHM, &i2c_data, 1); if (err < 0) goto out; *val |= (i2c_data & 0x3f) << 10; PDEBUG(D_V4L2, "Read exposure %d", *val); out: - return (err < 0) ? err : 0; + return err; } int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val) @@ -215,24 +251,24 @@ int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val) /* The 6 MSBs */ i2c_data = (val >> 10) & 0x3f; - err = ov9650_write_sensor(sd, OV9650_AECHM, + err = m5602_write_sensor(sd, OV9650_AECHM, &i2c_data, 1); if (err < 0) goto out; /* The 8 middle bits */ i2c_data = (val >> 2) & 0xff; - err = ov9650_write_sensor(sd, OV9650_AECH, + err = m5602_write_sensor(sd, OV9650_AECH, &i2c_data, 1); if (err < 0) goto out; /* The 2 LSBs */ i2c_data = val & 0x03; - err = ov9650_write_sensor(sd, OV9650_COM1, &i2c_data, 1); + err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1); out: - return (err < 0) ? err : 0; + return err; } int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val) @@ -241,13 +277,13 @@ int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1); + m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1); *val = (i2c_data & 0x03) << 8; - err = ov9650_read_sensor(sd, OV9650_GAIN, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_GAIN, &i2c_data, 1); *val |= i2c_data; PDEBUG(D_V4L2, "Read gain %d", *val); - return (err < 0) ? err : 0; + return err; } int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val) @@ -259,16 +295,16 @@ int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val) /* The 2 MSB */ /* Read the OV9650_VREF register first to avoid corrupting the VREF high and low bits */ - ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1); + m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1); /* Mask away all uninteresting bits */ i2c_data = ((val & 0x0300) >> 2) | (i2c_data & 0x3F); - err = ov9650_write_sensor(sd, OV9650_VREF, &i2c_data, 1); + err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1); /* The 8 LSBs */ i2c_data = val & 0xff; - err = ov9650_write_sensor(sd, OV9650_GAIN, &i2c_data, 1); - return (err < 0) ? err : 0; + err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1); + return err; } int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) @@ -277,12 +313,12 @@ int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - err = ov9650_read_sensor(sd, OV9650_RED, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_RED, &i2c_data, 1); *val = i2c_data; PDEBUG(D_V4L2, "Read red gain %d", *val); - return (err < 0) ? err : 0; + return err; } int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) @@ -295,9 +331,9 @@ int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) val & 0xff); i2c_data = val & 0xff; - err = ov9650_write_sensor(sd, OV9650_RED, &i2c_data, 1); + err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1); - return (err < 0) ? err : 0; + return err; } int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) @@ -306,12 +342,12 @@ int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - err = ov9650_read_sensor(sd, OV9650_BLUE, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_BLUE, &i2c_data, 1); *val = i2c_data; PDEBUG(D_V4L2, "Read blue gain %d", *val); - return (err < 0) ? err : 0; + return err; } int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) @@ -324,9 +360,9 @@ int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) val & 0xff); i2c_data = val & 0xff; - err = ov9650_write_sensor(sd, OV9650_BLUE, &i2c_data, 1); + err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1); - return (err < 0) ? err : 0; + return err; } int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -335,14 +371,14 @@ int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); if (dmi_check_system(ov9650_flip_dmi_table)) *val = ((i2c_data & OV9650_HFLIP) >> 5) ? 0 : 1; else *val = (i2c_data & OV9650_HFLIP) >> 5; PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - return (err < 0) ? err : 0; + return err; } int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val) @@ -352,20 +388,20 @@ int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; PDEBUG(D_V4L2, "Set horizontal flip to %d", val); - err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); if (err < 0) goto out; if (dmi_check_system(ov9650_flip_dmi_table)) i2c_data = ((i2c_data & 0xdf) | - (((val ? 0 : 1) & 0x01) << 5)); + (((val ? 0 : 1) & 0x01) << 5)); else i2c_data = ((i2c_data & 0xdf) | - ((val & 0x01) << 5)); + ((val & 0x01) << 5)); - err = ov9650_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); + err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); out: - return (err < 0) ? err : 0; + return err; } int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -374,14 +410,14 @@ int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); if (dmi_check_system(ov9650_flip_dmi_table)) *val = ((i2c_data & 0x10) >> 4) ? 0 : 1; else *val = (i2c_data & 0x10) >> 4; PDEBUG(D_V4L2, "Read vertical flip %d", *val); - return (err < 0) ? err : 0; + return err; } int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val) @@ -391,7 +427,7 @@ int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; PDEBUG(D_V4L2, "Set vertical flip to %d", val); - err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); if (err < 0) goto out; @@ -402,9 +438,9 @@ int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val) i2c_data = ((i2c_data & 0xef) | ((val & 0x01) << 4)); - err = ov9650_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); + err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); out: - return (err < 0) ? err : 0; + return err; } int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) @@ -413,16 +449,16 @@ int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - err = ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1); if (err < 0) goto out; *val = (i2c_data & 0x03) << 8; - err = ov9650_read_sensor(sd, OV9650_GAIN, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_GAIN, &i2c_data, 1); *val |= i2c_data; PDEBUG(D_V4L2, "Read gain %d", *val); out: - return (err < 0) ? err : 0; + return err; } int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val) @@ -435,22 +471,22 @@ int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val) /* Read the OV9650_VREF register first to avoid corrupting the VREF high and low bits */ - err = ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1); if (err < 0) goto out; /* Mask away all uninteresting bits */ i2c_data = ((val & 0x0300) >> 2) | (i2c_data & 0x3F); - err = ov9650_write_sensor(sd, OV9650_VREF, &i2c_data, 1); + err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1); if (err < 0) goto out; /* The 8 LSBs */ i2c_data = val & 0xff; - err = ov9650_write_sensor(sd, OV9650_GAIN, &i2c_data, 1); + err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1); out: - return (err < 0) ? err : 0; + return err; } int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val) @@ -459,11 +495,11 @@ int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); *val = (i2c_data & OV9650_AWB_EN) >> 1; PDEBUG(D_V4L2, "Read auto white balance %d", *val); - return (err < 0) ? err : 0; + return err; } int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val) @@ -473,14 +509,14 @@ int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; PDEBUG(D_V4L2, "Set auto white balance to %d", val); - err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); if (err < 0) goto out; i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1)); - err = ov9650_write_sensor(sd, OV9650_COM8, &i2c_data, 1); + err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); out: - return (err < 0) ? err : 0; + return err; } int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val) @@ -489,11 +525,11 @@ int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); *val = (i2c_data & OV9650_AGC_EN) >> 2; PDEBUG(D_V4L2, "Read auto gain control %d", *val); - return (err < 0) ? err : 0; + return err; } int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) @@ -503,23 +539,23 @@ int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; PDEBUG(D_V4L2, "Set auto gain control to %d", val); - err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); if (err < 0) goto out; i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2)); - err = ov9650_write_sensor(sd, OV9650_COM8, &i2c_data, 1); + err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); out: - return (err < 0) ? err : 0; + return err; } -void ov9650_dump_registers(struct sd *sd) +static void ov9650_dump_registers(struct sd *sd) { int address; info("Dumping the ov9650 register state"); for (address = 0; address < 0xa9; address++) { u8 value; - ov9650_read_sensor(sd, address, &value, 1); + m5602_read_sensor(sd, address, &value, 1); info("register 0x%x contains 0x%x", address, value); } @@ -531,9 +567,9 @@ void ov9650_dump_registers(struct sd *sd) u8 old_value, ctrl_value; u8 test_value[2] = {0xff, 0xff}; - ov9650_read_sensor(sd, address, &old_value, 1); - ov9650_write_sensor(sd, address, test_value, 1); - ov9650_read_sensor(sd, address, &ctrl_value, 1); + m5602_read_sensor(sd, address, &old_value, 1); + m5602_write_sensor(sd, address, test_value, 1); + m5602_read_sensor(sd, address, &ctrl_value, 1); if (ctrl_value == test_value[0]) info("register 0x%x is writeable", address); @@ -541,6 +577,6 @@ void ov9650_dump_registers(struct sd *sd) info("register 0x%x is read only", address); /* Restore original value */ - ov9650_write_sensor(sd, address, &old_value, 1); + m5602_write_sensor(sd, address, &old_value, 1); } } diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.h b/drivers/media/video/gspca/m5602/m5602_ov9650.h index 065632f..f4b33b8 100644 --- a/drivers/media/video/gspca/m5602/m5602_ov9650.h +++ b/drivers/media/video/gspca/m5602/m5602_ov9650.h @@ -20,7 +20,6 @@ #define M5602_OV9650_H_ #include <linux/dmi.h> - #include "m5602_sensor.h" /*****************************************************************************/ @@ -36,6 +35,7 @@ #define OV9650_PID 0x0a #define OV9650_VER 0x0b #define OV9650_COM3 0x0c +#define OV9650_COM4 0x0d #define OV9650_COM5 0x0e #define OV9650_COM6 0x0f #define OV9650_AECH 0x10 @@ -94,6 +94,8 @@ #define OV9650_REGISTER_RESET (1 << 7) #define OV9650_VGA_SELECT (1 << 6) +#define OV9650_CIF_SELECT (1 << 5) +#define OV9650_QVGA_SELECT (1 << 4) #define OV9650_RGB_SELECT (1 << 2) #define OV9650_RAW_RGB_SELECT (1 << 0) @@ -108,6 +110,8 @@ #define OV9650_SYSTEM_CLK_SEL (1 << 7) #define OV9650_SLAM_MODE (1 << 4) +#define OV9650_QVGA_VARIOPIXEL (1 << 7) + #define OV9650_VFLIP (1 << 4) #define OV9650_HFLIP (1 << 5) @@ -124,15 +128,9 @@ extern int dump_sensor; int ov9650_probe(struct sd *sd); int ov9650_init(struct sd *sd); +int ov9650_start(struct sd *sd); int ov9650_power_down(struct sd *sd); -int ov9650_read_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); -int ov9650_write_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); - -void ov9650_dump_registers(struct sd *sd); - int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val); int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val); @@ -155,11 +153,11 @@ int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val); static struct m5602_sensor ov9650 = { .name = "OV9650", .i2c_slave_id = 0x60, + .i2c_regW = 1, .probe = ov9650_probe, .init = ov9650_init, + .start = ov9650_start, .power_down = ov9650_power_down, - .read_sensor = ov9650_read_sensor, - .write_sensor = ov9650_write_sensor, .nctrls = 8, .ctrls = { @@ -264,18 +262,38 @@ static struct m5602_sensor ov9650 = { } }, - .nmodes = 1, + .nmodes = 3, .modes = { { - M5602_DEFAULT_FRAME_WIDTH, - M5602_DEFAULT_FRAME_HEIGHT, + 320, + 240, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 320 * 240, + .bytesperline = 320, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, { + 352, + 288, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 352 * 288, + .bytesperline = 352, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, { + 640, + 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, .sizeimage = - M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT, - .bytesperline = M5602_DEFAULT_FRAME_WIDTH, + 640 * 480, + .bytesperline = 640, .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 + .priv = 0 } } }; @@ -324,6 +342,7 @@ static const unsigned char init_ov9650[][3] = {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a}, + /* Reset chip */ {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, /* Enable double clock */ @@ -331,8 +350,6 @@ static const unsigned char init_ov9650[][3] = /* Do something out of spec with the power */ {SENSOR, OV9650_OFON, 0x40}, - /* Set QQVGA */ - {SENSOR, OV9650_COM1, 0x20}, /* Set fast AGC/AEC algorithm with unlimited step size */ {SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC | OV9650_AEC_UNLIM_STEP_SIZE | @@ -343,7 +360,7 @@ static const unsigned char init_ov9650[][3] = {SENSOR, OV9650_ACOM38, 0x81}, /* Turn off color matrix coefficient double option */ {SENSOR, OV9650_COM16, 0x00}, - /* Enable color matrix for RGB/YUV, Delay Y channel, + /* Enable color matrix for RGB/YUV, Delay Y channel, set output Y/UV delay to 1 */ {SENSOR, OV9650_COM13, 0x19}, /* Enable digital BLC, Set output mode to U Y V Y */ @@ -352,7 +369,7 @@ static const unsigned char init_ov9650[][3] = {SENSOR, OV9650_COM24, 0x00}, /* Enable HREF and some out of spec things */ {SENSOR, OV9650_COM12, 0x73}, - /* Set all DBLC offset signs to positive and + /* Set all DBLC offset signs to positive and do some out of spec stuff */ {SENSOR, OV9650_DBLC1, 0xdf}, {SENSOR, OV9650_COM21, 0x06}, @@ -364,7 +381,7 @@ static const unsigned char init_ov9650[][3] = {SENSOR, OV9650_RSVD96, 0x04}, /* Enable full range output */ {SENSOR, OV9650_COM15, 0x0}, - /* Enable HREF at optical black, enable ADBLC bias, + /* Enable HREF at optical black, enable ADBLC bias, enable ADBLC, reset timings at format change */ {SENSOR, OV9650_COM6, 0x4b}, /* Subtract 32 from the B channel bias */ @@ -385,7 +402,7 @@ static const unsigned char init_ov9650[][3] = {SENSOR, OV9650_AEB, 0x5c}, /* Set the high and low limit nibbles to 3 */ {SENSOR, OV9650_VPT, 0xc3}, - /* Set the Automatic Gain Ceiling (AGC) to 128x, + /* Set the Automatic Gain Ceiling (AGC) to 128x, drop VSYNC at frame drop, limit exposure timing, drop frame when the AEC step is larger than the exposure gap */ @@ -394,9 +411,9 @@ static const unsigned char init_ov9650[][3] = and set PWDN to SLVS (slave mode vertical sync) */ {SENSOR, OV9650_COM10, 0x42}, /* Set horizontal column start high to default value */ - {SENSOR, OV9650_HSTART, 0x1a}, + {SENSOR, OV9650_HSTART, 0x1a}, /* 210 */ /* Set horizontal column end */ - {SENSOR, OV9650_HSTOP, 0xbf}, + {SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */ /* Complementing register to the two writes above */ {SENSOR, OV9650_HREF, 0xb2}, /* Set vertical row start high bits */ @@ -405,10 +422,6 @@ static const unsigned char init_ov9650[][3] = {SENSOR, OV9650_VSTOP, 0x7e}, /* Set complementing vertical frame control */ {SENSOR, OV9650_VREF, 0x10}, - /* Set raw RGB output format with VGA resolution */ - {SENSOR, OV9650_COM7, OV9650_VGA_SELECT | - OV9650_RGB_SELECT | - OV9650_RAW_RGB_SELECT}, {SENSOR, OV9650_ADC, 0x04}, {SENSOR, OV9650_HV, 0x40}, /* Enable denoise, and white-pixel erase */ @@ -417,30 +430,15 @@ static const unsigned char init_ov9650[][3] = /* Set the high bits of the exposure value */ {SENSOR, OV9650_AECH, ((EXPOSURE_DEFAULT & 0xff00) >> 8)}, + /* Enable VARIOPIXEL */ + {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL}, + {SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL}, + /* Set the low bits of the exposure value */ {SENSOR, OV9650_COM1, (EXPOSURE_DEFAULT & 0xff)}, {SENSOR, OV9650_GAIN, GAIN_DEFAULT}, {SENSOR, OV9650_BLUE, BLUE_GAIN_DEFAULT}, {SENSOR, OV9650_RED, RED_GAIN_DEFAULT}, - - {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL}, - {SENSOR, OV9650_COM5, OV9650_SYSTEM_CLK_SEL}, - - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, - {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x01}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x09}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x5e}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0xde} }; static const unsigned char power_down_ov9650[][3] = @@ -460,43 +458,76 @@ static const unsigned char power_down_ov9650[][3] = {BRIDGE, M5602_XB_GPIO_EN_L, 0x06}, {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0} + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, }; -/* Vertically and horizontally flips the image if matched, needed for machines - where the sensor is mounted upside down */ -static - const - struct dmi_system_id ov9650_flip_dmi_table[] = { - { - .ident = "ASUS A6VC", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6VC") - } - }, - { - .ident = "ASUS A6VM", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") - } - }, - { - .ident = "ASUS A6JC", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6JC") - } - }, - { - .ident = "ASUS A6Kt", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt") - } - }, - { } +static const unsigned char res_init_ov9650[][2] = +{ + {M5602_XB_LINE_OF_FRAME_H, 0x82}, + {M5602_XB_LINE_OF_FRAME_L, 0x00}, + {M5602_XB_PIX_OF_LINE_H, 0x82}, + {M5602_XB_PIX_OF_LINE_L, 0x00}, + {M5602_XB_SIG_INI, 0x01} +}; + +static const unsigned char VGA_ov9650[][3] = +{ + /* Moves the view window in a vertical orientation */ + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x09}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0}, /* 480 */ + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x62}, /* 98 */ + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, /* 640 + 98 */ + {BRIDGE, M5602_XB_HSYNC_PARA, 0xe2}, + + {SENSOR, OV9650_COM7, OV9650_VGA_SELECT | + OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT}, +}; + +static const unsigned char CIF_ov9650[][3] = +{ + /* Moves the view window in a vertical orientation */ + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x09}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x20}, /* 288 */ + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x62}, /* 98 */ + {BRIDGE, M5602_XB_HSYNC_PARA, 0x01}, /* 352 + 98 */ + {BRIDGE, M5602_XB_HSYNC_PARA, 0xc2}, + + {SENSOR, OV9650_COM7, OV9650_CIF_SELECT | + OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT}, +}; + +static const unsigned char QVGA_ov9650[][3] = +{ + /* Moves the view window in a vertical orientation */ + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x08}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xf0}, /* 240 */ + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x31}, /* 50 */ + {BRIDGE, M5602_XB_HSYNC_PARA, 0x01}, /* 320 + 50 */ + {BRIDGE, M5602_XB_HSYNC_PARA, 0x71}, + + {SENSOR, OV9650_COM7, OV9650_QVGA_SELECT | + OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT}, }; #endif diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/video/gspca/m5602/m5602_po1030.c index d17ac52..2e7fb916 100644 --- a/drivers/media/video/gspca/m5602/m5602_po1030.c +++ b/drivers/media/video/gspca/m5602/m5602_po1030.c @@ -18,6 +18,8 @@ #include "m5602_po1030.h" +static void po1030_dump_registers(struct sd *sd); + int po1030_probe(struct sd *sd) { u8 prod_id = 0, ver_id = 0, i; @@ -38,16 +40,16 @@ int po1030_probe(struct sd *sd) for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) { u8 data = preinit_po1030[i][2]; if (preinit_po1030[i][0] == SENSOR) - po1030_write_sensor(sd, + m5602_write_sensor(sd, preinit_po1030[i][1], &data, 1); else m5602_write_bridge(sd, preinit_po1030[i][1], data); } - if (po1030_read_sensor(sd, 0x3, &prod_id, 1)) + if (m5602_read_sensor(sd, 0x3, &prod_id, 1)) return -ENODEV; - if (po1030_read_sensor(sd, 0x4, &ver_id, 1)) + if (m5602_read_sensor(sd, 0x4, &ver_id, 1)) return -ENODEV; if ((prod_id == 0x02) && (ver_id == 0xef)) { @@ -64,78 +66,12 @@ sensor_found: return 0; } -int po1030_read_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len) -{ - int err, i; - - do { - err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); - } while ((*i2c_data & I2C_BUSY) && !err); - - m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, - sd->sensor->i2c_slave_id); - m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); - m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x10 + len); - m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08); - - for (i = 0; i < len; i++) { - err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); - - PDEBUG(D_CONF, "Reading sensor register " - "0x%x containing 0x%x ", address, *i2c_data); - } - return (err < 0) ? err : 0; -} - -int po1030_write_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len) -{ - int err, i; - u8 *p; - struct usb_device *udev = sd->gspca_dev.dev; - __u8 *buf = sd->gspca_dev.usb_buf; - - /* The po1030 only supports one byte writes */ - if (len > 1 || !len) - return -EINVAL; - - memcpy(buf, sensor_urb_skeleton, sizeof(sensor_urb_skeleton)); - - buf[11] = sd->sensor->i2c_slave_id; - buf[15] = address; - - p = buf + 16; - - /* Copy a four byte write sequence for each byte to be written to */ - for (i = 0; i < len; i++) { - memcpy(p, sensor_urb_skeleton + 16, 4); - p[3] = i2c_data[i]; - p += 4; - PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x", - address, i2c_data[i]); - } - - /* Copy the footer */ - memcpy(p, sensor_urb_skeleton + 20, 4); - - /* Set the total length */ - p[3] = 0x10 + len; - - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x04, 0x40, 0x19, - 0x0000, buf, - 20 + len * 4, M5602_URB_MSG_TIMEOUT); - - return (err < 0) ? err : 0; -} - int po1030_init(struct sd *sd) { int i, err = 0; /* Init the sensor */ - for (i = 0; i < ARRAY_SIZE(init_po1030); i++) { + for (i = 0; i < ARRAY_SIZE(init_po1030) && !err; i++) { u8 data[2] = {0x00, 0x00}; switch (init_po1030[i][0]) { @@ -147,16 +83,10 @@ int po1030_init(struct sd *sd) case SENSOR: data[0] = init_po1030[i][2]; - err = po1030_write_sensor(sd, + err = m5602_write_sensor(sd, init_po1030[i][1], data, 1); break; - case SENSOR_LONG: - data[0] = init_po1030[i][2]; - data[1] = init_po1030[i][3]; - err = po1030_write_sensor(sd, - init_po1030[i][1], data, 2); - break; default: info("Invalid stream command, exiting init"); return -EINVAL; @@ -166,7 +96,7 @@ int po1030_init(struct sd *sd) if (dump_sensor) po1030_dump_registers(sd); - return (err < 0) ? err : 0; + return err; } int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) @@ -175,19 +105,19 @@ int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; int err; - err = po1030_read_sensor(sd, PO1030_REG_INTEGLINES_H, + err = m5602_read_sensor(sd, PO1030_REG_INTEGLINES_H, &i2c_data, 1); if (err < 0) goto out; *val = (i2c_data << 8); - err = po1030_read_sensor(sd, PO1030_REG_INTEGLINES_M, + err = m5602_read_sensor(sd, PO1030_REG_INTEGLINES_M, &i2c_data, 1); *val |= i2c_data; PDEBUG(D_V4L2, "Exposure read as %d", *val); out: - return (err < 0) ? err : 0; + return err; } int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val) @@ -202,7 +132,7 @@ int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val) PDEBUG(D_V4L2, "Set exposure to high byte to 0x%x", i2c_data); - err = po1030_write_sensor(sd, PO1030_REG_INTEGLINES_H, + err = m5602_write_sensor(sd, PO1030_REG_INTEGLINES_H, &i2c_data, 1); if (err < 0) goto out; @@ -210,11 +140,11 @@ int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val) i2c_data = (val & 0xff); PDEBUG(D_V4L2, "Set exposure to low byte to 0x%x", i2c_data); - err = po1030_write_sensor(sd, PO1030_REG_INTEGLINES_M, + err = m5602_write_sensor(sd, PO1030_REG_INTEGLINES_M, &i2c_data, 1); out: - return (err < 0) ? err : 0; + return err; } int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val) @@ -223,12 +153,12 @@ int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; int err; - err = po1030_read_sensor(sd, PO1030_REG_GLOBALGAIN, + err = m5602_read_sensor(sd, PO1030_REG_GLOBALGAIN, &i2c_data, 1); *val = i2c_data; PDEBUG(D_V4L2, "Read global gain %d", *val); - return (err < 0) ? err : 0; + return err; } int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -237,14 +167,14 @@ int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; int err; - err = po1030_read_sensor(sd, PO1030_REG_CONTROL2, + err = m5602_read_sensor(sd, PO1030_REG_CONTROL2, &i2c_data, 1); *val = (i2c_data >> 7) & 0x01 ; PDEBUG(D_V4L2, "Read hflip %d", *val); - return (err < 0) ? err : 0; + return err; } int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val) @@ -254,13 +184,17 @@ int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val) int err; PDEBUG(D_V4L2, "Set hflip %d", val); + err = m5602_read_sensor(sd, PO1030_REG_CONTROL2, &i2c_data, 1); + if (err < 0) + goto out; - i2c_data = (val & 0x01) << 7; + i2c_data = (0x7f & i2c_data) | ((val & 0x01) << 7); - err = po1030_write_sensor(sd, PO1030_REG_CONTROL2, - &i2c_data, 1); + err = m5602_write_sensor(sd, PO1030_REG_CONTROL2, + &i2c_data, 1); - return (err < 0) ? err : 0; +out: + return err; } int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -269,14 +203,14 @@ int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; int err; - err = po1030_read_sensor(sd, PO1030_REG_GLOBALGAIN, + err = m5602_read_sensor(sd, PO1030_REG_GLOBALGAIN, &i2c_data, 1); *val = (i2c_data >> 6) & 0x01; PDEBUG(D_V4L2, "Read vflip %d", *val); - return (err < 0) ? err : 0; + return err; } int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val) @@ -286,13 +220,17 @@ int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val) int err; PDEBUG(D_V4L2, "Set vflip %d", val); + err = m5602_read_sensor(sd, PO1030_REG_CONTROL2, &i2c_data, 1); + if (err < 0) + goto out; - i2c_data = (val & 0x01) << 6; + i2c_data = (i2c_data & 0xbf) | ((val & 0x01) << 6); - err = po1030_write_sensor(sd, PO1030_REG_CONTROL2, - &i2c_data, 1); + err = m5602_write_sensor(sd, PO1030_REG_CONTROL2, + &i2c_data, 1); - return (err < 0) ? err : 0; +out: + return err; } int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val) @@ -303,9 +241,9 @@ int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val) i2c_data = val & 0xff; PDEBUG(D_V4L2, "Set global gain to %d", i2c_data); - err = po1030_write_sensor(sd, PO1030_REG_GLOBALGAIN, + err = m5602_write_sensor(sd, PO1030_REG_GLOBALGAIN, &i2c_data, 1); - return (err < 0) ? err : 0; + return err; } int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) @@ -314,11 +252,11 @@ int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; int err; - err = po1030_read_sensor(sd, PO1030_REG_RED_GAIN, + err = m5602_read_sensor(sd, PO1030_REG_RED_GAIN, &i2c_data, 1); *val = i2c_data; PDEBUG(D_V4L2, "Read red gain %d", *val); - return (err < 0) ? err : 0; + return err; } int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) @@ -329,9 +267,9 @@ int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) i2c_data = val & 0xff; PDEBUG(D_V4L2, "Set red gain to %d", i2c_data); - err = po1030_write_sensor(sd, PO1030_REG_RED_GAIN, + err = m5602_write_sensor(sd, PO1030_REG_RED_GAIN, &i2c_data, 1); - return (err < 0) ? err : 0; + return err; } int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) @@ -340,12 +278,12 @@ int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) u8 i2c_data; int err; - err = po1030_read_sensor(sd, PO1030_REG_BLUE_GAIN, + err = m5602_read_sensor(sd, PO1030_REG_BLUE_GAIN, &i2c_data, 1); *val = i2c_data; PDEBUG(D_V4L2, "Read blue gain %d", *val); - return (err < 0) ? err : 0; + return err; } int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) @@ -355,10 +293,10 @@ int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) int err; i2c_data = val & 0xff; PDEBUG(D_V4L2, "Set blue gain to %d", i2c_data); - err = po1030_write_sensor(sd, PO1030_REG_BLUE_GAIN, + err = m5602_write_sensor(sd, PO1030_REG_BLUE_GAIN, &i2c_data, 1); - return (err < 0) ? err : 0; + return err; } int po1030_power_down(struct sd *sd) @@ -366,14 +304,14 @@ int po1030_power_down(struct sd *sd) return 0; } -void po1030_dump_registers(struct sd *sd) +static void po1030_dump_registers(struct sd *sd) { int address; u8 value = 0; info("Dumping the po1030 sensor core registers"); for (address = 0; address < 0x7f; address++) { - po1030_read_sensor(sd, address, &value, 1); + m5602_read_sensor(sd, address, &value, 1); info("register 0x%x contains 0x%x", address, value); } @@ -385,9 +323,9 @@ void po1030_dump_registers(struct sd *sd) u8 old_value, ctrl_value; u8 test_value[2] = {0xff, 0xff}; - po1030_read_sensor(sd, address, &old_value, 1); - po1030_write_sensor(sd, address, test_value, 1); - po1030_read_sensor(sd, address, &ctrl_value, 1); + m5602_read_sensor(sd, address, &old_value, 1); + m5602_write_sensor(sd, address, test_value, 1); + m5602_read_sensor(sd, address, &ctrl_value, 1); if (ctrl_value == test_value[0]) info("register 0x%x is writeable", address); @@ -395,6 +333,6 @@ void po1030_dump_registers(struct sd *sd) info("register 0x%x is read only", address); /* Restore original value */ - po1030_write_sensor(sd, address, &old_value, 1); + m5602_write_sensor(sd, address, &old_value, 1); } } diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.h b/drivers/media/video/gspca/m5602/m5602_po1030.h index a0b75ff..def39d5 100644 --- a/drivers/media/video/gspca/m5602/m5602_po1030.h +++ b/drivers/media/video/gspca/m5602/m5602_po1030.h @@ -10,7 +10,7 @@ * v4l2 interface modeled after the V4L2 driver * for SN9C10x PC Camera Controllers * - * Register defines taken from Pascal Stangs Proxycon Armlib + * Register defines taken from Pascal Stangs Procyon Armlib * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -128,13 +128,6 @@ int po1030_probe(struct sd *sd); int po1030_init(struct sd *sd); int po1030_power_down(struct sd *sd); -void po1030_dump_registers(struct sd *sd); - -int po1030_read_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); -int po1030_write_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); - int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val); int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val); @@ -152,6 +145,7 @@ static struct m5602_sensor po1030 = { .name = "PO1030", .i2c_slave_id = 0xdc, + .i2c_regW = 1, .probe = po1030_probe, .init = po1030_init, diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c index 14b1eac..e564a61 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c @@ -18,6 +18,40 @@ #include "m5602_s5k4aa.h" +static + const + struct dmi_system_id s5k4aa_vflip_dmi_table[] = { + { + .ident = "Fujitsu-Siemens Amilo Xa 2528", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528") + } + }, { + .ident = "Fujitsu-Siemens Amilo Xi 2550", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550") + } + }, { + .ident = "MSI GX700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "GX700"), + DMI_MATCH(DMI_BIOS_DATE, "07/26/2007") + } + }, { + .ident = "MSI GX700/GX705/EX700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "GX700/GX705/EX700") + } + }, + { } +}; + +static void s5k4aa_dump_registers(struct sd *sd); + int s5k4aa_probe(struct sd *sd) { u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -49,7 +83,7 @@ int s5k4aa_probe(struct sd *sd) case SENSOR: data[0] = preinit_s5k4aa[i][2]; - err = s5k4aa_write_sensor(sd, + err = m5602_write_sensor(sd, preinit_s5k4aa[i][1], data, 1); break; @@ -57,7 +91,7 @@ int s5k4aa_probe(struct sd *sd) case SENSOR_LONG: data[0] = preinit_s5k4aa[i][2]; data[1] = preinit_s5k4aa[i][3]; - err = s5k4aa_write_sensor(sd, + err = m5602_write_sensor(sd, preinit_s5k4aa[i][1], data, 2); break; @@ -68,13 +102,14 @@ int s5k4aa_probe(struct sd *sd) } /* Test some registers, but we don't know their exact meaning yet */ - if (s5k4aa_read_sensor(sd, 0x00, prod_id, sizeof(prod_id))) + if (m5602_read_sensor(sd, 0x00, prod_id, sizeof(prod_id))) return -ENODEV; if (memcmp(prod_id, expected_prod_id, sizeof(prod_id))) return -ENODEV; else info("Detected a s5k4aa sensor"); + sensor_found: sd->gspca_dev.cam.cam_mode = s5k4aa.modes; sd->gspca_dev.cam.nmodes = s5k4aa.nmodes; @@ -84,90 +119,6 @@ sensor_found: return 0; } -int s5k4aa_read_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len) -{ - int err, i; - - do { - err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); - } while ((*i2c_data & I2C_BUSY) && !err); - if (err < 0) - goto out; - - err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, - sd->sensor->i2c_slave_id); - if (err < 0) - goto out; - - err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); - if (err < 0) - goto out; - - err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len); - if (err < 0) - goto out; - - do { - err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); - } while ((*i2c_data & I2C_BUSY) && !err); - if (err < 0) - goto out; - - for (i = 0; (i < len) & !err; i++) { - err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); - - PDEBUG(D_CONF, "Reading sensor register " - "0x%x containing 0x%x ", address, *i2c_data); - } -out: - return (err < 0) ? err : 0; -} - -int s5k4aa_write_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len) -{ - int err, i; - u8 *p; - struct usb_device *udev = sd->gspca_dev.dev; - __u8 *buf = sd->gspca_dev.usb_buf; - - /* No sensor with a data width larger than 16 bits has yet been seen */ - if (len > 2 || !len) - return -EINVAL; - - memcpy(buf, sensor_urb_skeleton, - sizeof(sensor_urb_skeleton)); - - buf[11] = sd->sensor->i2c_slave_id; - buf[15] = address; - - /* Special case larger sensor writes */ - p = buf + 16; - - /* Copy a four byte write sequence for each byte to be written to */ - for (i = 0; i < len; i++) { - memcpy(p, sensor_urb_skeleton + 16, 4); - p[3] = i2c_data[i]; - p += 4; - PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x", - address, i2c_data[i]); - } - - /* Copy the tailer */ - memcpy(p, sensor_urb_skeleton + 20, 4); - - /* Set the total length */ - p[3] = 0x10 + len; - - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x04, 0x40, 0x19, - 0x0000, buf, - 20 + len * 4, M5602_URB_MSG_TIMEOUT); - - return (err < 0) ? err : 0; -} - int s5k4aa_init(struct sd *sd) { int i, err = 0; @@ -184,14 +135,14 @@ int s5k4aa_init(struct sd *sd) case SENSOR: data[0] = init_s5k4aa[i][2]; - err = s5k4aa_write_sensor(sd, + err = m5602_write_sensor(sd, init_s5k4aa[i][1], data, 1); break; case SENSOR_LONG: data[0] = init_s5k4aa[i][2]; data[1] = init_s5k4aa[i][3]; - err = s5k4aa_write_sensor(sd, + err = m5602_write_sensor(sd, init_s5k4aa[i][1], data, 2); break; default: @@ -206,21 +157,21 @@ int s5k4aa_init(struct sd *sd) if (!err && dmi_check_system(s5k4aa_vflip_dmi_table)) { u8 data = 0x02; info("vertical flip quirk active"); - s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); - s5k4aa_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); + m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); data |= S5K4AA_RM_V_FLIP; data &= ~S5K4AA_RM_H_FLIP; - s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); /* Decrement COLSTART to preserve color order (BGGR) */ - s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); data--; - s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); /* Increment ROWSTART to preserve color order (BGGR) */ - s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); data++; - s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); } return (err < 0) ? err : 0; @@ -237,20 +188,20 @@ int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) u8 data = S5K4AA_PAGE_MAP_2; int err; - err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) goto out; - err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1); + err = m5602_read_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1); if (err < 0) goto out; *val = data << 8; - err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1); + err = m5602_read_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1); *val |= data; PDEBUG(D_V4L2, "Read exposure %d", *val); out: - return (err < 0) ? err : 0; + return err; } int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val) @@ -260,17 +211,17 @@ int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val) int err; PDEBUG(D_V4L2, "Set exposure to %d", val); - err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) goto out; data = (val >> 8) & 0xff; - err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1); if (err < 0) goto out; data = val & 0xff; - err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1); out: - return (err < 0) ? err : 0; + return err; } int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -279,16 +230,16 @@ int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) u8 data = S5K4AA_PAGE_MAP_2; int err; - err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) goto out; - err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + err = m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); *val = (data & S5K4AA_RM_V_FLIP) >> 7; PDEBUG(D_V4L2, "Read vertical flip %d", *val); out: - return (err < 0) ? err : 0; + return err; } int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val) @@ -298,35 +249,35 @@ int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val) int err; PDEBUG(D_V4L2, "Set vertical flip to %d", val); - err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) goto out; - err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) goto out; data = ((data & ~S5K4AA_RM_V_FLIP) | ((val & 0x01) << 7)); - err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) goto out; if (val) { - err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); if (err < 0) goto out; data++; - err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); } else { - err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); if (err < 0) goto out; data--; - err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); } out: - return (err < 0) ? err : 0; + return err; } int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -335,15 +286,15 @@ int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) u8 data = S5K4AA_PAGE_MAP_2; int err; - err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) goto out; - err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + err = m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); *val = (data & S5K4AA_RM_H_FLIP) >> 6; PDEBUG(D_V4L2, "Read horizontal flip %d", *val); out: - return (err < 0) ? err : 0; + return err; } int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val) @@ -354,35 +305,35 @@ int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val) PDEBUG(D_V4L2, "Set horizontal flip to %d", val); - err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) goto out; - err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) goto out; data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6)); - err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) goto out; if (val) { - err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); if (err < 0) goto out; data++; - err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); if (err < 0) goto out; } else { - err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); if (err < 0) goto out; data--; - err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); } out: - return (err < 0) ? err : 0; + return err; } int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val) @@ -391,16 +342,16 @@ int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val) u8 data = S5K4AA_PAGE_MAP_2; int err; - err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) goto out; - err = s5k4aa_read_sensor(sd, S5K4AA_GAIN_2, &data, 1); + err = m5602_read_sensor(sd, S5K4AA_GAIN_2, &data, 1); *val = data; PDEBUG(D_V4L2, "Read gain %d", *val); out: - return (err < 0) ? err : 0; + return err; } int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val) @@ -410,28 +361,28 @@ int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val) int err; PDEBUG(D_V4L2, "Set gain to %d", val); - err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) goto out; data = val & 0xff; - err = s5k4aa_write_sensor(sd, S5K4AA_GAIN_2, &data, 1); + err = m5602_write_sensor(sd, S5K4AA_GAIN_2, &data, 1); out: - return (err < 0) ? err : 0; + return err; } -void s5k4aa_dump_registers(struct sd *sd) +static void s5k4aa_dump_registers(struct sd *sd) { int address; u8 page, old_page; - s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); + m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); for (page = 0; page < 16; page++) { - s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); + m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); info("Dumping the s5k4aa register state for page 0x%x", page); for (address = 0; address <= 0xff; address++) { u8 value = 0; - s5k4aa_read_sensor(sd, address, &value, 1); + m5602_read_sensor(sd, address, &value, 1); info("register 0x%x contains 0x%x", address, value); } @@ -439,15 +390,15 @@ void s5k4aa_dump_registers(struct sd *sd) info("s5k4aa register state dump complete"); for (page = 0; page < 16; page++) { - s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); + m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); info("Probing for which registers that are " "read/write for page 0x%x", page); for (address = 0; address <= 0xff; address++) { u8 old_value, ctrl_value, test_value = 0xff; - s5k4aa_read_sensor(sd, address, &old_value, 1); - s5k4aa_write_sensor(sd, address, &test_value, 1); - s5k4aa_read_sensor(sd, address, &ctrl_value, 1); + m5602_read_sensor(sd, address, &old_value, 1); + m5602_write_sensor(sd, address, &test_value, 1); + m5602_read_sensor(sd, address, &ctrl_value, 1); if (ctrl_value == test_value) info("register 0x%x is writeable", address); @@ -455,9 +406,9 @@ void s5k4aa_dump_registers(struct sd *sd) info("register 0x%x is read only", address); /* Restore original value */ - s5k4aa_write_sensor(sd, address, &old_value, 1); + m5602_write_sensor(sd, address, &old_value, 1); } } info("Read/write register probing complete"); - s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); + m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); } diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h b/drivers/media/video/gspca/m5602/m5602_s5k4aa.h index eaef676..1f88b0d 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h +++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.h @@ -41,11 +41,10 @@ #define S5K4AA_WINDOW_HEIGHT_LO 0x09 #define S5K4AA_WINDOW_WIDTH_HI 0x0a #define S5K4AA_WINDOW_WIDTH_LO 0x0b -#define S5K4AA_GLOBAL_GAIN__ 0x0f /* Only a guess ATM !!! */ -#define S5K4AA_H_BLANK_HI__ 0x1d /* Only a guess ATM !!! sync lost - if too low, reduces frame rate - if too high */ -#define S5K4AA_H_BLANK_LO__ 0x1e /* Only a guess ATM !!! */ +#define S5K4AA_GLOBAL_GAIN__ 0x0f +/* sync lost, if too low, reduces frame rate if too high */ +#define S5K4AA_H_BLANK_HI__ 0x1d +#define S5K4AA_H_BLANK_LO__ 0x1e #define S5K4AA_EXPOSURE_HI 0x17 #define S5K4AA_EXPOSURE_LO 0x18 #define S5K4AA_GAIN_1 0x1f /* (digital?) gain : 5 bits */ @@ -68,13 +67,6 @@ int s5k4aa_probe(struct sd *sd); int s5k4aa_init(struct sd *sd); int s5k4aa_power_down(struct sd *sd); -void s5k4aa_dump_registers(struct sd *sd); - -int s5k4aa_read_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); -int s5k4aa_write_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); - int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val); int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); @@ -89,9 +81,8 @@ static struct m5602_sensor s5k4aa = { .probe = s5k4aa_probe, .init = s5k4aa_init, .power_down = s5k4aa_power_down, - .read_sensor = s5k4aa_read_sensor, - .write_sensor = s5k4aa_write_sensor, .i2c_slave_id = 0x5a, + .i2c_regW = 2, .nctrls = 4, .ctrls = { { @@ -338,32 +329,4 @@ static const unsigned char init_s5k4aa[][4] = {SENSOR, S5K4AA_GAIN_2, 0xa0, 0x00} }; -static - const - struct dmi_system_id s5k4aa_vflip_dmi_table[] = { - { - .ident = "Fujitsu-Siemens Amilo Xa 2528", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528") - } - }, - { - .ident = "Fujitsu-Siemens Amilo Xi 2550", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550") - } - }, - { - .ident = "MSI GX700", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), - DMI_MATCH(DMI_PRODUCT_NAME, "GX700"), - DMI_MATCH(DMI_BIOS_DATE, "07/26/2007") - } - }, - { } -}; - #endif diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c index 8988a72..af3f2dc 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c @@ -18,6 +18,8 @@ #include "m5602_s5k83a.h" +static void s5k83a_dump_registers(struct sd *sd); + int s5k83a_probe(struct sd *sd) { u8 prod_id = 0, ver_id = 0; @@ -39,7 +41,7 @@ int s5k83a_probe(struct sd *sd) for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) { u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]}; if (preinit_s5k83a[i][0] == SENSOR) - err = s5k83a_write_sensor(sd, preinit_s5k83a[i][1], + err = m5602_write_sensor(sd, preinit_s5k83a[i][1], data, 2); else err = m5602_write_bridge(sd, preinit_s5k83a[i][1], @@ -49,10 +51,10 @@ int s5k83a_probe(struct sd *sd) /* We don't know what register (if any) that contain the product id * Just pick the first addresses that seem to produce the same results * on multiple machines */ - if (s5k83a_read_sensor(sd, 0x00, &prod_id, 1)) + if (m5602_read_sensor(sd, 0x00, &prod_id, 1)) return -ENODEV; - if (s5k83a_read_sensor(sd, 0x01, &ver_id, 1)) + if (m5602_read_sensor(sd, 0x01, &ver_id, 1)) return -ENODEV; if ((prod_id == 0xff) || (ver_id == 0xff)) @@ -68,91 +70,6 @@ sensor_found: return 0; } -int s5k83a_read_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len) -{ - int err, i; - - do { - err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); - } while ((*i2c_data & I2C_BUSY) && !err); - if (err < 0) - goto out; - - err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, - sd->sensor->i2c_slave_id); - if (err < 0) - goto out; - - err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); - if (err < 0) - goto out; - - err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len); - if (err < 0) - goto out; - - do { - err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); - } while ((*i2c_data & I2C_BUSY) && !err); - - if (err < 0) - goto out; - for (i = 0; i < len && !len; i++) { - err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); - - PDEBUG(D_CONF, "Reading sensor register " - "0x%x containing 0x%x ", address, *i2c_data); - } - -out: - return (err < 0) ? err : 0; -} - -int s5k83a_write_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len) -{ - int err, i; - u8 *p; - struct usb_device *udev = sd->gspca_dev.dev; - __u8 *buf = sd->gspca_dev.usb_buf; - - /* No sensor with a data width larger than 16 bits has yet been seen */ - if (len > 2 || !len) - return -EINVAL; - - memcpy(buf, sensor_urb_skeleton, - sizeof(sensor_urb_skeleton)); - - buf[11] = sd->sensor->i2c_slave_id; - buf[15] = address; - - /* Special case larger sensor writes */ - p = buf + 16; - - /* Copy a four byte write sequence for each byte to be written to */ - for (i = 0; i < len; i++) { - memcpy(p, sensor_urb_skeleton + 16, 4); - p[3] = i2c_data[i]; - p += 4; - PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x", - address, i2c_data[i]); - } - - /* Copy the tailer */ - memcpy(p, sensor_urb_skeleton + 20, 4); - - /* Set the total length */ - p[3] = 0x10 + len; - - err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x04, 0x40, 0x19, - 0x0000, buf, - 20 + len * 4, M5602_URB_MSG_TIMEOUT); - - return (err < 0) ? err : 0; -} - int s5k83a_init(struct sd *sd) { int i, err = 0; @@ -169,14 +86,14 @@ int s5k83a_init(struct sd *sd) case SENSOR: data[0] = init_s5k83a[i][2]; - err = s5k83a_write_sensor(sd, + err = m5602_write_sensor(sd, init_s5k83a[i][1], data, 1); break; case SENSOR_LONG: data[0] = init_s5k83a[i][2]; data[1] = init_s5k83a[i][3]; - err = s5k83a_write_sensor(sd, + err = m5602_write_sensor(sd, init_s5k83a[i][1], data, 2); break; default: @@ -200,14 +117,14 @@ void s5k83a_dump_registers(struct sd *sd) { int address; u8 page, old_page; - s5k83a_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); + m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); for (page = 0; page < 16; page++) { - s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); + m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); info("Dumping the s5k83a register state for page 0x%x", page); for (address = 0; address <= 0xff; address++) { u8 val = 0; - s5k83a_read_sensor(sd, address, &val, 1); + m5602_read_sensor(sd, address, &val, 1); info("register 0x%x contains 0x%x", address, val); } @@ -215,15 +132,15 @@ void s5k83a_dump_registers(struct sd *sd) info("s5k83a register state dump complete"); for (page = 0; page < 16; page++) { - s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); + m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); info("Probing for which registers that are read/write " "for page 0x%x", page); for (address = 0; address <= 0xff; address++) { u8 old_val, ctrl_val, test_val = 0xff; - s5k83a_read_sensor(sd, address, &old_val, 1); - s5k83a_write_sensor(sd, address, &test_val, 1); - s5k83a_read_sensor(sd, address, &ctrl_val, 1); + m5602_read_sensor(sd, address, &old_val, 1); + m5602_write_sensor(sd, address, &test_val, 1); + m5602_read_sensor(sd, address, &ctrl_val, 1); if (ctrl_val == test_val) info("register 0x%x is writeable", address); @@ -231,11 +148,11 @@ void s5k83a_dump_registers(struct sd *sd) info("register 0x%x is read only", address); /* Restore original val */ - s5k83a_write_sensor(sd, address, &old_val, 1); + m5602_write_sensor(sd, address, &old_val, 1); } } info("Read/write register probing complete"); - s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); + m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); } int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) @@ -244,11 +161,15 @@ int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) u8 data[2]; struct sd *sd = (struct sd *) gspca_dev; - err = s5k83a_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2); + err = m5602_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2); + if (err < 0) + goto out; + data[1] = data[1] << 1; *val = data[1]; - return (err < 0) ? err : 0; +out: + return err; } int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val) @@ -259,23 +180,24 @@ int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val) data[0] = 0x00; data[1] = 0x20; - err = s5k83a_write_sensor(sd, 0x14, data, 2); + err = m5602_write_sensor(sd, 0x14, data, 2); if (err < 0) - return err; + goto out; data[0] = 0x01; data[1] = 0x00; - err = s5k83a_write_sensor(sd, 0x0d, data, 2); + err = m5602_write_sensor(sd, 0x0d, data, 2); if (err < 0) - return err; + goto out; /* FIXME: This is not sane, we need to figure out the composition of these registers */ data[0] = val >> 3; /* brightness, high 5 bits */ data[1] = val >> 1; /* brightness, high 7 bits */ - err = s5k83a_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2); + err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2); - return (err < 0) ? err : 0; +out: + return err; } int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val) @@ -284,10 +206,14 @@ int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val) u8 data; struct sd *sd = (struct sd *) gspca_dev; - err = s5k83a_read_sensor(sd, S5K83A_WHITENESS, &data, 1); + err = m5602_read_sensor(sd, S5K83A_WHITENESS, &data, 1); + if (err < 0) + goto out; *val = data; - return (err < 0) ? err : 0; + +out: + return err; } int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val) @@ -297,9 +223,9 @@ int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; data[0] = val; - err = s5k83a_write_sensor(sd, S5K83A_WHITENESS, data, 1); + err = m5602_write_sensor(sd, S5K83A_WHITENESS, data, 1); - return (err < 0) ? err : 0; + return err; } int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val) @@ -308,7 +234,9 @@ int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val) u8 data[2]; struct sd *sd = (struct sd *) gspca_dev; - err = s5k83a_read_sensor(sd, S5K83A_GAIN, data, 2); + err = m5602_read_sensor(sd, S5K83A_GAIN, data, 2); + if (err < 0) + goto out; data[1] = data[1] & 0x3f; if (data[1] > S5K83A_MAXIMUM_GAIN) @@ -316,7 +244,8 @@ int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val) *val = data[1]; - return (err < 0) ? err : 0; +out: + return err; } int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val) @@ -327,9 +256,8 @@ int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val) data[0] = 0; data[1] = val; - err = s5k83a_write_sensor(sd, S5K83A_GAIN, data, 2); - - return (err < 0) ? err : 0; + err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2); + return err; } int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -339,14 +267,15 @@ int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) struct sd *sd = (struct sd *) gspca_dev; data[0] = 0x05; - err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); + err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); if (err < 0) - return err; + goto out; - err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1); + err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); *val = (data[0] | 0x40) ? 1 : 0; - return (err < 0) ? err : 0; +out: + return err; } int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val) @@ -356,25 +285,26 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; data[0] = 0x05; - err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); + err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); if (err < 0) - return err; + goto out; - err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1); + err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); if (err < 0) - return err; + goto out; /* set or zero six bit, seven is hflip */ data[0] = (val) ? (data[0] & 0x80) | 0x40 | S5K83A_FLIP_MASK : (data[0] & 0x80) | S5K83A_FLIP_MASK; - err = s5k83a_write_sensor(sd, S5K83A_FLIP, data, 1); + err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1); if (err < 0) - return err; + goto out; data[0] = (val) ? 0x0b : 0x0a; - err = s5k83a_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1); + err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1); - return (err < 0) ? err : 0; +out: + return err; } int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -384,14 +314,15 @@ int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) struct sd *sd = (struct sd *) gspca_dev; data[0] = 0x05; - err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); + err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); if (err < 0) - return err; + goto out; - err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1); + err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); *val = (data[0] | 0x80) ? 1 : 0; - return (err < 0) ? err : 0; +out: + return err; } int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val) @@ -401,23 +332,23 @@ int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; data[0] = 0x05; - err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); + err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); if (err < 0) - return err; + goto out; - err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1); + err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); if (err < 0) - return err; + goto out; /* set or zero seven bit, six is vflip */ data[0] = (val) ? (data[0] & 0x40) | 0x80 | S5K83A_FLIP_MASK : (data[0] & 0x40) | S5K83A_FLIP_MASK; - err = s5k83a_write_sensor(sd, S5K83A_FLIP, data, 1); + err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1); if (err < 0) - return err; + goto out; data[0] = (val) ? 0x0a : 0x0b; - err = s5k83a_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1); - - return (err < 0) ? err : 0; + err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1); +out: + return err; } diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.h b/drivers/media/video/gspca/m5602/m5602_s5k83a.h index ee3ee9c..05ccb5b 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k83a.h +++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.h @@ -22,15 +22,15 @@ #include "m5602_sensor.h" #define S5K83A_FLIP 0x01 -#define S5K83A_HFLIP_TUNE 0x03 -#define S5K83A_VFLIP_TUNE 0x05 -#define S5K83A_WHITENESS 0x0a +#define S5K83A_HFLIP_TUNE 0x03 +#define S5K83A_VFLIP_TUNE 0x05 +#define S5K83A_WHITENESS 0x0a #define S5K83A_GAIN 0x18 -#define S5K83A_BRIGHTNESS 0x1b -#define S5K83A_PAGE_MAP 0xec +#define S5K83A_BRIGHTNESS 0x1b +#define S5K83A_PAGE_MAP 0xec -#define S5K83A_DEFAULT_BRIGHTNESS 0x71 -#define S5K83A_DEFAULT_WHITENESS 0x7e +#define S5K83A_DEFAULT_BRIGHTNESS 0x71 +#define S5K83A_DEFAULT_WHITENESS 0x7e #define S5K83A_DEFAULT_GAIN 0x00 #define S5K83A_MAXIMUM_GAIN 0x3c #define S5K83A_FLIP_MASK 0x10 @@ -46,13 +46,6 @@ int s5k83a_probe(struct sd *sd); int s5k83a_init(struct sd *sd); int s5k83a_power_down(struct sd *sd); -void s5k83a_dump_registers(struct sd *sd); - -int s5k83a_read_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); -int s5k83a_write_sensor(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); - int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val); int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val); @@ -64,15 +57,13 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val); int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val); - static struct m5602_sensor s5k83a = { .name = "S5K83A", .probe = s5k83a_probe, .init = s5k83a_init, .power_down = s5k83a_power_down, - .read_sensor = s5k83a_read_sensor, - .write_sensor = s5k83a_write_sensor, .i2c_slave_id = 0x5a, + .i2c_regW = 2, .nctrls = 5, .ctrls = { { diff --git a/drivers/media/video/gspca/m5602/m5602_sensor.h b/drivers/media/video/gspca/m5602/m5602_sensor.h index 60c9a48..261623f 100644 --- a/drivers/media/video/gspca/m5602/m5602_sensor.h +++ b/drivers/media/video/gspca/m5602/m5602_sensor.h @@ -49,23 +49,21 @@ struct m5602_sensor { /* What i2c address the sensor is connected to */ u8 i2c_slave_id; + /* Width of each i2c register (in bytes) */ + u8 i2c_regW; + /* Probes if the sensor is connected */ int (*probe)(struct sd *sd); /* Performs a initialization sequence */ int (*init)(struct sd *sd); + /* Executed when the camera starts to send data */ + int (*start)(struct sd *sd); + /* Performs a power down sequence */ int (*power_down)(struct sd *sd); - /* Reads a sensor register */ - int (*read_sensor)(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); - - /* Writes to a sensor register */ - int (*write_sensor)(struct sd *sd, const u8 address, - u8 *i2c_data, const u8 len); - int nctrls; struct ctrl ctrls[M5602_MAX_CTRLS]; diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index 277ca34..3d2090e 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -39,7 +39,7 @@ struct sd { static struct ctrl sd_ctrls[] = { }; -static struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 589, @@ -123,7 +123,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->epaddr = 0x01; cam->cam_mode = vga_mode; - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes = ARRAY_SIZE(vga_mode); sd->qindex = 1; /* set the quantization table */ return 0; } diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index ca67119..ee23295 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -3,7 +3,18 @@ * * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr) * - * (This module is adapted from the ov51x-jpeg package) + * This module is adapted from the ov51x-jpeg package, which itself + * was adapted from the ov511 driver. + * + * Original copyright for the ov511 driver is: + * + * Copyright (c) 1999-2004 Mark W. McClelland + * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach + * + * ov51x-jpeg original copyright is: + * + * Copyright (c) 2004-2007 Romain Beauxis <toots@rastageeks.org> + * Support for OV7670 sensors was contributed by Sam Skipsey <aoanla@yahoo.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 @@ -40,22 +51,18 @@ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ /* Determined by sensor type */ - char sif; - - unsigned char primary_i2c_slave; /* I2C write id of sensor */ + __u8 sif; - unsigned char brightness; - unsigned char contrast; - unsigned char colors; + __u8 brightness; + __u8 contrast; + __u8 colors; __u8 hflip; __u8 vflip; - char compress; /* Should the next frame be compressed? */ - char compress_inited; /* Are compression params uploaded? */ - char stopped; /* Streaming is temporarily paused */ + __u8 stopped; /* Streaming is temporarily paused */ - char frame_rate; /* current Framerate (OV519 only) */ - char clockdiv; /* clockdiv override for OV519 only */ + __u8 frame_rate; /* current Framerate (OV519 only) */ + __u8 clockdiv; /* clockdiv override for OV519 only */ char sensor; /* Type of image sensor chip (SEN_*) */ #define SEN_UNKNOWN 0 @@ -67,7 +74,6 @@ struct sd { #define SEN_OV7670 6 #define SEN_OV76BE 7 #define SEN_OV8610 8 - }; /* V4L2 controls supported by the driver */ @@ -158,7 +164,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, @@ -170,7 +176,7 @@ static struct v4l2_pix_format vga_mode[] = { .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; -static struct v4l2_pix_format sif_mode[] = { +static const struct v4l2_pix_format sif_mode[] = { {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144 * 3 / 8 + 590, @@ -184,15 +190,15 @@ static struct v4l2_pix_format sif_mode[] = { }; /* OV519 Camera interface register numbers */ -#define OV519_CAM_H_SIZE 0x10 -#define OV519_CAM_V_SIZE 0x11 -#define OV519_CAM_X_OFFSETL 0x12 -#define OV519_CAM_X_OFFSETH 0x13 -#define OV519_CAM_Y_OFFSETL 0x14 -#define OV519_CAM_Y_OFFSETH 0x15 -#define OV519_CAM_DIVIDER 0x16 -#define OV519_CAM_DFR 0x20 -#define OV519_CAM_FORMAT 0x25 +#define OV519_R10_H_SIZE 0x10 +#define OV519_R11_V_SIZE 0x11 +#define OV519_R12_X_OFFSETL 0x12 +#define OV519_R13_X_OFFSETH 0x13 +#define OV519_R14_Y_OFFSETL 0x14 +#define OV519_R15_Y_OFFSETH 0x15 +#define OV519_R16_DIVIDER 0x16 +#define OV519_R20_DFR 0x20 +#define OV519_R25_FORMAT 0x25 /* OV519 System Controller register numbers */ #define OV519_SYS_RESET1 0x51 @@ -562,8 +568,8 @@ static const struct ov_i2c_regvals norm_7670[] = { { OV7670_REG_VSTOP, 0x7a }, { OV7670_REG_VREF, 0x0a }, - { OV7670_REG_COM3, 0 }, - { OV7670_REG_COM14, 0 }, + { OV7670_REG_COM3, 0x00 }, + { OV7670_REG_COM14, 0x00 }, /* Mystery scaling numbers */ { 0x70, 0x3a }, { 0x71, 0x35 }, @@ -595,8 +601,8 @@ static const struct ov_i2c_regvals norm_7670[] = { { OV7670_REG_COM8, OV7670_COM8_FASTAEC | OV7670_COM8_AECSTEP | OV7670_COM8_BFILT }, - { OV7670_REG_GAIN, 0 }, - { OV7670_REG_AECH, 0 }, + { OV7670_REG_GAIN, 0x00 }, + { OV7670_REG_AECH, 0x00 }, { OV7670_REG_COM4, 0x40 }, /* magic reserved bit */ { OV7670_REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ { OV7670_REG_BD50MAX, 0x05 }, @@ -634,16 +640,16 @@ static const struct ov_i2c_regvals norm_7670[] = { { OV7670_REG_COM12, 0x78 }, { 0x4d, 0x40 }, { 0x4e, 0x20 }, - { OV7670_REG_GFIX, 0 }, + { OV7670_REG_GFIX, 0x00 }, { 0x6b, 0x4a }, { 0x74, 0x10 }, { 0x8d, 0x4f }, - { 0x8e, 0 }, - { 0x8f, 0 }, - { 0x90, 0 }, - { 0x91, 0 }, - { 0x96, 0 }, - { 0x9a, 0 }, + { 0x8e, 0x00 }, + { 0x8f, 0x00 }, + { 0x90, 0x00 }, + { 0x91, 0x00 }, + { 0x96, 0x00 }, + { 0x9a, 0x00 }, { 0xb0, 0x84 }, { 0xb1, 0x0c }, { 0xb2, 0x0e }, @@ -681,17 +687,17 @@ static const struct ov_i2c_regvals norm_7670[] = { /* Matrix coefficients */ { 0x4f, 0x80 }, { 0x50, 0x80 }, - { 0x51, 0 }, + { 0x51, 0x00 }, { 0x52, 0x22 }, { 0x53, 0x5e }, { 0x54, 0x80 }, { 0x58, 0x9e }, { OV7670_REG_COM16, OV7670_COM16_AWBGAIN }, - { OV7670_REG_EDGE, 0 }, + { OV7670_REG_EDGE, 0x00 }, { 0x75, 0x05 }, { 0x76, 0xe1 }, - { 0x4c, 0 }, + { 0x4c, 0x00 }, { 0x77, 0x01 }, { OV7670_REG_COM13, OV7670_COM13_GAMMA | OV7670_COM13_UVSAT @@ -704,7 +710,7 @@ static const struct ov_i2c_regvals norm_7670[] = { { 0x34, 0x11 }, { OV7670_REG_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO }, { 0xa4, 0x88 }, - { 0x96, 0 }, + { 0x96, 0x00 }, { 0x97, 0x30 }, { 0x98, 0x20 }, { 0x99, 0x30 }, @@ -942,11 +948,11 @@ static int i2c_w(struct sd *sd, /* Initiate 3-byte write cycle */ rc = reg_w(sd, R518_I2C_CTL, 0x01); + if (rc < 0) + return rc; /* wait for write complete */ msleep(4); - if (rc < 0) - return rc; return reg_r8(sd, R518_I2C_CTL); } @@ -1029,7 +1035,7 @@ static inline int ov51x_restart(struct sd *sd) */ static int init_ov_sensor(struct sd *sd) { - int i, success; + int i; /* Reset the sensor */ if (i2c_w(sd, 0x12, 0x80) < 0) @@ -1038,11 +1044,11 @@ static int init_ov_sensor(struct sd *sd) /* Wait for it to initialize */ msleep(150); - for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { + for (i = 0; i < i2c_detect_tries; i++) { if (i2c_r(sd, OV7610_REG_ID_HIGH) == 0x7f && i2c_r(sd, OV7610_REG_ID_LOW) == 0xa2) { - success = 1; - continue; + PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i); + return 0; } /* Reset the sensor */ @@ -1054,10 +1060,7 @@ static int init_ov_sensor(struct sd *sd) if (i2c_r(sd, 0x00) < 0) return -EIO; } - if (!success) - return -EIO; - PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i); - return 0; + return -EIO; } /* Set the read and write slave IDs. The "slave" argument is the write slave, @@ -1073,7 +1076,6 @@ static int ov51x_set_slave_ids(struct sd *sd, rc = reg_w(sd, R51x_I2C_W_SID, slave); if (rc < 0) return rc; - sd->primary_i2c_slave = slave; return reg_w(sd, R51x_I2C_R_SID, slave + 1); } @@ -1285,7 +1287,6 @@ static int ov6xx0_configure(struct sd *sd) /* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */ static void ov51x_led_control(struct sd *sd, int on) { -/* PDEBUG(D_STREAM, "LED (%s)", on ? "on" : "off"); */ reg_w_mask(sd, OV519_GPIO_DATA_OUT0, !on, 1); /* 0 / 1 */ } @@ -1352,7 +1353,7 @@ static int sd_config(struct gspca_dev *gspca_dev, } if (ov8xx0_configure(sd) < 0) { PDEBUG(D_ERR, - "Failed to configure OV8xx0 sensor"); + "Failed to configure OV8xx0 sensor"); goto error; } } @@ -1482,7 +1483,7 @@ static int ov519_mode_init_regs(struct sd *sd) return -EIO; if (sd->sensor == SEN_OV7640) { /* Select 8-bit input mode */ - reg_w_mask(sd, OV519_CAM_DFR, 0x10, 0x10); + reg_w_mask(sd, OV519_R20_DFR, 0x10, 0x10); } } else { if (write_regvals(sd, mode_init_519_ov7670, @@ -1490,14 +1491,14 @@ static int ov519_mode_init_regs(struct sd *sd) return -EIO; } - reg_w(sd, OV519_CAM_H_SIZE, sd->gspca_dev.width >> 4); - reg_w(sd, OV519_CAM_V_SIZE, sd->gspca_dev.height >> 3); - reg_w(sd, OV519_CAM_X_OFFSETL, 0x00); - reg_w(sd, OV519_CAM_X_OFFSETH, 0x00); - reg_w(sd, OV519_CAM_Y_OFFSETL, 0x00); - reg_w(sd, OV519_CAM_Y_OFFSETH, 0x00); - reg_w(sd, OV519_CAM_DIVIDER, 0x00); - reg_w(sd, OV519_CAM_FORMAT, 0x03); /* YUV422 */ + reg_w(sd, OV519_R10_H_SIZE, sd->gspca_dev.width >> 4); + reg_w(sd, OV519_R11_V_SIZE, sd->gspca_dev.height >> 3); + reg_w(sd, OV519_R12_X_OFFSETL, 0x00); + reg_w(sd, OV519_R13_X_OFFSETH, 0x00); + reg_w(sd, OV519_R14_Y_OFFSETL, 0x00); + reg_w(sd, OV519_R15_Y_OFFSETH, 0x00); + reg_w(sd, OV519_R16_DIVIDER, 0x00); + reg_w(sd, OV519_R25_FORMAT, 0x03); /* YUV422 */ reg_w(sd, 0x26, 0x00); /* Undocumented */ /******** Set the framerate ********/ @@ -1509,8 +1510,8 @@ static int ov519_mode_init_regs(struct sd *sd) switch (sd->sensor) { case SEN_OV7640: switch (sd->frame_rate) { -/*fixme: default was 30 fps */ - case 30: + default: +/* case 30: */ reg_w(sd, 0xa4, 0x0c); reg_w(sd, 0x23, 0xff); break; @@ -1522,8 +1523,7 @@ static int ov519_mode_init_regs(struct sd *sd) reg_w(sd, 0xa4, 0x0c); reg_w(sd, 0x23, 0x1b); break; - default: -/* case 15: */ + case 15: reg_w(sd, 0xa4, 0x04); reg_w(sd, 0x23, 0xff); sd->clockdiv = 1; @@ -1576,7 +1576,6 @@ static int ov519_mode_init_regs(struct sd *sd) } break; } - return 0; } @@ -1667,7 +1666,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd) * the gain or the contrast. The "reserved" bits seem * to have some effect in this case. */ i2c_w(sd, 0x2d, 0x85); - } else if (sd->clockdiv >= 0) { + } else { i2c_w(sd, 0x11, sd->clockdiv); } @@ -1869,7 +1868,6 @@ static int sd_start(struct gspca_dev *gspca_dev) ret = ov51x_restart(sd); if (ret < 0) goto out; - PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt); ov51x_led_control(sd, 1); return 0; out: @@ -1879,8 +1877,10 @@ out: static void sd_stopN(struct gspca_dev *gspca_dev) { - ov51x_stop((struct sd *) gspca_dev); - ov51x_led_control((struct sd *) gspca_dev, 0); + struct sd *sd = (struct sd *) gspca_dev; + + ov51x_stop(sd); + ov51x_led_control(sd, 0); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, @@ -1935,9 +1935,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) int val; val = sd->brightness; - PDEBUG(D_CONF, "brightness:%d", val); -/* if (gspca_dev->streaming) - * ov51x_stop(sd); */ switch (sd->sensor) { case SEN_OV8610: case SEN_OV7610: @@ -1959,8 +1956,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) i2c_w(sd, OV7670_REG_BRIGHT, ov7670_abs_to_sm(val)); break; } -/* if (gspca_dev->streaming) - * ov51x_restart(sd); */ } static void setcontrast(struct gspca_dev *gspca_dev) @@ -1969,9 +1964,6 @@ static void setcontrast(struct gspca_dev *gspca_dev) int val; val = sd->contrast; - PDEBUG(D_CONF, "contrast:%d", val); -/* if (gspca_dev->streaming) - ov51x_stop(sd); */ switch (sd->sensor) { case SEN_OV7610: case SEN_OV6620: @@ -2007,8 +1999,6 @@ static void setcontrast(struct gspca_dev *gspca_dev) i2c_w(sd, OV7670_REG_CONTRAS, val >> 1); break; } -/* if (gspca_dev->streaming) - ov51x_restart(sd); */ } static void setcolors(struct gspca_dev *gspca_dev) @@ -2017,9 +2007,6 @@ static void setcolors(struct gspca_dev *gspca_dev) int val; val = sd->colors; - PDEBUG(D_CONF, "saturation:%d", val); -/* if (gspca_dev->streaming) - ov51x_stop(sd); */ switch (sd->sensor) { case SEN_OV8610: case SEN_OV7610: @@ -2044,8 +2031,6 @@ static void setcolors(struct gspca_dev *gspca_dev) /* set REG_COM13 values for UV sat auto mode */ break; } -/* if (gspca_dev->streaming) - ov51x_restart(sd); */ } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) @@ -2053,7 +2038,8 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->brightness = val; - setbrightness(gspca_dev); + if (gspca_dev->streaming) + setbrightness(gspca_dev); return 0; } @@ -2070,7 +2056,8 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->contrast = val; - setcontrast(gspca_dev); + if (gspca_dev->streaming) + setcontrast(gspca_dev); return 0; } @@ -2087,7 +2074,8 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->colors = val; - setcolors(gspca_dev); + if (gspca_dev->streaming) + setcolors(gspca_dev); return 0; } @@ -2104,7 +2092,8 @@ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->hflip = val; - sethvflip(sd); + if (gspca_dev->streaming) + sethvflip(sd); return 0; } @@ -2121,7 +2110,8 @@ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->vflip = val; - sethvflip(sd); + if (gspca_dev->streaming) + sethvflip(sd); return 0; } @@ -2162,7 +2152,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x05a9, 0x8519)}, {} }; -#undef DVNAME + MODULE_DEVICE_TABLE(usb, device_table); /* -- device connect -- */ diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c new file mode 100644 index 0000000..3bf15e4 --- /dev/null +++ b/drivers/media/video/gspca/ov534.c @@ -0,0 +1,601 @@ +/* + * ov534/ov772x gspca driver + * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it> + * Copyright (C) 2008 Jim Paris <jim@jtan.com> + * + * Based on a prototype written by Mark Ferrell <majortrips@gmail.com> + * USB protocol reverse engineered by Jim Paris <jim@jtan.com> + * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "ov534" + +#include "gspca.h" + +#define OV534_REG_ADDRESS 0xf1 /* ? */ +#define OV534_REG_SUBADDR 0xf2 +#define OV534_REG_WRITE 0xf3 +#define OV534_REG_READ 0xf4 +#define OV534_REG_OPERATION 0xf5 +#define OV534_REG_STATUS 0xf6 + +#define OV534_OP_WRITE_3 0x37 +#define OV534_OP_WRITE_2 0x33 +#define OV534_OP_READ_2 0xf9 + +#define CTRL_TIMEOUT 500 + +MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); +MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + __u32 last_fid; + __u32 last_pts; + int frame_rate; +}; + +/* V4L2 controls supported by the driver */ +static struct ctrl sd_ctrls[] = { +}; + +static const struct v4l2_pix_format vga_mode[] = { + {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 640 * 2, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) +{ + struct usb_device *udev = gspca_dev->dev; + int ret; + + PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val); + gspca_dev->usb_buf[0] = val; + ret = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + 0x1, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); + if (ret < 0) + PDEBUG(D_ERR, "write failed"); +} + +static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg) +{ + struct usb_device *udev = gspca_dev->dev; + int ret; + + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + 0x1, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); + PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, gspca_dev->usb_buf[0]); + if (ret < 0) + PDEBUG(D_ERR, "read failed"); + return gspca_dev->usb_buf[0]; +} + +/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7. + * (direction and output)? */ +static void ov534_set_led(struct gspca_dev *gspca_dev, int status) +{ + u8 data; + + PDEBUG(D_CONF, "led status: %d", status); + + data = ov534_reg_read(gspca_dev, 0x21); + data |= 0x80; + ov534_reg_write(gspca_dev, 0x21, data); + + data = ov534_reg_read(gspca_dev, 0x23); + if (status) + data |= 0x80; + else + data &= ~(0x80); + + ov534_reg_write(gspca_dev, 0x23, data); +} + +static int sccb_check_status(struct gspca_dev *gspca_dev) +{ + u8 data; + int i; + + for (i = 0; i < 5; i++) { + data = ov534_reg_read(gspca_dev, OV534_REG_STATUS); + + switch (data) { + case 0x00: + return 1; + case 0x04: + return 0; + case 0x03: + break; + default: + PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5", + data, i + 1); + } + } + return 0; +} + +static void sccb_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) +{ + PDEBUG(D_USBO, "reg: 0x%04x, val: 0x%02x", reg, val); + ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); + ov534_reg_write(gspca_dev, OV534_REG_WRITE, val); + ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); + + if (!sccb_check_status(gspca_dev)) + PDEBUG(D_ERR, "sccb_reg_write failed"); +} + +#ifdef GSPCA_DEBUG +static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg) +{ + ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); + ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2); + if (!sccb_check_status(gspca_dev)) + PDEBUG(D_ERR, "sccb_reg_read failed 1"); + + ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2); + if (!sccb_check_status(gspca_dev)) + PDEBUG(D_ERR, "sccb_reg_read failed 2"); + + return ov534_reg_read(gspca_dev, OV534_REG_READ); +} +#endif + +static const __u8 ov534_reg_initdata[][2] = { + { 0xe7, 0x3a }, + + { OV534_REG_ADDRESS, 0x42 }, /* select OV772x sensor */ + + { 0xc2, 0x0c }, + { 0x88, 0xf8 }, + { 0xc3, 0x69 }, + { 0x89, 0xff }, + { 0x76, 0x03 }, + { 0x92, 0x01 }, + { 0x93, 0x18 }, + { 0x94, 0x10 }, + { 0x95, 0x10 }, + { 0xe2, 0x00 }, + { 0xe7, 0x3e }, + + { 0x96, 0x00 }, + + { 0x97, 0x20 }, + { 0x97, 0x20 }, + { 0x97, 0x20 }, + { 0x97, 0x0a }, + { 0x97, 0x3f }, + { 0x97, 0x4a }, + { 0x97, 0x20 }, + { 0x97, 0x15 }, + { 0x97, 0x0b }, + + { 0x8e, 0x40 }, + { 0x1f, 0x81 }, + { 0x34, 0x05 }, + { 0xe3, 0x04 }, + { 0x88, 0x00 }, + { 0x89, 0x00 }, + { 0x76, 0x00 }, + { 0xe7, 0x2e }, + { 0x31, 0xf9 }, + { 0x25, 0x42 }, + { 0x21, 0xf0 }, + + { 0x1c, 0x00 }, + { 0x1d, 0x40 }, + { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */ + { 0x1d, 0x00 }, /* payload size */ + { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */ + { 0x1d, 0x58 }, /* frame size */ + { 0x1d, 0x00 }, /* frame size */ + + { 0x1c, 0x0a }, + { 0x1d, 0x08 }, /* turn on UVC header */ + { 0x1d, 0x0e }, /* .. */ + + { 0x8d, 0x1c }, + { 0x8e, 0x80 }, + { 0xe5, 0x04 }, + + { 0xc0, 0x50 }, + { 0xc1, 0x3c }, + { 0xc2, 0x0c }, +}; + +static const __u8 ov772x_reg_initdata[][2] = { + { 0x12, 0x80 }, + { 0x11, 0x01 }, + + { 0x3d, 0x03 }, + { 0x17, 0x26 }, + { 0x18, 0xa0 }, + { 0x19, 0x07 }, + { 0x1a, 0xf0 }, + { 0x32, 0x00 }, + { 0x29, 0xa0 }, + { 0x2c, 0xf0 }, + { 0x65, 0x20 }, + { 0x11, 0x01 }, + { 0x42, 0x7f }, + { 0x63, 0xe0 }, + { 0x64, 0xff }, + { 0x66, 0x00 }, + { 0x13, 0xf0 }, + { 0x0d, 0x41 }, + { 0x0f, 0xc5 }, + { 0x14, 0x11 }, + + { 0x22, 0x7f }, + { 0x23, 0x03 }, + { 0x24, 0x40 }, + { 0x25, 0x30 }, + { 0x26, 0xa1 }, + { 0x2a, 0x00 }, + { 0x2b, 0x00 }, + { 0x6b, 0xaa }, + { 0x13, 0xff }, + + { 0x90, 0x05 }, + { 0x91, 0x01 }, + { 0x92, 0x03 }, + { 0x93, 0x00 }, + { 0x94, 0x60 }, + { 0x95, 0x3c }, + { 0x96, 0x24 }, + { 0x97, 0x1e }, + { 0x98, 0x62 }, + { 0x99, 0x80 }, + { 0x9a, 0x1e }, + { 0x9b, 0x08 }, + { 0x9c, 0x20 }, + { 0x9e, 0x81 }, + + { 0xa6, 0x04 }, + { 0x7e, 0x0c }, + { 0x7f, 0x16 }, + { 0x80, 0x2a }, + { 0x81, 0x4e }, + { 0x82, 0x61 }, + { 0x83, 0x6f }, + { 0x84, 0x7b }, + { 0x85, 0x86 }, + { 0x86, 0x8e }, + { 0x87, 0x97 }, + { 0x88, 0xa4 }, + { 0x89, 0xaf }, + { 0x8a, 0xc5 }, + { 0x8b, 0xd7 }, + { 0x8c, 0xe8 }, + { 0x8d, 0x20 }, + + { 0x0c, 0x90 }, + + { 0x2b, 0x00 }, + { 0x22, 0x7f }, + { 0x23, 0x03 }, + { 0x11, 0x01 }, + { 0x0c, 0xd0 }, + { 0x64, 0xff }, + { 0x0d, 0x41 }, + + { 0x14, 0x41 }, + { 0x0e, 0xcd }, + { 0xac, 0xbf }, + { 0x8e, 0x00 }, + { 0x0c, 0xd0 } +}; + +/* set framerate */ +static void ov534_set_frame_rate(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int fr = sd->frame_rate; + + switch (fr) { + case 50: + sccb_reg_write(gspca_dev, 0x11, 0x01); + sccb_reg_write(gspca_dev, 0x0d, 0x41); + ov534_reg_write(gspca_dev, 0xe5, 0x02); + break; + case 40: + sccb_reg_write(gspca_dev, 0x11, 0x02); + sccb_reg_write(gspca_dev, 0x0d, 0xc1); + ov534_reg_write(gspca_dev, 0xe5, 0x04); + break; +/* case 30: */ + default: + fr = 30; + sccb_reg_write(gspca_dev, 0x11, 0x04); + sccb_reg_write(gspca_dev, 0x0d, 0x81); + ov534_reg_write(gspca_dev, 0xe5, 0x02); + break; + case 15: + sccb_reg_write(gspca_dev, 0x11, 0x03); + sccb_reg_write(gspca_dev, 0x0d, 0x41); + ov534_reg_write(gspca_dev, 0xe5, 0x04); + break; + } + + sd->frame_rate = fr; + PDEBUG(D_PROBE, "frame_rate: %d", fr); +} + +/* setup method */ +static void ov534_setup(struct gspca_dev *gspca_dev) +{ + int i; + + /* Initialize bridge chip */ + for (i = 0; i < ARRAY_SIZE(ov534_reg_initdata); i++) + ov534_reg_write(gspca_dev, ov534_reg_initdata[i][0], + ov534_reg_initdata[i][1]); + + PDEBUG(D_PROBE, "sensor is ov%02x%02x", + sccb_reg_read(gspca_dev, 0x0a), + sccb_reg_read(gspca_dev, 0x0b)); + + ov534_set_led(gspca_dev, 1); + + /* Initialize sensor */ + for (i = 0; i < ARRAY_SIZE(ov772x_reg_initdata); i++) + sccb_reg_write(gspca_dev, ov772x_reg_initdata[i][0], + ov772x_reg_initdata[i][1]); + + ov534_reg_write(gspca_dev, 0xe0, 0x09); + ov534_set_led(gspca_dev, 0); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam; + + cam = &gspca_dev->cam; + + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + + cam->bulk_size = 16384; + cam->bulk_nurbs = 2; + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + ov534_setup(gspca_dev); + ov534_set_frame_rate(gspca_dev); + + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + /* start streaming data */ + ov534_set_led(gspca_dev, 1); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* stop streaming data */ + ov534_reg_write(gspca_dev, 0xe0, 0x09); + ov534_set_led(gspca_dev, 0); +} + +/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ +#define UVC_STREAM_EOH (1 << 7) +#define UVC_STREAM_ERR (1 << 6) +#define UVC_STREAM_STI (1 << 5) +#define UVC_STREAM_RES (1 << 4) +#define UVC_STREAM_SCR (1 << 3) +#define UVC_STREAM_PTS (1 << 2) +#define UVC_STREAM_EOF (1 << 1) +#define UVC_STREAM_FID (1 << 0) + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, + __u8 *data, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u32 this_pts; + int this_fid; + int remaining_len = len; + __u8 *next_data = data; + +scan_next: + if (remaining_len <= 0) + return; + + data = next_data; + len = min(remaining_len, 2048); + remaining_len -= len; + next_data += len; + + /* Payloads are prefixed with a UVC-style header. We + consider a frame to start when the FID toggles, or the PTS + changes. A frame ends when EOF is set, and we've received + the correct number of bytes. */ + + /* Verify UVC header. Header length is always 12 */ + if (data[0] != 12 || len < 12) { + PDEBUG(D_PACK, "bad header"); + goto discard; + } + + /* Check errors */ + if (data[1] & UVC_STREAM_ERR) { + PDEBUG(D_PACK, "payload error"); + goto discard; + } + + /* Extract PTS and FID */ + if (!(data[1] & UVC_STREAM_PTS)) { + PDEBUG(D_PACK, "PTS not present"); + goto discard; + } + this_pts = (data[5] << 24) | (data[4] << 16) | (data[3] << 8) | data[2]; + this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0; + + /* If PTS or FID has changed, start a new frame. */ + if (this_pts != sd->last_pts || this_fid != sd->last_fid) { + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); + sd->last_pts = this_pts; + sd->last_fid = this_fid; + } + + /* Add the data from this payload */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data + 12, len - 12); + + /* If this packet is marked as EOF, end the frame */ + if (data[1] & UVC_STREAM_EOF) { + sd->last_pts = 0; + + if ((frame->data_end - frame->data) != + (gspca_dev->width * gspca_dev->height * 2)) { + PDEBUG(D_PACK, "short frame"); + goto discard; + } + + gspca_frame_add(gspca_dev, LAST_PACKET, frame, NULL, 0); + } + + /* Done this payload */ + goto scan_next; + +discard: + /* Discard data until a new frame starts. */ + gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0); + goto scan_next; +} + +/* get stream parameters (framerate) */ +static int sd_get_streamparm(struct gspca_dev *gspca_dev, + struct v4l2_streamparm *parm) +{ + struct v4l2_captureparm *cp = &parm->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + struct sd *sd = (struct sd *) gspca_dev; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + cp->capability |= V4L2_CAP_TIMEPERFRAME; + tpf->numerator = 1; + tpf->denominator = sd->frame_rate; + + return 0; +} + +/* set stream parameters (framerate) */ +static int sd_set_streamparm(struct gspca_dev *gspca_dev, + struct v4l2_streamparm *parm) +{ + struct v4l2_captureparm *cp = &parm->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + struct sd *sd = (struct sd *) gspca_dev; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* Set requested framerate */ + sd->frame_rate = tpf->denominator / tpf->numerator; + ov534_set_frame_rate(gspca_dev); + + /* Return the actual framerate */ + tpf->numerator = 1; + tpf->denominator = sd->frame_rate; + + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .get_streamparm = sd_get_streamparm, + .set_streamparm = sd_set_streamparm, +}; + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x06f8, 0x3002)}, /* Hercules Blog Webcam */ + {USB_DEVICE(0x06f8, 0x3003)}, /* Hercules Dualpix HD Weblog */ + {USB_DEVICE(0x1415, 0x2000)}, /* Sony HD Eye for PS3 (SLEH 00201) */ + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "registered"); + return 0; +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 0b0c573..c90ac85 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -1,7 +1,7 @@ /* * Pixart PAC207BCA library * - * Copyright (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> + * Copyright (C) 2008 Hans de Goede <hdgoede@redhat.com> * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr * @@ -27,7 +27,7 @@ #include "gspca.h" -MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>"); +MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>"); MODULE_DESCRIPTION("Pixart PAC207"); MODULE_LICENSE("GPL"); @@ -149,7 +149,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static struct v4l2_pix_format sif_mode[] = { +static const struct v4l2_pix_format sif_mode[] = { {176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = (176 + 2) * 144, @@ -529,6 +529,7 @@ static const struct sd_desc sd_desc = { static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x4028)}, {USB_DEVICE(0x093a, 0x2460)}, + {USB_DEVICE(0x093a, 0x2461)}, {USB_DEVICE(0x093a, 0x2463)}, {USB_DEVICE(0x093a, 0x2464)}, {USB_DEVICE(0x093a, 0x2468)}, @@ -536,6 +537,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x2471)}, {USB_DEVICE(0x093a, 0x2472)}, {USB_DEVICE(0x093a, 0x2476)}, + {USB_DEVICE(0x145f, 0x013a)}, {USB_DEVICE(0x2001, 0xf115)}, {} }; diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index fbd45e2..a9c95cb 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -226,7 +226,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120 * 3 / 8 + 590, @@ -1064,10 +1064,13 @@ static __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x2608), .driver_info = SENSOR_PAC7311}, {USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311}, {USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311}, + {USB_DEVICE(0x093a, 0x2620), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x262c), .driver_info = SENSOR_PAC7302}, {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 6c69bc7..b3e4e06 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -132,8 +132,6 @@ struct sensor_data { ignore atleast the 2 next frames for the new settings to come into effect before doing any other adjustments */ #define AUTOGAIN_IGNORE_FRAMES 3 -#define AUTOGAIN_DEADZONE 1000 -#define DESIRED_AVG_LUM 7000 /* V4L2 controls supported by the driver */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); @@ -229,7 +227,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120, @@ -251,7 +249,7 @@ static struct v4l2_pix_format vga_mode[] = { .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; -static struct v4l2_pix_format sif_mode[] = { +static const struct v4l2_pix_format sif_mode[] = { {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120, @@ -827,18 +825,29 @@ static void setfreq(struct gspca_dev *gspca_dev) static void do_autogain(struct gspca_dev *gspca_dev) { + int deadzone, desired_avg_lum; struct sd *sd = (struct sd *) gspca_dev; int avg_lum = atomic_read(&sd->avg_lum); if (avg_lum == -1) return; + /* SIF / VGA sensors have a different autoexposure area and thus + different avg_lum values for the same picture brightness */ + if (sensor_data[sd->sensor].flags & F_SIF) { + deadzone = 1000; + desired_avg_lum = 7000; + } else { + deadzone = 3000; + desired_avg_lum = 23000; + } + if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, - sd->brightness * DESIRED_AVG_LUM / 127, - AUTOGAIN_DEADZONE, GAIN_KNEE, EXPOSURE_KNEE)) { - PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d\n", + sd->brightness * desired_avg_lum / 127, + deadzone, GAIN_KNEE, EXPOSURE_KNEE)) { + PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d", (int)sd->gain, (int)sd->exposure); sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; } @@ -1226,8 +1235,8 @@ static __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)}, {USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)}, {USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)}, - {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)}, #endif + {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)}, {USB_DEVICE(0x0c45, 0x602d), SB(HV7131R, 102)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x602e), SB(OV7630, 102)}, diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 53cb82d..3373b8d 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -24,6 +24,8 @@ #include "gspca.h" #include "jpeg.h" +#define V4L2_CID_INFRARED (V4L2_CID_PRIVATE_BASE + 0) + MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -35,23 +37,26 @@ struct sd { atomic_t avg_lum; unsigned int exposure; - unsigned short brightness; - unsigned char contrast; - unsigned char colors; - unsigned char autogain; + __u16 brightness; + __u8 contrast; + __u8 colors; + __u8 autogain; + __u8 blue; + __u8 red; __u8 vflip; /* ov7630 only */ + __u8 infrared; /* mi0360 only */ - signed char ag_cnt; + __s8 ag_cnt; #define AG_CNT_START 13 - char qindex; - unsigned char bridge; + __u8 qindex; + __u8 bridge; #define BRIDGE_SN9C102P 0 #define BRIDGE_SN9C105 1 #define BRIDGE_SN9C110 2 #define BRIDGE_SN9C120 3 #define BRIDGE_SN9C325 4 - char sensor; /* Type of image sensor chip */ + __u8 sensor; /* Type of image sensor chip */ #define SENSOR_HV7131R 0 #define SENSOR_MI0360 1 #define SENSOR_MO4000 2 @@ -59,7 +64,7 @@ struct sd { #define SENSOR_OV7630 4 #define SENSOR_OV7648 5 #define SENSOR_OV7660 6 - unsigned char i2c_base; + __u8 i2c_base; }; /* V4L2 controls supported by the driver */ @@ -69,10 +74,16 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setinfrared(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { { @@ -84,7 +95,7 @@ static struct ctrl sd_ctrls[] = { #define BRIGHTNESS_MAX 0xffff .maximum = BRIGHTNESS_MAX, .step = 1, -#define BRIGHTNESS_DEF 0x7fff +#define BRIGHTNESS_DEF 0x8000 .default_value = BRIGHTNESS_DEF, }, .set = sd_setbrightness, @@ -111,7 +122,7 @@ static struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Color", .minimum = 0, - .maximum = 64, + .maximum = 40, .step = 1, #define COLOR_DEF 32 .default_value = COLOR_DEF, @@ -119,7 +130,35 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcolors, .get = sd_getcolors, }, -#define AUTOGAIN_IDX 3 + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 24, + .maximum = 40, + .step = 1, +#define BLUE_BALANCE_DEF 32 + .default_value = BLUE_BALANCE_DEF, + }, + .set = sd_setblue_balance, + .get = sd_getblue_balance, + }, + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 24, + .maximum = 40, + .step = 1, +#define RED_BALANCE_DEF 32 + .default_value = RED_BALANCE_DEF, + }, + .set = sd_setred_balance, + .get = sd_getred_balance, + }, +#define AUTOGAIN_IDX 5 { { .id = V4L2_CID_AUTOGAIN, @@ -135,7 +174,7 @@ static struct ctrl sd_ctrls[] = { .get = sd_getautogain, }, /* ov7630 only */ -#define VFLIP_IDX 4 +#define VFLIP_IDX 6 { { .id = V4L2_CID_VFLIP, @@ -150,9 +189,43 @@ static struct ctrl sd_ctrls[] = { .set = sd_setvflip, .get = sd_getvflip, }, +/* mi0360 only */ +#define INFRARED_IDX 7 + { + { + .id = V4L2_CID_INFRARED, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Infrared", + .minimum = 0, + .maximum = 1, + .step = 1, +#define INFRARED_DEF 0 + .default_value = INFRARED_DEF, + }, + .set = sd_setinfrared, + .get = sd_getinfrared, + }, +}; + +/* table of the disabled controls */ +static __u32 ctrl_dis[] = { + (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + /* SENSOR_HV7131R 0 */ + (1 << VFLIP_IDX), + /* SENSOR_MI0360 1 */ + (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + /* SENSOR_MO4000 2 */ + (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + /* SENSOR_OM6802 3 */ + (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX), + /* SENSOR_OV7630 4 */ + (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + /* SENSOR_OV7648 5 */ + (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + /* SENSOR_OV7660 6 */ }; -static struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120 * 4 / 8 + 590, @@ -231,13 +304,13 @@ static const __u8 sn_ov7630[] = { static const __u8 sn_ov7648[] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20, + 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0xa1, 0x6e, 0x18, 0x65, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x82, + 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00, /* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const __u8 sn_ov7660[] = { @@ -469,6 +542,53 @@ static const __u8 ov7630_sensor_init[][8] = { /* {0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */ {} }; + +static const __u8 ov7648_sensor_init[][8] = { + {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */ + {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10}, + {0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10}, + {0xc1, 0x21, 0x13, 0xa0, 0x04, 0x84, 0x00, 0x10}, + {0xd1, 0x21, 0x17, 0x1a, 0x02, 0xba, 0xf4, 0x10}, + {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x1f, 0x41, 0xc0, 0x80, 0x80, 0x10}, + {0xd1, 0x21, 0x23, 0xde, 0xa0, 0x80, 0x32, 0x10}, + {0xd1, 0x21, 0x27, 0xfe, 0xa0, 0x00, 0x91, 0x10}, + {0xd1, 0x21, 0x2b, 0x00, 0x88, 0x85, 0x80, 0x10}, + {0xc1, 0x21, 0x2f, 0x9c, 0x00, 0xc4, 0x00, 0x10}, + {0xd1, 0x21, 0x60, 0xa6, 0x60, 0x88, 0x12, 0x10}, + {0xd1, 0x21, 0x64, 0x88, 0x00, 0x00, 0x94, 0x10}, + {0xd1, 0x21, 0x68, 0x7a, 0x0c, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x6c, 0x11, 0x33, 0x22, 0x00, 0x10}, + {0xd1, 0x21, 0x70, 0x11, 0x00, 0x10, 0x50, 0x10}, + {0xd1, 0x21, 0x74, 0x20, 0x06, 0x00, 0xb5, 0x10}, + {0xd1, 0x21, 0x78, 0x8a, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x7c, 0x00, 0x43, 0x00, 0x00, 0x10}, + + {0xd1, 0x21, 0x21, 0x86, 0x00, 0xde, 0xa0, 0x10}, +/* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */ +/* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */ + {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, +/*...*/ +/* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, jfm done */ + {0xa1, 0x21, 0x19, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10}, +/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, * GAIN - def */ +/* {0xb1, 0x21, 0x01, 0x6c, 0x6c, 0x00, 0x00, 0x10}, * B R - def: 80 */ +/*...*/ + {0xa1, 0x21, 0x11, 0x81, 0x00, 0x00, 0x00, 0x10}, /* CLKRC */ +/* {0xa1, 0x21, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x2a, 0x91, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xb1, 0x21, 0x01, 0x64, 0x84, 0x00, 0x00, 0x10}, * B R - def: 80 */ + + {} +}; + static const __u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */ /* (delay 20ms) */ @@ -557,64 +677,6 @@ static const __u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x10}, {} }; -/* reg 0x04 reg 0x07 reg 0x10 */ -/* expo = (COM1 & 0x02) | ((AECHH & 0x2f) << 10) | (AECh << 2) */ - -static const __u8 ov7648_sensor_init[][8] = { - {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, - {0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}, - {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, - {0xA1, 0x6E, 0x3F, 0x20, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x04, 0x02, 0xB1, 0x02, 0x39, 0x10}, - {0xD1, 0x6E, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x0C, 0x02, 0x7F, 0x01, 0xE0, 0x10}, - {0xD1, 0x6E, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10}, - {0xD1, 0x6E, 0x16, 0x85, 0x40, 0x4A, 0x40, 0x10}, - {0xC1, 0x6E, 0x1A, 0x00, 0x80, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x1D, 0x08, 0x03, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x23, 0x00, 0xB0, 0x00, 0x94, 0x10}, - {0xD1, 0x6E, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x2D, 0x14, 0x35, 0x61, 0x84, 0x10}, - {0xD1, 0x6E, 0x31, 0xA2, 0xBD, 0xD8, 0xFF, 0x10}, - {0xD1, 0x6E, 0x35, 0x06, 0x1E, 0x12, 0x02, 0x10}, - {0xD1, 0x6E, 0x39, 0xAA, 0x53, 0x37, 0xD5, 0x10}, - {0xA1, 0x6E, 0x3D, 0xF2, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x3E, 0x00, 0x00, 0x80, 0x03, 0x10}, - {0xD1, 0x6E, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10}, - {0xC1, 0x6E, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10}, - {0xD1, 0x6E, 0x4B, 0x02, 0xEF, 0x08, 0xCD, 0x10}, - {0xD1, 0x6E, 0x4F, 0x00, 0xD0, 0x00, 0xA0, 0x10}, - {0xD1, 0x6E, 0x53, 0x01, 0xAA, 0x01, 0x40, 0x10}, - {0xD1, 0x6E, 0x5A, 0x50, 0x04, 0x30, 0x03, 0x10}, - {0xA1, 0x6E, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x5F, 0x10, 0x40, 0xFF, 0x00, 0x10}, - /* {0xD1, 0x6E, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10}, - * This is currently setting a - * blue tint, and some things more , i leave it here for future test if - * somene is having problems with color on this sensor - {0xD1, 0x6E, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xC1, 0x6E, 0x73, 0x10, 0x80, 0xEB, 0x00, 0x10}, - {0xA1, 0x6E, 0x1E, 0x03, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x15, 0x01, 0x00, 0x00, 0x00, 0x10}, - {0xC1, 0x6E, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, - {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x07, 0xB5, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x18, 0x6B, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x07, 0xB8, 0x00, 0x00, 0x00, 0x10}, */ - {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, - {0xA1, 0x6E, 0x06, 0x03, 0x00, 0x00, 0x00, 0x10}, /* Bright... */ - {0xA1, 0x6E, 0x07, 0x66, 0x00, 0x00, 0x00, 0x10}, /* B.. */ - {0xC1, 0x6E, 0x1A, 0x03, 0x65, 0x90, 0x00, 0x10}, /* Bright/Witen....*/ -/* {0xC1, 0x6E, 0x16, 0x45, 0x40, 0x60, 0x00, 0x10}, * Bright/Witene */ - {} -}; static const __u8 qtable4[] = { 0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x08, 0x06, @@ -757,8 +819,6 @@ static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg) static int probesensor(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */ msleep(10); reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */ @@ -770,8 +830,7 @@ static int probesensor(struct gspca_dev *gspca_dev) && gspca_dev->usb_buf[3] == 0x00 && gspca_dev->usb_buf[4] == 0x00) { PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R"); - sd->sensor = SENSOR_HV7131R; - return SENSOR_HV7131R; + return 0; } PDEBUG(D_PROBE, "Find Sensor 0x%02x 0x%02x 0x%02x", gspca_dev->usb_buf[0], gspca_dev->usb_buf[1], @@ -827,17 +886,20 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, 0x40); break; case SENSOR_OV7648: - reg_w1(gspca_dev, 0x01, 0x43); - reg_w1(gspca_dev, 0x17, 0xae); + reg_w1(gspca_dev, 0x01, 0x63); + reg_w1(gspca_dev, 0x17, 0x20); reg_w1(gspca_dev, 0x01, 0x42); break; /*jfm: from win trace */ case SENSOR_OV7660: - reg_w1(gspca_dev, 0x01, 0x61); - reg_w1(gspca_dev, 0x17, 0x20); - reg_w1(gspca_dev, 0x01, 0x60); - reg_w1(gspca_dev, 0x01, 0x40); - break; + if (sd->bridge == BRIDGE_SN9C120) { + reg_w1(gspca_dev, 0x01, 0x61); + reg_w1(gspca_dev, 0x17, 0x20); + reg_w1(gspca_dev, 0x01, 0x60); + reg_w1(gspca_dev, 0x01, 0x40); + break; + } + /* fall thru */ default: reg_w1(gspca_dev, 0x01, 0x43); reg_w1(gspca_dev, 0x17, 0x61); @@ -922,6 +984,13 @@ static void ov7648_InitSensor(struct gspca_dev *gspca_dev) { int i = 0; + i2c_w8(gspca_dev, ov7648_sensor_init[i]); + i++; +/* win: dble reset */ + i2c_w8(gspca_dev, ov7648_sensor_init[i]); /* reset */ + i++; + msleep(20); +/* win: i2c reg read 00..7f */ while (ov7648_sensor_init[i][0]) { i2c_w8(gspca_dev, ov7648_sensor_init[i]); i++; @@ -961,19 +1030,14 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; + sd->blue = BLUE_BALANCE_DEF; + sd->red = RED_BALANCE_DEF; sd->autogain = AUTOGAIN_DEF; sd->ag_cnt = -1; + sd->vflip = VFLIP_DEF; + sd->infrared = INFRARED_DEF; - switch (sd->sensor) { - case SENSOR_OV7630: - case SENSOR_OV7648: - case SENSOR_OV7660: - gspca_dev->ctrl_dis = (1 << AUTOGAIN_IDX); - break; - } - if (sd->sensor != SENSOR_OV7630) - gspca_dev->ctrl_dis |= (1 << VFLIP_IDX); - + gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; return 0; } @@ -981,7 +1045,6 @@ static int sd_config(struct gspca_dev *gspca_dev, static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; -/* const __u8 *sn9c1xx; */ __u8 regGpio[] = { 0x29, 0x74 }; __u8 regF1; @@ -1100,32 +1163,13 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev, return expo; } -/* this function is used for sensors o76xx only */ -static void setbrightcont(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int val; - __u8 reg84_full[0x15]; - - memcpy(reg84_full, reg84, sizeof reg84_full); - val = sd->contrast * 0x30 / CONTRAST_MAX + 0x10; /* 10..40 */ - reg84_full[0] = (val + 1) / 2; /* red */ - reg84_full[2] = val; /* green */ - reg84_full[4] = (val + 1) / 5; /* blue */ - val = (sd->brightness - BRIGHTNESS_DEF) * 0x10 - / BRIGHTNESS_MAX; - reg84_full[0x12] = val & 0x1f; /* 5:0 signed value */ - reg_w(gspca_dev, 0x84, reg84_full, sizeof reg84_full); -} - -/* sensor != ov76xx */ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; unsigned int expo; __u8 k2; - k2 = sd->brightness >> 10; + k2 = ((int) sd->brightness - 0x8000) >> 10; switch (sd->sensor) { case SENSOR_HV7131R: expo = sd->brightness << 4; @@ -1147,38 +1191,49 @@ static void setbrightness(struct gspca_dev *gspca_dev) break; } - reg_w1(gspca_dev, 0x96, k2); + reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */ } -/* sensor != ov76xx */ static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u8 k2; - __u8 contrast[] = { 0x00, 0x00, 0x28, 0x00, 0x07, 0x00 }; - - k2 = sd->contrast; - contrast[2] = k2; - contrast[0] = (k2 + 1) >> 1; - contrast[4] = (k2 + 1) / 5; - reg_w(gspca_dev, 0x84, contrast, 6); + __u8 contrast[6]; + + k2 = sd->contrast * 0x30 / (CONTRAST_MAX + 1) + 0x10; /* 10..40 */ + contrast[0] = (k2 + 1) / 2; /* red */ + contrast[1] = 0; + contrast[2] = k2; /* green */ + contrast[3] = 0; + contrast[4] = (k2 + 1) / 5; /* blue */ + contrast[5] = 0; + reg_w(gspca_dev, 0x84, contrast, sizeof contrast); } static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 blue, red; - - if (sd->colors >= 32) { - red = 32 + (sd->colors - 32) / 2; - blue = 64 - sd->colors; - } else { - red = sd->colors; - blue = 32 + (32 - sd->colors) / 2; + int i, v; + __u8 reg8a[12]; /* U & V gains */ + static __s16 uv[6] = { /* same as reg84 in signed decimal */ + -24, -38, 64, /* UR UG UB */ + 62, -51, -9 /* VR VG VB */ + }; + for (i = 0; i < 6; i++) { + v = uv[i] * sd->colors / COLOR_DEF; + reg8a[i * 2] = v; + reg8a[i * 2 + 1] = (v >> 8) & 0x0f; } - reg_w1(gspca_dev, 0x05, red); + reg_w(gspca_dev, 0x8a, reg8a, sizeof reg8a); +} + +static void setredblue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w1(gspca_dev, 0x05, sd->red); /* reg_w1(gspca_dev, 0x07, 32); */ - reg_w1(gspca_dev, 0x06, blue); + reg_w1(gspca_dev, 0x06, sd->blue); } static void setautogain(struct gspca_dev *gspca_dev) @@ -1195,12 +1250,18 @@ static void setautogain(struct gspca_dev *gspca_dev) static void setvflip(struct sd *sd) { - if (sd->sensor != SENSOR_OV7630) - return; i2c_w1(&sd->gspca_dev, 0x75, /* COMN */ sd->vflip ? 0x82 : 0x02); } +static void setinfrared(struct sd *sd) +{ +/*fixme: different sequence for StarCam Clip and StarCam 370i */ +/* Clip */ + i2c_w1(&sd->gspca_dev, 0x02, /* gpio */ + sd->infrared ? 0x66 : 0x64); +} + /* -- start the camera -- */ static int sd_start(struct gspca_dev *gspca_dev) { @@ -1235,28 +1296,39 @@ static int sd_start(struct gspca_dev *gspca_dev) reg17 = 0xe2; break; case SENSOR_OV7648: - reg17 = 0xae; + reg17 = 0x20; break; /*jfm: from win trace */ case SENSOR_OV7660: - reg17 = 0xa0; - break; + if (sd->bridge == BRIDGE_SN9C120) { + reg17 = 0xa0; + break; + } + /* fall thru */ default: reg17 = 0x60; break; } reg_w1(gspca_dev, 0x17, reg17); - reg_w1(gspca_dev, 0x05, sn9c1xx[5]); - reg_w1(gspca_dev, 0x07, sn9c1xx[7]); - reg_w1(gspca_dev, 0x06, sn9c1xx[6]); +/* set reg1 was here */ + reg_w1(gspca_dev, 0x05, sn9c1xx[5]); /* red */ + reg_w1(gspca_dev, 0x07, sn9c1xx[7]); /* green */ + reg_w1(gspca_dev, 0x06, sn9c1xx[6]); /* blue */ reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]); reg_w(gspca_dev, 0x20, gamma_def, sizeof gamma_def); for (i = 0; i < 8; i++) reg_w(gspca_dev, 0x84, reg84, sizeof reg84); switch (sd->sensor) { - case SENSOR_OV7660: - reg_w1(gspca_dev, 0x9a, 0x05); + case SENSOR_OV7648: + reg_w1(gspca_dev, 0x9a, 0x0a); + reg_w1(gspca_dev, 0x99, 0x60); break; + case SENSOR_OV7660: + if (sd->bridge == BRIDGE_SN9C120) { + reg_w1(gspca_dev, 0x9a, 0x05); + break; + } + /* fall thru */ default: reg_w1(gspca_dev, 0x9a, 0x08); reg_w1(gspca_dev, 0x99, 0x59); @@ -1265,10 +1337,10 @@ static int sd_start(struct gspca_dev *gspca_dev) mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; if (mode) - reg1 = 0x46; /* 320 clk 48Mhz */ + reg1 = 0x46; /* 320x240: clk 48Mhz, video trf enable */ else - reg1 = 0x06; /* 640 clk 24Mz */ - reg17 = 0x61; + reg1 = 0x06; /* 640x480: clk 24Mhz, video trf enable */ + reg17 = 0x61; /* 0x:20: enable sensor clock */ switch (sd->sensor) { case SENSOR_HV7131R: hv7131R_InitSensor(gspca_dev); @@ -1298,23 +1370,21 @@ static int sd_start(struct gspca_dev *gspca_dev) break; case SENSOR_OV7648: ov7648_InitSensor(gspca_dev); - reg17 = 0xa2; - reg1 = 0x44; -/* if (mode) - ; * 320x2... - else - ; * 640x... */ + reg17 = 0x21; +/* reg1 = 0x42; * 42 - 46? */ break; default: /* case SENSOR_OV7660: */ ov7660_InitSensor(gspca_dev); - if (mode) { -/* reg17 = 0x21; * 320 */ -/* reg1 = 0x44; */ -/* reg1 = 0x46; (done) */ + if (sd->bridge == BRIDGE_SN9C120) { + if (mode) { /* 320x240 - 160x120 */ + reg17 = 0xa2; + reg1 = 0x44; /* 48 Mhz, video trf eneble */ + } } else { - reg17 = 0xa2; /* 640 */ - reg1 = 0x44; + reg17 = 0x22; + reg1 = 0x06; /* 24 Mhz, video trf eneble + * inverse power down */ } break; } @@ -1342,23 +1412,18 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x18, reg18); reg_w1(gspca_dev, 0x17, reg17); + reg_w1(gspca_dev, 0x01, reg1); switch (sd->sensor) { - case SENSOR_HV7131R: case SENSOR_MI0360: - case SENSOR_MO4000: - case SENSOR_OM6802: - setbrightness(gspca_dev); - setcontrast(gspca_dev); + setinfrared(sd); break; case SENSOR_OV7630: setvflip(sd); - /* fall thru */ - default: /* OV76xx */ - setbrightcont(gspca_dev); break; } + setbrightness(gspca_dev); + setcontrast(gspca_dev); setautogain(gspca_dev); - reg_w1(gspca_dev, 0x01, reg1); return 0; } @@ -1369,6 +1434,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev) { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 }; static const __u8 stopmi0360[] = { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 }; + static const __u8 stopov7648[] = + { 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 }; __u8 data; const __u8 *sn9c1xx; @@ -1382,8 +1449,10 @@ static void sd_stopN(struct gspca_dev *gspca_dev) i2c_w8(gspca_dev, stopmi0360); data = 0x29; break; - case SENSOR_OV7630: case SENSOR_OV7648: + i2c_w8(gspca_dev, stopov7648); + /* fall thru */ + case SENSOR_OV7630: data = 0x29; break; default: @@ -1437,7 +1506,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) expotimes = 0; sd->exposure = setexposure(gspca_dev, (unsigned int) expotimes); - setcolors(gspca_dev); + setredblue(gspca_dev); break; } } @@ -1491,19 +1560,8 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->brightness = val; - if (gspca_dev->streaming) { - switch (sd->sensor) { - case SENSOR_HV7131R: - case SENSOR_MI0360: - case SENSOR_MO4000: - case SENSOR_OM6802: - setbrightness(gspca_dev); - break; - default: /* OV76xx */ - setbrightcont(gspca_dev); - break; - } - } + if (gspca_dev->streaming) + setbrightness(gspca_dev); return 0; } @@ -1520,19 +1578,8 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->contrast = val; - if (gspca_dev->streaming) { - switch (sd->sensor) { - case SENSOR_HV7131R: - case SENSOR_MI0360: - case SENSOR_MO4000: - case SENSOR_OM6802: - setcontrast(gspca_dev); - break; - default: /* OV76xx */ - setbrightcont(gspca_dev); - break; - } - } + if (gspca_dev->streaming) + setcontrast(gspca_dev); return 0; } @@ -1562,6 +1609,42 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->blue = val; + if (gspca_dev->streaming) + setredblue(gspca_dev); + return 0; +} + +static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->blue; + return 0; +} + +static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->red = val; + if (gspca_dev->streaming) + setredblue(gspca_dev); + return 0; +} + +static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->red; + return 0; +} + static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1598,6 +1681,24 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setinfrared(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->infrared = val; + if (gspca_dev->streaming) + setinfrared(sd); + return 0; +} + +static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->infrared; + return 0; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -1620,12 +1721,15 @@ static const __devinitdata struct usb_device_id device_table[] = { #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)}, {USB_DEVICE(0x0458, 0x702e), BSI(SN9C120, OV7660, 0x21)}, +#endif {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)}, {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)}, +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)}, - {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)}, #endif + {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)}, {USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)}, + {USB_DEVICE(0x06f8, 0x3004), BSI(SN9C105, OV7660, 0x21)}, {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, HV7131R, 0x11)}, /* bw600.inf: {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, MI0360, 0x5d)}, */ @@ -1649,7 +1753,7 @@ static const __devinitdata struct usb_device_id device_table[] = { /* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */ {USB_DEVICE(0x0c45, 0x6128), BSI(SN9C110, OM6802, 0x21)}, /*sn9c325?*/ /*bw600.inf:*/ - {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C110, OV7648, 0x21)}, /*sn9c325?*/ + {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c110?*/ {USB_DEVICE(0x0c45, 0x612c), BSI(SN9C110, MO4000, 0x21)}, {USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x21)}, /* {USB_DEVICE(0x0c45, 0x612f), BSI(SN9C110, ICM105C, 0x??)}, */ @@ -1657,8 +1761,8 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)}, #endif {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)}, + {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE -/* {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x??)}, */ {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)}, {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, /* {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x??)}, */ diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index bca106c..942f04c 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -111,7 +111,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, @@ -124,7 +124,7 @@ static struct v4l2_pix_format vga_mode[] = { .priv = 0}, }; -static struct v4l2_pix_format sif_mode[] = { +static const struct v4l2_pix_format sif_mode[] = { {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144 * 3 / 8 + 590, @@ -633,10 +633,10 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->subtype = id->driver_info; if (sd->subtype != LogitechClickSmart310) { cam->cam_mode = vga_mode; - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes = ARRAY_SIZE(vga_mode); } else { cam->cam_mode = sif_mode; - cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + cam->nmodes = ARRAY_SIZE(sif_mode); } sd->qindex = 5; sd->brightness = BRIGHTNESS_DEF; diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c index e29954c..82e3e3e 100644 --- a/drivers/media/video/gspca/spca501.c +++ b/drivers/media/video/gspca/spca501.c @@ -34,6 +34,8 @@ struct sd { unsigned short contrast; __u8 brightness; __u8 colors; + __u8 blue_balance; + __u8 red_balance; char subtype; #define Arowana300KCMOSCamera 0 @@ -52,6 +54,10 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { #define MY_BRIGHTNESS 0 @@ -63,7 +69,7 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 127, .step = 1, - .default_value = 63, + .default_value = 0, }, .set = sd_setbrightness, .get = sd_getbrightness, @@ -75,9 +81,9 @@ static struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Contrast", .minimum = 0, - .maximum = 0xffff, + .maximum = 64725, .step = 1, - .default_value = 0xaa00, + .default_value = 64725, }, .set = sd_setcontrast, .get = sd_getcontrast, @@ -91,14 +97,42 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 63, .step = 1, - .default_value = 31, + .default_value = 20, }, .set = sd_setcolors, .get = sd_getcolors, }, +#define MY_BLUE_BALANCE 3 + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 0, + }, + .set = sd_setblue_balance, + .get = sd_getblue_balance, + }, +#define MY_RED_BALANCE 4 + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 0, + }, + .set = sd_setred_balance, + .get = sd_getred_balance, + }, }; -static struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120 * 3 / 2, @@ -1822,29 +1856,6 @@ static int reg_write(struct usb_device *dev, return ret; } -/* returns: negative is error, pos or zero is data */ -static int reg_read(struct gspca_dev *gspca_dev, - __u16 req, /* bRequest */ - __u16 index, /* wIndex */ - __u16 length) /* wLength (1 or 2 only) */ -{ - int ret; - - gspca_dev->usb_buf[1] = 0; - ret = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, - gspca_dev->usb_buf, length, - 500); /* timeout */ - if (ret < 0) { - PDEBUG(D_ERR, "reg_read err %d", ret); - return -1; - } - return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; -} static int write_vector(struct gspca_dev *gspca_dev, const __u16 data[][3]) @@ -1869,18 +1880,11 @@ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->brightness); reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness); - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->brightness); } static void getbrightness(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - __u16 brightness; - - brightness = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x11, 2); - sd->brightness = brightness << 1; } static void setcontrast(struct gspca_dev *gspca_dev) @@ -1895,7 +1899,6 @@ static void setcontrast(struct gspca_dev *gspca_dev) static void getcontrast(struct gspca_dev *gspca_dev) { -/* spca50x->contrast = 0xaa01; */ } static void setcolors(struct gspca_dev *gspca_dev) @@ -1907,11 +1910,20 @@ static void setcolors(struct gspca_dev *gspca_dev) static void getcolors(struct gspca_dev *gspca_dev) { +} + +static void setblue_balance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->blue_balance); +} + +static void setred_balance(struct gspca_dev *gspca_dev) +{ struct sd *sd = (struct sd *) gspca_dev; - sd->colors = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x0c, 2); -/* sd->hue = (reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x13, */ -/* 2) & 0xFF) << 8; */ + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->red_balance); } /* this function is called at probe time */ @@ -1930,6 +1942,14 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value; sd->colors = sd_ctrls[MY_COLOR].qctrl.default_value; + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + switch (sd->subtype) { case Arowana300KCMOSCamera: case SmileIntlCamera: @@ -1948,15 +1968,17 @@ static int sd_config(struct gspca_dev *gspca_dev, goto error; break; } + PDEBUG(D_STREAM, "Initializing SPCA501 finished"); return 0; error: return -EINVAL; } -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int mode; switch (sd->subtype) { case ThreeComHomeConnectLite: @@ -1976,14 +1998,6 @@ static int sd_init(struct gspca_dev *gspca_dev) /* Generic 501 open data */ write_vector(gspca_dev, spca501_open_data); } - PDEBUG(D_STREAM, "Initializing SPCA501 finished"); - return 0; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct usb_device *dev = gspca_dev->dev; - int mode; /* memorize the wanted pixel format */ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; @@ -2113,6 +2127,42 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->blue_balance = val; + if (gspca_dev->streaming) + setblue_balance(gspca_dev); + return 0; +} + +static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->blue_balance; + return 0; +} + +static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->red_balance = val; + if (gspca_dev->streaming) + setred_balance(gspca_dev); + return 0; +} + +static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->red_balance; + return 0; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c index 895b9fe..2a33a29 100644 --- a/drivers/media/video/gspca/spca505.c +++ b/drivers/media/video/gspca/spca505.c @@ -59,7 +59,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120 * 3 / 2, diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c index 645ee9d..96e2512 100644 --- a/drivers/media/video/gspca/spca506.c +++ b/drivers/media/video/gspca/spca506.c @@ -110,7 +110,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120 * 3 / 2, diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index 63ec902..be5d740 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/drivers/media/video/gspca/spca508.c @@ -62,7 +62,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static struct v4l2_pix_format sif_mode[] = { +static const struct v4l2_pix_format sif_mode[] = { {160, 120, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120 * 3 / 2, diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c index c3de4e4..3c92880 100644 --- a/drivers/media/video/gspca/spca561.c +++ b/drivers/media/video/gspca/spca561.c @@ -32,22 +32,22 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - __u16 contrast; /* rev72a only */ -#define CONTRAST_MIN 0x0000 -#define CONTRAST_DEF 0x2000 -#define CONTRAST_MAX 0x3fff - __u16 exposure; /* rev12a only */ #define EXPOSURE_MIN 1 #define EXPOSURE_DEF 200 #define EXPOSURE_MAX (4095 - 900) /* see set_exposure */ + __u8 contrast; /* rev72a only */ +#define CONTRAST_MIN 0x00 +#define CONTRAST_DEF 0x20 +#define CONTRAST_MAX 0x3f + __u8 brightness; /* rev72a only */ #define BRIGHTNESS_MIN 0 -#define BRIGHTNESS_DEF 32 -#define BRIGHTNESS_MAX 63 +#define BRIGHTNESS_DEF 0x20 +#define BRIGHTNESS_MAX 0x3f - __u8 white; /* rev12a only */ + __u8 white; #define WHITE_MIN 1 #define WHITE_DEF 0x40 #define WHITE_MAX 0x7f @@ -73,7 +73,7 @@ struct sd { #define AG_CNT_START 13 }; -static struct v4l2_pix_format sif_012a_mode[] = { +static const struct v4l2_pix_format sif_012a_mode[] = { {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120, @@ -96,7 +96,7 @@ static struct v4l2_pix_format sif_012a_mode[] = { .priv = 0}, }; -static struct v4l2_pix_format sif_072a_mode[] = { +static const struct v4l2_pix_format sif_072a_mode[] = { {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120, @@ -146,98 +146,7 @@ static struct v4l2_pix_format sif_072a_mode[] = { #define SPCA561_SNAPBIT 0x20 #define SPCA561_SNAPCTRL 0x40 -static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value) -{ - int ret; - - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, 500); - PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value); - if (ret < 0) - PDEBUG(D_ERR, "reg write: error %d", ret); -} - -static void write_vector(struct gspca_dev *gspca_dev, - const __u16 data[][2]) -{ - struct usb_device *dev = gspca_dev->dev; - int i; - - i = 0; - while (data[i][1] != 0) { - reg_w_val(dev, data[i][1], data[i][0]); - i++; - } -} - -/* read 'len' bytes to gspca_dev->usb_buf */ -static void reg_r(struct gspca_dev *gspca_dev, - __u16 index, __u16 length) -{ - usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - 0, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, gspca_dev->usb_buf, length, 500); -} - -static void reg_w_buf(struct gspca_dev *gspca_dev, - __u16 index, const __u8 *buffer, __u16 len) -{ - memcpy(gspca_dev->usb_buf, buffer, len); - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, gspca_dev->usb_buf, len, 500); -} - -static void i2c_write(struct gspca_dev *gspca_dev, __u16 valeur, __u16 reg) -{ - int retry = 60; - __u8 DataLow; - __u8 DataHight; - - DataLow = valeur; - DataHight = valeur >> 8; - reg_w_val(gspca_dev->dev, 0x8801, reg); - reg_w_val(gspca_dev->dev, 0x8805, DataLow); - reg_w_val(gspca_dev->dev, 0x8800, DataHight); - while (retry--) { - reg_r(gspca_dev, 0x8803, 1); - if (!gspca_dev->usb_buf[0]) - break; - } -} - -static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) -{ - int retry = 60; - __u8 value; - __u8 vallsb; - - reg_w_val(gspca_dev->dev, 0x8804, 0x92); - reg_w_val(gspca_dev->dev, 0x8801, reg); - reg_w_val(gspca_dev->dev, 0x8802, (mode | 0x01)); - do { - reg_r(gspca_dev, 0x8803, 1); - if (!gspca_dev->usb_buf[0]) - break; - } while (--retry); - if (retry == 0) - return -1; - reg_r(gspca_dev, 0x8800, 1); - value = gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8805, 1); - vallsb = gspca_dev->usb_buf[0]; - return ((int) value << 8) | vallsb; -} - -static const __u16 spca561_init_data[][2] = { +static const __u16 rev72a_init_data1[][2] = { {0x0000, 0x8114}, /* Software GPIO output data */ {0x0001, 0x8114}, /* Software GPIO output data */ {0x0000, 0x8112}, /* Some kind of reset */ @@ -247,44 +156,26 @@ static const __u16 spca561_init_data[][2] = { {0x0001, 0x8118}, /* Conf sensor */ {0x0092, 0x8804}, /* I know nothing about these */ {0x0010, 0x8802}, /* 0x88xx registers, so I won't */ - /***************/ {0x000d, 0x8805}, /* sensor default setting */ - {0x0001, 0x8801}, /* 1 <- 0x0d */ - {0x0000, 0x8800}, - {0x0018, 0x8805}, - {0x0002, 0x8801}, /* 2 <- 0x18 */ - {0x0000, 0x8800}, - {0x0065, 0x8805}, - {0x0004, 0x8801}, /* 4 <- 0x01 0x65 */ - {0x0001, 0x8800}, - {0x0021, 0x8805}, - {0x0005, 0x8801}, /* 5 <- 0x21 */ - {0x0000, 0x8800}, - {0x00aa, 0x8805}, - {0x0007, 0x8801}, /* 7 <- 0xaa */ - {0x0000, 0x8800}, - {0x0004, 0x8805}, - {0x0020, 0x8801}, /* 0x20 <- 0x15 0x04 */ - {0x0015, 0x8800}, - {0x0002, 0x8805}, - {0x0039, 0x8801}, /* 0x39 <- 0x02 */ - {0x0000, 0x8800}, - {0x0010, 0x8805}, - {0x0035, 0x8801}, /* 0x35 <- 0x10 */ - {0x0000, 0x8800}, - {0x0049, 0x8805}, - {0x0009, 0x8801}, /* 0x09 <- 0x10 0x49 */ - {0x0010, 0x8800}, - {0x000b, 0x8805}, - {0x0028, 0x8801}, /* 0x28 <- 0x0b */ - {0x0000, 0x8800}, - {0x000f, 0x8805}, - {0x003b, 0x8801}, /* 0x3b <- 0x0f */ - {0x0000, 0x8800}, - {0x0000, 0x8805}, - {0x003c, 0x8801}, /* 0x3c <- 0x00 */ - {0x0000, 0x8800}, - /***************/ + {} +}; +static const __u16 rev72a_init_sensor1[][2] = { + /* ms-win values */ + {0x0001, 0x0018}, /* 0x01 <- 0x0d */ + {0x0002, 0x0065}, /* 0x02 <- 0x18 */ + {0x0004, 0x0121}, /* 0x04 <- 0x0165 */ + {0x0005, 0x00aa}, /* 0x05 <- 0x21 */ + {0x0007, 0x0004}, /* 0x07 <- 0xaa */ + {0x0020, 0x1502}, /* 0x20 <- 0x1504 */ + {0x0039, 0x0010}, /* 0x39 <- 0x02 */ + {0x0035, 0x0049}, /* 0x35 <- 0x10 */ + {0x0009, 0x100b}, /* 0x09 <- 0x1049 */ + {0x0028, 0x000f}, /* 0x28 <- 0x0b */ + {0x003b, 0x003c}, /* 0x3b <- 0x0f */ + {0x003c, 0x0000}, /* 0x3c <- 0x00 */ + {} +}; +static const __u16 rev72a_init_data2[][2] = { {0x0018, 0x8601}, /* Pixel/line selection for color separation */ {0x0000, 0x8602}, /* Optical black level for user setting */ {0x0060, 0x8604}, /* Optical black horizontal offset */ @@ -309,10 +200,11 @@ static const __u16 spca561_init_data[][2] = { {0x0004, 0x8612}, /* Gr offset for white balance */ {0x0007, 0x8613}, /* B offset for white balance */ {0x0000, 0x8614}, /* Gb offset for white balance */ - {0x008c, 0x8651}, /* R gain for white balance */ - {0x008c, 0x8652}, /* Gr gain for white balance */ - {0x00b5, 0x8653}, /* B gain for white balance */ - {0x008c, 0x8654}, /* Gb gain for white balance */ +/* from ms-win */ + {0x0035, 0x8651}, /* R gain for white balance */ + {0x0040, 0x8652}, /* Gr gain for white balance */ + {0x005f, 0x8653}, /* B gain for white balance */ + {0x0040, 0x8654}, /* Gb gain for white balance */ {0x0002, 0x8502}, /* Maximum average bit rate stuff */ {0x0011, 0x8802}, @@ -324,29 +216,22 @@ static const __u16 spca561_init_data[][2] = { {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ {0x0003, 0x865c}, /* Vertical offset for valid lines */ - /***************//* sensor active */ - {0x0003, 0x8801}, /* 0x03 <- 0x01 0x21 //289 */ - {0x0021, 0x8805}, - {0x0001, 0x8800}, - {0x0004, 0x8801}, /* 0x04 <- 0x01 0x65 //357 */ - {0x0065, 0x8805}, - {0x0001, 0x8800}, - {0x0005, 0x8801}, /* 0x05 <- 0x2f */ - {0x002f, 0x8805}, - {0x0000, 0x8800}, - {0x0006, 0x8801}, /* 0x06 <- 0 */ - {0x0000, 0x8805}, - {0x0000, 0x8800}, - {0x000a, 0x8801}, /* 0x0a <- 2 */ - {0x0002, 0x8805}, - {0x0000, 0x8800}, - {0x0009, 0x8801}, /* 0x09 <- 0x1061 */ - {0x0061, 0x8805}, - {0x0010, 0x8800}, - {0x0035, 0x8801}, /* 0x35 <-0x14 */ - {0x0014, 0x8805}, - {0x0000, 0x8800}, + {} +}; +static const __u16 rev72a_init_sensor2[][2] = { + /* ms-win values */ + {0x0003, 0x0121}, /* 0x03 <- 0x01 0x21 //289 */ + {0x0004, 0x0165}, /* 0x04 <- 0x01 0x65 //357 */ + {0x0005, 0x002f}, /* 0x05 <- 0x2f */ + {0x0006, 0x0000}, /* 0x06 <- 0 */ + {0x000a, 0x0002}, /* 0x0a <- 2 */ + {0x0009, 0x1061}, /* 0x09 <- 0x1061 */ + {0x0035, 0x0014}, /* 0x35 <- 0x14 */ + {} +}; +static const __u16 rev72a_init_data3[][2] = { {0x0030, 0x8112}, /* ISO and drop packet enable */ +/*fixme: should stop here*/ {0x0000, 0x8112}, /* Some kind of reset ???? */ {0x0009, 0x8118}, /* Enable sensor and set standby */ {0x0000, 0x8114}, /* Software GPIO output data */ @@ -434,7 +319,6 @@ static const __u16 spca561_init_data[][2] = { {} }; - /******************** QC Express etch2 stuff ********************/ static const __u16 Pb100_1map8300[][2] = { /* reg, value */ @@ -515,22 +399,112 @@ static const __u16 spca561_161rev12A_data2[][2] = { {} }; -static void sensor_mapwrite(struct gspca_dev *gspca_dev, - const __u16 sensormap[][2]) +static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value) +{ + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); +} + +static void write_vector(struct gspca_dev *gspca_dev, + const __u16 data[][2]) { - int i = 0; - __u8 usbval[2]; + struct usb_device *dev = gspca_dev->dev; + int i; - while (sensormap[i][0]) { - usbval[0] = sensormap[i][1]; - usbval[1] = sensormap[i][1] >> 8; - reg_w_buf(gspca_dev, sensormap[i][0], usbval, 2); + i = 0; + while (data[i][1] != 0) { + reg_w_val(dev, data[i][1], data[i][0]); i++; } } + +/* read 'len' bytes to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 index, __u16 length) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, length, 500); +} + +/* write 'len' bytes from gspca_dev->usb_buf */ +static void reg_w_buf(struct gspca_dev *gspca_dev, + __u16 index, __u16 len) +{ + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, len, 500); +} + +static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg) +{ + int retry = 60; + + reg_w_val(gspca_dev->dev, 0x8801, reg); + reg_w_val(gspca_dev->dev, 0x8805, value); + reg_w_val(gspca_dev->dev, 0x8800, value >> 8); + do { + reg_r(gspca_dev, 0x8803, 1); + if (!gspca_dev->usb_buf[0]) + return; + } while (--retry); +} + +static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) +{ + int retry = 60; + __u8 value; + + reg_w_val(gspca_dev->dev, 0x8804, 0x92); + reg_w_val(gspca_dev->dev, 0x8801, reg); + reg_w_val(gspca_dev->dev, 0x8802, mode | 0x01); + do { + reg_r(gspca_dev, 0x8803, 1); + if (!gspca_dev->usb_buf[0]) { + reg_r(gspca_dev, 0x8800, 1); + value = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8805, 1); + return ((int) value << 8) | gspca_dev->usb_buf[0]; + } + } while (--retry); + return -1; +} + +static void sensor_mapwrite(struct gspca_dev *gspca_dev, + const __u16 (*sensormap)[2]) +{ + while ((*sensormap)[0]) { + gspca_dev->usb_buf[0] = (*sensormap)[1]; + gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8; + reg_w_buf(gspca_dev, (*sensormap)[0], 2); + sensormap++; + } +} + +static void write_sensor_72a(struct gspca_dev *gspca_dev, + const __u16 (*sensor)[2]) +{ + while ((*sensor)[0]) { + i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]); + sensor++; + } +} + static void init_161rev12A(struct gspca_dev *gspca_dev) { -/* sensor_reset(gspca_dev); (not in win) */ write_vector(gspca_dev, spca561_161rev12A_data1); sensor_mapwrite(gspca_dev, Pb100_1map8300); /*fixme: should be in sd_start*/ @@ -598,49 +572,68 @@ static int sd_init_12a(struct gspca_dev *gspca_dev) static int sd_init_72a(struct gspca_dev *gspca_dev) { PDEBUG(D_STREAM, "Chip revision: 072a"); - write_vector(gspca_dev, spca561_init_data); + write_vector(gspca_dev, rev72a_init_data1); + write_sensor_72a(gspca_dev, rev72a_init_sensor1); + write_vector(gspca_dev, rev72a_init_data2); + write_sensor_72a(gspca_dev, rev72a_init_sensor2); + write_vector(gspca_dev, rev72a_init_data3); return 0; } -static void setcontrast(struct gspca_dev *gspca_dev) +/* rev 72a only */ +static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; - __u8 lowb; + __u8 value; - switch (sd->chip_revision) { - case Rev072A: - lowb = sd->contrast >> 8; - reg_w_val(dev, 0x8651, lowb); - reg_w_val(dev, 0x8652, lowb); - reg_w_val(dev, 0x8653, lowb); - reg_w_val(dev, 0x8654, lowb); - break; - default: { -/* case Rev012A: { */ - static const __u8 Reg8391[] = - { 0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00 }; + value = sd->brightness; - reg_w_buf(gspca_dev, 0x8391, Reg8391, 8); - reg_w_buf(gspca_dev, 0x8390, Reg8391, 8); - break; - } - } + /* offsets for white balance */ + reg_w_val(dev, 0x8611, value); /* R */ + reg_w_val(dev, 0x8612, value); /* Gr */ + reg_w_val(dev, 0x8613, value); /* B */ + reg_w_val(dev, 0x8614, value); /* Gb */ } -/* rev12a only */ static void setwhite(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u16 white; - __u8 reg8614, reg8616; + __u8 blue, red; + __u16 reg; - white = sd->white; /* try to emulate MS-win as possible */ - reg8616 = 0x90 - white * 5 / 8; - reg_w_val(gspca_dev->dev, 0x8616, reg8616); - reg8614 = 0x20 + white * 3 / 8; - reg_w_val(gspca_dev->dev, 0x8614, reg8614); + white = sd->white; + red = 0x20 + white * 3 / 8; + blue = 0x90 - white * 5 / 8; + if (sd->chip_revision == Rev012A) { + reg = 0x8614; + } else { + reg = 0x8651; + red += sd->contrast - 0x20; + blue += sd->contrast - 0x20; + } + reg_w_val(gspca_dev->dev, reg, red); + reg_w_val(gspca_dev->dev, reg + 2, blue); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 value; + + if (sd->chip_revision != Rev072A) + return; + value = sd->contrast + 0x20; + + /* gains for white balance */ + setwhite(gspca_dev); +/* reg_w_val(dev, 0x8651, value); * R - done by setwhite */ + reg_w_val(dev, 0x8652, value); /* Gr */ +/* reg_w_val(dev, 0x8653, value); * B - done by setwhite */ + reg_w_val(dev, 0x8654, value); /* Gb */ } /* rev 12a only */ @@ -649,7 +642,6 @@ static void setexposure(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int expo; int clock_divider; - __u8 data[2]; /* Register 0x8309 controls exposure for the spca561, the basic exposure setting goes from 1-2047, where 1 is completely @@ -673,20 +665,19 @@ static void setexposure(struct gspca_dev *gspca_dev) clock_divider = 3; } expo |= clock_divider << 11; - data[0] = expo; - data[1] = expo >> 8; - reg_w_buf(gspca_dev, 0x8309, data, 2); + gspca_dev->usb_buf[0] = expo; + gspca_dev->usb_buf[1] = expo >> 8; + reg_w_buf(gspca_dev, 0x8309, 2); } /* rev 12a only */ static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 data[2]; - data[0] = sd->gain; - data[1] = 0; - reg_w_buf(gspca_dev, 0x8335, data, 2); + gspca_dev->usb_buf[0] = sd->gain; + gspca_dev->usb_buf[1] = 0; + reg_w_buf(gspca_dev, 0x8335, 2); } static void setautogain(struct gspca_dev *gspca_dev) @@ -702,9 +693,9 @@ static void setautogain(struct gspca_dev *gspca_dev) static int sd_start_12a(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; - int Clck = 0x8a; /* lower 0x8X values lead to fps > 30 */ - __u8 Reg8307[] = { 0xaa, 0x00 }; int mode; + static const __u8 Reg8391[8] = + {0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00}; mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; if (mode <= 1) { @@ -716,14 +707,21 @@ static int sd_start_12a(struct gspca_dev *gspca_dev) * is sufficient to push raw frames at ~20fps */ reg_w_val(dev, 0x8500, mode); } /* -- qq@kuku.eu.org */ - reg_w_buf(gspca_dev, 0x8307, Reg8307, 2); - reg_w_val(gspca_dev->dev, 0x8700, Clck); + + gspca_dev->usb_buf[0] = 0xaa; + gspca_dev->usb_buf[1] = 0x00; + reg_w_buf(gspca_dev, 0x8307, 2); + /* clock - lower 0x8X values lead to fps > 30 */ + reg_w_val(gspca_dev->dev, 0x8700, 0x8a); /* 0x8f 0x85 0x27 clock */ reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20); reg_w_val(gspca_dev->dev, 0x850b, 0x03); - setcontrast(gspca_dev); + memcpy(gspca_dev->usb_buf, Reg8391, 8); + reg_w_buf(gspca_dev, 0x8391, 8); + reg_w_buf(gspca_dev, 0x8390, 8); setwhite(gspca_dev); setautogain(gspca_dev); +/* setgain(gspca_dev); */ setexposure(gspca_dev); return 0; } @@ -750,6 +748,9 @@ static int sd_start_72a(struct gspca_dev *gspca_dev) reg_w_val(dev, 0x8500, mode); /* mode */ reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ reg_w_val(dev, 0x8112, 0x10 | 0x20); + setcontrast(gspca_dev); +/* setbrightness(gspca_dev); * fixme: bad values */ + setwhite(gspca_dev); setautogain(gspca_dev); return 0; } @@ -791,7 +792,6 @@ static void do_autogain(struct gspca_dev *gspca_dev) __u8 luma_mean = 110; __u8 luma_delta = 20; __u8 spring = 4; - __u8 reg8339[2]; if (sd->ag_cnt < 0) return; @@ -834,13 +834,13 @@ static void do_autogain(struct gspca_dev *gspca_dev) if (gainG > 0x3f) gainG = 0x3f; - else if (gainG < 4) + else if (gainG < 3) gainG = 3; i2c_write(gspca_dev, gainG, 0x35); - if (expotimes >= 0x0256) + if (expotimes > 0x0256) expotimes = 0x0256; - else if (expotimes < 4) + else if (expotimes < 3) expotimes = 3; i2c_write(gspca_dev, expotimes | pixelclk, 0x09); } @@ -848,13 +848,13 @@ static void do_autogain(struct gspca_dev *gspca_dev) case Rev012A: reg_r(gspca_dev, 0x8330, 2); if (gspca_dev->usb_buf[1] > 0x08) { - reg8339[0] = ++sd->expo12a; - reg8339[1] = 0; - reg_w_buf(gspca_dev, 0x8339, reg8339, 2); + gspca_dev->usb_buf[0] = ++sd->expo12a; + gspca_dev->usb_buf[1] = 0; + reg_w_buf(gspca_dev, 0x8339, 2); } else if (gspca_dev->usb_buf[1] < 0x02) { - reg8339[0] = --sd->expo12a; - reg8339[1] = 0; - reg_w_buf(gspca_dev, 0x8339, reg8339, 2); + gspca_dev->usb_buf[0] = --sd->expo12a; + gspca_dev->usb_buf[1] = 0; + reg_w_buf(gspca_dev, 0x8339, 2); } break; } @@ -867,8 +867,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; - switch (data[0]) { - case 0: /* start of frame */ + switch (data[0]) { /* sequence number */ + case 0: /* start of frame */ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); data += SPCA561_OFFSET_DATA; @@ -890,8 +890,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, frame, data, len); } return; - case 0xff: /* drop */ -/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + case 0xff: /* drop (empty mpackets) */ return; } data++; @@ -900,55 +899,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } /* rev 72a only */ -static void setbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u8 value; - - value = sd->brightness; - reg_w_val(gspca_dev->dev, 0x8611, value); - reg_w_val(gspca_dev->dev, 0x8612, value); - reg_w_val(gspca_dev->dev, 0x8613, value); - reg_w_val(gspca_dev->dev, 0x8614, value); -} - -static void getbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u16 tot; - - tot = 0; - reg_r(gspca_dev, 0x8611, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8612, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8613, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8614, 1); - tot += gspca_dev->usb_buf[0]; - sd->brightness = tot >> 2; -} - -/* rev72a only */ -static void getcontrast(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u16 tot; - - tot = 0; - reg_r(gspca_dev, 0x8651, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8652, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8653, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8654, 1); - tot += gspca_dev->usb_buf[0]; - sd->contrast = tot << 6; - PDEBUG(D_CONF, "get contrast %d", sd->contrast); -} - -/* rev 72a only */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -963,7 +913,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getbrightness(gspca_dev); *val = sd->brightness; return 0; } @@ -983,7 +932,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getcontrast(gspca_dev); *val = sd->contrast; return 0; } @@ -1006,7 +954,6 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } -/* rev12a only */ static int sd_setwhite(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1121,6 +1068,19 @@ static struct ctrl sd_ctrls_12a[] = { static struct ctrl sd_ctrls_72a[] = { { + { + .id = V4L2_CID_DO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "White Balance", + .minimum = WHITE_MIN, + .maximum = WHITE_MAX, + .step = 1, + .default_value = WHITE_DEF, + }, + .set = sd_setwhite, + .get = sd_getwhite, + }, + { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index d9d6491..60de9af 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -109,7 +109,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, @@ -424,10 +424,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* beginning of the frame */ #define STKHDRSZ 12 - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data + STKHDRSZ, len - STKHDRSZ); -#undef STKHDRSZ - return; + data += STKHDRSZ; + len -= STKHDRSZ; } gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } diff --git a/drivers/media/video/gspca/stv06xx/Kconfig b/drivers/media/video/gspca/stv06xx/Kconfig new file mode 100644 index 0000000..634ad38d --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/Kconfig @@ -0,0 +1,9 @@ +config USB_STV06XX + tristate "STV06XX USB Camera Driver" + depends on USB_GSPCA + help + Say Y here if you want support for cameras based on + the ST STV06XX chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_stv06xx. diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile new file mode 100644 index 0000000..feeaa94 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/Makefile @@ -0,0 +1,9 @@ +obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o + +gspca_stv06xx-objs := stv06xx.o \ + stv06xx_vv6410.o \ + stv06xx_hdcs.o \ + stv06xx_pb0100.o + +EXTRA_CFLAGS += -Idrivers/media/video/gspca + diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c new file mode 100644 index 0000000..13a021e --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#include "stv06xx_sensor.h" + +MODULE_AUTHOR("Erik Andrén"); +MODULE_DESCRIPTION("STV06XX USB Camera Driver"); +MODULE_LICENSE("GPL"); + +static int dump_bridge; +static int dump_sensor; + +int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data) +{ + int err; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + u8 len = (i2c_data > 0xff) ? 2 : 1; + + buf[0] = i2c_data & 0xff; + buf[1] = (i2c_data >> 8) & 0xff; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, address, 0, buf, len, + STV06XX_URB_MSG_TIMEOUT); + + + PDEBUG(D_CONF, "Written 0x%x to address 0x%x, status: %d", + i2c_data, address, err); + + return (err < 0) ? err : 0; +} + +int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data) +{ + int err; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0x04, 0xc0, address, 0, buf, 1, + STV06XX_URB_MSG_TIMEOUT); + + *i2c_data = buf[0]; + + PDEBUG(D_CONF, "Read 0x%x from address 0x%x, status %d", + *i2c_data, address, err); + + return (err < 0) ? err : 0; +} + +/* Wraps the normal write sensor bytes / words functions for writing a + single value */ +int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value) +{ + if (sd->sensor->i2c_len == 2) { + u16 data[2] = { address, value }; + return stv06xx_write_sensor_words(sd, data, 1); + } else { + u8 data[2] = { address, value }; + return stv06xx_write_sensor_bytes(sd, data, 1); + } +} + +static int stv06xx_write_sensor_finish(struct sd *sd) +{ + int err = 0; + + if (IS_850(sd)) { + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + /* Quickam Web needs an extra packet */ + buf[0] = 0; + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x1704, 0, buf, 1, + STV06XX_URB_MSG_TIMEOUT); + } + + return (err < 0) ? err : 0; +} + +int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len) +{ + int err, i, j; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + PDEBUG(D_USBO, "I2C: Command buffer contains %d entries", len); + for (i = 0; i < len;) { + /* Build the command buffer */ + memset(buf, 0, I2C_BUFFER_LENGTH); + for (j = 0; j < I2C_MAX_BYTES && i < len; j++, i++) { + buf[j] = data[2*i]; + buf[0x10 + j] = data[2*i+1]; + PDEBUG(D_USBO, "I2C: Writing 0x%02x to reg 0x%02x", + data[2*i+1], data[2*i]); + } + buf[0x20] = sd->sensor->i2c_addr; + buf[0x21] = j - 1; /* Number of commands to send - 1 */ + buf[0x22] = I2C_WRITE_CMD; + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x0400, 0, buf, + I2C_BUFFER_LENGTH, + STV06XX_URB_MSG_TIMEOUT); + if (err < 0) + return err; + } + return stv06xx_write_sensor_finish(sd); +} + +int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len) +{ + int err, i, j; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + PDEBUG(D_USBO, "I2C: Command buffer contains %d entries", len); + + for (i = 0; i < len;) { + /* Build the command buffer */ + memset(buf, 0, I2C_BUFFER_LENGTH); + for (j = 0; j < I2C_MAX_WORDS && i < len; j++, i++) { + buf[j] = data[2*i]; + buf[0x10 + j * 2] = data[2*i+1]; + buf[0x10 + j * 2 + 1] = data[2*i+1] >> 8; + PDEBUG(D_USBO, "I2C: Writing 0x%04x to reg 0x%02x", + data[2*i+1], data[2*i]); + } + buf[0x20] = sd->sensor->i2c_addr; + buf[0x21] = j - 1; /* Number of commands to send - 1 */ + buf[0x22] = I2C_WRITE_CMD; + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x0400, 0, buf, + I2C_BUFFER_LENGTH, + STV06XX_URB_MSG_TIMEOUT); + if (err < 0) + return err; + } + return stv06xx_write_sensor_finish(sd); +} + +int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value) +{ + int err; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + err = stv06xx_write_bridge(sd, STV_I2C_FLUSH, sd->sensor->i2c_flush); + if (err < 0) + return err; + + /* Clear mem */ + memset(buf, 0, I2C_BUFFER_LENGTH); + + buf[0] = address; + buf[0x20] = sd->sensor->i2c_addr; + buf[0x21] = 0; + + /* Read I2C register */ + buf[0x22] = I2C_READ_CMD; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH, + STV06XX_URB_MSG_TIMEOUT); + if (err < 0) { + PDEBUG(D_ERR, "I2C Read: error writing address: %d", err); + return err; + } + + err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0x04, 0xc0, 0x1410, 0, buf, sd->sensor->i2c_len, + STV06XX_URB_MSG_TIMEOUT); + if (sd->sensor->i2c_len == 2) + *value = buf[0] | (buf[1] << 8); + else + *value = buf[0]; + + PDEBUG(D_USBO, "I2C: Read 0x%x from address 0x%x, status: %d", + *value, address, err); + + return (err < 0) ? err : 0; +} + +/* Dumps all bridge registers */ +static void stv06xx_dump_bridge(struct sd *sd) +{ + int i; + u8 data, buf; + + info("Dumping all stv06xx bridge registers"); + for (i = 0x1400; i < 0x160f; i++) { + stv06xx_read_bridge(sd, i, &data); + + info("Read 0x%x from address 0x%x", data, i); + } + + for (i = 0x1400; i < 0x160f; i++) { + stv06xx_read_bridge(sd, i, &data); + buf = data; + + stv06xx_write_bridge(sd, i, 0xff); + stv06xx_read_bridge(sd, i, &data); + if (data == 0xff) + info("Register 0x%x is read/write", i); + else if (data != buf) + info("Register 0x%x is read/write," + "but only partially", i); + else + info("Register 0x%x is read-only", i); + + stv06xx_write_bridge(sd, i, buf); + } +} + +/* this function is called at probe and resume time */ +static int stv06xx_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err; + + PDEBUG(D_PROBE, "Initializing camera"); + + /* Let the usb init settle for a bit + before performing the initialization */ + msleep(250); + + err = sd->sensor->init(sd); + + if (dump_sensor) + sd->sensor->dump(sd); + + return (err < 0) ? err : 0; +} + +/* Start the camera */ +static int stv06xx_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err; + + /* Prepare the sensor for start */ + err = sd->sensor->start(sd); + if (err < 0) + goto out; + + /* Start isochronous streaming */ + err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 1); + +out: + if (err < 0) + PDEBUG(D_STREAM, "Starting stream failed"); + else + PDEBUG(D_STREAM, "Started streaming"); + + return (err < 0) ? err : 0; +} + +static void stv06xx_stopN(struct gspca_dev *gspca_dev) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + + /* stop ISO-streaming */ + err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 0); + if (err < 0) + goto out; + + err = sd->sensor->stop(sd); + if (err < 0) + goto out; + +out: + if (err < 0) + PDEBUG(D_STREAM, "Failed to stop stream"); + else + PDEBUG(D_STREAM, "Stopped streaming"); +} + +/* + * Analyse an USB packet of the data stream and store it appropriately. + * Each packet contains an integral number of chunks. Each chunk has + * 2-bytes identification, followed by 2-bytes that describe the chunk + * length. Known/guessed chunk identifications are: + * 8001/8005/C001/C005 - Begin new frame + * 8002/8006/C002/C006 - End frame + * 0200/4200 - Contains actual image data, bayer or compressed + * 0005 - 11 bytes of unknown data + * 0100 - 2 bytes of unknown data + * The 0005 and 0100 chunks seem to appear only in compressed stream. + */ +static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + PDEBUG(D_PACK, "Packet of length %d arrived", len); + + /* A packet may contain several frames + loop until the whole packet is reached */ + while (len) { + int id, chunk_len; + + if (len < 4) { + PDEBUG(D_PACK, "Packet is smaller than 4 bytes"); + return; + } + + /* Capture the id */ + id = (data[0] << 8) | data[1]; + + /* Capture the chunk length */ + chunk_len = (data[2] << 8) | data[3]; + PDEBUG(D_PACK, "Chunk id: %x, length: %d", id, chunk_len); + + data += 4; + len -= 4; + + if (len < chunk_len) { + PDEBUG(D_ERR, "URB packet length is smaller" + " than the specified chunk length"); + return; + } + + switch (id) { + case 0x0200: + case 0x4200: + PDEBUG(D_PACK, "Frame data packet detected"); + + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, chunk_len); + break; + + case 0x8001: + case 0x8005: + case 0xc001: + case 0xc005: + PDEBUG(D_PACK, "Starting new frame"); + + /* Create a new frame, chunk length should be zero */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + frame, data, 0); + + if (chunk_len) + PDEBUG(D_ERR, "Chunk length is " + "non-zero on a SOF"); + break; + + case 0x8002: + case 0x8006: + case 0xc002: + PDEBUG(D_PACK, "End of frame detected"); + + /* Complete the last frame (if any) */ + gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); + + if (chunk_len) + PDEBUG(D_ERR, "Chunk length is " + "non-zero on a EOF"); + break; + + case 0x0005: + PDEBUG(D_PACK, "Chunk 0x005 detected"); + /* Unknown chunk with 11 bytes of data, + occurs just before end of each frame + in compressed mode */ + break; + + case 0x0100: + PDEBUG(D_PACK, "Chunk 0x0100 detected"); + /* Unknown chunk with 2 bytes of data, + occurs 2-3 times per USB interrupt */ + break; + default: + PDEBUG(D_PACK, "Unknown chunk %d detected", id); + /* Unknown chunk */ + } + data += chunk_len; + len -= chunk_len; + } +} + +static int stv06xx_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id); + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = stv06xx_config, + .init = stv06xx_init, + .start = stv06xx_start, + .stopN = stv06xx_stopN, + .pkt_scan = stv06xx_pkt_scan +}; + +/* This function is called at probe time */ +static int stv06xx_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + PDEBUG(D_PROBE, "Configuring camera"); + + cam = &gspca_dev->cam; + cam->epaddr = STV_ISOC_ENDPOINT_ADDR; + sd->desc = sd_desc; + gspca_dev->sd_desc = &sd->desc; + + if (dump_bridge) + stv06xx_dump_bridge(sd); + + sd->sensor = &stv06xx_sensor_vv6410; + if (!sd->sensor->probe(sd)) + return 0; + + sd->sensor = &stv06xx_sensor_hdcs1x00; + if (!sd->sensor->probe(sd)) + return 0; + + sd->sensor = &stv06xx_sensor_hdcs1020; + if (!sd->sensor->probe(sd)) + return 0; + + sd->sensor = &stv06xx_sensor_pb0100; + if (!sd->sensor->probe(sd)) + return 0; + + sd->sensor = NULL; + return -ENODEV; +} + + + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x046d, 0x0840)}, /* QuickCam Express */ + {USB_DEVICE(0x046d, 0x0850)}, /* LEGO cam / QuickCam Web */ + {USB_DEVICE(0x046d, 0x0870)}, /* Dexxa WebCam USB */ + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + PDEBUG(D_PROBE, "Probing for a stv06xx device"); + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static void sd_disconnect(struct usb_interface *intf) +{ + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + struct sd *sd = (struct sd *) gspca_dev; + PDEBUG(D_PROBE, "Disconnecting the stv06xx device"); + + if (sd->sensor->disconnect) + sd->sensor->disconnect(sd); + gspca_disconnect(intf); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = sd_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); + +module_param(dump_bridge, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup"); + +module_param(dump_sensor, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dump_sensor, "Dumps all sensor registers at startup"); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h new file mode 100644 index 0000000..1207e7d --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#ifndef STV06XX_H_ +#define STV06XX_H_ + +#include "gspca.h" + +#define MODULE_NAME "STV06xx" + +#define STV_ISOC_ENDPOINT_ADDR 0x81 + +#ifndef V4L2_PIX_FMT_SGRBG8 +#define V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G') +#endif + +#define STV_REG23 0x0423 + +/* Control registers of the STV0600 ASIC */ +#define STV_I2C_PARTNER 0x1420 +#define STV_I2C_VAL_REG_VAL_PAIRS_MIN1 0x1421 +#define STV_I2C_READ_WRITE_TOGGLE 0x1422 +#define STV_I2C_FLUSH 0x1423 +#define STV_I2C_SUCC_READ_REG_VALS 0x1424 + +#define STV_ISO_ENABLE 0x1440 +#define STV_SCAN_RATE 0x1443 +#define STV_LED_CTRL 0x1445 +#define STV_STV0600_EMULATION 0x1446 +#define STV_REG00 0x1500 +#define STV_REG01 0x1501 +#define STV_REG02 0x1502 +#define STV_REG03 0x1503 +#define STV_REG04 0x1504 + +#define STV_ISO_SIZE_L 0x15c1 +#define STV_ISO_SIZE_H 0x15c2 + +/* Refers to the CIF 352x288 and QCIF 176x144 */ +/* 1: 288 lines, 2: 144 lines */ +#define STV_Y_CTRL 0x15c3 + +/* 0xa: 352 columns, 0x6: 176 columns */ +#define STV_X_CTRL 0x1680 + +#define STV06XX_URB_MSG_TIMEOUT 5000 + +#define I2C_MAX_BYTES 16 +#define I2C_MAX_WORDS 8 + +#define I2C_BUFFER_LENGTH 0x23 +#define I2C_READ_CMD 3 +#define I2C_WRITE_CMD 1 + +#define LED_ON 1 +#define LED_OFF 0 + +/* STV06xx device descriptor */ +struct sd { + struct gspca_dev gspca_dev; + + /* A pointer to the currently connected sensor */ + const struct stv06xx_sensor *sensor; + + /* A pointer to the sd_desc struct */ + struct sd_desc desc; + + /* Sensor private data */ + void *sensor_priv; +}; + +int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data); +int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data); + +int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len); +int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len); + +int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value); +int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value); + +#endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c new file mode 100644 index 0000000..14335a9 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c @@ -0,0 +1,535 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * Copyright (c) 2008 Chia-I Wu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#include "stv06xx_hdcs.h" + +enum hdcs_power_state { + HDCS_STATE_SLEEP, + HDCS_STATE_IDLE, + HDCS_STATE_RUN +}; + +/* no lock? */ +struct hdcs { + enum hdcs_power_state state; + int w, h; + + /* visible area of the sensor array */ + struct { + int left, top; + int width, height; + int border; + } array; + + struct { + /* Column timing overhead */ + u8 cto; + /* Column processing overhead */ + u8 cpo; + /* Row sample period constant */ + u16 rs; + /* Exposure reset duration */ + u16 er; + } exp; + + int psmp; +}; + +static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len) +{ + u8 regs[I2C_MAX_BYTES * 2]; + int i; + + if (unlikely((len <= 0) || (len >= I2C_MAX_BYTES) || + (reg + len > 0xff))) + return -EINVAL; + + for (i = 0; i < len; i++, reg++) { + regs[2*i] = reg; + regs[2*i+1] = vals[i]; + } + + return stv06xx_write_sensor_bytes(sd, regs, len); +} + +static int hdcs_set_state(struct sd *sd, enum hdcs_power_state state) +{ + struct hdcs *hdcs = sd->sensor_priv; + u8 val; + int ret; + + if (hdcs->state == state) + return 0; + + /* we need to go idle before running or sleeping */ + if (hdcs->state != HDCS_STATE_IDLE) { + ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0); + if (ret) + return ret; + } + + hdcs->state = HDCS_STATE_IDLE; + + if (state == HDCS_STATE_IDLE) + return 0; + + switch (state) { + case HDCS_STATE_SLEEP: + val = HDCS_SLEEP_MODE; + break; + + case HDCS_STATE_RUN: + val = HDCS_RUN_ENABLE; + break; + + default: + return -EINVAL; + } + + ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), val); + if (ret < 0) + hdcs->state = state; + + return ret; +} + +static int hdcs_reset(struct sd *sd) +{ + struct hdcs *hdcs = sd->sensor_priv; + int err; + + err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 1); + if (err < 0) + return err; + + err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0); + if (err < 0) + hdcs->state = HDCS_STATE_IDLE; + + return err; +} + +static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct hdcs *hdcs = sd->sensor_priv; + + /* Column time period */ + int ct; + /* Column processing period */ + int cp; + /* Row processing period */ + int rp; + int cycles; + int err; + int rowexp; + u16 data[2]; + + err = stv06xx_read_sensor(sd, HDCS_ROWEXPL, &data[0]); + if (err < 0) + return err; + + err = stv06xx_read_sensor(sd, HDCS_ROWEXPH, &data[1]); + if (err < 0) + return err; + + rowexp = (data[1] << 8) | data[0]; + + ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2); + cp = hdcs->exp.cto + (hdcs->w * ct / 2); + rp = hdcs->exp.rs + cp; + + cycles = rp * rowexp; + *val = cycles / HDCS_CLK_FREQ_MHZ; + PDEBUG(D_V4L2, "Read exposure %d", *val); + return 0; +} + +static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct hdcs *hdcs = sd->sensor_priv; + int rowexp, srowexp; + int max_srowexp; + /* Column time period */ + int ct; + /* Column processing period */ + int cp; + /* Row processing period */ + int rp; + /* Minimum number of column timing periods + within the column processing period */ + int mnct; + int cycles, err; + u8 exp[4]; + + cycles = val * HDCS_CLK_FREQ_MHZ; + + ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2); + cp = hdcs->exp.cto + (hdcs->w * ct / 2); + + /* the cycles one row takes */ + rp = hdcs->exp.rs + cp; + + rowexp = cycles / rp; + + /* the remaining cycles */ + cycles -= rowexp * rp; + + /* calculate sub-row exposure */ + if (IS_1020(sd)) { + /* see HDCS-1020 datasheet 3.5.6.4, p. 63 */ + srowexp = hdcs->w - (cycles + hdcs->exp.er + 13) / ct; + + mnct = (hdcs->exp.er + 12 + ct - 1) / ct; + max_srowexp = hdcs->w - mnct; + } else { + /* see HDCS-1000 datasheet 3.4.5.5, p. 61 */ + srowexp = cp - hdcs->exp.er - 6 - cycles; + + mnct = (hdcs->exp.er + 5 + ct - 1) / ct; + max_srowexp = cp - mnct * ct - 1; + } + + if (srowexp < 0) + srowexp = 0; + else if (srowexp > max_srowexp) + srowexp = max_srowexp; + + if (IS_1020(sd)) { + exp[0] = rowexp & 0xff; + exp[1] = rowexp >> 8; + exp[2] = (srowexp >> 2) & 0xff; + /* this clears exposure error flag */ + exp[3] = 0x1; + err = hdcs_reg_write_seq(sd, HDCS_ROWEXPL, exp, 4); + } else { + exp[0] = rowexp & 0xff; + exp[1] = rowexp >> 8; + exp[2] = srowexp & 0xff; + exp[3] = srowexp >> 8; + err = hdcs_reg_write_seq(sd, HDCS_ROWEXPL, exp, 4); + if (err < 0) + return err; + + /* clear exposure error flag */ + err = stv06xx_write_sensor(sd, + HDCS_STATUS, BIT(4)); + } + PDEBUG(D_V4L2, "Writing exposure %d, rowexp %d, srowexp %d", + val, rowexp, srowexp); + return err; +} + +static int hdcs_set_gains(struct sd *sd, u8 r, u8 g, u8 b) +{ + u8 gains[4]; + + /* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */ + if (r > 127) + r = 0x80 | (r / 2); + if (g > 127) + g = 0x80 | (g / 2); + if (b > 127) + b = 0x80 | (b / 2); + + gains[0] = g; + gains[1] = r; + gains[2] = b; + gains[3] = g; + + return hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4); +} + +static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err; + u16 data; + + err = stv06xx_read_sensor(sd, HDCS_ERECPGA, &data); + + /* Bit 7 doubles the gain */ + if (data & 0x80) + *val = (data & 0x7f) * 2; + else + *val = data; + + PDEBUG(D_V4L2, "Read gain %d", *val); + return err; +} + +static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + PDEBUG(D_V4L2, "Writing gain %d", val); + return hdcs_set_gains((struct sd *) gspca_dev, + val & 0xff, val & 0xff, val & 0xff); +} + +static int hdcs_set_size(struct sd *sd, + unsigned int width, unsigned int height) +{ + struct hdcs *hdcs = sd->sensor_priv; + u8 win[4]; + unsigned int x, y; + int err; + + /* must be multiple of 4 */ + width = (width + 3) & ~0x3; + height = (height + 3) & ~0x3; + + if (width > hdcs->array.width) + width = hdcs->array.width; + + if (IS_1020(sd)) { + /* the borders are also invalid */ + if (height + 2 * hdcs->array.border + HDCS_1020_BOTTOM_Y_SKIP + > hdcs->array.height) + height = hdcs->array.height - 2 * hdcs->array.border - + HDCS_1020_BOTTOM_Y_SKIP; + + y = (hdcs->array.height - HDCS_1020_BOTTOM_Y_SKIP - height) / 2 + + hdcs->array.top; + } else { + if (height > hdcs->array.height) + height = hdcs->array.height; + + y = hdcs->array.top + (hdcs->array.height - height) / 2; + } + + x = hdcs->array.left + (hdcs->array.width - width) / 2; + + win[0] = y / 4; + win[1] = x / 4; + win[2] = (y + height) / 4 - 1; + win[3] = (x + width) / 4 - 1; + + err = hdcs_reg_write_seq(sd, HDCS_FWROW, win, 4); + if (err < 0) + return err; + + /* Update the current width and height */ + hdcs->w = width; + hdcs->h = height; + return err; +} + +static int hdcs_probe_1x00(struct sd *sd) +{ + struct hdcs *hdcs; + u16 sensor; + int ret; + + ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor); + if (ret < 0 || sensor != 0x08) + return -ENODEV; + + info("HDCS-1000/1100 sensor detected"); + + sd->gspca_dev.cam.cam_mode = stv06xx_sensor_hdcs1x00.modes; + sd->gspca_dev.cam.nmodes = stv06xx_sensor_hdcs1x00.nmodes; + sd->desc.ctrls = stv06xx_sensor_hdcs1x00.ctrls; + sd->desc.nctrls = stv06xx_sensor_hdcs1x00.nctrls; + + hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); + if (!hdcs) + return -ENOMEM; + + hdcs->array.left = 8; + hdcs->array.top = 8; + hdcs->array.width = HDCS_1X00_DEF_WIDTH; + hdcs->array.height = HDCS_1X00_DEF_HEIGHT; + hdcs->array.border = 4; + + hdcs->exp.cto = 4; + hdcs->exp.cpo = 2; + hdcs->exp.rs = 186; + hdcs->exp.er = 100; + + /* + * Frame rate on HDCS-1000 0x46D:0x840 depends on PSMP: + * 4 = doesn't work at all + * 5 = 7.8 fps, + * 6 = 6.9 fps, + * 8 = 6.3 fps, + * 10 = 5.5 fps, + * 15 = 4.4 fps, + * 31 = 2.8 fps + * + * Frame rate on HDCS-1000 0x46D:0x870 depends on PSMP: + * 15 = doesn't work at all + * 18 = doesn't work at all + * 19 = 7.3 fps + * 20 = 7.4 fps + * 21 = 7.4 fps + * 22 = 7.4 fps + * 24 = 6.3 fps + * 30 = 5.4 fps + */ + hdcs->psmp = IS_870(sd) ? 20 : 5; + + sd->sensor_priv = hdcs; + + return 0; +} + +static int hdcs_probe_1020(struct sd *sd) +{ + struct hdcs *hdcs; + u16 sensor; + int ret; + + ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor); + if (ret < 0 || sensor != 0x10) + return -ENODEV; + + info("HDCS-1020 sensor detected"); + + sd->gspca_dev.cam.cam_mode = stv06xx_sensor_hdcs1020.modes; + sd->gspca_dev.cam.nmodes = stv06xx_sensor_hdcs1020.nmodes; + sd->desc.ctrls = stv06xx_sensor_hdcs1020.ctrls; + sd->desc.nctrls = stv06xx_sensor_hdcs1020.nctrls; + + hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); + if (!hdcs) + return -ENOMEM; + + /* + * From Andrey's test image: looks like HDCS-1020 upper-left + * visible pixel is at 24,8 (y maybe even smaller?) and lower-right + * visible pixel at 375,299 (x maybe even larger?) + */ + hdcs->array.left = 24; + hdcs->array.top = 4; + hdcs->array.width = HDCS_1020_DEF_WIDTH; + hdcs->array.height = 304; + hdcs->array.border = 4; + + hdcs->psmp = 6; + + hdcs->exp.cto = 3; + hdcs->exp.cpo = 3; + hdcs->exp.rs = 155; + hdcs->exp.er = 96; + + sd->sensor_priv = hdcs; + + return 0; +} + +static int hdcs_start(struct sd *sd) +{ + PDEBUG(D_STREAM, "Starting stream"); + + return hdcs_set_state(sd, HDCS_STATE_RUN); +} + +static int hdcs_stop(struct sd *sd) +{ + PDEBUG(D_STREAM, "Halting stream"); + + return hdcs_set_state(sd, HDCS_STATE_SLEEP); +} + +static void hdcs_disconnect(struct sd *sd) +{ + PDEBUG(D_PROBE, "Disconnecting the sensor"); + kfree(sd->sensor_priv); +} + +static int hdcs_init(struct sd *sd) +{ + struct hdcs *hdcs = sd->sensor_priv; + int i, err = 0; + + /* Set the STV0602AA in STV0600 emulation mode */ + if (IS_870(sd)) + stv06xx_write_bridge(sd, STV_STV0600_EMULATION, 1); + + /* Execute the bridge init */ + for (i = 0; i < ARRAY_SIZE(stv_bridge_init) && !err; i++) { + err = stv06xx_write_bridge(sd, stv_bridge_init[i][0], + stv_bridge_init[i][1]); + } + if (err < 0) + return err; + + /* sensor soft reset */ + hdcs_reset(sd); + + /* Execute the sensor init */ + for (i = 0; i < ARRAY_SIZE(stv_sensor_init) && !err; i++) { + err = stv06xx_write_sensor(sd, stv_sensor_init[i][0], + stv_sensor_init[i][1]); + } + if (err < 0) + return err; + + /* Enable continous frame capture, bit 2: stop when frame complete */ + err = stv06xx_write_sensor(sd, HDCS_REG_CONFIG(sd), BIT(3)); + if (err < 0) + return err; + + /* Set PGA sample duration + (was 0x7E for IS_870, but caused slow framerate with HDCS-1020) */ + if (IS_1020(sd)) + err = stv06xx_write_sensor(sd, HDCS_TCTRL, + (HDCS_ADC_START_SIG_DUR << 6) | hdcs->psmp); + else + err = stv06xx_write_sensor(sd, HDCS_TCTRL, + (HDCS_ADC_START_SIG_DUR << 5) | hdcs->psmp); + if (err < 0) + return err; + + err = hdcs_set_gains(sd, HDCS_DEFAULT_GAIN, HDCS_DEFAULT_GAIN, + HDCS_DEFAULT_GAIN); + if (err < 0) + return err; + + err = hdcs_set_exposure(&sd->gspca_dev, HDCS_DEFAULT_EXPOSURE); + if (err < 0) + return err; + + err = hdcs_set_size(sd, hdcs->array.width, hdcs->array.height); + return err; +} + +static int hdcs_dump(struct sd *sd) +{ + u16 reg, val; + + info("Dumping sensor registers:"); + + for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) { + stv06xx_read_sensor(sd, reg, &val); + info("reg 0x%02x = 0x%02x", reg, val); + } + return 0; +} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h new file mode 100644 index 0000000..9c7279a --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * Copyright (c) 2008 Chia-I Wu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#ifndef STV06XX_HDCS_H_ +#define STV06XX_HDCS_H_ + +#include "stv06xx_sensor.h" + +#define HDCS_REG_CONFIG(sd) (IS_1020(sd) ? HDCS20_CONFIG : HDCS00_CONFIG) +#define HDCS_REG_CONTROL(sd) (IS_1020(sd) ? HDCS20_CONTROL : HDCS00_CONTROL) + +#define HDCS_1X00_DEF_WIDTH 360 +#define HDCS_1X00_DEF_HEIGHT 296 + +#define HDCS_1020_DEF_WIDTH 352 +#define HDCS_1020_DEF_HEIGHT 292 + +#define HDCS_1020_BOTTOM_Y_SKIP 4 + +#define HDCS_CLK_FREQ_MHZ 25 + +#define HDCS_ADC_START_SIG_DUR 3 + +/* LSB bit of I2C or register address signifies write (0) or read (1) */ +/* I2C Registers common for both HDCS-1000/1100 and HDCS-1020 */ +/* Identifications Register */ +#define HDCS_IDENT (0x00 << 1) +/* Status Register */ +#define HDCS_STATUS (0x01 << 1) +/* Interrupt Mask Register */ +#define HDCS_IMASK (0x02 << 1) +/* Pad Control Register */ +#define HDCS_PCTRL (0x03 << 1) +/* Pad Drive Control Register */ +#define HDCS_PDRV (0x04 << 1) +/* Interface Control Register */ +#define HDCS_ICTRL (0x05 << 1) +/* Interface Timing Register */ +#define HDCS_ITMG (0x06 << 1) +/* Baud Fraction Register */ +#define HDCS_BFRAC (0x07 << 1) +/* Baud Rate Register */ +#define HDCS_BRATE (0x08 << 1) +/* ADC Control Register */ +#define HDCS_ADCCTRL (0x09 << 1) +/* First Window Row Register */ +#define HDCS_FWROW (0x0a << 1) +/* First Window Column Register */ +#define HDCS_FWCOL (0x0b << 1) +/* Last Window Row Register */ +#define HDCS_LWROW (0x0c << 1) +/* Last Window Column Register */ +#define HDCS_LWCOL (0x0d << 1) +/* Timing Control Register */ +#define HDCS_TCTRL (0x0e << 1) +/* PGA Gain Register: Even Row, Even Column */ +#define HDCS_ERECPGA (0x0f << 1) +/* PGA Gain Register: Even Row, Odd Column */ +#define HDCS_EROCPGA (0x10 << 1) +/* PGA Gain Register: Odd Row, Even Column */ +#define HDCS_ORECPGA (0x11 << 1) +/* PGA Gain Register: Odd Row, Odd Column */ +#define HDCS_OROCPGA (0x12 << 1) +/* Row Exposure Low Register */ +#define HDCS_ROWEXPL (0x13 << 1) +/* Row Exposure High Register */ +#define HDCS_ROWEXPH (0x14 << 1) + +/* I2C Registers only for HDCS-1000/1100 */ +/* Sub-Row Exposure Low Register */ +#define HDCS00_SROWEXPL (0x15 << 1) +/* Sub-Row Exposure High Register */ +#define HDCS00_SROWEXPH (0x16 << 1) +/* Configuration Register */ +#define HDCS00_CONFIG (0x17 << 1) +/* Control Register */ +#define HDCS00_CONTROL (0x18 << 1) + +/* I2C Registers only for HDCS-1020 */ +/* Sub-Row Exposure Register */ +#define HDCS20_SROWEXP (0x15 << 1) +/* Error Control Register */ +#define HDCS20_ERROR (0x16 << 1) +/* Interface Timing 2 Register */ +#define HDCS20_ITMG2 (0x17 << 1) +/* Interface Control 2 Register */ +#define HDCS20_ICTRL2 (0x18 << 1) +/* Horizontal Blank Register */ +#define HDCS20_HBLANK (0x19 << 1) +/* Vertical Blank Register */ +#define HDCS20_VBLANK (0x1a << 1) +/* Configuration Register */ +#define HDCS20_CONFIG (0x1b << 1) +/* Control Register */ +#define HDCS20_CONTROL (0x1c << 1) + +#define HDCS_RUN_ENABLE (1 << 2) +#define HDCS_SLEEP_MODE (1 << 1) + +#define HDCS_DEFAULT_EXPOSURE 5000 +#define HDCS_DEFAULT_GAIN 128 + +static int hdcs_probe_1x00(struct sd *sd); +static int hdcs_probe_1020(struct sd *sd); +static int hdcs_start(struct sd *sd); +static int hdcs_init(struct sd *sd); +static int hdcs_stop(struct sd *sd); +static int hdcs_dump(struct sd *sd); +static void hdcs_disconnect(struct sd *sd); + +static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val); + +const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = { + .name = "HP HDCS-1000/1100", + .i2c_flush = 0, + .i2c_addr = (0x55 << 1), + .i2c_len = 1, + + .init = hdcs_init, + .probe = hdcs_probe_1x00, + .start = hdcs_start, + .stop = hdcs_stop, + .disconnect = hdcs_disconnect, + .dump = hdcs_dump, + + .nctrls = 2, + .ctrls = { + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0xffff, + .step = 0x1, + .default_value = HDCS_DEFAULT_EXPOSURE, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = hdcs_set_exposure, + .get = hdcs_get_exposure + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = HDCS_DEFAULT_GAIN, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = hdcs_set_gain, + .get = hdcs_get_gain + } + }, + + .nmodes = 1, + .modes = { + { + HDCS_1X00_DEF_WIDTH, + HDCS_1X00_DEF_HEIGHT, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT, + .bytesperline = HDCS_1X00_DEF_WIDTH, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + } + } +}; + +const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = { + .name = "HDCS-1020", + .i2c_flush = 0, + .i2c_addr = (0x55 << 1), + .i2c_len = 1, + + .nctrls = 0, + .ctrls = {}, + + .init = hdcs_init, + .probe = hdcs_probe_1020, + .start = hdcs_start, + .stop = hdcs_stop, + .dump = hdcs_dump, + + .nmodes = 1, + .modes = { + { + HDCS_1020_DEF_WIDTH, + HDCS_1020_DEF_HEIGHT, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT, + .bytesperline = HDCS_1020_DEF_WIDTH, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + } + } +}; + +static const u16 stv_bridge_init[][2] = { + {STV_ISO_ENABLE, 0}, + {STV_REG23, 0}, + {STV_REG00, 0x1d}, + {STV_REG01, 0xb5}, + {STV_REG02, 0xa8}, + {STV_REG03, 0x95}, + {STV_REG04, 0x07}, + + {STV_SCAN_RATE, 0x20}, + {STV_ISO_SIZE_L, 847}, + {STV_Y_CTRL, 0x01}, + {STV_X_CTRL, 0x0a} +}; + +static const u8 stv_sensor_init[][2] = { + /* Clear status (writing 1 will clear the corresponding status bit) */ + {HDCS_STATUS, BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1)}, + /* Disable all interrupts */ + {HDCS_IMASK, 0x00}, + {HDCS_PCTRL, BIT(6) | BIT(5) | BIT(1) | BIT(0)}, + {HDCS_PDRV, 0x00}, + {HDCS_ICTRL, BIT(5)}, + {HDCS_ITMG, BIT(4) | BIT(1)}, + /* ADC output resolution to 10 bits */ + {HDCS_ADCCTRL, 10} +}; + +#endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c new file mode 100644 index 0000000..d0a0f85 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +/* + * The spec file for the PB-0100 suggests the following for best quality + * images after the sensor has been reset : + * + * PB_ADCGAINL = R60 = 0x03 (3 dec) : sets low reference of ADC + to produce good black level + * PB_PREADCTRL = R32 = 0x1400 (5120 dec) : Enables global gain changes + through R53 + * PB_ADCMINGAIN = R52 = 0x10 (16 dec) : Sets the minimum gain for + auto-exposure + * PB_ADCGLOBALGAIN = R53 = 0x10 (16 dec) : Sets the global gain + * PB_EXPGAIN = R14 = 0x11 (17 dec) : Sets the auto-exposure value + * PB_UPDATEINT = R23 = 0x02 (2 dec) : Sets the speed on + auto-exposure routine + * PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate + */ + +#include "stv06xx_pb0100.h" + +static int pb0100_probe(struct sd *sd) +{ + u16 sensor; + int i, err; + s32 *sensor_settings; + + err = stv06xx_read_sensor(sd, PB_IDENT, &sensor); + + if (err < 0) + return -ENODEV; + + if ((sensor >> 8) == 0x64) { + sensor_settings = kmalloc( + stv06xx_sensor_pb0100.nctrls * sizeof(s32), + GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + info("Photobit pb0100 sensor detected"); + + sd->gspca_dev.cam.cam_mode = stv06xx_sensor_pb0100.modes; + sd->gspca_dev.cam.nmodes = stv06xx_sensor_pb0100.nmodes; + sd->desc.ctrls = stv06xx_sensor_pb0100.ctrls; + sd->desc.nctrls = stv06xx_sensor_pb0100.nctrls; + for (i = 0; i < stv06xx_sensor_pb0100.nctrls; i++) + sensor_settings[i] = stv06xx_sensor_pb0100. + ctrls[i].qctrl.default_value; + sd->sensor_priv = sensor_settings; + + return 0; + } + + return -ENODEV; +} + +static int pb0100_start(struct sd *sd) +{ + int err; + struct cam *cam = &sd->gspca_dev.cam; + s32 *sensor_settings = sd->sensor_priv; + u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv; + + /* Setup sensor window */ + if (mode & PB0100_CROP_TO_VGA) { + stv06xx_write_sensor(sd, PB_RSTART, 30); + stv06xx_write_sensor(sd, PB_CSTART, 20); + stv06xx_write_sensor(sd, PB_RWSIZE, 240 - 1); + stv06xx_write_sensor(sd, PB_CWSIZE, 320 - 1); + } else { + stv06xx_write_sensor(sd, PB_RSTART, 8); + stv06xx_write_sensor(sd, PB_CSTART, 4); + stv06xx_write_sensor(sd, PB_RWSIZE, 288 - 1); + stv06xx_write_sensor(sd, PB_CWSIZE, 352 - 1); + } + + if (mode & PB0100_SUBSAMPLE) { + stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); /* Wrong, FIXME */ + stv06xx_write_bridge(sd, STV_X_CTRL, 0x06); + + stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10); + } else { + stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01); + stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a); + /* larger -> slower */ + stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20); + } + + /* set_gain also sets red and blue balance */ + pb0100_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); + pb0100_set_exposure(&sd->gspca_dev, sensor_settings[EXPOSURE_IDX]); + pb0100_set_autogain_target(&sd->gspca_dev, + sensor_settings[AUTOGAIN_TARGET_IDX]); + pb0100_set_autogain(&sd->gspca_dev, sensor_settings[AUTOGAIN_IDX]); + + err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1)); + PDEBUG(D_STREAM, "Started stream, status: %d", err); + + return (err < 0) ? err : 0; +} + +static int pb0100_stop(struct sd *sd) +{ + int err; + + err = stv06xx_write_sensor(sd, PB_ABORTFRAME, 1); + + if (err < 0) + goto out; + + /* Set bit 1 to zero */ + err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)); + + PDEBUG(D_STREAM, "Halting stream"); +out: + return (err < 0) ? err : 0; +} + +/* FIXME: Sort the init commands out and put them into tables, + this is only for getting the camera to work */ +/* FIXME: No error handling for now, + add this once the init has been converted to proper tables */ +static int pb0100_init(struct sd *sd) +{ + stv06xx_write_bridge(sd, STV_REG00, 1); + stv06xx_write_bridge(sd, STV_SCAN_RATE, 0); + + /* Reset sensor */ + stv06xx_write_sensor(sd, PB_RESET, 1); + stv06xx_write_sensor(sd, PB_RESET, 0); + + /* Disable chip */ + stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)); + + /* Gain stuff...*/ + stv06xx_write_sensor(sd, PB_PREADCTRL, BIT(12)|BIT(10)|BIT(6)); + stv06xx_write_sensor(sd, PB_ADCGLOBALGAIN, 12); + + /* Set up auto-exposure */ + /* ADC VREF_HI new setting for a transition + from the Expose1 to the Expose2 setting */ + stv06xx_write_sensor(sd, PB_R28, 12); + /* gain max for autoexposure */ + stv06xx_write_sensor(sd, PB_ADCMAXGAIN, 180); + /* gain min for autoexposure */ + stv06xx_write_sensor(sd, PB_ADCMINGAIN, 12); + /* Maximum frame integration time (programmed into R8) + allowed for auto-exposure routine */ + stv06xx_write_sensor(sd, PB_R54, 3); + /* Minimum frame integration time (programmed into R8) + allowed for auto-exposure routine */ + stv06xx_write_sensor(sd, PB_R55, 0); + stv06xx_write_sensor(sd, PB_UPDATEINT, 1); + /* R15 Expose0 (maximum that auto-exposure may use) */ + stv06xx_write_sensor(sd, PB_R15, 800); + /* R17 Expose2 (minimum that auto-exposure may use) */ + stv06xx_write_sensor(sd, PB_R17, 10); + + stv06xx_write_sensor(sd, PB_EXPGAIN, 0); + + /* 0x14 */ + stv06xx_write_sensor(sd, PB_VOFFSET, 0); + /* 0x0D */ + stv06xx_write_sensor(sd, PB_ADCGAINH, 11); + /* Set black level (important!) */ + stv06xx_write_sensor(sd, PB_ADCGAINL, 0); + + /* ??? */ + stv06xx_write_bridge(sd, STV_REG00, 0x11); + stv06xx_write_bridge(sd, STV_REG03, 0x45); + stv06xx_write_bridge(sd, STV_REG04, 0x07); + + /* ISO-Size (0x27b: 635... why? - HDCS uses 847) */ + stv06xx_write_bridge(sd, STV_ISO_SIZE_L, 847); + + /* Scan/timing for the sensor */ + stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1)); + stv06xx_write_sensor(sd, PB_CFILLIN, 14); + stv06xx_write_sensor(sd, PB_VBL, 0); + stv06xx_write_sensor(sd, PB_FINTTIME, 0); + stv06xx_write_sensor(sd, PB_RINTTIME, 123); + + stv06xx_write_bridge(sd, STV_REG01, 0xc2); + stv06xx_write_bridge(sd, STV_REG02, 0xb0); + return 0; +} + +static int pb0100_dump(struct sd *sd) +{ + return 0; +} + +static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[GAIN_IDX]; + + return 0; +} + +static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + if (sensor_settings[AUTOGAIN_IDX]) + return -EBUSY; + + sensor_settings[GAIN_IDX] = val; + err = stv06xx_write_sensor(sd, PB_G1GAIN, val); + if (!err) + err = stv06xx_write_sensor(sd, PB_G2GAIN, val); + PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err); + + if (!err) + err = pb0100_set_red_balance(gspca_dev, + sensor_settings[RED_BALANCE_IDX]); + if (!err) + err = pb0100_set_blue_balance(gspca_dev, + sensor_settings[BLUE_BALANCE_IDX]); + + return err; +} + +static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[RED_BALANCE_IDX]; + + return 0; +} + +static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + if (sensor_settings[AUTOGAIN_IDX]) + return -EBUSY; + + sensor_settings[RED_BALANCE_IDX] = val; + val += sensor_settings[GAIN_IDX]; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + + err = stv06xx_write_sensor(sd, PB_RGAIN, val); + PDEBUG(D_V4L2, "Set red gain to %d, status: %d", val, err); + + return err; +} + +static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[BLUE_BALANCE_IDX]; + + return 0; +} + +static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + if (sensor_settings[AUTOGAIN_IDX]) + return -EBUSY; + + sensor_settings[BLUE_BALANCE_IDX] = val; + val += sensor_settings[GAIN_IDX]; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + + err = stv06xx_write_sensor(sd, PB_BGAIN, val); + PDEBUG(D_V4L2, "Set blue gain to %d, status: %d", val, err); + + return err; +} + +static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[EXPOSURE_IDX]; + + return 0; +} + +static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + if (sensor_settings[AUTOGAIN_IDX]) + return -EBUSY; + + sensor_settings[EXPOSURE_IDX] = val; + err = stv06xx_write_sensor(sd, PB_RINTTIME, val); + PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err); + + return err; +} + +static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTOGAIN_IDX]; + + return 0; +} + +static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[AUTOGAIN_IDX] = val; + if (sensor_settings[AUTOGAIN_IDX]) { + if (sensor_settings[NATURAL_IDX]) + val = BIT(6)|BIT(4)|BIT(0); + else + val = BIT(4)|BIT(0); + } else + val = 0; + + err = stv06xx_write_sensor(sd, PB_EXPGAIN, val); + PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d", + sensor_settings[AUTOGAIN_IDX], sensor_settings[NATURAL_IDX], + err); + + return err; +} + +static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTOGAIN_TARGET_IDX]; + + return 0; +} + +static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val) +{ + int err, totalpixels, brightpixels, darkpixels; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[AUTOGAIN_TARGET_IDX] = val; + + /* Number of pixels counted by the sensor when subsampling the pixels. + * Slightly larger than the real value to avoid oscillation */ + totalpixels = gspca_dev->width * gspca_dev->height; + totalpixels = totalpixels/(8*8) + totalpixels/(64*64); + + brightpixels = (totalpixels * val) >> 8; + darkpixels = totalpixels - brightpixels; + err = stv06xx_write_sensor(sd, PB_R21, brightpixels); + if (!err) + err = stv06xx_write_sensor(sd, PB_R22, darkpixels); + + PDEBUG(D_V4L2, "Set autogain target to %d, status: %d", val, err); + + return err; +} + +static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[NATURAL_IDX]; + + return 0; +} + +static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[NATURAL_IDX] = val; + + return pb0100_set_autogain(gspca_dev, sensor_settings[AUTOGAIN_IDX]); +} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h new file mode 100644 index 0000000..5ea21a1 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#ifndef STV06XX_PB0100_H_ +#define STV06XX_PB0100_H_ + +#include "stv06xx_sensor.h" + +/* mode priv field flags */ +#define PB0100_CROP_TO_VGA 0x01 +#define PB0100_SUBSAMPLE 0x02 + +/* I2C Registers */ +#define PB_IDENT 0x00 /* Chip Version */ +#define PB_RSTART 0x01 /* Row Window Start */ +#define PB_CSTART 0x02 /* Column Window Start */ +#define PB_RWSIZE 0x03 /* Row Window Size */ +#define PB_CWSIZE 0x04 /* Column Window Size */ +#define PB_CFILLIN 0x05 /* Column Fill-In */ +#define PB_VBL 0x06 /* Vertical Blank Count */ +#define PB_CONTROL 0x07 /* Control Mode */ +#define PB_FINTTIME 0x08 /* Integration Time/Frame Unit Count */ +#define PB_RINTTIME 0x09 /* Integration Time/Row Unit Count */ +#define PB_ROWSPEED 0x0a /* Row Speed Control */ +#define PB_ABORTFRAME 0x0b /* Abort Frame */ +#define PB_R12 0x0c /* Reserved */ +#define PB_RESET 0x0d /* Reset */ +#define PB_EXPGAIN 0x0e /* Exposure Gain Command */ +#define PB_R15 0x0f /* Expose0 */ +#define PB_R16 0x10 /* Expose1 */ +#define PB_R17 0x11 /* Expose2 */ +#define PB_R18 0x12 /* Low0_DAC */ +#define PB_R19 0x13 /* Low1_DAC */ +#define PB_R20 0x14 /* Low2_DAC */ +#define PB_R21 0x15 /* Threshold11 */ +#define PB_R22 0x16 /* Threshold0x */ +#define PB_UPDATEINT 0x17 /* Update Interval */ +#define PB_R24 0x18 /* High_DAC */ +#define PB_R25 0x19 /* Trans0H */ +#define PB_R26 0x1a /* Trans1L */ +#define PB_R27 0x1b /* Trans1H */ +#define PB_R28 0x1c /* Trans2L */ +#define PB_R29 0x1d /* Reserved */ +#define PB_R30 0x1e /* Reserved */ +#define PB_R31 0x1f /* Wait to Read */ +#define PB_PREADCTRL 0x20 /* Pixel Read Control Mode */ +#define PB_R33 0x21 /* IREF_VLN */ +#define PB_R34 0x22 /* IREF_VLP */ +#define PB_R35 0x23 /* IREF_VLN_INTEG */ +#define PB_R36 0x24 /* IREF_MASTER */ +#define PB_R37 0x25 /* IDACP */ +#define PB_R38 0x26 /* IDACN */ +#define PB_R39 0x27 /* DAC_Control_Reg */ +#define PB_R40 0x28 /* VCL */ +#define PB_R41 0x29 /* IREF_VLN_ADCIN */ +#define PB_R42 0x2a /* Reserved */ +#define PB_G1GAIN 0x2b /* Green 1 Gain */ +#define PB_BGAIN 0x2c /* Blue Gain */ +#define PB_RGAIN 0x2d /* Red Gain */ +#define PB_G2GAIN 0x2e /* Green 2 Gain */ +#define PB_R47 0x2f /* Dark Row Address */ +#define PB_R48 0x30 /* Dark Row Options */ +#define PB_R49 0x31 /* Reserved */ +#define PB_R50 0x32 /* Image Test Data */ +#define PB_ADCMAXGAIN 0x33 /* Maximum Gain */ +#define PB_ADCMINGAIN 0x34 /* Minimum Gain */ +#define PB_ADCGLOBALGAIN 0x35 /* Global Gain */ +#define PB_R54 0x36 /* Maximum Frame */ +#define PB_R55 0x37 /* Minimum Frame */ +#define PB_R56 0x38 /* Reserved */ +#define PB_VOFFSET 0x39 /* VOFFSET */ +#define PB_R58 0x3a /* Snap-Shot Sequence Trigger */ +#define PB_ADCGAINH 0x3b /* VREF_HI */ +#define PB_ADCGAINL 0x3c /* VREF_LO */ +#define PB_R61 0x3d /* Reserved */ +#define PB_R62 0x3e /* Reserved */ +#define PB_R63 0x3f /* Reserved */ +#define PB_R64 0x40 /* Red/Blue Gain */ +#define PB_R65 0x41 /* Green 2/Green 1 Gain */ +#define PB_R66 0x42 /* VREF_HI/LO */ +#define PB_R67 0x43 /* Integration Time/Row Unit Count */ +#define PB_R240 0xf0 /* ADC Test */ +#define PB_R241 0xf1 /* Chip Enable */ +#define PB_R242 0xf2 /* Reserved */ + +static int pb0100_probe(struct sd *sd); +static int pb0100_start(struct sd *sd); +static int pb0100_init(struct sd *sd); +static int pb0100_stop(struct sd *sd); +static int pb0100_dump(struct sd *sd); + +/* V4L2 controls supported by the driver */ +static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); +static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); +static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val); +static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val); +static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val); +static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val); +static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val); +static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val); + +const struct stv06xx_sensor stv06xx_sensor_pb0100 = { + .name = "PB-0100", + .i2c_flush = 1, + .i2c_addr = 0xba, + .i2c_len = 2, + + .nctrls = 7, + .ctrls = { +#define GAIN_IDX 0 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 128 + }, + .set = pb0100_set_gain, + .get = pb0100_get_gain + }, +#define RED_BALANCE_IDX 1 + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = -255, + .maximum = 255, + .step = 1, + .default_value = 0 + }, + .set = pb0100_set_red_balance, + .get = pb0100_get_red_balance + }, +#define BLUE_BALANCE_IDX 2 + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = -255, + .maximum = 255, + .step = 1, + .default_value = 0 + }, + .set = pb0100_set_blue_balance, + .get = pb0100_get_blue_balance + }, +#define EXPOSURE_IDX 3 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 511, + .step = 1, + .default_value = 12 + }, + .set = pb0100_set_exposure, + .get = pb0100_get_exposure + }, +#define AUTOGAIN_IDX 4 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Automatic Gain and Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = pb0100_set_autogain, + .get = pb0100_get_autogain + }, +#define AUTOGAIN_TARGET_IDX 5 + { + { + .id = V4L2_CTRL_CLASS_USER + 0x1000, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Automatic Gain Target", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 128 + }, + .set = pb0100_set_autogain_target, + .get = pb0100_get_autogain_target + }, +#define NATURAL_IDX 6 + { + { + .id = V4L2_CTRL_CLASS_USER + 0x1001, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Natural Light Source", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = pb0100_set_natural, + .get = pb0100_get_natural + }, + }, + + .init = pb0100_init, + .probe = pb0100_probe, + .start = pb0100_start, + .stop = pb0100_stop, + .dump = pb0100_dump, + + .nmodes = 2, + .modes = { +/* low res / subsample modes disabled as they are only half res horizontal, + halving the vertical resolution does not seem to work */ + { + 320, + 240, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 320 * 240, + .bytesperline = 320, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = PB0100_CROP_TO_VGA + }, + { + 352, + 288, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 352 * 288, + .bytesperline = 352, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + } +}; + +#endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h new file mode 100644 index 0000000..c726dac --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#ifndef STV06XX_SENSOR_H_ +#define STV06XX_SENSOR_H_ + +#include "stv06xx.h" + +#define IS_850(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x850) +#define IS_870(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x870) +#define IS_1020(sd) ((sd)->sensor == &stv06xx_sensor_hdcs1020) + +extern const struct stv06xx_sensor stv06xx_sensor_vv6410; +extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00; +extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020; +extern const struct stv06xx_sensor stv06xx_sensor_pb0100; + +#define STV06XX_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10) + +struct stv06xx_sensor { + /* Defines the name of a sensor */ + char name[32]; + + /* Sensor i2c address */ + u8 i2c_addr; + + /* Flush value*/ + u8 i2c_flush; + + /* length of an i2c word */ + u8 i2c_len; + + /* Probes if the sensor is connected */ + int (*probe)(struct sd *sd); + + /* Performs a initialization sequence */ + int (*init)(struct sd *sd); + + /* Executed at device disconnect */ + void (*disconnect)(struct sd *sd); + + /* Reads a sensor register */ + int (*read_sensor)(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + + /* Writes to a sensor register */ + int (*write_sensor)(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + + /* Instructs the sensor to start streaming */ + int (*start)(struct sd *sd); + + /* Instructs the sensor to stop streaming */ + int (*stop)(struct sd *sd); + + /* Instructs the sensor to dump all its contents */ + int (*dump)(struct sd *sd); + + int nctrls; + struct ctrl ctrls[STV06XX_MAX_CTRLS]; + + char nmodes; + struct v4l2_pix_format modes[]; +}; + +#endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c new file mode 100644 index 0000000..1ca91f2 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#include "stv06xx_vv6410.h" + +static int vv6410_probe(struct sd *sd) +{ + u16 data; + int err; + + err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data); + + if (err < 0) + return -ENODEV; + + if (data == 0x19) { + info("vv6410 sensor detected"); + + sd->gspca_dev.cam.cam_mode = stv06xx_sensor_vv6410.modes; + sd->gspca_dev.cam.nmodes = stv06xx_sensor_vv6410.nmodes; + sd->desc.ctrls = stv06xx_sensor_vv6410.ctrls; + sd->desc.nctrls = stv06xx_sensor_vv6410.nctrls; + return 0; + } + + return -ENODEV; +} + +static int vv6410_init(struct sd *sd) +{ + int err = 0, i; + + for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) { + /* if NULL then len contains single value */ + if (stv_bridge_init[i].data == NULL) { + err = stv06xx_write_bridge(sd, + stv_bridge_init[i].start, + stv_bridge_init[i].len); + } else { + int j; + for (j = 0; j < stv_bridge_init[i].len; j++) + err = stv06xx_write_bridge(sd, + stv_bridge_init[i].start + j, + stv_bridge_init[i].data[j]); + } + } + + if (err < 0) + return err; + + err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init, + ARRAY_SIZE(vv6410_sensor_init)); + + return (err < 0) ? err : 0; +} + +static int vv6410_start(struct sd *sd) +{ + int err; + struct cam *cam = &sd->gspca_dev.cam; + u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv; + + if (priv & VV6410_CROP_TO_QVGA) { + PDEBUG(D_CONF, "Cropping to QVGA"); + stv06xx_write_sensor(sd, VV6410_XENDH, 320 - 1); + stv06xx_write_sensor(sd, VV6410_YENDH, 240 - 1); + } else { + stv06xx_write_sensor(sd, VV6410_XENDH, 360 - 1); + stv06xx_write_sensor(sd, VV6410_YENDH, 294 - 1); + } + + if (priv & VV6410_SUBSAMPLE) { + PDEBUG(D_CONF, "Enabling subsampling"); + stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); + stv06xx_write_bridge(sd, STV_X_CTRL, 0x06); + + stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10); + } else { + stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01); + stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a); + + stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20); + } + + /* Turn on LED */ + err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON); + if (err < 0) + return err; + + err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0); + if (err < 0) + return err; + + PDEBUG(D_STREAM, "Starting stream"); + + return 0; +} + +static int vv6410_stop(struct sd *sd) +{ + int err; + + /* Turn off LED */ + err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF); + if (err < 0) + return err; + + err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE); + if (err < 0) + return err; + + PDEBUG(D_STREAM, "Halting stream"); + + return (err < 0) ? err : 0; +} + +static int vv6410_dump(struct sd *sd) +{ + u8 i; + int err = 0; + + info("Dumping all vv6410 sensor registers"); + for (i = 0; i < 0xff && !err; i++) { + u16 data; + err = stv06xx_read_sensor(sd, i, &data); + info("Register 0x%x contained 0x%x", i, data); + } + return (err < 0) ? err : 0; +} + +static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u16 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); + + *val = (i2c_data & VV6410_HFLIP) ? 1 : 0; + + PDEBUG(D_V4L2, "Read horizontal flip %d", *val); + + return (err < 0) ? err : 0; +} + +static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u16 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); + if (err < 0) + return err; + + if (val) + i2c_data |= VV6410_HFLIP; + else + i2c_data &= ~VV6410_HFLIP; + + PDEBUG(D_V4L2, "Set horizontal flip to %d", val); + err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data); + + return (err < 0) ? err : 0; +} + +static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u16 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); + + *val = (i2c_data & VV6410_VFLIP) ? 1 : 0; + + PDEBUG(D_V4L2, "Read vertical flip %d", *val); + + return (err < 0) ? err : 0; +} + +static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u16 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); + if (err < 0) + return err; + + if (val) + i2c_data |= VV6410_VFLIP; + else + i2c_data &= ~VV6410_VFLIP; + + PDEBUG(D_V4L2, "Set vertical flip to %d", val); + err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data); + + return (err < 0) ? err : 0; +} + +static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u16 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + err = stv06xx_read_sensor(sd, VV6410_ANALOGGAIN, &i2c_data); + + *val = i2c_data & 0xf; + + PDEBUG(D_V4L2, "Read analog gain %d", *val); + + return (err < 0) ? err : 0; +} + +static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(D_V4L2, "Set analog gain to %d", val); + err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf)); + + return (err < 0) ? err : 0; +} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h new file mode 100644 index 0000000..3ff8c4e --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * Copyright (c) 2008 Erik Andrén + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * P/N 861037: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 + * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express + * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam + * P/N 861075-0040: Sensor HDCS1000 ASIC + * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB + * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web + */ + +#ifndef STV06XX_VV6410_H_ +#define STV06XX_VV6410_H_ + +#include "stv06xx_sensor.h" + +#define VV6410_COLS 416 +#define VV6410_ROWS 320 + +/* Status registers */ +/* Chip identification number including revision indicator */ +#define VV6410_DEVICEH 0x00 +#define VV6410_DEVICEL 0x01 + +/* User can determine whether timed I2C data + has been consumed by interrogating flag states */ +#define VV6410_STATUS0 0x02 + +/* Current line counter value */ +#define VV6410_LINECOUNTH 0x03 +#define VV6410_LINECOUNTL 0x04 + +/* End x coordinate of image size */ +#define VV6410_XENDH 0x05 +#define VV6410_XENDL 0x06 + +/* End y coordinate of image size */ +#define VV6410_YENDH 0x07 +#define VV6410_YENDL 0x08 + +/* This is the average pixel value returned from the + dark line offset cancellation algorithm */ +#define VV6410_DARKAVGH 0x09 +#define VV6410_DARKAVGL 0x0a + +/* This is the average pixel value returned from the + black line offset cancellation algorithm */ +#define VV6410_BLACKAVGH 0x0b +#define VV6410_BLACKAVGL 0x0c + +/* Flags to indicate whether the x or y image coordinates have been clipped */ +#define VV6410_STATUS1 0x0d + +/* Setup registers */ + +/* Low-power/sleep modes & video timing */ +#define VV6410_SETUP0 0x10 + +/* Various parameters */ +#define VV6410_SETUP1 0x11 + +/* Contains pixel counter reset value used by external sync */ +#define VV6410_SYNCVALUE 0x12 + +/* Frame grabbing modes (FST, LST and QCK) */ +#define VV6410_FGMODES 0x14 + +/* FST and QCK mapping modes. */ +#define VV6410_PINMAPPING 0x15 + +/* Data resolution */ +#define VV6410_DATAFORMAT 0x16 + +/* Output coding formats */ +#define VV6410_OPFORMAT 0x17 + +/* Various mode select bits */ +#define VV6410_MODESELECT 0x18 + +/* Exposure registers */ +/* Fine exposure. */ +#define VV6410_FINEH 0x20 +#define VV6410_FINEL 0x21 + +/* Coarse exposure */ +#define VV6410_COARSEH 0x22 +#define VV6410_COARSEL 0x23 + +/* Analog gain setting */ +#define VV6410_ANALOGGAIN 0x24 + +/* Clock division */ +#define VV6410_CLKDIV 0x25 + +/* Dark line offset cancellation value */ +#define VV6410_DARKOFFSETH 0x2c +#define VV6410_DARKOFFSETL 0x2d + +/* Dark line offset cancellation enable */ +#define VV6410_DARKOFFSETSETUP 0x2e + +/* Video timing registers */ +/* Line Length (Pixel Clocks) */ +#define VV6410_LINELENGTHH 0x52 +#define VV6410_LINELENGTHL 0x53 + +/* X-co-ordinate of top left corner of region of interest (x-offset) */ +#define VV6410_XOFFSETH 0x57 +#define VV6410_XOFFSETL 0x58 + +/* Y-coordinate of top left corner of region of interest (y-offset) */ +#define VV6410_YOFFSETH 0x59 +#define VV6410_YOFFSETL 0x5a + +/* Field length (Lines) */ +#define VV6410_FIELDLENGTHH 0x61 +#define VV6410_FIELDLENGTHL 0x62 + +/* System registers */ +/* Black offset cancellation default value */ +#define VV6410_BLACKOFFSETH 0x70 +#define VV6410_BLACKOFFSETL 0x71 + +/* Black offset cancellation setup */ +#define VV6410_BLACKOFFSETSETUP 0x72 + +/* Analog Control Register 0 */ +#define VV6410_CR0 0x75 + +/* Analog Control Register 1 */ +#define VV6410_CR1 0x76 + +/* ADC Setup Register */ +#define VV6410_AS0 0x77 + +/* Analog Test Register */ +#define VV6410_AT0 0x78 + +/* Audio Amplifier Setup Register */ +#define VV6410_AT1 0x79 + +#define VV6410_HFLIP (1 << 3) +#define VV6410_VFLIP (1 << 4) + +#define VV6410_LOW_POWER_MODE (1 << 0) +#define VV6410_SOFT_RESET (1 << 2) +#define VV6410_PAL_25_FPS (0 << 3) + +#define VV6410_CLK_DIV_2 (1 << 1) + +#define VV6410_FINE_EXPOSURE 320 +#define VV6410_COARSE_EXPOSURE 192 +#define VV6410_DEFAULT_GAIN 5 + +#define VV6410_SUBSAMPLE 0x01 +#define VV6410_CROP_TO_QVGA 0x02 + +static int vv6410_probe(struct sd *sd); +static int vv6410_start(struct sd *sd); +static int vv6410_init(struct sd *sd); +static int vv6410_stop(struct sd *sd); +static int vv6410_dump(struct sd *sd); + +/* V4L2 controls supported by the driver */ +static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val); + +const struct stv06xx_sensor stv06xx_sensor_vv6410 = { + .name = "ST VV6410", + .i2c_flush = 5, + .i2c_addr = 0x20, + .i2c_len = 1, + .init = vv6410_init, + .probe = vv6410_probe, + .start = vv6410_start, + .stop = vv6410_stop, + .dump = vv6410_dump, + + .nctrls = 3, + .ctrls = { + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = vv6410_set_hflip, + .get = vv6410_get_hflip + }, { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = vv6410_set_vflip, + .get = vv6410_get_vflip + }, { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "analog gain", + .minimum = 0, + .maximum = 15, + .step = 1, + .default_value = 0 + }, + .set = vv6410_set_analog_gain, + .get = vv6410_get_analog_gain + } + }, + + .nmodes = 1, + .modes = { + { + 356, + 292, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = + 356 * 292, + .bytesperline = 356, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + } + } +}; + +/* If NULL, only single value to write, stored in len */ +struct stv_init { + const u8 *data; + u16 start; + u8 len; +}; + +static const u8 x1500[] = { /* 0x1500 - 0x150f */ + 0x0b, 0xa7, 0xb7, 0x00, 0x00 +}; + +static const u8 x1536[] = { /* 0x1536 - 0x153b */ + 0x02, 0x00, 0x60, 0x01, 0x20, 0x01 +}; + +static const u8 x15c1[] = { /* 0x15c1 - 0x15c2 */ + 0xff, 0x03 /* Output word 0x03ff = 1023 (ISO size) */ +}; + +static const struct stv_init stv_bridge_init[] = { + /* This reg is written twice. Some kind of reset? */ + {NULL, 0x1620, 0x80}, + {NULL, 0x1620, 0x00}, + {NULL, 0x1423, 0x04}, + {x1500, 0x1500, ARRAY_SIZE(x1500)}, + {x1536, 0x1536, ARRAY_SIZE(x1536)}, + {x15c1, 0x15c1, ARRAY_SIZE(x15c1)} +}; + +static const u8 vv6410_sensor_init[][2] = { + /* Setup registers */ + {VV6410_SETUP0, VV6410_SOFT_RESET}, + {VV6410_SETUP0, VV6410_LOW_POWER_MODE}, + /* Use shuffled read-out mode */ + {VV6410_SETUP1, BIT(6)}, + /* All modes to 1 */ + {VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)}, + {VV6410_PINMAPPING, 0x00}, + /* Pre-clock generator divide off */ + {VV6410_DATAFORMAT, BIT(7) | BIT(0)}, + + /* Exposure registers */ + {VV6410_FINEH, VV6410_FINE_EXPOSURE >> 8}, + {VV6410_FINEL, VV6410_FINE_EXPOSURE & 0xff}, + {VV6410_COARSEH, VV6410_COARSE_EXPOSURE >> 8}, + {VV6410_COARSEL, VV6410_COARSE_EXPOSURE & 0xff}, + {VV6410_ANALOGGAIN, 0xf0 | VV6410_DEFAULT_GAIN}, + {VV6410_CLKDIV, VV6410_CLK_DIV_2}, + + /* System registers */ + /* Enable voltage doubler */ + {VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)}, + {VV6410_AT0, 0x00}, + /* Power up audio, differential */ + {VV6410_AT1, BIT(4)|BIT(0)}, +}; + +#endif diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index bd92886..6d904d5 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -123,7 +123,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, @@ -136,7 +136,7 @@ static struct v4l2_pix_format vga_mode[] = { .priv = 1}, }; -static struct v4l2_pix_format custom_mode[] = { +static const struct v4l2_pix_format custom_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, @@ -149,7 +149,7 @@ static struct v4l2_pix_format custom_mode[] = { .priv = 1}, }; -static struct v4l2_pix_format vga_mode2[] = { +static const struct v4l2_pix_format vga_mode2[] = { {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144 * 3 / 8 + 590, diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index eac245d..6ee111a 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -233,7 +233,7 @@ static char *effects_control[] = { "Negative", }; -static struct v4l2_pix_format vga_mode_t16[] = { +static const struct v4l2_pix_format vga_mode_t16[] = { {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120 * 4 / 8 + 590, @@ -499,7 +499,7 @@ static void om6802_sensor_init(struct gspca_dev *gspca_dev) reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset); msleep(5); i = 4; - while (--i < 0) { + while (--i > 0) { byte = reg_r(gspca_dev, 0x0060); if (!(byte & 0x01)) break; diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c index 968a591..94163cc 100644 --- a/drivers/media/video/gspca/tv8532.c +++ b/drivers/media/video/gspca/tv8532.c @@ -30,15 +30,10 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - int buflen; /* current length of tmpbuf */ - __u8 tmpbuf[352 * 288 + 10 * 288]; /* no protection... */ - __u8 tmpbuf2[352 * 288]; /* no protection... */ + __u16 brightness; + __u16 contrast; - unsigned short brightness; - unsigned short contrast; - - char packet; - char synchro; + __u8 packet; }; /* V4L2 controls supported by the driver */ @@ -78,7 +73,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static struct v4l2_pix_format sif_mode[] = { +static const struct v4l2_pix_format sif_mode[] = { {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144, @@ -392,6 +387,8 @@ static void setbrightness(struct gspca_dev *gspca_dev) /* -- start the camera -- */ static int sd_start(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32); reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00); tv_8532ReadRegisters(gspca_dev); @@ -443,6 +440,10 @@ static int sd_start(struct gspca_dev *gspca_dev) /************************************************/ tv_8532_PollReg(gspca_dev); reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */ + + gspca_dev->empty_packet = 0; /* check the empty packets */ + sd->packet = 0; /* ignore the first packets */ + return 0; } @@ -451,111 +452,36 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b); } -static void tv8532_preprocess(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; -/* we should received a whole frame with header and EOL marker - * in gspca_dev->tmpbuf and return a GBRG pattern in gspca_dev->tmpbuf2 - * sequence 2bytes header the Alternate pixels bayer GB 4 bytes - * Alternate pixels bayer RG 4 bytes EOL */ - int width = gspca_dev->width; - int height = gspca_dev->height; - unsigned char *dst = sd->tmpbuf2; - unsigned char *data = sd->tmpbuf; - int i; - - /* precompute where is the good bayer line */ - if (((data[3] + data[width + 7]) >> 1) - + (data[4] >> 2) - + (data[width + 6] >> 1) >= ((data[2] + data[width + 6]) >> 1) - + (data[3] >> 2) - + (data[width + 5] >> 1)) - data += 3; - else - data += 2; - for (i = 0; i < height / 2; i++) { - memcpy(dst, data, width); - data += width + 3; - dst += width; - memcpy(dst, data, width); - data += width + 7; - dst += width; - } -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; - - if (data[0] != 0x80) { - sd->packet++; - if (sd->buflen + len > sizeof sd->tmpbuf) { - if (gspca_dev->last_packet_type != DISCARD_PACKET) { - PDEBUG(D_PACK, "buffer overflow"); - gspca_dev->last_packet_type = DISCARD_PACKET; - } - return; - } - memcpy(&sd->tmpbuf[sd->buflen], data, len); - sd->buflen += len; - return; - } - - /* here we detect 0x80 */ - /* counter is limited so we need few header for a frame :) */ - - /* header 0x80 0x80 0x80 0x80 0x80 */ - /* packet 00 63 127 145 00 */ - /* sof 0 1 1 0 0 */ - - /* update sequence */ - if (sd->packet == 63 || sd->packet == 127) - sd->synchro = 1; - - /* is there a frame start ? */ - if (sd->packet >= (gspca_dev->height >> 1) - 1) { - PDEBUG(D_PACK, "SOF > %d packet %d", sd->synchro, - sd->packet); - if (!sd->synchro) { /* start of frame */ - if (gspca_dev->last_packet_type == FIRST_PACKET) { - tv8532_preprocess(gspca_dev); - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, sd->tmpbuf2, - gspca_dev->width * - gspca_dev->width); - } - gspca_frame_add(gspca_dev, FIRST_PACKET, - frame, data, 0); - memcpy(sd->tmpbuf, data, len); - sd->buflen = len; - sd->packet = 0; - return; - } - if (gspca_dev->last_packet_type != DISCARD_PACKET) { - PDEBUG(D_PACK, - "Warning wrong TV8532 frame detection %d", - sd->packet); - gspca_dev->last_packet_type = DISCARD_PACKET; - } - return; - } - - if (!sd->synchro) { - /* Drop packet frame corrupt */ - PDEBUG(D_PACK, "DROP SOF %d packet %d", - sd->synchro, sd->packet); - sd->packet = 0; - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - sd->synchro = 1; - sd->packet++; - memcpy(&sd->tmpbuf[sd->buflen], data, len); - sd->buflen += len; + int packet_type0, packet_type1; + + packet_type0 = packet_type1 = INTER_PACKET; + if (gspca_dev->empty_packet) { + gspca_dev->empty_packet = 0; + sd->packet = gspca_dev->height / 2; + packet_type0 = FIRST_PACKET; + } else if (sd->packet == 0) + return; /* 2 more lines in 352x288 ! */ + sd->packet--; + if (sd->packet == 0) + packet_type1 = LAST_PACKET; + + /* each packet contains: + * - header 2 bytes + * - RG line + * - 4 bytes + * - GB line + * - 4 bytes + */ + gspca_frame_add(gspca_dev, packet_type0, + frame, data + 2, gspca_dev->width); + gspca_frame_add(gspca_dev, packet_type1, + frame, data + gspca_dev->width + 6, gspca_dev->width); } static void setcontrast(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 17af353..0525ea5 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -32,44 +32,68 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char autogain; - unsigned char lightfreq; + __u8 hflip; + __u8 vflip; + __u8 lightfreq; + __u8 sharpness; - char qindex; char bridge; #define BRIDGE_VC0321 0 #define BRIDGE_VC0323 1 char sensor; #define SENSOR_HV7131R 0 -#define SENSOR_MI1320 1 -#define SENSOR_MI1310_SOC 2 -#define SENSOR_OV7660 3 -#define SENSOR_OV7670 4 -#define SENSOR_PO3130NC 5 +#define SENSOR_MI0360 1 +#define SENSOR_MI1320 2 +#define SENSOR_MI1310_SOC 3 +#define SENSOR_OV7660 4 +#define SENSOR_OV7670 5 +#define SENSOR_PO1200 6 +#define SENSOR_PO3130NC 7 }; /* V4L2 controls supported by the driver */ -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { +/* next 2 controls work with ov7660 and ov7670 only */ +#define HFLIP_IDX 0 { { - .id = V4L2_CID_AUTOGAIN, + .id = V4L2_CID_HFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain", + .name = "Mirror", .minimum = 0, .maximum = 1, .step = 1, -#define AUTOGAIN_DEF 1 - .default_value = AUTOGAIN_DEF, +#define HFLIP_DEF 0 + .default_value = HFLIP_DEF, }, - .set = sd_setautogain, - .get = sd_getautogain, + .set = sd_sethflip, + .get = sd_gethflip, }, -#define LIGHTFREQ_IDX 1 +#define VFLIP_IDX 1 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEF 0 + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, +#define LIGHTFREQ_IDX 2 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -84,9 +108,25 @@ static struct ctrl sd_ctrls[] = { .set = sd_setfreq, .get = sd_getfreq, }, +/* po1200 only */ +#define SHARPNESS_IDX 3 + { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 2, + .step = 1, +#define SHARPNESS_DEF 1 + .default_value = SHARPNESS_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, }; -static struct v4l2_pix_format vc0321_mode[] = { +static const struct v4l2_pix_format vc0321_mode[] = { {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 2, @@ -98,7 +138,7 @@ static struct v4l2_pix_format vc0321_mode[] = { .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; -static struct v4l2_pix_format vc0323_mode[] = { +static const struct v4l2_pix_format vc0323_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, @@ -111,6 +151,252 @@ static struct v4l2_pix_format vc0323_mode[] = { .priv = 0}, }; +static const struct v4l2_pix_format svga_mode[] = { + {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600 * 1 / 4 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/* OV7660/7670 registers */ +#define OV7660_REG_MVFP 0x1e +#define OV7660_MVFP_MIRROR 0x20 +#define OV7660_MVFP_VFLIP 0x10 + +static const __u8 mi0360_matrix[9] = { + 0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50 +}; + +static const __u8 mi0360_initVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xb8, 0x00, 0x13, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, + {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, + {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x50, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, + {0xb8, 0x08, 0xe0, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, + {0xb8, 0x14, 0x18, 0xcc}, + {0xb8, 0xb2, 0x0a, 0xcc}, + {0xb8, 0xb4, 0x0a, 0xcc}, + {0xb8, 0xb5, 0x0a, 0xcc}, + {0xb8, 0xfe, 0x00, 0xcc}, + {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, + {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, + {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, + {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, + {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0x31, 0x00, 0x00, 0xbb}, + {0x09, 0x01, 0xc7, 0xbb}, + {0x34, 0x01, 0x00, 0xbb}, + {0x2b, 0x00, 0x28, 0xbb}, + {0x2c, 0x00, 0x30, 0xbb}, + {0x2d, 0x00, 0x30, 0xbb}, + {0x2e, 0x00, 0x28, 0xbb}, + {0x62, 0x04, 0x11, 0xbb}, + {0x03, 0x01, 0xe0, 0xbb}, + {0x2c, 0x00, 0x2c, 0xbb}, + {0x20, 0xd0, 0x00, 0xbb}, + {0x01, 0x00, 0x08, 0xbb}, + {0x06, 0x00, 0x10, 0xbb}, + {0x05, 0x00, 0x20, 0xbb}, + {0x20, 0x00, 0x00, 0xbb}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, + {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, + {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, + {0xb6, 0x17, 0x58, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x10, 0xcc}, + {0xb9, 0x12, 0x00, 0xcc}, + {0xb9, 0x13, 0x0a, 0xcc}, + {0xb9, 0x14, 0x0a, 0xcc}, + {0xb9, 0x15, 0x0a, 0xcc}, + {0xb9, 0x16, 0x0a, 0xcc}, + {0xb9, 0x18, 0x00, 0xcc}, + {0xb9, 0x19, 0x0f, 0xcc}, + {0xb9, 0x1a, 0x0f, 0xcc}, + {0xb9, 0x1b, 0x0f, 0xcc}, + {0xb9, 0x1c, 0x0f, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb8, 0x0c, 0x20, 0xcc}, + {0xb8, 0x0d, 0x70, 0xcc}, + {0xb6, 0x13, 0x13, 0xcc}, + {0x35, 0x00, 0x60, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; +static const __u8 mi0360_initQVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xbc, 0x00, 0xd1, 0xcc}, + {0xb8, 0x00, 0x13, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, + {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, + {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x50, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, + {0xb8, 0x08, 0xe0, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, + {0xb8, 0x14, 0x18, 0xcc}, + {0xb8, 0xb2, 0x0a, 0xcc}, + {0xb8, 0xb4, 0x0a, 0xcc}, + {0xb8, 0xb5, 0x0a, 0xcc}, + {0xb8, 0xfe, 0x00, 0xcc}, + {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, + {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, + {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, + {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, + {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0x31, 0x00, 0x00, 0xbb}, + {0x09, 0x01, 0xc7, 0xbb}, + {0x34, 0x01, 0x00, 0xbb}, + {0x2b, 0x00, 0x28, 0xbb}, + {0x2c, 0x00, 0x30, 0xbb}, + {0x2d, 0x00, 0x30, 0xbb}, + {0x2e, 0x00, 0x28, 0xbb}, + {0x62, 0x04, 0x11, 0xbb}, + {0x03, 0x01, 0xe0, 0xbb}, + {0x2c, 0x00, 0x2c, 0xbb}, + {0x20, 0xd0, 0x00, 0xbb}, + {0x01, 0x00, 0x08, 0xbb}, + {0x06, 0x00, 0x10, 0xbb}, + {0x05, 0x00, 0x20, 0xbb}, + {0x20, 0x00, 0x00, 0xbb}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, + {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, + {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, + {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x10, 0xcc}, + {0xb9, 0x12, 0x00, 0xcc}, + {0xb9, 0x13, 0x0a, 0xcc}, + {0xb9, 0x14, 0x0a, 0xcc}, + {0xb9, 0x15, 0x0a, 0xcc}, + {0xb9, 0x16, 0x0a, 0xcc}, + {0xb9, 0x18, 0x00, 0xcc}, + {0xb9, 0x19, 0x0f, 0xcc}, + {0xb9, 0x1a, 0x0f, 0xcc}, + {0xb9, 0x1b, 0x0f, 0xcc}, + {0xb9, 0x1c, 0x0f, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x13, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xb8, 0x0c, 0x20, 0xcc}, + {0xb8, 0x0d, 0x70, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0x35, 0x00, 0xef, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; + static const __u8 mi1310_socinitVGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, @@ -823,7 +1109,7 @@ static const __u8 ov7660_initVGA_data[][4] = { {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa}, {0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa}, - {0x00, 0x1e, 0x01, 0xaa}, + {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */ {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */ {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */ {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa}, @@ -877,7 +1163,7 @@ static const __u8 ov7660_initQVGA_data[][4] = { {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x8f, 0x50, 0xcc}, {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa}, {0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa}, - {0x00, 0x1e, 0x01, 0xaa}, + {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */ {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */ {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */ {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa}, @@ -983,7 +1269,8 @@ static const __u8 ov7670_initVGA_JPG[][4] = { {0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, {0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa}, {0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, - {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa}, + {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */ + {0x00, 0x21, 0x02, 0xaa}, {0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa}, {0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa}, {0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa}, @@ -1048,7 +1335,8 @@ static const __u8 ov7670_initVGA_JPG[][4] = { {0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa}, {0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa}, {0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, - {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */ + {0x00, 0xaa, 0x14, 0xaa}, {0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa}, {0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa}, {0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa}, @@ -1110,7 +1398,8 @@ static const __u8 ov7670_initQVGA_JPG[][4] = { {0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, {0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa}, {0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, - {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa}, + {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */ + {0x00, 0x21, 0x02, 0xaa}, {0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa}, {0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa}, {0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa}, @@ -1175,7 +1464,8 @@ static const __u8 ov7670_initQVGA_JPG[][4] = { {0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa}, {0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa}, {0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, - {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */ + {0x00, 0xaa, 0x14, 0xaa}, {0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa}, {0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa}, {0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa}, @@ -1204,6 +1494,275 @@ static const __u8 ov7670_initQVGA_JPG[][4] = { {}, }; +/* PO1200 - values from usbvm326.inf and ms-win trace */ +static const __u8 po1200_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static const __u8 po1200_matrix[9] = { + 0x60, 0xf9, 0xe5, 0xe7, 0x50, 0x05, 0xf3, 0xe6, 0x5e +}; +static const __u8 po1200_initVGA_data[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, /* reset? */ + {0xb0, 0x03, 0x19, 0xcc}, +/* {0x00, 0x00, 0x33, 0xdd}, */ + {0xb0, 0x04, 0x02, 0xcc}, + {0xb0, 0x02, 0x02, 0xcc}, + {0xb3, 0x5d, 0x00, 0xcc}, + {0xb3, 0x01, 0x01, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x02, 0xb2, 0xcc}, + {0xb3, 0x03, 0x18, 0xcc}, + {0xb3, 0x04, 0x15, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x02, 0xcc}, + {0xb3, 0x23, 0x58, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x03, 0xcc}, + {0xb3, 0x17, 0x1f, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb0, 0x54, 0x13, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xdc, 0xcc}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x12, 0x05, 0xaa}, + {0x00, 0x13, 0x02, 0xaa}, + {0x00, 0x1e, 0xc6, 0xaa}, /* h/v flip */ + {0x00, 0x21, 0x00, 0xaa}, + {0x00, 0x25, 0x02, 0xaa}, + {0x00, 0x3c, 0x4f, 0xaa}, + {0x00, 0x3f, 0xe0, 0xaa}, + {0x00, 0x42, 0xff, 0xaa}, + {0x00, 0x45, 0x34, 0xaa}, + {0x00, 0x55, 0xfe, 0xaa}, + {0x00, 0x59, 0xd3, 0xaa}, + {0x00, 0x5e, 0x04, 0xaa}, + {0x00, 0x61, 0xb8, 0xaa}, /* sharpness */ + {0x00, 0x62, 0x02, 0xaa}, + {0x00, 0xa7, 0x31, 0xaa}, + {0x00, 0xa9, 0x66, 0xaa}, + {0x00, 0xb0, 0x00, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, + {0x00, 0xb3, 0x11, 0xaa}, + {0x00, 0xb6, 0x26, 0xaa}, + {0x00, 0xb7, 0x20, 0xaa}, + {0x00, 0xba, 0x04, 0xaa}, + {0x00, 0x88, 0x42, 0xaa}, + {0x00, 0x89, 0x9a, 0xaa}, + {0x00, 0x8a, 0x88, 0xaa}, + {0x00, 0x8b, 0x8e, 0xaa}, + {0x00, 0x8c, 0x3e, 0xaa}, + {0x00, 0x8d, 0x90, 0xaa}, + {0x00, 0x8e, 0x87, 0xaa}, + {0x00, 0x8f, 0x96, 0xaa}, + {0x00, 0x90, 0x3d, 0xaa}, + {0x00, 0x64, 0x00, 0xaa}, + {0x00, 0x65, 0x10, 0xaa}, + {0x00, 0x66, 0x20, 0xaa}, + {0x00, 0x67, 0x2b, 0xaa}, + {0x00, 0x68, 0x36, 0xaa}, + {0x00, 0x69, 0x49, 0xaa}, + {0x00, 0x6a, 0x5a, 0xaa}, + {0x00, 0x6b, 0x7f, 0xaa}, + {0x00, 0x6c, 0x9b, 0xaa}, + {0x00, 0x6d, 0xba, 0xaa}, + {0x00, 0x6e, 0xd4, 0xaa}, + {0x00, 0x6f, 0xea, 0xaa}, + {0x00, 0x70, 0x00, 0xaa}, + {0x00, 0x71, 0x10, 0xaa}, + {0x00, 0x72, 0x20, 0xaa}, + {0x00, 0x73, 0x2b, 0xaa}, + {0x00, 0x74, 0x36, 0xaa}, + {0x00, 0x75, 0x49, 0xaa}, + {0x00, 0x76, 0x5a, 0xaa}, + {0x00, 0x77, 0x7f, 0xaa}, + {0x00, 0x78, 0x9b, 0xaa}, + {0x00, 0x79, 0xba, 0xaa}, + {0x00, 0x7a, 0xd4, 0xaa}, + {0x00, 0x7b, 0xea, 0xaa}, + {0x00, 0x7c, 0x00, 0xaa}, + {0x00, 0x7d, 0x10, 0xaa}, + {0x00, 0x7e, 0x20, 0xaa}, + {0x00, 0x7f, 0x2b, 0xaa}, + {0x00, 0x80, 0x36, 0xaa}, + {0x00, 0x81, 0x49, 0xaa}, + {0x00, 0x82, 0x5a, 0xaa}, + {0x00, 0x83, 0x7f, 0xaa}, + {0x00, 0x84, 0x9b, 0xaa}, + {0x00, 0x85, 0xba, 0xaa}, + {0x00, 0x86, 0xd4, 0xaa}, + {0x00, 0x87, 0xea, 0xaa}, + {0x00, 0x57, 0x2a, 0xaa}, + {0x00, 0x03, 0x01, 0xaa}, + {0x00, 0x04, 0x10, 0xaa}, + {0x00, 0x05, 0x10, 0xaa}, + {0x00, 0x06, 0x10, 0xaa}, + {0x00, 0x07, 0x10, 0xaa}, + {0x00, 0x08, 0x13, 0xaa}, + {0x00, 0x0a, 0x00, 0xaa}, + {0x00, 0x0b, 0x10, 0xaa}, + {0x00, 0x0c, 0x20, 0xaa}, + {0x00, 0x0d, 0x18, 0xaa}, + {0x00, 0x22, 0x01, 0xaa}, + {0x00, 0x23, 0x60, 0xaa}, + {0x00, 0x25, 0x08, 0xaa}, + {0x00, 0x26, 0x82, 0xaa}, + {0x00, 0x2e, 0x0f, 0xaa}, + {0x00, 0x2f, 0x1e, 0xaa}, + {0x00, 0x30, 0x2d, 0xaa}, + {0x00, 0x31, 0x3c, 0xaa}, + {0x00, 0x32, 0x4b, 0xaa}, + {0x00, 0x33, 0x5a, 0xaa}, + {0x00, 0x34, 0x69, 0xaa}, + {0x00, 0x35, 0x78, 0xaa}, + {0x00, 0x36, 0x87, 0xaa}, + {0x00, 0x37, 0x96, 0xaa}, + {0x00, 0x38, 0xa5, 0xaa}, + {0x00, 0x39, 0xb4, 0xaa}, + {0x00, 0x3a, 0xc3, 0xaa}, + {0x00, 0x3b, 0xd2, 0xaa}, + {0x00, 0x3c, 0xe1, 0xaa}, + {0x00, 0x3e, 0xff, 0xaa}, + {0x00, 0x3f, 0xff, 0xaa}, + {0x00, 0x40, 0xff, 0xaa}, + {0x00, 0x41, 0xff, 0xaa}, + {0x00, 0x42, 0xff, 0xaa}, + {0x00, 0x43, 0xff, 0xaa}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x20, 0xc4, 0xaa}, + {0x00, 0x13, 0x03, 0xaa}, + {0x00, 0x3c, 0x50, 0xaa}, + {0x00, 0x61, 0x6a, 0xaa}, /* sharpness? */ + {0x00, 0x51, 0x5b, 0xaa}, + {0x00, 0x52, 0x91, 0xaa}, + {0x00, 0x53, 0x4c, 0xaa}, + {0x00, 0x54, 0x50, 0xaa}, + {0x00, 0x56, 0x02, 0xaa}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x03, 0xcc}, + {0xb6, 0x02, 0x20, 0xcc}, + {0xb6, 0x05, 0x02, 0xcc}, + {0xb6, 0x04, 0x58, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x21, 0xcc}, + {0xb6, 0x18, 0x03, 0xcc}, + {0xb6, 0x17, 0xa9, 0xcc}, + {0xb6, 0x16, 0x80, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, + {0xb8, 0x06, 0x20, 0xcc}, + {0xb8, 0x07, 0x03, 0xcc}, + {0xb8, 0x08, 0x58, 0xcc}, + {0xb8, 0x09, 0x02, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0xd9, 0x0f, 0xaa}, + {0x00, 0xda, 0xaa, 0xaa}, + {0x00, 0xd9, 0x10, 0xaa}, + {0x00, 0xda, 0xaa, 0xaa}, + {0x00, 0xd9, 0x11, 0xaa}, + {0x00, 0xda, 0x00, 0xaa}, + {0x00, 0xd9, 0x12, 0xaa}, + {0x00, 0xda, 0xff, 0xaa}, + {0x00, 0xd9, 0x13, 0xaa}, + {0x00, 0xda, 0xff, 0xaa}, + {0x00, 0xe8, 0x11, 0xaa}, + {0x00, 0xe9, 0x12, 0xaa}, + {0x00, 0xea, 0x5c, 0xaa}, + {0x00, 0xeb, 0xff, 0xaa}, + {0x00, 0xd8, 0x80, 0xaa}, + {0x00, 0xe6, 0x02, 0xaa}, + {0x00, 0xd6, 0x40, 0xaa}, + {0x00, 0xe3, 0x05, 0xaa}, + {0x00, 0xe0, 0x40, 0xaa}, + {0x00, 0xde, 0x03, 0xaa}, + {0x00, 0xdf, 0x03, 0xaa}, + {0x00, 0xdb, 0x02, 0xaa}, + {0x00, 0xdc, 0x00, 0xaa}, + {0x00, 0xdd, 0x03, 0xaa}, + {0x00, 0xe1, 0x08, 0xaa}, + {0x00, 0xe2, 0x01, 0xaa}, + {0x00, 0xd6, 0x40, 0xaa}, + {0x00, 0xe4, 0x40, 0xaa}, + {0x00, 0xa8, 0x8f, 0xaa}, + {0x00, 0xb4, 0x16, 0xaa}, + {0xb0, 0x02, 0x06, 0xcc}, + {0xb0, 0x18, 0x06, 0xcc}, + {0xb0, 0x19, 0x06, 0xcc}, + {0xb3, 0x5d, 0x18, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0x00, 0xb4, 0x0e, 0xaa}, + {0x00, 0xb5, 0x49, 0xaa}, + {0x00, 0xb6, 0x1c, 0xaa}, + {0x00, 0xb7, 0x96, 0xaa}, +/* end of usbvm326.inf - start of ms-win trace */ + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x3d, 0xcc}, +/*read b306*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x1a, 0x09, 0xaa}, + {0x00, 0x1b, 0x8a, 0xaa}, +/*read b827*/ + {0xb8, 0x27, 0x00, 0xcc}, + {0xb8, 0x26, 0x60, 0xcc}, + {0xb8, 0x26, 0x60, 0xcc}, +/*gamma - to do?*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0xae, 0x84, 0xaa}, +/*gamma again*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x96, 0xa0, 0xaa}, +/*matrix*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x91, 0x35, 0xaa}, + {0x00, 0x92, 0x22, 0xaa}, +/*gamma*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x95, 0x85, 0xaa}, +/*matrix*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x4d, 0x20, 0xaa}, + {0xb8, 0x22, 0x40, 0xcc}, + {0xb8, 0x23, 0x40, 0xcc}, + {0xb8, 0x24, 0x40, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0x00, 0x00, 0x64, 0xdd}, + {0x00, 0x03, 0x01, 0xaa}, +/*read 46*/ + {0x00, 0x46, 0x3c, 0xaa}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x16, 0x40, 0xaa}, + {0x00, 0x17, 0x40, 0xaa}, + {0x00, 0x18, 0x40, 0xaa}, + {0x00, 0x19, 0x41, 0xaa}, + {0x00, 0x03, 0x01, 0xaa}, + {0x00, 0x46, 0x3c, 0xaa}, + {0x00, 0x00, 0x18, 0xdd}, +/*read bfff*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0xb4, 0x1c, 0xaa}, + {0x00, 0xb5, 0x92, 0xaa}, + {0x00, 0xb6, 0x39, 0xaa}, + {0x00, 0xb7, 0x24, 0xaa}, +/*write 89 0400 1415*/ +}; + struct sensor_info { int sensorId; __u8 I2cAdd; @@ -1222,6 +1781,9 @@ static const struct sensor_info sensor_info_data[] = { {SENSOR_MI1320, 0x80 | 0xc8, 0x00, 0x148c, 0x64, 0x65, 0x01}, {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05}, {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, +/* (tested in vc032x_probe_sensor) */ +/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */ + {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01}, }; /* read 'len' bytes in gspca_dev->usb_buf */ @@ -1278,18 +1840,18 @@ static void read_sensor_register(struct gspca_dev *gspca_dev, msleep(1); } reg_r(gspca_dev, 0xa1, 0xb33e, 1); - hdata = gspca_dev->usb_buf[0]; + ldata = gspca_dev->usb_buf[0]; reg_r(gspca_dev, 0xa1, 0xb33d, 1); mdata = gspca_dev->usb_buf[0]; reg_r(gspca_dev, 0xa1, 0xb33c, 1); - ldata = gspca_dev->usb_buf[0]; - PDEBUG(D_PROBE, "Read Sensor h (0x%02X) m (0x%02X) l (0x%02X)", + hdata = gspca_dev->usb_buf[0]; + PDEBUG(D_PROBE, "Read Sensor %02x%02x %02x", hdata, mdata, ldata); reg_r(gspca_dev, 0xa1, 0xb334, 1); if (gspca_dev->usb_buf[0] == 0x02) - *value = (ldata << 8) + mdata; + *value = (hdata << 8) + mdata; else - *value = ldata; + *value = hdata; } static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) @@ -1300,7 +1862,7 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) const struct sensor_info *ptsensor_info; reg_r(gspca_dev, 0xa1, 0xbfcf, 1); - PDEBUG(D_PROBE, "check sensor header %d", gspca_dev->usb_buf[0]); + PDEBUG(D_PROBE, "check sensor header %02x", gspca_dev->usb_buf[0]); for (i = 0; i < ARRAY_SIZE(sensor_info_data); i++) { ptsensor_info = &sensor_info_data[i]; reg_w(dev, 0xa0, 0x02, 0xb334); @@ -1309,16 +1871,15 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) reg_w(dev, 0xa0, 0x01, 0xb308); reg_w(dev, 0xa0, 0x0c, 0xb309); reg_w(dev, 0xa0, ptsensor_info->I2cAdd, 0xb335); -/* PDEBUG(D_PROBE, - "check sensor VC032X -> %d Add -> ox%02X!", - i, ptsensor_info->I2cAdd); */ reg_w(dev, 0xa0, ptsensor_info->op, 0xb301); read_sensor_register(gspca_dev, ptsensor_info->IdAdd, &value); - if (value == ptsensor_info->VpId) { -/* PDEBUG(D_PROBE, "find sensor VC032X -> ox%04X!", - ptsensor_info->VpId); */ + if (value == ptsensor_info->VpId) return ptsensor_info->sensorId; - } + + /* special case for MI0360 */ + if (ptsensor_info->sensorId == SENSOR_MI1310_SOC + && value == 0x8243) + return SENSOR_MI0360; } return -1; } @@ -1420,13 +1981,6 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->epaddr = 0x02; sd->bridge = id->driver_info; - if (sd->bridge == BRIDGE_VC0321) { - cam->cam_mode = vc0321_mode; - cam->nmodes = ARRAY_SIZE(vc0321_mode); - } else { - cam->cam_mode = vc0323_mode; - cam->nmodes = ARRAY_SIZE(vc0323_mode); - } vc0321_reset(gspca_dev); sensor = vc032x_probe_sensor(gspca_dev); @@ -1436,35 +1990,66 @@ static int sd_config(struct gspca_dev *gspca_dev, return -EINVAL; case SENSOR_HV7131R: PDEBUG(D_PROBE, "Find Sensor HV7131R"); - sd->sensor = SENSOR_HV7131R; + break; + case SENSOR_MI0360: + PDEBUG(D_PROBE, "Find Sensor MI0360"); + sd->bridge = BRIDGE_VC0323; break; case SENSOR_MI1310_SOC: PDEBUG(D_PROBE, "Find Sensor MI1310_SOC"); - sd->sensor = SENSOR_MI1310_SOC; break; case SENSOR_MI1320: PDEBUG(D_PROBE, "Find Sensor MI1320"); - sd->sensor = SENSOR_MI1320; break; case SENSOR_OV7660: PDEBUG(D_PROBE, "Find Sensor OV7660"); - sd->sensor = SENSOR_OV7660; break; case SENSOR_OV7670: PDEBUG(D_PROBE, "Find Sensor OV7670"); - sd->sensor = SENSOR_OV7670; + break; + case SENSOR_PO1200: + PDEBUG(D_PROBE, "Find Sensor PO1200"); break; case SENSOR_PO3130NC: PDEBUG(D_PROBE, "Find Sensor PO3130NC"); - sd->sensor = SENSOR_PO3130NC; break; } + sd->sensor = sensor; - sd->qindex = 7; - sd->autogain = AUTOGAIN_DEF; + if (sd->bridge == BRIDGE_VC0321) { + cam->cam_mode = vc0321_mode; + cam->nmodes = ARRAY_SIZE(vc0321_mode); + } else { + if (sensor != SENSOR_PO1200) { + cam->cam_mode = vc0323_mode; + cam->nmodes = ARRAY_SIZE(vc0323_mode); + } else { + cam->cam_mode = svga_mode; + cam->nmodes = ARRAY_SIZE(svga_mode); + } + } + + sd->hflip = HFLIP_DEF; + sd->vflip = VFLIP_DEF; + if (sd->sensor == SENSOR_OV7670) { + sd->hflip = 1; + sd->vflip = 1; + } sd->lightfreq = FREQ_DEF; if (sd->sensor != SENSOR_OV7670) gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX); + switch (sd->sensor) { + case SENSOR_OV7660: + case SENSOR_OV7670: + case SENSOR_PO1200: + break; + default: + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) + | (1 << VFLIP_IDX); + break; + } + + sd->sharpness = SHARPNESS_DEF; if (sd->bridge == BRIDGE_VC0321) { reg_r(gspca_dev, 0x8a, 0, 3); @@ -1482,12 +2067,33 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void setquality(struct gspca_dev *gspca_dev) +/* for OV7660 and OV7670 only */ +static void sethvflip(struct gspca_dev *gspca_dev) { -} + struct sd *sd = (struct sd *) gspca_dev; + __u8 data; -static void setautogain(struct gspca_dev *gspca_dev) -{ + switch (sd->sensor) { + case SENSOR_OV7660: + data = 1; + break; + case SENSOR_OV7670: + data = 7; + break; + case SENSOR_PO1200: + data = 0; + i2c_write(gspca_dev, 0x03, &data, 1); + data = 0x80 * sd->hflip + | 0x40 * sd->vflip + | 0x06; + i2c_write(gspca_dev, 0x1e, &data, 1); + return; + default: + return; + } + data |= OV7660_MVFP_MIRROR * sd->hflip + | OV7660_MVFP_VFLIP * sd->vflip; + i2c_write(gspca_dev, OV7660_REG_MVFP, &data, 1); } static void setlightfreq(struct gspca_dev *gspca_dev) @@ -1501,6 +2107,20 @@ static void setlightfreq(struct gspca_dev *gspca_dev) usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]); } +/* po1200 only */ +static void setsharpness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 data; + + if (sd->sensor != SENSOR_PO1200) + return; + data = 0; + i2c_write(gspca_dev, 0x03, &data, 1); + data = 0xb5 + sd->sharpness * 3; + i2c_write(gspca_dev, 0x61, &data, 1); +} + static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1551,6 +2171,17 @@ static int sd_start(struct gspca_dev *gspca_dev) usb_exchange(gspca_dev, ov7670_initVGA_JPG); } break; + case SENSOR_MI0360: + GammaT = mi1320_gamma; + MatrixT = mi0360_matrix; + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, mi0360_initQVGA_JPG); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, mi0360_initVGA_JPG); + } + break; case SENSOR_MI1310_SOC: if (mode) { /* 320x240 */ @@ -1583,6 +2214,11 @@ static int sd_start(struct gspca_dev *gspca_dev) } usb_exchange(gspca_dev, po3130_rundata); break; + case SENSOR_PO1200: + GammaT = po1200_gamma; + MatrixT = po1200_matrix; + usb_exchange(gspca_dev, po1200_initVGA_data); + break; default: PDEBUG(D_PROBE, "Damned !! no sensor found Bye"); return -EMEDIUMTYPE; @@ -1615,11 +2251,16 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev->dev, 0xa0, 0x23, 0xb800); * ISP CTRL_BAS */ /* set the led on 0x0892 0x0896 */ - reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); - msleep(100); - setquality(gspca_dev); - setautogain(gspca_dev); - setlightfreq(gspca_dev); + if (sd->sensor != SENSOR_PO1200) { + reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); + msleep(100); + sethvflip(gspca_dev); + setlightfreq(gspca_dev); + } else { + setsharpness(gspca_dev); + sethvflip(gspca_dev); + reg_w(gspca_dev->dev, 0x89, 0x0400, 0x1415); + } } return 0; } @@ -1665,24 +2306,48 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data, len); return; } + + /* The vc0321 sends some additional data after sending the complete + * frame, we ignore this. */ + if (sd->bridge == BRIDGE_VC0321 + && len > frame->v4l2_buf.length - (frame->data_end - frame->data)) + len = frame->v4l2_buf.length - (frame->data_end - frame->data); gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return 0; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hflip; + return 0; +} + +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - sd->autogain = val; + sd->vflip = val; if (gspca_dev->streaming) - setautogain(gspca_dev); + sethvflip(gspca_dev); return 0; } -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->autogain; + *val = sd->vflip; return 0; } @@ -1704,6 +2369,24 @@ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) + setsharpness(gspca_dev); + return 0; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu) { @@ -1743,11 +2426,13 @@ static const struct sd_desc sd_desc = { static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x046d, 0x0892), .driver_info = BRIDGE_VC0321}, {USB_DEVICE(0x046d, 0x0896), .driver_info = BRIDGE_VC0321}, + {USB_DEVICE(0x046d, 0x0897), .driver_info = BRIDGE_VC0321}, {USB_DEVICE(0x0ac8, 0x0321), .driver_info = BRIDGE_VC0321}, {USB_DEVICE(0x0ac8, 0x0323), .driver_info = BRIDGE_VC0323}, {USB_DEVICE(0x0ac8, 0x0328), .driver_info = BRIDGE_VC0321}, {USB_DEVICE(0x0ac8, 0xc001), .driver_info = BRIDGE_VC0321}, {USB_DEVICE(0x0ac8, 0xc002), .driver_info = BRIDGE_VC0321}, + {USB_DEVICE(0x15b8, 0x6002), .driver_info = BRIDGE_VC0323}, {USB_DEVICE(0x17ef, 0x4802), .driver_info = BRIDGE_VC0323}, {} }; diff --git a/drivers/media/video/gspca/zc3xx-reg.h b/drivers/media/video/gspca/zc3xx-reg.h index f52e09c..bfb559c 100644 --- a/drivers/media/video/gspca/zc3xx-reg.h +++ b/drivers/media/video/gspca/zc3xx-reg.h @@ -244,14 +244,6 @@ #define ZC3XX_R1CA_SHARPNESS04 0x01ca #define ZC3XX_R1CB_SHARPNESS05 0x01cb -/* Synchronization */ -#define ZC3XX_R190_SYNC00LOW 0x0190 -#define ZC3XX_R191_SYNC00MID 0x0191 -#define ZC3XX_R192_SYNC00HIGH 0x0192 -#define ZC3XX_R195_SYNC01LOW 0x0195 -#define ZC3XX_R196_SYNC01MID 0x0196 -#define ZC3XX_R197_SYNC01HIGH 0x0197 - /* Dead pixels */ #define ZC3XX_R250_DEADPIXELSMODE 0x0250 diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 0befacf..ec2a53d 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -51,16 +51,16 @@ struct sd { #define SENSOR_CS2102 0 #define SENSOR_CS2102K 1 #define SENSOR_GC0305 2 -#define SENSOR_HDCS2020 3 -#define SENSOR_HDCS2020b 4 -#define SENSOR_HV7131B 5 -#define SENSOR_HV7131C 6 -#define SENSOR_ICM105A 7 -#define SENSOR_MC501CB 8 -#define SENSOR_OV7620 9 -/*#define SENSOR_OV7648 9 - same values */ -#define SENSOR_OV7630C 10 -#define SENSOR_PAS106 11 +#define SENSOR_HDCS2020b 3 +#define SENSOR_HV7131B 4 +#define SENSOR_HV7131C 5 +#define SENSOR_ICM105A 6 +#define SENSOR_MC501CB 7 +#define SENSOR_OV7620 8 +/*#define SENSOR_OV7648 8 - same values */ +#define SENSOR_OV7630C 9 +#define SENSOR_PAS106 10 +#define SENSOR_PAS202B 11 #define SENSOR_PB0330 12 #define SENSOR_PO2030 13 #define SENSOR_TAS5130CK 14 @@ -173,7 +173,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, @@ -186,7 +186,7 @@ static struct v4l2_pix_format vga_mode[] = { .priv = 0}, }; -static struct v4l2_pix_format sif_mode[] = { +static const struct v4l2_pix_format sif_mode[] = { {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144 * 3 / 8 + 590, @@ -1653,295 +1653,6 @@ static const struct usb_action gc0305_NoFliker[] = { {} }; -/* play poker with registers at your own risk !! */ -static const struct usb_action hdcs2020xx_Initial[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, - /* D0 ?? E0 did not start */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, - {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, - {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, - {0xaa, 0x02, 0x0002}, - {0xaa, 0x07, 0x0006}, - {0xaa, 0x08, 0x0002}, - {0xaa, 0x09, 0x0006}, - {0xaa, 0x0a, 0x0001}, - {0xaa, 0x0b, 0x0001}, - {0xaa, 0x0c, 0x0008}, - {0xaa, 0x0d, 0x0000}, - {0xaa, 0x10, 0x0000}, - {0xaa, 0x12, 0x0005}, - {0xaa, 0x13, 0x0063}, - {0xaa, 0x15, 0x0070}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x70, ZC3XX_R18D_YTARGET}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x04, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x07, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x11, ZC3XX_R120_GAMMA00}, /* gamma ~4 */ - {0xa0, 0x37, ZC3XX_R121_GAMMA01}, - {0xa0, 0x58, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x91, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa6, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb8, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc7, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd3, ZC3XX_R128_GAMMA08}, - {0xa0, 0xde, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe6, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xed, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf3, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf8, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfb, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x23, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - - {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf5, ZC3XX_R10B_RGB01}, - {0xa0, 0xff, ZC3XX_R10C_RGB02}, - {0xa0, 0xf9, ZC3XX_R10D_RGB10}, - {0xa0, 0x51, ZC3XX_R10E_RGB11}, - {0xa0, 0xf5, ZC3XX_R10F_RGB12}, - {0xa0, 0xfb, ZC3XX_R110_RGB20}, - {0xa0, 0xed, ZC3XX_R111_RGB21}, - {0xa0, 0x5f, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, - {0xaa, 0x20, 0x0004}, - {0xaa, 0x21, 0x003d}, - {0xaa, 0x03, 0x0041}, - {0xaa, 0x04, 0x0010}, - {0xaa, 0x05, 0x003d}, - {0xaa, 0x0e, 0x0001}, - {0xaa, 0x0f, 0x0000}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0195}, - {0xa1, 0x01, 0x0196}, - {0xa1, 0x01, 0x0197}, - {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x1d, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x85, ZC3XX_R118_BGAIN}, - {0xa1, 0x01, 0x0116}, - {0xa1, 0x01, 0x0118}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x1d, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x85, ZC3XX_R118_BGAIN}, - {0xa1, 0x01, 0x0116}, - {0xa1, 0x01, 0x0118}, -/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */ - {0xa0, 0x00, 0x0007}, - {} -}; - -static const struct usb_action hdcs2020xx_InitialScale[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, - {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, - {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, - {0xaa, 0x02, 0x0002}, - {0xaa, 0x07, 0x0006}, - {0xaa, 0x08, 0x0002}, - {0xaa, 0x09, 0x0006}, - {0xaa, 0x0a, 0x0001}, - {0xaa, 0x0b, 0x0001}, - {0xaa, 0x0c, 0x0008}, - {0xaa, 0x0d, 0x0000}, - {0xaa, 0x10, 0x0000}, - {0xaa, 0x12, 0x0005}, - {0xaa, 0x13, 0x0063}, - {0xaa, 0x15, 0x0070}, - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x70, ZC3XX_R18D_YTARGET}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x04, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x07, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x11, ZC3XX_R120_GAMMA00}, /* gamma ~4*/ - {0xa0, 0x37, ZC3XX_R121_GAMMA01}, - {0xa0, 0x58, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x91, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa6, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb8, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc7, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd3, ZC3XX_R128_GAMMA08}, - {0xa0, 0xde, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe6, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xed, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf3, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf8, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfb, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x23, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xff, ZC3XX_R10B_RGB01}, - {0xa0, 0xff, ZC3XX_R10C_RGB02}, - {0xa0, 0xff, ZC3XX_R10D_RGB10}, - {0xa0, 0x60, ZC3XX_R10E_RGB11}, - {0xa0, 0xff, ZC3XX_R10F_RGB12}, - {0xa0, 0xff, ZC3XX_R110_RGB20}, - {0xa0, 0xff, ZC3XX_R111_RGB21}, - {0xa0, 0x60, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, - {0xaa, 0x20, 0x0002}, - {0xaa, 0x21, 0x001b}, - {0xaa, 0x03, 0x0044}, - {0xaa, 0x04, 0x0008}, - {0xaa, 0x05, 0x001b}, - {0xaa, 0x0e, 0x0001}, - {0xaa, 0x0f, 0x0000}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xeb, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0195}, - {0xa1, 0x01, 0x0196}, - {0xa1, 0x01, 0x0197}, - {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x1d, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x99, ZC3XX_R118_BGAIN}, - {0xa1, 0x01, 0x0116}, - {0xa1, 0x01, 0x0118}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x1d, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x99, ZC3XX_R118_BGAIN}, -/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */ - {0xa0, 0x00, 0x0007}, -/* {0xa0, 0x18, 0x00fe}, */ - {} -}; static const struct usb_action hdcs2020xb_Initial[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT}, @@ -2310,67 +2021,6 @@ static const struct usb_action hv7131bxx_Initial[] = { /* 320x240 */ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xaa, 0x02, 0x0090}, /* 00,02,80,aa */ - {0xa1, 0x01, 0x0002}, - {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, - {0xa1, 0x01, 0x0091}, - {0xa1, 0x01, 0x0095}, - {0xa1, 0x01, 0x0096}, - - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x25, 0x0007}, - {0xaa, 0x26, 0x00a1}, - {0xaa, 0x27, 0x0020}, - {0xaa, 0x20, 0x0000}, - {0xaa, 0x21, 0x00a0}, - {0xaa, 0x22, 0x0016}, - {0xaa, 0x23, 0x0040}, - - {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2F */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 4d */ - {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa1, 0x01, 0x001d}, - {0xa1, 0x01, 0x001e}, - {0xa1, 0x01, 0x001f}, - {0xa1, 0x01, 0x0020}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, -/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */ {} }; @@ -2418,65 +2068,156 @@ static const struct usb_action hv7131bxx_InitialScale[] = { /* 640x480*/ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xaa, 0x02, 0x0090}, /* {0xaa, 0x02, 0x0080}, */ - {0xa1, 0x01, 0x0002}, - {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, - {0xa1, 0x01, 0x0091}, - {0xa1, 0x01, 0x0095}, - {0xa1, 0x01, 0x0096}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x25, 0x0007}, - {0xaa, 0x26, 0x00a1}, - {0xaa, 0x27, 0x0020}, - {0xaa, 0x20, 0x0000}, - {0xaa, 0x21, 0x0040}, - {0xaa, 0x22, 0x0013}, - {0xaa, 0x23, 0x004c}, - {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2f */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 4d */ - {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 60 */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x13, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0x4c, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa1, 0x01, 0x001d}, - {0xa1, 0x01, 0x001e}, - {0xa1, 0x01, 0x001f}, - {0xa1, 0x01, 0x0020}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, -/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */ + {} +}; +static const struct usb_action hv7131b_50HZ[] = { /* 640x480*/ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */ + {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */ + {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */ + {0xaa, 0x22, 0x001b}, /* 00,22,1b,aa */ + {0xaa, 0x23, 0x00fc}, /* 00,23,fc,aa */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */ + {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,ea,cc */ + {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,60,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */ + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */ + {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ + {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */ + {0xa0, 0x1b, ZC3XX_R01F_HSYNC_2}, /* 00,1f,1b,cc */ + {0xa0, 0xfc, ZC3XX_R020_HSYNC_3}, /* 00,20,fc,cc */ + {} +}; +static const struct usb_action hv7131b_50HZScale[] = { /* 320x240 */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */ + {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */ + {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */ + {0xaa, 0x22, 0x0012}, /* 00,22,12,aa */ + {0xaa, 0x23, 0x0080}, /* 00,23,80,aa */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */ + {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */ + {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */ + {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,d4,cc */ + {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,c0,cc */ + {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */ + {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */ + {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ + {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */ + {0xa0, 0x12, ZC3XX_R01F_HSYNC_2}, /* 00,1f,12,cc */ + {0xa0, 0x80, ZC3XX_R020_HSYNC_3}, /* 00,20,80,cc */ + {} +}; +static const struct usb_action hv7131b_60HZ[] = { /* 640x480*/ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */ + {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */ + {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x21, 0x0040}, /* 00,21,40,aa */ + {0xaa, 0x22, 0x0013}, /* 00,22,13,aa */ + {0xaa, 0x23, 0x004c}, /* 00,23,4c,aa */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */ + {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,c3,cc */ + {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,50,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */ + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */ + {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */ + {0xa0, 0x13, ZC3XX_R01F_HSYNC_2}, /* 00,1f,13,cc */ + {0xa0, 0x4c, ZC3XX_R020_HSYNC_3}, /* 00,20,4c,cc */ + {} +}; +static const struct usb_action hv7131b_60HZScale[] = { /* 320x240 */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */ + {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */ + {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */ + {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */ + {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */ + {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */ + {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */ + {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,86,cc */ + {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,a0,cc */ + {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */ + {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */ + {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ + {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */ + {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */ + {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */ + {} +}; +static const struct usb_action hv7131b_NoFliker[] = { /* 640x480*/ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */ + {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */ + {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */ + {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */ + {0xaa, 0x23, 0x0003}, /* 00,23,03,aa */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */ + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */ + {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ + {0xa0, 0x10, ZC3XX_R01E_HSYNC_1}, /* 00,1e,10,cc */ + {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, /* 00,1f,00,cc */ + {0xa0, 0x03, ZC3XX_R020_HSYNC_3}, /* 00,20,03,cc */ + {} +}; +static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */ + {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */ + {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */ + {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */ + {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */ + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */ + {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */ + {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */ + {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */ + {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */ {} }; @@ -4389,6 +4130,270 @@ static const struct usb_action pas106b_NoFliker[] = { {} }; +/* from usbvm31b.inf */ +static const struct usb_action pas202b_Initial[] = { /* 640x480 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */ + {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */ + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,03,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,03,cc */ + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */ + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ + {0xaa, 0x02, 0x0002}, /* 00,02,04,aa --> 02 */ + {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */ + {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */ + {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */ + {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */ + {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */ + {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */ + {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */ + {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */ + {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */ + {} +}; +static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */ + {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */ + {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,08,cc */ + {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,02,cc */ + {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,08,cc */ + {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,02,cc */ + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ + {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */ + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ + {0xaa, 0x02, 0x0002}, /* 00,02,02,aa */ + {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */ + {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */ + {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */ + {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */ + {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */ + {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */ + {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */ + {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */ + {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */ + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */ + {} +}; +static const struct usb_action pas202b_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x0068}, /* 00,21,68,aa */ + {0xaa, 0x03, 0x0044}, /* 00,03,44,aa */ + {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */ + {0xaa, 0x05, 0x0028}, /* 00,05,28,aa */ + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ + {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,d2,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4d,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, /* 00,1d,44,cc */ + {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ + {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */ + {0xa0, 0xeb, ZC3XX_R020_HSYNC_3}, /* 00,20,eb,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x006c}, /* 00,21,6c,aa */ + {0xaa, 0x03, 0x0041}, /* 00,03,41,aa */ + {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */ + {0xaa, 0x05, 0x002c}, /* 00,05,2c,aa */ + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */ + {0xa0, 0xbe, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,be,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,9b,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */ + {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ + {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ + {0xaa, 0x03, 0x0045}, /* 00,03,45,aa */ + {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ + {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ + {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,c0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x40, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,40,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x45, ZC3XX_R01D_HSYNC_0}, /* 00,1d,45,cc */ + {0xa0, 0x8e, ZC3XX_R01E_HSYNC_1}, /* 00,1e,8e,cc */ + {0xa0, 0xc1, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c1,cc */ + {0xa0, 0xf5, ZC3XX_R020_HSYNC_3}, /* 00,20,f5,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_60HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x0004}, /* 00,21,04,aa */ + {0xaa, 0x03, 0x0042}, /* 00,03,42,aa */ + {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ + {0xaa, 0x05, 0x0004}, /* 00,05,04,aa */ + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */ + {0xa0, 0x9f, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,9f,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x42, ZC3XX_R01D_HSYNC_0}, /* 00,1d,42,cc */ + {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ + {0xa0, 0xaf, ZC3XX_R01F_HSYNC_2}, /* 00,1f,af,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x0020}, /* 00,21,20,aa */ + {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */ + {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ + {0xaa, 0x05, 0x0020}, /* 00,05,20,aa */ + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */ + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */ + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_NoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */ + {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */ + {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ + {0xaa, 0x05, 0x0010}, /* 00,05,10,aa */ + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */ + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */ + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */ + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; + static const struct usb_action pb03303x_Initial[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, @@ -5725,7 +5730,7 @@ static const struct usb_action tas5130cxx_Initial[] = { {} }; static const struct usb_action tas5130cxx_InitialScale[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, +/*?? {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, @@ -6049,7 +6054,7 @@ static const struct usb_action tas5130c_vf0250_InitialScale[] = { {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa, */ {0xaa, 0x13, 0x0002}, /* 00,13,02,aa, */ {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */ - {0xaa, 0x01, 0x0000}, +/*?? {0xaa, 0x01, 0x0000}, */ {0xaa, 0x01, 0x0000}, {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */ {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */ @@ -6065,8 +6070,8 @@ static const struct usb_action tas5130c_vf0250_InitialScale[] = { {0xaa, 0x0f, 0x00a0}, /* 00,0f,a0,aa, */ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa, */ {0xaa, 0x11, 0x00a0}, /* 00,11,a0,aa, */ - {0xa0, 0x00, 0x0039}, - {0xa1, 0x01, 0x0037}, +/*?? {0xa0, 0x00, 0x0039}, + {0xa1, 0x01, 0x0037}, */ {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */ {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa (e6 -> e8) */ {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */ @@ -6303,7 +6308,7 @@ static __u8 i2c_write(struct gspca_dev *gspca_dev, reg_w_i(gspca_dev->dev, valL, 0x93); reg_w_i(gspca_dev->dev, valH, 0x94); reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */ - msleep(5); + msleep(15); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)", reg, valH, valL, retbyte); @@ -6346,30 +6351,35 @@ static void setmatrix(struct gspca_dev *gspca_dev) {0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50}; static const __u8 ov7620_matrix[9] = {0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58}; + static const __u8 pas202b_matrix[9] = + {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f}; static const __u8 po2030_matrix[9] = {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60}; static const __u8 vf0250_matrix[9] = {0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b}; + static const __u8 *matrix_tb[SENSOR_MAX] = { + NULL, /* SENSOR_CS2102 0 */ + NULL, /* SENSOR_CS2102K 1 */ + gc0305_matrix, /* SENSOR_GC0305 2 */ + NULL, /* SENSOR_HDCS2020b 3 */ + NULL, /* SENSOR_HV7131B 4 */ + NULL, /* SENSOR_HV7131C 5 */ + NULL, /* SENSOR_ICM105A 6 */ + NULL, /* SENSOR_MC501CB 7 */ + ov7620_matrix, /* SENSOR_OV7620 8 */ + NULL, /* SENSOR_OV7630C 9 */ + NULL, /* SENSOR_PAS106 10 */ + pas202b_matrix, /* SENSOR_PAS202B 11 */ + NULL, /* SENSOR_PB0330 12 */ + po2030_matrix, /* SENSOR_PO2030 13 */ + NULL, /* SENSOR_TAS5130CK 14 */ + NULL, /* SENSOR_TAS5130CXX 15 */ + vf0250_matrix, /* SENSOR_TAS5130C_VF0250 16 */ + }; - switch (sd->sensor) { - case SENSOR_GC0305: - matrix = gc0305_matrix; - break; - case SENSOR_MC501CB: - return; /* no matrix? */ - case SENSOR_OV7620: -/* case SENSOR_OV7648: */ - matrix = ov7620_matrix; - break; - case SENSOR_PO2030: - matrix = po2030_matrix; - break; - case SENSOR_TAS5130C_VF0250: - matrix = vf0250_matrix; - break; - default: /* matrix already loaded */ - return; - } + matrix = matrix_tb[sd->sensor]; + if (matrix == NULL) + return; /* matrix already loaded */ for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++) reg_w(gspca_dev->dev, matrix[i], 0x010a + i); } @@ -6585,42 +6595,42 @@ static int setlightfreq(struct gspca_dev *gspca_dev) {gc0305_NoFliker, gc0305_NoFliker, gc0305_50HZ, gc0305_50HZ, gc0305_60HZ, gc0305_60HZ}, -/* SENSOR_HDCS2020 3 */ - {NULL, NULL, - NULL, NULL, - NULL, NULL}, -/* SENSOR_HDCS2020b 4 */ +/* SENSOR_HDCS2020b 3 */ {hdcs2020b_NoFliker, hdcs2020b_NoFliker, hdcs2020b_50HZ, hdcs2020b_50HZ, hdcs2020b_60HZ, hdcs2020b_60HZ}, -/* SENSOR_HV7131B 5 */ +/* SENSOR_HV7131B 4 */ + {hv7131b_NoFlikerScale, hv7131b_NoFliker, + hv7131b_50HZScale, hv7131b_50HZ, + hv7131b_60HZScale, hv7131b_60HZ}, +/* SENSOR_HV7131C 5 */ {NULL, NULL, NULL, NULL, NULL, NULL}, -/* SENSOR_HV7131C 6 */ - {NULL, NULL, - NULL, NULL, - NULL, NULL}, -/* SENSOR_ICM105A 7 */ +/* SENSOR_ICM105A 6 */ {icm105a_NoFliker, icm105a_NoFlikerScale, icm105a_50HZ, icm105a_50HZScale, icm105a_60HZ, icm105a_60HZScale}, -/* SENSOR_MC501CB 8 */ +/* SENSOR_MC501CB 7 */ {MC501CB_NoFliker, MC501CB_NoFlikerScale, MC501CB_50HZ, MC501CB_50HZScale, MC501CB_60HZ, MC501CB_60HZScale}, -/* SENSOR_OV7620 9 */ +/* SENSOR_OV7620 8 */ {OV7620_NoFliker, OV7620_NoFliker, OV7620_50HZ, OV7620_50HZ, OV7620_60HZ, OV7620_60HZ}, -/* SENSOR_OV7630C 10 */ +/* SENSOR_OV7630C 9 */ {NULL, NULL, NULL, NULL, NULL, NULL}, -/* SENSOR_PAS106 11 */ +/* SENSOR_PAS106 10 */ {pas106b_NoFliker, pas106b_NoFliker, pas106b_50HZ, pas106b_50HZ, pas106b_60HZ, pas106b_60HZ}, +/* SENSOR_PAS202B 11 */ + {pas202b_NoFlikerScale, pas202b_NoFliker, + pas202b_50HZScale, pas202b_50HZ, + pas202b_60HZScale, pas202b_60HZ}, /* SENSOR_PB0330 12 */ {pb0330_NoFliker, pb0330_NoFlikerScale, pb0330_50HZ, pb0330_50HZScale, @@ -7002,15 +7012,15 @@ static int sd_config(struct gspca_dev *gspca_dev, 5, /* SENSOR_CS2102 0 */ 5, /* SENSOR_CS2102K 1 */ 4, /* SENSOR_GC0305 2 */ - 4, /* SENSOR_HDCS2020 3 */ - 4, /* SENSOR_HDCS2020b 4 */ - 4, /* SENSOR_HV7131B 5 */ - 4, /* SENSOR_HV7131C 6 */ - 4, /* SENSOR_ICM105A 7 */ - 4, /* SENSOR_MC501CB 8 */ - 3, /* SENSOR_OV7620 9 */ - 4, /* SENSOR_OV7630C 10 */ - 4, /* SENSOR_PAS106 11 */ + 4, /* SENSOR_HDCS2020b 3 */ + 4, /* SENSOR_HV7131B 4 */ + 4, /* SENSOR_HV7131C 5 */ + 4, /* SENSOR_ICM105A 6 */ + 4, /* SENSOR_MC501CB 7 */ + 3, /* SENSOR_OV7620 8 */ + 4, /* SENSOR_OV7630C 9 */ + 4, /* SENSOR_PAS106 10 */ + 4, /* SENSOR_PAS202B 11 */ 4, /* SENSOR_PB0330 12 */ 4, /* SENSOR_PO2030 13 */ 4, /* SENSOR_TAS5130CK 14 */ @@ -7066,8 +7076,8 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = SENSOR_ICM105A; break; case 0x0e: - PDEBUG(D_PROBE, "Find Sensor HDCS2020"); - sd->sensor = SENSOR_HDCS2020; + PDEBUG(D_PROBE, "Find Sensor PAS202B"); + sd->sensor = SENSOR_PAS202B; sd->sharpness = 1; break; case 0x0f: @@ -7153,7 +7163,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->gamma = gamma[(int) sd->sensor]; sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value; - sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value; switch (sd->sensor) { case SENSOR_GC0305: @@ -7161,7 +7170,6 @@ static int sd_config(struct gspca_dev *gspca_dev, case SENSOR_PO2030: gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX); break; - case SENSOR_HDCS2020: case SENSOR_HV7131B: case SENSOR_HV7131C: case SENSOR_OV7630C: @@ -7191,15 +7199,15 @@ static int sd_start(struct gspca_dev *gspca_dev) {cs2102_InitialScale, cs2102_Initial}, /* 0 */ {cs2102K_InitialScale, cs2102K_Initial}, /* 1 */ {gc0305_Initial, gc0305_InitialScale}, /* 2 */ - {hdcs2020xx_InitialScale, hdcs2020xx_Initial}, /* 3 */ - {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 4 */ - {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 5 */ - {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 6 */ - {icm105axx_InitialScale, icm105axx_Initial}, /* 7 */ - {MC501CB_InitialScale, MC501CB_Initial}, /* 9 */ - {OV7620_mode0, OV7620_mode1}, /* 9 */ - {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */ - {pas106b_InitialScale, pas106b_Initial}, /* 11 */ + {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 3 */ + {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 4 */ + {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 5 */ + {icm105axx_InitialScale, icm105axx_Initial}, /* 6 */ + {MC501CB_InitialScale, MC501CB_Initial}, /* 7 */ + {OV7620_mode0, OV7620_mode1}, /* 8 */ + {ov7630c_InitialScale, ov7630c_Initial}, /* 9 */ + {pas106b_InitialScale, pas106b_Initial}, /* 10 */ + {pas202b_Initial, pas202b_InitialScale}, /* 11 */ {pb0330xx_InitialScale, pb0330xx_Initial}, /* 12 */ /* or {pb03303x_InitialScale, pb03303x_Initial}, */ {PO2030_mode0, PO2030_mode1}, /* 13 */ @@ -7256,6 +7264,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_r(gspca_dev, 0x0008); reg_w(dev, 0x00, 0x0008); break; + case SENSOR_PAS202B: case SENSOR_GC0305: reg_r(gspca_dev, 0x0008); /* fall thru */ @@ -7269,7 +7278,6 @@ static int sd_start(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_CS2102: /* gamma set in xxx_Initial */ case SENSOR_CS2102K: - case SENSOR_HDCS2020: case SENSOR_HDCS2020b: case SENSOR_PB0330: /* pb with chip_revision - see above */ case SENSOR_OV7630C: @@ -7282,6 +7290,7 @@ static int sd_start(struct gspca_dev *gspca_dev) setmatrix(gspca_dev); /* one more time? */ switch (sd->sensor) { case SENSOR_OV7620: + case SENSOR_PAS202B: reg_r(gspca_dev, 0x0180); /* from win */ reg_w(dev, 0x00, 0x0180); break; @@ -7293,37 +7302,29 @@ static int sd_start(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_GC0305: - case SENSOR_OV7620: reg_w(dev, 0x09, 0x01ad); /* (from win traces) */ reg_w(dev, 0x15, 0x01ae); - sd->autogain = 0; - break; + /* fall thru */ + case SENSOR_PAS202B: case SENSOR_PO2030: - reg_w(dev, 0x40, 0x0117); /* (from win traces) */ +/* reg_w(dev, 0x40, ZC3XX_R117_GGAIN); * (from win traces) */ reg_r(gspca_dev, 0x0180); break; - } - - setautogain(gspca_dev); - switch (sd->sensor) { - case SENSOR_GC0305: -/* setlightfreq(gspca_dev); ?? (end: 80 -> [18d]) */ - reg_w(dev, 0x09, 0x01ad); /* (from win traces) */ - reg_w(dev, 0x15, 0x01ae); - reg_w(dev, 0x40, 0x0180); - reg_w(dev, 0x40, 0x0117); - reg_r(gspca_dev, 0x0180); - sd->autogain = 1; - setautogain(gspca_dev); - break; case SENSOR_OV7620: + reg_w(dev, 0x09, 0x01ad); + reg_w(dev, 0x15, 0x01ae); i2c_read(gspca_dev, 0x13); /*fixme: returns 0xa3 */ i2c_write(gspca_dev, 0x13, 0xa3, 0x00); /*fixme: returned value to send? */ - reg_w(dev, 0x40, 0x0117); /* (from win traces) */ + reg_w(dev, 0x40, 0x0117); reg_r(gspca_dev, 0x0180); - setautogain(gspca_dev); - msleep(500); + break; + } + + setautogain(gspca_dev); + switch (sd->sensor) { + case SENSOR_PAS202B: + reg_w(dev, 0x00, 0x0007); /* (from win traces) */ break; case SENSOR_PO2030: msleep(500); @@ -7333,6 +7334,8 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(dev, 0x02, 0x0008); break; } + if (sd->sensor == SENSOR_PAS202B) + reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING); return 0; } @@ -7530,6 +7533,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0458, 0x700c)}, {USB_DEVICE(0x0458, 0x700f)}, {USB_DEVICE(0x0461, 0x0a00)}, + {USB_DEVICE(0x046d, 0x089d), .driver_info = SENSOR_MC501CB}, {USB_DEVICE(0x046d, 0x08a0)}, {USB_DEVICE(0x046d, 0x08a1)}, {USB_DEVICE(0x046d, 0x08a2)}, diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index efe8499..d4658c5 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -385,10 +385,10 @@ static int ir_attach(struct i2c_adapter *adap, int addr, goto err_out_detach; } - /* Phys addr can only be set after attaching (for ir->c.dev.bus_id) */ + /* Phys addr can only be set after attaching (for ir->c.dev) */ snprintf(ir->phys, sizeof(ir->phys), "%s/%s/ir0", - ir->c.adapter->dev.bus_id, - ir->c.dev.bus_id); + dev_name(&ir->c.adapter->dev), + dev_name(&ir->c.dev)); /* init + register input device */ ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes); diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c index 4e05f91..2883c87 100644 --- a/drivers/media/video/ivtv/ivtv-cards.c +++ b/drivers/media/video/ivtv/ivtv-cards.c @@ -877,20 +877,28 @@ static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = { static const struct ivtv_card ivtv_card_pg600v2 = { .type = IVTV_CARD_PG600V2, .name = "Yuan PG600-2, GotView PCI DVD Lite", - .comment = "only Composite and S-Video inputs are supported, not the tuner\n", .v4l2_capabilities = IVTV_CAP_ENCODER, .hw_video = IVTV_HW_CX25840, .hw_audio = IVTV_HW_CX25840, .hw_audio_ctrl = IVTV_HW_CX25840, - .hw_all = IVTV_HW_CX25840, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + /* XC2028 support apparently works for the Yuan, it's still + uncertain whether it also works with the GotView. */ .video_inputs = { - { IVTV_CARD_INPUT_SVIDEO1, 0, + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, - { IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, }, .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + .xceive_pin = 12, + .tuners = { + { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, + }, .pci_list = ivtv_pci_pg600v2, .i2c = &ivtv_i2c_std, }; diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c index 48e103b..62aa06f 100644 --- a/drivers/media/video/ivtv/ivtv-controls.c +++ b/drivers/media/video/ivtv/ivtv-controls.c @@ -63,7 +63,7 @@ int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) case V4L2_CID_HUE: case V4L2_CID_SATURATION: case V4L2_CID_CONTRAST: - if (itv->video_dec_func(itv, VIDIOC_QUERYCTRL, qctrl)) + if (v4l2_subdev_call(itv->sd_video, core, queryctrl, qctrl)) qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; return 0; @@ -73,7 +73,7 @@ int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_LOUDNESS: - if (ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl)) + if (v4l2_subdev_call(itv->sd_audio, core, queryctrl, qctrl)) qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; return 0; @@ -122,7 +122,7 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) case V4L2_CID_HUE: case V4L2_CID_SATURATION: case V4L2_CID_CONTRAST: - return itv->video_dec_func(itv, VIDIOC_S_CTRL, vctrl); + return v4l2_subdev_call(itv->sd_video, core, s_ctrl, vctrl); case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_MUTE: @@ -130,7 +130,7 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_LOUDNESS: - return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl); + return v4l2_subdev_call(itv->sd_audio, core, s_ctrl, vctrl); default: IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); @@ -147,7 +147,7 @@ static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) case V4L2_CID_HUE: case V4L2_CID_SATURATION: case V4L2_CID_CONTRAST: - return itv->video_dec_func(itv, VIDIOC_G_CTRL, vctrl); + return v4l2_subdev_call(itv->sd_video, core, g_ctrl, vctrl); case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_MUTE: @@ -155,7 +155,7 @@ static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_LOUDNESS: - return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl); + return v4l2_subdev_call(itv->sd_audio, core, g_ctrl, vctrl); default: IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); return -EINVAL; @@ -268,7 +268,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1); fmt.fmt.pix.height = itv->params.height; - itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, &fmt); } err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p); if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) @@ -279,7 +279,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) /* The audio clock of the digitizer must match the codec sample rate otherwise you get some very strange effects. */ if (idx < sizeof(freqs)) - ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[idx]); + ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]); return err; } return -EINVAL; diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index b69cc1d..08b7629 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -60,9 +60,6 @@ #include <media/v4l2-chip-ident.h> #include "tuner-xc2028.h" -/* var to keep track of the number of array elements in use */ -int ivtv_cards_active; - /* If you have already X v4l cards, then set this to X. This way the device numbers stay matched. Example: you have a WinTV card without radio and a PVR-350 with. Normally this would give a @@ -70,12 +67,6 @@ int ivtv_cards_active; setting this to 1 you ensure that radio0 is now also radio1. */ int ivtv_first_minor; -/* Master variable for all ivtv info */ -struct ivtv *ivtv_cards[IVTV_MAX_CARDS]; - -/* Protects ivtv_cards_active */ -DEFINE_SPINLOCK(ivtv_cards_lock); - /* add your revision and whatnot here */ static struct pci_device_id ivtv_pci_tbl[] __devinitdata = { {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15, @@ -87,6 +78,9 @@ static struct pci_device_id ivtv_pci_tbl[] __devinitdata = { MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl); +/* ivtv instance counter */ +static atomic_t ivtv_instance = ATOMIC_INIT(0); + /* Parameter declarations */ static int cardtype[IVTV_MAX_CARDS]; static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, @@ -599,9 +593,9 @@ static void ivtv_process_options(struct ivtv *itv) itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers * 1024; itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers * 1024; itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers; - itv->options.cardtype = cardtype[itv->num]; - itv->options.tuner = tuner[itv->num]; - itv->options.radio = radio[itv->num]; + itv->options.cardtype = cardtype[itv->instance]; + itv->options.tuner = tuner[itv->instance]; + itv->options.radio = radio[itv->instance]; itv->options.newi2c = newi2c; if (tunertype < -1 || tunertype > 1) { IVTV_WARN("Invalid tunertype argument, will autodetect instead\n"); @@ -688,7 +682,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) spin_lock_init(&itv->lock); spin_lock_init(&itv->dma_reg_lock); - itv->irq_work_queues = create_singlethread_workqueue(itv->name); + itv->irq_work_queues = create_singlethread_workqueue(itv->device.name); if (itv->irq_work_queues == NULL) { IVTV_ERR("Could not create ivtv workqueue\n"); return -1; @@ -770,12 +764,6 @@ static void __devinit ivtv_init_struct2(struct ivtv *itv) i = 0; itv->active_input = i; itv->audio_input = itv->card->video_inputs[i].audio_index; - if (itv->card->hw_all & IVTV_HW_CX25840) - itv->video_dec_func = ivtv_cx25840; - else if (itv->card->hw_all & IVTV_HW_SAA717X) - itv->video_dec_func = ivtv_saa717x; - else - itv->video_dec_func = ivtv_saa7115; } static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, @@ -788,21 +776,21 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, IVTV_DEBUG_INFO("Enabling pci device\n"); if (pci_enable_device(dev)) { - IVTV_ERR("Can't enable device %d!\n", itv->num); + IVTV_ERR("Can't enable device!\n"); return -EIO; } if (pci_set_dma_mask(dev, 0xffffffff)) { - IVTV_ERR("No suitable DMA available on card %d.\n", itv->num); + IVTV_ERR("No suitable DMA available.\n"); return -EIO; } if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) { - IVTV_ERR("Cannot request encoder memory region on card %d.\n", itv->num); + IVTV_ERR("Cannot request encoder memory region.\n"); return -EIO; } if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE, "ivtv registers")) { - IVTV_ERR("Cannot request register memory region on card %d.\n", itv->num); + IVTV_ERR("Cannot request register memory region.\n"); release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); return -EIO; } @@ -810,7 +798,7 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, if (itv->has_cx23415 && !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE, "ivtv decoder")) { - IVTV_ERR("Cannot request decoder memory region on card %d.\n", itv->num); + IVTV_ERR("Cannot request decoder memory region.\n"); release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); return -EIO; @@ -853,69 +841,11 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, return 0; } -#ifdef MODULE -static u32 ivtv_request_module(struct ivtv *itv, u32 hw, - const char *name, u32 id) -{ - if ((hw & id) == 0) - return hw; - if (request_module(name) != 0) { - IVTV_ERR("Failed to load module %s\n", name); - return hw & ~id; - } - IVTV_DEBUG_INFO("Loaded module %s\n", name); - return hw; -} -#endif - static void ivtv_load_and_init_modules(struct ivtv *itv) { u32 hw = itv->card->hw_all; unsigned i; -#ifdef MODULE - /* load modules */ -#ifdef CONFIG_MEDIA_TUNER_MODULE - hw = ivtv_request_module(itv, hw, "tuner", IVTV_HW_TUNER); -#endif -#ifdef CONFIG_VIDEO_CX25840_MODULE - hw = ivtv_request_module(itv, hw, "cx25840", IVTV_HW_CX25840); -#endif -#ifdef CONFIG_VIDEO_SAA711X_MODULE - hw = ivtv_request_module(itv, hw, "saa7115", IVTV_HW_SAA711X); -#endif -#ifdef CONFIG_VIDEO_SAA7127_MODULE - hw = ivtv_request_module(itv, hw, "saa7127", IVTV_HW_SAA7127); -#endif -#ifdef CONFIG_VIDEO_SAA717X_MODULE - hw = ivtv_request_module(itv, hw, "saa717x", IVTV_HW_SAA717X); -#endif -#ifdef CONFIG_VIDEO_UPD64031A_MODULE - hw = ivtv_request_module(itv, hw, "upd64031a", IVTV_HW_UPD64031A); -#endif -#ifdef CONFIG_VIDEO_UPD64083_MODULE - hw = ivtv_request_module(itv, hw, "upd64083", IVTV_HW_UPD6408X); -#endif -#ifdef CONFIG_VIDEO_MSP3400_MODULE - hw = ivtv_request_module(itv, hw, "msp3400", IVTV_HW_MSP34XX); -#endif -#ifdef CONFIG_VIDEO_VP27SMPX_MODULE - hw = ivtv_request_module(itv, hw, "vp27smpx", IVTV_HW_VP27SMPX); -#endif -#ifdef CONFIG_VIDEO_WM8775_MODULE - hw = ivtv_request_module(itv, hw, "wm8775", IVTV_HW_WM8775); -#endif -#ifdef CONFIG_VIDEO_WM8739_MODULE - hw = ivtv_request_module(itv, hw, "wm8739", IVTV_HW_WM8739); -#endif -#ifdef CONFIG_VIDEO_CS53L32A_MODULE - hw = ivtv_request_module(itv, hw, "cs53l32a", IVTV_HW_CS53L32A); -#endif -#ifdef CONFIG_VIDEO_M52790_MODULE - hw = ivtv_request_module(itv, hw, "m52790", IVTV_HW_M52790); -#endif -#endif - /* check which i2c devices are actually found */ for (i = 0; i < 32; i++) { u32 device = 1 << i; @@ -927,11 +857,21 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) itv->hw_flags |= device; continue; } - ivtv_i2c_register(itv, i); - if (ivtv_i2c_hw_addr(itv, device) > 0) + if (ivtv_i2c_register(itv, i) == 0) itv->hw_flags |= device; } + if (itv->card->hw_all & IVTV_HW_CX25840) + itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840); + else if (itv->card->hw_all & IVTV_HW_SAA717X) + itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA717X); + else if (itv->card->hw_all & IVTV_HW_SAA7114) + itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7114); + else + itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7115); + itv->sd_audio = ivtv_find_hw(itv, itv->card->hw_audio_ctrl); + itv->sd_muxer = ivtv_find_hw(itv, itv->card->hw_muxer); + hw = itv->hw_flags; if (itv->card->type == IVTV_CARD_CX23416GYC) { @@ -949,7 +889,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) /* The crystal frequency of GVMVPRX is 24.576MHz */ crystal_freq.freq = SAA7115_FREQ_24_576_MHZ; crystal_freq.flags = SAA7115_FREQ_FL_UCGC; - itv->video_dec_func(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq); + v4l2_subdev_call(itv->sd_video, video, s_crystal_freq, &crystal_freq); } if (hw & IVTV_HW_CX25840) { @@ -967,7 +907,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) /* determine the exact saa711x model */ itv->hw_flags &= ~IVTV_HW_SAA711X; - ivtv_saa7115(itv, VIDIOC_G_CHIP_IDENT, &v); + ivtv_call_hw(itv, IVTV_HW_SAA711X, core, g_chip_ident, &v); if (v.ident == V4L2_IDENT_SAA7114) { itv->hw_flags |= IVTV_HW_SAA7114; /* VBI is not yet supported by the saa7114 driver. */ @@ -1001,28 +941,20 @@ static int __devinit ivtv_probe(struct pci_dev *dev, int vbi_buf_size; struct ivtv *itv; - spin_lock(&ivtv_cards_lock); - - /* Make sure we've got a place for this card */ - if (ivtv_cards_active == IVTV_MAX_CARDS) { - printk(KERN_ERR "ivtv: Maximum number of cards detected (%d)\n", - ivtv_cards_active); - spin_unlock(&ivtv_cards_lock); - return -ENOMEM; - } - itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC); - if (itv == NULL) { - spin_unlock(&ivtv_cards_lock); + if (itv == NULL) return -ENOMEM; - } - ivtv_cards[ivtv_cards_active] = itv; itv->dev = dev; - itv->num = ivtv_cards_active++; - snprintf(itv->name, sizeof(itv->name), "ivtv%d", itv->num); - IVTV_INFO("Initializing card #%d\n", itv->num); + itv->instance = atomic_inc_return(&ivtv_instance) - 1; - spin_unlock(&ivtv_cards_lock); + retval = v4l2_device_register(&dev->dev, &itv->device); + if (retval) + return retval; + /* "ivtv + PCI ID" is a bit of a mouthful, so use + "ivtv + instance" instead. */ + snprintf(itv->device.name, sizeof(itv->device.name), + "ivtv%d", itv->instance); + IVTV_INFO("Initializing card %d\n", itv->instance); ivtv_process_options(itv); if (itv->options.cardtype == -1) { @@ -1043,8 +975,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev, else if (retval == -ENXIO) goto free_mem; } - /* save itv in the pci struct for later use */ - pci_set_drvdata(dev, itv); /* map io memory */ IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", @@ -1086,7 +1016,9 @@ static int __devinit ivtv_probe(struct pci_dev *dev, goto free_io; } - ivtv_gpio_init(itv); + retval = ivtv_gpio_init(itv); + if (retval) + goto free_io; /* active i2c */ IVTV_DEBUG_INFO("activating i2c...\n"); @@ -1095,8 +1027,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev, goto free_io; } - IVTV_DEBUG_INFO("Active card count: %d.\n", ivtv_cards_active); - if (itv->card->hw_all & IVTV_HW_TVEEPROM) { /* Based on the model number the cardtype may be changed. The PCI IDs are not always reliable. */ @@ -1191,7 +1121,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ setup.tuner_callback = (setup.type == TUNER_XC2028) ? ivtv_reset_tuner_gpio : NULL; - ivtv_call_i2c_clients(itv, TUNER_SET_TYPE_ADDR, &setup); + ivtv_call_all(itv, tuner, s_type_addr, &setup); if (setup.type == TUNER_XC2028) { static struct xc2028_ctrl ctrl = { .fname = XC2028_DEFAULT_FIRMWARE, @@ -1201,7 +1131,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, .tuner = itv->options.tuner, .priv = &ctrl, }; - ivtv_call_i2c_clients(itv, TUNER_SET_CONFIG, &cfg); + ivtv_call_all(itv, tuner, s_config, &cfg); } } @@ -1210,11 +1140,11 @@ static int __devinit ivtv_probe(struct pci_dev *dev, itv->tuner_std = itv->std; if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { - ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std); + ivtv_call_all(itv, video, s_std_output, itv->std); /* Turn off the output signal. The mpeg decoder is not yet active so without this you would get a green image until the mpeg decoder becomes active. */ - ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); } /* clear interrupt mask, effectively disabling interrupts */ @@ -1222,7 +1152,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, /* Register IRQ */ retval = request_irq(itv->dev->irq, ivtv_irq_handler, - IRQF_SHARED | IRQF_DISABLED, itv->name, (void *)itv); + IRQF_SHARED | IRQF_DISABLED, itv->device.name, (void *)itv); if (retval) { IVTV_ERR("Failed to register irq %d\n", retval); goto free_i2c; @@ -1238,7 +1168,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, IVTV_ERR("Error %d registering devices\n", retval); goto free_streams; } - IVTV_INFO("Initialized card #%d: %s\n", itv->num, itv->card_name); + IVTV_INFO("Initialized card: %s\n", itv->card_name); return 0; free_streams: @@ -1261,10 +1191,8 @@ err: retval = -ENODEV; IVTV_ERR("Error %d on initialization\n", retval); - spin_lock(&ivtv_cards_lock); - kfree(ivtv_cards[ivtv_cards_active]); - ivtv_cards[ivtv_cards_active] = NULL; - spin_unlock(&ivtv_cards_lock); + v4l2_device_unregister(&itv->device); + kfree(itv); return retval; } @@ -1304,10 +1232,11 @@ int ivtv_init_on_first_open(struct ivtv *itv) if (itv->card->hw_all & IVTV_HW_CX25840) { struct v4l2_control ctrl; + v4l2_subdev_call(itv->sd_video, core, init, 0); /* CX25840_CID_ENABLE_PVR150_WORKAROUND */ ctrl.id = V4L2_CID_PRIVATE_BASE; ctrl.value = itv->pvr150_workaround; - itv->video_dec_func(itv, VIDIOC_S_CTRL, &ctrl); + v4l2_subdev_call(itv->sd_video, core, s_ctrl, &ctrl); } vf.tuner = 0; @@ -1337,7 +1266,7 @@ int ivtv_init_on_first_open(struct ivtv *itv) /* Turn on the TV-out: ivtv_init_mpeg_decoder() initializes the mpeg decoder so now the saa7127 receives a proper signal. */ - ivtv_saa7127(itv, VIDIOC_STREAMON, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); ivtv_init_mpeg_decoder(itv); } ivtv_s_std(NULL, &fh, &itv->tuner_std); @@ -1362,9 +1291,11 @@ int ivtv_init_on_first_open(struct ivtv *itv) static void ivtv_remove(struct pci_dev *pci_dev) { - struct ivtv *itv = pci_get_drvdata(pci_dev); + struct v4l2_device *dev = dev_get_drvdata(&pci_dev->dev); + struct ivtv *itv = to_ivtv(dev); + int i; - IVTV_DEBUG_INFO("Removing Card #%d\n", itv->num); + IVTV_DEBUG_INFO("Removing card\n"); if (test_bit(IVTV_F_I_INITED, &itv->i_flags)) { /* Stop all captures */ @@ -1377,7 +1308,7 @@ static void ivtv_remove(struct pci_dev *pci_dev) /* Turn off the TV-out */ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) - ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); if (atomic_read(&itv->decoding) > 0) { int type; @@ -1402,6 +1333,8 @@ static void ivtv_remove(struct pci_dev *pci_dev) ivtv_streams_cleanup(itv, 1); ivtv_udma_free(itv); + v4l2_device_unregister(&itv->device); + exit_ivtv_i2c(itv); free_irq(itv->dev->irq, (void *)itv); @@ -1413,8 +1346,11 @@ static void ivtv_remove(struct pci_dev *pci_dev) release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); pci_disable_device(itv->dev); + for (i = 0; i < IVTV_VBI_FRAMES; i++) + kfree(itv->vbi.sliced_mpeg_data[i]); - IVTV_INFO("Removed %s, card #%d\n", itv->card_name, itv->num); + printk(KERN_INFO "ivtv: Removed %s\n", itv->card_name); + kfree(itv); } /* define a pci_driver for card detection */ @@ -1427,54 +1363,36 @@ static struct pci_driver ivtv_pci_driver = { static int module_start(void) { - printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION); - - memset(ivtv_cards, 0, sizeof(ivtv_cards)); + printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION); /* Validate parameters */ if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) { - printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n", + printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n", IVTV_MAX_CARDS - 1); return -1; } if (ivtv_debug < 0 || ivtv_debug > 2047) { ivtv_debug = 0; - printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n"); + printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n"); } if (pci_register_driver(&ivtv_pci_driver)) { - printk(KERN_ERR "ivtv: Error detecting PCI card\n"); + printk(KERN_ERR "ivtv: Error detecting PCI card\n"); return -ENODEV; } - printk(KERN_INFO "ivtv: End initialization\n"); + printk(KERN_INFO "ivtv: End initialization\n"); return 0; } static void module_cleanup(void) { - int i, j; - pci_unregister_driver(&ivtv_pci_driver); - - spin_lock(&ivtv_cards_lock); - for (i = 0; i < ivtv_cards_active; i++) { - if (ivtv_cards[i] == NULL) - continue; - for (j = 0; j < IVTV_VBI_FRAMES; j++) { - kfree(ivtv_cards[i]->vbi.sliced_mpeg_data[j]); - } - kfree(ivtv_cards[i]); - } - spin_unlock(&ivtv_cards_lock); } /* Note: These symbols are exported because they are used by the ivtvfb framebuffer module and an infrared module for the IR-blaster. */ EXPORT_SYMBOL(ivtv_set_irq_mask); -EXPORT_SYMBOL(ivtv_cards_active); -EXPORT_SYMBOL(ivtv_cards); -EXPORT_SYMBOL(ivtv_cards_lock); EXPORT_SYMBOL(ivtv_api); EXPORT_SYMBOL(ivtv_vapi); EXPORT_SYMBOL(ivtv_vapi_result); diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 3733b2a..ce8d9b7 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -61,6 +61,7 @@ #include <linux/dvb/audio.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> #include <media/tuner.h> #include <media/cx2341x.h> @@ -113,9 +114,6 @@ #define IVTV_REG_VPU (0x9058) #define IVTV_REG_APU (0xA064) -/* i2c stuff */ -#define I2C_CLIENTS_MAX 16 - /* debugging */ extern int ivtv_debug; @@ -132,12 +130,10 @@ extern int ivtv_debug; /* Flag to turn on high volume debugging */ #define IVTV_DBGFLG_HIGHVOL (1 << 10) -/* NOTE: extra space before comma in 'itv->num , ## args' is required for - gcc-2.95, otherwise it won't compile. */ #define IVTV_DEBUG(x, type, fmt, args...) \ do { \ if ((x) & ivtv_debug) \ - printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \ + v4l2_info(&itv->device, " " type ": " fmt , ##args); \ } while (0) #define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warn", fmt , ## args) #define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args) @@ -152,8 +148,8 @@ extern int ivtv_debug; #define IVTV_DEBUG_HIGH_VOL(x, type, fmt, args...) \ do { \ - if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \ - printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \ + if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \ + v4l2_info(&itv->device, " " type ": " fmt , ##args); \ } while (0) #define IVTV_DEBUG_HI_WARN(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_WARN, "warn", fmt , ## args) #define IVTV_DEBUG_HI_INFO(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_INFO, "info", fmt , ## args) @@ -167,9 +163,9 @@ extern int ivtv_debug; #define IVTV_DEBUG_HI_YUV(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) /* Standard kernel messages */ -#define IVTV_ERR(fmt, args...) printk(KERN_ERR "ivtv%d: " fmt, itv->num , ## args) -#define IVTV_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args) -#define IVTV_INFO(fmt, args...) printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_ERR(fmt, args...) v4l2_err(&itv->device, fmt , ## args) +#define IVTV_WARN(fmt, args...) v4l2_warn(&itv->device, fmt , ## args) +#define IVTV_INFO(fmt, args...) v4l2_info(&itv->device, fmt , ## args) /* output modes (cx23415 only) */ #define OUT_NONE 0 @@ -596,8 +592,6 @@ struct ivtv_card; /* Struct to hold info about ivtv cards */ struct ivtv { /* General fixed card data */ - int num; /* board number, -1 during init! */ - char name[8]; /* board name for printk and interrupts (e.g. 'ivtv0') */ struct pci_dev *dev; /* PCI device */ const struct ivtv_card *card; /* card information */ const char *card_name; /* full name of the card */ @@ -609,14 +603,18 @@ struct ivtv { u32 v4l2_cap; /* V4L2 capabilities of card */ u32 hw_flags; /* hardware description of the board */ v4l2_std_id tuner_std; /* the norm of the card's tuner (fixed) */ - /* controlling video decoder function */ - int (*video_dec_func)(struct ivtv *, unsigned int, void *); + struct v4l2_subdev *sd_video; /* controlling video decoder subdev */ + struct v4l2_subdev *sd_audio; /* controlling audio subdev */ + struct v4l2_subdev *sd_muxer; /* controlling audio muxer subdev */ u32 base_addr; /* PCI resource base address */ volatile void __iomem *enc_mem; /* pointer to mapped encoder memory */ volatile void __iomem *dec_mem; /* pointer to mapped decoder memory */ volatile void __iomem *reg_mem; /* pointer to mapped registers */ struct ivtv_options options; /* user options */ + struct v4l2_device device; + struct v4l2_subdev sd_gpio; /* GPIO sub-device */ + u16 instance; /* High-level state info */ unsigned long i_flags; /* global ivtv flags */ @@ -676,7 +674,6 @@ struct ivtv { struct i2c_adapter i2c_adap; struct i2c_algo_bit_data i2c_algo; struct i2c_client i2c_client; - struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];/* pointers to all I2C clients */ int i2c_state; /* i2c bit state */ struct mutex i2c_bus_lock; /* lock i2c bus */ @@ -722,11 +719,13 @@ struct ivtv { struct osd_info *osd_info; /* ivtvfb private OSD info */ }; +static inline struct ivtv *to_ivtv(struct v4l2_device *dev) +{ + return container_of(dev, struct ivtv, device); +} + /* Globals */ -extern struct ivtv *ivtv_cards[]; -extern int ivtv_cards_active; extern int ivtv_first_minor; -extern spinlock_t ivtv_cards_lock; /*==============Prototypes==================*/ @@ -786,4 +785,19 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv) #define write_dec_sync(val, addr) \ do { write_dec(val, addr); read_dec(addr); } while (0) +/* Call the specified callback for all subdevs matching hw (if 0, then + match them all). Ignore any errors. */ +#define ivtv_call_hw(itv, hw, o, f, args...) \ + __v4l2_device_call_subdevs(&(itv)->device, !(hw) || (sd->grp_id & (hw)), o, f , ##args) + +#define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args) + +/* Call the specified callback for all subdevs matching hw (if 0, then + match them all). If the callback returns an error other than 0 or + -ENOIOCTLCMD, then return with that error code. */ +#define ivtv_call_hw_err(itv, hw, o, f, args...) \ + __v4l2_device_call_subdevs_until_err(&(itv)->device, !(hw) || (sd->grp_id & (hw)), o, f , ##args) + +#define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args) + #endif diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index 1c404e4..5eb5875 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -155,7 +155,7 @@ static void ivtv_dualwatch(struct ivtv *itv) new_stereo_mode = itv->params.audio_properties & stereo_mask; memset(&vt, 0, sizeof(vt)); - ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, &vt); + ivtv_call_all(itv, tuner, g_tuner, &vt); if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2)) new_stereo_mode = dual; @@ -857,7 +857,7 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp) /* Mark that the radio is no longer in use */ clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); /* Switch tuner to TV */ - ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std); + ivtv_call_all(itv, tuner, s_std, itv->std); /* Select correct audio input (i.e. TV tuner or Line in) */ ivtv_audio_set_io(itv); if (itv->hw_flags & IVTV_HW_SAA711X) @@ -865,7 +865,7 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp) struct v4l2_crystal_freq crystal_freq; crystal_freq.freq = SAA7115_FREQ_32_11_MHZ; crystal_freq.flags = 0; - ivtv_saa7115(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq); + ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, &crystal_freq); } if (atomic_read(&itv->capturing) > 0) { /* Undo video mute */ @@ -952,15 +952,14 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) /* We have the radio */ ivtv_mute(itv); /* Switch tuner to radio */ - ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL); + ivtv_call_all(itv, tuner, s_radio); /* Select the correct audio input (i.e. radio tuner) */ ivtv_audio_set_io(itv); - if (itv->hw_flags & IVTV_HW_SAA711X) - { + if (itv->hw_flags & IVTV_HW_SAA711X) { struct v4l2_crystal_freq crystal_freq; crystal_freq.freq = SAA7115_FREQ_32_11_MHZ; crystal_freq.flags = SAA7115_FREQ_FL_APLL; - ivtv_saa7115(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq); + ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, &crystal_freq); } /* Done! Unmute and continue. */ ivtv_unmute(itv); @@ -981,37 +980,18 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) int ivtv_v4l2_open(struct inode *inode, struct file *filp) { - int res, x, y = 0; + int res; struct ivtv *itv = NULL; struct ivtv_stream *s = NULL; - int minor = iminor(inode); - - /* Find which card this open was on */ - spin_lock(&ivtv_cards_lock); - for (x = 0; itv == NULL && x < ivtv_cards_active; x++) { - if (ivtv_cards[x] == NULL) - continue; - /* find out which stream this open was on */ - for (y = 0; y < IVTV_MAX_STREAMS; y++) { - s = &ivtv_cards[x]->streams[y]; - if (s->v4l2dev && s->v4l2dev->minor == minor) { - itv = ivtv_cards[x]; - break; - } - } - } - spin_unlock(&ivtv_cards_lock); + struct video_device *vdev = video_devdata(filp); - if (itv == NULL) { - /* Couldn't find a device registered - on that minor, shouldn't happen! */ - printk(KERN_WARNING "No ivtv device found on minor %d\n", minor); - return -ENXIO; - } + s = video_get_drvdata(vdev); + itv = s->itv; mutex_lock(&itv->serialize_lock); if (ivtv_init_on_first_open(itv)) { - IVTV_ERR("Failed to initialize on minor %d\n", minor); + IVTV_ERR("Failed to initialize on minor %d\n", + s->v4l2dev->minor); mutex_unlock(&itv->serialize_lock); return -ENXIO; } diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c index 74a4484..dc2850e 100644 --- a/drivers/media/video/ivtv/ivtv-gpio.c +++ b/drivers/media/video/ivtv/ivtv-gpio.c @@ -144,22 +144,9 @@ int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value) return 0; } -void ivtv_gpio_init(struct ivtv *itv) +static inline struct ivtv *sd_to_ivtv(struct v4l2_subdev *sd) { - u16 pin = 0; - - if (itv->card->xceive_pin) - pin = 1 << itv->card->xceive_pin; - - if ((itv->card->gpio_init.direction | pin) == 0) - return; - - IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n", - read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT)); - - /* init output data then direction */ - write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT); - write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR); + return container_of(sd, struct ivtv, sd_gpio); } static struct v4l2_queryctrl gpio_ctrl_mute = { @@ -173,134 +160,231 @@ static struct v4l2_queryctrl gpio_ctrl_mute = { .flags = 0, }; -int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg) +static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { - struct v4l2_tuner *tuner = arg; - struct v4l2_control *ctrl = arg; - struct v4l2_routing *route = arg; + struct ivtv *itv = sd_to_ivtv(sd); u16 mask, data; - switch (command) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - mask = itv->card->gpio_audio_freq.mask; - switch (*(u32 *)arg) { - case 32000: - data = itv->card->gpio_audio_freq.f32000; - break; - case 44100: - data = itv->card->gpio_audio_freq.f44100; - break; - case 48000: - default: - data = itv->card->gpio_audio_freq.f48000; - break; - } + mask = itv->card->gpio_audio_freq.mask; + switch (freq) { + case 32000: + data = itv->card->gpio_audio_freq.f32000; + break; + case 44100: + data = itv->card->gpio_audio_freq.f44100; + break; + case 48000: + default: + data = itv->card->gpio_audio_freq.f48000; break; + } + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} - case VIDIOC_G_TUNER: - mask = itv->card->gpio_audio_detect.mask; - if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask)) - tuner->rxsubchans = V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; - else - tuner->rxsubchans = V4L2_TUNER_SUB_MONO; - return 0; +static int subdev_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask; + + mask = itv->card->gpio_audio_detect.mask; + if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask)) + vt->rxsubchans = V4L2_TUNER_MODE_STEREO | + V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + else + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + return 0; +} - case VIDIOC_S_TUNER: - mask = itv->card->gpio_audio_mode.mask; - switch (tuner->audmode) { - case V4L2_TUNER_MODE_LANG1: - data = itv->card->gpio_audio_mode.lang1; - break; - case V4L2_TUNER_MODE_LANG2: - data = itv->card->gpio_audio_mode.lang2; - break; - case V4L2_TUNER_MODE_MONO: - data = itv->card->gpio_audio_mode.mono; - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1_LANG2: - default: - data = itv->card->gpio_audio_mode.stereo; - break; - } - break; +static int subdev_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; - case AUDC_SET_RADIO: - mask = itv->card->gpio_audio_input.mask; - data = itv->card->gpio_audio_input.radio; + mask = itv->card->gpio_audio_mode.mask; + switch (vt->audmode) { + case V4L2_TUNER_MODE_LANG1: + data = itv->card->gpio_audio_mode.lang1; + break; + case V4L2_TUNER_MODE_LANG2: + data = itv->card->gpio_audio_mode.lang2; + break; + case V4L2_TUNER_MODE_MONO: + data = itv->card->gpio_audio_mode.mono; break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: + default: + data = itv->card->gpio_audio_mode.stereo; + break; + } + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} + +static int subdev_s_radio(struct v4l2_subdev *sd) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; + + mask = itv->card->gpio_audio_input.mask; + data = itv->card->gpio_audio_input.radio; + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} - case VIDIOC_S_STD: - mask = itv->card->gpio_audio_input.mask; +static int subdev_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; + + mask = itv->card->gpio_audio_input.mask; + data = itv->card->gpio_audio_input.tuner; + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} + +static int subdev_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; + + if (route->input > 2) + return -EINVAL; + mask = itv->card->gpio_audio_input.mask; + switch (route->input) { + case 0: data = itv->card->gpio_audio_input.tuner; break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - if (route->input > 2) - return -EINVAL; - mask = itv->card->gpio_audio_input.mask; - switch (route->input) { - case 0: - data = itv->card->gpio_audio_input.tuner; - break; - case 1: - data = itv->card->gpio_audio_input.linein; - break; - case 2: - default: - data = itv->card->gpio_audio_input.radio; - break; - } + case 1: + data = itv->card->gpio_audio_input.linein; + break; + case 2: + default: + data = itv->card->gpio_audio_input.radio; break; + } + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} - case VIDIOC_G_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - mask = itv->card->gpio_audio_mute.mask; - data = itv->card->gpio_audio_mute.mute; - ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data; - return 0; +static int subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; - case VIDIOC_S_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - mask = itv->card->gpio_audio_mute.mask; - data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0; - break; + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + mask = itv->card->gpio_audio_mute.mask; + data = itv->card->gpio_audio_mute.mute; + ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data; + return 0; +} - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; +static int subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; - if (qc->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - *qc = gpio_ctrl_mute; - return 0; - } + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + mask = itv->card->gpio_audio_mute.mask; + data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0; + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} + +static int subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + if (qc->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + *qc = gpio_ctrl_mute; + return 0; +} + +static int subdev_log_status(struct v4l2_subdev *sd) +{ + struct ivtv *itv = sd_to_ivtv(sd); - case VIDIOC_LOG_STATUS: - IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n", + IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n", read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT), read_reg(IVTV_REG_GPIO_IN)); - return 0; + return 0; +} - case VIDIOC_INT_S_VIDEO_ROUTING: - if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */ - return -EINVAL; - mask = itv->card->gpio_video_input.mask; - if (route->input == 0) - data = itv->card->gpio_video_input.tuner; - else if (route->input == 1) - data = itv->card->gpio_video_input.composite; - else - data = itv->card->gpio_video_input.svideo; - break; +static int subdev_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; - default: + if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */ return -EINVAL; - } + mask = itv->card->gpio_video_input.mask; + if (route->input == 0) + data = itv->card->gpio_video_input.tuner; + else if (route->input == 1) + data = itv->card->gpio_video_input.composite; + else + data = itv->card->gpio_video_input.svideo; if (mask) write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); return 0; } + +static const struct v4l2_subdev_core_ops subdev_core_ops = { + .log_status = subdev_log_status, + .g_ctrl = subdev_g_ctrl, + .s_ctrl = subdev_s_ctrl, + .queryctrl = subdev_queryctrl, +}; + +static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = { + .s_std = subdev_s_std, + .s_radio = subdev_s_radio, + .g_tuner = subdev_g_tuner, + .s_tuner = subdev_s_tuner, +}; + +static const struct v4l2_subdev_audio_ops subdev_audio_ops = { + .s_clock_freq = subdev_s_clock_freq, + .s_routing = subdev_s_audio_routing, +}; + +static const struct v4l2_subdev_video_ops subdev_video_ops = { + .s_routing = subdev_s_video_routing, +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &subdev_core_ops, + .tuner = &subdev_tuner_ops, + .audio = &subdev_audio_ops, + .video = &subdev_video_ops, +}; + +int ivtv_gpio_init(struct ivtv *itv) +{ + u16 pin = 0; + + if (itv->card->xceive_pin) + pin = 1 << itv->card->xceive_pin; + + if ((itv->card->gpio_init.direction | pin) == 0) + return 0; + + IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n", + read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT)); + + /* init output data then direction */ + write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT); + write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR); + v4l2_subdev_init(&itv->sd_gpio, &subdev_ops); + snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->device.name); + itv->sd_gpio.grp_id = IVTV_HW_GPIO; + return v4l2_device_register_subdev(&itv->device, &itv->sd_gpio); +} diff --git a/drivers/media/video/ivtv/ivtv-gpio.h b/drivers/media/video/ivtv/ivtv-gpio.h index 48b6291..0b5d19c 100644 --- a/drivers/media/video/ivtv/ivtv-gpio.h +++ b/drivers/media/video/ivtv/ivtv-gpio.h @@ -22,9 +22,8 @@ #define IVTV_GPIO_H /* GPIO stuff */ -void ivtv_gpio_init(struct ivtv *itv); +int ivtv_gpio_init(struct ivtv *itv); void ivtv_reset_ir_gpio(struct ivtv *itv); int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value); -int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg); #endif diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index 41dbbe9..ca1d955 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -90,26 +90,6 @@ #define IVTV_M52790_I2C_ADDR 0x48 /* This array should match the IVTV_HW_ defines */ -static const u8 hw_driverids[] = { - I2C_DRIVERID_CX25840, - I2C_DRIVERID_SAA711X, - I2C_DRIVERID_SAA7127, - I2C_DRIVERID_MSP3400, - I2C_DRIVERID_TUNER, - I2C_DRIVERID_WM8775, - I2C_DRIVERID_CS53L32A, - I2C_DRIVERID_TVEEPROM, - I2C_DRIVERID_SAA711X, - I2C_DRIVERID_UPD64031A, - I2C_DRIVERID_UPD64083, - I2C_DRIVERID_SAA717X, - I2C_DRIVERID_WM8739, - I2C_DRIVERID_VP27SMPX, - I2C_DRIVERID_M52790, - 0 /* IVTV_HW_GPIO dummy driver ID */ -}; - -/* This array should match the IVTV_HW_ defines */ static const u8 hw_addrs[] = { IVTV_CX25840_I2C_ADDR, IVTV_SAA7115_I2C_ADDR, @@ -130,6 +110,26 @@ static const u8 hw_addrs[] = { }; /* This array should match the IVTV_HW_ defines */ +static const char *hw_modules[] = { + "cx25840", + "saa7115", + "saa7127", + "msp3400", + "tuner", + "wm8775", + "cs53l32a", + NULL, + "saa7115", + "upd64031a", + "upd64083", + "saa717x", + "wm8739", + "vp27smpx", + "m52790", + NULL +}; + +/* This array should match the IVTV_HW_ defines */ static const char * const hw_devicenames[] = { "cx25840", "saa7115", @@ -151,80 +151,58 @@ static const char * const hw_devicenames[] = { int ivtv_i2c_register(struct ivtv *itv, unsigned idx) { - struct i2c_board_info info; - struct i2c_client *c; - u8 id; - int i; + struct v4l2_subdev *sd; + struct i2c_adapter *adap = &itv->i2c_adap; + const char *mod = hw_modules[idx]; + const char *type = hw_devicenames[idx]; + u32 hw = 1 << idx; - IVTV_DEBUG_I2C("i2c client register\n"); - if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0) + if (idx >= ARRAY_SIZE(hw_addrs)) return -1; - id = hw_driverids[idx]; - memset(&info, 0, sizeof(info)); - strlcpy(info.type, hw_devicenames[idx], sizeof(info.type)); - info.addr = hw_addrs[idx]; - for (i = 0; itv->i2c_clients[i] && i < I2C_CLIENTS_MAX; i++) {} - - if (i == I2C_CLIENTS_MAX) { - IVTV_ERR("insufficient room for new I2C client!\n"); - return -ENOMEM; + if (hw == IVTV_HW_TUNER) { + /* special tuner handling */ + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + itv->card_i2c->radio); + if (sd) + sd->grp_id = 1 << idx; + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + itv->card_i2c->demod); + if (sd) + sd->grp_id = 1 << idx; + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + itv->card_i2c->tv); + if (sd) + sd->grp_id = 1 << idx; + return sd ? 0 : -1; } + if (!hw_addrs[idx]) + return -1; + if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) { + unsigned short addrs[2] = { hw_addrs[idx], I2C_CLIENT_END }; - if (id != I2C_DRIVERID_TUNER) { - if (id == I2C_DRIVERID_UPD64031A || - id == I2C_DRIVERID_UPD64083) { - unsigned short addrs[2] = { info.addr, I2C_CLIENT_END }; - - c = i2c_new_probed_device(&itv->i2c_adap, &info, addrs); - } else - c = i2c_new_device(&itv->i2c_adap, &info); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - itv->i2c_clients[i] = c; - return itv->i2c_clients[i] ? 0 : -ENODEV; + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, addrs); + } else { + sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]); } - - /* special tuner handling */ - c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->radio); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - itv->i2c_clients[i++] = c; - c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->demod); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - itv->i2c_clients[i++] = c; - c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->tv); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - itv->i2c_clients[i++] = c; - return 0; -} - -static int attach_inform(struct i2c_client *client) -{ - return 0; + if (sd) + sd->grp_id = 1 << idx; + return sd ? 0 : -1; } -static int detach_inform(struct i2c_client *client) +struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw) { - int i; - struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter); + struct v4l2_subdev *result = NULL; + struct v4l2_subdev *sd; - IVTV_DEBUG_I2C("i2c client detach\n"); - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - if (itv->i2c_clients[i] == client) { - itv->i2c_clients[i] = NULL; + spin_lock(&itv->device.lock); + v4l2_device_for_each_subdev(sd, &itv->device) { + if (sd->grp_id == hw) { + result = sd; break; } } - IVTV_DEBUG_I2C("i2c detach [client=%s,%s]\n", - client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); - - return 0; + spin_unlock(&itv->device.lock); + return result; } /* Set the serial clock line to the desired state */ @@ -494,7 +472,8 @@ static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data, intervening stop condition */ static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) { - struct ivtv *itv = i2c_get_adapdata(i2c_adap); + struct v4l2_device *drv = i2c_get_adapdata(i2c_adap); + struct ivtv *itv = to_ivtv(drv); int retval; int i; @@ -530,8 +509,6 @@ static struct i2c_adapter ivtv_i2c_adap_hw_template = { .id = I2C_HW_B_CX2341X, .algo = &ivtv_algo, .algo_data = NULL, /* filled from template */ - .client_register = attach_inform, - .client_unregister = detach_inform, .owner = THIS_MODULE, }; @@ -583,8 +560,6 @@ static struct i2c_adapter ivtv_i2c_adap_template = { .id = I2C_HW_B_CX2341X, .algo = NULL, /* set by i2c-algo-bit */ .algo_data = NULL, /* filled from template */ - .client_register = attach_inform, - .client_unregister = detach_inform, .owner = THIS_MODULE, }; @@ -601,160 +576,6 @@ static struct i2c_client ivtv_i2c_client_template = { .name = "ivtv internal", }; -int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg) -{ - struct i2c_client *client; - int retval; - int i; - - IVTV_DEBUG_I2C("call_i2c_client addr=%02x\n", addr); - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - client = itv->i2c_clients[i]; - if (client == NULL || client->driver == NULL || - client->driver->command == NULL) - continue; - if (addr == client->addr) { - retval = client->driver->command(client, cmd, arg); - return retval; - } - } - if (cmd != VIDIOC_G_CHIP_IDENT) - IVTV_ERR("i2c addr 0x%02x not found for command 0x%x\n", addr, cmd); - return -ENODEV; -} - -/* Find the i2c device based on the driver ID and return - its i2c address or -ENODEV if no matching device was found. */ -static int ivtv_i2c_id_addr(struct ivtv *itv, u32 id) -{ - struct i2c_client *client; - int retval = -ENODEV; - int i; - - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - client = itv->i2c_clients[i]; - if (client == NULL || client->driver == NULL) - continue; - if (id == client->driver->id) { - retval = client->addr; - break; - } - } - return retval; -} - -/* Find the i2c device name matching the DRIVERID */ -static const char *ivtv_i2c_id_name(u32 id) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) - if (hw_driverids[i] == id) - return hw_devicenames[i]; - return "unknown device"; -} - -/* Find the i2c device name matching the IVTV_HW_ flag */ -static const char *ivtv_i2c_hw_name(u32 hw) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) - if (1 << i == hw) - return hw_devicenames[i]; - return "unknown device"; -} - -/* Find the i2c device matching the IVTV_HW_ flag and return - its i2c address or -ENODEV if no matching device was found. */ -int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) - if (1 << i == hw) - return ivtv_i2c_id_addr(itv, hw_driverids[i]); - return -ENODEV; -} - -/* Calls i2c device based on IVTV_HW_ flag. If hw == 0, then do nothing. - If hw == IVTV_HW_GPIO then call the gpio handler. */ -int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg) -{ - int addr; - - if (hw == IVTV_HW_GPIO) - return ivtv_gpio(itv, cmd, arg); - if (hw == 0) - return 0; - - addr = ivtv_i2c_hw_addr(itv, hw); - if (addr < 0) { - IVTV_ERR("i2c hardware 0x%08x (%s) not found for command 0x%x\n", - hw, ivtv_i2c_hw_name(hw), cmd); - return addr; - } - return ivtv_call_i2c_client(itv, addr, cmd, arg); -} - -/* Calls i2c device based on I2C driver ID. */ -int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg) -{ - int addr; - - addr = ivtv_i2c_id_addr(itv, id); - if (addr < 0) { - if (cmd != VIDIOC_G_CHIP_IDENT) - IVTV_ERR("i2c ID 0x%08x (%s) not found for command 0x%x\n", - id, ivtv_i2c_id_name(id), cmd); - return addr; - } - return ivtv_call_i2c_client(itv, addr, cmd, arg); -} - -int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_CX25840_I2C_ADDR, cmd, arg); -} - -int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR, cmd, arg); -} - -int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_SAA7127_I2C_ADDR, cmd, arg); -} -EXPORT_SYMBOL(ivtv_saa7127); - -int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_SAA717x_I2C_ADDR, cmd, arg); -} - -int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_UPD64031A_I2C_ADDR, cmd, arg); -} - -int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_UPD64083_I2C_ADDR, cmd, arg); -} - -/* broadcast cmd for all I2C clients and for the gpio subsystem */ -void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg) -{ - if (itv->i2c_adap.algo == NULL) { - IVTV_ERR("Adapter is not set"); - return; - } - i2c_clients_command(&itv->i2c_adap, cmd, arg); - if (itv->hw_flags & IVTV_HW_GPIO) - ivtv_gpio(itv, cmd, arg); -} - /* init + register i2c algo-bit adapter */ int init_ivtv_i2c(struct ivtv *itv) { @@ -763,10 +584,9 @@ int init_ivtv_i2c(struct ivtv *itv) /* Sanity checks for the I2C hardware arrays. They must be the * same size and GPIO must be the last entry. */ - if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) || - ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) || - IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1)) || - hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) { + if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) || + ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules) || + IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1))) { IVTV_ERR("Mismatched I2C hardware arrays\n"); return -ENODEV; } @@ -783,8 +603,8 @@ int init_ivtv_i2c(struct ivtv *itv) itv->i2c_adap.algo_data = &itv->i2c_algo; sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d", - itv->num); - i2c_set_adapdata(&itv->i2c_adap, itv); + itv->instance); + i2c_set_adapdata(&itv->i2c_adap, &itv->device); memcpy(&itv->i2c_client, &ivtv_i2c_client_template, sizeof(struct i2c_client)); diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h index 022978c..396928a 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.h +++ b/drivers/media/video/ivtv/ivtv-i2c.h @@ -21,19 +21,8 @@ #ifndef IVTV_I2C_H #define IVTV_I2C_H -int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg); - -int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw); -int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg); -int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg); -int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg); -void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg); int ivtv_i2c_register(struct ivtv *itv, unsigned idx); +struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw); /* init + register i2c algo-bit adapter */ int init_ivtv_i2c(struct ivtv *itv); diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 4bae38d..cd990a4 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -393,7 +393,7 @@ static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo return 0; } - itv->video_dec_func(itv, VIDIOC_G_FMT, fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); vbifmt->service_set = ivtv_get_service_set(vbifmt); return 0; } @@ -581,7 +581,7 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f p->height = h; if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) fmt->fmt.pix.width /= 2; - itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); return ivtv_g_fmt_vid_cap(file, fh, fmt); } @@ -593,7 +593,7 @@ static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f return -EBUSY; itv->vbi.sliced_in->service_set = 0; itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; - itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); return ivtv_g_fmt_vbi_cap(file, fh, fmt); } @@ -611,7 +611,7 @@ static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo if (ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0) return -EBUSY; itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in)); return 0; } @@ -685,18 +685,17 @@ static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_chip_ident chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416; return 0; } - if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return ivtv_i2c_id(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip); - if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR) - return ivtv_call_i2c_client(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip); - return -EINVAL; + if (chip->match_type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + /* TODO: is this correct? */ + return ivtv_call_all_err(itv, core, g_chip_ident, chip); } #ifdef CONFIG_VIDEO_ADV_DEBUG static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) { struct v4l2_register *regs = arg; - unsigned long flags; volatile u8 __iomem *reg_start; if (!capable(CAP_SYS_ADMIN)) @@ -711,12 +710,10 @@ static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) else return -EINVAL; - spin_lock_irqsave(&ivtv_cards_lock, flags); if (cmd == VIDIOC_DBG_G_REGISTER) regs->val = readl(regs->reg + reg_start); else writel(regs->val, regs->reg + reg_start); - spin_unlock_irqrestore(&ivtv_cards_lock, flags); return 0; } @@ -726,9 +723,10 @@ static int ivtv_g_register(struct file *file, void *fh, struct v4l2_register *re if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg); - if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg); - return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg); + /* TODO: subdev errors should not be ignored, this should become a + subdev helper function. */ + ivtv_call_all(itv, core, g_register, reg); + return 0; } static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *reg) @@ -737,9 +735,10 @@ static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *re if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg); - if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg); - return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg); + /* TODO: subdev errors should not be ignored, this should become a + subdev helper function. */ + ivtv_call_all(itv, core, s_register, reg); + return 0; } #endif @@ -884,12 +883,6 @@ static int ivtv_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) streamtype = id->type; - if (ivtv_debug & IVTV_DBGFLG_IOCTL) { - printk(KERN_INFO "ivtv%d ioctl: ", itv->num); - /* Should be replaced */ - /* v4l_printk_ioctl(VIDIOC_S_CROP); */ - } - if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { @@ -1050,7 +1043,7 @@ static int ivtv_s_output(struct file *file, void *fh, unsigned int outp) itv->active_output = outp; route.input = SAA7127_INPUT_TYPE_NORMAL; route.output = itv->card->video_outputs[outp].video_output; - ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, &route); return 0; } @@ -1062,7 +1055,7 @@ static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency * if (vf->tuner != 0) return -EINVAL; - ivtv_call_i2c_clients(itv, VIDIOC_G_FREQUENCY, vf); + ivtv_call_all(itv, tuner, g_frequency, vf); return 0; } @@ -1075,7 +1068,7 @@ int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) ivtv_mute(itv); IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency); - ivtv_call_i2c_clients(itv, VIDIOC_S_FREQUENCY, vf); + ivtv_call_all(itv, tuner, s_frequency, vf); ivtv_unmute(itv); return 0; } @@ -1123,14 +1116,14 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) IVTV_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)itv->std); /* Tuner */ - ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std); + ivtv_call_all(itv, tuner, s_std, itv->std); if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { /* set display standard */ itv->std_out = *std; itv->is_out_60hz = itv->is_60hz; itv->is_out_50hz = itv->is_50hz; - ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out); + ivtv_call_all(itv, video, s_std_output, itv->std_out); ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz); itv->main_rect.left = itv->main_rect.top = 0; itv->main_rect.width = 720; @@ -1154,7 +1147,7 @@ static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) if (vt->index != 0) return -EINVAL; - ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt); + ivtv_call_all(itv, tuner, s_tuner, vt); return 0; } @@ -1166,7 +1159,7 @@ static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) if (vt->index != 0) return -EINVAL; - ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt); + ivtv_call_all(itv, tuner, g_tuner, vt); if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name)); @@ -1444,14 +1437,15 @@ static int ivtv_log_status(struct file *file, void *fh) struct v4l2_audio audin; int i; - IVTV_INFO("================= START STATUS CARD #%d =================\n", itv->num); + IVTV_INFO("================= START STATUS CARD #%d =================\n", + itv->instance); IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name); if (itv->hw_flags & IVTV_HW_TVEEPROM) { struct tveeprom tv; ivtv_read_eeprom(itv, &tv); } - ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL); + ivtv_call_all(itv, core, log_status); ivtv_get_input(itv, itv->active_input, &vidin); ivtv_get_audio_input(itv, itv->audio_input, &audin); IVTV_INFO("Video Input: %s\n", vidin.name); @@ -1518,7 +1512,7 @@ static int ivtv_log_status(struct file *file, void *fh) } IVTV_INFO("Tuner: %s\n", test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV"); - cx2341x_log_status(&itv->params, itv->name); + cx2341x_log_status(&itv->params, itv->device.name); IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags); for (i = 0; i < IVTV_MAX_STREAMS; i++) { struct ivtv_stream *s = &itv->streams[i]; @@ -1530,8 +1524,11 @@ static int ivtv_log_status(struct file *file, void *fh) (s->buffers * s->buf_size) / 1024, s->buffers); } - IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", (long long)itv->mpg_data_received, (long long)itv->vbi_data_inserted); - IVTV_INFO("================== END STATUS CARD #%d ==================\n", itv->num); + IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", + (long long)itv->mpg_data_received, + (long long)itv->vbi_data_inserted); + IVTV_INFO("================== END STATUS CARD #%d ==================\n", + itv->instance); return 0; } @@ -1736,7 +1733,7 @@ static int ivtv_default(struct file *file, void *fh, int cmd, void *arg) case VIDIOC_INT_S_AUDIO_ROUTING: { struct v4l2_routing *route = arg; - ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route); + ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, route); break; } @@ -1746,7 +1743,7 @@ static int ivtv_default(struct file *file, void *fh, int cmd, void *arg) if ((val == 0 && itv->options.newi2c) || (val & 0x01)) ivtv_reset_ir_gpio(itv); if (val & 0x02) - itv->video_dec_func(itv, cmd, NULL); + v4l2_subdev_call(itv->sd_video, core, reset, 0); break; } diff --git a/drivers/media/video/ivtv/ivtv-routing.c b/drivers/media/video/ivtv/ivtv-routing.c index 0556491..3fd3022 100644 --- a/drivers/media/video/ivtv/ivtv-routing.c +++ b/drivers/media/video/ivtv/ivtv-routing.c @@ -47,13 +47,13 @@ void ivtv_audio_set_io(struct ivtv *itv) route.output = 0; if (itv->card->hw_muxer & IVTV_HW_M52790) route.output = M52790_OUT_STEREO; - ivtv_i2c_hw(itv, itv->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route); + v4l2_subdev_call(itv->sd_muxer, audio, s_routing, &route); route.input = in->audio_input; route.output = 0; if (itv->card->hw_audio & IVTV_HW_MSP34XX) route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); - ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, &route); + ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, &route); } /* Selects the video input and output according to the current @@ -66,7 +66,7 @@ void ivtv_video_set_io(struct ivtv *itv) route.input = itv->card->video_inputs[inp].video_input; route.output = 0; - itv->video_dec_func(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + v4l2_subdev_call(itv->sd_video, video, s_routing, &route); type = itv->card->video_inputs[inp].video_type; @@ -79,7 +79,7 @@ void ivtv_video_set_io(struct ivtv *itv) } if (itv->card->hw_video & IVTV_HW_GPIO) - ivtv_gpio(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + ivtv_call_hw(itv, IVTV_HW_GPIO, video, s_routing, &route); if (itv->card->hw_video & IVTV_HW_UPD64031A) { if (type == IVTV_CARD_INPUT_VID_TUNER || @@ -92,7 +92,7 @@ void ivtv_video_set_io(struct ivtv *itv) } route.input |= itv->card->gr_config; - ivtv_upd64031a(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + ivtv_call_hw(itv, IVTV_HW_UPD64031A, video, s_routing, &route); } if (itv->card->hw_video & IVTV_HW_UPD6408X) { @@ -110,6 +110,6 @@ void ivtv_video_set_io(struct ivtv *itv) route.input |= UPD64083_EXT_Y_ADC; } } - ivtv_upd64083(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + ivtv_call_hw(itv, IVTV_HW_UPD6408X, video, s_routing, &route); } } diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index 9b7aa79..f77d764 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -172,7 +172,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) { struct ivtv_stream *s = &itv->streams[type]; int num_offset = ivtv_stream_info[type].num_offset; - int num = itv->num + ivtv_first_minor + num_offset; + int num = itv->instance + ivtv_first_minor + num_offset; /* These four fields are always initialized. If v4l2dev == NULL, then this stream is not in use. In that case no other fields but these @@ -205,11 +205,11 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) return -ENOMEM; } - snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s", - itv->num, s->name); + snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "%s %s", + itv->device.name, s->name); s->v4l2dev->num = num; - s->v4l2dev->parent = &itv->dev->dev; + s->v4l2dev->v4l2_dev = &itv->device; s->v4l2dev->fops = ivtv_stream_info[type].fops; s->v4l2dev->release = video_device_release; s->v4l2dev->tvnorms = V4L2_STD_ALL; @@ -260,6 +260,7 @@ static int ivtv_reg_dev(struct ivtv *itv, int type) if (s_mpg->v4l2dev) num = s_mpg->v4l2dev->num + ivtv_stream_info[type].num_offset; } + video_set_drvdata(s->v4l2dev, s); /* Register device. First try the desired minor, then any free one. */ if (video_register_device(s->v4l2dev, vfl_type, num)) { @@ -343,7 +344,7 @@ static void ivtv_vbi_setup(struct ivtv *itv) ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0); /* setup VBI registers */ - itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in); + v4l2_subdev_call(itv->sd_video, video, s_fmt, &itv->vbi.in); /* determine number of lines and total number of VBI bytes. A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1 @@ -577,10 +578,10 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) clear_bit(IVTV_F_I_EOS, &itv->i_flags); /* Initialize Digitizer for Capture */ - itv->video_dec_func(itv, VIDIOC_STREAMOFF, NULL); + v4l2_subdev_call(itv->sd_video, video, s_stream, 0); ivtv_msleep_timeout(300, 1); ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); - itv->video_dec_func(itv, VIDIOC_STREAMON, NULL); + v4l2_subdev_call(itv->sd_video, video, s_stream, 1); } /* begin_capture */ diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c index 4a37a7d..5c5d1c4 100644 --- a/drivers/media/video/ivtv/ivtv-vbi.c +++ b/drivers/media/video/ivtv/ivtv-vbi.c @@ -21,6 +21,7 @@ #include "ivtv-i2c.h" #include "ivtv-ioctl.h" #include "ivtv-queue.h" +#include "ivtv-cards.h" #include "ivtv-vbi.h" static void ivtv_set_vps(struct ivtv *itv, int enabled) @@ -37,7 +38,7 @@ static void ivtv_set_vps(struct ivtv *itv, int enabled) data.data[9] = itv->vbi.vps_payload.data[2]; data.data[10] = itv->vbi.vps_payload.data[3]; data.data[11] = itv->vbi.vps_payload.data[4]; - ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); } static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc) @@ -51,12 +52,12 @@ static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc) data.line = (mode & 1) ? 21 : 0; data.data[0] = cc->odd[0]; data.data[1] = cc->odd[1]; - ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); data.field = 1; data.line = (mode & 2) ? 21 : 0; data.data[0] = cc->even[0]; data.data[1] = cc->even[1]; - ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); } static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode) @@ -79,7 +80,7 @@ static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode) data.line = enabled ? 23 : 0; data.data[0] = mode & 0xff; data.data[1] = (mode >> 8) & 0xff; - ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); } static int odd_parity(u8 c) @@ -313,7 +314,7 @@ static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 continue; } vbi.p = p + 4; - itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi); + v4l2_subdev_call(itv->sd_video, video, decode_vbi_line, &vbi); if (vbi.type && !(lines & (1 << vbi.line))) { lines |= 1 << vbi.line; itv->vbi.sliced_data[line].id = vbi.type; @@ -437,7 +438,7 @@ void ivtv_vbi_work_handler(struct ivtv *itv) data.id = V4L2_SLICED_WSS_625; data.field = 0; - if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { ivtv_set_wss(itv, 1, data.data[0] & 0xf); vi->wss_missing_cnt = 0; } else if (vi->wss_missing_cnt == 4) { @@ -451,13 +452,13 @@ void ivtv_vbi_work_handler(struct ivtv *itv) data.id = V4L2_SLICED_CAPTION_525; data.field = 0; - if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { mode |= 1; cc.odd[0] = data.data[0]; cc.odd[1] = data.data[1]; } data.field = 1; - if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { mode |= 2; cc.even[0] = data.data[0]; cc.even[1] = data.data[1]; diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c index 921e281..36abd2a 100644 --- a/drivers/media/video/ivtv/ivtvfb.c +++ b/drivers/media/video/ivtv/ivtvfb.c @@ -48,6 +48,7 @@ #endif #include "ivtv-driver.h" +#include "ivtv-cards.h" #include "ivtv-i2c.h" #include "ivtv-udma.h" #include "ivtv-mailbox.h" @@ -121,15 +122,15 @@ MODULE_LICENSE("GPL"); #define IVTVFB_DEBUG(x, type, fmt, args...) \ do { \ if ((x) & ivtvfb_debug) \ - printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->num , ## args); \ + printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->instance , ## args); \ } while (0) #define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args) #define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args) /* Standard kernel messages */ -#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->num , ## args) -#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->num , ## args) -#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->num , ## args) +#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->instance , ## args) +#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->instance , ## args) +#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->instance , ## args) /* --------------------------------------------------------------------- */ @@ -895,16 +896,16 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info) switch (blank_mode) { case FB_BLANK_UNBLANK: ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1); - ivtv_saa7127(itv, VIDIOC_STREAMON, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); break; case FB_BLANK_NORMAL: case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_VSYNC_SUSPEND: ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); - ivtv_saa7127(itv, VIDIOC_STREAMON, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); break; case FB_BLANK_POWERDOWN: - ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); break; } @@ -1188,10 +1189,45 @@ static int ivtvfb_init_card(struct ivtv *itv) } +static int __init ivtvfb_callback_init(struct device *dev, void *p) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct ivtv *itv = container_of(v4l2_dev, struct ivtv, device); + + if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + if (ivtvfb_init_card(itv) == 0) { + IVTVFB_INFO("Framebuffer registered on %s\n", + itv->device.name); + (*(int *)p)++; + } + } + return 0; +} + +static int ivtvfb_callback_cleanup(struct device *dev, void *p) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct ivtv *itv = container_of(v4l2_dev, struct ivtv, device); + + if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) { + IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", + itv->instance); + return 0; + } + IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance); + ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info); + ivtvfb_release_buffers(itv); + itv->osd_video_pbase = 0; + } + return 0; +} + static int __init ivtvfb_init(void) { - struct ivtv *itv; - int i, registered = 0; + struct device_driver *drv; + int registered = 0; + int err; if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) { printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n", @@ -1199,20 +1235,11 @@ static int __init ivtvfb_init(void) return -EINVAL; } - /* Locate & initialise all cards supporting an OSD. */ - for (i = 0; i < ivtv_cards_active; i++) { - if (ivtvfb_card_id != -1 && i != ivtvfb_card_id) - continue; - itv = ivtv_cards[i]; - if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { - if (ivtvfb_init_card(itv) == 0) { - IVTVFB_INFO("Framebuffer registered on ivtv card id %d\n", i); - registered++; - } - } - } + drv = driver_find("ivtv", &pci_bus_type); + err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init); + put_driver(drv); if (!registered) { - printk(KERN_ERR "ivtvfb: no cards found"); + printk(KERN_ERR "ivtvfb: no cards found\n"); return -ENODEV; } return 0; @@ -1220,24 +1247,14 @@ static int __init ivtvfb_init(void) static void ivtvfb_cleanup(void) { - struct ivtv *itv; - int i; + struct device_driver *drv; + int err; printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n"); - for (i = 0; i < ivtv_cards_active; i++) { - itv = ivtv_cards[i]; - if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) { - if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) { - IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", i); - return; - } - IVTVFB_DEBUG_INFO("Unregister framebuffer %d\n", i); - ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info); - ivtvfb_release_buffers(itv); - itv->osd_video_pbase = 0; - } - } + drv = driver_find("ivtv", &pci_bus_type); + err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup); + put_driver(drv); } module_init(ivtvfb_init); diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c index 89a781c..07be14a 100644 --- a/drivers/media/video/m52790.c +++ b/drivers/media/video/m52790.c @@ -28,7 +28,7 @@ #include <linux/i2c-id.h> #include <linux/videodev2.h> #include <media/m52790.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> @@ -38,89 +38,130 @@ MODULE_LICENSE("GPL"); struct m52790_state { + struct v4l2_subdev sd; u16 input; u16 output; }; +static inline struct m52790_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct m52790_state, sd); +} + /* ----------------------------------------------------------------------- */ -static int m52790_write(struct i2c_client *client) +static int m52790_write(struct v4l2_subdev *sd) { - struct m52790_state *state = i2c_get_clientdata(client); + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 sw1 = (state->input | state->output) & 0xff; u8 sw2 = (state->input | state->output) >> 8; return i2c_smbus_write_byte_data(client, sw1, sw2); } -static int m52790_command(struct i2c_client *client, unsigned int cmd, - void *arg) +/* Note: audio and video are linked and cannot be switched separately. + So audio and video routing commands are identical for this chip. + In theory the video amplifier and audio modes could be handled + separately for the output, but that seems to be overkill right now. + The same holds for implementing an audio mute control, this is now + part of the audio output routing. The normal case is that another + chip takes care of the actual muting so making it part of the + output routing seems to be the right thing to do for now. */ +static int m52790_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct m52790_state *state = i2c_get_clientdata(client); - struct v4l2_routing *route = arg; - - /* Note: audio and video are linked and cannot be switched separately. - So audio and video routing commands are identical for this chip. - In theory the video amplifier and audio modes could be handled - separately for the output, but that seems to be overkill right now. - The same holds for implementing an audio mute control, this is now - part of the audio output routing. The normal case is that another - chip takes care of the actual muting so making it part of the - output routing seems to be the right thing to do for now. */ - switch (cmd) { - case VIDIOC_INT_G_AUDIO_ROUTING: - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = state->input; - route->output = state->output; - break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - case VIDIOC_INT_S_VIDEO_ROUTING: - state->input = route->input; - state->output = route->output; - m52790_write(client); - break; + struct m52790_state *state = to_state(sd); + + state->input = route->input; + state->output = route->output; + m52790_write(sd); + return 0; +} #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (reg->reg != 0) - return -EINVAL; - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = state->input | state->output; - else { - state->input = reg->val & 0x0303; - state->output = reg->val & ~0x0303; - m52790_write(client); - } - break; - } -#endif +static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, - V4L2_IDENT_M52790, 0); + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (reg->reg != 0) + return -EINVAL; + reg->val = state->input | state->output; + return 0; +} - case VIDIOC_LOG_STATUS: - v4l_info(client, "Switch 1: %02x\n", - (state->input | state->output) & 0xff); - v4l_info(client, "Switch 2: %02x\n", - (state->input | state->output) >> 8); - break; +static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - default: + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) return -EINVAL; - } + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (reg->reg != 0) + return -EINVAL; + state->input = reg->val & 0x0303; + state->output = reg->val & ~0x0303; + m52790_write(sd); return 0; } +#endif + +static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_M52790, 0); +} + +static int m52790_log_status(struct v4l2_subdev *sd) +{ + struct m52790_state *state = to_state(sd); + + v4l2_info(sd, "Switch 1: %02x\n", + (state->input | state->output) & 0xff); + v4l2_info(sd, "Switch 2: %02x\n", + (state->input | state->output) >> 8); + return 0; +} + +static int m52790_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops m52790_core_ops = { + .log_status = m52790_log_status, + .g_chip_ident = m52790_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = m52790_g_register, + .s_register = m52790_s_register, +#endif +}; + +static const struct v4l2_subdev_audio_ops m52790_audio_ops = { + .s_routing = m52790_s_routing, +}; + +static const struct v4l2_subdev_video_ops m52790_video_ops = { + .s_routing = m52790_s_routing, +}; + +static const struct v4l2_subdev_ops m52790_ops = { + .core = &m52790_core_ops, + .audio = &m52790_audio_ops, + .video = &m52790_video_ops, +}; /* ----------------------------------------------------------------------- */ @@ -130,6 +171,7 @@ static int m52790_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct m52790_state *state; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -142,16 +184,20 @@ static int m52790_probe(struct i2c_client *client, if (state == NULL) return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &m52790_ops); state->input = M52790_IN_TUNER; state->output = M52790_OUT_STEREO; - i2c_set_clientdata(client, state); - m52790_write(client); + m52790_write(sd); return 0; } static int m52790_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 3da74dc..a622dbb 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -51,14 +51,14 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/kthread.h> +#include <linux/freezer.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-i2c-drv-legacy.h> -#include <media/tvaudio.h> #include <media/msp3400.h> -#include <linux/kthread.h> -#include <linux/freezer.h> +#include <media/tvaudio.h> #include "msp3400-driver.h" /* ---------------------------------------------------------------------- */ @@ -265,7 +265,7 @@ static char *scart_names[] = { void msp_set_scart(struct i2c_client *client, int in, int out) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); state->in_scart = in; @@ -289,7 +289,7 @@ void msp_set_scart(struct i2c_client *client, int in, int out) void msp_set_audio(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int bal = 0, bass, treble, loudness; int val = 0; int reallymuted = state->muted | state->scan_in_progress; @@ -336,7 +336,7 @@ void msp_set_audio(struct i2c_client *client) static void msp_wake_thread(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (NULL == state->kthread) return; @@ -390,9 +390,9 @@ static int msp_mode_v4l1_to_v4l2(int mode) } #endif -static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -433,9 +433,10 @@ static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) return 0; } -static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -481,40 +482,16 @@ static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) return 0; } -static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) +#ifdef CONFIG_VIDEO_ALLOW_V4L1 +static int msp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { - struct msp_state *state = i2c_get_clientdata(client); - - if (msp_debug >= 2) - v4l_i2c_print_ioctl(client, cmd); + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); switch (cmd) { - case AUDC_SET_RADIO: - if (state->radio) - return 0; - state->radio = 1; - v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); - state->watch_stereo = 0; - switch (state->opmode) { - case OPMODE_MANUAL: - /* set msp3400 to FM radio mode */ - msp3400c_set_mode(client, MSP_MODE_FM_RADIO); - msp3400c_set_carrier(client, MSP_CARRIER(10.7), - MSP_CARRIER(10.7)); - msp_set_audio(client); - break; - case OPMODE_AUTODETECT: - case OPMODE_AUTOSELECT: - /* the thread will do for us */ - msp_wake_thread(client); - break; - } - break; - /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a kernel pointer here... */ -#ifdef CONFIG_VIDEO_ALLOW_V4L1 case VIDIOCGAUDIO: { struct video_audio *va = arg; @@ -588,105 +565,137 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) msp_wake_thread(client); break; } -#endif - case VIDIOC_S_FREQUENCY: - { - /* new channel -- kick audio carrier scan */ - msp_wake_thread(client); - break; + default: + return -ENOIOCTLCMD; } + return 0; +} +#endif - /* --- v4l2 ioctls --- */ - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg; - int update = state->radio || state->v4l2_std != *id; +/* --- v4l2 ioctls --- */ +static int msp_s_radio(struct v4l2_subdev *sd) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - state->v4l2_std = *id; - state->radio = 0; - if (update) - msp_wake_thread(client); + if (state->radio) return 0; + state->radio = 1; + v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); + state->watch_stereo = 0; + switch (state->opmode) { + case OPMODE_MANUAL: + /* set msp3400 to FM radio mode */ + msp3400c_set_mode(client, MSP_MODE_FM_RADIO); + msp3400c_set_carrier(client, MSP_CARRIER(10.7), + MSP_CARRIER(10.7)); + msp_set_audio(client); + break; + case OPMODE_AUTODETECT: + case OPMODE_AUTOSELECT: + /* the thread will do for us */ + msp_wake_thread(client); + break; } + return 0; +} - case VIDIOC_INT_G_AUDIO_ROUTING: - { - struct v4l2_routing *rt = arg; +static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - *rt = state->routing; - break; - } + /* new channel -- kick audio carrier scan */ + msp_wake_thread(client); + return 0; +} - case VIDIOC_INT_S_AUDIO_ROUTING: - { - struct v4l2_routing *rt = arg; - int tuner = (rt->input >> 3) & 1; - int sc_in = rt->input & 0x7; - int sc1_out = rt->output & 0xf; - int sc2_out = (rt->output >> 4) & 0xf; - u16 val, reg; - int i; - int extern_input = 1; - - if (state->routing.input == rt->input && - state->routing.output == rt->output) - break; - state->routing = *rt; - /* check if the tuner input is used */ - for (i = 0; i < 5; i++) { - if (((rt->input >> (4 + i * 4)) & 0xf) == 0) - extern_input = 0; - } - state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT; - state->rxsubchans = V4L2_TUNER_SUB_STEREO; - msp_set_scart(client, sc_in, 0); - msp_set_scart(client, sc1_out, 1); - msp_set_scart(client, sc2_out, 2); - msp_set_audmode(client); - reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; - val = msp_read_dem(client, reg); - msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); - /* wake thread when a new input is chosen */ +static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int update = state->radio || state->v4l2_std != id; + + state->v4l2_std = id; + state->radio = 0; + if (update) msp_wake_thread(client); - break; + return 0; +} + +static int msp_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int tuner = (rt->input >> 3) & 1; + int sc_in = rt->input & 0x7; + int sc1_out = rt->output & 0xf; + int sc2_out = (rt->output >> 4) & 0xf; + u16 val, reg; + int i; + int extern_input = 1; + + if (state->routing.input == rt->input && + state->routing.output == rt->output) + return 0; + state->routing = *rt; + /* check if the tuner input is used */ + for (i = 0; i < 5; i++) { + if (((rt->input >> (4 + i * 4)) & 0xf) == 0) + extern_input = 0; } + state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT; + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + msp_set_scart(client, sc_in, 0); + msp_set_scart(client, sc1_out, 1); + msp_set_scart(client, sc2_out, 2); + msp_set_audmode(client); + reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; + val = msp_read_dem(client, reg); + msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); + /* wake thread when a new input is chosen */ + msp_wake_thread(client); + return 0; +} - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = arg; +static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->radio) - break; - if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); - vt->audmode = state->audmode; - vt->rxsubchans = state->rxsubchans; - vt->capability |= V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - break; - } + if (state->radio) + return 0; + if (state->opmode == OPMODE_AUTOSELECT) + msp_detect_stereo(client); + vt->audmode = state->audmode; + vt->rxsubchans = state->rxsubchans; + vt->capability |= V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + return 0; +} - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; +static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->radio) /* TODO: add mono/stereo support for radio */ - break; - if (state->audmode == vt->audmode) - break; - state->audmode = vt->audmode; - /* only set audmode */ - msp_set_audmode(client); - break; - } + if (state->radio) /* TODO: add mono/stereo support for radio */ + return 0; + if (state->audmode == vt->audmode) + return 0; + state->audmode = vt->audmode; + /* only set audmode */ + msp_set_audmode(client); + return 0; +} - case VIDIOC_INT_I2S_CLOCK_FREQ: - { - u32 *a = (u32 *)arg; +static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", *a); + v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq); - switch (*a) { + switch (freq) { case 1024000: state->i2s_mode = 0; break; @@ -695,24 +704,24 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) break; default: return -EINVAL; - } - break; } + return 0; +} - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; +static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + struct msp_state *state = to_state(sd); - switch (qc->id) { + switch (qc->id) { case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_MUTE: return v4l2_ctrl_query_fill_std(qc); default: break; - } - if (!state->has_sound_processing) - return -EINVAL; - switch (qc->id) { + } + if (!state->has_sound_processing) + return -EINVAL; + switch (qc->id) { case V4L2_CID_AUDIO_LOUDNESS: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: @@ -720,32 +729,38 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) return v4l2_ctrl_query_fill_std(qc); default: return -EINVAL; - } } + return 0; +} - case VIDIOC_G_CTRL: - return msp_get_ctrl(client, arg); - - case VIDIOC_S_CTRL: - return msp_set_ctrl(client, arg); +static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_LOG_STATUS: - { - const char *p; + return v4l2_chip_ident_i2c_client(client, chip, state->ident, + (state->rev1 << 16) | state->rev2); +} - if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); - v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", - client->name, state->rev1, state->rev2); - v4l_info(client, "Audio: volume %d%s\n", - state->volume, state->muted ? " (muted)" : ""); - if (state->has_sound_processing) { - v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n", - state->balance, state->bass, - state->treble, - state->loudness ? "on" : "off"); - } - switch (state->mode) { +static int msp_log_status(struct v4l2_subdev *sd) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + const char *p; + + if (state->opmode == OPMODE_AUTOSELECT) + msp_detect_stereo(client); + v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", + client->name, state->rev1, state->rev2); + v4l_info(client, "Audio: volume %d%s\n", + state->volume, state->muted ? " (muted)" : ""); + if (state->has_sound_processing) { + v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n", + state->balance, state->bass, + state->treble, + state->loudness ? "on" : "off"); + } + switch (state->mode) { case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break; case MSP_MODE_FM_RADIO: p = "FM Radio"; break; case MSP_MODE_FM_TERRA: p = "Terrestial FM-mono/stereo"; break; @@ -756,36 +771,25 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) case MSP_MODE_BTSC: p = "BTSC"; break; case MSP_MODE_EXTERN: p = "External input"; break; default: p = "unknown"; break; - } - if (state->mode == MSP_MODE_EXTERN) { - v4l_info(client, "Mode: %s\n", p); - } else if (state->opmode == OPMODE_MANUAL) { - v4l_info(client, "Mode: %s (%s%s)\n", p, + } + if (state->mode == MSP_MODE_EXTERN) { + v4l_info(client, "Mode: %s\n", p); + } else if (state->opmode == OPMODE_MANUAL) { + v4l_info(client, "Mode: %s (%s%s)\n", p, (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); - } else { - if (state->opmode == OPMODE_AUTODETECT) - v4l_info(client, "Mode: %s\n", p); - v4l_info(client, "Standard: %s (%s%s)\n", + } else { + if (state->opmode == OPMODE_AUTODETECT) + v4l_info(client, "Mode: %s\n", p); + v4l_info(client, "Standard: %s (%s%s)\n", msp_standard_std_name(state->std), (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); - } - v4l_info(client, "Audmode: 0x%04x\n", state->audmode); - v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", - state->routing.input, state->routing.output); - v4l_info(client, "ACB: 0x%04x\n", state->acb); - break; - } - - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, state->ident, - (state->rev1 << 16) | state->rev2); - - default: - /* unknown */ - return -EINVAL; } + v4l_info(client, "Audmode: 0x%04x\n", state->audmode); + v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", + state->routing.input, state->routing.output); + v4l_info(client, "ACB: 0x%04x\n", state->acb); return 0; } @@ -803,11 +807,49 @@ static int msp_resume(struct i2c_client *client) return 0; } +static int msp_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops msp_core_ops = { + .log_status = msp_log_status, + .g_chip_ident = msp_g_chip_ident, + .g_ctrl = msp_g_ctrl, + .s_ctrl = msp_s_ctrl, + .queryctrl = msp_queryctrl, +#ifdef CONFIG_VIDEO_ALLOW_V4L1 + .ioctl = msp_ioctl, +#endif +}; + +static const struct v4l2_subdev_tuner_ops msp_tuner_ops = { + .s_frequency = msp_s_frequency, + .g_tuner = msp_g_tuner, + .s_tuner = msp_s_tuner, + .s_radio = msp_s_radio, + .s_std = msp_s_std, +}; + +static const struct v4l2_subdev_audio_ops msp_audio_ops = { + .s_routing = msp_s_routing, + .s_i2s_clock_freq = msp_s_i2s_clock_freq, +}; + +static const struct v4l2_subdev_ops msp_ops = { + .core = &msp_core_ops, + .tuner = &msp_tuner_ops, + .audio = &msp_audio_ops, +}; + /* ----------------------------------------------------------------------- */ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct msp_state *state; + struct v4l2_subdev *sd; int (*thread_func)(void *data) = NULL; int msp_hard; int msp_family; @@ -827,7 +869,8 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) if (!state) return -ENOMEM; - i2c_set_clientdata(client, state); + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &msp_ops); state->v4l2_std = V4L2_STD_NTSC; state->audmode = V4L2_TUNER_MODE_STEREO; @@ -972,8 +1015,9 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) static int msp_remove(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); + v4l2_device_unregister_subdev(&state->sd); /* shutdown control thread */ if (state->kthread) { state->restart = 1; diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h index ab69a29..3fe1c1b 100644 --- a/drivers/media/video/msp3400-driver.h +++ b/drivers/media/video/msp3400-driver.h @@ -5,6 +5,7 @@ #define MSP3400_DRIVER_H #include <media/msp3400.h> +#include <media/v4l2-device.h> /* ---------------------------------------------------------------------- */ @@ -49,6 +50,7 @@ extern int msp_dolby; extern int msp_stereo_thresh; struct msp_state { + struct v4l2_subdev sd; int rev1, rev2; int ident; u8 has_nicam; @@ -96,6 +98,11 @@ struct msp_state { unsigned int watch_stereo:1; }; +static inline struct msp_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct msp_state, sd); +} + /* msp3400-driver.c */ int msp_write_dem(struct i2c_client *client, int addr, int val); int msp_write_dsp(struct i2c_client *client, int addr, int val); diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c index 846a14a..a655e9c 100644 --- a/drivers/media/video/msp3400-kthreads.c +++ b/drivers/media/video/msp3400-kthreads.c @@ -159,7 +159,7 @@ const char *msp_standard_std_name(int std) static void msp_set_source(struct i2c_client *client, u16 src) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (msp_dolby) { msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */ @@ -186,7 +186,7 @@ void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2) void msp3400c_set_mode(struct i2c_client *client, int mode) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode]; int tuner = (state->routing.input >> 3) & 1; int i; @@ -227,7 +227,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) static char *strmode[] = { "mono", "stereo", "lang2", "lang1", "lang1+lang2" }; - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); char *modestr = (state->audmode >= 0 && state->audmode < 5) ? strmode[state->audmode] : "unknown"; int src = 0; /* channel source: FM/AM, nicam or SCART */ @@ -356,7 +356,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) static void msp3400c_print_mode(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (state->main == state->second) v4l_dbg(1, msp_debug, client, @@ -385,7 +385,7 @@ static void msp3400c_print_mode(struct i2c_client *client) static int msp3400c_detect_stereo(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int val; int rxsubchans = state->rxsubchans; int newnicam = state->nicam_on; @@ -463,7 +463,7 @@ static int msp3400c_detect_stereo(struct i2c_client *client) /* stereo/multilang monitoring */ static void watch_stereo(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (msp_detect_stereo(client)) msp_set_audmode(client); @@ -475,7 +475,7 @@ static void watch_stereo(struct i2c_client *client) int msp3400c_thread(void *data) { struct i2c_client *client = data; - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); struct msp3400c_carrier_detect *cd; int count, max1, max2, val1, val2, val, i; @@ -659,7 +659,7 @@ no_second: int msp3410d_thread(void *data) { struct i2c_client *client = data; - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int val, i, std, count; v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n"); @@ -825,7 +825,7 @@ restart: static int msp34xxg_modus(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (state->radio) { v4l_dbg(1, msp_debug, client, "selected radio modus\n"); @@ -852,7 +852,7 @@ static int msp34xxg_modus(struct i2c_client *client) static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int source, matrix; switch (state->audmode) { @@ -895,7 +895,7 @@ static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) static void msp34xxg_set_sources(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); u32 in = state->routing.input; msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf); @@ -911,7 +911,7 @@ static void msp34xxg_set_sources(struct i2c_client *client) /* (re-)initialize the msp34xxg */ static void msp34xxg_reset(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int tuner = (state->routing.input >> 3) & 1; int modus; @@ -954,7 +954,7 @@ static void msp34xxg_reset(struct i2c_client *client) int msp34xxg_thread(void *data) { struct i2c_client *client = data; - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int val, i; v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n"); @@ -1049,7 +1049,7 @@ unmute: static int msp34xxg_detect_stereo(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int status = msp_read_dem(client, 0x0200); int is_bilingual = status & 0x100; int is_stereo = status & 0x40; @@ -1078,7 +1078,7 @@ static int msp34xxg_detect_stereo(struct i2c_client *client) static void msp34xxg_set_audmode(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (state->std == 0x20) { if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) && @@ -1095,7 +1095,7 @@ static void msp34xxg_set_audmode(struct i2c_client *client) void msp_set_audmode(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); switch (state->opmode) { case OPMODE_MANUAL: @@ -1110,7 +1110,7 @@ void msp_set_audmode(struct i2c_client *client) int msp_detect_stereo(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); switch (state->opmode) { case OPMODE_MANUAL: diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 0c52437..1a1a124 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -272,21 +272,20 @@ static int mt9m001_set_bus_param(struct soc_camera_device *icd, static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) { struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - unsigned int width_flag = SOCAM_DATAWIDTH_10; + struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + /* MT9M001 has all capture_format parameters fixed */ + unsigned long flags = SOCAM_DATAWIDTH_10 | SOCAM_PCLK_SAMPLE_RISING | + SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_MASTER; if (bus_switch_possible(mt9m001)) - width_flag |= SOCAM_DATAWIDTH_8; + flags |= SOCAM_DATAWIDTH_8; - /* MT9M001 has all capture_format parameters fixed */ - return SOCAM_PCLK_SAMPLE_RISING | - SOCAM_HSYNC_ACTIVE_HIGH | - SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_MASTER | - width_flag; + return soc_camera_apply_sensor_flags(icl, flags); } -static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int mt9m001_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); int ret; @@ -298,7 +297,7 @@ static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, ret = reg_write(icd, MT9M001_VERTICAL_BLANKING, vblank); /* The caller provides a supported format, as verified per - * call to icd->try_fmt_cap() */ + * call to icd->try_fmt() */ if (!ret) ret = reg_write(icd, MT9M001_COLUMN_START, rect->left); if (!ret) @@ -325,18 +324,20 @@ static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, return ret; } -static int mt9m001_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9m001_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { - if (f->fmt.pix.height < 32 + icd->y_skip_top) - f->fmt.pix.height = 32 + icd->y_skip_top; - if (f->fmt.pix.height > 1024 + icd->y_skip_top) - f->fmt.pix.height = 1024 + icd->y_skip_top; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.width > 1280) - f->fmt.pix.width = 1280; - f->fmt.pix.width &= ~0x01; /* has to be even, unsure why was ~3 */ + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (pix->height < 32 + icd->y_skip_top) + pix->height = 32 + icd->y_skip_top; + if (pix->height > 1024 + icd->y_skip_top) + pix->height = 1024 + icd->y_skip_top; + if (pix->width < 48) + pix->width = 48; + if (pix->width > 1280) + pix->width = 1280; + pix->width &= ~0x01; /* has to be even, unsure why was ~3 */ return 0; } @@ -447,8 +448,8 @@ static struct soc_camera_ops mt9m001_ops = { .release = mt9m001_release, .start_capture = mt9m001_start_capture, .stop_capture = mt9m001_stop_capture, - .set_fmt_cap = mt9m001_set_fmt_cap, - .try_fmt_cap = mt9m001_try_fmt_cap, + .set_fmt = mt9m001_set_fmt, + .try_fmt = mt9m001_try_fmt, .set_bus_param = mt9m001_set_bus_param, .query_bus_param = mt9m001_query_bus_param, .controls = mt9m001_controls, @@ -578,6 +579,7 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro static int mt9m001_video_probe(struct soc_camera_device *icd) { struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct soc_camera_link *icl = mt9m001->client->dev.platform_data; s32 data; int ret; @@ -588,7 +590,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) return -ENODEV; /* Enable the chip */ - data = reg_write(&mt9m001->icd, MT9M001_CHIP_ENABLE, 1); + data = reg_write(icd, MT9M001_CHIP_ENABLE, 1); dev_dbg(&icd->dev, "write: %d\n", data); /* Read out the chip version register */ @@ -600,7 +602,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) case 0x8421: mt9m001->model = V4L2_IDENT_MT9M001C12ST; icd->formats = mt9m001_colour_formats; - if (mt9m001->client->dev.platform_data) + if (gpio_is_valid(icl->gpio)) icd->num_formats = ARRAY_SIZE(mt9m001_colour_formats); else icd->num_formats = 1; @@ -608,7 +610,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) case 0x8431: mt9m001->model = V4L2_IDENT_MT9M001C12STM; icd->formats = mt9m001_monochrome_formats; - if (mt9m001->client->dev.platform_data) + if (gpio_is_valid(icl->gpio)) icd->num_formats = ARRAY_SIZE(mt9m001_monochrome_formats); else icd->num_formats = 1; @@ -640,8 +642,8 @@ static void mt9m001_video_remove(struct soc_camera_device *icd) struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m001->client->addr, - mt9m001->icd.dev.parent, mt9m001->icd.vdev); - soc_camera_video_stop(&mt9m001->icd); + icd->dev.parent, icd->vdev); + soc_camera_video_stop(icd); } static int mt9m001_probe(struct i2c_client *client, diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index da0b2d5..c89ea41 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -1,5 +1,5 @@ /* - * Driver for MT9M111 CMOS Image Sensor from Micron + * Driver for MT9M111/MT9M112 CMOS Image Sensor from Micron * * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr> * @@ -19,7 +19,7 @@ #include <media/soc_camera.h> /* - * mt9m111 i2c address is 0x5d or 0x48 (depending on SAddr pin) + * mt9m111 and mt9m112 i2c address is 0x5d or 0x48 (depending on SAddr pin) * The platform has to define i2c_board_info and call i2c_register_board_info() */ @@ -90,7 +90,7 @@ #define MT9M111_OUTPUT_FORMAT_CTRL2_B 0x19b #define MT9M111_OPMODE_AUTOEXPO_EN (1 << 14) - +#define MT9M111_OPMODE_AUTOWHITEBAL_EN (1 << 1) #define MT9M111_OUTFMT_PROCESSED_BAYER (1 << 14) #define MT9M111_OUTFMT_BYPASS_IFP (1 << 10) @@ -128,9 +128,14 @@ .colorspace = _colorspace } #define RGB_FMT(_name, _depth, _fourcc) \ COL_FMT(_name, _depth, _fourcc, V4L2_COLORSPACE_SRGB) +#define JPG_FMT(_name, _depth, _fourcc) \ + COL_FMT(_name, _depth, _fourcc, V4L2_COLORSPACE_JPEG) static const struct soc_camera_data_format mt9m111_colour_formats[] = { - COL_FMT("YCrYCb 8 bit", 8, V4L2_PIX_FMT_YUYV, V4L2_COLORSPACE_JPEG), + JPG_FMT("CbYCrY 16 bit", 16, V4L2_PIX_FMT_UYVY), + JPG_FMT("CrYCbY 16 bit", 16, V4L2_PIX_FMT_VYUY), + JPG_FMT("YCbYCr 16 bit", 16, V4L2_PIX_FMT_YUYV), + JPG_FMT("YCrYCb 16 bit", 16, V4L2_PIX_FMT_YVYU), RGB_FMT("RGB 565", 16, V4L2_PIX_FMT_RGB565), RGB_FMT("RGB 555", 16, V4L2_PIX_FMT_RGB555), RGB_FMT("Bayer (sRGB) 10 bit", 10, V4L2_PIX_FMT_SBGGR16), @@ -145,7 +150,7 @@ enum mt9m111_context { struct mt9m111 { struct i2c_client *client; struct soc_camera_device icd; - int model; /* V4L2_IDENT_MT9M111* codes from v4l2-chip-ident.h */ + int model; /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */ enum mt9m111_context context; unsigned int left, top, width, height; u32 pixfmt; @@ -158,6 +163,7 @@ struct mt9m111 { unsigned int swap_rgb_red_blue:1; unsigned int swap_yuv_y_chromas:1; unsigned int swap_yuv_cb_cr:1; + unsigned int autowhitebalance:1; }; static int reg_page_map_set(struct i2c_client *client, const u16 reg) @@ -410,9 +416,13 @@ static int mt9m111_stop_capture(struct soc_camera_device *icd) static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd) { - return SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING | + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct soc_camera_link *icl = mt9m111->client->dev.platform_data; + unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; + + return soc_camera_apply_sensor_flags(icl, flags); } static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f) @@ -438,7 +448,24 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt) case V4L2_PIX_FMT_RGB565: ret = mt9m111_setfmt_rgb565(icd); break; + case V4L2_PIX_FMT_UYVY: + mt9m111->swap_yuv_y_chromas = 0; + mt9m111->swap_yuv_cb_cr = 0; + ret = mt9m111_setfmt_yuv(icd); + break; + case V4L2_PIX_FMT_VYUY: + mt9m111->swap_yuv_y_chromas = 0; + mt9m111->swap_yuv_cb_cr = 1; + ret = mt9m111_setfmt_yuv(icd); + break; case V4L2_PIX_FMT_YUYV: + mt9m111->swap_yuv_y_chromas = 1; + mt9m111->swap_yuv_cb_cr = 0; + ret = mt9m111_setfmt_yuv(icd); + break; + case V4L2_PIX_FMT_YVYU: + mt9m111->swap_yuv_y_chromas = 1; + mt9m111->swap_yuv_cb_cr = 1; ret = mt9m111_setfmt_yuv(icd); break; default: @@ -452,8 +479,8 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt) return ret; } -static int mt9m111_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int mt9m111_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); int ret; @@ -473,13 +500,15 @@ static int mt9m111_set_fmt_cap(struct soc_camera_device *icd, return ret; } -static int mt9m111_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9m111_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { - if (f->fmt.pix.height > MT9M111_MAX_HEIGHT) - f->fmt.pix.height = MT9M111_MAX_HEIGHT; - if (f->fmt.pix.width > MT9M111_MAX_WIDTH) - f->fmt.pix.width = MT9M111_MAX_WIDTH; + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (pix->height > MT9M111_MAX_HEIGHT) + pix->height = MT9M111_MAX_HEIGHT; + if (pix->width > MT9M111_MAX_WIDTH) + pix->width = MT9M111_MAX_WIDTH; return 0; } @@ -597,8 +626,8 @@ static struct soc_camera_ops mt9m111_ops = { .release = mt9m111_release, .start_capture = mt9m111_start_capture, .stop_capture = mt9m111_stop_capture, - .set_fmt_cap = mt9m111_set_fmt_cap, - .try_fmt_cap = mt9m111_try_fmt_cap, + .set_fmt = mt9m111_set_fmt, + .try_fmt = mt9m111_try_fmt, .query_bus_param = mt9m111_query_bus_param, .set_bus_param = mt9m111_set_bus_param, .controls = mt9m111_controls, @@ -634,18 +663,15 @@ static int mt9m111_set_flip(struct soc_camera_device *icd, int flip, int mask) static int mt9m111_get_global_gain(struct soc_camera_device *icd) { - unsigned int data, gain; + int data; data = reg_read(GLOBAL_GAIN); if (data >= 0) - gain = ((data & (1 << 10)) * 2) - | ((data & (1 << 9)) * 2) - | (data & 0x2f); - else - gain = data; - - return gain; + return (data & 0x2f) * (1 << ((data >> 10) & 1)) * + (1 << ((data >> 9) & 1)); + return data; } + static int mt9m111_set_global_gain(struct soc_camera_device *icd, int gain) { u16 val; @@ -679,6 +705,23 @@ static int mt9m111_set_autoexposure(struct soc_camera_device *icd, int on) return ret; } + +static int mt9m111_set_autowhitebalance(struct soc_camera_device *icd, int on) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret; + + if (on) + ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN); + else + ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN); + + if (!ret) + mt9m111->autowhitebalance = on; + + return ret; +} + static int mt9m111_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) { @@ -715,6 +758,9 @@ static int mt9m111_get_control(struct soc_camera_device *icd, case V4L2_CID_EXPOSURE_AUTO: ctrl->value = mt9m111->autoexposure; break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl->value = mt9m111->autowhitebalance; + break; } return 0; } @@ -748,6 +794,9 @@ static int mt9m111_set_control(struct soc_camera_device *icd, case V4L2_CID_EXPOSURE_AUTO: ret = mt9m111_set_autoexposure(icd, ctrl->value); break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = mt9m111_set_autowhitebalance(icd, ctrl->value); + break; default: ret = -EINVAL; } @@ -766,6 +815,7 @@ static int mt9m111_restore_state(struct soc_camera_device *icd) mt9m111_set_flip(icd, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS); mt9m111_set_global_gain(icd, icd->gain); mt9m111_set_autoexposure(icd, mt9m111->autoexposure); + mt9m111_set_autowhitebalance(icd, mt9m111->autowhitebalance); return 0; } @@ -798,7 +848,7 @@ static int mt9m111_init(struct soc_camera_device *icd) if (!ret) ret = mt9m111_set_autoexposure(icd, mt9m111->autoexposure); if (ret) - dev_err(&icd->dev, "mt9m111 init failed: %d\n", ret); + dev_err(&icd->dev, "mt9m11x init failed: %d\n", ret); return ret; } @@ -808,7 +858,7 @@ static int mt9m111_release(struct soc_camera_device *icd) ret = mt9m111_disable(icd); if (ret < 0) - dev_err(&icd->dev, "mt9m111 release failed: %d\n", ret); + dev_err(&icd->dev, "mt9m11x release failed: %d\n", ret); return ret; } @@ -841,25 +891,30 @@ static int mt9m111_video_probe(struct soc_camera_device *icd) data = reg_read(CHIP_VERSION); switch (data) { - case 0x143a: + case 0x143a: /* MT9M111 */ mt9m111->model = V4L2_IDENT_MT9M111; - icd->formats = mt9m111_colour_formats; - icd->num_formats = ARRAY_SIZE(mt9m111_colour_formats); + break; + case 0x148c: /* MT9M112 */ + mt9m111->model = V4L2_IDENT_MT9M112; break; default: ret = -ENODEV; dev_err(&icd->dev, - "No MT9M111 chip detected, register read %x\n", data); + "No MT9M11x chip detected, register read %x\n", data); goto ei2c; } - dev_info(&icd->dev, "Detected a MT9M111 chip ID 0x143a\n"); + icd->formats = mt9m111_colour_formats; + icd->num_formats = ARRAY_SIZE(mt9m111_colour_formats); + + dev_info(&icd->dev, "Detected a MT9M11x chip ID %x\n", data); ret = soc_camera_video_start(icd); if (ret) goto eisis; mt9m111->autoexposure = 1; + mt9m111->autowhitebalance = 1; mt9m111->swap_rgb_even_odd = 1; mt9m111->swap_rgb_red_blue = 1; @@ -889,7 +944,7 @@ static int mt9m111_probe(struct i2c_client *client, int ret; if (!icl) { - dev_err(&client->dev, "MT9M111 driver needs platform data\n"); + dev_err(&client->dev, "MT9M11x driver needs platform data\n"); return -EINVAL; } @@ -968,6 +1023,6 @@ static void __exit mt9m111_mod_exit(void) module_init(mt9m111_mod_init); module_exit(mt9m111_mod_exit); -MODULE_DESCRIPTION("Micron MT9M111 Camera driver"); +MODULE_DESCRIPTION("Micron MT9M111/MT9M112 Camera driver"); MODULE_AUTHOR("Robert Jarzmik"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c new file mode 100644 index 0000000..1a9d539 --- /dev/null +++ b/drivers/media/video/mt9t031.c @@ -0,0 +1,736 @@ +/* + * Driver for MT9T031 CMOS Image Sensor from Micron + * + * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering <lg@denx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/videodev2.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/log2.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> +#include <media/soc_camera.h> + +/* mt9t031 i2c address 0x5d + * The platform has to define i2c_board_info + * and call i2c_register_board_info() */ + +/* mt9t031 selected register addresses */ +#define MT9T031_CHIP_VERSION 0x00 +#define MT9T031_ROW_START 0x01 +#define MT9T031_COLUMN_START 0x02 +#define MT9T031_WINDOW_HEIGHT 0x03 +#define MT9T031_WINDOW_WIDTH 0x04 +#define MT9T031_HORIZONTAL_BLANKING 0x05 +#define MT9T031_VERTICAL_BLANKING 0x06 +#define MT9T031_OUTPUT_CONTROL 0x07 +#define MT9T031_SHUTTER_WIDTH_UPPER 0x08 +#define MT9T031_SHUTTER_WIDTH 0x09 +#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a +#define MT9T031_FRAME_RESTART 0x0b +#define MT9T031_SHUTTER_DELAY 0x0c +#define MT9T031_RESET 0x0d +#define MT9T031_READ_MODE_1 0x1e +#define MT9T031_READ_MODE_2 0x20 +#define MT9T031_READ_MODE_3 0x21 +#define MT9T031_ROW_ADDRESS_MODE 0x22 +#define MT9T031_COLUMN_ADDRESS_MODE 0x23 +#define MT9T031_GLOBAL_GAIN 0x35 +#define MT9T031_CHIP_ENABLE 0xF8 + +#define MT9T031_MAX_HEIGHT 1536 +#define MT9T031_MAX_WIDTH 2048 +#define MT9T031_MIN_HEIGHT 2 +#define MT9T031_MIN_WIDTH 2 +#define MT9T031_HORIZONTAL_BLANK 142 +#define MT9T031_VERTICAL_BLANK 25 +#define MT9T031_COLUMN_SKIP 32 +#define MT9T031_ROW_SKIP 20 + +#define MT9T031_BUS_PARAM (SOCAM_PCLK_SAMPLE_RISING | \ + SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | \ + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | \ + SOCAM_MASTER | SOCAM_DATAWIDTH_10) + +static const struct soc_camera_data_format mt9t031_colour_formats[] = { + { + .name = "Bayer (sRGB) 10 bit", + .depth = 10, + .fourcc = V4L2_PIX_FMT_SGRBG10, + .colorspace = V4L2_COLORSPACE_SRGB, + } +}; + +struct mt9t031 { + struct i2c_client *client; + struct soc_camera_device icd; + int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */ + unsigned char autoexposure; + u16 xskip; + u16 yskip; +}; + +static int reg_read(struct soc_camera_device *icd, const u8 reg) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct i2c_client *client = mt9t031->client; + s32 data = i2c_smbus_read_word_data(client, reg); + return data < 0 ? data : swab16(data); +} + +static int reg_write(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + return i2c_smbus_write_word_data(mt9t031->client, reg, swab16(data)); +} + +static int reg_set(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(icd, reg); + if (ret < 0) + return ret; + return reg_write(icd, reg, ret | data); +} + +static int reg_clear(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(icd, reg); + if (ret < 0) + return ret; + return reg_write(icd, reg, ret & ~data); +} + +static int set_shutter(struct soc_camera_device *icd, const u32 data) +{ + int ret; + + ret = reg_write(icd, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16); + + if (ret >= 0) + ret = reg_write(icd, MT9T031_SHUTTER_WIDTH, data & 0xffff); + + return ret; +} + +static int get_shutter(struct soc_camera_device *icd, u32 *data) +{ + int ret; + + ret = reg_read(icd, MT9T031_SHUTTER_WIDTH_UPPER); + *data = ret << 16; + + if (ret >= 0) + ret = reg_read(icd, MT9T031_SHUTTER_WIDTH); + *data |= ret & 0xffff; + + return ret < 0 ? ret : 0; +} + +static int mt9t031_init(struct soc_camera_device *icd) +{ + int ret; + + /* Disable chip output, synchronous option update */ + dev_dbg(icd->vdev->parent, "%s\n", __func__); + + ret = reg_write(icd, MT9T031_RESET, 1); + if (ret >= 0) + ret = reg_write(icd, MT9T031_RESET, 0); + if (ret >= 0) + ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3); + + return ret >= 0 ? 0 : -EIO; +} + +static int mt9t031_release(struct soc_camera_device *icd) +{ + /* Disable the chip */ + reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3); + return 0; +} + +static int mt9t031_start_capture(struct soc_camera_device *icd) +{ + /* Switch to master "normal" mode */ + if (reg_set(icd, MT9T031_OUTPUT_CONTROL, 3) < 0) + return -EIO; + return 0; +} + +static int mt9t031_stop_capture(struct soc_camera_device *icd) +{ + /* Stop sensor readout */ + if (reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3) < 0) + return -EIO; + return 0; +} + +static int mt9t031_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + /* The caller should have queried our parameters, check anyway */ + if (flags & ~MT9T031_BUS_PARAM) + return -EINVAL; + + if (flags & SOCAM_PCLK_SAMPLE_FALLING) + reg_set(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); + else + reg_clear(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); + + return 0; +} + +static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct soc_camera_link *icl = mt9t031->client->dev.platform_data; + + return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM); +} + +static int mt9t031_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + int ret; + const u16 hblank = MT9T031_HORIZONTAL_BLANK, + vblank = MT9T031_VERTICAL_BLANK; + u16 xbin, xskip = mt9t031->xskip, ybin, yskip = mt9t031->yskip, + width = rect->width * xskip, height = rect->height * yskip; + + if (pixfmt) { + /* S_FMT - use binning and skipping for scaling, recalculate */ + /* Is this more optimal than just a division? */ + for (xskip = 8; xskip > 1; xskip--) + if (rect->width * xskip <= icd->width_max) + break; + + for (yskip = 8; yskip > 1; yskip--) + if (rect->height * yskip <= icd->height_max) + break; + + width = rect->width * xskip; + height = rect->height * yskip; + + dev_dbg(&icd->dev, "xskip %u, width %u, yskip %u, height %u\n", + xskip, width, yskip, height); + } + + xbin = min(xskip, (u16)3); + ybin = min(yskip, (u16)3); + + /* Make sure we don't exceed frame limits */ + if (rect->left + width > icd->width_max) + rect->left = (icd->width_max - width) / 2; + + if (rect->top + height > icd->height_max) + rect->top = (icd->height_max - height) / 2; + + /* Could just do roundup(rect->left, [xy]bin); but this is cheaper */ + switch (xbin) { + case 2: + rect->left = (rect->left + 1) & ~1; + break; + case 3: + rect->left = roundup(rect->left, 3); + } + + switch (ybin) { + case 2: + rect->top = (rect->top + 1) & ~1; + break; + case 3: + rect->top = roundup(rect->top, 3); + } + + /* Blanking and start values - default... */ + ret = reg_write(icd, MT9T031_HORIZONTAL_BLANKING, hblank); + if (ret >= 0) + ret = reg_write(icd, MT9T031_VERTICAL_BLANKING, vblank); + + if (pixfmt) { + /* Binning, skipping */ + if (ret >= 0) + ret = reg_write(icd, MT9T031_COLUMN_ADDRESS_MODE, + ((xbin - 1) << 4) | (xskip - 1)); + if (ret >= 0) + ret = reg_write(icd, MT9T031_ROW_ADDRESS_MODE, + ((ybin - 1) << 4) | (yskip - 1)); + } + dev_dbg(&icd->dev, "new left %u, top %u\n", rect->left, rect->top); + + /* The caller provides a supported format, as guaranteed by + * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */ + if (ret >= 0) + ret = reg_write(icd, MT9T031_COLUMN_START, rect->left); + if (ret >= 0) + ret = reg_write(icd, MT9T031_ROW_START, rect->top); + if (ret >= 0) + ret = reg_write(icd, MT9T031_WINDOW_WIDTH, width - 1); + if (ret >= 0) + ret = reg_write(icd, MT9T031_WINDOW_HEIGHT, + height + icd->y_skip_top - 1); + if (ret >= 0 && mt9t031->autoexposure) { + ret = set_shutter(icd, height + icd->y_skip_top + vblank); + if (ret >= 0) { + const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank; + const struct v4l2_queryctrl *qctrl = + soc_camera_find_qctrl(icd->ops, + V4L2_CID_EXPOSURE); + icd->exposure = (shutter_max / 2 + (height + + icd->y_skip_top + vblank - 1) * + (qctrl->maximum - qctrl->minimum)) / + shutter_max + qctrl->minimum; + } + } + + if (!ret && pixfmt) { + mt9t031->xskip = xskip; + mt9t031->yskip = yskip; + } + + return ret < 0 ? ret : 0; +} + +static int mt9t031_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (pix->height < icd->height_min) + pix->height = icd->height_min; + if (pix->height > icd->height_max) + pix->height = icd->height_max; + if (pix->width < icd->width_min) + pix->width = icd->width_min; + if (pix->width > icd->width_max) + pix->width = icd->width_max; + + pix->width &= ~0x01; /* has to be even */ + pix->height &= ~0x01; /* has to be even */ + + return 0; +} + +static int mt9t031_get_chip_id(struct soc_camera_device *icd, + struct v4l2_chip_ident *id) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + + if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match_chip != mt9t031->client->addr) + return -ENODEV; + + id->ident = mt9t031->model; + id->revision = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9t031_get_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match_chip != mt9t031->client->addr) + return -ENODEV; + + reg->val = reg_read(icd, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int mt9t031_set_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match_chip != mt9t031->client->addr) + return -ENODEV; + + if (reg_write(icd, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static const struct v4l2_queryctrl mt9t031_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 64, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 1, + .maximum = 255, + .step = 1, + .default_value = 255, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Automatic Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + } +}; + +static int mt9t031_video_probe(struct soc_camera_device *); +static void mt9t031_video_remove(struct soc_camera_device *); +static int mt9t031_get_control(struct soc_camera_device *, struct v4l2_control *); +static int mt9t031_set_control(struct soc_camera_device *, struct v4l2_control *); + +static struct soc_camera_ops mt9t031_ops = { + .owner = THIS_MODULE, + .probe = mt9t031_video_probe, + .remove = mt9t031_video_remove, + .init = mt9t031_init, + .release = mt9t031_release, + .start_capture = mt9t031_start_capture, + .stop_capture = mt9t031_stop_capture, + .set_fmt = mt9t031_set_fmt, + .try_fmt = mt9t031_try_fmt, + .set_bus_param = mt9t031_set_bus_param, + .query_bus_param = mt9t031_query_bus_param, + .controls = mt9t031_controls, + .num_controls = ARRAY_SIZE(mt9t031_controls), + .get_control = mt9t031_get_control, + .set_control = mt9t031_set_control, + .get_chip_id = mt9t031_get_chip_id, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .get_register = mt9t031_get_register, + .set_register = mt9t031_set_register, +#endif +}; + +static int mt9t031_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + data = reg_read(icd, MT9T031_READ_MODE_2); + if (data < 0) + return -EIO; + ctrl->value = !!(data & 0x8000); + break; + case V4L2_CID_HFLIP: + data = reg_read(icd, MT9T031_READ_MODE_2); + if (data < 0) + return -EIO; + ctrl->value = !!(data & 0x4000); + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl->value = mt9t031->autoexposure; + break; + } + return 0; +} + +static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + const struct v4l2_queryctrl *qctrl; + int data; + + qctrl = soc_camera_find_qctrl(&mt9t031_ops, ctrl->id); + + if (!qctrl) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->value) + data = reg_set(icd, MT9T031_READ_MODE_2, 0x8000); + else + data = reg_clear(icd, MT9T031_READ_MODE_2, 0x8000); + if (data < 0) + return -EIO; + break; + case V4L2_CID_HFLIP: + if (ctrl->value) + data = reg_set(icd, MT9T031_READ_MODE_2, 0x4000); + else + data = reg_clear(icd, MT9T031_READ_MODE_2, 0x4000); + if (data < 0) + return -EIO; + break; + case V4L2_CID_GAIN: + if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) + return -EINVAL; + /* See Datasheet Table 7, Gain settings. */ + if (ctrl->value <= qctrl->default_value) { + /* Pack it into 0..1 step 0.125, register values 0..8 */ + unsigned long range = qctrl->default_value - qctrl->minimum; + data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range; + + dev_dbg(&icd->dev, "Setting gain %d\n", data); + data = reg_write(icd, MT9T031_GLOBAL_GAIN, data); + if (data < 0) + return -EIO; + } else { + /* Pack it into 1.125..15 variable step, register values 9..67 */ + /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ + unsigned long range = qctrl->maximum - qctrl->default_value - 1; + unsigned long gain = ((ctrl->value - qctrl->default_value - 1) * + 111 + range / 2) / range + 9; + + if (gain <= 32) + data = gain; + else if (gain <= 64) + data = ((gain - 32) * 16 + 16) / 32 + 80; + else + data = ((gain - 64) * 7 + 28) / 56 + 96; + + dev_dbg(&icd->dev, "Setting gain from %d to %d\n", + reg_read(icd, MT9T031_GLOBAL_GAIN), data); + data = reg_write(icd, MT9T031_GLOBAL_GAIN, data); + if (data < 0) + return -EIO; + } + + /* Success */ + icd->gain = ctrl->value; + break; + case V4L2_CID_EXPOSURE: + /* mt9t031 has maximum == default */ + if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) + return -EINVAL; + else { + const unsigned long range = qctrl->maximum - qctrl->minimum; + const u32 shutter = ((ctrl->value - qctrl->minimum) * 1048 + + range / 2) / range + 1; + u32 old; + + get_shutter(icd, &old); + dev_dbg(&icd->dev, "Setting shutter width from %u to %u\n", + old, shutter); + if (set_shutter(icd, shutter) < 0) + return -EIO; + icd->exposure = ctrl->value; + mt9t031->autoexposure = 0; + } + break; + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->value) { + const u16 vblank = MT9T031_VERTICAL_BLANK; + const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank; + if (set_shutter(icd, icd->height + + icd->y_skip_top + vblank) < 0) + return -EIO; + qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); + icd->exposure = (shutter_max / 2 + (icd->height + + icd->y_skip_top + vblank - 1) * + (qctrl->maximum - qctrl->minimum)) / + shutter_max + qctrl->minimum; + mt9t031->autoexposure = 1; + } else + mt9t031->autoexposure = 0; + break; + } + return 0; +} + +/* Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one */ +static int mt9t031_video_probe(struct soc_camera_device *icd) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + s32 data; + int ret; + + /* We must have a parent by now. And it cannot be a wrong one. + * So this entire test is completely redundant. */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) + return -ENODEV; + + /* Enable the chip */ + data = reg_write(icd, MT9T031_CHIP_ENABLE, 1); + dev_dbg(&icd->dev, "write: %d\n", data); + + /* Read out the chip version register */ + data = reg_read(icd, MT9T031_CHIP_VERSION); + + switch (data) { + case 0x1621: + mt9t031->model = V4L2_IDENT_MT9T031; + icd->formats = mt9t031_colour_formats; + icd->num_formats = ARRAY_SIZE(mt9t031_colour_formats); + break; + default: + ret = -ENODEV; + dev_err(&icd->dev, + "No MT9T031 chip detected, register read %x\n", data); + goto ei2c; + } + + dev_info(&icd->dev, "Detected a MT9T031 chip ID %x\n", data); + + /* Now that we know the model, we can start video */ + ret = soc_camera_video_start(icd); + if (ret) + goto evstart; + + return 0; + +evstart: +ei2c: + return ret; +} + +static void mt9t031_video_remove(struct soc_camera_device *icd) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + + dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9t031->client->addr, + icd->dev.parent, icd->vdev); + soc_camera_video_stop(icd); +} + +static int mt9t031_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9t031 *mt9t031; + struct soc_camera_device *icd; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl = client->dev.platform_data; + int ret; + + if (!icl) { + dev_err(&client->dev, "MT9T031 driver needs platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9t031 = kzalloc(sizeof(struct mt9t031), GFP_KERNEL); + if (!mt9t031) + return -ENOMEM; + + mt9t031->client = client; + i2c_set_clientdata(client, mt9t031); + + /* Second stage probe - when a capture adapter is there */ + icd = &mt9t031->icd; + icd->ops = &mt9t031_ops; + icd->control = &client->dev; + icd->x_min = MT9T031_COLUMN_SKIP; + icd->y_min = MT9T031_ROW_SKIP; + icd->x_current = icd->x_min; + icd->y_current = icd->y_min; + icd->width_min = MT9T031_MIN_WIDTH; + icd->width_max = MT9T031_MAX_WIDTH; + icd->height_min = MT9T031_MIN_HEIGHT; + icd->height_max = MT9T031_MAX_HEIGHT; + icd->y_skip_top = 0; + icd->iface = icl->bus_id; + /* Simulated autoexposure. If enabled, we calculate shutter width + * ourselves in the driver based on vertical blanking and frame width */ + mt9t031->autoexposure = 1; + + mt9t031->xskip = 1; + mt9t031->yskip = 1; + + ret = soc_camera_device_register(icd); + if (ret) + goto eisdr; + + return 0; + +eisdr: + i2c_set_clientdata(client, NULL); + kfree(mt9t031); + return ret; +} + +static int mt9t031_remove(struct i2c_client *client) +{ + struct mt9t031 *mt9t031 = i2c_get_clientdata(client); + + soc_camera_device_unregister(&mt9t031->icd); + i2c_set_clientdata(client, NULL); + kfree(mt9t031); + + return 0; +} + +static const struct i2c_device_id mt9t031_id[] = { + { "mt9t031", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9t031_id); + +static struct i2c_driver mt9t031_i2c_driver = { + .driver = { + .name = "mt9t031", + }, + .probe = mt9t031_probe, + .remove = mt9t031_remove, + .id_table = mt9t031_id, +}; + +static int __init mt9t031_mod_init(void) +{ + return i2c_add_driver(&mt9t031_i2c_driver); +} + +static void __exit mt9t031_mod_exit(void) +{ + i2c_del_driver(&mt9t031_i2c_driver); +} + +module_init(mt9t031_mod_init); +module_exit(mt9t031_mod_exit); + +MODULE_DESCRIPTION("Micron MT9T031 Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 2584201..14a5f9c 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -273,6 +273,7 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct soc_camera_link *icl = mt9v022->client->dev.platform_data; unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK; int ret; u16 pixclk = 0; @@ -296,6 +297,8 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd, mt9v022->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10; } + flags = soc_camera_apply_sensor_flags(icl, flags); + if (flags & SOCAM_PCLK_SAMPLE_RISING) pixclk |= 0x10; @@ -337,14 +340,14 @@ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) width_flag; } -static int mt9v022_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int mt9v022_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); int ret; /* The caller provides a supported format, as verified per call to - * icd->try_fmt_cap(), datawidth is from our supported format list */ + * icd->try_fmt(), datawidth is from our supported format list */ switch (pixfmt) { case V4L2_PIX_FMT_GREY: case V4L2_PIX_FMT_Y16: @@ -400,18 +403,20 @@ static int mt9v022_set_fmt_cap(struct soc_camera_device *icd, return 0; } -static int mt9v022_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9v022_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { - if (f->fmt.pix.height < 32 + icd->y_skip_top) - f->fmt.pix.height = 32 + icd->y_skip_top; - if (f->fmt.pix.height > 480 + icd->y_skip_top) - f->fmt.pix.height = 480 + icd->y_skip_top; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.width > 752) - f->fmt.pix.width = 752; - f->fmt.pix.width &= ~0x03; /* ? */ + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (pix->height < 32 + icd->y_skip_top) + pix->height = 32 + icd->y_skip_top; + if (pix->height > 480 + icd->y_skip_top) + pix->height = 480 + icd->y_skip_top; + if (pix->width < 48) + pix->width = 48; + if (pix->width > 752) + pix->width = 752; + pix->width &= ~0x03; /* ? */ return 0; } @@ -538,8 +543,8 @@ static struct soc_camera_ops mt9v022_ops = { .release = mt9v022_release, .start_capture = mt9v022_start_capture, .stop_capture = mt9v022_stop_capture, - .set_fmt_cap = mt9v022_set_fmt_cap, - .try_fmt_cap = mt9v022_try_fmt_cap, + .set_fmt = mt9v022_set_fmt, + .try_fmt = mt9v022_try_fmt, .set_bus_param = mt9v022_set_bus_param, .query_bus_param = mt9v022_query_bus_param, .controls = mt9v022_controls, @@ -690,6 +695,7 @@ static int mt9v022_set_control(struct soc_camera_device *icd, static int mt9v022_video_probe(struct soc_camera_device *icd) { struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct soc_camera_link *icl = mt9v022->client->dev.platform_data; s32 data; int ret; @@ -725,7 +731,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11); mt9v022->model = V4L2_IDENT_MT9V022IX7ATC; icd->formats = mt9v022_colour_formats; - if (mt9v022->client->dev.platform_data) + if (gpio_is_valid(icl->gpio)) icd->num_formats = ARRAY_SIZE(mt9v022_colour_formats); else icd->num_formats = 1; @@ -733,7 +739,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 0x11); mt9v022->model = V4L2_IDENT_MT9V022IX7ATM; icd->formats = mt9v022_monochrome_formats; - if (mt9v022->client->dev.platform_data) + if (gpio_is_valid(icl->gpio)) icd->num_formats = ARRAY_SIZE(mt9v022_monochrome_formats); else icd->num_formats = 1; @@ -760,8 +766,8 @@ static void mt9v022_video_remove(struct soc_camera_device *icd) struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9v022->client->addr, - mt9v022->icd.dev.parent, mt9v022->icd.vdev); - soc_camera_video_stop(&mt9v022->icd); + icd->dev.parent, icd->vdev); + soc_camera_video_stop(icd); } static int mt9v022_probe(struct i2c_client *client, diff --git a/drivers/media/video/omap24xxcam-dma.c b/drivers/media/video/omap24xxcam-dma.c new file mode 100644 index 0000000..1d54b86 --- /dev/null +++ b/drivers/media/video/omap24xxcam-dma.c @@ -0,0 +1,601 @@ +/* + * drivers/media/video/omap24xxcam-dma.c + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2004 Texas Instruments. + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Sakari Ailus <sakari.ailus@nokia.com> + * + * Based on code from Andy Lowe <source@mvista.com> and + * David Cohen <david.cohen@indt.org.br>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/scatterlist.h> + +#include "omap24xxcam.h" + +/* + * + * DMA hardware. + * + */ + +/* Ack all interrupt on CSR and IRQSTATUS_L0 */ +static void omap24xxcam_dmahw_ack_all(unsigned long base) +{ + u32 csr; + int i; + + for (i = 0; i < NUM_CAMDMA_CHANNELS; ++i) { + csr = omap24xxcam_reg_in(base, CAMDMA_CSR(i)); + /* ack interrupt in CSR */ + omap24xxcam_reg_out(base, CAMDMA_CSR(i), csr); + } + omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, 0xf); +} + +/* Ack dmach on CSR and IRQSTATUS_L0 */ +static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach) +{ + u32 csr; + + csr = omap24xxcam_reg_in(base, CAMDMA_CSR(dmach)); + /* ack interrupt in CSR */ + omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), csr); + /* ack interrupt in IRQSTATUS */ + omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, (1 << dmach)); + + return csr; +} + +static int omap24xxcam_dmahw_running(unsigned long base, int dmach) +{ + return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE; +} + +static void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach, + dma_addr_t start, u32 len) +{ + omap24xxcam_reg_out(base, CAMDMA_CCR(dmach), + CAMDMA_CCR_SEL_SRC_DST_SYNC + | CAMDMA_CCR_BS + | CAMDMA_CCR_DST_AMODE_POST_INC + | CAMDMA_CCR_SRC_AMODE_POST_INC + | CAMDMA_CCR_FS + | CAMDMA_CCR_WR_ACTIVE + | CAMDMA_CCR_RD_ACTIVE + | CAMDMA_CCR_SYNCHRO_CAMERA); + omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(dmach), 0); + omap24xxcam_reg_out(base, CAMDMA_CEN(dmach), len); + omap24xxcam_reg_out(base, CAMDMA_CFN(dmach), 1); + omap24xxcam_reg_out(base, CAMDMA_CSDP(dmach), + CAMDMA_CSDP_WRITE_MODE_POSTED + | CAMDMA_CSDP_DST_BURST_EN_32 + | CAMDMA_CSDP_DST_PACKED + | CAMDMA_CSDP_SRC_BURST_EN_32 + | CAMDMA_CSDP_SRC_PACKED + | CAMDMA_CSDP_DATA_TYPE_8BITS); + omap24xxcam_reg_out(base, CAMDMA_CSSA(dmach), 0); + omap24xxcam_reg_out(base, CAMDMA_CDSA(dmach), start); + omap24xxcam_reg_out(base, CAMDMA_CSEI(dmach), 0); + omap24xxcam_reg_out(base, CAMDMA_CSFI(dmach), DMA_THRESHOLD); + omap24xxcam_reg_out(base, CAMDMA_CDEI(dmach), 0); + omap24xxcam_reg_out(base, CAMDMA_CDFI(dmach), 0); + omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), + CAMDMA_CSR_MISALIGNED_ERR + | CAMDMA_CSR_SECURE_ERR + | CAMDMA_CSR_TRANS_ERR + | CAMDMA_CSR_BLOCK + | CAMDMA_CSR_DROP); + omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), + CAMDMA_CICR_MISALIGNED_ERR_IE + | CAMDMA_CICR_SECURE_ERR_IE + | CAMDMA_CICR_TRANS_ERR_IE + | CAMDMA_CICR_BLOCK_IE + | CAMDMA_CICR_DROP_IE); +} + +static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach) +{ + omap24xxcam_reg_out(base, CAMDMA_CCR(dmach), + CAMDMA_CCR_SEL_SRC_DST_SYNC + | CAMDMA_CCR_BS + | CAMDMA_CCR_DST_AMODE_POST_INC + | CAMDMA_CCR_SRC_AMODE_POST_INC + | CAMDMA_CCR_ENABLE + | CAMDMA_CCR_FS + | CAMDMA_CCR_SYNCHRO_CAMERA); +} + +static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach, + int free_dmach) +{ + int prev_dmach, ch; + + if (dmach == 0) + prev_dmach = NUM_CAMDMA_CHANNELS - 1; + else + prev_dmach = dmach - 1; + omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(prev_dmach), + CAMDMA_CLNK_CTRL_ENABLE_LNK | dmach); + /* Did we chain the DMA transfer before the previous one + * finished? + */ + ch = (dmach + free_dmach) % NUM_CAMDMA_CHANNELS; + while (!(omap24xxcam_reg_in(base, CAMDMA_CCR(ch)) + & CAMDMA_CCR_ENABLE)) { + if (ch == dmach) { + /* The previous transfer has ended and this one + * hasn't started, so we must not have chained + * to the previous one in time. We'll have to + * start it now. + */ + omap24xxcam_dmahw_transfer_start(base, dmach); + break; + } else + ch = (ch + 1) % NUM_CAMDMA_CHANNELS; + } +} + +/* Abort all chained DMA transfers. After all transfers have been + * aborted and the DMA controller is idle, the completion routines for + * any aborted transfers will be called in sequence. The DMA + * controller may not be idle after this routine completes, because + * the completion routines might start new transfers. + */ +static void omap24xxcam_dmahw_abort_ch(unsigned long base, int dmach) +{ + /* mask all interrupts from this channel */ + omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0); + /* unlink this channel */ + omap24xxcam_reg_merge(base, CAMDMA_CLNK_CTRL(dmach), 0, + CAMDMA_CLNK_CTRL_ENABLE_LNK); + /* disable this channel */ + omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE); +} + +static void omap24xxcam_dmahw_init(unsigned long base) +{ + omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG, + CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY + | CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE + | CAMDMA_OCP_SYSCONFIG_AUTOIDLE); + + omap24xxcam_reg_merge(base, CAMDMA_GCR, 0x10, + CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH); + + omap24xxcam_reg_out(base, CAMDMA_IRQENABLE_L0, 0xf); +} + +/* + * + * Individual DMA channel handling. + * + */ + +/* Start a DMA transfer from the camera to memory. + * Returns zero if the transfer was successfully started, or non-zero if all + * DMA channels are already in use or starting is currently inhibited. + */ +static int omap24xxcam_dma_start(struct omap24xxcam_dma *dma, dma_addr_t start, + u32 len, dma_callback_t callback, void *arg) +{ + unsigned long flags; + int dmach; + + spin_lock_irqsave(&dma->lock, flags); + + if (!dma->free_dmach || atomic_read(&dma->dma_stop)) { + spin_unlock_irqrestore(&dma->lock, flags); + return -EBUSY; + } + + dmach = dma->next_dmach; + + dma->ch_state[dmach].callback = callback; + dma->ch_state[dmach].arg = arg; + + omap24xxcam_dmahw_transfer_setup(dma->base, dmach, start, len); + + /* We're ready to start the DMA transfer. */ + + if (dma->free_dmach < NUM_CAMDMA_CHANNELS) { + /* A transfer is already in progress, so try to chain to it. */ + omap24xxcam_dmahw_transfer_chain(dma->base, dmach, + dma->free_dmach); + } else { + /* No transfer is in progress, so we'll just start this one + * now. + */ + omap24xxcam_dmahw_transfer_start(dma->base, dmach); + } + + dma->next_dmach = (dma->next_dmach + 1) % NUM_CAMDMA_CHANNELS; + dma->free_dmach--; + + spin_unlock_irqrestore(&dma->lock, flags); + + return 0; +} + +/* Abort all chained DMA transfers. After all transfers have been + * aborted and the DMA controller is idle, the completion routines for + * any aborted transfers will be called in sequence. The DMA + * controller may not be idle after this routine completes, because + * the completion routines might start new transfers. + */ +static void omap24xxcam_dma_abort(struct omap24xxcam_dma *dma, u32 csr) +{ + unsigned long flags; + int dmach, i, free_dmach; + dma_callback_t callback; + void *arg; + + spin_lock_irqsave(&dma->lock, flags); + + /* stop any DMA transfers in progress */ + dmach = (dma->next_dmach + dma->free_dmach) % NUM_CAMDMA_CHANNELS; + for (i = 0; i < NUM_CAMDMA_CHANNELS; i++) { + omap24xxcam_dmahw_abort_ch(dma->base, dmach); + dmach = (dmach + 1) % NUM_CAMDMA_CHANNELS; + } + + /* We have to be careful here because the callback routine + * might start a new DMA transfer, and we only want to abort + * transfers that were started before this routine was called. + */ + free_dmach = dma->free_dmach; + while ((dma->free_dmach < NUM_CAMDMA_CHANNELS) && + (free_dmach < NUM_CAMDMA_CHANNELS)) { + dmach = (dma->next_dmach + dma->free_dmach) + % NUM_CAMDMA_CHANNELS; + callback = dma->ch_state[dmach].callback; + arg = dma->ch_state[dmach].arg; + dma->free_dmach++; + free_dmach++; + if (callback) { + /* leave interrupts disabled during callback */ + spin_unlock(&dma->lock); + (*callback) (dma, csr, arg); + spin_lock(&dma->lock); + } + } + + spin_unlock_irqrestore(&dma->lock, flags); +} + +/* Abort all chained DMA transfers. After all transfers have been + * aborted and the DMA controller is idle, the completion routines for + * any aborted transfers will be called in sequence. If the completion + * routines attempt to start a new DMA transfer it will fail, so the + * DMA controller will be idle after this routine completes. + */ +static void omap24xxcam_dma_stop(struct omap24xxcam_dma *dma, u32 csr) +{ + atomic_inc(&dma->dma_stop); + omap24xxcam_dma_abort(dma, csr); + atomic_dec(&dma->dma_stop); +} + +/* Camera DMA interrupt service routine. */ +void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma) +{ + int dmach; + dma_callback_t callback; + void *arg; + u32 csr; + const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR + | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR + | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; + + spin_lock(&dma->lock); + + if (dma->free_dmach == NUM_CAMDMA_CHANNELS) { + /* A camera DMA interrupt occurred while all channels + * are idle, so we'll acknowledge the interrupt in the + * IRQSTATUS register and exit. + */ + omap24xxcam_dmahw_ack_all(dma->base); + spin_unlock(&dma->lock); + return; + } + + while (dma->free_dmach < NUM_CAMDMA_CHANNELS) { + dmach = (dma->next_dmach + dma->free_dmach) + % NUM_CAMDMA_CHANNELS; + if (omap24xxcam_dmahw_running(dma->base, dmach)) { + /* This buffer hasn't finished yet, so we're done. */ + break; + } + csr = omap24xxcam_dmahw_ack_ch(dma->base, dmach); + if (csr & csr_error) { + /* A DMA error occurred, so stop all DMA + * transfers in progress. + */ + spin_unlock(&dma->lock); + omap24xxcam_dma_stop(dma, csr); + return; + } else { + callback = dma->ch_state[dmach].callback; + arg = dma->ch_state[dmach].arg; + dma->free_dmach++; + if (callback) { + spin_unlock(&dma->lock); + (*callback) (dma, csr, arg); + spin_lock(&dma->lock); + } + } + } + + spin_unlock(&dma->lock); + + omap24xxcam_sgdma_process( + container_of(dma, struct omap24xxcam_sgdma, dma)); +} + +void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma) +{ + unsigned long flags; + + spin_lock_irqsave(&dma->lock, flags); + + omap24xxcam_dmahw_init(dma->base); + + spin_unlock_irqrestore(&dma->lock, flags); +} + +static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma, + unsigned long base) +{ + int ch; + + /* group all channels on DMA IRQ0 and unmask irq */ + spin_lock_init(&dma->lock); + dma->base = base; + dma->free_dmach = NUM_CAMDMA_CHANNELS; + dma->next_dmach = 0; + for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) { + dma->ch_state[ch].callback = NULL; + dma->ch_state[ch].arg = NULL; + } +} + +/* + * + * Scatter-gather DMA. + * + * High-level DMA construct for transferring whole picture frames to + * memory that is discontinuous. + * + */ + +/* DMA completion routine for the scatter-gather DMA fragments. */ +static void omap24xxcam_sgdma_callback(struct omap24xxcam_dma *dma, u32 csr, + void *arg) +{ + struct omap24xxcam_sgdma *sgdma = + container_of(dma, struct omap24xxcam_sgdma, dma); + int sgslot = (int)arg; + struct sgdma_state *sg_state; + const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR + | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR + | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; + + spin_lock(&sgdma->lock); + + /* We got an interrupt, we can remove the timer */ + del_timer(&sgdma->reset_timer); + + sg_state = sgdma->sg_state + sgslot; + if (!sg_state->queued_sglist) { + spin_unlock(&sgdma->lock); + printk(KERN_ERR "%s: sgdma completed when none queued!\n", + __func__); + return; + } + + sg_state->csr |= csr; + if (!--sg_state->queued_sglist) { + /* Queue for this sglist is empty, so check to see if we're + * done. + */ + if ((sg_state->next_sglist == sg_state->sglen) + || (sg_state->csr & csr_error)) { + sgdma_callback_t callback = sg_state->callback; + void *arg = sg_state->arg; + u32 sg_csr = sg_state->csr; + /* All done with this sglist */ + sgdma->free_sgdma++; + if (callback) { + spin_unlock(&sgdma->lock); + (*callback) (sgdma, sg_csr, arg); + return; + } + } + } + + spin_unlock(&sgdma->lock); +} + +/* Start queued scatter-gather DMA transfers. */ +void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma) +{ + unsigned long flags; + int queued_sgdma, sgslot; + struct sgdma_state *sg_state; + const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR + | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR + | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; + + spin_lock_irqsave(&sgdma->lock, flags); + + queued_sgdma = NUM_SG_DMA - sgdma->free_sgdma; + sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA; + while (queued_sgdma > 0) { + sg_state = sgdma->sg_state + sgslot; + while ((sg_state->next_sglist < sg_state->sglen) && + !(sg_state->csr & csr_error)) { + const struct scatterlist *sglist; + unsigned int len; + + sglist = sg_state->sglist + sg_state->next_sglist; + /* try to start the next DMA transfer */ + if (sg_state->next_sglist + 1 == sg_state->sglen) { + /* + * On the last sg, we handle the case where + * cam->img.pix.sizeimage % PAGE_ALIGN != 0 + */ + len = sg_state->len - sg_state->bytes_read; + } else { + len = sg_dma_len(sglist); + } + + if (omap24xxcam_dma_start(&sgdma->dma, + sg_dma_address(sglist), + len, + omap24xxcam_sgdma_callback, + (void *)sgslot)) { + /* DMA start failed */ + spin_unlock_irqrestore(&sgdma->lock, flags); + return; + } else { + unsigned long expires; + /* DMA start was successful */ + sg_state->next_sglist++; + sg_state->bytes_read += len; + sg_state->queued_sglist++; + + /* We start the reset timer */ + expires = jiffies + HZ; + mod_timer(&sgdma->reset_timer, expires); + } + } + queued_sgdma--; + sgslot = (sgslot + 1) % NUM_SG_DMA; + } + + spin_unlock_irqrestore(&sgdma->lock, flags); +} + +/* + * Queue a scatter-gather DMA transfer from the camera to memory. + * Returns zero if the transfer was successfully queued, or non-zero + * if all of the scatter-gather slots are already in use. + */ +int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma, + const struct scatterlist *sglist, int sglen, + int len, sgdma_callback_t callback, void *arg) +{ + unsigned long flags; + struct sgdma_state *sg_state; + + if ((sglen < 0) || ((sglen > 0) & !sglist)) + return -EINVAL; + + spin_lock_irqsave(&sgdma->lock, flags); + + if (!sgdma->free_sgdma) { + spin_unlock_irqrestore(&sgdma->lock, flags); + return -EBUSY; + } + + sg_state = sgdma->sg_state + sgdma->next_sgdma; + + sg_state->sglist = sglist; + sg_state->sglen = sglen; + sg_state->next_sglist = 0; + sg_state->bytes_read = 0; + sg_state->len = len; + sg_state->queued_sglist = 0; + sg_state->csr = 0; + sg_state->callback = callback; + sg_state->arg = arg; + + sgdma->next_sgdma = (sgdma->next_sgdma + 1) % NUM_SG_DMA; + sgdma->free_sgdma--; + + spin_unlock_irqrestore(&sgdma->lock, flags); + + omap24xxcam_sgdma_process(sgdma); + + return 0; +} + +/* Sync scatter-gather DMA by aborting any DMA transfers currently in progress. + * Any queued scatter-gather DMA transactions that have not yet been started + * will remain queued. The DMA controller will be idle after this routine + * completes. When the scatter-gather queue is restarted, the next + * scatter-gather DMA transfer will begin at the start of a new transaction. + */ +void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma) +{ + unsigned long flags; + int sgslot; + struct sgdma_state *sg_state; + u32 csr = CAMDMA_CSR_TRANS_ERR; + + /* stop any DMA transfers in progress */ + omap24xxcam_dma_stop(&sgdma->dma, csr); + + spin_lock_irqsave(&sgdma->lock, flags); + + if (sgdma->free_sgdma < NUM_SG_DMA) { + sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA; + sg_state = sgdma->sg_state + sgslot; + if (sg_state->next_sglist != 0) { + /* This DMA transfer was in progress, so abort it. */ + sgdma_callback_t callback = sg_state->callback; + void *arg = sg_state->arg; + sgdma->free_sgdma++; + if (callback) { + /* leave interrupts masked */ + spin_unlock(&sgdma->lock); + (*callback) (sgdma, csr, arg); + spin_lock(&sgdma->lock); + } + } + } + + spin_unlock_irqrestore(&sgdma->lock, flags); +} + +void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma, + unsigned long base, + void (*reset_callback)(unsigned long data), + unsigned long reset_callback_data) +{ + int sg; + + spin_lock_init(&sgdma->lock); + sgdma->free_sgdma = NUM_SG_DMA; + sgdma->next_sgdma = 0; + for (sg = 0; sg < NUM_SG_DMA; sg++) { + sgdma->sg_state[sg].sglen = 0; + sgdma->sg_state[sg].next_sglist = 0; + sgdma->sg_state[sg].bytes_read = 0; + sgdma->sg_state[sg].queued_sglist = 0; + sgdma->sg_state[sg].csr = 0; + sgdma->sg_state[sg].callback = NULL; + sgdma->sg_state[sg].arg = NULL; + } + + omap24xxcam_dma_init(&sgdma->dma, base); + setup_timer(&sgdma->reset_timer, reset_callback, reset_callback_data); +} diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c new file mode 100644 index 0000000..85c3c7c --- /dev/null +++ b/drivers/media/video/omap24xxcam.c @@ -0,0 +1,1908 @@ +/* + * drivers/media/video/omap24xxcam.c + * + * OMAP 2 camera block driver. + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2004 Texas Instruments. + * Copyright (C) 2007-2008 Nokia Corporation. + * + * Contact: Sakari Ailus <sakari.ailus@nokia.com> + * + * Based on code from Andy Lowe <source@mvista.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/videodev2.h> +#include <linux/pci.h> /* needed for videobufs */ +#include <linux/version.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> + +#include "omap24xxcam.h" + +#define OMAP24XXCAM_VERSION KERNEL_VERSION(0, 0, 0) + +#define RESET_TIMEOUT_NS 10000 + +static void omap24xxcam_reset(struct omap24xxcam_device *cam); +static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam); +static void omap24xxcam_device_unregister(struct v4l2_int_device *s); +static int omap24xxcam_remove(struct platform_device *pdev); + +/* module parameters */ +static int video_nr = -1; /* video device minor (-1 ==> auto assign) */ +/* + * Maximum amount of memory to use for capture buffers. + * Default is 4800KB, enough to double-buffer SXGA. + */ +static int capture_mem = 1280 * 960 * 2 * 2; + +static struct v4l2_int_device omap24xxcam; + +/* + * + * Clocks. + * + */ + +static void omap24xxcam_clock_put(struct omap24xxcam_device *cam) +{ + if (cam->ick != NULL && !IS_ERR(cam->ick)) + clk_put(cam->ick); + if (cam->fck != NULL && !IS_ERR(cam->fck)) + clk_put(cam->fck); + + cam->ick = cam->fck = NULL; +} + +static int omap24xxcam_clock_get(struct omap24xxcam_device *cam) +{ + int rval = 0; + + cam->fck = clk_get(cam->dev, "cam_fck"); + if (IS_ERR(cam->fck)) { + dev_err(cam->dev, "can't get cam_fck"); + rval = PTR_ERR(cam->fck); + omap24xxcam_clock_put(cam); + return rval; + } + + cam->ick = clk_get(cam->dev, "cam_ick"); + if (IS_ERR(cam->ick)) { + dev_err(cam->dev, "can't get cam_ick"); + rval = PTR_ERR(cam->ick); + omap24xxcam_clock_put(cam); + } + + return rval; +} + +static void omap24xxcam_clock_on(struct omap24xxcam_device *cam) +{ + clk_enable(cam->fck); + clk_enable(cam->ick); +} + +static void omap24xxcam_clock_off(struct omap24xxcam_device *cam) +{ + clk_disable(cam->fck); + clk_disable(cam->ick); +} + +/* + * + * Camera core + * + */ + +/* + * Set xclk. + * + * To disable xclk, use value zero. + */ +static void omap24xxcam_core_xclk_set(const struct omap24xxcam_device *cam, + u32 xclk) +{ + if (xclk) { + u32 divisor = CAM_MCLK / xclk; + + if (divisor == 1) + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, + CC_CTRL_XCLK, + CC_CTRL_XCLK_DIV_BYPASS); + else + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, + CC_CTRL_XCLK, divisor); + } else + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, + CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW); +} + +static void omap24xxcam_core_hwinit(const struct omap24xxcam_device *cam) +{ + /* + * Setting the camera core AUTOIDLE bit causes problems with frame + * synchronization, so we will clear the AUTOIDLE bit instead. + */ + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_SYSCONFIG, + CC_SYSCONFIG_AUTOIDLE); + + /* program the camera interface DMA packet size */ + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL_DMA, + CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1)); + + /* enable camera core error interrupts */ + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQENABLE, + CC_IRQENABLE_FW_ERR_IRQ + | CC_IRQENABLE_FSC_ERR_IRQ + | CC_IRQENABLE_SSC_ERR_IRQ + | CC_IRQENABLE_FIFO_OF_IRQ); +} + +/* + * Enable the camera core. + * + * Data transfer to the camera DMA starts from next starting frame. + */ +static void omap24xxcam_core_enable(const struct omap24xxcam_device *cam) +{ + + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL, + cam->cc_ctrl); +} + +/* + * Disable camera core. + * + * The data transfer will be stopped immediately (CC_CTRL_CC_RST). The + * core internal state machines will be reset. Use + * CC_CTRL_CC_FRAME_TRIG instead if you want to transfer the current + * frame completely. + */ +static void omap24xxcam_core_disable(const struct omap24xxcam_device *cam) +{ + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL, + CC_CTRL_CC_RST); +} + +/* Interrupt service routine for camera core interrupts. */ +static void omap24xxcam_core_isr(struct omap24xxcam_device *cam) +{ + u32 cc_irqstatus; + const u32 cc_irqstatus_err = + CC_IRQSTATUS_FW_ERR_IRQ + | CC_IRQSTATUS_FSC_ERR_IRQ + | CC_IRQSTATUS_SSC_ERR_IRQ + | CC_IRQSTATUS_FIFO_UF_IRQ + | CC_IRQSTATUS_FIFO_OF_IRQ; + + cc_irqstatus = omap24xxcam_reg_in(cam->mmio_base + CC_REG_OFFSET, + CC_IRQSTATUS); + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQSTATUS, + cc_irqstatus); + + if (cc_irqstatus & cc_irqstatus_err + && !atomic_read(&cam->in_reset)) { + dev_dbg(cam->dev, "resetting camera, cc_irqstatus 0x%x\n", + cc_irqstatus); + omap24xxcam_reset(cam); + } +} + +/* + * + * videobuf_buffer handling. + * + * Memory for mmapped videobuf_buffers is not allocated + * conventionally, but by several kmalloc allocations and then + * creating the scatterlist on our own. User-space buffers are handled + * normally. + * + */ + +/* + * Free the memory-mapped buffer memory allocated for a + * videobuf_buffer and the associated scatterlist. + */ +static void omap24xxcam_vbq_free_mmap_buffer(struct videobuf_buffer *vb) +{ + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + size_t alloc_size; + struct page *page; + int i; + + if (dma->sglist == NULL) + return; + + i = dma->sglen; + while (i) { + i--; + alloc_size = sg_dma_len(&dma->sglist[i]); + page = sg_page(&dma->sglist[i]); + do { + ClearPageReserved(page++); + } while (alloc_size -= PAGE_SIZE); + __free_pages(sg_page(&dma->sglist[i]), + get_order(sg_dma_len(&dma->sglist[i]))); + } + + kfree(dma->sglist); + dma->sglist = NULL; +} + +/* Release all memory related to the videobuf_queue. */ +static void omap24xxcam_vbq_free_mmap_buffers(struct videobuf_queue *vbq) +{ + int i; + + mutex_lock(&vbq->vb_lock); + + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (NULL == vbq->bufs[i]) + continue; + if (V4L2_MEMORY_MMAP != vbq->bufs[i]->memory) + continue; + vbq->ops->buf_release(vbq, vbq->bufs[i]); + omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]); + kfree(vbq->bufs[i]); + vbq->bufs[i] = NULL; + } + + mutex_unlock(&vbq->vb_lock); + + videobuf_mmap_free(vbq); +} + +/* + * Allocate physically as contiguous as possible buffer for video + * frame and allocate and build DMA scatter-gather list for it. + */ +static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb) +{ + unsigned int order; + size_t alloc_size, size = vb->bsize; /* vb->bsize is page aligned */ + struct page *page; + int max_pages, err = 0, i = 0; + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + + /* + * allocate maximum size scatter-gather list. Note this is + * overhead. We may not use as many entries as we allocate + */ + max_pages = vb->bsize >> PAGE_SHIFT; + dma->sglist = kcalloc(max_pages, sizeof(*dma->sglist), GFP_KERNEL); + if (dma->sglist == NULL) { + err = -ENOMEM; + goto out; + } + + while (size) { + order = get_order(size); + /* + * do not over-allocate even if we would get larger + * contiguous chunk that way + */ + if ((PAGE_SIZE << order) > size) + order--; + + /* try to allocate as many contiguous pages as possible */ + page = alloc_pages(GFP_KERNEL | GFP_DMA, order); + /* if allocation fails, try to allocate smaller amount */ + while (page == NULL) { + order--; + page = alloc_pages(GFP_KERNEL | GFP_DMA, order); + if (page == NULL && !order) { + err = -ENOMEM; + goto out; + } + } + size -= (PAGE_SIZE << order); + + /* append allocated chunk of pages into scatter-gather list */ + sg_set_page(&dma->sglist[i], page, PAGE_SIZE << order, 0); + dma->sglen++; + i++; + + alloc_size = (PAGE_SIZE << order); + + /* clear pages before giving them to user space */ + memset(page_address(page), 0, alloc_size); + + /* mark allocated pages reserved */ + do { + SetPageReserved(page++); + } while (alloc_size -= PAGE_SIZE); + } + /* + * REVISIT: not fully correct to assign nr_pages == sglen but + * video-buf is passing nr_pages for e.g. unmap_sg calls + */ + dma->nr_pages = dma->sglen; + dma->direction = PCI_DMA_FROMDEVICE; + + return 0; + +out: + omap24xxcam_vbq_free_mmap_buffer(vb); + return err; +} + +static int omap24xxcam_vbq_alloc_mmap_buffers(struct videobuf_queue *vbq, + unsigned int count) +{ + int i, err = 0; + struct omap24xxcam_fh *fh = + container_of(vbq, struct omap24xxcam_fh, vbq); + + mutex_lock(&vbq->vb_lock); + + for (i = 0; i < count; i++) { + err = omap24xxcam_vbq_alloc_mmap_buffer(vbq->bufs[i]); + if (err) + goto out; + dev_dbg(fh->cam->dev, "sglen is %d for buffer %d\n", + videobuf_to_dma(vbq->bufs[i])->sglen, i); + } + + mutex_unlock(&vbq->vb_lock); + + return 0; +out: + while (i) { + i--; + omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]); + } + + mutex_unlock(&vbq->vb_lock); + + return err; +} + +/* + * This routine is called from interrupt context when a scatter-gather DMA + * transfer of a videobuf_buffer completes. + */ +static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma, + u32 csr, void *arg) +{ + struct omap24xxcam_device *cam = + container_of(sgdma, struct omap24xxcam_device, sgdma); + struct omap24xxcam_fh *fh = cam->streaming->private_data; + struct videobuf_buffer *vb = (struct videobuf_buffer *)arg; + const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR + | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR + | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; + unsigned long flags; + + spin_lock_irqsave(&cam->core_enable_disable_lock, flags); + if (--cam->sgdma_in_queue == 0) + omap24xxcam_core_disable(cam); + spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); + + do_gettimeofday(&vb->ts); + vb->field_count = atomic_add_return(2, &fh->field_count); + if (csr & csr_error) { + vb->state = VIDEOBUF_ERROR; + if (!atomic_read(&fh->cam->in_reset)) { + dev_dbg(cam->dev, "resetting camera, csr 0x%x\n", csr); + omap24xxcam_reset(cam); + } + } else + vb->state = VIDEOBUF_DONE; + wake_up(&vb->done); +} + +static void omap24xxcam_vbq_release(struct videobuf_queue *vbq, + struct videobuf_buffer *vb) +{ + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + + /* wait for buffer, especially to get out of the sgdma queue */ + videobuf_waiton(vb, 0, 0); + if (vb->memory == V4L2_MEMORY_MMAP) { + dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen, + dma->direction); + dma->direction = DMA_NONE; + } else { + videobuf_dma_unmap(vbq, videobuf_to_dma(vb)); + videobuf_dma_free(videobuf_to_dma(vb)); + } + + vb->state = VIDEOBUF_NEEDS_INIT; +} + +/* + * Limit the number of available kernel image capture buffers based on the + * number requested, the currently selected image size, and the maximum + * amount of memory permitted for kernel capture buffers. + */ +static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt, + unsigned int *size) +{ + struct omap24xxcam_fh *fh = vbq->priv_data; + + if (*cnt <= 0) + *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */ + + if (*cnt > VIDEO_MAX_FRAME) + *cnt = VIDEO_MAX_FRAME; + + *size = fh->pix.sizeimage; + + /* accessing fh->cam->capture_mem is ok, it's constant */ + while (*size * *cnt > fh->cam->capture_mem) + (*cnt)--; + + return 0; +} + +static int omap24xxcam_dma_iolock(struct videobuf_queue *vbq, + struct videobuf_dmabuf *dma) +{ + int err = 0; + + dma->direction = PCI_DMA_FROMDEVICE; + if (!dma_map_sg(vbq->dev, dma->sglist, dma->sglen, dma->direction)) { + kfree(dma->sglist); + dma->sglist = NULL; + dma->sglen = 0; + err = -EIO; + } + + return err; +} + +static int omap24xxcam_vbq_prepare(struct videobuf_queue *vbq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct omap24xxcam_fh *fh = vbq->priv_data; + int err = 0; + + /* + * Accessing pix here is okay since it's constant while + * streaming is on (and we only get called then). + */ + if (vb->baddr) { + /* This is a userspace buffer. */ + if (fh->pix.sizeimage > vb->bsize) { + /* The buffer isn't big enough. */ + err = -EINVAL; + } else + vb->size = fh->pix.sizeimage; + } else { + if (vb->state != VIDEOBUF_NEEDS_INIT) { + /* + * We have a kernel bounce buffer that has + * already been allocated. + */ + if (fh->pix.sizeimage > vb->size) { + /* + * The image size has been changed to + * a larger size since this buffer was + * allocated, so we need to free and + * reallocate it. + */ + omap24xxcam_vbq_release(vbq, vb); + vb->size = fh->pix.sizeimage; + } + } else { + /* We need to allocate a new kernel bounce buffer. */ + vb->size = fh->pix.sizeimage; + } + } + + if (err) + return err; + + vb->width = fh->pix.width; + vb->height = fh->pix.height; + vb->field = field; + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + if (vb->memory == V4L2_MEMORY_MMAP) + /* + * we have built the scatter-gather list by ourself so + * do the scatter-gather mapping as well + */ + err = omap24xxcam_dma_iolock(vbq, videobuf_to_dma(vb)); + else + err = videobuf_iolock(vbq, vb, NULL); + } + + if (!err) + vb->state = VIDEOBUF_PREPARED; + else + omap24xxcam_vbq_release(vbq, vb); + + return err; +} + +static void omap24xxcam_vbq_queue(struct videobuf_queue *vbq, + struct videobuf_buffer *vb) +{ + struct omap24xxcam_fh *fh = vbq->priv_data; + struct omap24xxcam_device *cam = fh->cam; + enum videobuf_state state = vb->state; + unsigned long flags; + int err; + + /* + * FIXME: We're marking the buffer active since we have no + * pretty way of marking it active exactly when the + * scatter-gather transfer starts. + */ + vb->state = VIDEOBUF_ACTIVE; + + err = omap24xxcam_sgdma_queue(&fh->cam->sgdma, + videobuf_to_dma(vb)->sglist, + videobuf_to_dma(vb)->sglen, vb->size, + omap24xxcam_vbq_complete, vb); + + if (!err) { + spin_lock_irqsave(&cam->core_enable_disable_lock, flags); + if (++cam->sgdma_in_queue == 1 + && !atomic_read(&cam->in_reset)) + omap24xxcam_core_enable(cam); + spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); + } else { + /* + * Oops. We're not supposed to get any errors here. + * The only way we could get an error is if we ran out + * of scatter-gather DMA slots, but we are supposed to + * have at least as many scatter-gather DMA slots as + * video buffers so that can't happen. + */ + dev_err(cam->dev, "failed to queue a video buffer for dma!\n"); + dev_err(cam->dev, "likely a bug in the driver!\n"); + vb->state = state; + } +} + +static struct videobuf_queue_ops omap24xxcam_vbq_ops = { + .buf_setup = omap24xxcam_vbq_setup, + .buf_prepare = omap24xxcam_vbq_prepare, + .buf_queue = omap24xxcam_vbq_queue, + .buf_release = omap24xxcam_vbq_release, +}; + +/* + * + * OMAP main camera system + * + */ + +/* + * Reset camera block to power-on state. + */ +static void omap24xxcam_poweron_reset(struct omap24xxcam_device *cam) +{ + int max_loop = RESET_TIMEOUT_NS; + + /* Reset whole camera subsystem */ + omap24xxcam_reg_out(cam->mmio_base, + CAM_SYSCONFIG, + CAM_SYSCONFIG_SOFTRESET); + + /* Wait till it's finished */ + while (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS) + & CAM_SYSSTATUS_RESETDONE) + && --max_loop) { + ndelay(1); + } + + if (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS) + & CAM_SYSSTATUS_RESETDONE)) + dev_err(cam->dev, "camera soft reset timeout\n"); +} + +/* + * (Re)initialise the camera block. + */ +static void omap24xxcam_hwinit(struct omap24xxcam_device *cam) +{ + omap24xxcam_poweron_reset(cam); + + /* set the camera subsystem autoidle bit */ + omap24xxcam_reg_out(cam->mmio_base, CAM_SYSCONFIG, + CAM_SYSCONFIG_AUTOIDLE); + + /* set the camera MMU autoidle bit */ + omap24xxcam_reg_out(cam->mmio_base, + CAMMMU_REG_OFFSET + CAMMMU_SYSCONFIG, + CAMMMU_SYSCONFIG_AUTOIDLE); + + omap24xxcam_core_hwinit(cam); + + omap24xxcam_dma_hwinit(&cam->sgdma.dma); +} + +/* + * Callback for dma transfer stalling. + */ +static void omap24xxcam_stalled_dma_reset(unsigned long data) +{ + struct omap24xxcam_device *cam = (struct omap24xxcam_device *)data; + + if (!atomic_read(&cam->in_reset)) { + dev_dbg(cam->dev, "dma stalled, resetting camera\n"); + omap24xxcam_reset(cam); + } +} + +/* + * Stop capture. Mark we're doing a reset, stop DMA transfers and + * core. (No new scatter-gather transfers will be queued whilst + * in_reset is non-zero.) + * + * If omap24xxcam_capture_stop is called from several places at + * once, only the first call will have an effect. Similarly, the last + * call omap24xxcam_streaming_cont will have effect. + * + * Serialisation is ensured by using cam->core_enable_disable_lock. + */ +static void omap24xxcam_capture_stop(struct omap24xxcam_device *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->core_enable_disable_lock, flags); + + if (atomic_inc_return(&cam->in_reset) != 1) { + spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); + return; + } + + omap24xxcam_core_disable(cam); + + spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); + + omap24xxcam_sgdma_sync(&cam->sgdma); +} + +/* + * Reset and continue streaming. + * + * Note: Resetting the camera FIFO via the CC_RST bit in the CC_CTRL + * register is supposed to be sufficient to recover from a camera + * interface error, but it doesn't seem to be enough. If we only do + * that then subsequent image captures are out of sync by either one + * or two times DMA_THRESHOLD bytes. Resetting and re-initializing the + * entire camera subsystem prevents the problem with frame + * synchronization. + */ +static void omap24xxcam_capture_cont(struct omap24xxcam_device *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->core_enable_disable_lock, flags); + + if (atomic_read(&cam->in_reset) != 1) + goto out; + + omap24xxcam_hwinit(cam); + + omap24xxcam_sensor_if_enable(cam); + + omap24xxcam_sgdma_process(&cam->sgdma); + + if (cam->sgdma_in_queue) + omap24xxcam_core_enable(cam); + +out: + atomic_dec(&cam->in_reset); + spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); +} + +static ssize_t +omap24xxcam_streaming_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct omap24xxcam_device *cam = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", cam->streaming ? "active" : "inactive"); +} +static DEVICE_ATTR(streaming, S_IRUGO, omap24xxcam_streaming_show, NULL); + +/* + * Stop capture and restart it. I.e. reset the camera during use. + */ +static void omap24xxcam_reset(struct omap24xxcam_device *cam) +{ + omap24xxcam_capture_stop(cam); + omap24xxcam_capture_cont(cam); +} + +/* + * The main interrupt handler. + */ +static irqreturn_t omap24xxcam_isr(int irq, void *arg) +{ + struct omap24xxcam_device *cam = (struct omap24xxcam_device *)arg; + u32 irqstatus; + unsigned int irqhandled = 0; + + irqstatus = omap24xxcam_reg_in(cam->mmio_base, CAM_IRQSTATUS); + + if (irqstatus & + (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1 + | CAM_IRQSTATUS_DMA_IRQ0)) { + omap24xxcam_dma_isr(&cam->sgdma.dma); + irqhandled = 1; + } + if (irqstatus & CAM_IRQSTATUS_CC_IRQ) { + omap24xxcam_core_isr(cam); + irqhandled = 1; + } + if (irqstatus & CAM_IRQSTATUS_MMU_IRQ) + dev_err(cam->dev, "unhandled camera MMU interrupt!\n"); + + return IRQ_RETVAL(irqhandled); +} + +/* + * + * Sensor handling. + * + */ + +/* + * Enable the external sensor interface. Try to negotiate interface + * parameters with the sensor and start using the new ones. The calls + * to sensor_if_enable and sensor_if_disable need not to be balanced. + */ +static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam) +{ + int rval; + struct v4l2_ifparm p; + + rval = vidioc_int_g_ifparm(cam->sdev, &p); + if (rval) { + dev_err(cam->dev, "vidioc_int_g_ifparm failed with %d\n", rval); + return rval; + } + + cam->if_type = p.if_type; + + cam->cc_ctrl = CC_CTRL_CC_EN; + + switch (p.if_type) { + case V4L2_IF_TYPE_BT656: + if (p.u.bt656.frame_start_on_rising_vs) + cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO; + if (p.u.bt656.bt_sync_correct) + cam->cc_ctrl |= CC_CTRL_BT_CORRECT; + if (p.u.bt656.swap) + cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM; + if (p.u.bt656.latch_clk_inv) + cam->cc_ctrl |= CC_CTRL_PAR_CLK_POL; + if (p.u.bt656.nobt_hs_inv) + cam->cc_ctrl |= CC_CTRL_NOBT_HS_POL; + if (p.u.bt656.nobt_vs_inv) + cam->cc_ctrl |= CC_CTRL_NOBT_VS_POL; + + switch (p.u.bt656.mode) { + case V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT: + cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT8; + break; + case V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT: + cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT10; + break; + case V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT: + cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT12; + break; + case V4L2_IF_TYPE_BT656_MODE_BT_8BIT: + cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT8; + break; + case V4L2_IF_TYPE_BT656_MODE_BT_10BIT: + cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT10; + break; + default: + dev_err(cam->dev, + "bt656 interface mode %d not supported\n", + p.u.bt656.mode); + return -EINVAL; + } + /* + * The clock rate that the sensor wants has changed. + * We have to adjust the xclk from OMAP 2 side to + * match the sensor's wish as closely as possible. + */ + if (p.u.bt656.clock_curr != cam->if_u.bt656.xclk) { + u32 xclk = p.u.bt656.clock_curr; + u32 divisor; + + if (xclk == 0) + return -EINVAL; + + if (xclk > CAM_MCLK) + xclk = CAM_MCLK; + + divisor = CAM_MCLK / xclk; + if (divisor * xclk < CAM_MCLK) + divisor++; + if (CAM_MCLK / divisor < p.u.bt656.clock_min + && divisor > 1) + divisor--; + if (divisor > 30) + divisor = 30; + + xclk = CAM_MCLK / divisor; + + if (xclk < p.u.bt656.clock_min + || xclk > p.u.bt656.clock_max) + return -EINVAL; + + cam->if_u.bt656.xclk = xclk; + } + omap24xxcam_core_xclk_set(cam, cam->if_u.bt656.xclk); + break; + default: + /* FIXME: how about other interfaces? */ + dev_err(cam->dev, "interface type %d not supported\n", + p.if_type); + return -EINVAL; + } + + return 0; +} + +static void omap24xxcam_sensor_if_disable(const struct omap24xxcam_device *cam) +{ + switch (cam->if_type) { + case V4L2_IF_TYPE_BT656: + omap24xxcam_core_xclk_set(cam, 0); + break; + } +} + +/* + * Initialise the sensor hardware. + */ +static int omap24xxcam_sensor_init(struct omap24xxcam_device *cam) +{ + int err = 0; + struct v4l2_int_device *sdev = cam->sdev; + + omap24xxcam_clock_on(cam); + err = omap24xxcam_sensor_if_enable(cam); + if (err) { + dev_err(cam->dev, "sensor interface could not be enabled at " + "initialisation, %d\n", err); + cam->sdev = NULL; + goto out; + } + + /* power up sensor during sensor initialization */ + vidioc_int_s_power(sdev, 1); + + err = vidioc_int_dev_init(sdev); + if (err) { + dev_err(cam->dev, "cannot initialize sensor, error %d\n", err); + /* Sensor init failed --- it's nonexistent to us! */ + cam->sdev = NULL; + goto out; + } + + dev_info(cam->dev, "sensor is %s\n", sdev->name); + +out: + omap24xxcam_sensor_if_disable(cam); + omap24xxcam_clock_off(cam); + + vidioc_int_s_power(sdev, 0); + + return err; +} + +static void omap24xxcam_sensor_exit(struct omap24xxcam_device *cam) +{ + if (cam->sdev) + vidioc_int_dev_exit(cam->sdev); +} + +static void omap24xxcam_sensor_disable(struct omap24xxcam_device *cam) +{ + omap24xxcam_sensor_if_disable(cam); + omap24xxcam_clock_off(cam); + vidioc_int_s_power(cam->sdev, 0); +} + +/* + * Power-up and configure camera sensor. It's ready for capturing now. + */ +static int omap24xxcam_sensor_enable(struct omap24xxcam_device *cam) +{ + int rval; + + omap24xxcam_clock_on(cam); + + omap24xxcam_sensor_if_enable(cam); + + rval = vidioc_int_s_power(cam->sdev, 1); + if (rval) + goto out; + + rval = vidioc_int_init(cam->sdev); + if (rval) + goto out; + + return 0; + +out: + omap24xxcam_sensor_disable(cam); + + return rval; +} + +static void omap24xxcam_sensor_reset_work(struct work_struct *work) +{ + struct omap24xxcam_device *cam = + container_of(work, struct omap24xxcam_device, + sensor_reset_work); + + if (atomic_read(&cam->reset_disable)) + return; + + omap24xxcam_capture_stop(cam); + + if (vidioc_int_reset(cam->sdev) == 0) { + vidioc_int_init(cam->sdev); + } else { + /* Can't reset it by vidioc_int_reset. */ + omap24xxcam_sensor_disable(cam); + omap24xxcam_sensor_enable(cam); + } + + omap24xxcam_capture_cont(cam); +} + +/* + * + * IOCTL interface. + * + */ + +static int vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + + strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver)); + strlcpy(cap->card, cam->vfd->name, sizeof(cap->card)); + cap->version = OMAP24XXCAM_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + rval = vidioc_int_enum_fmt_cap(cam->sdev, f); + + return rval; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + mutex_lock(&cam->mutex); + rval = vidioc_int_g_fmt_cap(cam->sdev, f); + mutex_unlock(&cam->mutex); + + return rval; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + mutex_lock(&cam->mutex); + if (cam->streaming) { + rval = -EBUSY; + goto out; + } + + rval = vidioc_int_s_fmt_cap(cam->sdev, f); + +out: + mutex_unlock(&cam->mutex); + + if (!rval) { + mutex_lock(&ofh->vbq.vb_lock); + ofh->pix = f->fmt.pix; + mutex_unlock(&ofh->vbq.vb_lock); + } + + memset(f, 0, sizeof(*f)); + vidioc_g_fmt_vid_cap(file, fh, f); + + return rval; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + mutex_lock(&cam->mutex); + rval = vidioc_int_try_fmt_cap(cam->sdev, f); + mutex_unlock(&cam->mutex); + + return rval; +} + +static int vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *b) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + mutex_lock(&cam->mutex); + if (cam->streaming) { + mutex_unlock(&cam->mutex); + return -EBUSY; + } + + omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq); + mutex_unlock(&cam->mutex); + + rval = videobuf_reqbufs(&ofh->vbq, b); + + /* + * Either videobuf_reqbufs failed or the buffers are not + * memory-mapped (which would need special attention). + */ + if (rval < 0 || b->memory != V4L2_MEMORY_MMAP) + goto out; + + rval = omap24xxcam_vbq_alloc_mmap_buffers(&ofh->vbq, rval); + if (rval) + omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq); + +out: + return rval; +} + +static int vidioc_querybuf(struct file *file, void *fh, + struct v4l2_buffer *b) +{ + struct omap24xxcam_fh *ofh = fh; + + return videobuf_querybuf(&ofh->vbq, b); +} + +static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct omap24xxcam_fh *ofh = fh; + + return videobuf_qbuf(&ofh->vbq, b); +} + +static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + struct videobuf_buffer *vb; + int rval; + +videobuf_dqbuf_again: + rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK); + if (rval) + goto out; + + vb = ofh->vbq.bufs[b->index]; + + mutex_lock(&cam->mutex); + /* _needs_reset returns -EIO if reset is required. */ + rval = vidioc_int_g_needs_reset(cam->sdev, (void *)vb->baddr); + mutex_unlock(&cam->mutex); + if (rval == -EIO) + schedule_work(&cam->sensor_reset_work); + else + rval = 0; + +out: + /* + * This is a hack. We don't want to show -EIO to the user + * space. Requeue the buffer and try again if we're not doing + * this in non-blocking mode. + */ + if (rval == -EIO) { + videobuf_qbuf(&ofh->vbq, b); + if (!(file->f_flags & O_NONBLOCK)) + goto videobuf_dqbuf_again; + /* + * We don't have a videobuf_buffer now --- maybe next + * time... + */ + rval = -EAGAIN; + } + + return rval; +} + +static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + mutex_lock(&cam->mutex); + if (cam->streaming) { + rval = -EBUSY; + goto out; + } + + rval = omap24xxcam_sensor_if_enable(cam); + if (rval) { + dev_dbg(cam->dev, "vidioc_int_g_ifparm failed\n"); + goto out; + } + + rval = videobuf_streamon(&ofh->vbq); + if (!rval) { + cam->streaming = file; + sysfs_notify(&cam->dev->kobj, NULL, "streaming"); + } + +out: + mutex_unlock(&cam->mutex); + + return rval; +} + +static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + struct videobuf_queue *q = &ofh->vbq; + int rval; + + atomic_inc(&cam->reset_disable); + + flush_scheduled_work(); + + rval = videobuf_streamoff(q); + if (!rval) { + mutex_lock(&cam->mutex); + cam->streaming = NULL; + mutex_unlock(&cam->mutex); + sysfs_notify(&cam->dev->kobj, NULL, "streaming"); + } + + atomic_dec(&cam->reset_disable); + + return rval; +} + +static int vidioc_enum_input(struct file *file, void *fh, + struct v4l2_input *inp) +{ + if (inp->index > 0) + return -EINVAL; + + strlcpy(inp->name, "camera", sizeof(inp->name)); + inp->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +{ + *i = 0; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int i) +{ + if (i > 0) + return -EINVAL; + + return 0; +} + +static int vidioc_queryctrl(struct file *file, void *fh, + struct v4l2_queryctrl *a) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + rval = vidioc_int_queryctrl(cam->sdev, a); + + return rval; +} + +static int vidioc_g_ctrl(struct file *file, void *fh, + struct v4l2_control *a) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + mutex_lock(&cam->mutex); + rval = vidioc_int_g_ctrl(cam->sdev, a); + mutex_unlock(&cam->mutex); + + return rval; +} + +static int vidioc_s_ctrl(struct file *file, void *fh, + struct v4l2_control *a) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + mutex_lock(&cam->mutex); + rval = vidioc_int_s_ctrl(cam->sdev, a); + mutex_unlock(&cam->mutex); + + return rval; +} + +static int vidioc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) { + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + mutex_lock(&cam->mutex); + rval = vidioc_int_g_parm(cam->sdev, a); + mutex_unlock(&cam->mutex); + + return rval; +} + +static int vidioc_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + struct v4l2_streamparm old_streamparm; + int rval; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + mutex_lock(&cam->mutex); + if (cam->streaming) { + rval = -EBUSY; + goto out; + } + + old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + rval = vidioc_int_g_parm(cam->sdev, &old_streamparm); + if (rval) + goto out; + + rval = vidioc_int_s_parm(cam->sdev, a); + if (rval) + goto out; + + rval = omap24xxcam_sensor_if_enable(cam); + /* + * Revert to old streaming parameters if enabling sensor + * interface with the new ones failed. + */ + if (rval) + vidioc_int_s_parm(cam->sdev, &old_streamparm); + +out: + mutex_unlock(&cam->mutex); + + return rval; +} + +/* + * + * File operations. + * + */ + +static unsigned int omap24xxcam_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct omap24xxcam_fh *fh = file->private_data; + struct omap24xxcam_device *cam = fh->cam; + struct videobuf_buffer *vb; + + mutex_lock(&cam->mutex); + if (cam->streaming != file) { + mutex_unlock(&cam->mutex); + return POLLERR; + } + mutex_unlock(&cam->mutex); + + mutex_lock(&fh->vbq.vb_lock); + if (list_empty(&fh->vbq.stream)) { + mutex_unlock(&fh->vbq.vb_lock); + return POLLERR; + } + vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream); + mutex_unlock(&fh->vbq.vb_lock); + + poll_wait(file, &vb->done, wait); + + if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int omap24xxcam_mmap_buffers(struct file *file, + struct vm_area_struct *vma) +{ + struct omap24xxcam_fh *fh = file->private_data; + struct omap24xxcam_device *cam = fh->cam; + struct videobuf_queue *vbq = &fh->vbq; + unsigned int first, last, size, i, j; + int err = 0; + + mutex_lock(&cam->mutex); + if (cam->streaming) { + mutex_unlock(&cam->mutex); + return -EBUSY; + } + mutex_unlock(&cam->mutex); + mutex_lock(&vbq->vb_lock); + + /* look for first buffer to map */ + for (first = 0; first < VIDEO_MAX_FRAME; first++) { + if (NULL == vbq->bufs[first]) + continue; + if (V4L2_MEMORY_MMAP != vbq->bufs[first]->memory) + continue; + if (vbq->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT)) + break; + } + + /* look for last buffer to map */ + for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) { + if (NULL == vbq->bufs[last]) + continue; + if (V4L2_MEMORY_MMAP != vbq->bufs[last]->memory) + continue; + size += vbq->bufs[last]->bsize; + if (size == (vma->vm_end - vma->vm_start)) + break; + } + + size = 0; + for (i = first; i <= last; i++) { + struct videobuf_dmabuf *dma = videobuf_to_dma(vbq->bufs[i]); + + for (j = 0; j < dma->sglen; j++) { + err = remap_pfn_range( + vma, vma->vm_start + size, + page_to_pfn(sg_page(&dma->sglist[j])), + sg_dma_len(&dma->sglist[j]), vma->vm_page_prot); + if (err) + goto out; + size += sg_dma_len(&dma->sglist[j]); + } + } + +out: + mutex_unlock(&vbq->vb_lock); + + return err; +} + +static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct omap24xxcam_fh *fh = file->private_data; + int rval; + + /* let the video-buf mapper check arguments and set-up structures */ + rval = videobuf_mmap_mapper(&fh->vbq, vma); + if (rval) + return rval; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + /* do mapping to our allocated buffers */ + rval = omap24xxcam_mmap_buffers(file, vma); + /* + * In case of error, free vma->vm_private_data allocated by + * videobuf_mmap_mapper. + */ + if (rval) + kfree(vma->vm_private_data); + + return rval; +} + +static int omap24xxcam_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + struct omap24xxcam_device *cam = omap24xxcam.priv; + struct omap24xxcam_fh *fh; + struct v4l2_format format; + + if (!cam || !cam->vfd || (cam->vfd->minor != minor)) + return -ENODEV; + + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (fh == NULL) + return -ENOMEM; + + mutex_lock(&cam->mutex); + if (cam->sdev == NULL || !try_module_get(cam->sdev->module)) { + mutex_unlock(&cam->mutex); + goto out_try_module_get; + } + + if (atomic_inc_return(&cam->users) == 1) { + omap24xxcam_hwinit(cam); + if (omap24xxcam_sensor_enable(cam)) { + mutex_unlock(&cam->mutex); + goto out_omap24xxcam_sensor_enable; + } + } + mutex_unlock(&cam->mutex); + + fh->cam = cam; + mutex_lock(&cam->mutex); + vidioc_int_g_fmt_cap(cam->sdev, &format); + mutex_unlock(&cam->mutex); + /* FIXME: how about fh->pix when there are more users? */ + fh->pix = format.fmt.pix; + + file->private_data = fh; + + spin_lock_init(&fh->vbq_lock); + + videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL, + &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, + sizeof(struct videobuf_buffer), fh); + + return 0; + +out_omap24xxcam_sensor_enable: + omap24xxcam_poweron_reset(cam); + module_put(cam->sdev->module); + +out_try_module_get: + kfree(fh); + + return -ENODEV; +} + +static int omap24xxcam_release(struct inode *inode, struct file *file) +{ + struct omap24xxcam_fh *fh = file->private_data; + struct omap24xxcam_device *cam = fh->cam; + + atomic_inc(&cam->reset_disable); + + flush_scheduled_work(); + + /* stop streaming capture */ + videobuf_streamoff(&fh->vbq); + + mutex_lock(&cam->mutex); + if (cam->streaming == file) { + cam->streaming = NULL; + mutex_unlock(&cam->mutex); + sysfs_notify(&cam->dev->kobj, NULL, "streaming"); + } else { + mutex_unlock(&cam->mutex); + } + + atomic_dec(&cam->reset_disable); + + omap24xxcam_vbq_free_mmap_buffers(&fh->vbq); + + /* + * Make sure the reset work we might have scheduled is not + * pending! It may be run *only* if we have users. (And it may + * not be scheduled anymore since streaming is already + * disabled.) + */ + flush_scheduled_work(); + + mutex_lock(&cam->mutex); + if (atomic_dec_return(&cam->users) == 0) { + omap24xxcam_sensor_disable(cam); + omap24xxcam_poweron_reset(cam); + } + mutex_unlock(&cam->mutex); + + file->private_data = NULL; + + module_put(cam->sdev->module); + kfree(fh); + + return 0; +} + +static struct file_operations omap24xxcam_fops = { + .llseek = no_llseek, + .ioctl = video_ioctl2, + .poll = omap24xxcam_poll, + .mmap = omap24xxcam_mmap, + .open = omap24xxcam_open, + .release = omap24xxcam_release, +}; + +/* + * + * Power management. + * + */ + +#ifdef CONFIG_PM +static int omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct omap24xxcam_device *cam = platform_get_drvdata(pdev); + + if (atomic_read(&cam->users) == 0) + return 0; + + if (!atomic_read(&cam->reset_disable)) + omap24xxcam_capture_stop(cam); + + omap24xxcam_sensor_disable(cam); + omap24xxcam_poweron_reset(cam); + + return 0; +} + +static int omap24xxcam_resume(struct platform_device *pdev) +{ + struct omap24xxcam_device *cam = platform_get_drvdata(pdev); + + if (atomic_read(&cam->users) == 0) + return 0; + + omap24xxcam_hwinit(cam); + omap24xxcam_sensor_enable(cam); + + if (!atomic_read(&cam->reset_disable)) + omap24xxcam_capture_cont(cam); + + return 0; +} +#endif /* CONFIG_PM */ + +static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, +}; + +/* + * + * Camera device (i.e. /dev/video). + * + */ + +static int omap24xxcam_device_register(struct v4l2_int_device *s) +{ + struct omap24xxcam_device *cam = s->u.slave->master->priv; + struct video_device *vfd; + int rval; + + /* We already have a slave. */ + if (cam->sdev) + return -EBUSY; + + cam->sdev = s; + + if (device_create_file(cam->dev, &dev_attr_streaming) != 0) { + dev_err(cam->dev, "could not register sysfs entry\n"); + rval = -EBUSY; + goto err; + } + + /* initialize the video_device struct */ + vfd = cam->vfd = video_device_alloc(); + if (!vfd) { + dev_err(cam->dev, "could not allocate video device struct\n"); + rval = -ENOMEM; + goto err; + } + vfd->release = video_device_release; + + vfd->parent = cam->dev; + + strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name)); + vfd->vfl_type = VID_TYPE_CAPTURE | VID_TYPE_CHROMAKEY; + vfd->fops = &omap24xxcam_fops; + vfd->minor = -1; + vfd->ioctl_ops = &omap24xxcam_ioctl_fops; + + omap24xxcam_hwinit(cam); + + rval = omap24xxcam_sensor_init(cam); + if (rval) + goto err; + + if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) { + dev_err(cam->dev, "could not register V4L device\n"); + vfd->minor = -1; + rval = -EBUSY; + goto err; + } + + omap24xxcam_poweron_reset(cam); + + dev_info(cam->dev, "registered device video%d\n", vfd->minor); + + return 0; + +err: + omap24xxcam_device_unregister(s); + + return rval; +} + +static void omap24xxcam_device_unregister(struct v4l2_int_device *s) +{ + struct omap24xxcam_device *cam = s->u.slave->master->priv; + + omap24xxcam_sensor_exit(cam); + + if (cam->vfd) { + if (cam->vfd->minor == -1) { + /* + * The device was never registered, so release the + * video_device struct directly. + */ + video_device_release(cam->vfd); + } else { + /* + * The unregister function will release the + * video_device struct as well as + * unregistering it. + */ + video_unregister_device(cam->vfd); + } + cam->vfd = NULL; + } + + device_remove_file(cam->dev, &dev_attr_streaming); + + cam->sdev = NULL; +} + +static struct v4l2_int_master omap24xxcam_master = { + .attach = omap24xxcam_device_register, + .detach = omap24xxcam_device_unregister, +}; + +static struct v4l2_int_device omap24xxcam = { + .module = THIS_MODULE, + .name = CAM_NAME, + .type = v4l2_int_type_master, + .u = { + .master = &omap24xxcam_master + }, +}; + +/* + * + * Driver initialisation and deinitialisation. + * + */ + +static int __init omap24xxcam_probe(struct platform_device *pdev) +{ + struct omap24xxcam_device *cam; + struct resource *mem; + int irq; + + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + if (!cam) { + dev_err(&pdev->dev, "could not allocate memory\n"); + goto err; + } + + platform_set_drvdata(pdev, cam); + + cam->dev = &pdev->dev; + + /* + * Impose a lower limit on the amount of memory allocated for + * capture. We require at least enough memory to double-buffer + * QVGA (300KB). + */ + if (capture_mem < 320 * 240 * 2 * 2) + capture_mem = 320 * 240 * 2 * 2; + cam->capture_mem = capture_mem; + + /* request the mem region for the camera registers */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(cam->dev, "no mem resource?\n"); + goto err; + } + if (!request_mem_region(mem->start, (mem->end - mem->start) + 1, + pdev->name)) { + dev_err(cam->dev, + "cannot reserve camera register I/O region\n"); + goto err; + } + cam->mmio_base_phys = mem->start; + cam->mmio_size = (mem->end - mem->start) + 1; + + /* map the region */ + cam->mmio_base = (unsigned long) + ioremap_nocache(cam->mmio_base_phys, cam->mmio_size); + if (!cam->mmio_base) { + dev_err(cam->dev, "cannot map camera register I/O region\n"); + goto err; + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(cam->dev, "no irq for camera?\n"); + goto err; + } + + /* install the interrupt service routine */ + if (request_irq(irq, omap24xxcam_isr, 0, CAM_NAME, cam)) { + dev_err(cam->dev, + "could not install interrupt service routine\n"); + goto err; + } + cam->irq = irq; + + if (omap24xxcam_clock_get(cam)) + goto err; + + INIT_WORK(&cam->sensor_reset_work, omap24xxcam_sensor_reset_work); + + mutex_init(&cam->mutex); + spin_lock_init(&cam->core_enable_disable_lock); + + omap24xxcam_sgdma_init(&cam->sgdma, + cam->mmio_base + CAMDMA_REG_OFFSET, + omap24xxcam_stalled_dma_reset, + (unsigned long)cam); + + omap24xxcam.priv = cam; + + if (v4l2_int_device_register(&omap24xxcam)) + goto err; + + return 0; + +err: + omap24xxcam_remove(pdev); + return -ENODEV; +} + +static int omap24xxcam_remove(struct platform_device *pdev) +{ + struct omap24xxcam_device *cam = platform_get_drvdata(pdev); + + if (!cam) + return 0; + + if (omap24xxcam.priv != NULL) + v4l2_int_device_unregister(&omap24xxcam); + omap24xxcam.priv = NULL; + + omap24xxcam_clock_put(cam); + + if (cam->irq) { + free_irq(cam->irq, cam); + cam->irq = 0; + } + + if (cam->mmio_base) { + iounmap((void *)cam->mmio_base); + cam->mmio_base = 0; + } + + if (cam->mmio_base_phys) { + release_mem_region(cam->mmio_base_phys, cam->mmio_size); + cam->mmio_base_phys = 0; + } + + kfree(cam); + + return 0; +} + +static struct platform_driver omap24xxcam_driver = { + .probe = omap24xxcam_probe, + .remove = omap24xxcam_remove, +#ifdef CONFIG_PM + .suspend = omap24xxcam_suspend, + .resume = omap24xxcam_resume, +#endif + .driver = { + .name = CAM_NAME, + .owner = THIS_MODULE, + }, +}; + +/* + * + * Module initialisation and deinitialisation + * + */ + +static int __init omap24xxcam_init(void) +{ + return platform_driver_register(&omap24xxcam_driver); +} + +static void __exit omap24xxcam_cleanup(void) +{ + platform_driver_unregister(&omap24xxcam_driver); +} + +MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>"); +MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver"); +MODULE_LICENSE("GPL"); +module_param(video_nr, int, 0); +MODULE_PARM_DESC(video_nr, + "Minor number for video device (-1 ==> auto assign)"); +module_param(capture_mem, int, 0); +MODULE_PARM_DESC(capture_mem, "Maximum amount of memory for capture " + "buffers (default 4800kiB)"); + +module_init(omap24xxcam_init); +module_exit(omap24xxcam_cleanup); diff --git a/drivers/media/video/omap24xxcam.h b/drivers/media/video/omap24xxcam.h new file mode 100644 index 0000000..2ce67f5 --- /dev/null +++ b/drivers/media/video/omap24xxcam.h @@ -0,0 +1,593 @@ +/* + * drivers/media/video/omap24xxcam.h + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2004 Texas Instruments. + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Sakari Ailus <sakari.ailus@nokia.com> + * + * Based on code from Andy Lowe <source@mvista.com>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP24XXCAM_H +#define OMAP24XXCAM_H + +#include <media/videobuf-dma-sg.h> +#include <media/v4l2-int-device.h> + +/* + * + * General driver related definitions. + * + */ + +#define CAM_NAME "omap24xxcam" + +#define CAM_MCLK 96000000 + +/* number of bytes transferred per DMA request */ +#define DMA_THRESHOLD 32 + +/* + * NUM_CAMDMA_CHANNELS is the number of logical channels provided by + * the camera DMA controller. + */ +#define NUM_CAMDMA_CHANNELS 4 + +/* + * NUM_SG_DMA is the number of scatter-gather DMA transfers that can + * be queued. (We don't have any overlay sglists now.) + */ +#define NUM_SG_DMA (VIDEO_MAX_FRAME) + +/* + * + * Register definitions. + * + */ + +/* subsystem register block offsets */ +#define CC_REG_OFFSET 0x00000400 +#define CAMDMA_REG_OFFSET 0x00000800 +#define CAMMMU_REG_OFFSET 0x00000C00 + +/* define camera subsystem register offsets */ +#define CAM_REVISION 0x000 +#define CAM_SYSCONFIG 0x010 +#define CAM_SYSSTATUS 0x014 +#define CAM_IRQSTATUS 0x018 +#define CAM_GPO 0x040 +#define CAM_GPI 0x050 + +/* define camera core register offsets */ +#define CC_REVISION 0x000 +#define CC_SYSCONFIG 0x010 +#define CC_SYSSTATUS 0x014 +#define CC_IRQSTATUS 0x018 +#define CC_IRQENABLE 0x01C +#define CC_CTRL 0x040 +#define CC_CTRL_DMA 0x044 +#define CC_CTRL_XCLK 0x048 +#define CC_FIFODATA 0x04C +#define CC_TEST 0x050 +#define CC_GENPAR 0x054 +#define CC_CCPFSCR 0x058 +#define CC_CCPFECR 0x05C +#define CC_CCPLSCR 0x060 +#define CC_CCPLECR 0x064 +#define CC_CCPDFR 0x068 + +/* define camera dma register offsets */ +#define CAMDMA_REVISION 0x000 +#define CAMDMA_IRQSTATUS_L0 0x008 +#define CAMDMA_IRQSTATUS_L1 0x00C +#define CAMDMA_IRQSTATUS_L2 0x010 +#define CAMDMA_IRQSTATUS_L3 0x014 +#define CAMDMA_IRQENABLE_L0 0x018 +#define CAMDMA_IRQENABLE_L1 0x01C +#define CAMDMA_IRQENABLE_L2 0x020 +#define CAMDMA_IRQENABLE_L3 0x024 +#define CAMDMA_SYSSTATUS 0x028 +#define CAMDMA_OCP_SYSCONFIG 0x02C +#define CAMDMA_CAPS_0 0x064 +#define CAMDMA_CAPS_2 0x06C +#define CAMDMA_CAPS_3 0x070 +#define CAMDMA_CAPS_4 0x074 +#define CAMDMA_GCR 0x078 +#define CAMDMA_CCR(n) (0x080 + (n)*0x60) +#define CAMDMA_CLNK_CTRL(n) (0x084 + (n)*0x60) +#define CAMDMA_CICR(n) (0x088 + (n)*0x60) +#define CAMDMA_CSR(n) (0x08C + (n)*0x60) +#define CAMDMA_CSDP(n) (0x090 + (n)*0x60) +#define CAMDMA_CEN(n) (0x094 + (n)*0x60) +#define CAMDMA_CFN(n) (0x098 + (n)*0x60) +#define CAMDMA_CSSA(n) (0x09C + (n)*0x60) +#define CAMDMA_CDSA(n) (0x0A0 + (n)*0x60) +#define CAMDMA_CSEI(n) (0x0A4 + (n)*0x60) +#define CAMDMA_CSFI(n) (0x0A8 + (n)*0x60) +#define CAMDMA_CDEI(n) (0x0AC + (n)*0x60) +#define CAMDMA_CDFI(n) (0x0B0 + (n)*0x60) +#define CAMDMA_CSAC(n) (0x0B4 + (n)*0x60) +#define CAMDMA_CDAC(n) (0x0B8 + (n)*0x60) +#define CAMDMA_CCEN(n) (0x0BC + (n)*0x60) +#define CAMDMA_CCFN(n) (0x0C0 + (n)*0x60) +#define CAMDMA_COLOR(n) (0x0C4 + (n)*0x60) + +/* define camera mmu register offsets */ +#define CAMMMU_REVISION 0x000 +#define CAMMMU_SYSCONFIG 0x010 +#define CAMMMU_SYSSTATUS 0x014 +#define CAMMMU_IRQSTATUS 0x018 +#define CAMMMU_IRQENABLE 0x01C +#define CAMMMU_WALKING_ST 0x040 +#define CAMMMU_CNTL 0x044 +#define CAMMMU_FAULT_AD 0x048 +#define CAMMMU_TTB 0x04C +#define CAMMMU_LOCK 0x050 +#define CAMMMU_LD_TLB 0x054 +#define CAMMMU_CAM 0x058 +#define CAMMMU_RAM 0x05C +#define CAMMMU_GFLUSH 0x060 +#define CAMMMU_FLUSH_ENTRY 0x064 +#define CAMMMU_READ_CAM 0x068 +#define CAMMMU_READ_RAM 0x06C +#define CAMMMU_EMU_FAULT_AD 0x070 + +/* Define bit fields within selected registers */ +#define CAM_REVISION_MAJOR (15 << 4) +#define CAM_REVISION_MAJOR_SHIFT 4 +#define CAM_REVISION_MINOR (15 << 0) +#define CAM_REVISION_MINOR_SHIFT 0 + +#define CAM_SYSCONFIG_SOFTRESET (1 << 1) +#define CAM_SYSCONFIG_AUTOIDLE (1 << 0) + +#define CAM_SYSSTATUS_RESETDONE (1 << 0) + +#define CAM_IRQSTATUS_CC_IRQ (1 << 4) +#define CAM_IRQSTATUS_MMU_IRQ (1 << 3) +#define CAM_IRQSTATUS_DMA_IRQ2 (1 << 2) +#define CAM_IRQSTATUS_DMA_IRQ1 (1 << 1) +#define CAM_IRQSTATUS_DMA_IRQ0 (1 << 0) + +#define CAM_GPO_CAM_S_P_EN (1 << 1) +#define CAM_GPO_CAM_CCP_MODE (1 << 0) + +#define CAM_GPI_CC_DMA_REQ1 (1 << 24) +#define CAP_GPI_CC_DMA_REQ0 (1 << 23) +#define CAP_GPI_CAM_MSTANDBY (1 << 21) +#define CAP_GPI_CAM_WAIT (1 << 20) +#define CAP_GPI_CAM_S_DATA (1 << 17) +#define CAP_GPI_CAM_S_CLK (1 << 16) +#define CAP_GPI_CAM_P_DATA (0xFFF << 3) +#define CAP_GPI_CAM_P_DATA_SHIFT 3 +#define CAP_GPI_CAM_P_VS (1 << 2) +#define CAP_GPI_CAM_P_HS (1 << 1) +#define CAP_GPI_CAM_P_CLK (1 << 0) + +#define CC_REVISION_MAJOR (15 << 4) +#define CC_REVISION_MAJOR_SHIFT 4 +#define CC_REVISION_MINOR (15 << 0) +#define CC_REVISION_MINOR_SHIFT 0 + +#define CC_SYSCONFIG_SIDLEMODE (3 << 3) +#define CC_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3) +#define CC_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3) +#define CC_SYSCONFIG_SOFTRESET (1 << 1) +#define CC_SYSCONFIG_AUTOIDLE (1 << 0) + +#define CC_SYSSTATUS_RESETDONE (1 << 0) + +#define CC_IRQSTATUS_FS_IRQ (1 << 19) +#define CC_IRQSTATUS_LE_IRQ (1 << 18) +#define CC_IRQSTATUS_LS_IRQ (1 << 17) +#define CC_IRQSTATUS_FE_IRQ (1 << 16) +#define CC_IRQSTATUS_FW_ERR_IRQ (1 << 10) +#define CC_IRQSTATUS_FSC_ERR_IRQ (1 << 9) +#define CC_IRQSTATUS_SSC_ERR_IRQ (1 << 8) +#define CC_IRQSTATUS_FIFO_NOEMPTY_IRQ (1 << 4) +#define CC_IRQSTATUS_FIFO_FULL_IRQ (1 << 3) +#define CC_IRQSTATUS_FIFO_THR_IRQ (1 << 2) +#define CC_IRQSTATUS_FIFO_OF_IRQ (1 << 1) +#define CC_IRQSTATUS_FIFO_UF_IRQ (1 << 0) + +#define CC_IRQENABLE_FS_IRQ (1 << 19) +#define CC_IRQENABLE_LE_IRQ (1 << 18) +#define CC_IRQENABLE_LS_IRQ (1 << 17) +#define CC_IRQENABLE_FE_IRQ (1 << 16) +#define CC_IRQENABLE_FW_ERR_IRQ (1 << 10) +#define CC_IRQENABLE_FSC_ERR_IRQ (1 << 9) +#define CC_IRQENABLE_SSC_ERR_IRQ (1 << 8) +#define CC_IRQENABLE_FIFO_NOEMPTY_IRQ (1 << 4) +#define CC_IRQENABLE_FIFO_FULL_IRQ (1 << 3) +#define CC_IRQENABLE_FIFO_THR_IRQ (1 << 2) +#define CC_IRQENABLE_FIFO_OF_IRQ (1 << 1) +#define CC_IRQENABLE_FIFO_UF_IRQ (1 << 0) + +#define CC_CTRL_CC_ONE_SHOT (1 << 20) +#define CC_CTRL_CC_IF_SYNCHRO (1 << 19) +#define CC_CTRL_CC_RST (1 << 18) +#define CC_CTRL_CC_FRAME_TRIG (1 << 17) +#define CC_CTRL_CC_EN (1 << 16) +#define CC_CTRL_NOBT_SYNCHRO (1 << 13) +#define CC_CTRL_BT_CORRECT (1 << 12) +#define CC_CTRL_PAR_ORDERCAM (1 << 11) +#define CC_CTRL_PAR_CLK_POL (1 << 10) +#define CC_CTRL_NOBT_HS_POL (1 << 9) +#define CC_CTRL_NOBT_VS_POL (1 << 8) +#define CC_CTRL_PAR_MODE (7 << 1) +#define CC_CTRL_PAR_MODE_SHIFT 1 +#define CC_CTRL_PAR_MODE_NOBT8 (0 << 1) +#define CC_CTRL_PAR_MODE_NOBT10 (1 << 1) +#define CC_CTRL_PAR_MODE_NOBT12 (2 << 1) +#define CC_CTRL_PAR_MODE_BT8 (4 << 1) +#define CC_CTRL_PAR_MODE_BT10 (5 << 1) +#define CC_CTRL_PAR_MODE_FIFOTEST (7 << 1) +#define CC_CTRL_CCP_MODE (1 << 0) + +#define CC_CTRL_DMA_EN (1 << 8) +#define CC_CTRL_DMA_FIFO_THRESHOLD (0x7F << 0) +#define CC_CTRL_DMA_FIFO_THRESHOLD_SHIFT 0 + +#define CC_CTRL_XCLK_DIV (0x1F << 0) +#define CC_CTRL_XCLK_DIV_SHIFT 0 +#define CC_CTRL_XCLK_DIV_STABLE_LOW (0 << 0) +#define CC_CTRL_XCLK_DIV_STABLE_HIGH (1 << 0) +#define CC_CTRL_XCLK_DIV_BYPASS (31 << 0) + +#define CC_TEST_FIFO_RD_POINTER (0xFF << 24) +#define CC_TEST_FIFO_RD_POINTER_SHIFT 24 +#define CC_TEST_FIFO_WR_POINTER (0xFF << 16) +#define CC_TEST_FIFO_WR_POINTER_SHIFT 16 +#define CC_TEST_FIFO_LEVEL (0xFF << 8) +#define CC_TEST_FIFO_LEVEL_SHIFT 8 +#define CC_TEST_FIFO_LEVEL_PEAK (0xFF << 0) +#define CC_TEST_FIFO_LEVEL_PEAK_SHIFT 0 + +#define CC_GENPAR_FIFO_DEPTH (7 << 0) +#define CC_GENPAR_FIFO_DEPTH_SHIFT 0 + +#define CC_CCPDFR_ALPHA (0xFF << 8) +#define CC_CCPDFR_ALPHA_SHIFT 8 +#define CC_CCPDFR_DATAFORMAT (15 << 0) +#define CC_CCPDFR_DATAFORMAT_SHIFT 0 +#define CC_CCPDFR_DATAFORMAT_YUV422BE (0 << 0) +#define CC_CCPDFR_DATAFORMAT_YUV422 (1 << 0) +#define CC_CCPDFR_DATAFORMAT_YUV420 (2 << 0) +#define CC_CCPDFR_DATAFORMAT_RGB444 (4 << 0) +#define CC_CCPDFR_DATAFORMAT_RGB565 (5 << 0) +#define CC_CCPDFR_DATAFORMAT_RGB888NDE (6 << 0) +#define CC_CCPDFR_DATAFORMAT_RGB888 (7 << 0) +#define CC_CCPDFR_DATAFORMAT_RAW8NDE (8 << 0) +#define CC_CCPDFR_DATAFORMAT_RAW8 (9 << 0) +#define CC_CCPDFR_DATAFORMAT_RAW10NDE (10 << 0) +#define CC_CCPDFR_DATAFORMAT_RAW10 (11 << 0) +#define CC_CCPDFR_DATAFORMAT_RAW12NDE (12 << 0) +#define CC_CCPDFR_DATAFORMAT_RAW12 (13 << 0) +#define CC_CCPDFR_DATAFORMAT_JPEG8 (15 << 0) + +#define CAMDMA_REVISION_MAJOR (15 << 4) +#define CAMDMA_REVISION_MAJOR_SHIFT 4 +#define CAMDMA_REVISION_MINOR (15 << 0) +#define CAMDMA_REVISION_MINOR_SHIFT 0 + +#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE (3 << 12) +#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY (0 << 12) +#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_NSTANDBY (1 << 12) +#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_SSTANDBY (2 << 12) +#define CAMDMA_OCP_SYSCONFIG_FUNC_CLOCK (1 << 9) +#define CAMDMA_OCP_SYSCONFIG_OCP_CLOCK (1 << 8) +#define CAMDMA_OCP_SYSCONFIG_EMUFREE (1 << 5) +#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE (3 << 3) +#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3) +#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3) +#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_SIDLE (2 << 3) +#define CAMDMA_OCP_SYSCONFIG_SOFTRESET (1 << 1) +#define CAMDMA_OCP_SYSCONFIG_AUTOIDLE (1 << 0) + +#define CAMDMA_SYSSTATUS_RESETDONE (1 << 0) + +#define CAMDMA_GCR_ARBITRATION_RATE (0xFF << 16) +#define CAMDMA_GCR_ARBITRATION_RATE_SHIFT 16 +#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH (0xFF << 0) +#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH_SHIFT 0 + +#define CAMDMA_CCR_SEL_SRC_DST_SYNC (1 << 24) +#define CAMDMA_CCR_PREFETCH (1 << 23) +#define CAMDMA_CCR_SUPERVISOR (1 << 22) +#define CAMDMA_CCR_SECURE (1 << 21) +#define CAMDMA_CCR_BS (1 << 18) +#define CAMDMA_CCR_TRANSPARENT_COPY_ENABLE (1 << 17) +#define CAMDMA_CCR_CONSTANT_FILL_ENABLE (1 << 16) +#define CAMDMA_CCR_DST_AMODE (3 << 14) +#define CAMDMA_CCR_DST_AMODE_CONST_ADDR (0 << 14) +#define CAMDMA_CCR_DST_AMODE_POST_INC (1 << 14) +#define CAMDMA_CCR_DST_AMODE_SGL_IDX (2 << 14) +#define CAMDMA_CCR_DST_AMODE_DBL_IDX (3 << 14) +#define CAMDMA_CCR_SRC_AMODE (3 << 12) +#define CAMDMA_CCR_SRC_AMODE_CONST_ADDR (0 << 12) +#define CAMDMA_CCR_SRC_AMODE_POST_INC (1 << 12) +#define CAMDMA_CCR_SRC_AMODE_SGL_IDX (2 << 12) +#define CAMDMA_CCR_SRC_AMODE_DBL_IDX (3 << 12) +#define CAMDMA_CCR_WR_ACTIVE (1 << 10) +#define CAMDMA_CCR_RD_ACTIVE (1 << 9) +#define CAMDMA_CCR_SUSPEND_SENSITIVE (1 << 8) +#define CAMDMA_CCR_ENABLE (1 << 7) +#define CAMDMA_CCR_PRIO (1 << 6) +#define CAMDMA_CCR_FS (1 << 5) +#define CAMDMA_CCR_SYNCHRO ((3 << 19) | (31 << 0)) +#define CAMDMA_CCR_SYNCHRO_CAMERA 0x01 + +#define CAMDMA_CLNK_CTRL_ENABLE_LNK (1 << 15) +#define CAMDMA_CLNK_CTRL_NEXTLCH_ID (0x1F << 0) +#define CAMDMA_CLNK_CTRL_NEXTLCH_ID_SHIFT 0 + +#define CAMDMA_CICR_MISALIGNED_ERR_IE (1 << 11) +#define CAMDMA_CICR_SUPERVISOR_ERR_IE (1 << 10) +#define CAMDMA_CICR_SECURE_ERR_IE (1 << 9) +#define CAMDMA_CICR_TRANS_ERR_IE (1 << 8) +#define CAMDMA_CICR_PACKET_IE (1 << 7) +#define CAMDMA_CICR_BLOCK_IE (1 << 5) +#define CAMDMA_CICR_LAST_IE (1 << 4) +#define CAMDMA_CICR_FRAME_IE (1 << 3) +#define CAMDMA_CICR_HALF_IE (1 << 2) +#define CAMDMA_CICR_DROP_IE (1 << 1) + +#define CAMDMA_CSR_MISALIGNED_ERR (1 << 11) +#define CAMDMA_CSR_SUPERVISOR_ERR (1 << 10) +#define CAMDMA_CSR_SECURE_ERR (1 << 9) +#define CAMDMA_CSR_TRANS_ERR (1 << 8) +#define CAMDMA_CSR_PACKET (1 << 7) +#define CAMDMA_CSR_SYNC (1 << 6) +#define CAMDMA_CSR_BLOCK (1 << 5) +#define CAMDMA_CSR_LAST (1 << 4) +#define CAMDMA_CSR_FRAME (1 << 3) +#define CAMDMA_CSR_HALF (1 << 2) +#define CAMDMA_CSR_DROP (1 << 1) + +#define CAMDMA_CSDP_SRC_ENDIANNESS (1 << 21) +#define CAMDMA_CSDP_SRC_ENDIANNESS_LOCK (1 << 20) +#define CAMDMA_CSDP_DST_ENDIANNESS (1 << 19) +#define CAMDMA_CSDP_DST_ENDIANNESS_LOCK (1 << 18) +#define CAMDMA_CSDP_WRITE_MODE (3 << 16) +#define CAMDMA_CSDP_WRITE_MODE_WRNP (0 << 16) +#define CAMDMA_CSDP_WRITE_MODE_POSTED (1 << 16) +#define CAMDMA_CSDP_WRITE_MODE_POSTED_LAST_WRNP (2 << 16) +#define CAMDMA_CSDP_DST_BURST_EN (3 << 14) +#define CAMDMA_CSDP_DST_BURST_EN_1 (0 << 14) +#define CAMDMA_CSDP_DST_BURST_EN_16 (1 << 14) +#define CAMDMA_CSDP_DST_BURST_EN_32 (2 << 14) +#define CAMDMA_CSDP_DST_BURST_EN_64 (3 << 14) +#define CAMDMA_CSDP_DST_PACKED (1 << 13) +#define CAMDMA_CSDP_WR_ADD_TRSLT (15 << 9) +#define CAMDMA_CSDP_WR_ADD_TRSLT_ENABLE_MREQADD (3 << 9) +#define CAMDMA_CSDP_SRC_BURST_EN (3 << 7) +#define CAMDMA_CSDP_SRC_BURST_EN_1 (0 << 7) +#define CAMDMA_CSDP_SRC_BURST_EN_16 (1 << 7) +#define CAMDMA_CSDP_SRC_BURST_EN_32 (2 << 7) +#define CAMDMA_CSDP_SRC_BURST_EN_64 (3 << 7) +#define CAMDMA_CSDP_SRC_PACKED (1 << 6) +#define CAMDMA_CSDP_RD_ADD_TRSLT (15 << 2) +#define CAMDMA_CSDP_RD_ADD_TRSLT_ENABLE_MREQADD (3 << 2) +#define CAMDMA_CSDP_DATA_TYPE (3 << 0) +#define CAMDMA_CSDP_DATA_TYPE_8BITS (0 << 0) +#define CAMDMA_CSDP_DATA_TYPE_16BITS (1 << 0) +#define CAMDMA_CSDP_DATA_TYPE_32BITS (2 << 0) + +#define CAMMMU_SYSCONFIG_AUTOIDLE (1 << 0) + +/* + * + * Declarations. + * + */ + +/* forward declarations */ +struct omap24xxcam_sgdma; +struct omap24xxcam_dma; + +typedef void (*sgdma_callback_t)(struct omap24xxcam_sgdma *cam, + u32 status, void *arg); +typedef void (*dma_callback_t)(struct omap24xxcam_dma *cam, + u32 status, void *arg); + +struct channel_state { + dma_callback_t callback; + void *arg; +}; + +/* sgdma state for each of the possible videobuf_buffers + 2 overlays */ +struct sgdma_state { + const struct scatterlist *sglist; + int sglen; /* number of sglist entries */ + int next_sglist; /* index of next sglist entry to process */ + unsigned int bytes_read; /* number of bytes read */ + unsigned int len; /* total length of sglist (excluding + * bytes due to page alignment) */ + int queued_sglist; /* number of sglist entries queued for DMA */ + u32 csr; /* DMA return code */ + sgdma_callback_t callback; + void *arg; +}; + +/* physical DMA channel management */ +struct omap24xxcam_dma { + spinlock_t lock; /* Lock for the whole structure. */ + + unsigned long base; /* base address for dma controller */ + + /* While dma_stop!=0, an attempt to start a new DMA transfer will + * fail. + */ + atomic_t dma_stop; + int free_dmach; /* number of dma channels free */ + int next_dmach; /* index of next dma channel to use */ + struct channel_state ch_state[NUM_CAMDMA_CHANNELS]; +}; + +/* scatter-gather DMA (scatterlist stuff) management */ +struct omap24xxcam_sgdma { + struct omap24xxcam_dma dma; + + spinlock_t lock; /* Lock for the fields below. */ + int free_sgdma; /* number of free sg dma slots */ + int next_sgdma; /* index of next sg dma slot to use */ + struct sgdma_state sg_state[NUM_SG_DMA]; + + /* Reset timer data */ + struct timer_list reset_timer; +}; + +/* per-device data structure */ +struct omap24xxcam_device { + /*** mutex ***/ + /* + * mutex serialises access to this structure. Also camera + * opening and releasing is synchronised by this. + */ + struct mutex mutex; + + /*** general driver state information ***/ + atomic_t users; + /* + * Lock to serialise core enabling and disabling and access to + * sgdma_in_queue. + */ + spinlock_t core_enable_disable_lock; + /* + * Number or sgdma requests in scatter-gather queue, protected + * by the lock above. + */ + int sgdma_in_queue; + /* + * Sensor interface parameters: interface type, CC_CTRL + * register value and interface specific data. + */ + int if_type; + union { + struct parallel { + u32 xclk; + } bt656; + } if_u; + u32 cc_ctrl; + + /*** subsystem structures ***/ + struct omap24xxcam_sgdma sgdma; + + /*** hardware resources ***/ + unsigned int irq; + unsigned long mmio_base; + unsigned long mmio_base_phys; + unsigned long mmio_size; + + /*** interfaces and device ***/ + struct v4l2_int_device *sdev; + struct device *dev; + struct video_device *vfd; + + /*** camera and sensor reset related stuff ***/ + struct work_struct sensor_reset_work; + /* + * We're in the middle of a reset. Don't enable core if this + * is non-zero! This exists to help decisionmaking in a case + * where videobuf_qbuf is called while we are in the middle of + * a reset. + */ + atomic_t in_reset; + /* + * Non-zero if we don't want any resets for now. Used to + * prevent reset work to run when we're about to stop + * streaming. + */ + atomic_t reset_disable; + + /*** video device parameters ***/ + int capture_mem; + + /*** camera module clocks ***/ + struct clk *fck; + struct clk *ick; + + /*** capture data ***/ + /* file handle, if streaming is on */ + struct file *streaming; +}; + +/* Per-file handle data. */ +struct omap24xxcam_fh { + spinlock_t vbq_lock; /* spinlock for the videobuf queue */ + struct videobuf_queue vbq; + struct v4l2_pix_format pix; /* serialise pix by vbq->lock */ + atomic_t field_count; /* field counter for videobuf_buffer */ + /* accessing cam here doesn't need serialisation: it's constant */ + struct omap24xxcam_device *cam; +}; + +/* + * + * Register I/O functions. + * + */ + +static inline u32 omap24xxcam_reg_in(unsigned long base, u32 offset) +{ + return readl(base + offset); +} + +static inline u32 omap24xxcam_reg_out(unsigned long base, u32 offset, + u32 val) +{ + writel(val, base + offset); + return val; +} + +static inline u32 omap24xxcam_reg_merge(unsigned long base, u32 offset, + u32 val, u32 mask) +{ + u32 addr = base + offset; + u32 new_val = (readl(addr) & ~mask) | (val & mask); + + writel(new_val, addr); + return new_val; +} + +/* + * + * Function prototypes. + * + */ + +/* dma prototypes */ + +void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma); +void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma); + +/* sgdma prototypes */ + +void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma); +int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma, + const struct scatterlist *sglist, int sglen, + int len, sgdma_callback_t callback, void *arg); +void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma); +void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma, + unsigned long base, + void (*reset_callback)(unsigned long data), + unsigned long reset_callback_data); +void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma); + +#endif diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c index 210f124..6ee9b69 100644 --- a/drivers/media/video/ov511.c +++ b/drivers/media/video/ov511.c @@ -4011,8 +4011,7 @@ ov51x_v4l1_close(struct inode *inode, struct file *file) /* Do not call this function directly! */ static int -ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +ov51x_v4l1_ioctl_internal(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = file->private_data; struct usb_ov511 *ov = video_get_drvdata(vdev); @@ -4461,7 +4460,7 @@ ov51x_v4l1_ioctl(struct inode *inode, struct file *file, if (mutex_lock_interruptible(&ov->lock)) return -EINTR; - rc = video_usercopy(inode, file, cmd, arg, ov51x_v4l1_ioctl_internal); + rc = video_usercopy(file, cmd, arg, ov51x_v4l1_ioctl_internal); mutex_unlock(&ov->lock); return rc; diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c new file mode 100644 index 0000000..54b736f --- /dev/null +++ b/drivers/media/video/ov772x.c @@ -0,0 +1,1012 @@ +/* + * ov772x Camera Driver + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * Based on ov7670 and soc_camera_platform driver, + * + * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/videodev2.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-common.h> +#include <media/soc_camera.h> +#include <media/ov772x.h> + +/* + * register offset + */ +#define GAIN 0x00 /* AGC - Gain control gain setting */ +#define BLUE 0x01 /* AWB - Blue channel gain setting */ +#define RED 0x02 /* AWB - Red channel gain setting */ +#define GREEN 0x03 /* AWB - Green channel gain setting */ +#define COM1 0x04 /* Common control 1 */ +#define BAVG 0x05 /* U/B Average Level */ +#define GAVG 0x06 /* Y/Gb Average Level */ +#define RAVG 0x07 /* V/R Average Level */ +#define AECH 0x08 /* Exposure Value - AEC MSBs */ +#define COM2 0x09 /* Common control 2 */ +#define PID 0x0A /* Product ID Number MSB */ +#define VER 0x0B /* Product ID Number LSB */ +#define COM3 0x0C /* Common control 3 */ +#define COM4 0x0D /* Common control 4 */ +#define COM5 0x0E /* Common control 5 */ +#define COM6 0x0F /* Common control 6 */ +#define AEC 0x10 /* Exposure Value */ +#define CLKRC 0x11 /* Internal clock */ +#define COM7 0x12 /* Common control 7 */ +#define COM8 0x13 /* Common control 8 */ +#define COM9 0x14 /* Common control 9 */ +#define COM10 0x15 /* Common control 10 */ +#define REG16 0x16 /* Register 16 */ +#define HSTART 0x17 /* Horizontal sensor size */ +#define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */ +#define VSTART 0x19 /* Vertical frame (row) start high 8-bit */ +#define VSIZE 0x1A /* Vertical sensor size */ +#define PSHFT 0x1B /* Data format - pixel delay select */ +#define MIDH 0x1C /* Manufacturer ID byte - high */ +#define MIDL 0x1D /* Manufacturer ID byte - low */ +#define LAEC 0x1F /* Fine AEC value */ +#define COM11 0x20 /* Common control 11 */ +#define BDBASE 0x22 /* Banding filter Minimum AEC value */ +#define DBSTEP 0x23 /* Banding filter Maximum Setp */ +#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ +#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ +#define VPT 0x26 /* AGC/AEC Fast mode operating region */ +#define REG28 0x28 /* Register 28 */ +#define HOUTSIZE 0x29 /* Horizontal data output size MSBs */ +#define EXHCH 0x2A /* Dummy pixel insert MSB */ +#define EXHCL 0x2B /* Dummy pixel insert LSB */ +#define VOUTSIZE 0x2C /* Vertical data output size MSBs */ +#define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ +#define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */ +#define YAVE 0x2F /* Y/G Channel Average value */ +#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */ +#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */ +#define HREF 0x32 /* Image start and size control */ +#define DM_LNL 0x33 /* Dummy line low 8 bits */ +#define DM_LNH 0x34 /* Dummy line high 8 bits */ +#define ADOFF_B 0x35 /* AD offset compensation value for B channel */ +#define ADOFF_R 0x36 /* AD offset compensation value for R channel */ +#define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */ +#define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */ +#define OFF_B 0x39 /* Analog process B channel offset value */ +#define OFF_R 0x3A /* Analog process R channel offset value */ +#define OFF_GB 0x3B /* Analog process Gb channel offset value */ +#define OFF_GR 0x3C /* Analog process Gr channel offset value */ +#define COM12 0x3D /* Common control 12 */ +#define COM13 0x3E /* Common control 13 */ +#define COM14 0x3F /* Common control 14 */ +#define COM15 0x40 /* Common control 15*/ +#define COM16 0x41 /* Common control 16 */ +#define TGT_B 0x42 /* BLC blue channel target value */ +#define TGT_R 0x43 /* BLC red channel target value */ +#define TGT_GB 0x44 /* BLC Gb channel target value */ +#define TGT_GR 0x45 /* BLC Gr channel target value */ +/* for ov7720 */ +#define LCC0 0x46 /* Lens correction control 0 */ +#define LCC1 0x47 /* Lens correction option 1 - X coordinate */ +#define LCC2 0x48 /* Lens correction option 2 - Y coordinate */ +#define LCC3 0x49 /* Lens correction option 3 */ +#define LCC4 0x4A /* Lens correction option 4 - radius of the circular */ +#define LCC5 0x4B /* Lens correction option 5 */ +#define LCC6 0x4C /* Lens correction option 6 */ +/* for ov7725 */ +#define LC_CTR 0x46 /* Lens correction control */ +#define LC_XC 0x47 /* X coordinate of lens correction center relative */ +#define LC_YC 0x48 /* Y coordinate of lens correction center relative */ +#define LC_COEF 0x49 /* Lens correction coefficient */ +#define LC_RADI 0x4A /* Lens correction radius */ +#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */ +#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */ + +#define FIXGAIN 0x4D /* Analog fix gain amplifer */ +#define AREF0 0x4E /* Sensor reference control */ +#define AREF1 0x4F /* Sensor reference current control */ +#define AREF2 0x50 /* Analog reference control */ +#define AREF3 0x51 /* ADC reference control */ +#define AREF4 0x52 /* ADC reference control */ +#define AREF5 0x53 /* ADC reference control */ +#define AREF6 0x54 /* Analog reference control */ +#define AREF7 0x55 /* Analog reference control */ +#define UFIX 0x60 /* U channel fixed value output */ +#define VFIX 0x61 /* V channel fixed value output */ +#define AWBB_BLK 0x62 /* AWB option for advanced AWB */ +#define AWB_CTRL0 0x63 /* AWB control byte 0 */ +#define DSP_CTRL1 0x64 /* DSP control byte 1 */ +#define DSP_CTRL2 0x65 /* DSP control byte 2 */ +#define DSP_CTRL3 0x66 /* DSP control byte 3 */ +#define DSP_CTRL4 0x67 /* DSP control byte 4 */ +#define AWB_BIAS 0x68 /* AWB BLC level clip */ +#define AWB_CTRL1 0x69 /* AWB control 1 */ +#define AWB_CTRL2 0x6A /* AWB control 2 */ +#define AWB_CTRL3 0x6B /* AWB control 3 */ +#define AWB_CTRL4 0x6C /* AWB control 4 */ +#define AWB_CTRL5 0x6D /* AWB control 5 */ +#define AWB_CTRL6 0x6E /* AWB control 6 */ +#define AWB_CTRL7 0x6F /* AWB control 7 */ +#define AWB_CTRL8 0x70 /* AWB control 8 */ +#define AWB_CTRL9 0x71 /* AWB control 9 */ +#define AWB_CTRL10 0x72 /* AWB control 10 */ +#define AWB_CTRL11 0x73 /* AWB control 11 */ +#define AWB_CTRL12 0x74 /* AWB control 12 */ +#define AWB_CTRL13 0x75 /* AWB control 13 */ +#define AWB_CTRL14 0x76 /* AWB control 14 */ +#define AWB_CTRL15 0x77 /* AWB control 15 */ +#define AWB_CTRL16 0x78 /* AWB control 16 */ +#define AWB_CTRL17 0x79 /* AWB control 17 */ +#define AWB_CTRL18 0x7A /* AWB control 18 */ +#define AWB_CTRL19 0x7B /* AWB control 19 */ +#define AWB_CTRL20 0x7C /* AWB control 20 */ +#define AWB_CTRL21 0x7D /* AWB control 21 */ +#define GAM1 0x7E /* Gamma Curve 1st segment input end point */ +#define GAM2 0x7F /* Gamma Curve 2nd segment input end point */ +#define GAM3 0x80 /* Gamma Curve 3rd segment input end point */ +#define GAM4 0x81 /* Gamma Curve 4th segment input end point */ +#define GAM5 0x82 /* Gamma Curve 5th segment input end point */ +#define GAM6 0x83 /* Gamma Curve 6th segment input end point */ +#define GAM7 0x84 /* Gamma Curve 7th segment input end point */ +#define GAM8 0x85 /* Gamma Curve 8th segment input end point */ +#define GAM9 0x86 /* Gamma Curve 9th segment input end point */ +#define GAM10 0x87 /* Gamma Curve 10th segment input end point */ +#define GAM11 0x88 /* Gamma Curve 11th segment input end point */ +#define GAM12 0x89 /* Gamma Curve 12th segment input end point */ +#define GAM13 0x8A /* Gamma Curve 13th segment input end point */ +#define GAM14 0x8B /* Gamma Curve 14th segment input end point */ +#define GAM15 0x8C /* Gamma Curve 15th segment input end point */ +#define SLOP 0x8D /* Gamma curve highest segment slope */ +#define DNSTH 0x8E /* De-noise threshold */ +#define EDGE0 0x8F /* Edge enhancement control 0 */ +#define EDGE1 0x90 /* Edge enhancement control 1 */ +#define DNSOFF 0x91 /* Auto De-noise threshold control */ +#define EDGE2 0x92 /* Edge enhancement strength low point control */ +#define EDGE3 0x93 /* Edge enhancement strength high point control */ +#define MTX1 0x94 /* Matrix coefficient 1 */ +#define MTX2 0x95 /* Matrix coefficient 2 */ +#define MTX3 0x96 /* Matrix coefficient 3 */ +#define MTX4 0x97 /* Matrix coefficient 4 */ +#define MTX5 0x98 /* Matrix coefficient 5 */ +#define MTX6 0x99 /* Matrix coefficient 6 */ +#define MTX_CTRL 0x9A /* Matrix control */ +#define BRIGHT 0x9B /* Brightness control */ +#define CNTRST 0x9C /* Contrast contrast */ +#define CNTRST_CTRL 0x9D /* Contrast contrast center */ +#define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */ +#define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */ +#define SCAL0 0xA0 /* Scaling control 0 */ +#define SCAL1 0xA1 /* Scaling control 1 */ +#define SCAL2 0xA2 /* Scaling control 2 */ +#define FIFODLYM 0xA3 /* FIFO manual mode delay control */ +#define FIFODLYA 0xA4 /* FIFO auto mode delay control */ +#define SDE 0xA6 /* Special digital effect control */ +#define USAT 0xA7 /* U component saturation control */ +#define VSAT 0xA8 /* V component saturation control */ +/* for ov7720 */ +#define HUE0 0xA9 /* Hue control 0 */ +#define HUE1 0xAA /* Hue control 1 */ +/* for ov7725 */ +#define HUECOS 0xA9 /* Cosine value */ +#define HUESIN 0xAA /* Sine value */ + +#define SIGN 0xAB /* Sign bit for Hue and contrast */ +#define DSPAUTO 0xAC /* DSP auto function ON/OFF control */ + +/* + * register detail + */ + +/* COM2 */ +#define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ + /* Output drive capability */ +#define OCAP_1x 0x00 /* 1x */ +#define OCAP_2x 0x01 /* 2x */ +#define OCAP_3x 0x02 /* 3x */ +#define OCAP_4x 0x03 /* 4x */ + +/* COM3 */ +#define SWAP_MASK 0x38 + +#define VFIMG_ON_OFF 0x80 /* Vertical flip image ON/OFF selection */ +#define HMIMG_ON_OFF 0x40 /* Horizontal mirror image ON/OFF selection */ +#define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */ +#define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */ +#define SWAP_ML 0x08 /* Swap output MSB/LSB */ + /* Tri-state option for output clock */ +#define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */ + /* 1: No tri-state at this period */ + /* Tri-state option for output data */ +#define NOTRI_DATA 0x02 /* 0: Tri-state at this period */ + /* 1: No tri-state at this period */ +#define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */ + +/* COM4 */ + /* PLL frequency control */ +#define PLL_BYPASS 0x00 /* 00: Bypass PLL */ +#define PLL_4x 0x40 /* 01: PLL 4x */ +#define PLL_6x 0x80 /* 10: PLL 6x */ +#define PLL_8x 0xc0 /* 11: PLL 8x */ + /* AEC evaluate window */ +#define AEC_FULL 0x00 /* 00: Full window */ +#define AEC_1p2 0x10 /* 01: 1/2 window */ +#define AEC_1p4 0x20 /* 10: 1/4 window */ +#define AEC_2p3 0x30 /* 11: Low 2/3 window */ + +/* COM5 */ +#define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */ +#define AFR_SPPED 0x40 /* Auto frame rate control speed slection */ + /* Auto frame rate max rate control */ +#define AFR_NO_RATE 0x00 /* No reduction of frame rate */ +#define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */ +#define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */ +#define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */ + /* Auto frame rate active point control */ +#define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */ +#define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */ +#define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */ +#define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */ + /* AEC max step control */ +#define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */ + /* 1 : No limit to AEC increase step */ + +/* COM7 */ + /* SCCB Register Reset */ +#define SCCB_RESET 0x80 /* 0 : No change */ + /* 1 : Resets all registers to default */ + /* Resolution selection */ +#define SLCT_MASK 0x40 /* Mask of VGA or QVGA */ +#define SLCT_VGA 0x00 /* 0 : VGA */ +#define SLCT_QVGA 0x40 /* 1 : QVGA */ +#define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */ + /* RGB output format control */ +#define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */ +#define FMT_RGB565 0x04 /* 01 : RGB 565 */ +#define FMT_RGB555 0x08 /* 10 : RGB 555 */ +#define FMT_RGB444 0x0c /* 11 : RGB 444 */ + /* Output format control */ +#define OFMT_YUV 0x00 /* 00 : YUV */ +#define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */ +#define OFMT_RGB 0x02 /* 10 : RGB */ +#define OFMT_BRAW 0x03 /* 11 : Bayer RAW */ + +/* COM8 */ +#define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */ + /* AEC Setp size limit */ +#define UNLMT_STEP 0x40 /* 0 : Step size is limited */ + /* 1 : Unlimited step size */ +#define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */ +#define AEC_BND 0x10 /* Enable AEC below banding value */ +#define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */ +#define AGC_ON 0x04 /* AGC Enable */ +#define AWB_ON 0x02 /* AWB Enable */ +#define AEC_ON 0x01 /* AEC Enable */ + +/* COM9 */ +#define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */ + /* Automatic gain ceiling - maximum AGC value */ +#define GAIN_2x 0x00 /* 000 : 2x */ +#define GAIN_4x 0x10 /* 001 : 4x */ +#define GAIN_8x 0x20 /* 010 : 8x */ +#define GAIN_16x 0x30 /* 011 : 16x */ +#define GAIN_32x 0x40 /* 100 : 32x */ +#define GAIN_64x 0x50 /* 101 : 64x */ +#define GAIN_128x 0x60 /* 110 : 128x */ +#define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */ +#define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */ + +/* COM11 */ +#define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */ +#define SGLF_TRIG 0x01 /* Single frame transfer trigger */ + +/* EXHCH */ +#define VSIZE_LSB 0x04 /* Vertical data output size LSB */ + +/* DSP_CTRL1 */ +#define FIFO_ON 0x80 /* FIFO enable/disable selection */ +#define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */ +#define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */ +#define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */ +#define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */ +#define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */ +#define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */ +#define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */ + +/* DSP_CTRL3 */ +#define UV_MASK 0x80 /* UV output sequence option */ +#define UV_ON 0x80 /* ON */ +#define UV_OFF 0x00 /* OFF */ +#define CBAR_MASK 0x20 /* DSP Color bar mask */ +#define CBAR_ON 0x20 /* ON */ +#define CBAR_OFF 0x00 /* OFF */ + +/* HSTART */ +#define HST_VGA 0x23 +#define HST_QVGA 0x3F + +/* HSIZE */ +#define HSZ_VGA 0xA0 +#define HSZ_QVGA 0x50 + +/* VSTART */ +#define VST_VGA 0x07 +#define VST_QVGA 0x03 + +/* VSIZE */ +#define VSZ_VGA 0xF0 +#define VSZ_QVGA 0x78 + +/* HOUTSIZE */ +#define HOSZ_VGA 0xA0 +#define HOSZ_QVGA 0x50 + +/* VOUTSIZE */ +#define VOSZ_VGA 0xF0 +#define VOSZ_QVGA 0x78 + +/* + * bit configure (32 bit) + * this is used in struct ov772x_color_format :: option + */ +#define OP_UV 0x00000001 +#define OP_SWAP_RGB 0x00000002 + +/* + * ID + */ +#define OV7720 0x7720 +#define OV7725 0x7721 +#define VERSION(pid, ver) ((pid<<8)|(ver&0xFF)) + +/* + * struct + */ +struct regval_list { + unsigned char reg_num; + unsigned char value; +}; + +struct ov772x_color_format { + char *name; + __u32 fourcc; + const struct regval_list *regs; + unsigned int option; +}; + +struct ov772x_win_size { + char *name; + __u32 width; + __u32 height; + unsigned char com7_bit; + const struct regval_list *regs; +}; + +struct ov772x_priv { + struct ov772x_camera_info *info; + struct i2c_client *client; + struct soc_camera_device icd; + const struct ov772x_color_format *fmt; + const struct ov772x_win_size *win; + int model; +}; + +#define ENDMARKER { 0xff, 0xff } + +/* + * register setting for color format + */ +static const struct regval_list ov772x_RGB555_regs[] = { + { COM3, 0x00 }, + { COM7, FMT_RGB555 | OFMT_RGB }, + ENDMARKER, +}; + +static const struct regval_list ov772x_RGB565_regs[] = { + { COM3, 0x00 }, + { COM7, FMT_RGB565 | OFMT_RGB }, + ENDMARKER, +}; + +static const struct regval_list ov772x_YYUV_regs[] = { + { COM3, SWAP_YUV }, + { COM7, OFMT_YUV }, + ENDMARKER, +}; + +static const struct regval_list ov772x_UVYY_regs[] = { + { COM3, 0x00 }, + { COM7, OFMT_YUV }, + ENDMARKER, +}; + + +/* + * register setting for window size + */ +static const struct regval_list ov772x_qvga_regs[] = { + { HSTART, HST_QVGA }, + { HSIZE, HSZ_QVGA }, + { VSTART, VST_QVGA }, + { VSIZE, VSZ_QVGA }, + { HOUTSIZE, HOSZ_QVGA }, + { VOUTSIZE, VOSZ_QVGA }, + ENDMARKER, +}; + +static const struct regval_list ov772x_vga_regs[] = { + { HSTART, HST_VGA }, + { HSIZE, HSZ_VGA }, + { VSTART, VST_VGA }, + { VSIZE, VSZ_VGA }, + { HOUTSIZE, HOSZ_VGA }, + { VOUTSIZE, VOSZ_VGA }, + ENDMARKER, +}; + +/* + * supported format list + */ + +#define SETFOURCC(type) .name = (#type), .fourcc = (V4L2_PIX_FMT_ ## type) +static const struct soc_camera_data_format ov772x_fmt_lists[] = { + { + SETFOURCC(YUYV), + .depth = 16, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + { + SETFOURCC(YVYU), + .depth = 16, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + { + SETFOURCC(UYVY), + .depth = 16, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + { + SETFOURCC(RGB555), + .depth = 16, + .colorspace = V4L2_COLORSPACE_SRGB, + }, + { + SETFOURCC(RGB555X), + .depth = 16, + .colorspace = V4L2_COLORSPACE_SRGB, + }, + { + SETFOURCC(RGB565), + .depth = 16, + .colorspace = V4L2_COLORSPACE_SRGB, + }, + { + SETFOURCC(RGB565X), + .depth = 16, + .colorspace = V4L2_COLORSPACE_SRGB, + }, +}; + +/* + * color format list + */ +#define T_YUYV 0 +static const struct ov772x_color_format ov772x_cfmts[] = { + [T_YUYV] = { + SETFOURCC(YUYV), + .regs = ov772x_YYUV_regs, + }, + { + SETFOURCC(YVYU), + .regs = ov772x_YYUV_regs, + .option = OP_UV, + }, + { + SETFOURCC(UYVY), + .regs = ov772x_UVYY_regs, + }, + { + SETFOURCC(RGB555), + .regs = ov772x_RGB555_regs, + .option = OP_SWAP_RGB, + }, + { + SETFOURCC(RGB555X), + .regs = ov772x_RGB555_regs, + }, + { + SETFOURCC(RGB565), + .regs = ov772x_RGB565_regs, + .option = OP_SWAP_RGB, + }, + { + SETFOURCC(RGB565X), + .regs = ov772x_RGB565_regs, + }, +}; + + +/* + * window size list + */ +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 +#define MAX_WIDTH VGA_WIDTH +#define MAX_HEIGHT VGA_HEIGHT + +static const struct ov772x_win_size ov772x_win_vga = { + .name = "VGA", + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .com7_bit = SLCT_VGA, + .regs = ov772x_vga_regs, +}; + +static const struct ov772x_win_size ov772x_win_qvga = { + .name = "QVGA", + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .com7_bit = SLCT_QVGA, + .regs = ov772x_qvga_regs, +}; + + +/* + * general function + */ + +static int ov772x_write_array(struct i2c_client *client, + const struct regval_list *vals) +{ + while (vals->reg_num != 0xff) { + int ret = i2c_smbus_write_byte_data(client, + vals->reg_num, + vals->value); + if (ret < 0) + return ret; + vals++; + } + return 0; +} + +static int ov772x_mask_set(struct i2c_client *client, + u8 command, + u8 mask, + u8 set) +{ + s32 val = i2c_smbus_read_byte_data(client, command); + val &= ~mask; + val |= set; + + return i2c_smbus_write_byte_data(client, command, val); +} + +static int ov772x_reset(struct i2c_client *client) +{ + int ret = i2c_smbus_write_byte_data(client, COM7, SCCB_RESET); + msleep(1); + return ret; +} + +/* + * soc_camera_ops function + */ + +static int ov772x_init(struct soc_camera_device *icd) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + int ret = 0; + + if (priv->info->link.power) { + ret = priv->info->link.power(&priv->client->dev, 1); + if (ret < 0) + return ret; + } + + if (priv->info->link.reset) + ret = priv->info->link.reset(&priv->client->dev); + + return ret; +} + +static int ov772x_release(struct soc_camera_device *icd) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + int ret = 0; + + if (priv->info->link.power) + ret = priv->info->link.power(&priv->client->dev, 0); + + return ret; +} + +static int ov772x_start_capture(struct soc_camera_device *icd) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + int ret; + + if (!priv->win) + priv->win = &ov772x_win_vga; + if (!priv->fmt) + priv->fmt = &ov772x_cfmts[T_YUYV]; + + /* + * reset hardware + */ + ov772x_reset(priv->client); + + /* + * set color format + */ + ret = ov772x_write_array(priv->client, priv->fmt->regs); + if (ret < 0) + goto start_end; + + /* + * set size format + */ + ret = ov772x_write_array(priv->client, priv->win->regs); + if (ret < 0) + goto start_end; + + /* + * set COM7 bit ( QVGA or VGA ) + */ + ret = ov772x_mask_set(priv->client, + COM7, SLCT_MASK, priv->win->com7_bit); + if (ret < 0) + goto start_end; + + /* + * set UV setting + */ + if (priv->fmt->option & OP_UV) { + ret = ov772x_mask_set(priv->client, + DSP_CTRL3, UV_MASK, UV_ON); + if (ret < 0) + goto start_end; + } + + /* + * set SWAP setting + */ + if (priv->fmt->option & OP_SWAP_RGB) { + ret = ov772x_mask_set(priv->client, + COM3, SWAP_MASK, SWAP_RGB); + if (ret < 0) + goto start_end; + } + + dev_dbg(&icd->dev, + "format %s, win %s\n", priv->fmt->name, priv->win->name); + +start_end: + priv->fmt = NULL; + priv->win = NULL; + + return ret; +} + +static int ov772x_stop_capture(struct soc_camera_device *icd) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + ov772x_reset(priv->client); + return 0; +} + +static int ov772x_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + return 0; +} + +static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct soc_camera_link *icl = priv->client->dev.platform_data; + unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | + priv->info->buswidth; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +static int ov772x_get_chip_id(struct soc_camera_device *icd, + struct v4l2_chip_ident *id) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + + id->ident = priv->model; + id->revision = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov772x_get_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + int ret; + + if (reg->reg > 0xff) + return -EINVAL; + + ret = i2c_smbus_read_byte_data(priv->client, reg->reg); + if (ret < 0) + return ret; + + reg->val = (__u64)ret; + + return 0; +} + +static int ov772x_set_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + + if (reg->reg > 0xff || + reg->val > 0xff) + return -EINVAL; + + return i2c_smbus_write_byte_data(priv->client, reg->reg, reg->val); +} +#endif + +static const struct ov772x_win_size* +ov772x_select_win(u32 width, u32 height) +{ + __u32 diff; + const struct ov772x_win_size *win; + + /* default is QVGA */ + diff = abs(width - ov772x_win_qvga.width) + + abs(height - ov772x_win_qvga.height); + win = &ov772x_win_qvga; + + /* VGA */ + if (diff > + abs(width - ov772x_win_vga.width) + + abs(height - ov772x_win_vga.height)) + win = &ov772x_win_vga; + + return win; +} + + +static int ov772x_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, + struct v4l2_rect *rect) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + int ret = -EINVAL; + int i; + + /* + * select format + */ + priv->fmt = NULL; + for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) { + if (pixfmt == ov772x_cfmts[i].fourcc) { + priv->fmt = ov772x_cfmts + i; + ret = 0; + break; + } + } + + /* + * select win + */ + priv->win = ov772x_select_win(rect->width, rect->height); + + return ret; +} + +static int ov772x_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + const struct ov772x_win_size *win; + + /* + * select suitable win + */ + win = ov772x_select_win(pix->width, pix->height); + + pix->width = win->width; + pix->height = win->height; + pix->field = V4L2_FIELD_NONE; + + return 0; +} + +static int ov772x_video_probe(struct soc_camera_device *icd) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + u8 pid, ver; + const char *devname; + + /* + * We must have a parent by now. And it cannot be a wrong one. + * So this entire test is completely redundant. + */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) + return -ENODEV; + + /* + * ov772x only use 8 or 10 bit bus width + */ + if (SOCAM_DATAWIDTH_10 != priv->info->buswidth && + SOCAM_DATAWIDTH_8 != priv->info->buswidth) { + dev_err(&icd->dev, "bus width error\n"); + return -ENODEV; + } + + icd->formats = ov772x_fmt_lists; + icd->num_formats = ARRAY_SIZE(ov772x_fmt_lists); + + /* + * check and show product ID and manufacturer ID + */ + pid = i2c_smbus_read_byte_data(priv->client, PID); + ver = i2c_smbus_read_byte_data(priv->client, VER); + + switch (VERSION(pid, ver)) { + case OV7720: + devname = "ov7720"; + priv->model = V4L2_IDENT_OV7720; + break; + case OV7725: + devname = "ov7725"; + priv->model = V4L2_IDENT_OV7725; + break; + default: + dev_err(&icd->dev, + "Product ID error %x:%x\n", pid, ver); + return -ENODEV; + } + + dev_info(&icd->dev, + "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", + devname, + pid, + ver, + i2c_smbus_read_byte_data(priv->client, MIDH), + i2c_smbus_read_byte_data(priv->client, MIDL)); + + + return soc_camera_video_start(icd); +} + +static void ov772x_video_remove(struct soc_camera_device *icd) +{ + soc_camera_video_stop(icd); +} + +static struct soc_camera_ops ov772x_ops = { + .owner = THIS_MODULE, + .probe = ov772x_video_probe, + .remove = ov772x_video_remove, + .init = ov772x_init, + .release = ov772x_release, + .start_capture = ov772x_start_capture, + .stop_capture = ov772x_stop_capture, + .set_fmt = ov772x_set_fmt, + .try_fmt = ov772x_try_fmt, + .set_bus_param = ov772x_set_bus_param, + .query_bus_param = ov772x_query_bus_param, + .get_chip_id = ov772x_get_chip_id, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .get_register = ov772x_get_register, + .set_register = ov772x_set_register, +#endif +}; + +/* + * i2c_driver function + */ + +static int ov772x_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov772x_priv *priv; + struct ov772x_camera_info *info; + struct soc_camera_device *icd; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + int ret; + + info = client->dev.platform_data; + if (!info) + return -EINVAL; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&adapter->dev, + "I2C-Adapter doesn't support " + "I2C_FUNC_SMBUS_BYTE_DATA\n"); + return -EIO; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->info = info; + priv->client = client; + i2c_set_clientdata(client, priv); + + icd = &priv->icd; + icd->ops = &ov772x_ops; + icd->control = &client->dev; + icd->width_max = MAX_WIDTH; + icd->height_max = MAX_HEIGHT; + icd->iface = priv->info->link.bus_id; + + ret = soc_camera_device_register(icd); + + if (ret) { + i2c_set_clientdata(client, NULL); + kfree(priv); + } + + return ret; +} + +static int ov772x_remove(struct i2c_client *client) +{ + struct ov772x_priv *priv = i2c_get_clientdata(client); + + soc_camera_device_unregister(&priv->icd); + i2c_set_clientdata(client, NULL); + kfree(priv); + return 0; +} + +static const struct i2c_device_id ov772x_id[] = { + { "ov772x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov772x_id); + +static struct i2c_driver ov772x_i2c_driver = { + .driver = { + .name = "ov772x", + }, + .probe = ov772x_probe, + .remove = ov772x_remove, + .id_table = ov772x_id, +}; + +/* + * module function + */ + +static int __init ov772x_module_init(void) +{ + return i2c_add_driver(&ov772x_i2c_driver); +} + +static void __exit ov772x_module_exit(void) +{ + i2c_del_driver(&ov772x_i2c_driver); +} + +module_init(ov772x_module_init); +module_exit(ov772x_module_exit); + +MODULE_DESCRIPTION("SoC Camera driver for ov772x"); +MODULE_AUTHOR("Kuninori Morimoto"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index 9948078..45730fa 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -10,8 +10,8 @@ * 14478 Potsdam, Germany * * Most of this code is directly derived from his userspace driver. - * His driver works so send any reports to alan@redhat.com unless the - * userspace driver also doesn't work for you... + * His driver works so send any reports to alan@lxorguk.ukuu.org.uk + * unless the userspace driver also doesn't work for you... * * Changes: * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it> @@ -680,8 +680,7 @@ static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int * Video4linux interfacing */ -static int pms_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int pms_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); struct pms_device *pd=(struct pms_device *)dev; @@ -866,7 +865,7 @@ static int pms_do_ioctl(struct inode *inode, struct file *file, static int pms_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, pms_do_ioctl); + return video_usercopy(file, cmd, arg, pms_do_ioctl); } static ssize_t pms_read(struct file *file, char __user *buf, diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 5b81ba4..4358079 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2395,7 +2395,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, scnprintf(hdw->bus_info,sizeof(hdw->bus_info), "usb %s address %d", - hdw->usb_dev->dev.bus_id, + dev_name(&hdw->usb_dev->dev), hdw->usb_dev->devnum); ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber; diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 733680f..e641cd9 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -628,10 +628,10 @@ static void class_dev_create(struct pvr2_sysfs *sfp, class_dev->class = &class_ptr->class; if (pvr2_hdw_get_sn(sfp->channel.hdw)) { - snprintf(class_dev->bus_id, BUS_ID_SIZE, "sn-%lu", + dev_set_name(class_dev, "sn-%lu", pvr2_hdw_get_sn(sfp->channel.hdw)); } else if (pvr2_hdw_get_unit_number(sfp->channel.hdw) >= 0) { - snprintf(class_dev->bus_id, BUS_ID_SIZE, "unit-%c", + dev_set_name(class_dev, "unit-%c", pvr2_hdw_get_unit_number(sfp->channel.hdw) + 'a'); } else { kfree(class_dev); diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 97ed959..52af1c4 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -168,8 +168,7 @@ static const char *get_v4l_name(int v4l_type) * This is part of Video 4 Linux API. The procedure handles ioctl() calls. * */ -static int __pvr2_v4l2_do_ioctl(struct file *file, - unsigned int cmd, void *arg) +static int pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_v4l2 *vp = fh->vhead; @@ -864,7 +863,7 @@ static int __pvr2_v4l2_do_ioctl(struct file *file, default : ret = v4l_compat_translate_ioctl(file, cmd, - arg, __pvr2_v4l2_do_ioctl); + arg, pvr2_v4l2_do_ioctl); } pvr2_hdw_commit_ctl(hdw); @@ -890,12 +889,6 @@ static int __pvr2_v4l2_do_ioctl(struct file *file, return ret; } -static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - return __pvr2_v4l2_do_ioctl(file, cmd, arg); -} - static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) { int num = dip->devbase.num; @@ -963,7 +956,7 @@ static int pvr2_v4l2_ioctl(struct inode *inode, struct file *file, #define IVTV_IOC_G_CODEC 0xFFEE7703 #define IVTV_IOC_S_CODEC 0xFFEE7704 if (cmd == IVTV_IOC_G_CODEC || cmd == IVTV_IOC_S_CODEC) return 0; - return video_usercopy(inode, file, cmd, arg, pvr2_v4l2_do_ioctl); + return video_usercopy(file, cmd, arg, pvr2_v4l2_do_ioctl); } diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index f3897a3..1ce9da1 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -1412,7 +1412,7 @@ static int pwc_video_ioctl(struct inode *inode, struct file *file, mutex_lock(&pdev->modlock); if (!pdev->unplugged) - r = video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl); + r = video_usercopy(file, cmd, arg, pwc_video_do_ioctl); mutex_unlock(&pdev->modlock); out: return r; diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 76a1376..d7c1473 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -337,8 +337,7 @@ static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f) } -int pwc_video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +int pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); struct pwc_device *pdev; diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 7417875..c046a25 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -340,8 +340,7 @@ extern int pwc_camera_power(struct pwc_device *pdev, int power); extern int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg); /** Functions in pwc-v4l.c */ -extern int pwc_video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg); +extern int pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg); /** pwc-uncompress.c */ /* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 70a7762..9d33de2 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -25,7 +25,6 @@ #include <linux/version.h> #include <linux/device.h> #include <linux/platform_device.h> -#include <linux/mutex.h> #include <linux/clk.h> #include <media/v4l2-common.h> @@ -44,6 +43,101 @@ #define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5) #define PXA_CAM_DRV_NAME "pxa27x-camera" +/* Camera Interface */ +#define CICR0 0x0000 +#define CICR1 0x0004 +#define CICR2 0x0008 +#define CICR3 0x000C +#define CICR4 0x0010 +#define CISR 0x0014 +#define CIFR 0x0018 +#define CITOR 0x001C +#define CIBR0 0x0028 +#define CIBR1 0x0030 +#define CIBR2 0x0038 + +#define CICR0_DMAEN (1 << 31) /* DMA request enable */ +#define CICR0_PAR_EN (1 << 30) /* Parity enable */ +#define CICR0_SL_CAP_EN (1 << 29) /* Capture enable for slave mode */ +#define CICR0_ENB (1 << 28) /* Camera interface enable */ +#define CICR0_DIS (1 << 27) /* Camera interface disable */ +#define CICR0_SIM (0x7 << 24) /* Sensor interface mode mask */ +#define CICR0_TOM (1 << 9) /* Time-out mask */ +#define CICR0_RDAVM (1 << 8) /* Receive-data-available mask */ +#define CICR0_FEM (1 << 7) /* FIFO-empty mask */ +#define CICR0_EOLM (1 << 6) /* End-of-line mask */ +#define CICR0_PERRM (1 << 5) /* Parity-error mask */ +#define CICR0_QDM (1 << 4) /* Quick-disable mask */ +#define CICR0_CDM (1 << 3) /* Disable-done mask */ +#define CICR0_SOFM (1 << 2) /* Start-of-frame mask */ +#define CICR0_EOFM (1 << 1) /* End-of-frame mask */ +#define CICR0_FOM (1 << 0) /* FIFO-overrun mask */ + +#define CICR1_TBIT (1 << 31) /* Transparency bit */ +#define CICR1_RGBT_CONV (0x3 << 29) /* RGBT conversion mask */ +#define CICR1_PPL (0x7ff << 15) /* Pixels per line mask */ +#define CICR1_RGB_CONV (0x7 << 12) /* RGB conversion mask */ +#define CICR1_RGB_F (1 << 11) /* RGB format */ +#define CICR1_YCBCR_F (1 << 10) /* YCbCr format */ +#define CICR1_RGB_BPP (0x7 << 7) /* RGB bis per pixel mask */ +#define CICR1_RAW_BPP (0x3 << 5) /* Raw bis per pixel mask */ +#define CICR1_COLOR_SP (0x3 << 3) /* Color space mask */ +#define CICR1_DW (0x7 << 0) /* Data width mask */ + +#define CICR2_BLW (0xff << 24) /* Beginning-of-line pixel clock + wait count mask */ +#define CICR2_ELW (0xff << 16) /* End-of-line pixel clock + wait count mask */ +#define CICR2_HSW (0x3f << 10) /* Horizontal sync pulse width mask */ +#define CICR2_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock + wait count mask */ +#define CICR2_FSW (0x7 << 0) /* Frame stabilization + wait count mask */ + +#define CICR3_BFW (0xff << 24) /* Beginning-of-frame line clock + wait count mask */ +#define CICR3_EFW (0xff << 16) /* End-of-frame line clock + wait count mask */ +#define CICR3_VSW (0x3f << 10) /* Vertical sync pulse width mask */ +#define CICR3_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock + wait count mask */ +#define CICR3_LPF (0x7ff << 0) /* Lines per frame mask */ + +#define CICR4_MCLK_DLY (0x3 << 24) /* MCLK Data Capture Delay mask */ +#define CICR4_PCLK_EN (1 << 23) /* Pixel clock enable */ +#define CICR4_PCP (1 << 22) /* Pixel clock polarity */ +#define CICR4_HSP (1 << 21) /* Horizontal sync polarity */ +#define CICR4_VSP (1 << 20) /* Vertical sync polarity */ +#define CICR4_MCLK_EN (1 << 19) /* MCLK enable */ +#define CICR4_FR_RATE (0x7 << 8) /* Frame rate mask */ +#define CICR4_DIV (0xff << 0) /* Clock divisor mask */ + +#define CISR_FTO (1 << 15) /* FIFO time-out */ +#define CISR_RDAV_2 (1 << 14) /* Channel 2 receive data available */ +#define CISR_RDAV_1 (1 << 13) /* Channel 1 receive data available */ +#define CISR_RDAV_0 (1 << 12) /* Channel 0 receive data available */ +#define CISR_FEMPTY_2 (1 << 11) /* Channel 2 FIFO empty */ +#define CISR_FEMPTY_1 (1 << 10) /* Channel 1 FIFO empty */ +#define CISR_FEMPTY_0 (1 << 9) /* Channel 0 FIFO empty */ +#define CISR_EOL (1 << 8) /* End of line */ +#define CISR_PAR_ERR (1 << 7) /* Parity error */ +#define CISR_CQD (1 << 6) /* Camera interface quick disable */ +#define CISR_CDD (1 << 5) /* Camera interface disable done */ +#define CISR_SOF (1 << 4) /* Start of frame */ +#define CISR_EOF (1 << 3) /* End of frame */ +#define CISR_IFO_2 (1 << 2) /* FIFO overrun for Channel 2 */ +#define CISR_IFO_1 (1 << 1) /* FIFO overrun for Channel 1 */ +#define CISR_IFO_0 (1 << 0) /* FIFO overrun for Channel 0 */ + +#define CIFR_FLVL2 (0x7f << 23) /* FIFO 2 level mask */ +#define CIFR_FLVL1 (0x7f << 16) /* FIFO 1 level mask */ +#define CIFR_FLVL0 (0xff << 8) /* FIFO 0 level mask */ +#define CIFR_THL_0 (0x3 << 4) /* Threshold Level for Channel 0 FIFO */ +#define CIFR_RESET_F (1 << 3) /* Reset input FIFOs */ +#define CIFR_FEN2 (1 << 2) /* FIFO enable for channel 2 */ +#define CIFR_FEN1 (1 << 1) /* FIFO enable for channel 1 */ +#define CIFR_FEN0 (1 << 0) /* FIFO enable for channel 0 */ + #define CICR0_SIM_MP (0 << 24) #define CICR0_SIM_SP (1 << 24) #define CICR0_SIM_MS (2 << 24) @@ -71,8 +165,6 @@ CICR0_PERRM | CICR0_QDM | CICR0_CDM | CICR0_SOFM | \ CICR0_EOFM | CICR0_FOM) -static DEFINE_MUTEX(camera_lock); - /* * Structures */ @@ -122,7 +214,9 @@ struct pxa_camera_dev { struct pxacamera_platform_data *pdata; struct resource *res; unsigned long platform_flags; - unsigned long platform_mclk_10khz; + unsigned long ciclk; + unsigned long mclk; + u32 mclk_divisor; struct list_head capture; @@ -145,8 +239,7 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); @@ -172,8 +265,7 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) { struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); int i; @@ -249,8 +341,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field) { struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); int ret; @@ -369,8 +460,7 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); struct pxa_buffer *active; @@ -387,7 +477,10 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, active = pcdev->active; if (!active) { - CIFR |= CIFR_RESET_F; + unsigned long cifr, cicr0; + + cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F; + __raw_writel(cifr, pcdev->base + CIFR); for (i = 0; i < pcdev->channels; i++) { DDADR(pcdev->dma_chans[i]) = buf->dmas[i].sg_dma; @@ -396,7 +489,9 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, } pcdev->active = buf; - CICR0 |= CICR0_ENB; + + cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_ENB; + __raw_writel(cicr0, pcdev->base + CICR0); } else { struct pxa_cam_dma *buf_dma; struct pxa_cam_dma *act_dma; @@ -480,6 +575,8 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, struct videobuf_buffer *vb, struct pxa_buffer *buf) { + unsigned long cicr0; + /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ list_del_init(&vb->queue); vb->state = VIDEOBUF_DONE; @@ -492,7 +589,9 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, DCSR(pcdev->dma_chans[0]) = 0; DCSR(pcdev->dma_chans[1]) = 0; DCSR(pcdev->dma_chans[2]) = 0; - CICR0 &= ~CICR0_ENB; + + cicr0 = __raw_readl(pcdev->base + CICR0) & ~CICR0_ENB; + __raw_writel(cicr0, pcdev->base + CICR0); return; } @@ -507,6 +606,7 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, unsigned long flags; u32 status, camera_status, overrun; struct videobuf_buffer *vb; + unsigned long cifr, cicr0; spin_lock_irqsave(&pcdev->lock, flags); @@ -529,22 +629,26 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, goto out; } - camera_status = CISR; + camera_status = __raw_readl(pcdev->base + CISR); overrun = CISR_IFO_0; if (pcdev->channels == 3) overrun |= CISR_IFO_1 | CISR_IFO_2; if (camera_status & overrun) { dev_dbg(pcdev->dev, "FIFO overrun! CISR: %x\n", camera_status); /* Stop the Capture Interface */ - CICR0 &= ~CICR0_ENB; + cicr0 = __raw_readl(pcdev->base + CICR0) & ~CICR0_ENB; + __raw_writel(cicr0, pcdev->base + CICR0); + /* Stop DMA */ DCSR(channel) = 0; /* Reset the FIFOs */ - CIFR |= CIFR_RESET_F; + cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F; + __raw_writel(cifr, pcdev->base + CIFR); /* Enable End-Of-Frame Interrupt */ - CICR0 &= ~CICR0_EOFM; + cicr0 &= ~CICR0_EOFM; + __raw_writel(cicr0, pcdev->base + CICR0); /* Restart the Capture Interface */ - CICR0 |= CICR0_ENB; + __raw_writel(cicr0 | CICR0_ENB, pcdev->base + CICR0); goto out; } @@ -600,24 +704,43 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q, sizeof(struct pxa_buffer), icd); } -static int mclk_get_divisor(struct pxa_camera_dev *pcdev) +static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev) { - unsigned int mclk_10khz = pcdev->platform_mclk_10khz; - unsigned long div; + unsigned long mclk = pcdev->mclk; + u32 div; unsigned long lcdclk; - lcdclk = clk_get_rate(pcdev->clk) / 10000; + lcdclk = clk_get_rate(pcdev->clk); + pcdev->ciclk = lcdclk; + + /* mclk <= ciclk / 4 (27.4.2) */ + if (mclk > lcdclk / 4) { + mclk = lcdclk / 4; + dev_warn(pcdev->dev, "Limiting master clock to %lu\n", mclk); + } + + /* We verify mclk != 0, so if anyone breaks it, here comes their Oops */ + div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1; - /* We verify platform_mclk_10khz != 0, so if anyone breaks it, here - * they get a nice Oops */ - div = (lcdclk + 2 * mclk_10khz - 1) / (2 * mclk_10khz) - 1; + /* If we're not supplying MCLK, leave it at 0 */ + if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) + pcdev->mclk = lcdclk / (2 * (div + 1)); - dev_dbg(pcdev->dev, "LCD clock %lukHz, target freq %dkHz, " - "divisor %lu\n", lcdclk * 10, mclk_10khz * 10, div); + dev_dbg(pcdev->dev, "LCD clock %luHz, target freq %luHz, " + "divisor %u\n", lcdclk, mclk, div); return div; } +static void recalculate_fifo_timeout(struct pxa_camera_dev *pcdev, + unsigned long pclk) +{ + /* We want a timeout > 1 pixel time, not ">=" */ + u32 ciclk_per_pixel = pcdev->ciclk / pclk + 1; + + __raw_writel(ciclk_per_pixel, pcdev->base + CITOR); +} + static void pxa_camera_activate(struct pxa_camera_dev *pcdev) { struct pxacamera_platform_data *pdata = pcdev->pdata; @@ -631,7 +754,8 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev) pdata->init(pcdev->dev); } - CICR0 = 0x3FF; /* disable all interrupts */ + /* disable all interrupts */ + __raw_writel(0x3ff, pcdev->base + CICR0); if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) cicr4 |= CICR4_PCLK_EN; @@ -644,7 +768,14 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev) if (pcdev->platform_flags & PXA_CAMERA_VSP) cicr4 |= CICR4_VSP; - CICR4 = mclk_get_divisor(pcdev) | cicr4; + __raw_writel(pcdev->mclk_divisor | cicr4, pcdev->base + CICR4); + + if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) + /* Initialise the timeout under the assumption pclk = mclk */ + recalculate_fifo_timeout(pcdev, pcdev->mclk); + else + /* "Safe default" - 13MHz */ + recalculate_fifo_timeout(pcdev, 13000000); clk_enable(pcdev->clk); } @@ -657,14 +788,15 @@ static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev) static irqreturn_t pxa_camera_irq(int irq, void *data) { struct pxa_camera_dev *pcdev = data; - unsigned int status = CISR; + unsigned long status, cicr0; - dev_dbg(pcdev->dev, "Camera interrupt status 0x%x\n", status); + status = __raw_readl(pcdev->base + CISR); + dev_dbg(pcdev->dev, "Camera interrupt status 0x%lx\n", status); if (!status) return IRQ_NONE; - CISR = status; + __raw_writel(status, pcdev->base + CISR); if (status & CISR_EOF) { int i; @@ -673,22 +805,24 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) pcdev->active->dmas[i].sg_dma; DCSR(pcdev->dma_chans[i]) = DCSR_RUN; } - CICR0 |= CICR0_EOFM; + cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_EOFM; + __raw_writel(cicr0, pcdev->base + CICR0); } return IRQ_HANDLED; } -/* The following two functions absolutely depend on the fact, that - * there can be only one camera on PXA quick capture interface */ +/* + * The following two functions absolutely depend on the fact, that + * there can be only one camera on PXA quick capture interface + * Called with .video_lock held + */ static int pxa_camera_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; int ret; - mutex_lock(&camera_lock); - if (pcdev->icd) { ret = -EBUSY; goto ebusy; @@ -704,11 +838,10 @@ static int pxa_camera_add_device(struct soc_camera_device *icd) pcdev->icd = icd; ebusy: - mutex_unlock(&camera_lock); - return ret; } +/* Called with .video_lock held */ static void pxa_camera_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); @@ -720,7 +853,7 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd) icd->devnum); /* disable capture, disable interrupts */ - CICR0 = 0x3ff; + __raw_writel(0x3ff, pcdev->base + CICR0); /* Stop DMA engine */ DCSR(pcdev->dma_chans[0]) = 0; @@ -767,6 +900,9 @@ static int test_platform_param(struct pxa_camera_dev *pcdev, if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8)) return -EINVAL; *flags |= SOCAM_DATAWIDTH_8; + break; + default: + return -EINVAL; } return 0; @@ -774,11 +910,10 @@ static int test_platform_param(struct pxa_camera_dev *pcdev, static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; unsigned long dw, bpp, bus_flags, camera_flags, common_flags; - u32 cicr0, cicr1, cicr4 = 0; + u32 cicr0, cicr1, cicr2, cicr3, cicr4 = 0; int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags); if (ret < 0) @@ -825,12 +960,10 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) * We fix bit-per-pixel equal to data-width... */ switch (common_flags & SOCAM_DATAWIDTH_MASK) { case SOCAM_DATAWIDTH_10: - icd->buswidth = 10; dw = 4; bpp = 0x40; break; case SOCAM_DATAWIDTH_9: - icd->buswidth = 9; dw = 3; bpp = 0x20; break; @@ -838,7 +971,6 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) /* Actually it can only be 8 now, * default is just to silence compiler warnings */ case SOCAM_DATAWIDTH_8: - icd->buswidth = 8; dw = 2; bpp = 0; } @@ -854,9 +986,9 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) if (common_flags & SOCAM_VSYNC_ACTIVE_LOW) cicr4 |= CICR4_VSP; - cicr0 = CICR0; + cicr0 = __raw_readl(pcdev->base + CICR0); if (cicr0 & CICR0_ENB) - CICR0 = cicr0 & ~CICR0_ENB; + __raw_writel(cicr0 & ~CICR0_ENB, pcdev->base + CICR0); cicr1 = CICR1_PPL_VAL(icd->width - 1) | bpp | dw; @@ -864,7 +996,17 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) case V4L2_PIX_FMT_YUV422P: pcdev->channels = 3; cicr1 |= CICR1_YCBCR_F; + /* + * Normally, pxa bus wants as input UYVY format. We allow all + * reorderings of the YUV422 format, as no processing is done, + * and the YUV stream is just passed through without any + * transformation. Note that UYVY is the only format that + * should be used if pxa framebuffer Overlay2 is used. + */ + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: cicr1 |= CICR1_COLOR_SP_VAL(2); break; case V4L2_PIX_FMT_RGB555: @@ -876,27 +1018,32 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) break; } - CICR1 = cicr1; - CICR2 = 0; - CICR3 = CICR3_LPF_VAL(icd->height - 1) | + cicr2 = 0; + cicr3 = CICR3_LPF_VAL(icd->height - 1) | CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top)); - CICR4 = mclk_get_divisor(pcdev) | cicr4; + cicr4 |= pcdev->mclk_divisor; + + __raw_writel(cicr1, pcdev->base + CICR1); + __raw_writel(cicr2, pcdev->base + CICR2); + __raw_writel(cicr3, pcdev->base + CICR3); + __raw_writel(cicr4, pcdev->base + CICR4); /* CIF interrupts are not used, only DMA */ - CICR0 = (pcdev->platform_flags & PXA_CAMERA_MASTER ? - CICR0_SIM_MP : (CICR0_SL_CAP_EN | CICR0_SIM_SP)) | - CICR0_DMAEN | CICR0_IRQ_MASK | (cicr0 & CICR0_ENB); + cicr0 = (cicr0 & CICR0_ENB) | (pcdev->platform_flags & PXA_CAMERA_MASTER ? + CICR0_SIM_MP : (CICR0_SL_CAP_EN | CICR0_SIM_SP)); + cicr0 |= CICR0_DMAEN | CICR0_IRQ_MASK; + __raw_writel(cicr0, pcdev->base + CICR0); return 0; } -static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt) +static int pxa_camera_try_bus_param(struct soc_camera_device *icd, + unsigned char buswidth) { - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; unsigned long bus_flags, camera_flags; - int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags); + int ret = test_platform_param(pcdev, buswidth, &bus_flags); if (ret < 0) return ret; @@ -906,28 +1053,210 @@ static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt) return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL; } -static int pxa_camera_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static const struct soc_camera_data_format pxa_camera_formats[] = { + { + .name = "Planar YUV422 16 bit", + .depth = 16, + .fourcc = V4L2_PIX_FMT_YUV422P, + .colorspace = V4L2_COLORSPACE_JPEG, + }, +}; + +static bool buswidth_supported(struct soc_camera_device *icd, int depth) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + + switch (depth) { + case 8: + return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8); + case 9: + return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9); + case 10: + return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10); + } + return false; +} + +static int required_buswidth(const struct soc_camera_data_format *fmt) { - return icd->ops->set_fmt_cap(icd, pixfmt, rect); + switch (fmt->fourcc) { + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB555: + return 8; + default: + return fmt->depth; + } } -static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, + struct soc_camera_format_xlate *xlate) { - /* limit to pxa hardware capabilities */ - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.height > 2048) - f->fmt.pix.height = 2048; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.width > 2048) - f->fmt.pix.width = 2048; - f->fmt.pix.width &= ~0x01; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + int formats = 0, buswidth, ret; + + buswidth = required_buswidth(icd->formats + idx); + + if (!buswidth_supported(icd, buswidth)) + return 0; + + ret = pxa_camera_try_bus_param(icd, buswidth); + if (ret < 0) + return 0; + + switch (icd->formats[idx].fourcc) { + case V4L2_PIX_FMT_UYVY: + formats++; + if (xlate) { + xlate->host_fmt = &pxa_camera_formats[0]; + xlate->cam_fmt = icd->formats + idx; + xlate->buswidth = buswidth; + xlate++; + dev_dbg(&ici->dev, "Providing format %s using %s\n", + pxa_camera_formats[0].name, + icd->formats[idx].name); + } + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB555: + formats++; + if (xlate) { + xlate->host_fmt = icd->formats + idx; + xlate->cam_fmt = icd->formats + idx; + xlate->buswidth = buswidth; + xlate++; + dev_dbg(&ici->dev, "Providing format %s packed\n", + icd->formats[idx].name); + } + break; + default: + /* Generic pass-through */ + formats++; + if (xlate) { + xlate->host_fmt = icd->formats + idx; + xlate->cam_fmt = icd->formats + idx; + xlate->buswidth = icd->formats[idx].depth; + xlate++; + dev_dbg(&ici->dev, + "Providing format %s in pass-through mode\n", + icd->formats[idx].name); + } + } + + return formats; +} + +static int pxa_camera_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + const struct soc_camera_data_format *host_fmt, *cam_fmt = NULL; + const struct soc_camera_format_xlate *xlate; + struct soc_camera_sense sense = { + .master_clock = pcdev->mclk, + .pixel_clock_max = pcdev->ciclk / 4, + }; + int ret, buswidth; + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + return -EINVAL; + } + + buswidth = xlate->buswidth; + host_fmt = xlate->host_fmt; + cam_fmt = xlate->cam_fmt; + + /* If PCLK is used to latch data from the sensor, check sense */ + if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) + icd->sense = &sense; + + switch (pixfmt) { + case 0: /* Only geometry change */ + ret = icd->ops->set_fmt(icd, pixfmt, rect); + break; + default: + ret = icd->ops->set_fmt(icd, cam_fmt->fourcc, rect); + } + + icd->sense = NULL; + + if (ret < 0) { + dev_warn(&ici->dev, "Failed to configure for format %x\n", + pixfmt); + } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { + if (sense.pixel_clock > sense.pixel_clock_max) { + dev_err(&ici->dev, + "pixel clock %lu set by the camera too high!", + sense.pixel_clock); + return -EIO; + } + recalculate_fifo_timeout(pcdev, sense.pixel_clock); + } + if (pixfmt && !ret) { + icd->buswidth = buswidth; + icd->current_fmt = host_fmt; + } + + return ret; +} + +static int pxa_camera_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + __u32 pixfmt = pix->pixelformat; + enum v4l2_field field; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + return -EINVAL; + } + + /* limit to pxa hardware capabilities */ + if (pix->height < 32) + pix->height = 32; + if (pix->height > 2048) + pix->height = 2048; + if (pix->width < 48) + pix->width = 48; + if (pix->width > 2048) + pix->width = 2048; + pix->width &= ~0x01; + + pix->bytesperline = pix->width * + DIV_ROUND_UP(xlate->host_fmt->depth, 8); + pix->sizeimage = pix->height * pix->bytesperline; + + /* camera has to see its format, but the user the original one */ + pix->pixelformat = xlate->cam_fmt->fourcc; /* limit to sensor capabilities */ - return icd->ops->try_fmt_cap(icd, f); + ret = icd->ops->try_fmt(icd, f); + pix->pixelformat = xlate->host_fmt->fourcc; + + field = pix->field; + + if (field == V4L2_FIELD_ANY) { + pix->field = V4L2_FIELD_NONE; + } else if (field != V4L2_FIELD_NONE) { + dev_err(&icd->dev, "Field type %d unsupported.\n", field); + return -EINVAL; + } + + return ret; } static int pxa_camera_reqbufs(struct soc_camera_file *icf, @@ -979,16 +1308,15 @@ static int pxa_camera_querycap(struct soc_camera_host *ici, static int pxa_camera_suspend(struct soc_camera_device *icd, pm_message_t state) { - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; int i = 0, ret = 0; - pcdev->save_cicr[i++] = CICR0; - pcdev->save_cicr[i++] = CICR1; - pcdev->save_cicr[i++] = CICR2; - pcdev->save_cicr[i++] = CICR3; - pcdev->save_cicr[i++] = CICR4; + pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR0); + pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR1); + pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR2); + pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3); + pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4); if ((pcdev->icd) && (pcdev->icd->ops->suspend)) ret = pcdev->icd->ops->suspend(pcdev->icd, state); @@ -998,8 +1326,7 @@ static int pxa_camera_suspend(struct soc_camera_device *icd, pm_message_t state) static int pxa_camera_resume(struct soc_camera_device *icd) { - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; int i = 0, ret = 0; @@ -1007,23 +1334,27 @@ static int pxa_camera_resume(struct soc_camera_device *icd) DRCMR(69) = pcdev->dma_chans[1] | DRCMR_MAPVLD; DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD; - CICR0 = pcdev->save_cicr[i++] & ~CICR0_ENB; - CICR1 = pcdev->save_cicr[i++]; - CICR2 = pcdev->save_cicr[i++]; - CICR3 = pcdev->save_cicr[i++]; - CICR4 = pcdev->save_cicr[i++]; + __raw_writel(pcdev->save_cicr[i++] & ~CICR0_ENB, pcdev->base + CICR0); + __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR1); + __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR2); + __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR3); + __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4); if ((pcdev->icd) && (pcdev->icd->ops->resume)) ret = pcdev->icd->ops->resume(pcdev->icd); /* Restart frame capture if active buffer exists */ if (!ret && pcdev->active) { + unsigned long cifr, cicr0; + /* Reset the FIFOs */ - CIFR |= CIFR_RESET_F; - /* Enable End-Of-Frame Interrupt */ - CICR0 &= ~CICR0_EOFM; - /* Restart the Capture Interface */ - CICR0 |= CICR0_ENB; + cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F; + __raw_writel(cifr, pcdev->base + CIFR); + + cicr0 = __raw_readl(pcdev->base + CICR0); + cicr0 &= ~CICR0_EOFM; /* Enable End-Of-Frame Interrupt */ + cicr0 |= CICR0_ENB; /* Restart the Capture Interface */ + __raw_writel(cicr0, pcdev->base + CICR0); } return ret; @@ -1035,13 +1366,13 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = { .remove = pxa_camera_remove_device, .suspend = pxa_camera_suspend, .resume = pxa_camera_resume, - .set_fmt_cap = pxa_camera_set_fmt_cap, - .try_fmt_cap = pxa_camera_try_fmt_cap, + .get_formats = pxa_camera_get_formats, + .set_fmt = pxa_camera_set_fmt, + .try_fmt = pxa_camera_try_fmt, .init_videobuf = pxa_camera_init_videobuf, .reqbufs = pxa_camera_reqbufs, .poll = pxa_camera_poll, .querycap = pxa_camera_querycap, - .try_bus_param = pxa_camera_try_bus_param, .set_bus_param = pxa_camera_set_bus_param, }; @@ -1092,14 +1423,17 @@ static int pxa_camera_probe(struct platform_device *pdev) "data widths, using default 10 bit\n"); pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10; } - pcdev->platform_mclk_10khz = pcdev->pdata->mclk_10khz; - if (!pcdev->platform_mclk_10khz) { + pcdev->mclk = pcdev->pdata->mclk_10khz * 10000; + if (!pcdev->mclk) { dev_warn(&pdev->dev, - "mclk_10khz == 0! Please, fix your platform data. " + "mclk == 0! Please, fix your platform data. " "Using default 20MHz\n"); - pcdev->platform_mclk_10khz = 2000; + pcdev->mclk = 20000000; } + pcdev->dev = &pdev->dev; + pcdev->mclk_divisor = mclk_get_divisor(pcdev); + INIT_LIST_HEAD(&pcdev->capture); spin_lock_init(&pcdev->lock); @@ -1119,7 +1453,6 @@ static int pxa_camera_probe(struct platform_device *pdev) } pcdev->irq = irq; pcdev->base = base; - pcdev->dev = &pdev->dev; /* request dma */ err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH, diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c index 4a21b8a..f159441 100644 --- a/drivers/media/video/saa5246a.c +++ b/drivers/media/video/saa5246a.c @@ -15,7 +15,7 @@ * <richard.guenther@student.uni-tuebingen.de> * * with changes by - * Alan Cox <Alan.Cox@linux.org> + * Alan Cox <alan@lxorguk.ukuu.org.uk> * * and * @@ -804,8 +804,7 @@ static inline int saa5246a_stop_dau(struct saa5246a_device *t, * * Returns 0 if successful */ -static int do_saa5246a_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int do_saa5246a_ioctl(struct file *file, unsigned int cmd, void *arg) { struct saa5246a_device *t = video_drvdata(file); @@ -953,7 +952,7 @@ static int saa5246a_ioctl(struct inode *inode, struct file *file, cmd = vtx_fix_command(cmd); mutex_lock(&t->lock); - err = video_usercopy(inode, file, cmd, arg, do_saa5246a_ioctl); + err = video_usercopy(file, cmd, arg, do_saa5246a_ioctl); mutex_unlock(&t->lock); return err; } diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c index 3bb959c..6ef3aff 100644 --- a/drivers/media/video/saa5249.c +++ b/drivers/media/video/saa5249.c @@ -8,7 +8,7 @@ * you can add arbitary multiple teletext devices to Linux video4linux * now (well 32 anyway). * - * Alan Cox <Alan.Cox@linux.org> + * Alan Cox <alan@lxorguk.ukuu.org.uk> * * The original driver was heavily modified to match the i2c interface * It was truncated to use the WinTV boards, too. @@ -190,8 +190,7 @@ static int i2c_getdata(struct saa5249_device *t, int count, u8 *buf) * Standard character-device-driver functions */ -static int do_saa5249_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int do_saa5249_ioctl(struct file *file, unsigned int cmd, void *arg) { static int virtual_mode = false; struct saa5249_device *t = video_drvdata(file); @@ -488,7 +487,7 @@ static int saa5249_ioctl(struct inode *inode, struct file *file, cmd = vtx_fix_command(cmd); mutex_lock(&t->lock); - err = video_usercopy(inode,file,cmd,arg,do_saa5249_ioctl); + err = video_usercopy(file, cmd, arg, do_saa5249_ioctl); mutex_unlock(&t->lock); return err; } diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index c8e9cb3..22708ec 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -44,7 +44,7 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv-legacy.h> #include <media/saa7115.h> @@ -70,6 +70,7 @@ static unsigned short normal_i2c[] = { I2C_CLIENT_INSMOD; struct saa711x_state { + struct v4l2_subdev sd; v4l2_std_id std; int input; int output; @@ -89,10 +90,17 @@ struct saa711x_state { u8 apll; }; +static inline struct saa711x_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa711x_state, sd); +} + /* ----------------------------------------------------------------------- */ -static inline int saa711x_write(struct i2c_client *client, u8 reg, u8 value) +static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_write_byte_data(client, reg, value); } @@ -128,9 +136,9 @@ static int saa711x_has_reg(const int id, const u8 reg) return 1; } -static int saa711x_writeregs(struct i2c_client *client, const unsigned char *regs) +static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); unsigned char reg, data; while (*regs != 0x00) { @@ -139,18 +147,20 @@ static int saa711x_writeregs(struct i2c_client *client, const unsigned char *reg /* According with datasheets, reserved regs should be filled with 0 - seems better not to touch on they */ - if (saa711x_has_reg(state->ident,reg)) { - if (saa711x_write(client, reg, data) < 0) + if (saa711x_has_reg(state->ident, reg)) { + if (saa711x_write(sd, reg, data) < 0) return -1; } else { - v4l_dbg(1, debug, client, "tried to access reserved reg 0x%02x\n", reg); + v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg); } } return 0; } -static inline int saa711x_read(struct i2c_client *client, u8 reg) +static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte_data(client, reg); } @@ -601,7 +611,7 @@ static int saa711x_odd_parity(u8 c) return c & 1; } -static int saa711x_decode_vps(u8 * dst, u8 * p) +static int saa711x_decode_vps(u8 *dst, u8 *p) { static const u8 biphase_tbl[] = { 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, @@ -648,7 +658,7 @@ static int saa711x_decode_vps(u8 * dst, u8 * p) return err & 0xf0; } -static int saa711x_decode_wss(u8 * p) +static int saa711x_decode_wss(u8 *p) { static const int wss_bits[8] = { 0, 0, 0, 1, 0, 1, 1, 1 @@ -675,9 +685,9 @@ static int saa711x_decode_wss(u8 * p) return wss; } -static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq) +static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); u32 acpf; u32 acni; u32 hz; @@ -685,10 +695,10 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq) u8 acc = 0; /* reg 0x3a, audio clock control */ /* Checks for chips that don't have audio clock (saa7111, saa7113) */ - if (!saa711x_has_reg(state->ident,R_30_AUD_MAST_CLK_CYCLES_PER_FIELD)) + if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD)) return 0; - v4l_dbg(1, debug, client, "set audio clock freq: %d\n", freq); + v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq); /* sanity check */ if (freq < 32000 || freq > 48000) @@ -715,66 +725,66 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq) if (state->apll) acc |= 0x08; - saa711x_write(client, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); - saa711x_write(client, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); - saa711x_write(client, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc); + saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); + saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); + saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc); - saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); - saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1, + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1, (acpf >> 8) & 0xff); - saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2, + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2, (acpf >> 16) & 0x03); - saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff); - saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff); - saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f); + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff); + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff); + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f); state->audclk_freq = freq; return 0; } -static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(client, "invalid brightness setting %d\n", ctrl->value); + v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value); return -ERANGE; } state->bright = ctrl->value; - saa711x_write(client, R_0A_LUMA_BRIGHT_CNTL, state->bright); + saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright); break; case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid contrast setting %d\n", ctrl->value); + v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value); return -ERANGE; } state->contrast = ctrl->value; - saa711x_write(client, R_0B_LUMA_CONTRAST_CNTL, state->contrast); + saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast); break; case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid saturation setting %d\n", ctrl->value); + v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value); return -ERANGE; } state->sat = ctrl->value; - saa711x_write(client, R_0C_CHROMA_SAT_CNTL, state->sat); + saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat); break; case V4L2_CID_HUE: if (ctrl->value < -127 || ctrl->value > 127) { - v4l_err(client, "invalid hue setting %d\n", ctrl->value); + v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } state->hue = ctrl->value; - saa711x_write(client, R_0D_CHROMA_HUE_CNTL, state->hue); + saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue); break; default: @@ -784,9 +794,9 @@ static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c return 0; } -static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -808,16 +818,16 @@ static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *c return 0; } -static int saa711x_set_size(struct i2c_client *client, int width, int height) +static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); int HPSC, HFSC; int VSCY; int res; int is_50hz = state->std & V4L2_STD_625_50; int Vsrc = is_50hz ? 576 : 480; - v4l_dbg(1, debug, client, "decoder set size to %ix%i\n",width,height); + v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height); /* FIXME need better bounds checking here */ if ((width < 1) || (width > 1440)) @@ -825,7 +835,7 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height) if ((height < 1) || (height > Vsrc)) return -EINVAL; - if (!saa711x_has_reg(state->ident,R_D0_B_HORIZ_PRESCALING)) { + if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) { /* Decoder only supports 720 columns and 480 or 576 lines */ if (width != 720) return -EINVAL; @@ -843,22 +853,22 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height) /* Set output width/height */ /* width */ - saa711x_write(client, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, + saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, (u8) (width & 0xff)); - saa711x_write(client, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, + saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, (u8) ((width >> 8) & 0xff)); /* Vertical Scaling uses height/2 */ - res=height/2; + res = height / 2; /* On 60Hz, it is using a higher Vertical Output Size */ if (!is_50hz) res += (VRES_60HZ - 480) >> 1; /* height */ - saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, + saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, (u8) (res & 0xff)); - saa711x_write(client, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, + saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, (u8) ((res >> 8) & 0xff)); /* Scaling settings */ @@ -869,54 +879,54 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height) HFSC = (int)((1024 * 720) / (HPSC * width)); /* FIXME hardcodes to "Task B" * write H prescaler integer */ - saa711x_write(client, R_D0_B_HORIZ_PRESCALING, + saa711x_write(sd, R_D0_B_HORIZ_PRESCALING, (u8) (HPSC & 0x3f)); - v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); + v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); /* write H fine-scaling (luminance) */ - saa711x_write(client, R_D8_B_HORIZ_LUMA_SCALING_INC, + saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC, (u8) (HFSC & 0xff)); - saa711x_write(client, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, + saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, (u8) ((HFSC >> 8) & 0xff)); /* write H fine-scaling (chrominance) * must be lum/2, so i'll just bitshift :) */ - saa711x_write(client, R_DC_B_HORIZ_CHROMA_SCALING, + saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING, (u8) ((HFSC >> 1) & 0xff)); - saa711x_write(client, R_DD_B_HORIZ_CHROMA_SCALING_MSB, + saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB, (u8) ((HFSC >> 9) & 0xff)); VSCY = (int)((1024 * Vsrc) / height); - v4l_dbg(1, debug, client, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); + v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); /* Correct Contrast and Luminance */ - saa711x_write(client, R_D5_B_LUMA_CONTRAST_CNTL, + saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL, (u8) (64 * 1024 / VSCY)); - saa711x_write(client, R_D6_B_CHROMA_SATURATION_CNTL, + saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL, (u8) (64 * 1024 / VSCY)); /* write V fine-scaling (luminance) */ - saa711x_write(client, R_E0_B_VERT_LUMA_SCALING_INC, + saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC, (u8) (VSCY & 0xff)); - saa711x_write(client, R_E1_B_VERT_LUMA_SCALING_INC_MSB, + saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB, (u8) ((VSCY >> 8) & 0xff)); /* write V fine-scaling (chrominance) */ - saa711x_write(client, R_E2_B_VERT_CHROMA_SCALING_INC, + saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC, (u8) (VSCY & 0xff)); - saa711x_write(client, R_E3_B_VERT_CHROMA_SCALING_INC_MSB, + saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB, (u8) ((VSCY >> 8) & 0xff)); - saa711x_writeregs(client, saa7115_cfg_reset_scaler); + saa711x_writeregs(sd, saa7115_cfg_reset_scaler); /* Activates task "B" */ - saa711x_write(client, R_80_GLOBAL_CNTL_1, - saa711x_read(client,R_80_GLOBAL_CNTL_1) | 0x20); + saa711x_write(sd, R_80_GLOBAL_CNTL_1, + saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20); return 0; } -static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) +static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); /* Prevent unnecessary standard changes. During a standard change the I-Port is temporarily disabled. Any devices @@ -932,13 +942,13 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) // This works for NTSC-M, SECAM-L and the 50Hz PAL variants. if (std & V4L2_STD_525_60) { - v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n"); - saa711x_writeregs(client, saa7115_cfg_60hz_video); - saa711x_set_size(client, 720, 480); + v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n"); + saa711x_writeregs(sd, saa7115_cfg_60hz_video); + saa711x_set_size(sd, 720, 480); } else { - v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n"); - saa711x_writeregs(client, saa7115_cfg_50hz_video); - saa711x_set_size(client, 720, 576); + v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n"); + saa711x_writeregs(sd, saa7115_cfg_50hz_video); + saa711x_set_size(sd, 720, 576); } /* Register 0E - Bits D6-D4 on NO-AUTO mode @@ -952,7 +962,7 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) */ if (state->ident == V4L2_IDENT_SAA7111 || state->ident == V4L2_IDENT_SAA7113) { - u8 reg = saa711x_read(client, R_0E_CHROMA_CNTL_1) & 0x8f; + u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f; if (std == V4L2_STD_PAL_M) { reg |= 0x30; @@ -965,87 +975,31 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) } else if (std & V4L2_STD_SECAM) { reg |= 0x50; } - saa711x_write(client, R_0E_CHROMA_CNTL_1, reg); + saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg); } else { /* restart task B if needed */ - int taskb = saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10; + int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10; if (taskb && state->ident == V4L2_IDENT_SAA7114) { - saa711x_writeregs(client, saa7115_cfg_vbi_on); + saa711x_writeregs(sd, saa7115_cfg_vbi_on); } /* switch audio mode too! */ - saa711x_set_audio_clock_freq(client, state->audclk_freq); - } -} - -static v4l2_std_id saa711x_get_v4lstd(struct i2c_client *client) -{ - struct saa711x_state *state = i2c_get_clientdata(client); - - return state->std; -} - -static void saa711x_log_status(struct i2c_client *client) -{ - struct saa711x_state *state = i2c_get_clientdata(client); - int reg1e, reg1f; - int signalOk; - int vcr; - - v4l_info(client, "Audio frequency: %d Hz\n", state->audclk_freq); - if (state->ident != V4L2_IDENT_SAA7115) { - /* status for the saa7114 */ - reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC); - signalOk = (reg1f & 0xc1) == 0x81; - v4l_info(client, "Video signal: %s\n", signalOk ? "ok" : "bad"); - v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); - return; - } - - /* status for the saa7115 */ - reg1e = saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC); - reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC); - - signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; - vcr = !(reg1f & 0x10); - - if (state->input >= 6) { - v4l_info(client, "Input: S-Video %d\n", state->input - 6); - } else { - v4l_info(client, "Input: Composite %d\n", state->input); + saa711x_s_clock_freq(sd, state->audclk_freq); } - v4l_info(client, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); - v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); - - switch (reg1e & 0x03) { - case 1: - v4l_info(client, "Detected format: NTSC\n"); - break; - case 2: - v4l_info(client, "Detected format: PAL\n"); - break; - case 3: - v4l_info(client, "Detected format: SECAM\n"); - break; - default: - v4l_info(client, "Detected format: BW/No color\n"); - break; - } - v4l_info(client, "Width, Height: %d, %d\n", state->width, state->height); } /* setup the sliced VBI lcr registers according to the sliced VBI format */ -static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt) +static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); int is_50hz = (state->std & V4L2_STD_625_50); u8 lcr[24]; int i, x; #if 1 /* saa7113/7114/7118 VBI support are experimental */ - if (!saa711x_has_reg(state->ident,R_41_LCR_BASE)) + if (!saa711x_has_reg(state->ident, R_41_LCR_BASE)) return; #else @@ -1109,16 +1063,16 @@ static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo /* write the lcr registers */ for (i = 2; i <= 23; i++) { - saa711x_write(client, i - 2 + R_41_LCR_BASE, lcr[i]); + saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]); } /* enable/disable raw VBI capturing */ - saa711x_writeregs(client, fmt == NULL ? + saa711x_writeregs(sd, fmt == NULL ? saa7115_cfg_vbi_on : saa7115_cfg_vbi_off); } -static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { static u16 lcr2vbi[] = { 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ @@ -1134,10 +1088,10 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt return -EINVAL; memset(sliced, 0, sizeof(*sliced)); /* done if using raw VBI */ - if (saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10) + if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10) return 0; for (i = 2; i <= 23; i++) { - u8 v = saa711x_read(client, i - 2 + R_41_LCR_BASE); + u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE); sliced->service_lines[0][i] = lcr2vbi[v >> 4]; sliced->service_lines[1][i] = lcr2vbi[v & 0xf]; @@ -1147,20 +1101,20 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt return 0; } -static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int saa711x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { - saa711x_set_lcr(client, &fmt->fmt.sliced); + saa711x_set_lcr(sd, &fmt->fmt.sliced); return 0; } if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - saa711x_set_lcr(client, NULL); + saa711x_set_lcr(sd, NULL); return 0; } if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - return saa711x_set_size(client,fmt->fmt.pix.width,fmt->fmt.pix.height); + return saa711x_set_size(sd, fmt->fmt.pix.width, fmt->fmt.pix.height); } /* Decode the sliced VBI data stream as created by the saa7115. @@ -1169,13 +1123,12 @@ static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt The current implementation uses SAV/EAV codes and not the ancillary data headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV code. */ -static void saa711x_decode_vbi_line(struct i2c_client *client, - struct v4l2_decode_vbi_line *vbi) +static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) { + struct saa711x_state *state = to_state(sd); static const char vbi_no_data_pattern[] = { 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0 }; - struct saa711x_state *state = i2c_get_clientdata(client); u8 *p = vbi->p; u32 wss; int id1, id2; /* the ID1 and ID2 bytes from the internal header */ @@ -1202,7 +1155,7 @@ static void saa711x_decode_vbi_line(struct i2c_client *client, /* If the VBI slicer does not detect any signal it will fill up the payload buffer with 0xa0 bytes. */ if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern))) - return; + return 0; /* decode payloads */ switch (id2) { @@ -1211,275 +1164,352 @@ static void saa711x_decode_vbi_line(struct i2c_client *client, break; case 4: if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1])) - return; + return 0; vbi->type = V4L2_SLICED_CAPTION_525; break; case 5: wss = saa711x_decode_wss(p); if (wss == -1) - return; + return 0; p[0] = wss & 0xff; p[1] = wss >> 8; vbi->type = V4L2_SLICED_WSS_625; break; case 7: if (saa711x_decode_vps(p, p) != 0) - return; + return 0; vbi->type = V4L2_SLICED_VPS; break; default: - return; + break; } + return 0; } /* ============ SAA7115 AUDIO settings (end) ============= */ -static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *arg) +static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); + int status; - /* ioctls to allow direct access to the saa7115 registers for testing */ - switch (cmd) { - case VIDIOC_S_FMT: - return saa711x_set_v4lfmt(client, (struct v4l2_format *)arg); + if (state->radio) + return 0; + status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - case VIDIOC_G_FMT: - return saa711x_get_v4lfmt(client, (struct v4l2_format *)arg); + v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status); + vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; + return 0; +} - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - return saa711x_set_audio_clock_freq(client, *(u32 *)arg); +static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill_std(qc); + default: + return -EINVAL; + } +} - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = arg; - int status; +static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa711x_state *state = to_state(sd); - if (state->radio) - break; - status = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC); + state->radio = 0; + saa711x_set_v4lstd(sd, std); + return 0; +} - v4l_dbg(1, debug, client, "status: 0x%02x\n", status); - vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; - break; - } +static int saa711x_s_radio(struct v4l2_subdev *sd) +{ + struct saa711x_state *state = to_state(sd); - case VIDIOC_LOG_STATUS: - saa711x_log_status(client); - break; + state->radio = 1; + return 0; +} - case VIDIOC_G_CTRL: - return saa711x_get_v4lctrl(client, (struct v4l2_control *)arg); +static int saa711x_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct saa711x_state *state = to_state(sd); + u32 input = route->input; + u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0; + + v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", route->input, route->output); + /* saa7111/3 does not have these inputs */ + if ((state->ident == V4L2_IDENT_SAA7113 || + state->ident == V4L2_IDENT_SAA7111) && + (route->input == SAA7115_COMPOSITE4 || + route->input == SAA7115_COMPOSITE5)) { + return -EINVAL; + } + if (route->input > SAA7115_SVIDEO3) + return -EINVAL; + if (route->output > SAA7115_IPORT_ON) + return -EINVAL; + if (state->input == route->input && state->output == route->output) + return 0; + v4l2_dbg(1, debug, sd, "now setting %s input %s output\n", + (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", + (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); + state->input = route->input; + + /* saa7111 has slightly different input numbering */ + if (state->ident == V4L2_IDENT_SAA7111) { + if (input >= SAA7115_COMPOSITE4) + input -= 2; + /* saa7111 specific */ + saa711x_write(sd, R_10_CHROMA_CNTL_2, + (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) | + ((route->output & 0xc0) ^ 0x40)); + saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL, + (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | + ((route->output & 2) ? 0x0a : 0)); + } - case VIDIOC_S_CTRL: - return saa711x_set_v4lctrl(client, (struct v4l2_control *)arg); + /* select mode */ + saa711x_write(sd, R_02_INPUT_CNTL_1, + (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) | + input); - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; + /* bypass chrominance trap for S-Video modes */ + saa711x_write(sd, R_09_LUMA_CNTL, + (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) | + (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill_std(qc); - default: - return -EINVAL; - } + state->output = route->output; + if (state->ident == V4L2_IDENT_SAA7114 || + state->ident == V4L2_IDENT_SAA7115) { + saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK, + (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) | + (state->output & 0x01)); } + return 0; +} - case VIDIOC_G_STD: - *(v4l2_std_id *)arg = saa711x_get_v4lstd(client); - break; +static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val) +{ + struct saa711x_state *state = to_state(sd); - case VIDIOC_S_STD: - state->radio = 0; - saa711x_set_v4lstd(client, *(v4l2_std_id *)arg); - break; + if (state->ident != V4L2_IDENT_SAA7111) + return -EINVAL; + saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) | + (val ? 0x80 : 0)); + return 0; +} - case AUDC_SET_RADIO: - state->radio = 1; - break; +static int saa711x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa711x_state *state = to_state(sd); - case VIDIOC_INT_G_VIDEO_ROUTING: - { - struct v4l2_routing *route = arg; + v4l2_dbg(1, debug, sd, "%s output\n", + enable ? "enable" : "disable"); - route->input = state->input; - route->output = state->output; - break; + if (state->enable != enable) { + state->enable = enable; + saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, + state->enable); } + return 0; +} - case VIDIOC_INT_S_VIDEO_ROUTING: - { - struct v4l2_routing *route = arg; - u32 input = route->input; - u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0; - - v4l_dbg(1, debug, client, "decoder set input %d output %d\n", route->input, route->output); - /* saa7111/3 does not have these inputs */ - if ((state->ident == V4L2_IDENT_SAA7113 || - state->ident == V4L2_IDENT_SAA7111) && - (route->input == SAA7115_COMPOSITE4 || - route->input == SAA7115_COMPOSITE5)) { - return -EINVAL; - } - if (route->input > SAA7115_SVIDEO3) - return -EINVAL; - if (route->output > SAA7115_IPORT_ON) - return -EINVAL; - if (state->input == route->input && state->output == route->output) - break; - v4l_dbg(1, debug, client, "now setting %s input %s output\n", - (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); - state->input = route->input; - - /* saa7111 has slightly different input numbering */ - if (state->ident == V4L2_IDENT_SAA7111) { - if (input >= SAA7115_COMPOSITE4) - input -= 2; - /* saa7111 specific */ - saa711x_write(client, R_10_CHROMA_CNTL_2, - (saa711x_read(client, R_10_CHROMA_CNTL_2) & 0x3f) | - ((route->output & 0xc0) ^ 0x40)); - saa711x_write(client, R_13_RT_X_PORT_OUT_CNTL, - (saa711x_read(client, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | - ((route->output & 2) ? 0x0a : 0)); - } +static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, struct v4l2_crystal_freq *freq) +{ + struct saa711x_state *state = to_state(sd); - /* select mode */ - saa711x_write(client, R_02_INPUT_CNTL_1, - (saa711x_read(client, R_02_INPUT_CNTL_1) & mask) | - input); + if (freq->freq != SAA7115_FREQ_32_11_MHZ && + freq->freq != SAA7115_FREQ_24_576_MHZ) + return -EINVAL; + state->crystal_freq = freq->freq; + state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; + state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; + state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; + saa711x_s_clock_freq(sd, state->audclk_freq); + return 0; +} - /* bypass chrominance trap for S-Video modes */ - saa711x_write(client, R_09_LUMA_CNTL, - (saa711x_read(client, R_09_LUMA_CNTL) & 0x7f) | - (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); +static int saa711x_reset(struct v4l2_subdev *sd, u32 val) +{ + v4l2_dbg(1, debug, sd, "decoder RESET\n"); + saa711x_writeregs(sd, saa7115_cfg_reset_scaler); + return 0; +} - state->output = route->output; - if (state->ident == V4L2_IDENT_SAA7114 || - state->ident == V4L2_IDENT_SAA7115) { - saa711x_write(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK, - (saa711x_read(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) | - (state->output & 0x01)); +static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data) +{ + /* Note: the internal field ID is inverted for NTSC, + so data->field 0 maps to the saa7115 even field, + whereas for PAL it maps to the saa7115 odd field. */ + switch (data->id) { + case V4L2_SLICED_WSS_625: + if (saa711x_read(sd, 0x6b) & 0xc0) + return -EIO; + data->data[0] = saa711x_read(sd, 0x6c); + data->data[1] = saa711x_read(sd, 0x6d); + return 0; + case V4L2_SLICED_CAPTION_525: + if (data->field == 0) { + /* CC */ + if (saa711x_read(sd, 0x66) & 0x30) + return -EIO; + data->data[0] = saa711x_read(sd, 0x69); + data->data[1] = saa711x_read(sd, 0x6a); + return 0; } - break; + /* XDS */ + if (saa711x_read(sd, 0x66) & 0xc0) + return -EIO; + data->data[0] = saa711x_read(sd, 0x67); + data->data[1] = saa711x_read(sd, 0x68); + return 0; + default: + return -EINVAL; } +} - case VIDIOC_STREAMON: - case VIDIOC_STREAMOFF: - v4l_dbg(1, debug, client, "%s output\n", - (cmd == VIDIOC_STREAMON) ? "enable" : "disable"); - - if (state->enable != (cmd == VIDIOC_STREAMON)) { - state->enable = (cmd == VIDIOC_STREAMON); - saa711x_write(client, - R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, - state->enable); - } - break; +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_S_CRYSTAL_FREQ: - { - struct v4l2_crystal_freq *freq = arg; + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa711x_read(sd, reg->reg & 0xff); + return 0; +} - if (freq->freq != SAA7115_FREQ_32_11_MHZ && - freq->freq != SAA7115_FREQ_24_576_MHZ) - return -EINVAL; - state->crystal_freq = freq->freq; - state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; - state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; - state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; - saa711x_set_audio_clock_freq(client, state->audclk_freq); - break; - } +static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_DECODE_VBI_LINE: - saa711x_decode_vbi_line(client, arg); - break; + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif - case VIDIOC_INT_RESET: - v4l_dbg(1, debug, client, "decoder RESET\n"); - saa711x_writeregs(client, saa7115_cfg_reset_scaler); - break; +static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct saa711x_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_S_GPIO: - if (state->ident != V4L2_IDENT_SAA7111) - return -EINVAL; - saa711x_write(client, 0x11, (saa711x_read(client, 0x11) & 0x7f) | - (*(u32 *)arg ? 0x80 : 0)); - break; + return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); +} - case VIDIOC_INT_G_VBI_DATA: - { - struct v4l2_sliced_vbi_data *data = arg; +static int saa711x_log_status(struct v4l2_subdev *sd) +{ + struct saa711x_state *state = to_state(sd); + int reg1e, reg1f; + int signalOk; + int vcr; - /* Note: the internal field ID is inverted for NTSC, - so data->field 0 maps to the saa7115 even field, - whereas for PAL it maps to the saa7115 odd field. */ - switch (data->id) { - case V4L2_SLICED_WSS_625: - if (saa711x_read(client, 0x6b) & 0xc0) - return -EIO; - data->data[0] = saa711x_read(client, 0x6c); - data->data[1] = saa711x_read(client, 0x6d); - return 0; - case V4L2_SLICED_CAPTION_525: - if (data->field == 0) { - /* CC */ - if (saa711x_read(client, 0x66) & 0x30) - return -EIO; - data->data[0] = saa711x_read(client, 0x69); - data->data[1] = saa711x_read(client, 0x6a); - return 0; - } - /* XDS */ - if (saa711x_read(client, 0x66) & 0xc0) - return -EIO; - data->data[0] = saa711x_read(client, 0x67); - data->data[1] = saa711x_read(client, 0x68); - return 0; - default: - return -EINVAL; - } - break; + v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq); + if (state->ident != V4L2_IDENT_SAA7115) { + /* status for the saa7114 */ + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + signalOk = (reg1f & 0xc1) == 0x81; + v4l2_info(sd, "Video signal: %s\n", signalOk ? "ok" : "bad"); + v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); + return 0; } -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; + /* status for the saa7115 */ + reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = saa711x_read(client, reg->reg & 0xff); - else - saa711x_write(client, reg->reg & 0xff, reg->val & 0xff); - break; - } -#endif + signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; + vcr = !(reg1f & 0x10); - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0); + if (state->input >= 6) + v4l2_info(sd, "Input: S-Video %d\n", state->input - 6); + else + v4l2_info(sd, "Input: Composite %d\n", state->input); + v4l2_info(sd, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); + v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); + switch (reg1e & 0x03) { + case 1: + v4l2_info(sd, "Detected format: NTSC\n"); + break; + case 2: + v4l2_info(sd, "Detected format: PAL\n"); + break; + case 3: + v4l2_info(sd, "Detected format: SECAM\n"); + break; default: - return -EINVAL; + v4l2_info(sd, "Detected format: BW/No color\n"); + break; } - + v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height); return 0; } +static int saa711x_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa711x_core_ops = { + .log_status = saa711x_log_status, + .g_chip_ident = saa711x_g_chip_ident, + .g_ctrl = saa711x_g_ctrl, + .s_ctrl = saa711x_s_ctrl, + .queryctrl = saa711x_queryctrl, + .reset = saa711x_reset, + .s_gpio = saa711x_s_gpio, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa711x_g_register, + .s_register = saa711x_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = { + .s_std = saa711x_s_std, + .s_radio = saa711x_s_radio, + .g_tuner = saa711x_g_tuner, +}; + +static const struct v4l2_subdev_audio_ops saa711x_audio_ops = { + .s_clock_freq = saa711x_s_clock_freq, +}; + +static const struct v4l2_subdev_video_ops saa711x_video_ops = { + .s_routing = saa711x_s_routing, + .s_crystal_freq = saa711x_s_crystal_freq, + .g_fmt = saa711x_g_fmt, + .s_fmt = saa711x_s_fmt, + .g_vbi_data = saa711x_g_vbi_data, + .decode_vbi_line = saa711x_decode_vbi_line, + .s_stream = saa711x_s_stream, +}; + +static const struct v4l2_subdev_ops saa711x_ops = { + .core = &saa711x_core_ops, + .tuner = &saa711x_tuner_ops, + .audio = &saa711x_audio_ops, + .video = &saa711x_video_ops, +}; + /* ----------------------------------------------------------------------- */ -static int saa7115_probe(struct i2c_client *client, +static int saa711x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct saa711x_state *state; + struct v4l2_subdev *sd; int i; char name[17]; char chip_id; @@ -1490,8 +1520,8 @@ static int saa7115_probe(struct i2c_client *client, return -EIO; for (i = 0; i < 0x0f; i++) { - saa711x_write(client, 0, i); - name[i] = (saa711x_read(client, 0) & 0x0f) + '0'; + i2c_smbus_write_byte_data(client, 0, i); + name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0'; if (name[i] > '9') name[i] += 'a' - '9' - 1; } @@ -1518,7 +1548,8 @@ static int saa7115_probe(struct i2c_client *client, state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; - i2c_set_clientdata(client, state); + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &saa711x_ops); state->input = -1; state->output = SAA7115_IPORT_ON; state->enable = 1; @@ -1545,41 +1576,45 @@ static int saa7115_probe(struct i2c_client *client, break; default: state->ident = V4L2_IDENT_SAA7111; - v4l_info(client, "WARNING: Chip is not known - Falling back to saa7111\n"); + v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n"); } state->audclk_freq = 48000; - v4l_dbg(1, debug, client, "writing init values\n"); + v4l2_dbg(1, debug, sd, "writing init values\n"); /* init to 60hz/48khz */ state->crystal_freq = SAA7115_FREQ_24_576_MHZ; switch (state->ident) { case V4L2_IDENT_SAA7111: - saa711x_writeregs(client, saa7111_init); + saa711x_writeregs(sd, saa7111_init); break; case V4L2_IDENT_SAA7113: - saa711x_writeregs(client, saa7113_init); + saa711x_writeregs(sd, saa7113_init); break; default: state->crystal_freq = SAA7115_FREQ_32_11_MHZ; - saa711x_writeregs(client, saa7115_init_auto_input); + saa711x_writeregs(sd, saa7115_init_auto_input); } if (state->ident != V4L2_IDENT_SAA7111) - saa711x_writeregs(client, saa7115_init_misc); - saa711x_set_v4lstd(client, V4L2_STD_NTSC); + saa711x_writeregs(sd, saa7115_init_misc); + saa711x_set_v4lstd(sd, V4L2_STD_NTSC); - v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n", - saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC), saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC)); + v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n", + saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC), + saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC)); return 0; } /* ----------------------------------------------------------------------- */ -static int saa7115_remove(struct i2c_client *client) +static int saa711x_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } @@ -1597,9 +1632,9 @@ MODULE_DEVICE_TABLE(i2c, saa7115_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa7115", .driverid = I2C_DRIVERID_SAA711X, - .command = saa7115_command, - .probe = saa7115_probe, - .remove = saa7115_remove, + .command = saa711x_command, + .probe = saa711x_probe, + .remove = saa711x_remove, .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, .id_table = saa7115_id, }; diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index cc02fb1..bfc8565 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -53,7 +53,7 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> #include <media/saa7127.h> @@ -231,6 +231,7 @@ static struct i2c_reg_value saa7127_init_config_50hz[] = { */ struct saa7127_state { + struct v4l2_subdev sd; v4l2_std_id std; u32 ident; enum saa7127_input_type input_type; @@ -250,6 +251,11 @@ struct saa7127_state { u8 reg_61; }; +static inline struct saa7127_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7127_state, sd); +} + static const char * const output_strs[] = { "S-Video + Composite", @@ -281,32 +287,35 @@ static const char * const wss_strs[] = { /* ----------------------------------------------------------------------- */ -static int saa7127_read(struct i2c_client *client, u8 reg) +static int saa7127_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte_data(client, reg); } /* ----------------------------------------------------------------------- */ -static int saa7127_write(struct i2c_client *client, u8 reg, u8 val) +static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int i; for (i = 0; i < 3; i++) { if (i2c_smbus_write_byte_data(client, reg, val) == 0) return 0; } - v4l_err(client, "I2C Write Problem\n"); + v4l2_err(sd, "I2C Write Problem\n"); return -1; } /* ----------------------------------------------------------------------- */ -static int saa7127_write_inittab(struct i2c_client *client, +static int saa7127_write_inittab(struct v4l2_subdev *sd, const struct i2c_reg_value *regs) { while (regs->reg != 0) { - saa7127_write(client, regs->reg, regs->value); + saa7127_write(sd, regs->reg, regs->value); regs++; } return 0; @@ -314,16 +323,16 @@ static int saa7127_write_inittab(struct i2c_client *client, /* ----------------------------------------------------------------------- */ -static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); int enable = (data->line != 0); if (enable && (data->field != 0 || data->line != 16)) return -EINVAL; if (state->vps_enable != enable) { - v4l_dbg(1, debug, client, "Turn VPS Signal %s\n", enable ? "on" : "off"); - saa7127_write(client, 0x54, enable << 7); + v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n", enable ? "on" : "off"); + saa7127_write(sd, 0x54, enable << 7); state->vps_enable = enable; } if (!enable) @@ -334,91 +343,91 @@ static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_dat state->vps_data[2] = data->data[9]; state->vps_data[3] = data->data[10]; state->vps_data[4] = data->data[11]; - v4l_dbg(1, debug, client, "Set VPS data %02x %02x %02x %02x %02x\n", + v4l2_dbg(1, debug, sd, "Set VPS data %02x %02x %02x %02x %02x\n", state->vps_data[0], state->vps_data[1], state->vps_data[2], state->vps_data[3], state->vps_data[4]); - saa7127_write(client, 0x55, state->vps_data[0]); - saa7127_write(client, 0x56, state->vps_data[1]); - saa7127_write(client, 0x57, state->vps_data[2]); - saa7127_write(client, 0x58, state->vps_data[3]); - saa7127_write(client, 0x59, state->vps_data[4]); + saa7127_write(sd, 0x55, state->vps_data[0]); + saa7127_write(sd, 0x56, state->vps_data[1]); + saa7127_write(sd, 0x57, state->vps_data[2]); + saa7127_write(sd, 0x58, state->vps_data[3]); + saa7127_write(sd, 0x59, state->vps_data[4]); return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_cc(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); u16 cc = data->data[1] << 8 | data->data[0]; int enable = (data->line != 0); if (enable && (data->field != 0 || data->line != 21)) return -EINVAL; if (state->cc_enable != enable) { - v4l_dbg(1, debug, client, + v4l2_dbg(1, debug, sd, "Turn CC %s\n", enable ? "on" : "off"); - saa7127_write(client, SAA7127_REG_CLOSED_CAPTION, + saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, (state->xds_enable << 7) | (enable << 6) | 0x11); state->cc_enable = enable; } if (!enable) return 0; - v4l_dbg(2, debug, client, "CC data: %04x\n", cc); - saa7127_write(client, SAA7127_REG_LINE_21_ODD_0, cc & 0xff); - saa7127_write(client, SAA7127_REG_LINE_21_ODD_1, cc >> 8); + v4l2_dbg(2, debug, sd, "CC data: %04x\n", cc); + saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, cc & 0xff); + saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, cc >> 8); state->cc_data = cc; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_xds(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); u16 xds = data->data[1] << 8 | data->data[0]; int enable = (data->line != 0); if (enable && (data->field != 1 || data->line != 21)) return -EINVAL; if (state->xds_enable != enable) { - v4l_dbg(1, debug, client, "Turn XDS %s\n", enable ? "on" : "off"); - saa7127_write(client, SAA7127_REG_CLOSED_CAPTION, + v4l2_dbg(1, debug, sd, "Turn XDS %s\n", enable ? "on" : "off"); + saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, (enable << 7) | (state->cc_enable << 6) | 0x11); state->xds_enable = enable; } if (!enable) return 0; - v4l_dbg(2, debug, client, "XDS data: %04x\n", xds); - saa7127_write(client, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff); - saa7127_write(client, SAA7127_REG_LINE_21_EVEN_1, xds >> 8); + v4l2_dbg(2, debug, sd, "XDS data: %04x\n", xds); + saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff); + saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, xds >> 8); state->xds_data = xds; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); int enable = (data->line != 0); if (enable && (data->field != 0 || data->line != 23)) return -EINVAL; if (state->wss_enable != enable) { - v4l_dbg(1, debug, client, "Turn WSS %s\n", enable ? "on" : "off"); - saa7127_write(client, 0x27, enable << 7); + v4l2_dbg(1, debug, sd, "Turn WSS %s\n", enable ? "on" : "off"); + saa7127_write(sd, 0x27, enable << 7); state->wss_enable = enable; } if (!enable) return 0; - saa7127_write(client, 0x26, data->data[0]); - saa7127_write(client, 0x27, 0x80 | (data->data[1] & 0x3f)); - v4l_dbg(1, debug, client, + saa7127_write(sd, 0x26, data->data[0]); + saa7127_write(sd, 0x27, 0x80 | (data->data[1] & 0x3f)); + v4l2_dbg(1, debug, sd, "WSS mode: %s\n", wss_strs[data->data[0] & 0xf]); state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0]; return 0; @@ -426,18 +435,18 @@ static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_dat /* ----------------------------------------------------------------------- */ -static int saa7127_set_video_enable(struct i2c_client *client, int enable) +static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); if (enable) { - v4l_dbg(1, debug, client, "Enable Video Output\n"); - saa7127_write(client, 0x2d, state->reg_2d); - saa7127_write(client, 0x61, state->reg_61); + v4l2_dbg(1, debug, sd, "Enable Video Output\n"); + saa7127_write(sd, 0x2d, state->reg_2d); + saa7127_write(sd, 0x61, state->reg_61); } else { - v4l_dbg(1, debug, client, "Disable Video Output\n"); - saa7127_write(client, 0x2d, (state->reg_2d & 0xf0)); - saa7127_write(client, 0x61, (state->reg_61 | 0xc0)); + v4l2_dbg(1, debug, sd, "Disable Video Output\n"); + saa7127_write(sd, 0x2d, (state->reg_2d & 0xf0)); + saa7127_write(sd, 0x61, (state->reg_61 | 0xc0)); } state->video_enable = enable; return 0; @@ -445,32 +454,32 @@ static int saa7127_set_video_enable(struct i2c_client *client, int enable) /* ----------------------------------------------------------------------- */ -static int saa7127_set_std(struct i2c_client *client, v4l2_std_id std) +static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); const struct i2c_reg_value *inittab; if (std & V4L2_STD_525_60) { - v4l_dbg(1, debug, client, "Selecting 60 Hz video Standard\n"); + v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n"); inittab = saa7127_init_config_60hz; state->reg_61 = SAA7127_60HZ_DAC_CONTROL; } else { - v4l_dbg(1, debug, client, "Selecting 50 Hz video Standard\n"); + v4l2_dbg(1, debug, sd, "Selecting 50 Hz video Standard\n"); inittab = saa7127_init_config_50hz; state->reg_61 = SAA7127_50HZ_DAC_CONTROL; } /* Write Table */ - saa7127_write_inittab(client, inittab); + saa7127_write_inittab(sd, inittab); state->std = std; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_output_type(struct i2c_client *client, int output) +static int saa7127_set_output_type(struct v4l2_subdev *sd, int output) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); switch (output) { case SAA7127_OUTPUT_TYPE_RGB: @@ -506,165 +515,195 @@ static int saa7127_set_output_type(struct i2c_client *client, int output) default: return -EINVAL; } - v4l_dbg(1, debug, client, + v4l2_dbg(1, debug, sd, "Selecting %s output type\n", output_strs[output]); /* Configure Encoder */ - saa7127_write(client, 0x2d, state->reg_2d); - saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb); + saa7127_write(sd, 0x2d, state->reg_2d); + saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); state->output_type = output; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_input_type(struct i2c_client *client, int input) +static int saa7127_set_input_type(struct v4l2_subdev *sd, int input) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); switch (input) { case SAA7127_INPUT_TYPE_NORMAL: /* avia */ - v4l_dbg(1, debug, client, "Selecting Normal Encoder Input\n"); + v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n"); state->reg_3a_cb = 0; break; case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */ - v4l_dbg(1, debug, client, "Selecting Color Bar generator\n"); + v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n"); state->reg_3a_cb = 0x80; break; default: return -EINVAL; } - saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb); + saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); state->input_type = input; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_command(struct i2c_client *client, - unsigned int cmd, void *arg) +static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) { - struct saa7127_state *state = i2c_get_clientdata(client); - struct v4l2_format *fmt = arg; - struct v4l2_routing *route = arg; - - switch (cmd) { - case VIDIOC_INT_S_STD_OUTPUT: - if (state->std == *(v4l2_std_id *)arg) - break; - return saa7127_set_std(client, *(v4l2_std_id *)arg); - - case VIDIOC_INT_G_STD_OUTPUT: - *(v4l2_std_id *)arg = state->std; - break; + struct saa7127_state *state = to_state(sd); - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = state->input_type; - route->output = state->output_type; - break; + if (state->std == std) + return 0; + return saa7127_set_std(sd, std); +} - case VIDIOC_INT_S_VIDEO_ROUTING: - { - int rc = 0; +static int saa7127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct saa7127_state *state = to_state(sd); + int rc = 0; + + if (state->input_type != route->input) + rc = saa7127_set_input_type(sd, route->input); + if (rc == 0 && state->output_type != route->output) + rc = saa7127_set_output_type(sd, route->output); + return rc; +} - if (state->input_type != route->input) - rc = saa7127_set_input_type(client, route->input); - if (rc == 0 && state->output_type != route->output) - rc = saa7127_set_output_type(client, route->output); - return rc; - } +static int saa7127_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa7127_state *state = to_state(sd); - case VIDIOC_STREAMON: - case VIDIOC_STREAMOFF: - if (state->video_enable == (cmd == VIDIOC_STREAMON)) - break; - return saa7127_set_video_enable(client, cmd == VIDIOC_STREAMON); - - case VIDIOC_G_FMT: - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - - memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced)); - if (state->vps_enable) - fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS; - if (state->wss_enable) - fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; - if (state->cc_enable) { - fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525; - fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525; - } - fmt->fmt.sliced.service_set = - (state->vps_enable ? V4L2_SLICED_VPS : 0) | - (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | - (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); - break; + if (state->video_enable == enable) + return 0; + return saa7127_set_video_enable(sd, enable); +} - case VIDIOC_LOG_STATUS: - v4l_info(client, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz"); - v4l_info(client, "Input: %s\n", state->input_type ? "color bars" : "normal"); - v4l_info(client, "Output: %s\n", state->video_enable ? - output_strs[state->output_type] : "disabled"); - v4l_info(client, "WSS: %s\n", state->wss_enable ? - wss_strs[state->wss_mode] : "disabled"); - v4l_info(client, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled"); - v4l_info(client, "CC: %s\n", state->cc_enable ? "enabled" : "disabled"); - break; +static int saa7127_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct saa7127_state *state = to_state(sd); -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = saa7127_read(client, reg->reg & 0xff); - else - saa7127_write(client, reg->reg & 0xff, reg->val & 0xff); - break; - } -#endif + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; - case VIDIOC_INT_S_VBI_DATA: - { - struct v4l2_sliced_vbi_data *data = arg; - - switch (data->id) { - case V4L2_SLICED_WSS_625: - return saa7127_set_wss(client, data); - case V4L2_SLICED_VPS: - return saa7127_set_vps(client, data); - case V4L2_SLICED_CAPTION_525: - if (data->field == 0) - return saa7127_set_cc(client, data); - return saa7127_set_xds(client, data); - default: - return -EINVAL; - } - break; + memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced)); + if (state->vps_enable) + fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS; + if (state->wss_enable) + fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; + if (state->cc_enable) { + fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525; + fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525; } + fmt->fmt.sliced.service_set = + (state->vps_enable ? V4L2_SLICED_VPS : 0) | + (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | + (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); + return 0; +} - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0); - +static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) +{ + switch (data->id) { + case V4L2_SLICED_WSS_625: + return saa7127_set_wss(sd, data); + case V4L2_SLICED_VPS: + return saa7127_set_vps(sd, data); + case V4L2_SLICED_CAPTION_525: + if (data->field == 0) + return saa7127_set_cc(sd, data); + return saa7127_set_xds(sd, data); default: return -EINVAL; } return 0; } +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa7127_read(sd, reg->reg & 0xff); + return 0; +} + +static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct saa7127_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); +} + +static int saa7127_log_status(struct v4l2_subdev *sd) +{ + struct saa7127_state *state = to_state(sd); + + v4l2_info(sd, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz"); + v4l2_info(sd, "Input: %s\n", state->input_type ? "color bars" : "normal"); + v4l2_info(sd, "Output: %s\n", state->video_enable ? + output_strs[state->output_type] : "disabled"); + v4l2_info(sd, "WSS: %s\n", state->wss_enable ? + wss_strs[state->wss_mode] : "disabled"); + v4l2_info(sd, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled"); + v4l2_info(sd, "CC: %s\n", state->cc_enable ? "enabled" : "disabled"); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa7127_core_ops = { + .log_status = saa7127_log_status, + .g_chip_ident = saa7127_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa7127_g_register, + .s_register = saa7127_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops saa7127_video_ops = { + .s_vbi_data = saa7127_s_vbi_data, + .g_fmt = saa7127_g_fmt, + .s_std_output = saa7127_s_std_output, + .s_routing = saa7127_s_routing, + .s_stream = saa7127_s_stream, +}; + +static const struct v4l2_subdev_ops saa7127_ops = { + .core = &saa7127_core_ops, + .video = &saa7127_video_ops, +}; + /* ----------------------------------------------------------------------- */ static int saa7127_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct saa7127_state *state; + struct v4l2_subdev *sd; struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */ /* Check if the adapter supports the needed features */ @@ -674,40 +713,42 @@ static int saa7127_probe(struct i2c_client *client, v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n", client->addr << 1); + state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &saa7127_ops); + /* First test register 0: Bits 5-7 are a version ID (should be 0), and bit 2 should also be 0. This is rather general, so the second test is more specific and looks at the 'ending point of burst in clock cycles' which is 0x1d after a reset and not expected to ever change. */ - if ((saa7127_read(client, 0) & 0xe4) != 0 || - (saa7127_read(client, 0x29) & 0x3f) != 0x1d) { - v4l_dbg(1, debug, client, "saa7127 not found\n"); + if ((saa7127_read(sd, 0) & 0xe4) != 0 || + (saa7127_read(sd, 0x29) & 0x3f) != 0x1d) { + v4l2_dbg(1, debug, sd, "saa7127 not found\n"); + kfree(state); return -ENODEV; } - state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL); - - if (state == NULL) - return -ENOMEM; - - i2c_set_clientdata(client, state); /* Configure Encoder */ - v4l_dbg(1, debug, client, "Configuring encoder\n"); - saa7127_write_inittab(client, saa7127_init_config_common); - saa7127_set_std(client, V4L2_STD_NTSC); - saa7127_set_output_type(client, SAA7127_OUTPUT_TYPE_BOTH); - saa7127_set_vps(client, &vbi); - saa7127_set_wss(client, &vbi); - saa7127_set_cc(client, &vbi); - saa7127_set_xds(client, &vbi); + v4l2_dbg(1, debug, sd, "Configuring encoder\n"); + saa7127_write_inittab(sd, saa7127_init_config_common); + saa7127_set_std(sd, V4L2_STD_NTSC); + saa7127_set_output_type(sd, SAA7127_OUTPUT_TYPE_BOTH); + saa7127_set_vps(sd, &vbi); + saa7127_set_wss(sd, &vbi); + saa7127_set_cc(sd, &vbi); + saa7127_set_xds(sd, &vbi); if (test_image == 1) /* The Encoder has an internal Colorbar generator */ /* This can be used for debugging */ - saa7127_set_input_type(client, SAA7127_INPUT_TYPE_TEST_IMAGE); + saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_TEST_IMAGE); else - saa7127_set_input_type(client, SAA7127_INPUT_TYPE_NORMAL); - saa7127_set_video_enable(client, 1); + saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL); + saa7127_set_video_enable(sd, 1); if (id->driver_data) { /* Chip type is already known */ state->ident = id->driver_data; @@ -715,10 +756,10 @@ static int saa7127_probe(struct i2c_client *client, int read_result; /* Detect if it's an saa7129 */ - read_result = saa7127_read(client, SAA7129_REG_FADE_KEY_COL2); - saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, 0xaa); - if (saa7127_read(client, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { - saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, + read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2); + saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, 0xaa); + if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { + saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, read_result); state->ident = V4L2_IDENT_SAA7129; strlcpy(client->name, "saa7129", I2C_NAME_SIZE); @@ -728,10 +769,10 @@ static int saa7127_probe(struct i2c_client *client, } } - v4l_info(client, "%s found @ 0x%x (%s)\n", client->name, + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, client->addr << 1, client->adapter->name); if (state->ident == V4L2_IDENT_SAA7129) - saa7127_write_inittab(client, saa7129_init_config_extra); + saa7127_write_inittab(sd, saa7129_init_config_extra); return 0; } @@ -739,9 +780,12 @@ static int saa7127_probe(struct i2c_client *client, static int saa7127_remove(struct i2c_client *client) { + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); /* Turn off TV output */ - saa7127_set_video_enable(client, 0); - kfree(i2c_get_clientdata(client)); + saa7127_set_video_enable(sd, 0); + kfree(to_state(sd)); return 0; } @@ -760,7 +804,6 @@ MODULE_DEVICE_TABLE(i2c, saa7127_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa7127", .driverid = I2C_DRIVERID_SAA7127, - .command = saa7127_command, .probe = saa7127_probe, .remove = saa7127_remove, .id_table = saa7127_id, diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index ddc5402..a2e3f67 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -4606,6 +4606,43 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200000, }, }, + [SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG] = { + .name = "Kworld Plus TV Analog Lite PCI", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_YMEC_TVF_5533MF, + .radio_type = TUNER_TEA5767, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x80000700, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + .gpio = 0x100, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x200, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x200, + } }, + .radio = { + .name = name_radio, + .vmux = 1, + .amux = LINE1, + .gpio = 0x100, + }, + .mute = { + .name = name_mute, + .vmux = 8, + .amux = 2, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -4736,6 +4773,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .driver_data = SAA7134_BOARD_MD7134, },{ .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x16be, /* CTX946 analog TV, HW mpeg, DVB-T */ + .subdevice = 0x5000, /* only analog TV and DVB-T for now */ + .driver_data = SAA7134_BOARD_MD7134, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7130, .subvendor = 0x1048, .subdevice = 0x226b, @@ -5653,6 +5696,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x4878, /* REV:1.02G */ .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1, }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x17de, + .subdevice = 0x7128, + .driver_data = SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG, + }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -5880,6 +5929,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_507_9FM: case SAA7134_BOARD_GENIUS_TVGO_A11MCE: case SAA7134_BOARD_REAL_ANGEL_220: + case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: @@ -6048,7 +6098,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev) struct v4l2_priv_tun_config xc2028_cfg; struct xc2028_ctrl ctl; - memset(&xc2028_cfg, 0, sizeof(ctl)); + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); memset(&ctl, 0, sizeof(ctl)); ctl.fname = XC2028_DEFAULT_FIRMWARE; diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 8c46115..d9a5652 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -954,20 +954,14 @@ static int dvb_init(struct saa7134_dev *dev) /* FIXME: add support for multi-frontend */ mutex_init(&dev->frontends.lock); INIT_LIST_HEAD(&dev->frontends.felist); - dev->frontends.active_fe_id = 0; printk(KERN_INFO "%s() allocating 1 frontend\n", __func__); - - if (videobuf_dvb_alloc_frontend(&dev->frontends, 1) == NULL) { + fe0 = videobuf_dvb_alloc_frontend(&dev->frontends, 1); + if (!fe0) { printk(KERN_ERR "%s() failed to alloc\n", __func__); return -ENOMEM; } - /* Get the first frontend */ - fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); - if (!fe0) - return -EINVAL; - /* init struct videobuf_dvb */ dev->ts.nr_bufs = 32; dev->ts.nr_packets = 32*4; @@ -1376,7 +1370,7 @@ static int dvb_init(struct saa7134_dev *dev) }; if (!fe0->dvb.frontend) - return -1; + goto dettach_frontend; fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg); if (!fe) { @@ -1388,7 +1382,7 @@ static int dvb_init(struct saa7134_dev *dev) if (NULL == fe0->dvb.frontend) { printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name); - return -1; + goto dettach_frontend; } /* define general-purpose callback pointer */ fe0->dvb.frontend->callback = saa7134_tuner_callback; @@ -1411,11 +1405,8 @@ static int dvb_init(struct saa7134_dev *dev) return ret; dettach_frontend: - if (fe0->dvb.frontend) - dvb_frontend_detach(fe0->dvb.frontend); - fe0->dvb.frontend = NULL; - - return -1; + videobuf_dvb_dealloc_frontends(&dev->frontends); + return -EINVAL; } static int dvb_fini(struct saa7134_dev *dev) @@ -1454,8 +1445,7 @@ static int dvb_fini(struct saa7134_dev *dev) } } } - if (fe0->dvb.frontend) - videobuf_dvb_unregister_bus(&dev->frontends); + videobuf_dvb_unregister_bus(&dev->frontends); return 0; } diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index c53fd5f..d2124f6 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -97,6 +97,15 @@ static int build_key(struct saa7134_dev *dev) dprintk("build_key gpio=0x%x mask=0x%x data=%d\n", gpio, ir->mask_keycode, data); + switch (dev->board) { + case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: + if (data == ir->mask_keycode) + ir_input_nokey(ir->dev, &ir->ir); + else + ir_input_keydown(ir->dev, &ir->ir, data, data); + return 0; + } + if (ir->polling) { if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { @@ -586,6 +595,11 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keyup = 0x4000; polling = 50; /* ms */ break; + case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: + ir_codes = ir_codes_kworld_plus_tv_analog; + mask_keycode = 0x7f; + polling = 40; /* ms */ + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c index c5d0b44..76b1640 100644 --- a/drivers/media/video/saa7134/saa7134-tvaudio.c +++ b/drivers/media/video/saa7134/saa7134-tvaudio.c @@ -159,7 +159,7 @@ static struct saa7134_tvaudio tvaudio[] = { .mode = TVAUDIO_FM_MONO, } }; -#define TVAUDIO (sizeof(tvaudio)/sizeof(struct saa7134_tvaudio)) +#define TVAUDIO ARRAY_SIZE(tvaudio) /* ------------------------------------------------------------------ */ diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 24096d6..f6c1fcc 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -275,8 +275,9 @@ struct saa7134_format { #define SAA7134_BOARD_REAL_ANGEL_220 150 #define SAA7134_BOARD_ADS_INSTANT_HDTV_PCI 151 #define SAA7134_BOARD_ASUSTeK_TIGER 152 +#define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153 -#define SAA7134_MAXBOARDS 8 +#define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 /* ----------------------------------------------------------- */ diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c index af60ede..9befca6 100644 --- a/drivers/media/video/saa717x.c +++ b/drivers/media/video/saa717x.c @@ -37,7 +37,7 @@ #include <linux/videodev2.h> #include <linux/i2c.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver"); @@ -54,6 +54,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); */ struct saa717x_state { + struct v4l2_subdev sd; v4l2_std_id std; int input; int enable; @@ -75,6 +76,11 @@ struct saa717x_state { int audio_input; }; +static inline struct saa717x_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa717x_state, sd); +} + /* ----------------------------------------------------------------------- */ /* for audio mode */ @@ -88,8 +94,9 @@ struct saa717x_state { /* ----------------------------------------------------------------------- */ -static int saa717x_write(struct i2c_client *client, u32 reg, u32 value) +static int saa717x_write(struct v4l2_subdev *sd, u32 reg, u32 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); struct i2c_adapter *adap = client->adapter; int fw_addr = reg == 0x454 || (reg >= 0x464 && reg <= 0x478) || reg == 0x480 || reg == 0x488; unsigned char mm1[6]; @@ -109,20 +116,21 @@ static int saa717x_write(struct i2c_client *client, u32 reg, u32 value) } msg.len = fw_addr ? 5 : 3; /* Long Registers have *only* three bytes! */ msg.buf = mm1; - v4l_dbg(2, debug, client, "wrote: reg 0x%03x=%08x\n", reg, value); + v4l2_dbg(2, debug, sd, "wrote: reg 0x%03x=%08x\n", reg, value); return i2c_transfer(adap, &msg, 1) == 1; } -static void saa717x_write_regs(struct i2c_client *client, u32 *data) +static void saa717x_write_regs(struct v4l2_subdev *sd, u32 *data) { while (data[0] || data[1]) { - saa717x_write(client, data[0], data[1]); + saa717x_write(sd, data[0], data[1]); data += 2; } } -static u32 saa717x_read(struct i2c_client *client, u32 reg) +static u32 saa717x_read(struct v4l2_subdev *sd, u32 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); struct i2c_adapter *adap = client->adapter; int fw_addr = (reg >= 0x404 && reg <= 0x4b8) || reg == 0x528; unsigned char mm1[2]; @@ -146,7 +154,7 @@ static u32 saa717x_read(struct i2c_client *client, u32 reg) else value = mm2[0] & 0xff; - v4l_dbg(2, debug, client, "read: reg 0x%03x=0x%08x\n", reg, value); + v4l2_dbg(2, debug, sd, "read: reg 0x%03x=0x%08x\n", reg, value); return value; } @@ -680,7 +688,7 @@ static u32 reg_set_audio_template[4][2] = /* Get detected audio flags (from saa7134 driver) */ -static void get_inf_dev_status(struct i2c_client *client, +static void get_inf_dev_status(struct v4l2_subdev *sd, int *dual_flag, int *stereo_flag) { u32 reg_data3; @@ -719,13 +727,13 @@ static void get_inf_dev_status(struct i2c_client *client, /* (demdec status: 0x528) */ /* read current status */ - reg_data3 = saa717x_read(client, 0x0528); + reg_data3 = saa717x_read(sd, 0x0528); - v4l_dbg(1, debug, client, "tvaudio thread status: 0x%x [%s%s%s]\n", + v4l2_dbg(1, debug, sd, "tvaudio thread status: 0x%x [%s%s%s]\n", reg_data3, stdres[reg_data3 & 0x1f], (reg_data3 & 0x000020) ? ",stereo" : "", (reg_data3 & 0x000040) ? ",dual" : ""); - v4l_dbg(1, debug, client, "detailed status: " + v4l2_dbg(1, debug, sd, "detailed status: " "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", (reg_data3 & 0x000080) ? " A2/EIAJ pilot tone " : "", (reg_data3 & 0x000100) ? " A2/EIAJ dual " : "", @@ -746,51 +754,51 @@ static void get_inf_dev_status(struct i2c_client *client, (reg_data3 & 0x100000) ? " init done " : ""); if (reg_data3 & 0x000220) { - v4l_dbg(1, debug, client, "ST!!!\n"); + v4l2_dbg(1, debug, sd, "ST!!!\n"); *stereo_flag = 1; } if (reg_data3 & 0x000140) { - v4l_dbg(1, debug, client, "DUAL!!!\n"); + v4l2_dbg(1, debug, sd, "DUAL!!!\n"); *dual_flag = 1; } } /* regs write to set audio mode */ -static void set_audio_mode(struct i2c_client *client, int audio_mode) +static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode) { - v4l_dbg(1, debug, client, "writing registers to set audio mode by set %d\n", + v4l2_dbg(1, debug, sd, "writing registers to set audio mode by set %d\n", audio_mode); - saa717x_write(client, 0x46c, reg_set_audio_template[audio_mode][0]); - saa717x_write(client, 0x470, reg_set_audio_template[audio_mode][1]); + saa717x_write(sd, 0x46c, reg_set_audio_template[audio_mode][0]); + saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]); } /* write regs to video output level (bright,contrast,hue,sat) */ -static void set_video_output_level_regs(struct i2c_client *client, +static void set_video_output_level_regs(struct v4l2_subdev *sd, struct saa717x_state *decoder) { /* brightness ffh (bright) - 80h (ITU level) - 00h (dark) */ - saa717x_write(client, 0x10a, decoder->bright); + saa717x_write(sd, 0x10a, decoder->bright); /* contrast 7fh (max: 1.984) - 44h (ITU) - 40h (1.0) - 0h (luminance off) 40: i2c dump c0h (-1.0 inverse chrominance) 80h (-2.0 inverse chrominance) */ - saa717x_write(client, 0x10b, decoder->contrast); + saa717x_write(sd, 0x10b, decoder->contrast); /* saturation? 7fh(max)-40h(ITU)-0h(color off) c0h (-1.0 inverse chrominance) 80h (-2.0 inverse chrominance) */ - saa717x_write(client, 0x10c, decoder->sat); + saa717x_write(sd, 0x10c, decoder->sat); /* color hue (phase) control 7fh (+178.6) - 0h (0 normal) - 80h (-180.0) */ - saa717x_write(client, 0x10d, decoder->hue); + saa717x_write(sd, 0x10d, decoder->hue); } /* write regs to set audio volume, bass and treble */ -static int set_audio_regs(struct i2c_client *client, +static int set_audio_regs(struct v4l2_subdev *sd, struct saa717x_state *decoder) { u8 mute = 0xac; /* -84 dB */ @@ -798,8 +806,8 @@ static int set_audio_regs(struct i2c_client *client, unsigned int work_l, work_r; /* set SIF analog I/O select */ - saa717x_write(client, 0x0594, decoder->audio_input); - v4l_dbg(1, debug, client, "set audio input %d\n", + saa717x_write(sd, 0x0594, decoder->audio_input); + v4l2_dbg(1, debug, sd, "set audio input %d\n", decoder->audio_input); /* normalize ( 65535 to 0 -> 24 to -40 (not -84)) */ @@ -819,17 +827,17 @@ static int set_audio_regs(struct i2c_client *client, ((u8)decoder->audio_main_vol_r << 8); } - saa717x_write(client, 0x480, val); + saa717x_write(sd, 0x480, val); /* bass and treble; go to another function */ /* set bass and treble */ val = decoder->audio_main_bass | (decoder->audio_main_treble << 8); - saa717x_write(client, 0x488, val); + saa717x_write(sd, 0x488, val); return 0; } /********** scaling staff ***********/ -static void set_h_prescale(struct i2c_client *client, +static void set_h_prescale(struct v4l2_subdev *sd, int task, int prescale) { static const struct { @@ -862,107 +870,101 @@ static void set_h_prescale(struct i2c_client *client, return; /* horizonal prescaling */ - saa717x_write(client, 0x60 + task_shift, vals[i].xpsc); + saa717x_write(sd, 0x60 + task_shift, vals[i].xpsc); /* accumulation length */ - saa717x_write(client, 0x61 + task_shift, vals[i].xacl); + saa717x_write(sd, 0x61 + task_shift, vals[i].xacl); /* level control */ - saa717x_write(client, 0x62 + task_shift, + saa717x_write(sd, 0x62 + task_shift, (vals[i].xc2_1 << 3) | vals[i].xdcg); /*FIR prefilter control */ - saa717x_write(client, 0x63 + task_shift, + saa717x_write(sd, 0x63 + task_shift, (vals[i].vpfy << 2) | vals[i].vpfy); } /********** scaling staff ***********/ -static void set_v_scale(struct i2c_client *client, int task, int yscale) +static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale) { int task_shift; task_shift = task * 0x40; /* Vertical scaling ratio (LOW) */ - saa717x_write(client, 0x70 + task_shift, yscale & 0xff); + saa717x_write(sd, 0x70 + task_shift, yscale & 0xff); /* Vertical scaling ratio (HI) */ - saa717x_write(client, 0x71 + task_shift, yscale >> 8); -} - -static int saa717x_set_audio_clock_freq(struct i2c_client *client, u32 freq) -{ - /* not yet implament, so saa717x_cfg_??hz_??_audio is not defined. */ - return 0; + saa717x_write(sd, 0x71 + task_shift, yscale >> 8); } -static int saa717x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa717x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct saa717x_state *state = i2c_get_clientdata(client); + struct saa717x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(client, "invalid brightness setting %d\n", ctrl->value); + v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value); return -ERANGE; } state->bright = ctrl->value; - v4l_dbg(1, debug, client, "bright:%d\n", state->bright); - saa717x_write(client, 0x10a, state->bright); + v4l2_dbg(1, debug, sd, "bright:%d\n", state->bright); + saa717x_write(sd, 0x10a, state->bright); break; case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid contrast setting %d\n", ctrl->value); + v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value); return -ERANGE; } state->contrast = ctrl->value; - v4l_dbg(1, debug, client, "contrast:%d\n", state->contrast); - saa717x_write(client, 0x10b, state->contrast); + v4l2_dbg(1, debug, sd, "contrast:%d\n", state->contrast); + saa717x_write(sd, 0x10b, state->contrast); break; case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid saturation setting %d\n", ctrl->value); + v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value); return -ERANGE; } state->sat = ctrl->value; - v4l_dbg(1, debug, client, "sat:%d\n", state->sat); - saa717x_write(client, 0x10c, state->sat); + v4l2_dbg(1, debug, sd, "sat:%d\n", state->sat); + saa717x_write(sd, 0x10c, state->sat); break; case V4L2_CID_HUE: if (ctrl->value < -127 || ctrl->value > 127) { - v4l_err(client, "invalid hue setting %d\n", ctrl->value); + v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } state->hue = ctrl->value; - v4l_dbg(1, debug, client, "hue:%d\n", state->hue); - saa717x_write(client, 0x10d, state->hue); + v4l2_dbg(1, debug, sd, "hue:%d\n", state->hue); + saa717x_write(sd, 0x10d, state->hue); break; case V4L2_CID_AUDIO_MUTE: state->audio_main_mute = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; case V4L2_CID_AUDIO_VOLUME: state->audio_main_volume = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; case V4L2_CID_AUDIO_BALANCE: state->audio_main_balance = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; case V4L2_CID_AUDIO_TREBLE: state->audio_main_treble = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; case V4L2_CID_AUDIO_BASS: state->audio_main_bass = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; default: @@ -972,9 +974,9 @@ static int saa717x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c return 0; } -static int saa717x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa717x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct saa717x_state *state = i2c_get_clientdata(client); + struct saa717x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -1103,13 +1105,15 @@ static struct v4l2_queryctrl saa717x_qctrl[] = { }, }; -static int saa717x_set_video_input(struct i2c_client *client, struct saa717x_state *decoder, int inp) +static int saa717x_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { + struct saa717x_state *decoder = to_state(sd); + int inp = route->input; int is_tuner = inp & 0x80; /* tuner input flag */ inp &= 0x7f; - v4l_dbg(1, debug, client, "decoder set input (%d)\n", inp); + v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", inp); /* inputs from 0-9 are available*/ /* saa717x have mode0-mode9 but mode5 is reserved. */ if (inp < 0 || inp > 9 || inp == 5) @@ -1119,222 +1123,197 @@ static int saa717x_set_video_input(struct i2c_client *client, struct saa717x_sta int input_line = inp; decoder->input = input_line; - v4l_dbg(1, debug, client, "now setting %s input %d\n", + v4l2_dbg(1, debug, sd, "now setting %s input %d\n", input_line >= 6 ? "S-Video" : "Composite", input_line); /* select mode */ - saa717x_write(client, 0x102, - (saa717x_read(client, 0x102) & 0xf0) | + saa717x_write(sd, 0x102, + (saa717x_read(sd, 0x102) & 0xf0) | input_line); /* bypass chrominance trap for modes 6..9 */ - saa717x_write(client, 0x109, - (saa717x_read(client, 0x109) & 0x7f) | + saa717x_write(sd, 0x109, + (saa717x_read(sd, 0x109) & 0x7f) | (input_line < 6 ? 0x0 : 0x80)); /* change audio_mode */ if (is_tuner) { /* tuner */ - set_audio_mode(client, decoder->tuner_audio_mode); + set_audio_mode(sd, decoder->tuner_audio_mode); } else { /* Force to STEREO mode if Composite or * S-Video were chosen */ - set_audio_mode(client, TUNER_AUDIO_STEREO); + set_audio_mode(sd, TUNER_AUDIO_STEREO); } /* change initialize procedure (Composite/S-Video) */ if (is_tuner) - saa717x_write_regs(client, reg_init_tuner_input); + saa717x_write_regs(sd, reg_init_tuner_input); else if (input_line >= 6) - saa717x_write_regs(client, reg_init_svideo_input); + saa717x_write_regs(sd, reg_init_svideo_input); else - saa717x_write_regs(client, reg_init_composite_input); + saa717x_write_regs(sd, reg_init_composite_input); } return 0; } -static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg) +static int saa717x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { - struct saa717x_state *decoder = i2c_get_clientdata(client); - - v4l_dbg(1, debug, client, "IOCTL: %08x\n", cmd); + int i; - switch (cmd) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - return saa717x_set_audio_clock_freq(client, *(u32 *)arg); + for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++) + if (qc->id && qc->id == saa717x_qctrl[i].id) { + memcpy(qc, &saa717x_qctrl[i], sizeof(*qc)); + return 0; + } + return -EINVAL; +} - case VIDIOC_G_CTRL: - return saa717x_get_v4lctrl(client, (struct v4l2_control *)arg); +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_S_CTRL: - return saa717x_set_v4lctrl(client, (struct v4l2_control *)arg); + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa717x_read(sd, reg->reg); + return 0; +} - case VIDIOC_QUERYCTRL: { - struct v4l2_queryctrl *qc = arg; - int i; +static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 addr = reg->reg & 0xffff; + u8 val = reg->val & 0xff; - for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++) - if (qc->id && qc->id == saa717x_qctrl[i].id) { - memcpy(qc, &saa717x_qctrl[i], sizeof(*qc)); - return 0; - } + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) return -EINVAL; - } - -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = saa717x_read(client, reg->reg); - break; - } + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa717x_write(sd, addr, val); + return 0; +} +#endif - case VIDIOC_DBG_S_REGISTER: { - struct v4l2_register *reg = arg; - u16 addr = reg->reg & 0xffff; - u8 val = reg->val & 0xff; +static int saa717x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct v4l2_pix_format *pix; + int prescale, h_scale, v_scale; - if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - saa717x_write(client, addr, val); - break; - } -#endif + pix = &fmt->fmt.pix; + v4l2_dbg(1, debug, sd, "decoder set size\n"); - case VIDIOC_S_FMT: { - struct v4l2_format *fmt = (struct v4l2_format *)arg; - struct v4l2_pix_format *pix; - int prescale, h_scale, v_scale; - - pix = &fmt->fmt.pix; - v4l_dbg(1, debug, client, "decoder set size\n"); - - /* FIXME need better bounds checking here */ - if (pix->width < 1 || pix->width > 1440) - return -EINVAL; - if (pix->height < 1 || pix->height > 960) - return -EINVAL; - - /* scaling setting */ - /* NTSC and interlace only */ - prescale = SAA717X_NTSC_WIDTH / pix->width; - if (prescale == 0) - prescale = 1; - h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / pix->width; - /* interlace */ - v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / pix->height; - - /* Horizontal prescaling etc */ - set_h_prescale(client, 0, prescale); - set_h_prescale(client, 1, prescale); - - /* Horizontal scaling increment */ - /* TASK A */ - saa717x_write(client, 0x6C, (u8)(h_scale & 0xFF)); - saa717x_write(client, 0x6D, (u8)((h_scale >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(client, 0xAC, (u8)(h_scale & 0xFF)); - saa717x_write(client, 0xAD, (u8)((h_scale >> 8) & 0xFF)); - - /* Vertical prescaling etc */ - set_v_scale(client, 0, v_scale); - set_v_scale(client, 1, v_scale); - - /* set video output size */ - /* video number of pixels at output */ - /* TASK A */ - saa717x_write(client, 0x5C, (u8)(pix->width & 0xFF)); - saa717x_write(client, 0x5D, (u8)((pix->width >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(client, 0x9C, (u8)(pix->width & 0xFF)); - saa717x_write(client, 0x9D, (u8)((pix->width >> 8) & 0xFF)); - - /* video number of lines at output */ - /* TASK A */ - saa717x_write(client, 0x5E, (u8)(pix->height & 0xFF)); - saa717x_write(client, 0x5F, (u8)((pix->height >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(client, 0x9E, (u8)(pix->height & 0xFF)); - saa717x_write(client, 0x9F, (u8)((pix->height >> 8) & 0xFF)); - break; - } + /* FIXME need better bounds checking here */ + if (pix->width < 1 || pix->width > 1440) + return -EINVAL; + if (pix->height < 1 || pix->height > 960) + return -EINVAL; - case AUDC_SET_RADIO: - decoder->radio = 1; - break; + /* scaling setting */ + /* NTSC and interlace only */ + prescale = SAA717X_NTSC_WIDTH / pix->width; + if (prescale == 0) + prescale = 1; + h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / pix->width; + /* interlace */ + v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / pix->height; + + /* Horizontal prescaling etc */ + set_h_prescale(sd, 0, prescale); + set_h_prescale(sd, 1, prescale); + + /* Horizontal scaling increment */ + /* TASK A */ + saa717x_write(sd, 0x6C, (u8)(h_scale & 0xFF)); + saa717x_write(sd, 0x6D, (u8)((h_scale >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0xAC, (u8)(h_scale & 0xFF)); + saa717x_write(sd, 0xAD, (u8)((h_scale >> 8) & 0xFF)); + + /* Vertical prescaling etc */ + set_v_scale(sd, 0, v_scale); + set_v_scale(sd, 1, v_scale); + + /* set video output size */ + /* video number of pixels at output */ + /* TASK A */ + saa717x_write(sd, 0x5C, (u8)(pix->width & 0xFF)); + saa717x_write(sd, 0x5D, (u8)((pix->width >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0x9C, (u8)(pix->width & 0xFF)); + saa717x_write(sd, 0x9D, (u8)((pix->width >> 8) & 0xFF)); + + /* video number of lines at output */ + /* TASK A */ + saa717x_write(sd, 0x5E, (u8)(pix->height & 0xFF)); + saa717x_write(sd, 0x5F, (u8)((pix->height >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0x9E, (u8)(pix->height & 0xFF)); + saa717x_write(sd, 0x9F, (u8)((pix->height >> 8) & 0xFF)); + return 0; +} - case VIDIOC_S_STD: { - v4l2_std_id std = *(v4l2_std_id *) arg; +static int saa717x_s_radio(struct v4l2_subdev *sd) +{ + struct saa717x_state *decoder = to_state(sd); - v4l_dbg(1, debug, client, "decoder set norm "); - v4l_dbg(1, debug, client, "(not yet implementd)\n"); + decoder->radio = 1; + return 0; +} - decoder->radio = 0; - decoder->std = std; - break; - } +static int saa717x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa717x_state *decoder = to_state(sd); - case VIDIOC_INT_G_AUDIO_ROUTING: { - struct v4l2_routing *route = arg; + v4l2_dbg(1, debug, sd, "decoder set norm "); + v4l2_dbg(1, debug, sd, "(not yet implementd)\n"); - route->input = decoder->audio_input; - route->output = 0; - break; - } + decoder->radio = 0; + decoder->std = std; + return 0; +} - case VIDIOC_INT_S_AUDIO_ROUTING: { - struct v4l2_routing *route = arg; +static int saa717x_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct saa717x_state *decoder = to_state(sd); - if (route->input < 3) { /* FIXME! --tadachi */ - decoder->audio_input = route->input; - v4l_dbg(1, debug, client, + if (route->input < 3) { /* FIXME! --tadachi */ + decoder->audio_input = route->input; + v4l2_dbg(1, debug, sd, "set decoder audio input to %d\n", decoder->audio_input); - set_audio_regs(client, decoder); - break; - } - return -ERANGE; - } - - case VIDIOC_INT_S_VIDEO_ROUTING: { - struct v4l2_routing *route = arg; - int inp = route->input; - - return saa717x_set_video_input(client, decoder, inp); + set_audio_regs(sd, decoder); + return 0; } + return -ERANGE; +} - case VIDIOC_STREAMON: { - v4l_dbg(1, debug, client, "decoder enable output\n"); - decoder->enable = 1; - saa717x_write(client, 0x193, 0xa6); - break; - } +static int saa717x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa717x_state *decoder = to_state(sd); - case VIDIOC_STREAMOFF: { - v4l_dbg(1, debug, client, "decoder disable output\n"); - decoder->enable = 0; - saa717x_write(client, 0x193, 0x26); /* right? FIXME!--tadachi */ - break; - } + v4l2_dbg(1, debug, sd, "decoder %s output\n", + enable ? "enable" : "disable"); + decoder->enable = enable; + saa717x_write(sd, 0x193, enable ? 0xa6 : 0x26); + return 0; +} - /* change audio mode */ - case VIDIOC_S_TUNER: { - struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; - int audio_mode; - char *mes[4] = { - "MONO", "STEREO", "LANG1", "LANG2/SAP" - }; +/* change audio mode */ +static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa717x_state *decoder = to_state(sd); + int audio_mode; + char *mes[4] = { + "MONO", "STEREO", "LANG1", "LANG2/SAP" + }; - audio_mode = V4L2_TUNER_MODE_STEREO; + audio_mode = V4L2_TUNER_MODE_STEREO; - switch (vt->audmode) { + switch (vt->audmode) { case V4L2_TUNER_MODE_MONO: audio_mode = TUNER_AUDIO_MONO; break; @@ -1347,70 +1326,101 @@ static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg) case V4L2_TUNER_MODE_LANG1: audio_mode = TUNER_AUDIO_LANG1; break; - } - - v4l_dbg(1, debug, client, "change audio mode to %s\n", - mes[audio_mode]); - decoder->tuner_audio_mode = audio_mode; - /* The registers are not changed here. */ - /* See DECODER_ENABLE_OUTPUT section. */ - set_audio_mode(client, decoder->tuner_audio_mode); - break; } - case VIDIOC_G_TUNER: { - struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; - int dual_f, stereo_f; + v4l2_dbg(1, debug, sd, "change audio mode to %s\n", + mes[audio_mode]); + decoder->tuner_audio_mode = audio_mode; + /* The registers are not changed here. */ + /* See DECODER_ENABLE_OUTPUT section. */ + set_audio_mode(sd, decoder->tuner_audio_mode); + return 0; +} - if (decoder->radio) - break; - get_inf_dev_status(client, &dual_f, &stereo_f); +static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa717x_state *decoder = to_state(sd); + int dual_f, stereo_f; - v4l_dbg(1, debug, client, "DETECT==st:%d dual:%d\n", - stereo_f, dual_f); + if (decoder->radio) + return 0; + get_inf_dev_status(sd, &dual_f, &stereo_f); - /* mono */ - if ((dual_f == 0) && (stereo_f == 0)) { - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - v4l_dbg(1, debug, client, "DETECT==MONO\n"); - } + v4l2_dbg(1, debug, sd, "DETECT==st:%d dual:%d\n", + stereo_f, dual_f); - /* stereo */ - if (stereo_f == 1) { - if (vt->audmode == V4L2_TUNER_MODE_STEREO || - vt->audmode == V4L2_TUNER_MODE_LANG1) { - vt->rxsubchans = V4L2_TUNER_SUB_STEREO; - v4l_dbg(1, debug, client, "DETECT==ST(ST)\n"); - } else { - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - v4l_dbg(1, debug, client, "DETECT==ST(MONO)\n"); - } - } + /* mono */ + if ((dual_f == 0) && (stereo_f == 0)) { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==MONO\n"); + } - /* dual */ - if (dual_f == 1) { - if (vt->audmode == V4L2_TUNER_MODE_LANG2) { - vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO; - v4l_dbg(1, debug, client, "DETECT==DUAL1\n"); - } else { - vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO; - v4l_dbg(1, debug, client, "DETECT==DUAL2\n"); - } + /* stereo */ + if (stereo_f == 1) { + if (vt->audmode == V4L2_TUNER_MODE_STEREO || + vt->audmode == V4L2_TUNER_MODE_LANG1) { + vt->rxsubchans = V4L2_TUNER_SUB_STEREO; + v4l2_dbg(1, debug, sd, "DETECT==ST(ST)\n"); + } else { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==ST(MONO)\n"); } - break; } - case VIDIOC_LOG_STATUS: - /* not yet implemented */ - break; - - default: - return -EINVAL; + /* dual */ + if (dual_f == 1) { + if (vt->audmode == V4L2_TUNER_MODE_LANG2) { + vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==DUAL1\n"); + } else { + vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==DUAL2\n"); + } } - return 0; } +static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa717x_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa717x_g_register, + .s_register = saa717x_s_register, +#endif + .queryctrl = saa717x_queryctrl, + .g_ctrl = saa717x_g_ctrl, + .s_ctrl = saa717x_s_ctrl, +}; + +static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = { + .g_tuner = saa717x_g_tuner, + .s_tuner = saa717x_s_tuner, + .s_std = saa717x_s_std, + .s_radio = saa717x_s_radio, +}; + +static const struct v4l2_subdev_video_ops saa717x_video_ops = { + .s_routing = saa717x_s_video_routing, + .s_fmt = saa717x_s_fmt, + .s_stream = saa717x_s_stream, +}; + +static const struct v4l2_subdev_audio_ops saa717x_audio_ops = { + .s_routing = saa717x_s_audio_routing, +}; + +static const struct v4l2_subdev_ops saa717x_ops = { + .core = &saa717x_core_ops, + .tuner = &saa717x_tuner_ops, + .audio = &saa717x_audio_ops, + .video = &saa717x_video_ops, +}; + /* ----------------------------------------------------------------------- */ @@ -1421,6 +1431,7 @@ static int saa717x_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct saa717x_state *decoder; + struct v4l2_subdev *sd; u8 id = 0; char *p = ""; @@ -1428,13 +1439,21 @@ static int saa717x_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - if (saa717x_write(client, 0x5a4, 0xfe) && - saa717x_write(client, 0x5a5, 0x0f) && - saa717x_write(client, 0x5a6, 0x00) && - saa717x_write(client, 0x5a7, 0x01)) - id = saa717x_read(client, 0x5a0); + decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa717x_ops); + + if (saa717x_write(sd, 0x5a4, 0xfe) && + saa717x_write(sd, 0x5a5, 0x0f) && + saa717x_write(sd, 0x5a6, 0x00) && + saa717x_write(sd, 0x5a7, 0x01)) + id = saa717x_read(sd, 0x5a0); if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) { - v4l_dbg(1, debug, client, "saa717x not found (id=%02x)\n", id); + v4l2_dbg(1, debug, sd, "saa717x not found (id=%02x)\n", id); + kfree(decoder); return -ENODEV; } if (id == 0xc2) @@ -1445,14 +1464,8 @@ static int saa717x_probe(struct i2c_client *client, p = "saa7174HL"; else p = "saa7171"; - v4l_info(client, "%s found @ 0x%x (%s)\n", p, + v4l2_info(sd, "%s found @ 0x%x (%s)\n", p, client->addr << 1, client->adapter->name); - - decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL); - i2c_set_clientdata(client, decoder); - - if (decoder == NULL) - return -ENOMEM; decoder->std = V4L2_STD_NTSC; decoder->input = -1; decoder->enable = 1; @@ -1481,15 +1494,15 @@ static int saa717x_probe(struct i2c_client *client, decoder->audio_main_volume = (decoder->audio_main_vol_r + 41) * 65535 / (24 - (-40)); - v4l_dbg(1, debug, client, "writing init values\n"); + v4l2_dbg(1, debug, sd, "writing init values\n"); /* FIXME!! */ - saa717x_write_regs(client, reg_init_initialize); - set_video_output_level_regs(client, decoder); + saa717x_write_regs(sd, reg_init_initialize); + set_video_output_level_regs(sd, decoder); /* set bass,treble to 0db 20041101 K.Ohta */ decoder->audio_main_bass = 0; decoder->audio_main_treble = 0; - set_audio_regs(client, decoder); + set_audio_regs(sd, decoder); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(2*HZ); @@ -1498,7 +1511,10 @@ static int saa717x_probe(struct i2c_client *client, static int saa717x_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c index 044a2e9..d652f25 100644 --- a/drivers/media/video/se401.c +++ b/drivers/media/video/se401.c @@ -975,8 +975,7 @@ static int se401_close(struct inode *inode, struct file *file) return 0; } -static int se401_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int se401_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = file->private_data; struct usb_se401 *se401 = (struct usb_se401 *)vdev; @@ -1142,7 +1141,7 @@ static int se401_do_ioctl(struct inode *inode, struct file *file, static int se401_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, se401_do_ioctl); + return video_usercopy(file, cmd, arg, se401_do_ioctl); } static ssize_t se401_read(struct file *file, char __user *buf, diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 536b1a9..9a2586b 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -29,7 +29,6 @@ #include <linux/version.h> #include <linux/device.h> #include <linux/platform_device.h> -#include <linux/mutex.h> #include <linux/videodev2.h> #include <linux/clk.h> @@ -75,8 +74,6 @@ #define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */ #define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */ -static DEFINE_MUTEX(camera_lock); - /* per video frame buffer */ struct sh_mobile_ceu_buffer { struct videobuf_buffer vb; /* v4l buffer must be first */ @@ -97,18 +94,20 @@ struct sh_mobile_ceu_dev { spinlock_t lock; struct list_head capture; struct videobuf_buffer *active; + int is_interlace; struct sh_mobile_ceu_info *pdata; + + const struct soc_camera_data_format *camera_fmt; }; static void ceu_write(struct sh_mobile_ceu_dev *priv, - unsigned long reg_offs, unsigned long data) + unsigned long reg_offs, u32 data) { iowrite32(data, priv->base + reg_offs); } -static unsigned long ceu_read(struct sh_mobile_ceu_dev *priv, - unsigned long reg_offs) +static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs) { return ioread32(priv->base + reg_offs); } @@ -156,21 +155,52 @@ static void free_buffer(struct videobuf_queue *vq, buf->vb.state = VIDEOBUF_NEEDS_INIT; } +#define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */ +#define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */ +#define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */ +#define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */ + + static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) { - ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~1); - ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & 0x0317f313); - ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | 1); - - ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~0x10000); + struct soc_camera_device *icd = pcdev->icd; + dma_addr_t phys_addr_top, phys_addr_bottom; - ceu_write(pcdev, CETCR, 0x0317f313 ^ 0x10); + /* The hardware is _very_ picky about this sequence. Especially + * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge + * several not-so-well documented interrupt sources in CETCR. + */ + ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_CPEIE); + ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & CEU_CETCR_MAGIC); + ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_CPEIE); + ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP); + ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW); + + if (!pcdev->active) + return; + + phys_addr_top = videobuf_to_dma_contig(pcdev->active); + ceu_write(pcdev, CDAYR, phys_addr_top); + if (pcdev->is_interlace) { + phys_addr_bottom = phys_addr_top + icd->width; + ceu_write(pcdev, CDBYR, phys_addr_bottom); + } - if (pcdev->active) { - pcdev->active->state = VIDEOBUF_ACTIVE; - ceu_write(pcdev, CDAYR, videobuf_to_dma_contig(pcdev->active)); - ceu_write(pcdev, CAPSR, 0x1); /* start capture */ + switch (icd->current_fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + phys_addr_top += icd->width * icd->height; + ceu_write(pcdev, CDACR, phys_addr_top); + if (pcdev->is_interlace) { + phys_addr_bottom = phys_addr_top + icd->width; + ceu_write(pcdev, CDBCR, phys_addr_bottom); + } } + + pcdev->active->state = VIDEOBUF_ACTIVE; + ceu_write(pcdev, CAPSR, 0x1); /* start capture */ } static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, @@ -292,14 +322,13 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) return IRQ_HANDLED; } +/* Called with .video_lock held */ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; int ret = -EBUSY; - mutex_lock(&camera_lock); - if (pcdev->icd) goto err; @@ -319,11 +348,10 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) pcdev->icd = icd; err: - mutex_unlock(&camera_lock); - return ret; } +/* Called with .video_lock held */ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); @@ -362,8 +390,9 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - int ret, buswidth, width, cfszr_width, cdwdr_width; + int ret, buswidth, width, height, cfszr_width, cdwdr_width; unsigned long camera_flags, common_flags, value; + int yuv_mode, yuv_lineskip; camera_flags = icd->ops->query_bus_param(icd); common_flags = soc_camera_bus_param_compatible(camera_flags, @@ -389,27 +418,71 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, ceu_write(pcdev, CRCNTR, 0); ceu_write(pcdev, CRCMPR, 0); - value = 0x00000010; - value |= (common_flags & SOCAM_VSYNC_ACTIVE_LOW) ? (1 << 1) : 0; - value |= (common_flags & SOCAM_HSYNC_ACTIVE_LOW) ? (1 << 0) : 0; - value |= (buswidth == 16) ? (1 << 12) : 0; + value = 0x00000010; /* data fetch by default */ + yuv_mode = yuv_lineskip = 0; + + switch (icd->current_fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + yuv_lineskip = 1; /* skip for NV12/21, no skip for NV16/61 */ + /* fall-through */ + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + yuv_mode = 1; + switch (pcdev->camera_fmt->fourcc) { + case V4L2_PIX_FMT_UYVY: + value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */ + break; + case V4L2_PIX_FMT_VYUY: + value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */ + break; + case V4L2_PIX_FMT_YUYV: + value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */ + break; + case V4L2_PIX_FMT_YVYU: + value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */ + break; + default: + BUG(); + } + } + + if (icd->current_fmt->fourcc == V4L2_PIX_FMT_NV21 || + icd->current_fmt->fourcc == V4L2_PIX_FMT_NV61) + value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */ + + value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; + value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; + value |= buswidth == 16 ? 1 << 12 : 0; ceu_write(pcdev, CAMCR, value); ceu_write(pcdev, CAPCR, 0x00300000); - ceu_write(pcdev, CAIFR, 0); + ceu_write(pcdev, CAIFR, (pcdev->is_interlace) ? 0x101 : 0); mdelay(1); - width = icd->width * (icd->current_fmt->depth / 8); - width = (buswidth == 16) ? width / 2 : width; - cfszr_width = (buswidth == 8) ? width / 2 : width; - cdwdr_width = (buswidth == 16) ? width * 2 : width; + if (yuv_mode) { + width = icd->width * 2; + width = buswidth == 16 ? width / 2 : width; + cfszr_width = cdwdr_width = icd->width; + } else { + width = icd->width * ((icd->current_fmt->depth + 7) >> 3); + width = buswidth == 16 ? width / 2 : width; + cfszr_width = buswidth == 8 ? width / 2 : width; + cdwdr_width = buswidth == 16 ? width * 2 : width; + } + + height = icd->height; + if (pcdev->is_interlace) { + height /= 2; + cdwdr_width *= 2; + } ceu_write(pcdev, CAMOR, 0); - ceu_write(pcdev, CAPWR, (icd->height << 16) | width); - ceu_write(pcdev, CFLCR, 0); /* data fetch mode - no scaling */ - ceu_write(pcdev, CFSZR, (icd->height << 16) | cfszr_width); - ceu_write(pcdev, CLFCR, 0); /* data fetch mode - no lowpass filter */ + ceu_write(pcdev, CAPWR, (height << 16) | width); + ceu_write(pcdev, CFLCR, 0); /* no scaling */ + ceu_write(pcdev, CFSZR, (height << 16) | cfszr_width); + ceu_write(pcdev, CLFCR, 0); /* no lowpass filter */ /* A few words about byte order (observed in Big Endian mode) * @@ -423,19 +496,20 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, * using 7 we swap the data bytes to match the incoming order: * D0, D1, D2, D3, D4, D5, D6, D7 */ - ceu_write(pcdev, CDOCR, 0x00000017); + value = 0x00000017; + if (yuv_lineskip) + value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */ + + ceu_write(pcdev, CDOCR, value); ceu_write(pcdev, CDWDR, cdwdr_width); ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */ /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */ - /* in data fetch mode: no need for CDACR, CDBYR, CDBCR */ - return 0; } -static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, - __u32 pixfmt) +static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; @@ -450,15 +524,123 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, return 0; } -static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static const struct soc_camera_data_format sh_mobile_ceu_formats[] = { + { + .name = "NV12", + .depth = 12, + .fourcc = V4L2_PIX_FMT_NV12, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + { + .name = "NV21", + .depth = 12, + .fourcc = V4L2_PIX_FMT_NV21, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + { + .name = "NV16", + .depth = 16, + .fourcc = V4L2_PIX_FMT_NV16, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + { + .name = "NV61", + .depth = 16, + .fourcc = V4L2_PIX_FMT_NV61, + .colorspace = V4L2_COLORSPACE_JPEG, + }, +}; + +static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, + struct soc_camera_format_xlate *xlate) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + int ret, k, n; + int formats = 0; + + ret = sh_mobile_ceu_try_bus_param(icd); + if (ret < 0) + return 0; + + switch (icd->formats[idx].fourcc) { + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + n = ARRAY_SIZE(sh_mobile_ceu_formats); + formats += n; + for (k = 0; xlate && k < n; k++) { + xlate->host_fmt = &sh_mobile_ceu_formats[k]; + xlate->cam_fmt = icd->formats + idx; + xlate->buswidth = icd->formats[idx].depth; + xlate++; + dev_dbg(&ici->dev, "Providing format %s using %s\n", + sh_mobile_ceu_formats[k].name, + icd->formats[idx].name); + } + default: + /* Generic pass-through */ + formats++; + if (xlate) { + xlate->host_fmt = icd->formats + idx; + xlate->cam_fmt = icd->formats + idx; + xlate->buswidth = icd->formats[idx].depth; + xlate++; + dev_dbg(&ici->dev, + "Providing format %s in pass-through mode\n", + icd->formats[idx].name); + } + } + + return formats; +} + +static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { - return icd->ops->set_fmt_cap(icd, pixfmt, rect); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + const struct soc_camera_format_xlate *xlate; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + return -EINVAL; + } + + switch (pixfmt) { + case 0: /* Only geometry change */ + ret = icd->ops->set_fmt(icd, pixfmt, rect); + break; + default: + ret = icd->ops->set_fmt(icd, xlate->cam_fmt->fourcc, rect); + } + + if (pixfmt && !ret) { + icd->buswidth = xlate->buswidth; + icd->current_fmt = xlate->host_fmt; + pcdev->camera_fmt = xlate->cam_fmt; + } + + return ret; } -static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + const struct soc_camera_format_xlate *xlate; + __u32 pixfmt = f->fmt.pix.pixelformat; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + return -EINVAL; + } + /* FIXME: calculate using depth and bus width */ if (f->fmt.pix.height < 4) @@ -472,8 +654,31 @@ static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd, f->fmt.pix.width &= ~0x01; f->fmt.pix.height &= ~0x03; + f->fmt.pix.bytesperline = f->fmt.pix.width * + DIV_ROUND_UP(xlate->host_fmt->depth, 8); + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + /* limit to sensor capabilities */ - return icd->ops->try_fmt_cap(icd, f); + ret = icd->ops->try_fmt(icd, f); + if (ret < 0) + return ret; + + switch (f->fmt.pix.field) { + case V4L2_FIELD_INTERLACED: + pcdev->is_interlace = 1; + break; + case V4L2_FIELD_ANY: + f->fmt.pix.field = V4L2_FIELD_NONE; + /* fall-through */ + case V4L2_FIELD_NONE: + pcdev->is_interlace = 0; + break; + default: + ret = -EINVAL; + break; + } + + return ret; } static int sh_mobile_ceu_reqbufs(struct soc_camera_file *icf, @@ -532,7 +737,7 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q, &sh_mobile_ceu_videobuf_ops, &ici->dev, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_NONE, + V4L2_FIELD_ANY, sizeof(struct sh_mobile_ceu_buffer), icd); } @@ -541,12 +746,12 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .owner = THIS_MODULE, .add = sh_mobile_ceu_add_device, .remove = sh_mobile_ceu_remove_device, - .set_fmt_cap = sh_mobile_ceu_set_fmt_cap, - .try_fmt_cap = sh_mobile_ceu_try_fmt_cap, + .get_formats = sh_mobile_ceu_get_formats, + .set_fmt = sh_mobile_ceu_set_fmt, + .try_fmt = sh_mobile_ceu_try_fmt, .reqbufs = sh_mobile_ceu_reqbufs, .poll = sh_mobile_ceu_poll, .querycap = sh_mobile_ceu_querycap, - .try_bus_param = sh_mobile_ceu_try_bus_param, .set_bus_param = sh_mobile_ceu_set_bus_param, .init_videobuf = sh_mobile_ceu_init_videobuf, }; @@ -616,7 +821,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) /* request irq */ err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED, - pdev->dev.bus_id, pcdev); + dev_name(&pdev->dev), pcdev); if (err) { dev_err(&pdev->dev, "Unable to register CEU interrupt.\n"); goto exit_release_mem; @@ -633,8 +838,8 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->ici.priv = pcdev; pcdev->ici.dev.parent = &pdev->dev; pcdev->ici.nr = pdev->id; - pcdev->ici.drv_name = pdev->dev.bus_id, - pcdev->ici.ops = &sh_mobile_ceu_host_ops, + pcdev->ici.drv_name = dev_name(&pdev->dev); + pcdev->ici.ops = &sh_mobile_ceu_host_ops; err = soc_camera_host_register(&pcdev->ici); if (err) diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index fcd2b62..01a8efb8 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -2162,7 +2162,7 @@ sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg) strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) - strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, + strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), sizeof(cap.bus_info)); if (copy_to_user(arg, &cap, sizeof(cap))) diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index e23734f..8cb3457 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -55,7 +55,9 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, +#endif /* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */ { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, @@ -91,10 +93,14 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, /* SN9C105 */ +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), }, +#endif { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, @@ -113,7 +119,9 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */ +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 66ebe59..90077cb 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -33,10 +33,9 @@ static LIST_HEAD(hosts); static LIST_HEAD(devices); static DEFINE_MUTEX(list_lock); -static DEFINE_MUTEX(video_lock); -const static struct soc_camera_data_format* -format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc) +const struct soc_camera_data_format *soc_camera_format_by_fourcc( + struct soc_camera_device *icd, unsigned int fourcc) { unsigned int i; @@ -45,67 +44,87 @@ format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc) return icd->formats + i; return NULL; } +EXPORT_SYMBOL(soc_camera_format_by_fourcc); -static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) +const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( + struct soc_camera_device *icd, unsigned int fourcc) { - struct soc_camera_file *icf = file->private_data; - struct soc_camera_device *icd = icf->icd; - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); - enum v4l2_field field; - const struct soc_camera_data_format *fmt; - int ret; + unsigned int i; - WARN_ON(priv != file->private_data); + for (i = 0; i < icd->num_user_formats; i++) + if (icd->user_formats[i].host_fmt->fourcc == fourcc) + return icd->user_formats + i; + return NULL; +} +EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); - fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat); - if (!fmt) { - dev_dbg(&icd->dev, "invalid format 0x%08x\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } +/** + * soc_camera_apply_sensor_flags() - apply platform SOCAM_SENSOR_INVERT_* flags + * @icl: camera platform parameters + * @flags: flags to be inverted according to platform configuration + * @return: resulting flags + */ +unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl, + unsigned long flags) +{ + unsigned long f; - dev_dbg(&icd->dev, "fmt: 0x%08x\n", fmt->fourcc); + /* If only one of the two polarities is supported, switch to the opposite */ + if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) { + f = flags & (SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW); + if (f == SOCAM_HSYNC_ACTIVE_HIGH || f == SOCAM_HSYNC_ACTIVE_LOW) + flags ^= SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW; + } - field = f->fmt.pix.field; + if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) { + f = flags & (SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW); + if (f == SOCAM_VSYNC_ACTIVE_HIGH || f == SOCAM_VSYNC_ACTIVE_LOW) + flags ^= SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW; + } - if (field == V4L2_FIELD_ANY) { - field = V4L2_FIELD_NONE; - } else if (V4L2_FIELD_NONE != field) { - dev_err(&icd->dev, "Field type invalid.\n"); - return -EINVAL; + if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) { + f = flags & (SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING); + if (f == SOCAM_PCLK_SAMPLE_RISING || f == SOCAM_PCLK_SAMPLE_FALLING) + flags ^= SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING; } - /* test physical bus parameters */ - ret = ici->ops->try_bus_param(icd, f->fmt.pix.pixelformat); - if (ret) - return ret; + return flags; +} +EXPORT_SYMBOL(soc_camera_apply_sensor_flags); - /* limit format to hardware capabilities */ - ret = ici->ops->try_fmt_cap(icd, f); +static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - /* calculate missing fields */ - f->fmt.pix.field = field; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; + WARN_ON(priv != file->private_data); - return ret; + /* limit format to hardware capabilities */ + return ici->ops->try_fmt(icd, f); } static int soc_camera_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + int ret = 0; + if (inp->index != 0) return -EINVAL; - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = V4L2_STD_UNKNOWN; - strcpy(inp->name, "Camera"); + if (icd->ops->enum_input) + ret = icd->ops->enum_input(icd, inp); + else { + /* default is camera */ + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = V4L2_STD_UNKNOWN; + strcpy(inp->name, "Camera"); + } - return 0; + return ret; } static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i) @@ -125,7 +144,14 @@ static int soc_camera_s_input(struct file *file, void *priv, unsigned int i) static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a) { - return 0; + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + int ret = 0; + + if (icd->ops->set_std) + ret = icd->ops->set_std(icd, a); + + return ret; } static int soc_camera_reqbufs(struct file *file, void *priv, @@ -134,8 +160,7 @@ static int soc_camera_reqbufs(struct file *file, void *priv, int ret; struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); WARN_ON(priv != file->private_data); @@ -178,6 +203,59 @@ static int soc_camera_dqbuf(struct file *file, void *priv, return videobuf_dqbuf(&icf->vb_vidq, p, file->f_flags & O_NONBLOCK); } +static int soc_camera_init_user_formats(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + int i, fmts = 0; + + if (!ici->ops->get_formats) + /* + * Fallback mode - the host will have to serve all + * sensor-provided formats one-to-one to the user + */ + fmts = icd->num_formats; + else + /* + * First pass - only count formats this host-sensor + * configuration can provide + */ + for (i = 0; i < icd->num_formats; i++) + fmts += ici->ops->get_formats(icd, i, NULL); + + if (!fmts) + return -ENXIO; + + icd->user_formats = + vmalloc(fmts * sizeof(struct soc_camera_format_xlate)); + if (!icd->user_formats) + return -ENOMEM; + + icd->num_user_formats = fmts; + fmts = 0; + + dev_dbg(&icd->dev, "Found %d supported formats.\n", fmts); + + /* Second pass - actually fill data formats */ + for (i = 0; i < icd->num_formats; i++) + if (!ici->ops->get_formats) { + icd->user_formats[i].host_fmt = icd->formats + i; + icd->user_formats[i].cam_fmt = icd->formats + i; + icd->user_formats[i].buswidth = icd->formats[i].depth; + } else { + fmts += ici->ops->get_formats(icd, i, + &icd->user_formats[fmts]); + } + + icd->current_fmt = icd->user_formats[0].host_fmt; + + return 0; +} + +static void soc_camera_free_user_formats(struct soc_camera_device *icd) +{ + vfree(icd->user_formats); +} + static int soc_camera_open(struct inode *inode, struct file *file) { struct video_device *vdev; @@ -190,8 +268,10 @@ static int soc_camera_open(struct inode *inode, struct file *file) if (!icf) return -ENOMEM; - /* Protect against icd->remove() until we module_get() both drivers. */ - mutex_lock(&video_lock); + /* + * It is safe to dereference these pointers now as long as a user has + * the video device open - we are protected by the held cdev reference. + */ vdev = video_devdata(file); icd = container_of(vdev->parent, struct soc_camera_device, dev); @@ -209,20 +289,25 @@ static int soc_camera_open(struct inode *inode, struct file *file) goto emgi; } + /* Protect against icd->remove() until we module_get() both drivers. */ + mutex_lock(&icd->video_lock); + icf->icd = icd; icd->use_count++; /* Now we really have to activate the camera */ if (icd->use_count == 1) { + ret = soc_camera_init_user_formats(icd); + if (ret < 0) + goto eiufmt; ret = ici->ops->add(icd); if (ret < 0) { dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret); - icd->use_count--; goto eiciadd; } } - mutex_unlock(&video_lock); + mutex_unlock(&icd->video_lock); file->private_data = icf; dev_dbg(&icd->dev, "camera device open\n"); @@ -231,13 +316,16 @@ static int soc_camera_open(struct inode *inode, struct file *file) return 0; - /* All errors are entered with the video_lock held */ + /* First two errors are entered with the .video_lock held */ eiciadd: + soc_camera_free_user_formats(icd); +eiufmt: + icd->use_count--; + mutex_unlock(&icd->video_lock); module_put(ici->ops->owner); emgi: module_put(icd->ops->owner); emgd: - mutex_unlock(&video_lock); vfree(icf); return ret; } @@ -249,13 +337,16 @@ static int soc_camera_close(struct inode *inode, struct file *file) struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct video_device *vdev = icd->vdev; - mutex_lock(&video_lock); + mutex_lock(&icd->video_lock); icd->use_count--; - if (!icd->use_count) + if (!icd->use_count) { ici->ops->remove(icd); + soc_camera_free_user_formats(icd); + } + mutex_unlock(&icd->video_lock); + module_put(icd->ops->owner); module_put(ici->ops->owner); - mutex_unlock(&video_lock); vfree(icf); @@ -265,7 +356,7 @@ static int soc_camera_close(struct inode *inode, struct file *file) } static ssize_t soc_camera_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; @@ -299,8 +390,7 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); if (list_empty(&icf->vb_vidq.stream)) { dev_err(&icd->dev, "Trying to poll with no queued buffers!\n"); @@ -310,7 +400,6 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt) return ici->ops->poll(file, pt); } - static struct file_operations soc_camera_fops = { .owner = THIS_MODULE, .open = soc_camera_open, @@ -322,44 +411,50 @@ static struct file_operations soc_camera_fops = { .llseek = no_llseek, }; - static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) + struct v4l2_format *f) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct v4l2_pix_format *pix = &f->fmt.pix; + __u32 pixfmt = pix->pixelformat; int ret; struct v4l2_rect rect; - const static struct soc_camera_data_format *data_fmt; WARN_ON(priv != file->private_data); - data_fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat); - if (!data_fmt) - return -EINVAL; - - /* buswidth may be further adjusted by the ici */ - icd->buswidth = data_fmt->depth; - - ret = soc_camera_try_fmt_vid_cap(file, icf, f); + ret = soc_camera_try_fmt_vid_cap(file, priv, f); if (ret < 0) return ret; + mutex_lock(&icf->vb_vidq.vb_lock); + + if (videobuf_queue_is_busy(&icf->vb_vidq)) { + dev_err(&icd->dev, "S_FMT denied: queue busy\n"); + ret = -EBUSY; + goto unlock; + } + rect.left = icd->x_current; rect.top = icd->y_current; - rect.width = f->fmt.pix.width; - rect.height = f->fmt.pix.height; - ret = ici->ops->set_fmt_cap(icd, f->fmt.pix.pixelformat, &rect); - if (ret < 0) - return ret; + rect.width = pix->width; + rect.height = pix->height; + ret = ici->ops->set_fmt(icd, pix->pixelformat, &rect); + if (ret < 0) { + goto unlock; + } else if (!icd->current_fmt || + icd->current_fmt->fourcc != pixfmt) { + dev_err(&ici->dev, + "Host driver hasn't set up current format correctly!\n"); + ret = -EINVAL; + goto unlock; + } - icd->current_fmt = data_fmt; icd->width = rect.width; icd->height = rect.height; - icf->vb_vidq.field = f->fmt.pix.field; - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type) + icf->vb_vidq.field = pix->field; + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n", f->type); @@ -367,11 +462,16 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, icd->width, icd->height); /* set physical bus parameters */ - return ici->ops->set_bus_param(icd, f->fmt.pix.pixelformat); + ret = ici->ops->set_bus_param(icd, pixfmt); + +unlock: + mutex_unlock(&icf->vb_vidq.vb_lock); + + return ret; } static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) + struct v4l2_fmtdesc *f) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; @@ -379,10 +479,10 @@ static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, WARN_ON(priv != file->private_data); - if (f->index >= icd->num_formats) + if (f->index >= icd->num_user_formats) return -EINVAL; - format = &icd->formats[f->index]; + format = icd->user_formats[f->index].host_fmt; strlcpy(f->description, format->name, sizeof(f->description)); f->pixelformat = format->fourcc; @@ -390,21 +490,21 @@ static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, } static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) + struct v4l2_format *f) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct v4l2_pix_format *pix = &f->fmt.pix; WARN_ON(priv != file->private_data); - f->fmt.pix.width = icd->width; - f->fmt.pix.height = icd->height; - f->fmt.pix.field = icf->vb_vidq.field; - f->fmt.pix.pixelformat = icd->current_fmt->fourcc; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * icd->current_fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; + pix->width = icd->width; + pix->height = icd->height; + pix->field = icf->vb_vidq.field; + pix->pixelformat = icd->current_fmt->fourcc; + pix->bytesperline = pix->width * + DIV_ROUND_UP(icd->current_fmt->depth, 8); + pix->sizeimage = pix->height * pix->bytesperline; dev_dbg(&icd->dev, "current_fmt->fourcc: 0x%08x\n", icd->current_fmt->fourcc); return 0; @@ -415,8 +515,7 @@ static int soc_camera_querycap(struct file *file, void *priv, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); WARN_ON(priv != file->private_data); @@ -429,6 +528,7 @@ static int soc_camera_streamon(struct file *file, void *priv, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + int ret; WARN_ON(priv != file->private_data); @@ -437,10 +537,16 @@ static int soc_camera_streamon(struct file *file, void *priv, if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + mutex_lock(&icd->video_lock); + icd->ops->start_capture(icd); /* This calls buf_queue from host driver's videobuf_queue_ops */ - return videobuf_streamon(&icf->vb_vidq); + ret = videobuf_streamon(&icf->vb_vidq); + + mutex_unlock(&icd->video_lock); + + return ret; } static int soc_camera_streamoff(struct file *file, void *priv, @@ -456,12 +562,16 @@ static int soc_camera_streamoff(struct file *file, void *priv, if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + mutex_lock(&icd->video_lock); + /* This calls buf_release from host driver's videobuf_queue_ops for all * remaining buffers. When the last buffer is freed, stop capture */ videobuf_streamoff(&icf->vb_vidq); icd->ops->stop_capture(icd); + mutex_unlock(&icd->video_lock); + return 0; } @@ -567,14 +677,16 @@ static int soc_camera_s_crop(struct file *file, void *fh, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); int ret; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - ret = ici->ops->set_fmt_cap(icd, 0, &a->c); + /* Cropping is allowed during a running capture, guard consistency */ + mutex_lock(&icf->vb_vidq.vb_lock); + + ret = ici->ops->set_fmt(icd, 0, &a->c); if (!ret) { icd->width = a->c.width; icd->height = a->c.height; @@ -582,6 +694,8 @@ static int soc_camera_s_crop(struct file *file, void *fh, icd->y_current = a->c.top; } + mutex_unlock(&icf->vb_vidq.vb_lock); + return ret; } @@ -692,18 +806,35 @@ static int scan_add_device(struct soc_camera_device *icd) static int soc_camera_probe(struct device *dev) { struct soc_camera_device *icd = to_soc_camera_dev(dev); - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); int ret; - if (!icd->ops->probe) - return -ENODEV; + /* + * Possible race scenario: + * modprobe <camera-host-driver> triggers __func__ + * at this moment respective <camera-sensor-driver> gets rmmod'ed + * to protect take module references. + */ + + if (!try_module_get(icd->ops->owner)) { + dev_err(&icd->dev, "Couldn't lock sensor driver.\n"); + ret = -EINVAL; + goto emgd; + } + + if (!try_module_get(ici->ops->owner)) { + dev_err(&icd->dev, "Couldn't lock capture bus driver.\n"); + ret = -EINVAL; + goto emgi; + } + + mutex_lock(&icd->video_lock); /* We only call ->add() here to activate and probe the camera. * We shall ->remove() and deactivate it immediately afterwards. */ ret = ici->ops->add(icd); if (ret < 0) - return ret; + goto eiadd; ret = icd->ops->probe(icd); if (ret >= 0) { @@ -717,6 +848,12 @@ static int soc_camera_probe(struct device *dev) } ici->ops->remove(icd); +eiadd: + mutex_unlock(&icd->video_lock); + module_put(ici->ops->owner); +emgi: + module_put(icd->ops->owner); +emgd: return ret; } @@ -779,11 +916,20 @@ int soc_camera_host_register(struct soc_camera_host *ici) int ret; struct soc_camera_host *ix; - if (!ici->ops->init_videobuf || !ici->ops->add || !ici->ops->remove) + if (!ici || !ici->ops || + !ici->ops->try_fmt || + !ici->ops->set_fmt || + !ici->ops->set_bus_param || + !ici->ops->querycap || + !ici->ops->init_videobuf || + !ici->ops->reqbufs || + !ici->ops->add || + !ici->ops->remove || + !ici->ops->poll) return -EINVAL; /* Number might be equal to the platform device ID */ - sprintf(ici->dev.bus_id, "camera_host%d", ici->nr); + dev_set_name(&ici->dev, "camera_host%d", ici->nr); mutex_lock(&list_lock); list_for_each_entry(ix, &hosts, list) { @@ -847,7 +993,16 @@ int soc_camera_device_register(struct soc_camera_device *icd) struct soc_camera_device *ix; int num = -1, i; - if (!icd) + if (!icd || !icd->ops || + !icd->ops->probe || + !icd->ops->init || + !icd->ops->release || + !icd->ops->start_capture || + !icd->ops->stop_capture || + !icd->ops->set_fmt || + !icd->ops->try_fmt || + !icd->ops->query_bus_param || + !icd->ops->set_bus_param) return -EINVAL; for (i = 0; i < 256 && num < 0; i++) { @@ -867,10 +1022,12 @@ int soc_camera_device_register(struct soc_camera_device *icd) icd->devnum = num; icd->dev.bus = &soc_camera_bus_type; - snprintf(icd->dev.bus_id, sizeof(icd->dev.bus_id), - "%u-%u", icd->iface, icd->devnum); + dev_set_name(&icd->dev, "%u-%u", icd->iface, icd->devnum); - icd->dev.release = dummy_release; + icd->dev.release = dummy_release; + icd->use_count = 0; + icd->host_priv = NULL; + mutex_init(&icd->video_lock); return scan_add_device(icd); } @@ -917,6 +1074,10 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { #endif }; +/* + * Usually called from the struct soc_camera_ops .probe() method, i.e., from + * soc_camera_probe() above with .video_lock held + */ int soc_camera_video_start(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); @@ -932,7 +1093,7 @@ int soc_camera_video_start(struct soc_camera_device *icd) dev_dbg(&ici->dev, "Allocated video_device %p\n", vdev); strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name)); - /* Maybe better &ici->dev */ + vdev->parent = &icd->dev; vdev->current_norm = V4L2_STD_UNKNOWN; vdev->fops = &soc_camera_fops; @@ -941,8 +1102,6 @@ int soc_camera_video_start(struct soc_camera_device *icd) vdev->minor = -1; vdev->tvnorms = V4L2_STD_UNKNOWN, - icd->current_fmt = &icd->formats[0]; - err = video_register_device(vdev, VFL_TYPE_GRABBER, vdev->minor); if (err < 0) { dev_err(vdev->parent, "video_register_device failed\n"); @@ -968,10 +1127,10 @@ void soc_camera_video_stop(struct soc_camera_device *icd) if (!icd->dev.parent || !vdev) return; - mutex_lock(&video_lock); + mutex_lock(&icd->video_lock); video_unregister_device(vdev); icd->vdev = NULL; - mutex_unlock(&video_lock); + mutex_unlock(&icd->video_lock); } EXPORT_SYMBOL(soc_camera_video_stop); diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c index bb7a9d4..013ab06 100644 --- a/drivers/media/video/soc_camera_platform.c +++ b/drivers/media/video/soc_camera_platform.c @@ -79,19 +79,20 @@ soc_camera_platform_query_bus_param(struct soc_camera_device *icd) return p->bus_param; } -static int soc_camera_platform_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int soc_camera_platform_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { return 0; } -static int soc_camera_platform_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int soc_camera_platform_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); + struct v4l2_pix_format *pix = &f->fmt.pix; - f->fmt.pix.width = p->format.width; - f->fmt.pix.height = p->format.height; + pix->width = p->format.width; + pix->height = p->format.height; return 0; } @@ -124,8 +125,8 @@ static struct soc_camera_ops soc_camera_platform_ops = { .release = soc_camera_platform_release, .start_capture = soc_camera_platform_start_capture, .stop_capture = soc_camera_platform_stop_capture, - .set_fmt_cap = soc_camera_platform_set_fmt_cap, - .try_fmt_cap = soc_camera_platform_try_fmt_cap, + .set_fmt = soc_camera_platform_set_fmt, + .try_fmt = soc_camera_platform_try_fmt, .set_bus_param = soc_camera_platform_set_bus_param, .query_bus_param = soc_camera_platform_query_bus_param, }; diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index e9eb6d7..f9516d0 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -1262,6 +1262,25 @@ static int stk_vidioc_g_parm(struct file *filp, return 0; } +static int stk_vidioc_enum_framesizes(struct file *filp, + void *priv, struct v4l2_frmsizeenum *frms) +{ + if (frms->index >= ARRAY_SIZE(stk_sizes)) + return -EINVAL; + switch (frms->pixel_format) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_SBGGR8: + frms->type = V4L2_FRMSIZE_TYPE_DISCRETE; + frms->discrete.width = stk_sizes[frms->index].w; + frms->discrete.height = stk_sizes[frms->index].h; + return 0; + default: return -EINVAL; + } +} + static struct file_operations v4l_stk_fops = { .owner = THIS_MODULE, .open = v4l_stk_open, @@ -1296,6 +1315,7 @@ static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = { .vidioc_g_ctrl = stk_vidioc_g_ctrl, .vidioc_s_ctrl = stk_vidioc_s_ctrl, .vidioc_g_parm = stk_vidioc_g_parm, + .vidioc_enum_framesizes = stk_vidioc_enum_framesizes, }; static void stk_v4l_dev_release(struct video_device *vd) @@ -1376,12 +1396,9 @@ static int stk_camera_probe(struct usb_interface *interface, endpoint = &iface_desc->endpoint[i].desc; if (!dev->isoc_ep - && ((endpoint->bEndpointAddress - & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) - && ((endpoint->bmAttributes - & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC)) { + && usb_endpoint_is_isoc_in(endpoint)) { /* we found an isoc in endpoint */ - dev->isoc_ep = (endpoint->bEndpointAddress & 0xF); + dev->isoc_ep = usb_endpoint_num(endpoint); break; } } diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c index 328c41b..42acc92 100644 --- a/drivers/media/video/stv680.c +++ b/drivers/media/video/stv680.c @@ -1132,8 +1132,7 @@ static int stv_close (struct inode *inode, struct file *file) return 0; } -static int stv680_do_ioctl (struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int stv680_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = file->private_data; struct usb_stv *stv680 = video_get_drvdata(vdev); @@ -1303,7 +1302,7 @@ static int stv680_do_ioctl (struct inode *inode, struct file *file, static int stv680_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, stv680_do_ioctl); + return video_usercopy(file, cmd, arg, stv680_do_ioctl); } static int stv680_mmap (struct file *file, struct vm_area_struct *vma) diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index 4963d42..0c02058 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -47,9 +47,10 @@ #include <linux/videodev2.h> #include <linux/i2c.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/i2c-addr.h> +#include <media/v4l2-i2c-drv-legacy.h> #ifndef VIDEO_AUDIO_BALANCE # define VIDEO_AUDIO_BALANCE 32 @@ -79,6 +80,7 @@ I2C_CLIENT_INSMOD; /* Structure of address and subaddresses for the tda7432 */ struct tda7432 { + struct v4l2_subdev sd; int addr; int input; int volume; @@ -86,10 +88,12 @@ struct tda7432 { int bass, treble; int lf, lr, rf, rr; int loud; - struct i2c_client c; }; -static struct i2c_driver driver; -static struct i2c_client client_template; + +static inline struct tda7432 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tda7432, sd); +} /* The TDA7432 is made by STS-Thompson * http://www.st.com @@ -224,32 +228,33 @@ static struct i2c_client client_template; /* Begin code */ -static int tda7432_write(struct i2c_client *client, int subaddr, int val) +static int tda7432_write(struct v4l2_subdev *sd, int subaddr, int val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); unsigned char buffer[2]; - v4l_dbg(2, debug,client,"In tda7432_write\n"); - v4l_dbg(1, debug,client,"Writing %d 0x%x\n", subaddr, val); + + v4l2_dbg(2, debug, sd, "In tda7432_write\n"); + v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val); buffer[0] = subaddr; buffer[1] = val; - if (2 != i2c_master_send(client,buffer,2)) { - v4l_err(client,"I/O error, trying (write %d 0x%x)\n", + if (2 != i2c_master_send(client, buffer, 2)) { + v4l2_err(sd, "I/O error, trying (write %d 0x%x)\n", subaddr, val); return -1; } return 0; } -/* I don't think we ever actually _read_ the chip... */ - -static int tda7432_set(struct i2c_client *client) +static int tda7432_set(struct v4l2_subdev *sd) { - struct tda7432 *t = i2c_get_clientdata(client); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tda7432 *t = to_state(sd); unsigned char buf[16]; - v4l_dbg(2, debug,client,"In tda7432_set\n"); - v4l_dbg(1, debug,client, + v4l2_dbg(1, debug, sd, "tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", - t->input,t->volume,t->bass,t->treble,t->lf,t->lr,t->rf,t->rr,t->loud); + t->input, t->volume, t->bass, t->treble, t->lf, t->lr, + t->rf, t->rr, t->loud); buf[0] = TDA7432_IN; buf[1] = t->input; buf[2] = t->volume; @@ -260,18 +265,19 @@ static int tda7432_set(struct i2c_client *client) buf[7] = t->rf; buf[8] = t->rr; buf[9] = t->loud; - if (10 != i2c_master_send(client,buf,10)) { - v4l_err(client,"I/O error, trying tda7432_set\n"); + if (10 != i2c_master_send(client, buf, 10)) { + v4l2_err(sd, "I/O error, trying tda7432_set\n"); return -1; } return 0; } -static void do_tda7432_init(struct i2c_client *client) +static void do_tda7432_init(struct v4l2_subdev *sd) { - struct tda7432 *t = i2c_get_clientdata(client); - v4l_dbg(2, debug,client,"In tda7432_init\n"); + struct tda7432 *t = to_state(sd); + + v4l2_dbg(2, debug, sd, "In tda7432_init\n"); t->input = TDA7432_STEREO_IN | /* Main (stereo) input */ TDA7432_BASS_SYM | /* Symmetric bass cut */ @@ -288,57 +294,12 @@ static void do_tda7432_init(struct i2c_client *client) t->rr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ t->loud = loudness; /* insmod parameter */ - tda7432_set(client); + tda7432_set(sd); } -/* *********************** * - * i2c interface functions * - * *********************** */ - -static int tda7432_attach(struct i2c_adapter *adap, int addr, int kind) +static int tda7432_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct tda7432 *t; - struct i2c_client *client; - - t = kzalloc(sizeof *t,GFP_KERNEL); - if (!t) - return -ENOMEM; - - client = &t->c; - memcpy(client,&client_template,sizeof(struct i2c_client)); - client->adapter = adap; - client->addr = addr; - i2c_set_clientdata(client, t); - - do_tda7432_init(client); - i2c_attach_client(client); - - v4l_info(client, "chip found @ 0x%x (%s)\n", addr << 1, adap->name); - return 0; -} - -static int tda7432_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, tda7432_attach); - return 0; -} - -static int tda7432_detach(struct i2c_client *client) -{ - struct tda7432 *t = i2c_get_clientdata(client); - - do_tda7432_init(client); - i2c_detach_client(client); - - kfree(t); - return 0; -} - -static int tda7432_get_ctrl(struct i2c_client *client, - struct v4l2_control *ctrl) -{ - struct tda7432 *t = i2c_get_clientdata(client); + struct tda7432 *t = to_state(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -382,10 +343,9 @@ static int tda7432_get_ctrl(struct i2c_client *client, return -EINVAL; } -static int tda7432_set_ctrl(struct i2c_client *client, - struct v4l2_control *ctrl) +static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct tda7432 *t = i2c_get_clientdata(client); + struct tda7432 *t = to_state(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -400,7 +360,7 @@ static int tda7432_set_ctrl(struct i2c_client *client, if (loudness) /* Turn on the loudness bit */ t->volume |= TDA7432_LD_ON; - tda7432_write(client,TDA7432_VL, t->volume); + tda7432_write(sd, TDA7432_VL, t->volume); return 0; case V4L2_CID_AUDIO_BALANCE: if (ctrl->value < 32768) { @@ -428,14 +388,14 @@ static int tda7432_set_ctrl(struct i2c_client *client, if(t->bass>= 0x8) t->bass = (~t->bass & 0xf) + 0x8 ; - tda7432_write(client,TDA7432_TN, 0x10 | (t->bass << 4) | t->treble ); + tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); return 0; case V4L2_CID_AUDIO_TREBLE: t->treble= ctrl->value >> 12; if(t->treble>= 0x8) t->treble = (~t->treble & 0xf) + 0x8 ; - tda7432_write(client,TDA7432_TN, 0x10 | (t->bass << 4) | t->treble ); + tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); return 0; default: return -EINVAL; @@ -445,92 +405,102 @@ static int tda7432_set_ctrl(struct i2c_client *client, if (t->muted) { /* Mute & update balance*/ - tda7432_write(client,TDA7432_LF, t->lf | TDA7432_MUTE); - tda7432_write(client,TDA7432_LR, t->lr | TDA7432_MUTE); - tda7432_write(client,TDA7432_RF, t->rf | TDA7432_MUTE); - tda7432_write(client,TDA7432_RR, t->rr | TDA7432_MUTE); + tda7432_write(sd, TDA7432_LF, t->lf | TDA7432_MUTE); + tda7432_write(sd, TDA7432_LR, t->lr | TDA7432_MUTE); + tda7432_write(sd, TDA7432_RF, t->rf | TDA7432_MUTE); + tda7432_write(sd, TDA7432_RR, t->rr | TDA7432_MUTE); } else { - tda7432_write(client,TDA7432_LF, t->lf); - tda7432_write(client,TDA7432_LR, t->lr); - tda7432_write(client,TDA7432_RF, t->rf); - tda7432_write(client,TDA7432_RR, t->rr); + tda7432_write(sd, TDA7432_LF, t->lf); + tda7432_write(sd, TDA7432_LR, t->lr); + tda7432_write(sd, TDA7432_RF, t->rf); + tda7432_write(sd, TDA7432_RR, t->rr); } return 0; } -static int tda7432_command(struct i2c_client *client, - unsigned int cmd, void *arg) +static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { - v4l_dbg(2, debug,client,"In tda7432_command\n"); - if (debug>1) - v4l_i2c_print_ioctl(client,cmd); - - switch (cmd) { - /* --- v4l ioctls --- */ - /* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - default: - return -EINVAL; - } + switch (qc->id) { + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: return v4l2_ctrl_query_fill_std(qc); } - case VIDIOC_S_CTRL: - return tda7432_set_ctrl(client, arg); - - case VIDIOC_G_CTRL: - return tda7432_get_ctrl(client, arg); - - } /* end of (cmd) switch */ + return -EINVAL; +} - return 0; +static int tda7432_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); } -static struct i2c_driver driver = { - .driver = { - .name = "tda7432", - }, - .id = I2C_DRIVERID_TDA7432, - .attach_adapter = tda7432_probe, - .detach_client = tda7432_detach, - .command = tda7432_command, +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tda7432_core_ops = { + .queryctrl = tda7432_queryctrl, + .g_ctrl = tda7432_g_ctrl, + .s_ctrl = tda7432_s_ctrl, }; -static struct i2c_client client_template = -{ - .name = "tda7432", - .driver = &driver, +static const struct v4l2_subdev_ops tda7432_ops = { + .core = &tda7432_core_ops, }; -static int __init tda7432_init(void) +/* ----------------------------------------------------------------------- */ + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda7432_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - if ( (loudness < 0) || (loudness > 15) ) { - printk(KERN_ERR "loudness parameter must be between 0 and 15\n"); - return -EINVAL; + struct tda7432 *t; + struct v4l2_subdev *sd; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return -ENOMEM; + sd = &t->sd; + v4l2_i2c_subdev_init(sd, client, &tda7432_ops); + if (loudness < 0 || loudness > 15) { + v4l2_warn(sd, "loudness parameter must be between 0 and 15\n"); + if (loudness < 0) + loudness = 0; + if (loudness > 15) + loudness = 15; } - return i2c_add_driver(&driver); + do_tda7432_init(sd); + return 0; } -static void __exit tda7432_fini(void) +static int tda7432_remove(struct i2c_client *client) { - i2c_del_driver(&driver); -} + struct v4l2_subdev *sd = i2c_get_clientdata(client); -module_init(tda7432_init); -module_exit(tda7432_fini); + do_tda7432_init(sd); + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ +static const struct i2c_device_id tda7432_id[] = { + { "tda7432", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tda7432_id); + +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "tda7432", + .driverid = I2C_DRIVERID_TDA7432, + .command = tda7432_command, + .probe = tda7432_probe, + .remove = tda7432_remove, + .id_table = tda7432_id, +}; diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c index 1c391f0..2644e0d 100644 --- a/drivers/media/video/tda9840.c +++ b/drivers/media/video/tda9840.c @@ -29,7 +29,7 @@ #include <linux/module.h> #include <linux/ioctl.h> #include <linux/i2c.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-i2c-drv-legacy.h> #include "tda9840.h" @@ -62,85 +62,89 @@ static unsigned short normal_i2c[] = { I2C_ADDR_TDA9840, I2C_CLIENT_END }; /* magic definition of all other variables and things */ I2C_CLIENT_INSMOD; -static void tda9840_write(struct i2c_client *client, u8 reg, u8 val) +static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + if (i2c_smbus_write_byte_data(client, reg, val)) - v4l_dbg(1, debug, client, "error writing %02x to %02x\n", + v4l2_dbg(1, debug, sd, "error writing %02x to %02x\n", val, reg); } -static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg) +static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) { - int byte = *(int *)arg; - - switch (cmd) { - case VIDIOC_S_TUNER: { - struct v4l2_tuner *t = arg; - int byte; + int byte; - if (t->index) - return -EINVAL; + if (t->index) + return -EINVAL; - switch (t->audmode) { - case V4L2_TUNER_MODE_STEREO: - byte = TDA9840_SET_STEREO; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - byte = TDA9840_SET_BOTH; - break; - case V4L2_TUNER_MODE_LANG1: - byte = TDA9840_SET_LANG1; - break; - case V4L2_TUNER_MODE_LANG2: - byte = TDA9840_SET_LANG2; - break; - default: - byte = TDA9840_SET_MONO; - break; - } - v4l_dbg(1, debug, client, "TDA9840_SWITCH: 0x%02x\n", byte); - tda9840_write(client, SWITCH, byte); + switch (t->audmode) { + case V4L2_TUNER_MODE_STEREO: + byte = TDA9840_SET_STEREO; break; + case V4L2_TUNER_MODE_LANG1_LANG2: + byte = TDA9840_SET_BOTH; + break; + case V4L2_TUNER_MODE_LANG1: + byte = TDA9840_SET_LANG1; + break; + case V4L2_TUNER_MODE_LANG2: + byte = TDA9840_SET_LANG2; + break; + default: + byte = TDA9840_SET_MONO; + break; + } + v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte); + tda9840_write(sd, SWITCH, byte); + return 0; +} + +static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 byte; + + t->rxsubchans = V4L2_TUNER_SUB_MONO; + if (1 != i2c_master_recv(client, &byte, 1)) { + v4l2_dbg(1, debug, sd, + "i2c_master_recv() failed\n"); + return -EIO; + } + + if (byte & 0x80) { + v4l2_dbg(1, debug, sd, + "TDA9840_DETECT: register contents invalid\n"); + return -EINVAL; } - case VIDIOC_G_TUNER: { - struct v4l2_tuner *t = arg; - u8 byte; + v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte); + switch (byte & 0x60) { + case 0x00: t->rxsubchans = V4L2_TUNER_SUB_MONO; - if (1 != i2c_master_recv(client, &byte, 1)) { - v4l_dbg(1, debug, client, - "i2c_master_recv() failed\n"); - return -EIO; - } - - if (byte & 0x80) { - v4l_dbg(1, debug, client, - "TDA9840_DETECT: register contents invalid\n"); - return -EINVAL; - } - - v4l_dbg(1, debug, client, "TDA9840_DETECT: byte: 0x%02x\n", byte); - - switch (byte & 0x60) { - case 0x00: - t->rxsubchans = V4L2_TUNER_SUB_MONO; - break; - case 0x20: - t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - break; - case 0x40: - t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; - break; - default: /* Incorrect detect */ - t->rxsubchans = V4L2_TUNER_MODE_MONO; - break; - } + break; + case 0x20: + t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + break; + case 0x40: + t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; + break; + default: /* Incorrect detect */ + t->rxsubchans = V4L2_TUNER_MODE_MONO; break; } + return 0; +} +static int tda9840_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg) +{ + int byte; + + switch (cmd) { case TDA9840_LEVEL_ADJUST: - v4l_dbg(1, debug, client, "TDA9840_LEVEL_ADJUST: %d\n", byte); + byte = *(int *)arg; + v4l2_dbg(1, debug, sd, "TDA9840_LEVEL_ADJUST: %d\n", byte); /* check for correct range */ if (byte > 25 || byte < -20) @@ -152,11 +156,12 @@ static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg) byte += 0x8; else byte = -byte; - tda9840_write(client, LEVEL_ADJUST, byte); + tda9840_write(sd, LEVEL_ADJUST, byte); break; case TDA9840_STEREO_ADJUST: - v4l_dbg(1, debug, client, "TDA9840_STEREO_ADJUST: %d\n", byte); + byte = *(int *)arg; + v4l2_dbg(1, debug, sd, "TDA9840_STEREO_ADJUST: %d\n", byte); /* check for correct range */ if (byte > 25 || byte < -24) @@ -169,18 +174,41 @@ static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg) else byte = -byte; - tda9840_write(client, STEREO_ADJUST, byte); + tda9840_write(sd, STEREO_ADJUST, byte); break; default: return -ENOIOCTLCMD; } - return 0; } +static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tda9840_core_ops = { + .ioctl = tda9840_ioctl, +}; + +static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = { + .s_tuner = tda9840_s_tuner, + .g_tuner = tda9840_g_tuner, +}; + +static const struct v4l2_subdev_ops tda9840_ops = { + .core = &tda9840_core_ops, + .tuner = &tda9840_tuner_ops, +}; + +/* ----------------------------------------------------------------------- */ + static int tda9840_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_subdev *sd; int result; int byte; @@ -188,23 +216,38 @@ static int tda9840_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return 0; + return -EIO; v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); + sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &tda9840_ops); + /* set initial values for level & stereo - adjustment, mode */ byte = 0; - result = tda9840_command(client, TDA9840_LEVEL_ADJUST, &byte); - result += tda9840_command(client, TDA9840_STEREO_ADJUST, &byte); - tda9840_write(client, SWITCH, TDA9840_SET_STEREO); + result = tda9840_ioctl(sd, TDA9840_LEVEL_ADJUST, &byte); + result |= tda9840_ioctl(sd, TDA9840_STEREO_ADJUST, &byte); + tda9840_write(sd, SWITCH, TDA9840_SET_STEREO); if (result) { - v4l_dbg(1, debug, client, "could not initialize tda9840\n"); + v4l2_dbg(1, debug, sd, "could not initialize tda9840\n"); + kfree(sd); return -ENODEV; } return 0; } +static int tda9840_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + return 0; +} + static int tda9840_legacy_probe(struct i2c_adapter *adapter) { /* Let's see whether this is a known adapter we can attach to. @@ -222,6 +265,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .driverid = I2C_DRIVERID_TDA9840, .command = tda9840_command, .probe = tda9840_probe, + .remove = tda9840_remove, .legacy_probe = tda9840_legacy_probe, .id_table = tda9840_id, }; diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c index 792f0b0..56f0c0e 100644 --- a/drivers/media/video/tda9875.c +++ b/drivers/media/video/tda9875.c @@ -25,11 +25,10 @@ #include <linux/delay.h> #include <linux/errno.h> #include <linux/slab.h> -#include <linux/videodev2.h> -#include <media/v4l2-common.h> #include <linux/i2c.h> -#include <linux/init.h> - +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-i2c-drv-legacy.h> #include <media/i2c-addr.h> static int debug; /* insmod parameter */ @@ -46,13 +45,15 @@ I2C_CLIENT_INSMOD; /* This is a superset of the TDA9875 */ struct tda9875 { + struct v4l2_subdev sd; int rvol, lvol; int bass, treble; - struct i2c_client c; }; -static struct i2c_driver driver; -static struct i2c_client client_template; +static inline struct tda9875 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tda9875, sd); +} #define dprintk if (debug) printk @@ -105,15 +106,16 @@ static struct i2c_client client_template; /* Begin code */ -static int tda9875_write(struct i2c_client *client, int subaddr, unsigned char val) +static int tda9875_write(struct v4l2_subdev *sd, int subaddr, unsigned char val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); unsigned char buffer[2]; - dprintk("In tda9875_write\n"); - dprintk("Writing %d 0x%x\n", subaddr, val); + + v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val); buffer[0] = subaddr; buffer[1] = val; - if (2 != i2c_master_send(client,buffer,2)) { - printk(KERN_WARNING "tda9875: I/O error, trying (write %d 0x%x)\n", + if (2 != i2c_master_send(client, buffer, 2)) { + v4l2_warn(sd, "I/O error, trying (write %d 0x%x)\n", subaddr, val); return -1; } @@ -121,7 +123,7 @@ static int tda9875_write(struct i2c_client *client, int subaddr, unsigned char v } -static int i2c_read_register(struct i2c_adapter *adap, int addr, int reg) +static int i2c_read_register(struct i2c_client *client, int addr, int reg) { unsigned char write[1]; unsigned char read[1]; @@ -129,150 +131,83 @@ static int i2c_read_register(struct i2c_adapter *adap, int addr, int reg) { addr, 0, 1, write }, { addr, I2C_M_RD, 1, read } }; + write[0] = reg; - if (2 != i2c_transfer(adap,msgs,2)) { - printk(KERN_WARNING "tda9875: I/O error (read2)\n"); + if (2 != i2c_transfer(client->adapter, msgs, 2)) { + v4l_warn(client, "I/O error (read2)\n"); return -1; } - dprintk("tda9875: chip_read2: reg%d=0x%x\n",reg,read[0]); + v4l_dbg(1, debug, client, "chip_read2: reg%d=0x%x\n", reg, read[0]); return read[0]; } -static void tda9875_set(struct i2c_client *client) +static void tda9875_set(struct v4l2_subdev *sd) { - struct tda9875 *tda = i2c_get_clientdata(client); + struct tda9875 *tda = to_state(sd); unsigned char a; - dprintk(KERN_DEBUG "tda9875_set(%04x,%04x,%04x,%04x)\n", - tda->lvol,tda->rvol,tda->bass,tda->treble); - + v4l2_dbg(1, debug, sd, "tda9875_set(%04x,%04x,%04x,%04x)\n", + tda->lvol, tda->rvol, tda->bass, tda->treble); a = tda->lvol & 0xff; - tda9875_write(client, TDA9875_MVL, a); + tda9875_write(sd, TDA9875_MVL, a); a =tda->rvol & 0xff; - tda9875_write(client, TDA9875_MVR, a); + tda9875_write(sd, TDA9875_MVR, a); a =tda->bass & 0xff; - tda9875_write(client, TDA9875_MBA, a); + tda9875_write(sd, TDA9875_MBA, a); a =tda->treble & 0xff; - tda9875_write(client, TDA9875_MTR, a); + tda9875_write(sd, TDA9875_MTR, a); } -static void do_tda9875_init(struct i2c_client *client) +static void do_tda9875_init(struct v4l2_subdev *sd) { - struct tda9875 *t = i2c_get_clientdata(client); - dprintk("In tda9875_init\n"); - tda9875_write(client, TDA9875_CFG, 0xd0 ); /*reg de config 0 (reset)*/ - tda9875_write(client, TDA9875_MSR, 0x03 ); /* Monitor 0b00000XXX*/ - tda9875_write(client, TDA9875_C1MSB, 0x00 ); /*Car1(FM) MSB XMHz*/ - tda9875_write(client, TDA9875_C1MIB, 0x00 ); /*Car1(FM) MIB XMHz*/ - tda9875_write(client, TDA9875_C1LSB, 0x00 ); /*Car1(FM) LSB XMHz*/ - tda9875_write(client, TDA9875_C2MSB, 0x00 ); /*Car2(NICAM) MSB XMHz*/ - tda9875_write(client, TDA9875_C2MIB, 0x00 ); /*Car2(NICAM) MIB XMHz*/ - tda9875_write(client, TDA9875_C2LSB, 0x00 ); /*Car2(NICAM) LSB XMHz*/ - tda9875_write(client, TDA9875_DCR, 0x00 ); /*Demod config 0x00*/ - tda9875_write(client, TDA9875_DEEM, 0x44 ); /*DE-Emph 0b0100 0100*/ - tda9875_write(client, TDA9875_FMAT, 0x00 ); /*FM Matrix reg 0x00*/ - tda9875_write(client, TDA9875_SC1, 0x00 ); /* SCART 1 (SC1)*/ - tda9875_write(client, TDA9875_SC2, 0x01 ); /* SCART 2 (sc2)*/ - - tda9875_write(client, TDA9875_CH1V, 0x10 ); /* Channel volume 1 mute*/ - tda9875_write(client, TDA9875_CH2V, 0x10 ); /* Channel volume 2 mute */ - tda9875_write(client, TDA9875_DACOS, 0x02 ); /* sig DAC i/o(in:nicam)*/ - tda9875_write(client, TDA9875_ADCIS, 0x6f ); /* sig ADC input(in:mono)*/ - tda9875_write(client, TDA9875_LOSR, 0x00 ); /* line out (in:mono)*/ - tda9875_write(client, TDA9875_AER, 0x00 ); /*06 Effect (AVL+PSEUDO) */ - tda9875_write(client, TDA9875_MCS, 0x44 ); /* Main ch select (DAC) */ - tda9875_write(client, TDA9875_MVL, 0x03 ); /* Vol Main left 10dB */ - tda9875_write(client, TDA9875_MVR, 0x03 ); /* Vol Main right 10dB*/ - tda9875_write(client, TDA9875_MBA, 0x00 ); /* Main Bass Main 0dB*/ - tda9875_write(client, TDA9875_MTR, 0x00 ); /* Main Treble Main 0dB*/ - tda9875_write(client, TDA9875_ACS, 0x44 ); /* Aux chan select (dac)*/ - tda9875_write(client, TDA9875_AVL, 0x00 ); /* Vol Aux left 0dB*/ - tda9875_write(client, TDA9875_AVR, 0x00 ); /* Vol Aux right 0dB*/ - tda9875_write(client, TDA9875_ABA, 0x00 ); /* Aux Bass Main 0dB*/ - tda9875_write(client, TDA9875_ATR, 0x00 ); /* Aux Aigus Main 0dB*/ - - tda9875_write(client, TDA9875_MUT, 0xcc ); /* General mute */ - - t->lvol=t->rvol =0; /* 0dB */ - t->bass=0; /* 0dB */ - t->treble=0; /* 0dB */ - tda9875_set(client); - + struct tda9875 *t = to_state(sd); + + v4l2_dbg(1, debug, sd, "In tda9875_init\n"); + tda9875_write(sd, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/ + tda9875_write(sd, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/ + tda9875_write(sd, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/ + tda9875_write(sd, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/ + tda9875_write(sd, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/ + tda9875_write(sd, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/ + tda9875_write(sd, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/ + tda9875_write(sd, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/ + tda9875_write(sd, TDA9875_DCR, 0x00); /*Demod config 0x00*/ + tda9875_write(sd, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/ + tda9875_write(sd, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/ + tda9875_write(sd, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/ + tda9875_write(sd, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/ + + tda9875_write(sd, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/ + tda9875_write(sd, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */ + tda9875_write(sd, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/ + tda9875_write(sd, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/ + tda9875_write(sd, TDA9875_LOSR, 0x00); /* line out (in:mono)*/ + tda9875_write(sd, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */ + tda9875_write(sd, TDA9875_MCS, 0x44); /* Main ch select (DAC) */ + tda9875_write(sd, TDA9875_MVL, 0x03); /* Vol Main left 10dB */ + tda9875_write(sd, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/ + tda9875_write(sd, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/ + tda9875_write(sd, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/ + tda9875_write(sd, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/ + tda9875_write(sd, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/ + tda9875_write(sd, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/ + tda9875_write(sd, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/ + tda9875_write(sd, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/ + + tda9875_write(sd, TDA9875_MUT, 0xcc); /* General mute */ + + t->lvol = t->rvol = 0; /* 0dB */ + t->bass = 0; /* 0dB */ + t->treble = 0; /* 0dB */ + tda9875_set(sd); } -/* *********************** * - * i2c interface functions * - * *********************** */ - -static int tda9875_checkit(struct i2c_adapter *adap, int addr) -{ - int dic,rev; - - dic=i2c_read_register(adap,addr,254); - rev=i2c_read_register(adap,addr,255); - - if(dic==0 || dic==2) { // tda9875 and tda9875A - printk("tda9875: TDA9875%s Rev.%d detected at 0x%x\n", - dic==0?"":"A", rev,addr<<1); - return 1; - } - printk("tda9875: no such chip at 0x%x (dic=0x%x rev=0x%x)\n",addr<<1,dic,rev); - return(0); -} - -static int tda9875_attach(struct i2c_adapter *adap, int addr, int kind) +static int tda9875_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct tda9875 *t; - struct i2c_client *client; - dprintk("In tda9875_attach\n"); - - t = kzalloc(sizeof *t,GFP_KERNEL); - if (!t) - return -ENOMEM; - - client = &t->c; - memcpy(client,&client_template,sizeof(struct i2c_client)); - client->adapter = adap; - client->addr = addr; - i2c_set_clientdata(client, t); - - if(!tda9875_checkit(adap,addr)) { - kfree(t); - return 1; - } - - do_tda9875_init(client); - printk(KERN_INFO "tda9875: init\n"); - - i2c_attach_client(client); - return 0; -} - -static int tda9875_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, tda9875_attach); - return 0; -} - -static int tda9875_detach(struct i2c_client *client) -{ - struct tda9875 *t = i2c_get_clientdata(client); - - do_tda9875_init(client); - i2c_detach_client(client); - - kfree(t); - return 0; -} - -static int tda9875_get_ctrl(struct i2c_client *client, - struct v4l2_control *ctrl) -{ - struct tda9875 *t = i2c_get_clientdata(client); + struct tda9875 *t = to_state(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -304,10 +239,9 @@ static int tda9875_get_ctrl(struct i2c_client *client, return -EINVAL; } -static int tda9875_set_ctrl(struct i2c_client *client, - struct v4l2_control *ctrl) +static int tda9875_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct tda9875 *t = i2c_get_clientdata(client); + struct tda9875 *t = to_state(sd); int chvol=0, volume, balance, left, right; switch (ctrl->id) { @@ -371,85 +305,105 @@ static int tda9875_set_ctrl(struct i2c_client *client, t->rvol = -84 & 0xff; } -//printk("tda9875 bal:%04x vol:%04x bass:%04x treble:%04x\n",va->balance,va->volume,va->bass,va->treble); - - tda9875_set(client); - + tda9875_set(sd); return 0; } - -static int tda9875_command(struct i2c_client *client, - unsigned int cmd, void *arg) +static int tda9875_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { - dprintk("In tda9875_command...\n"); - - switch (cmd) { - /* --- v4l ioctls --- */ - /* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - default: - return -EINVAL; - } + switch (qc->id) { + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: return v4l2_ctrl_query_fill_std(qc); } - case VIDIOC_S_CTRL: - return tda9875_set_ctrl(client, arg); + return -EINVAL; +} - case VIDIOC_G_CTRL: - return tda9875_get_ctrl(client, arg); +static int tda9875_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} - default: /* Not VIDEOCGAUDIO or VIDEOCSAUDIO */ +/* ----------------------------------------------------------------------- */ - /* nothing */ - dprintk("Default\n"); +static const struct v4l2_subdev_core_ops tda9875_core_ops = { + .queryctrl = tda9875_queryctrl, + .g_ctrl = tda9875_g_ctrl, + .s_ctrl = tda9875_s_ctrl, +}; - } /* end of (cmd) switch */ +static const struct v4l2_subdev_ops tda9875_ops = { + .core = &tda9875_core_ops, +}; - return 0; -} +/* ----------------------------------------------------------------------- */ -static struct i2c_driver driver = { - .driver = { - .name = "tda9875", - }, - .id = I2C_DRIVERID_TDA9875, - .attach_adapter = tda9875_probe, - .detach_client = tda9875_detach, - .command = tda9875_command, -}; +/* *********************** * + * i2c interface functions * + * *********************** */ -static struct i2c_client client_template = +static int tda9875_checkit(struct i2c_client *client, int addr) { - .name = "tda9875", - .driver = &driver, -}; + int dic, rev; -static int __init tda9875_init(void) -{ - return i2c_add_driver(&driver); + dic = i2c_read_register(client, addr, 254); + rev = i2c_read_register(client, addr, 255); + + if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */ + v4l_info(client, "tda9875%s rev. %d detected at 0x%02x\n", + dic == 0 ? "" : "A", rev, addr << 1); + return 1; + } + v4l_info(client, "no such chip at 0x%02x (dic=0x%x rev=0x%x)\n", + addr << 1, dic, rev); + return 0; } -static void __exit tda9875_fini(void) +static int tda9875_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - i2c_del_driver(&driver); + struct tda9875 *t; + struct v4l2_subdev *sd; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + if (!tda9875_checkit(client, client->addr)) + return -ENODEV; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return -ENOMEM; + sd = &t->sd; + v4l2_i2c_subdev_init(sd, client, &tda9875_ops); + + do_tda9875_init(sd); + return 0; } -module_init(tda9875_init); -module_exit(tda9875_fini); +static int tda9875_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ + do_tda9875_init(sd); + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} +static const struct i2c_device_id tda9875_id[] = { + { "tda9875", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tda9875_id); + +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "tda9875", + .driverid = I2C_DRIVERID_TDA9875, + .command = tda9875_command, + .probe = tda9875_probe, + .remove = tda9875_remove, + .id_table = tda9875_id, +}; diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c index cde092a..31dde86 100644 --- a/drivers/media/video/tea6415c.c +++ b/drivers/media/video/tea6415c.c @@ -31,7 +31,7 @@ #include <linux/module.h> #include <linux/ioctl.h> #include <linux/i2c.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-i2c-drv-legacy.h> #include "tea6415c.h" @@ -122,31 +122,57 @@ static int switch_matrix(struct i2c_client *client, int i, int o) return ret; } -static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg) +static int tea6415c_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg) { - struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg; - int result = 0; + if (cmd == TEA6415C_SWITCH) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg; - switch (cmd) { - case TEA6415C_SWITCH: - result = switch_matrix(client, v->in, v->out); - break; - default: - return -ENOIOCTLCMD; + return switch_matrix(client, v->in, v->out); } - return result; + return -ENOIOCTLCMD; } +static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tea6415c_core_ops = { + .ioctl = tea6415c_ioctl, +}; + +static const struct v4l2_subdev_ops tea6415c_ops = { + .core = &tea6415c_core_ops, +}; + /* this function is called by i2c_probe */ static int tea6415c_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_subdev *sd; + /* let's see whether this adapter can support what we need */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) return 0; v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); + sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &tea6415c_ops); + return 0; +} + +static int tea6415c_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); return 0; } @@ -168,6 +194,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .driverid = I2C_DRIVERID_TEA6415C, .command = tea6415c_command, .probe = tea6415c_probe, + .remove = tea6415c_remove, .legacy_probe = tea6415c_legacy_probe, .id_table = tea6415c_id, }; diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c index e508209..38e519f 100644 --- a/drivers/media/video/tea6420.c +++ b/drivers/media/video/tea6420.c @@ -31,7 +31,7 @@ #include <linux/module.h> #include <linux/ioctl.h> #include <linux/i2c.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-i2c-drv-legacy.h> #include "tea6420.h" @@ -90,26 +90,37 @@ static int tea6420_switch(struct i2c_client *client, int i, int o, int g) return 0; } -static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg) +static int tea6420_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg) { - struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg; - int result = 0; + if (cmd == TEA6420_SWITCH) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg; - switch (cmd) { - case TEA6420_SWITCH: - result = tea6420_switch(client, a->in, a->out, a->gain); - break; - default: - return -ENOIOCTLCMD; + return tea6420_switch(client, a->in, a->out, a->gain); } + return -ENOIOCTLCMD; +} - return result; +static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); } +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tea6420_core_ops = { + .ioctl = tea6420_ioctl, +}; + +static const struct v4l2_subdev_ops tea6420_ops = { + .core = &tea6420_core_ops, +}; + /* this function is called by i2c_probe */ static int tea6420_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_subdev *sd; int err, i; /* let's see whether this adapter can support what we need */ @@ -126,9 +137,22 @@ static int tea6420_probe(struct i2c_client *client, } if (err) { v4l_dbg(1, debug, client, "could not initialize tea6420\n"); - kfree(client); return -ENODEV; } + + sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &tea6420_ops); + return 0; +} + +static int tea6420_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); return 0; } @@ -150,6 +174,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .driverid = I2C_DRIVERID_TEA6420, .command = tea6420_command, .probe = tea6420_probe, + .remove = tea6420_remove, .legacy_probe = tea6420_legacy_probe, .id_table = tea6420_id, }; diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c index 281065b..5c95ecd 100644 --- a/drivers/media/video/tlv320aic23b.c +++ b/drivers/media/video/tlv320aic23b.c @@ -30,7 +30,7 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-i2c-drv-legacy.h> MODULE_DESCRIPTION("tlv320aic23b driver"); @@ -44,15 +44,22 @@ I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ struct tlv320aic23b_state { + struct v4l2_subdev sd; u8 muted; }; -static int tlv320aic23b_write(struct i2c_client *client, int reg, u16 val) +static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd) { + return container_of(sd, struct tlv320aic23b_state, sd); +} + +static int tlv320aic23b_write(struct v4l2_subdev *sd, int reg, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); int i; if ((reg < 0 || reg > 9) && (reg != 15)) { - v4l_err(client, "Invalid register R%d\n", reg); + v4l2_err(sd, "Invalid register R%d\n", reg); return -1; } @@ -60,61 +67,82 @@ static int tlv320aic23b_write(struct i2c_client *client, int reg, u16 val) if (i2c_smbus_write_byte_data(client, (reg << 1) | (val >> 8), val & 0xff) == 0) return 0; - v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg); + v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); return -1; } -static int tlv320aic23b_command(struct i2c_client *client, - unsigned int cmd, void *arg) +static int tlv320aic23b_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { - struct tlv320aic23b_state *state = i2c_get_clientdata(client); - struct v4l2_control *ctrl = arg; - u32 *freq = arg; - - switch (cmd) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - switch (*freq) { - case 32000: /* set sample rate to 32 kHz */ - tlv320aic23b_write(client, 8, 0x018); - break; - case 44100: /* set sample rate to 44.1 kHz */ - tlv320aic23b_write(client, 8, 0x022); - break; - case 48000: /* set sample rate to 48 kHz */ - tlv320aic23b_write(client, 8, 0x000); - break; - default: - return -EINVAL; - } - break; - - case VIDIOC_G_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - ctrl->value = state->muted; + switch (freq) { + case 32000: /* set sample rate to 32 kHz */ + tlv320aic23b_write(sd, 8, 0x018); break; - - case VIDIOC_S_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - state->muted = ctrl->value; - tlv320aic23b_write(client, 0, 0x180); /* mute both channels */ - /* set gain on both channels to +3.0 dB */ - if (!state->muted) - tlv320aic23b_write(client, 0, 0x119); + case 44100: /* set sample rate to 44.1 kHz */ + tlv320aic23b_write(sd, 8, 0x022); break; - - case VIDIOC_LOG_STATUS: - v4l_info(client, "Input: %s\n", - state->muted ? "muted" : "active"); + case 48000: /* set sample rate to 48 kHz */ + tlv320aic23b_write(sd, 8, 0x000); break; - default: return -EINVAL; } return 0; } +static int tlv320aic23b_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct tlv320aic23b_state *state = to_state(sd); + + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + ctrl->value = state->muted; + return 0; +} + +static int tlv320aic23b_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct tlv320aic23b_state *state = to_state(sd); + + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + state->muted = ctrl->value; + tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */ + /* set gain on both channels to +3.0 dB */ + if (!state->muted) + tlv320aic23b_write(sd, 0, 0x119); + return 0; +} + +static int tlv320aic23b_log_status(struct v4l2_subdev *sd) +{ + struct tlv320aic23b_state *state = to_state(sd); + + v4l2_info(sd, "Input: %s\n", state->muted ? "muted" : "active"); + return 0; +} + +static int tlv320aic23b_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = { + .log_status = tlv320aic23b_log_status, + .g_ctrl = tlv320aic23b_g_ctrl, + .s_ctrl = tlv320aic23b_s_ctrl, +}; + +static const struct v4l2_subdev_audio_ops tlv320aic23b_audio_ops = { + .s_clock_freq = tlv320aic23b_s_clock_freq, +}; + +static const struct v4l2_subdev_ops tlv320aic23b_ops = { + .core = &tlv320aic23b_core_ops, + .audio = &tlv320aic23b_audio_ops, +}; + /* ----------------------------------------------------------------------- */ /* i2c implementation */ @@ -128,6 +156,7 @@ static int tlv320aic23b_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tlv320aic23b_state *state; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -136,32 +165,36 @@ static int tlv320aic23b_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - state = kmalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &tlv320aic23b_ops); state->muted = 0; - i2c_set_clientdata(client, state); /* Initialize tlv320aic23b */ /* RESET */ - tlv320aic23b_write(client, 15, 0x000); + tlv320aic23b_write(sd, 15, 0x000); /* turn off DAC & mic input */ - tlv320aic23b_write(client, 6, 0x00A); + tlv320aic23b_write(sd, 6, 0x00A); /* left-justified, 24-bit, master mode */ - tlv320aic23b_write(client, 7, 0x049); + tlv320aic23b_write(sd, 7, 0x049); /* set gain on both channels to +3.0 dB */ - tlv320aic23b_write(client, 0, 0x119); + tlv320aic23b_write(sd, 0, 0x119); /* set sample rate to 48 kHz */ - tlv320aic23b_write(client, 8, 0x000); + tlv320aic23b_write(sd, 8, 0x000); /* activate digital interface */ - tlv320aic23b_write(client, 9, 0x001); + tlv320aic23b_write(sd, 9, 0x001); return 0; } static int tlv320aic23b_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 4a7735c..97d7509 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -18,7 +18,7 @@ #include <linux/videodev.h> #include <media/tuner.h> #include <media/tuner-types.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-i2c-drv-legacy.h> #include "mt20xx.h" @@ -78,6 +78,7 @@ struct tuner { /* device */ struct dvb_frontend fe; struct i2c_client *i2c; + struct v4l2_subdev sd; struct list_head list; unsigned int using_v4l2:1; @@ -95,6 +96,11 @@ struct tuner { const char *name; }; +static inline struct tuner *to_tuner(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tuner, sd); +} + /* standard i2c insmod options */ static unsigned short normal_i2c[] = { #if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE)) @@ -213,7 +219,7 @@ static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg) static void tuner_status(struct dvb_frontend *fe); -static struct analog_demod_ops tuner_core_ops = { +static struct analog_demod_ops tuner_analog_ops = { .set_params = fe_set_params, .standby = fe_standby, .has_signal = fe_has_signal, @@ -224,7 +230,7 @@ static struct analog_demod_ops tuner_core_ops = { /* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */ static void set_tv_freq(struct i2c_client *c, unsigned int freq) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; struct analog_parameters params = { @@ -259,7 +265,7 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) static void set_radio_freq(struct i2c_client *c, unsigned int freq) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; struct analog_parameters params = { @@ -294,7 +300,7 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) static void set_freq(struct i2c_client *c, unsigned long freq) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); switch (t->mode) { case V4L2_TUNER_RADIO: @@ -347,7 +353,7 @@ static void set_type(struct i2c_client *c, unsigned int type, unsigned int new_mode_mask, unsigned int new_config, int (*tuner_callback) (void *dev, int component, int cmd, int arg)) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; unsigned char buffer[4]; @@ -470,7 +476,7 @@ static void set_type(struct i2c_client *c, unsigned int type, t->name = fe_tuner_ops->info.name; t->fe.analog_demod_priv = t; - memcpy(analog_ops, &tuner_core_ops, + memcpy(analog_ops, &tuner_analog_ops, sizeof(struct analog_demod_ops)); } else { @@ -515,7 +521,7 @@ attach_failed: static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) && (t->mode_mask & tun_setup->mode_mask))) || @@ -727,6 +733,8 @@ static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, t->mode = mode; if (check_mode(t, cmd) == -EINVAL) { + tuner_dbg("Tuner doesn't support this mode. " + "Putting tuner to sleep\n"); t->mode = T_STANDBY; if (analog_ops->standby) analog_ops->standby(&t->fe); @@ -748,43 +756,58 @@ static inline int check_v4l2(struct tuner *t) return 0; } -static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) +static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner_setup *type) { - struct tuner *t = i2c_get_clientdata(client); - struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", + type->type, + type->addr, + type->mode_mask, + type->config); + + set_addr(client, type); + return 0; +} + +static int tuner_s_radio(struct v4l2_subdev *sd) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO") + == -EINVAL) + return 0; + if (t->radio_freq) + set_freq(client, t->radio_freq); + return 0; +} + +static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby) +{ + struct tuner *t = to_tuner(sd); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - if (tuner_debug > 1) { - v4l_i2c_print_ioctl(client,cmd); - printk("\n"); - } + tuner_dbg("Putting tuner to sleep\n"); + + if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL) + return 0; + t->mode = T_STANDBY; + if (analog_ops->standby) + analog_ops->standby(&t->fe); + return 0; +} - switch (cmd) { - /* --- configuration --- */ - case TUNER_SET_TYPE_ADDR: - tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", - ((struct tuner_setup *)arg)->type, - ((struct tuner_setup *)arg)->addr, - ((struct tuner_setup *)arg)->mode_mask, - ((struct tuner_setup *)arg)->config); - - set_addr(client, (struct tuner_setup *)arg); - break; - case AUDC_SET_RADIO: - if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO") - == -EINVAL) - return 0; - if (t->radio_freq) - set_freq(client, t->radio_freq); - break; - case TUNER_SET_STANDBY: - if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL) - return 0; - t->mode = T_STANDBY; - if (analog_ops->standby) - analog_ops->standby(&t->fe); - break; #ifdef CONFIG_VIDEO_ALLOW_V4L1 +static int tuner_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; + + switch (cmd) { case VIDIOCSAUDIO: if (check_mode(t, "VIDIOCSAUDIO") == -EINVAL) return 0; @@ -897,149 +920,172 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) } return 0; } + } + return -ENOIOCTLCMD; +} #endif - case TUNER_SET_CONFIG: - { - struct v4l2_priv_tun_config *cfg = arg; - if (t->type != cfg->tuner) - break; +static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg) +{ + struct tuner *t = to_tuner(sd); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - if (analog_ops->set_config) { - analog_ops->set_config(&t->fe, cfg->priv); - break; - } + if (t->type != cfg->tuner) + return 0; - tuner_dbg("Tuner frontend module has no way to set config\n"); - break; + if (analog_ops->set_config) { + analog_ops->set_config(&t->fe, cfg->priv); + return 0; } - /* --- v4l ioctls --- */ - /* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg; - if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD") - == -EINVAL) - return 0; + tuner_dbg("Tuner frontend module has no way to set config\n"); + return 0; +} - switch_v4l2(); +/* --- v4l ioctls --- */ +/* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ +static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - t->std = *id; - tuner_fixup_std(t); - if (t->tv_freq) - set_freq(client, t->tv_freq); - break; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD") + == -EINVAL) + return 0; - if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY") - == -EINVAL) - return 0; - switch_v4l2(); - set_freq(client,f->frequency); + switch_v4l2(); - break; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + t->std = std; + tuner_fixup_std(t); + if (t->tv_freq) + set_freq(client, t->tv_freq); + return 0; +} - if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL) - return 0; - switch_v4l2(); - f->type = t->mode; - if (fe_tuner_ops->get_frequency) { - u32 abs_freq; - - fe_tuner_ops->get_frequency(&t->fe, &abs_freq); - f->frequency = (V4L2_TUNER_RADIO == t->mode) ? - (abs_freq * 2 + 125/2) / 125 : - (abs_freq + 62500/2) / 62500; - break; - } - f->frequency = (V4L2_TUNER_RADIO == t->mode) ? - t->radio_freq : t->tv_freq; - break; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *tuner = arg; +static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL) - return 0; - switch_v4l2(); - - tuner->type = t->mode; - if (analog_ops->get_afc) - tuner->afc = analog_ops->get_afc(&t->fe); - if (t->mode == V4L2_TUNER_ANALOG_TV) - tuner->capability |= V4L2_TUNER_CAP_NORM; - if (t->mode != V4L2_TUNER_RADIO) { - tuner->rangelow = tv_range[0] * 16; - tuner->rangehigh = tv_range[1] * 16; - break; - } + if (set_mode(client, t, f->type, "VIDIOC_S_FREQUENCY") + == -EINVAL) + return 0; + switch_v4l2(); + set_freq(client, f->frequency); - /* radio mode */ - tuner->rxsubchans = - V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - if (fe_tuner_ops->get_status) { - u32 tuner_status; - - fe_tuner_ops->get_status(&t->fe, &tuner_status); - tuner->rxsubchans = - (tuner_status & TUNER_STATUS_STEREO) ? - V4L2_TUNER_SUB_STEREO : - V4L2_TUNER_SUB_MONO; - } else { - if (analog_ops->is_stereo) { - tuner->rxsubchans = - analog_ops->is_stereo(&t->fe) ? - V4L2_TUNER_SUB_STEREO : - V4L2_TUNER_SUB_MONO; - } - } - if (analog_ops->has_signal) - tuner->signal = analog_ops->has_signal(&t->fe); - tuner->capability |= - V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; - tuner->audmode = t->audmode; - tuner->rangelow = radio_range[0] * 16000; - tuner->rangehigh = radio_range[1] * 16000; - break; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *tuner = arg; + return 0; +} - if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL) - return 0; +static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct tuner *t = to_tuner(sd); + struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - switch_v4l2(); + if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL) + return 0; + switch_v4l2(); + f->type = t->mode; + if (fe_tuner_ops->get_frequency) { + u32 abs_freq; + + fe_tuner_ops->get_frequency(&t->fe, &abs_freq); + f->frequency = (V4L2_TUNER_RADIO == t->mode) ? + (abs_freq * 2 + 125/2) / 125 : + (abs_freq + 62500/2) / 62500; + return 0; + } + f->frequency = (V4L2_TUNER_RADIO == t->mode) ? + t->radio_freq : t->tv_freq; + return 0; +} - /* do nothing unless we're a radio tuner */ - if (t->mode != V4L2_TUNER_RADIO) - break; - t->audmode = tuner->audmode; - set_radio_freq(client, t->radio_freq); - break; +static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct tuner *t = to_tuner(sd); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; + + if (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL) + return 0; + switch_v4l2(); + + vt->type = t->mode; + if (analog_ops->get_afc) + vt->afc = analog_ops->get_afc(&t->fe); + if (t->mode == V4L2_TUNER_ANALOG_TV) + vt->capability |= V4L2_TUNER_CAP_NORM; + if (t->mode != V4L2_TUNER_RADIO) { + vt->rangelow = tv_range[0] * 16; + vt->rangehigh = tv_range[1] * 16; + return 0; + } + + /* radio mode */ + vt->rxsubchans = + V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + if (fe_tuner_ops->get_status) { + u32 tuner_status; + + fe_tuner_ops->get_status(&t->fe, &tuner_status); + vt->rxsubchans = + (tuner_status & TUNER_STATUS_STEREO) ? + V4L2_TUNER_SUB_STEREO : + V4L2_TUNER_SUB_MONO; + } else { + if (analog_ops->is_stereo) { + vt->rxsubchans = + analog_ops->is_stereo(&t->fe) ? + V4L2_TUNER_SUB_STEREO : + V4L2_TUNER_SUB_MONO; } - case VIDIOC_LOG_STATUS: - if (analog_ops->tuner_status) - analog_ops->tuner_status(&t->fe); - break; } + if (analog_ops->has_signal) + vt->signal = analog_ops->has_signal(&t->fe); + vt->capability |= + V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + vt->audmode = t->audmode; + vt->rangelow = radio_range[0] * 16000; + vt->rangehigh = radio_range[1] * 16000; + return 0; +} + +static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL) + return 0; + + switch_v4l2(); + + /* do nothing unless we're a radio tuner */ + if (t->mode != V4L2_TUNER_RADIO) + return 0; + t->audmode = vt->audmode; + set_radio_freq(client, t->radio_freq); return 0; } +static int tuner_log_status(struct v4l2_subdev *sd) +{ + struct tuner *t = to_tuner(sd); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + + if (analog_ops->tuner_status) + analog_ops->tuner_status(&t->fe); + return 0; +} + +static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + static int tuner_suspend(struct i2c_client *c, pm_message_t state) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); tuner_dbg("suspend\n"); /* FIXME: power down ??? */ @@ -1048,7 +1094,7 @@ static int tuner_suspend(struct i2c_client *c, pm_message_t state) static int tuner_resume(struct i2c_client *c) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); tuner_dbg("resume\n"); if (V4L2_TUNER_RADIO == t->mode) { @@ -1061,6 +1107,32 @@ static int tuner_resume(struct i2c_client *c) return 0; } +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tuner_core_ops = { + .log_status = tuner_log_status, + .s_standby = tuner_s_standby, +#ifdef CONFIG_VIDEO_ALLOW_V4L1 + .ioctl = tuner_ioctl, +#endif +}; + +static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { + .s_std = tuner_s_std, + .s_radio = tuner_s_radio, + .g_tuner = tuner_g_tuner, + .s_tuner = tuner_s_tuner, + .s_frequency = tuner_s_frequency, + .g_frequency = tuner_g_frequency, + .s_type_addr = tuner_s_type_addr, + .s_config = tuner_s_config, +}; + +static const struct v4l2_subdev_ops tuner_ops = { + .core = &tuner_core_ops, + .tuner = &tuner_tuner_ops, +}; + /* ---------------------------------------------------------------------- */ static LIST_HEAD(tuner_list); @@ -1109,9 +1181,9 @@ static int tuner_probe(struct i2c_client *client, t = kzalloc(sizeof(struct tuner), GFP_KERNEL); if (NULL == t) return -ENOMEM; + v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops); t->i2c = client; t->name = "(tuner unset)"; - i2c_set_clientdata(client, t); t->type = UNSET; t->audmode = V4L2_TUNER_MODE_STEREO; t->mode_mask = T_UNINITIALIZED; @@ -1261,8 +1333,9 @@ static int tuner_legacy_probe(struct i2c_adapter *adap) static int tuner_remove(struct i2c_client *client) { - struct tuner *t = i2c_get_clientdata(client); + struct tuner *t = to_tuner(i2c_get_clientdata(client)); + v4l2_device_unregister_subdev(&t->sd); tuner_detach(&t->fe); t->fe.analog_demod_priv = NULL; diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 3720f0e..d0c794d 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -33,8 +33,7 @@ #include <linux/freezer.h> #include <media/tvaudio.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv-legacy.h> @@ -110,7 +109,7 @@ struct CHIPDESC { /* current state of the chip */ struct CHIPSTATE { - struct i2c_client *c; + struct v4l2_subdev sd; /* chip-specific description - should point to an entry at CHIPDESC table */ @@ -132,6 +131,11 @@ struct CHIPSTATE { int audmode; }; +static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct CHIPSTATE, sd); +} + /* ---------------------------------------------------------------------- */ /* i2c addresses */ @@ -152,34 +156,34 @@ I2C_CLIENT_INSMOD; static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) { + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); unsigned char buffer[2]; if (subaddr < 0) { - v4l_dbg(1, debug, chip->c, "%s: chip_write: 0x%x\n", - chip->c->name, val); + v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val); chip->shadow.bytes[1] = val; buffer[0] = val; - if (1 != i2c_master_send(chip->c,buffer,1)) { - v4l_warn(chip->c, "%s: I/O error (write 0x%x)\n", - chip->c->name, val); + if (1 != i2c_master_send(c, buffer, 1)) { + v4l2_warn(sd, "I/O error (write 0x%x)\n", val); return -1; } } else { if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { - v4l_info(chip->c, + v4l2_info(sd, "Tried to access a non-existent register: %d\n", subaddr); return -EINVAL; } - v4l_dbg(1, debug, chip->c, "%s: chip_write: reg%d=0x%x\n", - chip->c->name, subaddr, val); + v4l2_dbg(1, debug, sd, "chip_write: reg%d=0x%x\n", + subaddr, val); chip->shadow.bytes[subaddr+1] = val; buffer[0] = subaddr; buffer[1] = val; - if (2 != i2c_master_send(chip->c,buffer,2)) { - v4l_warn(chip->c, "%s: I/O error (write reg%d=0x%x)\n", - chip->c->name, subaddr, val); + if (2 != i2c_master_send(c, buffer, 2)) { + v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n", + subaddr, val); return -1; } } @@ -189,12 +193,14 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) static int chip_write_masked(struct CHIPSTATE *chip, int subaddr, int val, int mask) { + struct v4l2_subdev *sd = &chip->sd; + if (mask != 0) { if (subaddr < 0) { val = (chip->shadow.bytes[1] & ~mask) | (val & mask); } else { if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { - v4l_info(chip->c, + v4l2_info(sd, "Tried to access a non-existent register: %d\n", subaddr); return -EINVAL; @@ -208,45 +214,51 @@ static int chip_write_masked(struct CHIPSTATE *chip, static int chip_read(struct CHIPSTATE *chip) { + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); unsigned char buffer; - if (1 != i2c_master_recv(chip->c,&buffer,1)) { - v4l_warn(chip->c, "%s: I/O error (read)\n", - chip->c->name); + if (1 != i2c_master_recv(c, &buffer, 1)) { + v4l2_warn(sd, "I/O error (read)\n"); return -1; } - v4l_dbg(1, debug, chip->c, "%s: chip_read: 0x%x\n",chip->c->name, buffer); + v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer); return buffer; } static int chip_read2(struct CHIPSTATE *chip, int subaddr) { + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); unsigned char write[1]; unsigned char read[1]; struct i2c_msg msgs[2] = { - { chip->c->addr, 0, 1, write }, - { chip->c->addr, I2C_M_RD, 1, read } + { c->addr, 0, 1, write }, + { c->addr, I2C_M_RD, 1, read } }; + write[0] = subaddr; - if (2 != i2c_transfer(chip->c->adapter,msgs,2)) { - v4l_warn(chip->c, "%s: I/O error (read2)\n", chip->c->name); + if (2 != i2c_transfer(c->adapter, msgs, 2)) { + v4l2_warn(sd, "I/O error (read2)\n"); return -1; } - v4l_dbg(1, debug, chip->c, "%s: chip_read2: reg%d=0x%x\n", - chip->c->name, subaddr,read[0]); + v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n", + subaddr, read[0]); return read[0]; } static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) { + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); int i; if (0 == cmd->count) return 0; if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) { - v4l_info(chip->c, + v4l2_info(sd, "Tried to access a non-existent register range: %d to %d\n", cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1); return -EINVAL; @@ -255,19 +267,19 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) /* FIXME: it seems that the shadow bytes are wrong bellow !*/ /* update our shadow register set; print bytes if (debug > 0) */ - v4l_dbg(1, debug, chip->c, "%s: chip_cmd(%s): reg=%d, data:", - chip->c->name, name,cmd->bytes[0]); + v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:", + name, cmd->bytes[0]); for (i = 1; i < cmd->count; i++) { if (debug) - printk(" 0x%x",cmd->bytes[i]); + printk(KERN_CONT " 0x%x", cmd->bytes[i]); chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i]; } if (debug) - printk("\n"); + printk(KERN_CONT "\n"); /* send data to the chip */ - if (cmd->count != i2c_master_send(chip->c,cmd->bytes,cmd->count)) { - v4l_warn(chip->c, "%s: I/O error (%s)\n", chip->c->name, name); + if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) { + v4l2_warn(sd, "I/O error (%s)\n", name); return -1; } return 0; @@ -290,9 +302,10 @@ static int chip_thread(void *data) { struct CHIPSTATE *chip = data; struct CHIPDESC *desc = chip->desc; + struct v4l2_subdev *sd = &chip->sd; int mode; - v4l_dbg(1, debug, chip->c, "%s: thread started\n", chip->c->name); + v4l2_dbg(1, debug, sd, "thread started\n"); set_freezable(); for (;;) { set_current_state(TASK_INTERRUPTIBLE); @@ -302,7 +315,7 @@ static int chip_thread(void *data) try_to_freeze(); if (kthread_should_stop()) break; - v4l_dbg(1, debug, chip->c, "%s: thread wakeup\n", chip->c->name); + v4l2_dbg(1, debug, sd, "thread wakeup\n"); /* don't do anything for radio or if mode != auto */ if (chip->radio || chip->mode != 0) @@ -314,8 +327,7 @@ static int chip_thread(void *data) continue; /* chip detected a new audio mode - set it */ - v4l_dbg(1, debug, chip->c, "%s: thread checkmode\n", - chip->c->name); + v4l2_dbg(1, debug, sd, "thread checkmode\n"); chip->prevmode = mode; @@ -334,7 +346,7 @@ static int chip_thread(void *data) mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); } - v4l_dbg(1, debug, chip->c, "%s: thread exiting\n", chip->c->name); + v4l2_dbg(1, debug, sd, "thread exiting\n"); return 0; } @@ -363,6 +375,7 @@ static int chip_thread(void *data) static int tda9840_getmode(struct CHIPSTATE *chip) { + struct v4l2_subdev *sd = &chip->sd; int val, mode; val = chip_read(chip); @@ -372,7 +385,7 @@ static int tda9840_getmode(struct CHIPSTATE *chip) if (val & TDA9840_ST_STEREO) mode |= V4L2_TUNER_MODE_STEREO; - v4l_dbg(1, debug, chip->c, "tda9840_getmode(): raw chip read: %d, return: %d\n", + v4l2_dbg(1, debug, sd, "tda9840_getmode(): raw chip read: %d, return: %d\n", val, mode); return mode; } @@ -668,6 +681,7 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode) static int tda9873_getmode(struct CHIPSTATE *chip) { + struct v4l2_subdev *sd = &chip->sd; int val,mode; val = chip_read(chip); @@ -676,23 +690,24 @@ static int tda9873_getmode(struct CHIPSTATE *chip) mode |= V4L2_TUNER_MODE_STEREO; if (val & TDA9873_DUAL) mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; - v4l_dbg(1, debug, chip->c, "tda9873_getmode(): raw chip read: %d, return: %d\n", + v4l2_dbg(1, debug, sd, "tda9873_getmode(): raw chip read: %d, return: %d\n", val, mode); return mode; } static void tda9873_setmode(struct CHIPSTATE *chip, int mode) { + struct v4l2_subdev *sd = &chip->sd; int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK; /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */ if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) { - v4l_dbg(1, debug, chip->c, "tda9873_setmode(): external input\n"); + v4l2_dbg(1, debug, sd, "tda9873_setmode(): external input\n"); return; } - v4l_dbg(1, debug, chip->c, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); - v4l_dbg(1, debug, chip->c, "tda9873_setmode(): sw_data = %d\n", sw_data); + v4l2_dbg(1, debug, sd, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); + v4l2_dbg(1, debug, sd, "tda9873_setmode(): sw_data = %d\n", sw_data); switch (mode) { case V4L2_TUNER_MODE_MONO: @@ -713,7 +728,7 @@ static void tda9873_setmode(struct CHIPSTATE *chip, int mode) } chip_write(chip, TDA9873_SW, sw_data); - v4l_dbg(1, debug, chip->c, "tda9873_setmode(): req. mode %d; chip_write: %d\n", + v4l2_dbg(1, debug, sd, "tda9873_setmode(): req. mode %d; chip_write: %d\n", mode, sw_data); } @@ -822,6 +837,8 @@ static struct tda9874a_MODES { static int tda9874a_setup(struct CHIPSTATE *chip) { + struct v4l2_subdev *sd = &chip->sd; + chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */ chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR); chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02); @@ -852,13 +869,14 @@ static int tda9874a_setup(struct CHIPSTATE *chip) chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */ } - v4l_dbg(1, debug, chip->c, "tda9874a_setup(): %s [0x%02X].\n", + v4l2_dbg(1, debug, sd, "tda9874a_setup(): %s [0x%02X].\n", tda9874a_modelist[tda9874a_STD].name,tda9874a_STD); return 1; } static int tda9874a_getmode(struct CHIPSTATE *chip) { + struct v4l2_subdev *sd = &chip->sd; int dsr,nsr,mode; int necr; /* just for debugging */ @@ -895,16 +913,18 @@ static int tda9874a_getmode(struct CHIPSTATE *chip) mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; } - v4l_dbg(1, debug, chip->c, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", + v4l2_dbg(1, debug, sd, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", dsr, nsr, necr, mode); return mode; } static void tda9874a_setmode(struct CHIPSTATE *chip, int mode) { + struct v4l2_subdev *sd = &chip->sd; + /* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */ /* If auto-muting is disabled, we can hear a signal of degrading quality. */ - if(tda9874a_mode) { + if (tda9874a_mode) { if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */ tda9874a_NCONR &= 0xfe; /* enable */ else @@ -941,7 +961,7 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode) chip_write(chip, TDA9874A_AOSR, aosr); chip_write(chip, TDA9874A_MDACOSR, mdacosr); - v4l_dbg(1, debug, chip->c, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", + v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", mode, aosr, mdacosr); } else { /* dic == 0x07 */ @@ -976,13 +996,14 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode) chip_write(chip, TDA9874A_FMMR, fmmr); chip_write(chip, TDA9874A_AOSR, aosr); - v4l_dbg(1, debug, chip->c, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", + v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", mode, fmmr, aosr); } } static int tda9874a_checkit(struct CHIPSTATE *chip) { + struct v4l2_subdev *sd = &chip->sd; int dic,sic; /* device id. and software id. codes */ if(-1 == (dic = chip_read2(chip,TDA9874A_DIC))) @@ -990,10 +1011,10 @@ static int tda9874a_checkit(struct CHIPSTATE *chip) if(-1 == (sic = chip_read2(chip,TDA9874A_SIC))) return 0; - v4l_dbg(1, debug, chip->c, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); + v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); if((dic == 0x11)||(dic == 0x07)) { - v4l_info(chip->c, "found tda9874%s.\n", (dic == 0x11) ? "a":"h"); + v4l2_info(sd, "found tda9874%s.\n", (dic == 0x11) ? "a" : "h"); tda9874a_dic = dic; /* remember device id. */ return 1; } @@ -1113,12 +1134,12 @@ static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; } static int tda8425_initialize(struct CHIPSTATE *chip) { struct CHIPDESC *desc = chip->desc; + struct i2c_client *c = v4l2_get_subdevdata(&chip->sd); int inputmap[4] = { /* tuner */ TDA8425_S1_CH2, /* radio */ TDA8425_S1_CH1, /* extern */ TDA8425_S1_CH1, /* intern */ TDA8425_S1_OFF}; - if (chip->c->adapter->id == I2C_HW_B_RIVA) { - memcpy (desc->inputmap, inputmap, sizeof (inputmap)); - } + if (c->adapter->id == I2C_HW_B_RIVA) + memcpy(desc->inputmap, inputmap, sizeof(inputmap)); return 0; } @@ -1215,9 +1236,11 @@ static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT static void ta8874z_setmode(struct CHIPSTATE *chip, int mode) { + struct v4l2_subdev *sd = &chip->sd; int update = 1; audiocmd *t = NULL; - v4l_dbg(1, debug, chip->c, "ta8874z_setmode(): mode: 0x%02x\n", mode); + + v4l2_dbg(1, debug, sd, "ta8874z_setmode(): mode: 0x%02x\n", mode); switch(mode){ case V4L2_TUNER_MODE_MONO: @@ -1479,142 +1502,11 @@ static struct CHIPDESC chiplist[] = { /* ---------------------------------------------------------------------- */ -/* i2c registration */ - -static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct CHIPSTATE *chip; - struct CHIPDESC *desc; - - if (debug) { - printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n"); - printk(KERN_INFO "tvaudio: known chips: "); - for (desc = chiplist; desc->name != NULL; desc++) - printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name); - printk("\n"); - } - - chip = kzalloc(sizeof(*chip),GFP_KERNEL); - if (!chip) - return -ENOMEM; - chip->c = client; - i2c_set_clientdata(client, chip); - /* find description for the chip */ - v4l_dbg(1, debug, client, "chip found @ 0x%x\n", client->addr<<1); - for (desc = chiplist; desc->name != NULL; desc++) { - if (0 == *(desc->insmodopt)) - continue; - if (client->addr < desc->addr_lo || - client->addr > desc->addr_hi) - continue; - if (desc->checkit && !desc->checkit(chip)) - continue; - break; - } - if (desc->name == NULL) { - v4l_dbg(1, debug, client, "no matching chip description found\n"); - kfree(chip); - return -EIO; - } - v4l_info(client, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name); - if (desc->flags) { - v4l_dbg(1, debug, client, "matches:%s%s%s.\n", - (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", - (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", - (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); - } - - /* fill required data structures */ - if (!id) - strlcpy(client->name, desc->name, I2C_NAME_SIZE); - chip->desc = desc; - chip->shadow.count = desc->registers+1; - chip->prevmode = -1; - chip->audmode = V4L2_TUNER_MODE_LANG1; - - /* initialization */ - if (desc->initialize != NULL) - desc->initialize(chip); - else - chip_cmd(chip,"init",&desc->init); - - if (desc->flags & CHIP_HAS_VOLUME) { - if (!desc->volfunc) { - /* This shouldn't be happen. Warn user, but keep working - without volume controls - */ - v4l_info(chip->c, "volume callback undefined!\n"); - desc->flags &= ~CHIP_HAS_VOLUME; - } else { - chip->left = desc->leftinit ? desc->leftinit : 65535; - chip->right = desc->rightinit ? desc->rightinit : 65535; - chip_write(chip, desc->leftreg, - desc->volfunc(chip->left)); - chip_write(chip, desc->rightreg, - desc->volfunc(chip->right)); - } - } - if (desc->flags & CHIP_HAS_BASSTREBLE) { - if (!desc->bassfunc || !desc->treblefunc) { - /* This shouldn't be happen. Warn user, but keep working - without bass/treble controls - */ - v4l_info(chip->c, "bass/treble callbacks undefined!\n"); - desc->flags &= ~CHIP_HAS_BASSTREBLE; - } else { - chip->treble = desc->trebleinit ? - desc->trebleinit : 32768; - chip->bass = desc->bassinit ? - desc->bassinit : 32768; - chip_write(chip, desc->bassreg, - desc->bassfunc(chip->bass)); - chip_write(chip, desc->treblereg, - desc->treblefunc(chip->treble)); - } - } - - chip->thread = NULL; - if (desc->flags & CHIP_NEED_CHECKMODE) { - if (!desc->getmode || !desc->setmode) { - /* This shouldn't be happen. Warn user, but keep working - without kthread - */ - v4l_info(chip->c, "set/get mode callbacks undefined!\n"); - return 0; - } - /* start async thread */ - init_timer(&chip->wt); - chip->wt.function = chip_thread_wake; - chip->wt.data = (unsigned long)chip; - chip->thread = kthread_run(chip_thread, chip, chip->c->name); - if (IS_ERR(chip->thread)) { - v4l_warn(chip->c, "%s: failed to create kthread\n", - chip->c->name); - chip->thread = NULL; - } - } - return 0; -} - -static int chip_remove(struct i2c_client *client) -{ - struct CHIPSTATE *chip = i2c_get_clientdata(client); - - del_timer_sync(&chip->wt); - if (chip->thread) { - /* shutdown async thread */ - kthread_stop(chip->thread); - chip->thread = NULL; - } - - kfree(chip); - return 0; -} - -static int tvaudio_get_ctrl(struct CHIPSTATE *chip, +static int tvaudio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { + struct CHIPSTATE *chip = to_state(sd); struct CHIPDESC *desc = chip->desc; switch (ctrl->id) { @@ -1652,9 +1544,10 @@ static int tvaudio_get_ctrl(struct CHIPSTATE *chip, return -EINVAL; } -static int tvaudio_set_ctrl(struct CHIPSTATE *chip, +static int tvaudio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { + struct CHIPSTATE *chip = to_state(sd); struct CHIPDESC *desc = chip->desc; switch (ctrl->id) { @@ -1726,161 +1619,327 @@ static int tvaudio_set_ctrl(struct CHIPSTATE *chip, /* ---------------------------------------------------------------------- */ /* video4linux interface */ -static int chip_command(struct i2c_client *client, - unsigned int cmd, void *arg) +static int tvaudio_s_radio(struct v4l2_subdev *sd) { - struct CHIPSTATE *chip = i2c_get_clientdata(client); - struct CHIPDESC *desc = chip->desc; + struct CHIPSTATE *chip = to_state(sd); - if (debug > 0) { - v4l_i2c_print_ioctl(chip->c, cmd); - printk("\n"); + chip->radio = 1; + chip->watch_stereo = 0; + /* del_timer(&chip->wt); */ + return 0; +} + +static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + switch (qc->id) { + case V4L2_CID_AUDIO_MUTE: + break; + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BALANCE: + if (!(desc->flags & CHIP_HAS_VOLUME)) + return -EINVAL; + break; + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + return -EINVAL; + break; + default: + return -EINVAL; } + return v4l2_ctrl_query_fill_std(qc); +} + +static int tvaudio_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; - switch (cmd) { - case AUDC_SET_RADIO: - chip->radio = 1; + if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4) + return -EINVAL; + /* There are four inputs: tuner, radio, extern and intern. */ + chip->input = rt->input; + if (chip->muted) + return 0; + chip_write_masked(chip, desc->inputreg, + desc->inputmap[chip->input], desc->inputmask); + return 0; +} + +static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + int mode = 0; + + if (chip->radio) + return 0; + switch (vt->audmode) { + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + case V4L2_TUNER_MODE_LANG2: + mode = vt->audmode; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + mode = V4L2_TUNER_MODE_STEREO; + break; + default: + return -EINVAL; + } + chip->audmode = vt->audmode; + + if (desc->setmode && mode) { chip->watch_stereo = 0; /* del_timer(&chip->wt); */ - break; - /* --- v4l ioctls --- */ - /* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - break; - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BALANCE: - if (!(desc->flags & CHIP_HAS_VOLUME)) - return -EINVAL; - break; - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - return -EINVAL; - break; - default: - return -EINVAL; - } - return v4l2_ctrl_query_fill_std(qc); + chip->mode = mode; + desc->setmode(chip, mode); } - case VIDIOC_S_CTRL: - return tvaudio_set_ctrl(chip, arg); + return 0; +} - case VIDIOC_G_CTRL: - return tvaudio_get_ctrl(chip, arg); - case VIDIOC_INT_G_AUDIO_ROUTING: - { - struct v4l2_routing *rt = arg; +static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + int mode = V4L2_TUNER_MODE_MONO; - rt->input = chip->input; - rt->output = 0; - break; + if (chip->radio) + return 0; + vt->audmode = chip->audmode; + vt->rxsubchans = 0; + vt->capability = V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + + if (desc->getmode) + mode = desc->getmode(chip); + + if (mode & V4L2_TUNER_MODE_MONO) + vt->rxsubchans |= V4L2_TUNER_SUB_MONO; + if (mode & V4L2_TUNER_MODE_STEREO) + vt->rxsubchans |= V4L2_TUNER_SUB_STEREO; + /* Note: for SAP it should be mono/lang2 or stereo/lang2. + When this module is converted fully to v4l2, then this + should change for those chips that can detect SAP. */ + if (mode & V4L2_TUNER_MODE_LANG1) + vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + return 0; +} + +static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct CHIPSTATE *chip = to_state(sd); + + chip->radio = 0; + return 0; +} + +static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + chip->mode = 0; /* automatic */ + + /* For chips that provide getmode and setmode, and doesn't + automatically follows the stereo carrier, a kthread is + created to set the audio standard. In this case, when then + the video channel is changed, tvaudio starts on MONO mode. + After waiting for 2 seconds, the kernel thread is called, + to follow whatever audio standard is pointed by the + audio carrier. + */ + if (chip->thread) { + desc->setmode(chip, V4L2_TUNER_MODE_MONO); + if (chip->prevmode != V4L2_TUNER_MODE_MONO) + chip->prevmode = -1; /* reset previous mode */ + mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); } - case VIDIOC_INT_S_AUDIO_ROUTING: - { - struct v4l2_routing *rt = arg; + return 0; +} - if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4) - return -EINVAL; - /* There are four inputs: tuner, radio, extern and intern. */ - chip->input = rt->input; - if (chip->muted) - break; - chip_write_masked(chip, desc->inputreg, - desc->inputmap[chip->input], desc->inputmask); - break; +static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0); +} + +static int tvaudio_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tvaudio_core_ops = { + .g_chip_ident = tvaudio_g_chip_ident, + .queryctrl = tvaudio_queryctrl, + .g_ctrl = tvaudio_g_ctrl, + .s_ctrl = tvaudio_s_ctrl, +}; + +static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { + .s_radio = tvaudio_s_radio, + .s_frequency = tvaudio_s_frequency, + .s_std = tvaudio_s_std, + .s_tuner = tvaudio_s_tuner, + .s_tuner = tvaudio_g_tuner, +}; + +static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = { + .s_routing = tvaudio_s_routing, +}; + +static const struct v4l2_subdev_ops tvaudio_ops = { + .core = &tvaudio_core_ops, + .tuner = &tvaudio_tuner_ops, + .audio = &tvaudio_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ + + +/* i2c registration */ + +static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct CHIPSTATE *chip; + struct CHIPDESC *desc; + struct v4l2_subdev *sd; + + if (debug) { + printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n"); + printk(KERN_INFO "tvaudio: known chips: "); + for (desc = chiplist; desc->name != NULL; desc++) + printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name); + printk("\n"); } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *vt = arg; - int mode = 0; - if (chip->radio) - break; - switch (vt->audmode) { - case V4L2_TUNER_MODE_MONO: - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1: - case V4L2_TUNER_MODE_LANG2: - mode = vt->audmode; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - mode = V4L2_TUNER_MODE_STEREO; - break; - default: - return -EINVAL; - } - chip->audmode = vt->audmode; + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + sd = &chip->sd; + v4l2_i2c_subdev_init(sd, client, &tvaudio_ops); - if (desc->setmode && mode) { - chip->watch_stereo = 0; - /* del_timer(&chip->wt); */ - chip->mode = mode; - desc->setmode(chip, mode); - } + /* find description for the chip */ + v4l2_dbg(1, debug, sd, "chip found @ 0x%x\n", client->addr<<1); + for (desc = chiplist; desc->name != NULL; desc++) { + if (0 == *(desc->insmodopt)) + continue; + if (client->addr < desc->addr_lo || + client->addr > desc->addr_hi) + continue; + if (desc->checkit && !desc->checkit(chip)) + continue; break; } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = arg; - int mode = V4L2_TUNER_MODE_MONO; + if (desc->name == NULL) { + v4l2_dbg(1, debug, sd, "no matching chip description found\n"); + kfree(chip); + return -EIO; + } + v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name); + if (desc->flags) { + v4l2_dbg(1, debug, sd, "matches:%s%s%s.\n", + (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", + (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", + (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); + } - if (chip->radio) - break; - vt->audmode = chip->audmode; - vt->rxsubchans = 0; - vt->capability = V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + /* fill required data structures */ + if (!id) + strlcpy(client->name, desc->name, I2C_NAME_SIZE); + chip->desc = desc; + chip->shadow.count = desc->registers+1; + chip->prevmode = -1; + chip->audmode = V4L2_TUNER_MODE_LANG1; - if (desc->getmode) - mode = desc->getmode(chip); + /* initialization */ + if (desc->initialize != NULL) + desc->initialize(chip); + else + chip_cmd(chip, "init", &desc->init); - if (mode & V4L2_TUNER_MODE_MONO) - vt->rxsubchans |= V4L2_TUNER_SUB_MONO; - if (mode & V4L2_TUNER_MODE_STEREO) - vt->rxsubchans |= V4L2_TUNER_SUB_STEREO; - /* Note: for SAP it should be mono/lang2 or stereo/lang2. - When this module is converted fully to v4l2, then this - should change for those chips that can detect SAP. */ - if (mode & V4L2_TUNER_MODE_LANG1) - vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | - V4L2_TUNER_SUB_LANG2; - break; + if (desc->flags & CHIP_HAS_VOLUME) { + if (!desc->volfunc) { + /* This shouldn't be happen. Warn user, but keep working + without volume controls + */ + v4l2_info(sd, "volume callback undefined!\n"); + desc->flags &= ~CHIP_HAS_VOLUME; + } else { + chip->left = desc->leftinit ? desc->leftinit : 65535; + chip->right = desc->rightinit ? desc->rightinit : 65535; + chip_write(chip, desc->leftreg, + desc->volfunc(chip->left)); + chip_write(chip, desc->rightreg, + desc->volfunc(chip->right)); + } } - case VIDIOC_S_STD: - chip->radio = 0; - break; - case VIDIOC_S_FREQUENCY: - chip->mode = 0; /* automatic */ - - /* For chips that provide getmode and setmode, and doesn't - automatically follows the stereo carrier, a kthread is - created to set the audio standard. In this case, when then - the video channel is changed, tvaudio starts on MONO mode. - After waiting for 2 seconds, the kernel thread is called, - to follow whatever audio standard is pointed by the - audio carrier. - */ - if (chip->thread) { - desc->setmode(chip,V4L2_TUNER_MODE_MONO); - if (chip->prevmode != V4L2_TUNER_MODE_MONO) - chip->prevmode = -1; /* reset previous mode */ - mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); + if (desc->flags & CHIP_HAS_BASSTREBLE) { + if (!desc->bassfunc || !desc->treblefunc) { + /* This shouldn't be happen. Warn user, but keep working + without bass/treble controls + */ + v4l2_info(sd, "bass/treble callbacks undefined!\n"); + desc->flags &= ~CHIP_HAS_BASSTREBLE; + } else { + chip->treble = desc->trebleinit ? + desc->trebleinit : 32768; + chip->bass = desc->bassinit ? + desc->bassinit : 32768; + chip_write(chip, desc->bassreg, + desc->bassfunc(chip->bass)); + chip_write(chip, desc->treblereg, + desc->treblefunc(chip->treble)); } - break; + } - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_TVAUDIO, 0); + chip->thread = NULL; + if (desc->flags & CHIP_NEED_CHECKMODE) { + if (!desc->getmode || !desc->setmode) { + /* This shouldn't be happen. Warn user, but keep working + without kthread + */ + v4l2_info(sd, "set/get mode callbacks undefined!\n"); + return 0; + } + /* start async thread */ + init_timer(&chip->wt); + chip->wt.function = chip_thread_wake; + chip->wt.data = (unsigned long)chip; + chip->thread = kthread_run(chip_thread, chip, client->name); + if (IS_ERR(chip->thread)) { + v4l2_warn(sd, "failed to create kthread\n"); + chip->thread = NULL; + } } return 0; } -static int chip_legacy_probe(struct i2c_adapter *adap) +static int tvaudio_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct CHIPSTATE *chip = to_state(sd); + + del_timer_sync(&chip->wt); + if (chip->thread) { + /* shutdown async thread */ + kthread_stop(chip->thread); + chip->thread = NULL; + } + + v4l2_device_unregister_subdev(sd); + kfree(chip); + return 0; +} + +static int tvaudio_legacy_probe(struct i2c_adapter *adap) { /* don't attach on saa7146 based cards, because dedicated drivers are used */ @@ -1894,18 +1953,18 @@ static int chip_legacy_probe(struct i2c_adapter *adap) /* This driver supports many devices and the idea is to let the driver detect which device is present. So rather than listing all supported devices here, we pretend to support a single, fake device type. */ -static const struct i2c_device_id chip_id[] = { +static const struct i2c_device_id tvaudio_id[] = { { "tvaudio", 0 }, { } }; -MODULE_DEVICE_TABLE(i2c, chip_id); +MODULE_DEVICE_TABLE(i2c, tvaudio_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tvaudio", .driverid = I2C_DRIVERID_TVAUDIO, - .command = chip_command, - .probe = chip_probe, - .remove = chip_remove, - .legacy_probe = chip_legacy_probe, - .id_table = chip_id, + .command = tvaudio_command, + .probe = tvaudio_probe, + .remove = tvaudio_remove, + .legacy_probe = tvaudio_legacy_probe, + .id_table = tvaudio_id, }; diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c new file mode 100644 index 0000000..ac9aa40 --- /dev/null +++ b/drivers/media/video/tvp514x.c @@ -0,0 +1,1569 @@ +/* + * drivers/media/video/tvp514x.c + * + * TI TVP5146/47 decoder driver + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath <hvaibhav@ti.com> + * + * Contributors: + * Sivaraj R <sivaraj@ti.com> + * Brijesh R Jadav <brijesh.j@ti.com> + * Hardik Shah <hardik.shah@ti.com> + * Manjunath Hadli <mrh@ti.com> + * Karicheri Muralidharan <m-karicheri2@ti.com> + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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/i2c.h> +#include <linux/delay.h> +#include <linux/videodev2.h> +#include <media/v4l2-int-device.h> +#include <media/tvp514x.h> + +#include "tvp514x_regs.h" + +/* Module Name */ +#define TVP514X_MODULE_NAME "tvp514x" + +/* Private macros for TVP */ +#define I2C_RETRY_COUNT (5) +#define LOCK_RETRY_COUNT (5) +#define LOCK_RETRY_DELAY (200) + +/* Debug functions */ +static int debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define dump_reg(client, reg, val) \ + do { \ + val = tvp514x_read_reg(client, reg); \ + v4l_info(client, "Reg(0x%.2X): 0x%.2X\n", reg, val); \ + } while (0) + +/** + * enum tvp514x_std - enum for supported standards + */ +enum tvp514x_std { + STD_NTSC_MJ = 0, + STD_PAL_BDGHIN, + STD_INVALID +}; + +/** + * enum tvp514x_state - enum for different decoder states + */ +enum tvp514x_state { + STATE_NOT_DETECTED, + STATE_DETECTED +}; + +/** + * struct tvp514x_std_info - Structure to store standard informations + * @width: Line width in pixels + * @height:Number of active lines + * @video_std: Value to write in REG_VIDEO_STD register + * @standard: v4l2 standard structure information + */ +struct tvp514x_std_info { + unsigned long width; + unsigned long height; + u8 video_std; + struct v4l2_standard standard; +}; + +/** + * struct tvp514x_decoded - TVP5146/47 decoder object + * @v4l2_int_device: Slave handle + * @pdata: Board specific + * @client: I2C client data + * @id: Entry from I2C table + * @ver: Chip version + * @state: TVP5146/47 decoder state - detected or not-detected + * @pix: Current pixel format + * @num_fmts: Number of formats + * @fmt_list: Format list + * @current_std: Current standard + * @num_stds: Number of standards + * @std_list: Standards list + * @route: input and output routing at chip level + */ +struct tvp514x_decoder { + struct v4l2_int_device *v4l2_int_device; + const struct tvp514x_platform_data *pdata; + struct i2c_client *client; + + struct i2c_device_id *id; + + int ver; + enum tvp514x_state state; + + struct v4l2_pix_format pix; + int num_fmts; + const struct v4l2_fmtdesc *fmt_list; + + enum tvp514x_std current_std; + int num_stds; + struct tvp514x_std_info *std_list; + + struct v4l2_routing route; +}; + +/* TVP514x default register values */ +static struct tvp514x_reg tvp514x_reg_list[] = { + {TOK_WRITE, REG_INPUT_SEL, 0x05}, /* Composite selected */ + {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F}, + {TOK_WRITE, REG_VIDEO_STD, 0x00}, /* Auto mode */ + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F}, + {TOK_WRITE, REG_COLOR_KILLER, 0x10}, + {TOK_WRITE, REG_LUMA_CONTROL1, 0x00}, + {TOK_WRITE, REG_LUMA_CONTROL2, 0x00}, + {TOK_WRITE, REG_LUMA_CONTROL3, 0x02}, + {TOK_WRITE, REG_BRIGHTNESS, 0x80}, + {TOK_WRITE, REG_CONTRAST, 0x80}, + {TOK_WRITE, REG_SATURATION, 0x80}, + {TOK_WRITE, REG_HUE, 0x00}, + {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00}, + {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E}, + {TOK_SKIP, 0x0F, 0x00}, /* Reserved */ + {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80}, + {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80}, + {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80}, + {TOK_SKIP, 0x13, 0x00}, /* Reserved */ + {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80}, + {TOK_SKIP, 0x15, 0x00}, /* Reserved */ + {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, /* NTSC timing */ + {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25}, + {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03}, + {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, /* NTSC timing */ + {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40}, + {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, /* NTSC timing */ + {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07}, + {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, /* NTSC timing */ + {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15}, + {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00}, + {TOK_SKIP, 0x26, 0x00}, /* Reserved */ + {TOK_SKIP, 0x27, 0x00}, /* Reserved */ + {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC}, + {TOK_SKIP, 0x29, 0x00}, /* Reserved */ + {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00}, + {TOK_SKIP, 0x2B, 0x00}, /* Reserved */ + {TOK_SKIP, REG_SCART_DELAY, 0x00}, + {TOK_SKIP, REG_CTI_DELAY, 0x00}, + {TOK_SKIP, REG_CTI_CONTROL, 0x00}, + {TOK_SKIP, 0x2F, 0x00}, /* Reserved */ + {TOK_SKIP, 0x30, 0x00}, /* Reserved */ + {TOK_SKIP, 0x31, 0x00}, /* Reserved */ + {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, /* HS, VS active high */ + {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, /* 10-bit BT.656 */ + {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, /* Enable clk & data */ + {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, /* Enable AVID & FLD */ + {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, /* Enable VS & HS */ + {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF}, + {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF}, + {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, /* Clear status */ + {TOK_TERM, 0, 0}, +}; + +/* List of image formats supported by TVP5146/47 decoder + * Currently we are using 8 bit mode only, but can be + * extended to 10/20 bit mode. + */ +static const struct v4l2_fmtdesc tvp514x_fmt_list[] = { + { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = 0, + .description = "8-bit UYVY 4:2:2 Format", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, +}; + +/* + * Supported standards - + * + * Currently supports two standards only, need to add support for rest of the + * modes, like SECAM, etc... + */ +static struct tvp514x_std_info tvp514x_std_list[] = { + /* Standard: STD_NTSC_MJ */ + [STD_NTSC_MJ] = { + .width = NTSC_NUM_ACTIVE_PIXELS, + .height = NTSC_NUM_ACTIVE_LINES, + .video_std = VIDEO_STD_NTSC_MJ_BIT, + .standard = { + .index = 0, + .id = V4L2_STD_NTSC, + .name = "NTSC", + .frameperiod = {1001, 30000}, + .framelines = 525 + }, + /* Standard: STD_PAL_BDGHIN */ + }, + [STD_PAL_BDGHIN] = { + .width = PAL_NUM_ACTIVE_PIXELS, + .height = PAL_NUM_ACTIVE_LINES, + .video_std = VIDEO_STD_PAL_BDGHIN_BIT, + .standard = { + .index = 1, + .id = V4L2_STD_PAL, + .name = "PAL", + .frameperiod = {1, 25}, + .framelines = 625 + }, + }, + /* Standard: need to add for additional standard */ +}; +/* + * Control structure for Auto Gain + * This is temporary data, will get replaced once + * v4l2_ctrl_query_fill supports it. + */ +static const struct v4l2_queryctrl tvp514x_autogain_ctrl = { + .id = V4L2_CID_AUTOGAIN, + .name = "Gain, Automatic", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, +}; + +/* + * Read a value from a register in an TVP5146/47 decoder device. + * Returns value read if successful, or non-zero (-1) otherwise. + */ +static int tvp514x_read_reg(struct i2c_client *client, u8 reg) +{ + int err; + int retry = 0; +read_again: + + err = i2c_smbus_read_byte_data(client, reg); + if (err == -1) { + if (retry <= I2C_RETRY_COUNT) { + v4l_warn(client, "Read: retry ... %d\n", retry); + retry++; + msleep_interruptible(10); + goto read_again; + } + } + + return err; +} + +/* + * Write a value to a register in an TVP5146/47 decoder device. + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + int err; + int retry = 0; +write_again: + + err = i2c_smbus_write_byte_data(client, reg, val); + if (err) { + if (retry <= I2C_RETRY_COUNT) { + v4l_warn(client, "Write: retry ... %d\n", retry); + retry++; + msleep_interruptible(10); + goto write_again; + } + } + + return err; +} + +/* + * tvp514x_write_regs : Initializes a list of TVP5146/47 registers + * if token is TOK_TERM, then entire write operation terminates + * if token is TOK_DELAY, then a delay of 'val' msec is introduced + * if token is TOK_SKIP, then the register write is skipped + * if token is TOK_WRITE, then the register write is performed + * + * reglist - list of registers to be written + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_write_regs(struct i2c_client *client, + const struct tvp514x_reg reglist[]) +{ + int err; + const struct tvp514x_reg *next = reglist; + + for (; next->token != TOK_TERM; next++) { + if (next->token == TOK_DELAY) { + msleep(next->val); + continue; + } + + if (next->token == TOK_SKIP) + continue; + + err = tvp514x_write_reg(client, next->reg, (u8) next->val); + if (err) { + v4l_err(client, "Write failed. Err[%d]\n", err); + return err; + } + } + return 0; +} + +/* + * tvp514x_get_current_std: + * Returns the current standard detected by TVP5146/47 + */ +static enum tvp514x_std tvp514x_get_current_std(struct tvp514x_decoder + *decoder) +{ + u8 std, std_status; + + std = tvp514x_read_reg(decoder->client, REG_VIDEO_STD); + if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) { + /* use the standard status register */ + std_status = tvp514x_read_reg(decoder->client, + REG_VIDEO_STD_STATUS); + } else + std_status = std; /* use the standard register itself */ + + switch (std_status & VIDEO_STD_MASK) { + case VIDEO_STD_NTSC_MJ_BIT: + return STD_NTSC_MJ; + + case VIDEO_STD_PAL_BDGHIN_BIT: + return STD_PAL_BDGHIN; + + default: + return STD_INVALID; + } + + return STD_INVALID; +} + +/* + * TVP5146/47 register dump function + */ +static void tvp514x_reg_dump(struct tvp514x_decoder *decoder) +{ + u8 value; + + dump_reg(decoder->client, REG_INPUT_SEL, value); + dump_reg(decoder->client, REG_AFE_GAIN_CTRL, value); + dump_reg(decoder->client, REG_VIDEO_STD, value); + dump_reg(decoder->client, REG_OPERATION_MODE, value); + dump_reg(decoder->client, REG_COLOR_KILLER, value); + dump_reg(decoder->client, REG_LUMA_CONTROL1, value); + dump_reg(decoder->client, REG_LUMA_CONTROL2, value); + dump_reg(decoder->client, REG_LUMA_CONTROL3, value); + dump_reg(decoder->client, REG_BRIGHTNESS, value); + dump_reg(decoder->client, REG_CONTRAST, value); + dump_reg(decoder->client, REG_SATURATION, value); + dump_reg(decoder->client, REG_HUE, value); + dump_reg(decoder->client, REG_CHROMA_CONTROL1, value); + dump_reg(decoder->client, REG_CHROMA_CONTROL2, value); + dump_reg(decoder->client, REG_COMP_PR_SATURATION, value); + dump_reg(decoder->client, REG_COMP_Y_CONTRAST, value); + dump_reg(decoder->client, REG_COMP_PB_SATURATION, value); + dump_reg(decoder->client, REG_COMP_Y_BRIGHTNESS, value); + dump_reg(decoder->client, REG_AVID_START_PIXEL_LSB, value); + dump_reg(decoder->client, REG_AVID_START_PIXEL_MSB, value); + dump_reg(decoder->client, REG_AVID_STOP_PIXEL_LSB, value); + dump_reg(decoder->client, REG_AVID_STOP_PIXEL_MSB, value); + dump_reg(decoder->client, REG_HSYNC_START_PIXEL_LSB, value); + dump_reg(decoder->client, REG_HSYNC_START_PIXEL_MSB, value); + dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_LSB, value); + dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_MSB, value); + dump_reg(decoder->client, REG_VSYNC_START_LINE_LSB, value); + dump_reg(decoder->client, REG_VSYNC_START_LINE_MSB, value); + dump_reg(decoder->client, REG_VSYNC_STOP_LINE_LSB, value); + dump_reg(decoder->client, REG_VSYNC_STOP_LINE_MSB, value); + dump_reg(decoder->client, REG_VBLK_START_LINE_LSB, value); + dump_reg(decoder->client, REG_VBLK_START_LINE_MSB, value); + dump_reg(decoder->client, REG_VBLK_STOP_LINE_LSB, value); + dump_reg(decoder->client, REG_VBLK_STOP_LINE_MSB, value); + dump_reg(decoder->client, REG_SYNC_CONTROL, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER1, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER2, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER3, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER4, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER5, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER6, value); + dump_reg(decoder->client, REG_CLEAR_LOST_LOCK, value); +} + +/* + * Configure the TVP5146/47 with the current register settings + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_configure(struct tvp514x_decoder *decoder) +{ + int err; + + /* common register initialization */ + err = + tvp514x_write_regs(decoder->client, tvp514x_reg_list); + if (err) + return err; + + if (debug) + tvp514x_reg_dump(decoder); + + return 0; +} + +/* + * Detect if an tvp514x is present, and if so which revision. + * A device is considered to be detected if the chip ID (LSB and MSB) + * registers match the expected values. + * Any value of the rom version register is accepted. + * Returns ENODEV error number if no device is detected, or zero + * if a device is detected. + */ +static int tvp514x_detect(struct tvp514x_decoder *decoder) +{ + u8 chip_id_msb, chip_id_lsb, rom_ver; + + chip_id_msb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_MSB); + chip_id_lsb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_LSB); + rom_ver = tvp514x_read_reg(decoder->client, REG_ROM_VERSION); + + v4l_dbg(1, debug, decoder->client, + "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n", + chip_id_msb, chip_id_lsb, rom_ver); + if ((chip_id_msb != TVP514X_CHIP_ID_MSB) + || ((chip_id_lsb != TVP5146_CHIP_ID_LSB) + && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) { + /* We didn't read the values we expected, so this must not be + * an TVP5146/47. + */ + v4l_err(decoder->client, + "chip id mismatch msb:0x%x lsb:0x%x\n", + chip_id_msb, chip_id_lsb); + return -ENODEV; + } + + decoder->ver = rom_ver; + decoder->state = STATE_DETECTED; + + v4l_info(decoder->client, + "%s found at 0x%x (%s)\n", decoder->client->name, + decoder->client->addr << 1, + decoder->client->adapter->name); + return 0; +} + +/* + * Following are decoder interface functions implemented by + * TVP5146/47 decoder driver. + */ + +/** + * ioctl_querystd - V4L2 decoder interface handler for VIDIOC_QUERYSTD ioctl + * @s: pointer to standard V4L2 device structure + * @std_id: standard V4L2 std_id ioctl enum + * + * Returns the current standard detected by TVP5146/47. If no active input is + * detected, returns -EINVAL + */ +static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id) +{ + struct tvp514x_decoder *decoder = s->priv; + enum tvp514x_std current_std; + enum tvp514x_input input_sel; + u8 sync_lock_status, lock_mask; + + if (std_id == NULL) + return -EINVAL; + + /* get the current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + input_sel = decoder->route.input; + + switch (input_sel) { + case INPUT_CVBS_VI1A: + case INPUT_CVBS_VI1B: + case INPUT_CVBS_VI1C: + case INPUT_CVBS_VI2A: + case INPUT_CVBS_VI2B: + case INPUT_CVBS_VI2C: + case INPUT_CVBS_VI3A: + case INPUT_CVBS_VI3B: + case INPUT_CVBS_VI3C: + case INPUT_CVBS_VI4A: + lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | + STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + + case INPUT_SVIDEO_VI2A_VI1A: + case INPUT_SVIDEO_VI2B_VI1B: + case INPUT_SVIDEO_VI2C_VI1C: + case INPUT_SVIDEO_VI2A_VI3A: + case INPUT_SVIDEO_VI2B_VI3B: + case INPUT_SVIDEO_VI2C_VI3C: + case INPUT_SVIDEO_VI4A_VI1A: + case INPUT_SVIDEO_VI4A_VI1B: + case INPUT_SVIDEO_VI4A_VI1C: + case INPUT_SVIDEO_VI4A_VI3A: + case INPUT_SVIDEO_VI4A_VI3B: + case INPUT_SVIDEO_VI4A_VI3C: + lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + /*Need to add other interfaces*/ + default: + return -EINVAL; + } + /* check whether signal is locked */ + sync_lock_status = tvp514x_read_reg(decoder->client, REG_STATUS1); + if (lock_mask != (sync_lock_status & lock_mask)) + return -EINVAL; /* No input detected */ + + decoder->current_std = current_std; + *std_id = decoder->std_list[current_std].standard.id; + + v4l_dbg(1, debug, decoder->client, "Current STD: %s", + decoder->std_list[current_std].standard.name); + return 0; +} + +/** + * ioctl_s_std - V4L2 decoder interface handler for VIDIOC_S_STD ioctl + * @s: pointer to standard V4L2 device structure + * @std_id: standard V4L2 v4l2_std_id ioctl enum + * + * If std_id is supported, sets the requested standard. Otherwise, returns + * -EINVAL + */ +static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id) +{ + struct tvp514x_decoder *decoder = s->priv; + int err, i; + + if (std_id == NULL) + return -EINVAL; + + for (i = 0; i < decoder->num_stds; i++) + if (*std_id & decoder->std_list[i].standard.id) + break; + + if ((i == decoder->num_stds) || (i == STD_INVALID)) + return -EINVAL; + + err = tvp514x_write_reg(decoder->client, REG_VIDEO_STD, + decoder->std_list[i].video_std); + if (err) + return err; + + decoder->current_std = i; + tvp514x_reg_list[REG_VIDEO_STD].val = decoder->std_list[i].video_std; + + v4l_dbg(1, debug, decoder->client, "Standard set to: %s", + decoder->std_list[i].standard.name); + return 0; +} + +/** + * ioctl_s_routing - V4L2 decoder interface handler for VIDIOC_S_INPUT ioctl + * @s: pointer to standard V4L2 device structure + * @index: number of the input + * + * If index is valid, selects the requested input. Otherwise, returns -EINVAL if + * the input is not supported or there is no active signal present in the + * selected input. + */ +static int ioctl_s_routing(struct v4l2_int_device *s, + struct v4l2_routing *route) +{ + struct tvp514x_decoder *decoder = s->priv; + int err; + enum tvp514x_input input_sel; + enum tvp514x_output output_sel; + enum tvp514x_std current_std = STD_INVALID; + u8 sync_lock_status, lock_mask; + int try_count = LOCK_RETRY_COUNT; + + if ((!route) || (route->input >= INPUT_INVALID) || + (route->output >= OUTPUT_INVALID)) + return -EINVAL; /* Index out of bound */ + + input_sel = route->input; + output_sel = route->output; + + err = tvp514x_write_reg(decoder->client, REG_INPUT_SEL, input_sel); + if (err) + return err; + + output_sel |= tvp514x_read_reg(decoder->client, + REG_OUTPUT_FORMATTER1) & 0x7; + err = tvp514x_write_reg(decoder->client, REG_OUTPUT_FORMATTER1, + output_sel); + if (err) + return err; + + tvp514x_reg_list[REG_INPUT_SEL].val = input_sel; + tvp514x_reg_list[REG_OUTPUT_FORMATTER1].val = output_sel; + + /* Clear status */ + msleep(LOCK_RETRY_DELAY); + err = + tvp514x_write_reg(decoder->client, REG_CLEAR_LOST_LOCK, 0x01); + if (err) + return err; + + switch (input_sel) { + case INPUT_CVBS_VI1A: + case INPUT_CVBS_VI1B: + case INPUT_CVBS_VI1C: + case INPUT_CVBS_VI2A: + case INPUT_CVBS_VI2B: + case INPUT_CVBS_VI2C: + case INPUT_CVBS_VI3A: + case INPUT_CVBS_VI3B: + case INPUT_CVBS_VI3C: + case INPUT_CVBS_VI4A: + lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | + STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + + case INPUT_SVIDEO_VI2A_VI1A: + case INPUT_SVIDEO_VI2B_VI1B: + case INPUT_SVIDEO_VI2C_VI1C: + case INPUT_SVIDEO_VI2A_VI3A: + case INPUT_SVIDEO_VI2B_VI3B: + case INPUT_SVIDEO_VI2C_VI3C: + case INPUT_SVIDEO_VI4A_VI1A: + case INPUT_SVIDEO_VI4A_VI1B: + case INPUT_SVIDEO_VI4A_VI1C: + case INPUT_SVIDEO_VI4A_VI3A: + case INPUT_SVIDEO_VI4A_VI3B: + case INPUT_SVIDEO_VI4A_VI3C: + lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + /*Need to add other interfaces*/ + default: + return -EINVAL; + } + + while (try_count-- > 0) { + /* Allow decoder to sync up with new input */ + msleep(LOCK_RETRY_DELAY); + + /* get the current standard for future reference */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + continue; + + sync_lock_status = tvp514x_read_reg(decoder->client, + REG_STATUS1); + if (lock_mask == (sync_lock_status & lock_mask)) + break; /* Input detected */ + } + + if ((current_std == STD_INVALID) || (try_count < 0)) + return -EINVAL; + + decoder->current_std = current_std; + decoder->route.input = route->input; + decoder->route.output = route->output; + + v4l_dbg(1, debug, decoder->client, + "Input set to: %d, std : %d", + input_sel, current_std); + + return 0; +} + +/** + * ioctl_queryctrl - V4L2 decoder interface handler for VIDIOC_QUERYCTRL ioctl + * @s: pointer to standard V4L2 device structure + * @qctrl: standard V4L2 v4l2_queryctrl structure + * + * If the requested control is supported, returns the control information. + * Otherwise, returns -EINVAL if the control is not supported. + */ +static int +ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) +{ + struct tvp514x_decoder *decoder = s->priv; + int err = -EINVAL; + + if (qctrl == NULL) + return err; + + switch (qctrl->id) { + case V4L2_CID_BRIGHTNESS: + /* Brightness supported is same as standard one (0-255), + * so make use of standard API provided. + */ + err = v4l2_ctrl_query_fill_std(qctrl); + break; + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + /* Saturation and Contrast supported is - + * Contrast: 0 - 255 (Default - 128) + * Saturation: 0 - 255 (Default - 128) + */ + err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128); + break; + case V4L2_CID_HUE: + /* Hue Supported is - + * Hue - -180 - +180 (Default - 0, Step - +180) + */ + err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0); + break; + case V4L2_CID_AUTOGAIN: + /* Autogain is either 0 or 1*/ + memcpy(qctrl, &tvp514x_autogain_ctrl, + sizeof(struct v4l2_queryctrl)); + err = 0; + break; + default: + v4l_err(decoder->client, + "invalid control id %d\n", qctrl->id); + return err; + } + + v4l_dbg(1, debug, decoder->client, + "Query Control: %s : Min - %d, Max - %d, Def - %d", + qctrl->name, + qctrl->minimum, + qctrl->maximum, + qctrl->default_value); + + return err; +} + +/** + * ioctl_g_ctrl - V4L2 decoder interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @ctrl: pointer to v4l2_control structure + * + * If the requested control is supported, returns the control's current + * value from the decoder. Otherwise, returns -EINVAL if the control is not + * supported. + */ +static int +ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) +{ + struct tvp514x_decoder *decoder = s->priv; + + if (ctrl == NULL) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = tvp514x_reg_list[REG_BRIGHTNESS].val; + break; + case V4L2_CID_CONTRAST: + ctrl->value = tvp514x_reg_list[REG_CONTRAST].val; + break; + case V4L2_CID_SATURATION: + ctrl->value = tvp514x_reg_list[REG_SATURATION].val; + break; + case V4L2_CID_HUE: + ctrl->value = tvp514x_reg_list[REG_HUE].val; + if (ctrl->value == 0x7F) + ctrl->value = 180; + else if (ctrl->value == 0x80) + ctrl->value = -180; + else + ctrl->value = 0; + + break; + case V4L2_CID_AUTOGAIN: + ctrl->value = tvp514x_reg_list[REG_AFE_GAIN_CTRL].val; + if ((ctrl->value & 0x3) == 3) + ctrl->value = 1; + else + ctrl->value = 0; + + break; + default: + v4l_err(decoder->client, + "invalid control id %d\n", ctrl->id); + return -EINVAL; + } + + v4l_dbg(1, debug, decoder->client, + "Get Control: ID - %d - %d", + ctrl->id, ctrl->value); + return 0; +} + +/** + * ioctl_s_ctrl - V4L2 decoder interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @ctrl: pointer to v4l2_control structure + * + * If the requested control is supported, sets the control's current + * value in HW. Otherwise, returns -EINVAL if the control is not supported. + */ +static int +ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) +{ + struct tvp514x_decoder *decoder = s->priv; + int err = -EINVAL, value; + + if (ctrl == NULL) + return err; + + value = (__s32) ctrl->value; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(decoder->client, + "invalid brightness setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_BRIGHTNESS, + value); + if (err) + return err; + tvp514x_reg_list[REG_BRIGHTNESS].val = value; + break; + case V4L2_CID_CONTRAST: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(decoder->client, + "invalid contrast setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_CONTRAST, + value); + if (err) + return err; + tvp514x_reg_list[REG_CONTRAST].val = value; + break; + case V4L2_CID_SATURATION: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(decoder->client, + "invalid saturation setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_SATURATION, + value); + if (err) + return err; + tvp514x_reg_list[REG_SATURATION].val = value; + break; + case V4L2_CID_HUE: + if (value == 180) + value = 0x7F; + else if (value == -180) + value = 0x80; + else if (value == 0) + value = 0; + else { + v4l_err(decoder->client, + "invalid hue setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_HUE, + value); + if (err) + return err; + tvp514x_reg_list[REG_HUE].val = value; + break; + case V4L2_CID_AUTOGAIN: + if (value == 1) + value = 0x0F; + else if (value == 0) + value = 0x0C; + else { + v4l_err(decoder->client, + "invalid auto gain setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_AFE_GAIN_CTRL, + value); + if (err) + return err; + tvp514x_reg_list[REG_AFE_GAIN_CTRL].val = value; + break; + default: + v4l_err(decoder->client, + "invalid control id %d\n", ctrl->id); + return err; + } + + v4l_dbg(1, debug, decoder->client, + "Set Control: ID - %d - %d", + ctrl->id, ctrl->value); + + return err; +} + +/** + * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure + * + * Implement the VIDIOC_ENUM_FMT ioctl to enumerate supported formats + */ +static int +ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) +{ + struct tvp514x_decoder *decoder = s->priv; + int index; + + if (fmt == NULL) + return -EINVAL; + + index = fmt->index; + if ((index >= decoder->num_fmts) || (index < 0)) + return -EINVAL; /* Index out of bound */ + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + memcpy(fmt, &decoder->fmt_list[index], + sizeof(struct v4l2_fmtdesc)); + + v4l_dbg(1, debug, decoder->client, + "Current FMT: index - %d (%s)", + decoder->fmt_list[index].index, + decoder->fmt_list[index].description); + return 0; +} + +/** + * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure + * + * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This + * ioctl is used to negotiate the image capture size and pixel format + * without actually making it take effect. + */ +static int +ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct tvp514x_decoder *decoder = s->priv; + int ifmt; + struct v4l2_pix_format *pix; + enum tvp514x_std current_std; + + if (f == NULL) + return -EINVAL; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + pix = &f->fmt.pix; + + /* Calculate height and width based on current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + decoder->current_std = current_std; + pix->width = decoder->std_list[current_std].width; + pix->height = decoder->std_list[current_std].height; + + for (ifmt = 0; ifmt < decoder->num_fmts; ifmt++) { + if (pix->pixelformat == + decoder->fmt_list[ifmt].pixelformat) + break; + } + if (ifmt == decoder->num_fmts) + ifmt = 0; /* None of the format matched, select default */ + pix->pixelformat = decoder->fmt_list[ifmt].pixelformat; + + pix->field = V4L2_FIELD_INTERLACED; + pix->bytesperline = pix->width * 2; + pix->sizeimage = pix->bytesperline * pix->height; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->priv = 0; + + v4l_dbg(1, debug, decoder->client, + "Try FMT: pixelformat - %s, bytesperline - %d" + "Width - %d, Height - %d", + decoder->fmt_list[ifmt].description, pix->bytesperline, + pix->width, pix->height); + return 0; +} + +/** + * ioctl_s_fmt_cap - V4L2 decoder interface handler for VIDIOC_S_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure + * + * If the requested format is supported, configures the HW to use that + * format, returns error code if format not supported or HW can't be + * correctly configured. + */ +static int +ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct tvp514x_decoder *decoder = s->priv; + struct v4l2_pix_format *pix; + int rval; + + if (f == NULL) + return -EINVAL; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + pix = &f->fmt.pix; + rval = ioctl_try_fmt_cap(s, f); + if (rval) + return rval; + + decoder->pix = *pix; + + return rval; +} + +/** + * ioctl_g_fmt_cap - V4L2 decoder interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the decoder's current pixel format in the v4l2_format + * parameter. + */ +static int +ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct tvp514x_decoder *decoder = s->priv; + + if (f == NULL) + return -EINVAL; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + f->fmt.pix = decoder->pix; + + v4l_dbg(1, debug, decoder->client, + "Current FMT: bytesperline - %d" + "Width - %d, Height - %d", + decoder->pix.bytesperline, + decoder->pix.width, decoder->pix.height); + return 0; +} + +/** + * ioctl_g_parm - V4L2 decoder interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the decoder's video CAPTURE parameters. + */ +static int +ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct tvp514x_decoder *decoder = s->priv; + struct v4l2_captureparm *cparm; + enum tvp514x_std current_std; + + if (a == NULL) + return -EINVAL; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + /* get the current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + decoder->current_std = current_std; + + cparm = &a->parm.capture; + cparm->capability = V4L2_CAP_TIMEPERFRAME; + cparm->timeperframe = + decoder->std_list[current_std].standard.frameperiod; + + return 0; +} + +/** + * ioctl_s_parm - V4L2 decoder interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the decoder to use the input parameters, if possible. If + * not possible, returns the appropriate error code. + */ +static int +ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct tvp514x_decoder *decoder = s->priv; + struct v4l2_fract *timeperframe; + enum tvp514x_std current_std; + + if (a == NULL) + return -EINVAL; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + timeperframe = &a->parm.capture.timeperframe; + + /* get the current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + decoder->current_std = current_std; + + *timeperframe = + decoder->std_list[current_std].standard.frameperiod; + + return 0; +} + +/** + * ioctl_g_ifparm - V4L2 decoder interface handler for vidioc_int_g_ifparm_num + * @s: pointer to standard V4L2 device structure + * @p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure + * + * Gets slave interface parameters. + * Calculates the required xclk value to support the requested + * clock parameters in p. This value is returned in the p + * parameter. + */ +static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) +{ + struct tvp514x_decoder *decoder = s->priv; + int rval; + + if (p == NULL) + return -EINVAL; + + if (NULL == decoder->pdata->ifparm) + return -EINVAL; + + rval = decoder->pdata->ifparm(p); + if (rval) { + v4l_err(decoder->client, "g_ifparm.Err[%d]\n", rval); + return rval; + } + + p->u.bt656.clock_curr = TVP514X_XCLK_BT656; + + return 0; +} + +/** + * ioctl_g_priv - V4L2 decoder interface handler for vidioc_int_g_priv_num + * @s: pointer to standard V4L2 device structure + * @p: void pointer to hold decoder's private data address + * + * Returns device's (decoder's) private data area address in p parameter + */ +static int ioctl_g_priv(struct v4l2_int_device *s, void *p) +{ + struct tvp514x_decoder *decoder = s->priv; + + if (NULL == decoder->pdata->priv_data_set) + return -EINVAL; + + return decoder->pdata->priv_data_set(p); +} + +/** + * ioctl_s_power - V4L2 decoder interface handler for vidioc_int_s_power_num + * @s: pointer to standard V4L2 device structure + * @on: power state to which device is to be set + * + * Sets devices power state to requrested state, if possible. + */ +static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on) +{ + struct tvp514x_decoder *decoder = s->priv; + int err = 0; + + switch (on) { + case V4L2_POWER_OFF: + /* Power Down Sequence */ + err = + tvp514x_write_reg(decoder->client, REG_OPERATION_MODE, + 0x01); + /* Disable mux for TVP5146/47 decoder data path */ + if (decoder->pdata->power_set) + err |= decoder->pdata->power_set(on); + decoder->state = STATE_NOT_DETECTED; + break; + + case V4L2_POWER_STANDBY: + if (decoder->pdata->power_set) + err = decoder->pdata->power_set(on); + break; + + case V4L2_POWER_ON: + /* Enable mux for TVP5146/47 decoder data path */ + if ((decoder->pdata->power_set) && + (decoder->state == STATE_NOT_DETECTED)) { + int i; + struct tvp514x_init_seq *int_seq = + (struct tvp514x_init_seq *) + decoder->id->driver_data; + + err = decoder->pdata->power_set(on); + + /* Power Up Sequence */ + for (i = 0; i < int_seq->no_regs; i++) { + err |= tvp514x_write_reg(decoder->client, + int_seq->init_reg_seq[i].reg, + int_seq->init_reg_seq[i].val); + } + /* Detect the sensor is not already detected */ + err |= tvp514x_detect(decoder); + if (err) { + v4l_err(decoder->client, + "Unable to detect decoder\n"); + return err; + } + } + err |= tvp514x_configure(decoder); + break; + + default: + err = -ENODEV; + break; + } + + return err; +} + +/** + * ioctl_init - V4L2 decoder interface handler for VIDIOC_INT_INIT + * @s: pointer to standard V4L2 device structure + * + * Initialize the decoder device (calls tvp514x_configure()) + */ +static int ioctl_init(struct v4l2_int_device *s) +{ + struct tvp514x_decoder *decoder = s->priv; + + /* Set default standard to auto */ + tvp514x_reg_list[REG_VIDEO_STD].val = + VIDEO_STD_AUTO_SWITCH_BIT; + + return tvp514x_configure(decoder); +} + +/** + * ioctl_dev_exit - V4L2 decoder interface handler for vidioc_int_dev_exit_num + * @s: pointer to standard V4L2 device structure + * + * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init. + */ +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + return 0; +} + +/** + * ioctl_dev_init - V4L2 decoder interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. Returns 0 if + * TVP5146/47 device could be found, otherwise returns appropriate error. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct tvp514x_decoder *decoder = s->priv; + int err; + + err = tvp514x_detect(decoder); + if (err < 0) { + v4l_err(decoder->client, + "Unable to detect decoder\n"); + return err; + } + + v4l_info(decoder->client, + "chip version 0x%.2x detected\n", decoder->ver); + + return 0; +} + +static struct v4l2_int_ioctl_desc tvp514x_ioctl_desc[] = { + {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*) ioctl_dev_init}, + {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func*) ioctl_dev_exit}, + {vidioc_int_s_power_num, (v4l2_int_ioctl_func*) ioctl_s_power}, + {vidioc_int_g_priv_num, (v4l2_int_ioctl_func*) ioctl_g_priv}, + {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*) ioctl_g_ifparm}, + {vidioc_int_init_num, (v4l2_int_ioctl_func*) ioctl_init}, + {vidioc_int_enum_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, + {vidioc_int_try_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_try_fmt_cap}, + {vidioc_int_g_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_g_fmt_cap}, + {vidioc_int_s_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_s_fmt_cap}, + {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm}, + {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm}, + {vidioc_int_queryctrl_num, + (v4l2_int_ioctl_func *) ioctl_queryctrl}, + {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl}, + {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl}, + {vidioc_int_querystd_num, (v4l2_int_ioctl_func *) ioctl_querystd}, + {vidioc_int_s_std_num, (v4l2_int_ioctl_func *) ioctl_s_std}, + {vidioc_int_s_video_routing_num, + (v4l2_int_ioctl_func *) ioctl_s_routing}, +}; + +static struct v4l2_int_slave tvp514x_slave = { + .ioctls = tvp514x_ioctl_desc, + .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc), +}; + +static struct tvp514x_decoder tvp514x_dev = { + .state = STATE_NOT_DETECTED, + + .fmt_list = tvp514x_fmt_list, + .num_fmts = ARRAY_SIZE(tvp514x_fmt_list), + + .pix = { /* Default to NTSC 8-bit YUV 422 */ + .width = NTSC_NUM_ACTIVE_PIXELS, + .height = NTSC_NUM_ACTIVE_LINES, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_INTERLACED, + .bytesperline = NTSC_NUM_ACTIVE_PIXELS * 2, + .sizeimage = + NTSC_NUM_ACTIVE_PIXELS * 2 * NTSC_NUM_ACTIVE_LINES, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + }, + + .current_std = STD_NTSC_MJ, + .std_list = tvp514x_std_list, + .num_stds = ARRAY_SIZE(tvp514x_std_list), + +}; + +static struct v4l2_int_device tvp514x_int_device = { + .module = THIS_MODULE, + .name = TVP514X_MODULE_NAME, + .priv = &tvp514x_dev, + .type = v4l2_int_type_slave, + .u = { + .slave = &tvp514x_slave, + }, +}; + +/** + * tvp514x_probe - decoder driver i2c probe handler + * @client: i2c driver client device structure + * + * Register decoder as an i2c client device and V4L2 + * device. + */ +static int +tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct tvp514x_decoder *decoder = &tvp514x_dev; + int err; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + decoder->pdata = client->dev.platform_data; + if (!decoder->pdata) { + v4l_err(client, "No platform data\n!!"); + return -ENODEV; + } + /* + * Fetch platform specific data, and configure the + * tvp514x_reg_list[] accordingly. Since this is one + * time configuration, no need to preserve. + */ + tvp514x_reg_list[REG_OUTPUT_FORMATTER2].val |= + (decoder->pdata->clk_polarity << 1); + tvp514x_reg_list[REG_SYNC_CONTROL].val |= + ((decoder->pdata->hs_polarity << 2) | + (decoder->pdata->vs_polarity << 3)); + /* + * Save the id data, required for power up sequence + */ + decoder->id = (struct i2c_device_id *)id; + /* Attach to Master */ + strcpy(tvp514x_int_device.u.slave->attach_to, decoder->pdata->master); + decoder->v4l2_int_device = &tvp514x_int_device; + decoder->client = client; + i2c_set_clientdata(client, decoder); + + /* Register with V4L2 layer as slave device */ + err = v4l2_int_device_register(decoder->v4l2_int_device); + if (err) { + i2c_set_clientdata(client, NULL); + v4l_err(client, + "Unable to register to v4l2. Err[%d]\n", err); + + } else + v4l_info(client, "Registered to v4l2 master %s!!\n", + decoder->pdata->master); + + return 0; +} + +/** + * tvp514x_remove - decoder driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister decoder as an i2c client device and V4L2 + * device. Complement of tvp514x_probe(). + */ +static int __exit tvp514x_remove(struct i2c_client *client) +{ + struct tvp514x_decoder *decoder = i2c_get_clientdata(client); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + v4l2_int_device_unregister(decoder->v4l2_int_device); + i2c_set_clientdata(client, NULL); + + return 0; +} +/* + * TVP5146 Init/Power on Sequence + */ +static const struct tvp514x_reg tvp5146_init_reg_seq[] = { + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, +}; +static const struct tvp514x_init_seq tvp5146_init = { + .no_regs = ARRAY_SIZE(tvp5146_init_reg_seq), + .init_reg_seq = tvp5146_init_reg_seq, +}; +/* + * TVP5147 Init/Power on Sequence + */ +static const struct tvp514x_reg tvp5147_init_reg_seq[] = { + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, +}; +static const struct tvp514x_init_seq tvp5147_init = { + .no_regs = ARRAY_SIZE(tvp5147_init_reg_seq), + .init_reg_seq = tvp5147_init_reg_seq, +}; +/* + * TVP5146M2/TVP5147M1 Init/Power on Sequence + */ +static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, +}; +static const struct tvp514x_init_seq tvp514xm_init = { + .no_regs = ARRAY_SIZE(tvp514xm_init_reg_seq), + .init_reg_seq = tvp514xm_init_reg_seq, +}; +/* + * I2C Device Table - + * + * name - Name of the actual device/chip. + * driver_data - Driver data + */ +static const struct i2c_device_id tvp514x_id[] = { + {"tvp5146", (unsigned long)&tvp5146_init}, + {"tvp5146m2", (unsigned long)&tvp514xm_init}, + {"tvp5147", (unsigned long)&tvp5147_init}, + {"tvp5147m1", (unsigned long)&tvp514xm_init}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tvp514x_id); + +static struct i2c_driver tvp514x_i2c_driver = { + .driver = { + .name = TVP514X_MODULE_NAME, + .owner = THIS_MODULE, + }, + .probe = tvp514x_probe, + .remove = __exit_p(tvp514x_remove), + .id_table = tvp514x_id, +}; + +/** + * tvp514x_init + * + * Module init function + */ +static int __init tvp514x_init(void) +{ + return i2c_add_driver(&tvp514x_i2c_driver); +} + +/** + * tvp514x_cleanup + * + * Module exit function + */ +static void __exit tvp514x_cleanup(void) +{ + i2c_del_driver(&tvp514x_i2c_driver); +} + +module_init(tvp514x_init); +module_exit(tvp514x_cleanup); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TVP514X linux decoder driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/tvp514x_regs.h b/drivers/media/video/tvp514x_regs.h new file mode 100644 index 0000000..351620a --- /dev/null +++ b/drivers/media/video/tvp514x_regs.h @@ -0,0 +1,297 @@ +/* + * drivers/media/video/tvp514x_regs.h + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath <hvaibhav@ti.com> + * + * Contributors: + * Sivaraj R <sivaraj@ti.com> + * Brijesh R Jadav <brijesh.j@ti.com> + * Hardik Shah <hardik.shah@ti.com> + * Manjunath Hadli <mrh@ti.com> + * Karicheri Muralidharan <m-karicheri2@ti.com> + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 _TVP514X_REGS_H +#define _TVP514X_REGS_H + +/* + * TVP5146/47 registers + */ +#define REG_INPUT_SEL (0x00) +#define REG_AFE_GAIN_CTRL (0x01) +#define REG_VIDEO_STD (0x02) +#define REG_OPERATION_MODE (0x03) +#define REG_AUTOSWITCH_MASK (0x04) + +#define REG_COLOR_KILLER (0x05) +#define REG_LUMA_CONTROL1 (0x06) +#define REG_LUMA_CONTROL2 (0x07) +#define REG_LUMA_CONTROL3 (0x08) + +#define REG_BRIGHTNESS (0x09) +#define REG_CONTRAST (0x0A) +#define REG_SATURATION (0x0B) +#define REG_HUE (0x0C) + +#define REG_CHROMA_CONTROL1 (0x0D) +#define REG_CHROMA_CONTROL2 (0x0E) + +/* 0x0F Reserved */ + +#define REG_COMP_PR_SATURATION (0x10) +#define REG_COMP_Y_CONTRAST (0x11) +#define REG_COMP_PB_SATURATION (0x12) + +/* 0x13 Reserved */ + +#define REG_COMP_Y_BRIGHTNESS (0x14) + +/* 0x15 Reserved */ + +#define REG_AVID_START_PIXEL_LSB (0x16) +#define REG_AVID_START_PIXEL_MSB (0x17) +#define REG_AVID_STOP_PIXEL_LSB (0x18) +#define REG_AVID_STOP_PIXEL_MSB (0x19) + +#define REG_HSYNC_START_PIXEL_LSB (0x1A) +#define REG_HSYNC_START_PIXEL_MSB (0x1B) +#define REG_HSYNC_STOP_PIXEL_LSB (0x1C) +#define REG_HSYNC_STOP_PIXEL_MSB (0x1D) + +#define REG_VSYNC_START_LINE_LSB (0x1E) +#define REG_VSYNC_START_LINE_MSB (0x1F) +#define REG_VSYNC_STOP_LINE_LSB (0x20) +#define REG_VSYNC_STOP_LINE_MSB (0x21) + +#define REG_VBLK_START_LINE_LSB (0x22) +#define REG_VBLK_START_LINE_MSB (0x23) +#define REG_VBLK_STOP_LINE_LSB (0x24) +#define REG_VBLK_STOP_LINE_MSB (0x25) + +/* 0x26 - 0x27 Reserved */ + +#define REG_FAST_SWTICH_CONTROL (0x28) + +/* 0x29 Reserved */ + +#define REG_FAST_SWTICH_SCART_DELAY (0x2A) + +/* 0x2B Reserved */ + +#define REG_SCART_DELAY (0x2C) +#define REG_CTI_DELAY (0x2D) +#define REG_CTI_CONTROL (0x2E) + +/* 0x2F - 0x31 Reserved */ + +#define REG_SYNC_CONTROL (0x32) +#define REG_OUTPUT_FORMATTER1 (0x33) +#define REG_OUTPUT_FORMATTER2 (0x34) +#define REG_OUTPUT_FORMATTER3 (0x35) +#define REG_OUTPUT_FORMATTER4 (0x36) +#define REG_OUTPUT_FORMATTER5 (0x37) +#define REG_OUTPUT_FORMATTER6 (0x38) +#define REG_CLEAR_LOST_LOCK (0x39) + +#define REG_STATUS1 (0x3A) +#define REG_STATUS2 (0x3B) + +#define REG_AGC_GAIN_STATUS_LSB (0x3C) +#define REG_AGC_GAIN_STATUS_MSB (0x3D) + +/* 0x3E Reserved */ + +#define REG_VIDEO_STD_STATUS (0x3F) +#define REG_GPIO_INPUT1 (0x40) +#define REG_GPIO_INPUT2 (0x41) + +/* 0x42 - 0x45 Reserved */ + +#define REG_AFE_COARSE_GAIN_CH1 (0x46) +#define REG_AFE_COARSE_GAIN_CH2 (0x47) +#define REG_AFE_COARSE_GAIN_CH3 (0x48) +#define REG_AFE_COARSE_GAIN_CH4 (0x49) + +#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A) +#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B) +#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C) +#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D) +#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E) +#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F) +#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50) +#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51) + +/* 0x52 - 0x68 Reserved */ + +#define REG_FBIT_VBIT_CONTROL1 (0x69) + +/* 0x6A - 0x6B Reserved */ + +#define REG_BACKEND_AGC_CONTROL (0x6C) + +/* 0x6D - 0x6E Reserved */ + +#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F) +#define REG_ROM_VERSION (0x70) + +/* 0x71 - 0x73 Reserved */ + +#define REG_AGC_WHITE_PEAK_PROCESSING (0x74) +#define REG_FBIT_VBIT_CONTROL2 (0x75) +#define REG_VCR_TRICK_MODE_CONTROL (0x76) +#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77) +#define REG_AGC_INCREMENT_SPEED (0x78) +#define REG_AGC_INCREMENT_DELAY (0x79) + +/* 0x7A - 0x7F Reserved */ + +#define REG_CHIP_ID_MSB (0x80) +#define REG_CHIP_ID_LSB (0x81) + +/* 0x82 Reserved */ + +#define REG_CPLL_SPEED_CONTROL (0x83) + +/* 0x84 - 0x96 Reserved */ + +#define REG_STATUS_REQUEST (0x97) + +/* 0x98 - 0x99 Reserved */ + +#define REG_VERTICAL_LINE_COUNT_LSB (0x9A) +#define REG_VERTICAL_LINE_COUNT_MSB (0x9B) + +/* 0x9C - 0x9D Reserved */ + +#define REG_AGC_DECREMENT_DELAY (0x9E) + +/* 0x9F - 0xB0 Reserved */ + +#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1) +#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2) +#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3) +#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4) +#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5) +#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6) +#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7) +#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8) +#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9) +#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA) +#define REG_VDP_TTX_FILTER_CONTROL (0xBB) +#define REG_VDP_FIFO_WORD_COUNT (0xBC) +#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD) + +/* 0xBE Reserved */ + +#define REG_VDP_FIFO_RESET (0xBF) +#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0) +#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1) +#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2) +#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3) + +/* 0xC4 - 0xD5 Reserved */ + +#define REG_VDP_LINE_START (0xD6) +#define REG_VDP_LINE_STOP (0xD7) +#define REG_VDP_GLOBAL_LINE_MODE (0xD8) +#define REG_VDP_FULL_FIELD_ENABLE (0xD9) +#define REG_VDP_FULL_FIELD_MODE (0xDA) + +/* 0xDB - 0xDF Reserved */ + +#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0) +#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1) +#define REG_FIFO_READ_DATA (0xE2) + +/* 0xE3 - 0xE7 Reserved */ + +#define REG_VBUS_ADDRESS_ACCESS1 (0xE8) +#define REG_VBUS_ADDRESS_ACCESS2 (0xE9) +#define REG_VBUS_ADDRESS_ACCESS3 (0xEA) + +/* 0xEB - 0xEF Reserved */ + +#define REG_INTERRUPT_RAW_STATUS0 (0xF0) +#define REG_INTERRUPT_RAW_STATUS1 (0xF1) +#define REG_INTERRUPT_STATUS0 (0xF2) +#define REG_INTERRUPT_STATUS1 (0xF3) +#define REG_INTERRUPT_MASK0 (0xF4) +#define REG_INTERRUPT_MASK1 (0xF5) +#define REG_INTERRUPT_CLEAR0 (0xF6) +#define REG_INTERRUPT_CLEAR1 (0xF7) + +/* 0xF8 - 0xFF Reserved */ + +/* + * Mask and bit definitions of TVP5146/47 registers + */ +/* The ID values we are looking for */ +#define TVP514X_CHIP_ID_MSB (0x51) +#define TVP5146_CHIP_ID_LSB (0x46) +#define TVP5147_CHIP_ID_LSB (0x47) + +#define VIDEO_STD_MASK (0x07) +#define VIDEO_STD_AUTO_SWITCH_BIT (0x00) +#define VIDEO_STD_NTSC_MJ_BIT (0x01) +#define VIDEO_STD_PAL_BDGHIN_BIT (0x02) +#define VIDEO_STD_PAL_M_BIT (0x03) +#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04) +#define VIDEO_STD_NTSC_4_43_BIT (0x05) +#define VIDEO_STD_SECAM_BIT (0x06) +#define VIDEO_STD_PAL_60_BIT (0x07) + +/* + * Status bit + */ +#define STATUS_TV_VCR_BIT (1<<0) +#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1) +#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2) +#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3) +#define STATUS_LOST_LOCK_DETECT_BIT (1<<4) +#define STATUS_FEILD_RATE_BIT (1<<5) +#define STATUS_LINE_ALTERNATING_BIT (1<<6) +#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7) + +/* Tokens for register write */ +#define TOK_WRITE (0) /* token for write operation */ +#define TOK_TERM (1) /* terminating token */ +#define TOK_DELAY (2) /* delay token for reg list */ +#define TOK_SKIP (3) /* token to skip a register */ +/** + * struct tvp514x_reg - Structure for TVP5146/47 register initialization values + * @token - Token: TOK_WRITE, TOK_TERM etc.. + * @reg - Register offset + * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY + */ +struct tvp514x_reg { + u8 token; + u8 reg; + u32 val; +}; + +/** + * struct tvp514x_init_seq - Structure for TVP5146/47/46M2/47M1 power up + * Sequence. + * @ no_regs - Number of registers to write for power up sequence. + * @ init_reg_seq - Array of registers and respective value to write. + */ +struct tvp514x_init_seq { + unsigned int no_regs; + const struct tvp514x_reg *init_reg_seq; +}; +#endif /* ifndef _TVP514X_REGS_H */ diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 28af5ce..a388a9f 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -9,8 +9,10 @@ #include <linux/videodev2.h> #include <linux/delay.h> #include <linux/video_decoder.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/tvp5150.h> +#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-chip-ident.h> #include "tvp5150_reg.h" @@ -29,21 +31,7 @@ I2C_CLIENT_INSMOD; static int debug; module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -#define tvp5150_err(fmt, arg...) do { \ - printk(KERN_ERR "%s %d-%04x: " fmt, c->driver->driver.name, \ - i2c_adapter_id(c->adapter), c->addr , ## arg); } while (0) -#define tvp5150_info(fmt, arg...) do { \ - printk(KERN_INFO "%s %d-%04x: " fmt, c->driver->driver.name, \ - i2c_adapter_id(c->adapter), c->addr , ## arg); } while (0) -#define tvp5150_dbg(num, fmt, arg...) \ - do { \ - if (debug >= num) \ - printk(KERN_DEBUG "%s debug %d-%04x: " fmt,\ - c->driver->driver.name, \ - i2c_adapter_id(c->adapter), \ - c->addr , ## arg); } while (0) +MODULE_PARM_DESC(debug, "Debug level (0-2)"); /* supported controls */ static struct v4l2_queryctrl tvp5150_qctrl[] = { @@ -87,7 +75,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = { }; struct tvp5150 { - struct i2c_client *client; + struct v4l2_subdev sd; v4l2_std_id norm; /* Current set standard */ struct v4l2_routing route; @@ -98,49 +86,57 @@ struct tvp5150 { int sat; }; -static int tvp5150_read(struct i2c_client *c, unsigned char addr) +static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd) { + return container_of(sd, struct tvp5150, sd); +} + +static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr) +{ + struct i2c_client *c = v4l2_get_subdevdata(sd); unsigned char buffer[1]; int rc; buffer[0] = addr; if (1 != (rc = i2c_master_send(c, buffer, 1))) - tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 1)\n", rc); + v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc); msleep(10); if (1 != (rc = i2c_master_recv(c, buffer, 1))) - tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 1)\n", rc); + v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc); - tvp5150_dbg(2, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); + v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); return (buffer[0]); } -static inline void tvp5150_write(struct i2c_client *c, unsigned char addr, +static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr, unsigned char value) { + struct i2c_client *c = v4l2_get_subdevdata(sd); unsigned char buffer[2]; int rc; buffer[0] = addr; buffer[1] = value; - tvp5150_dbg(2, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]); + v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]); if (2 != (rc = i2c_master_send(c, buffer, 2))) - tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 2)\n", rc); + v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc); } -static void dump_reg_range(struct i2c_client *c, char *s, u8 init, const u8 end,int max_line) +static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init, + const u8 end, int max_line) { - int i=0; + int i = 0; - while (init!=(u8)(end+1)) { - if ((i%max_line) == 0) { - if (i>0) + while (init != (u8)(end + 1)) { + if ((i % max_line) == 0) { + if (i > 0) printk("\n"); - printk("tvp5150: %s reg 0x%02x = ",s,init); + printk("tvp5150: %s reg 0x%02x = ", s, init); } - printk("%02x ",tvp5150_read(c, init)); + printk("%02x ", tvp5150_read(sd, init)); init++; i++; @@ -148,147 +144,148 @@ static void dump_reg_range(struct i2c_client *c, char *s, u8 init, const u8 end, printk("\n"); } -static void dump_reg(struct i2c_client *c) +static int tvp5150_log_status(struct v4l2_subdev *sd) { printk("tvp5150: Video input source selection #1 = 0x%02x\n", - tvp5150_read(c, TVP5150_VD_IN_SRC_SEL_1)); + tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1)); printk("tvp5150: Analog channel controls = 0x%02x\n", - tvp5150_read(c, TVP5150_ANAL_CHL_CTL)); + tvp5150_read(sd, TVP5150_ANAL_CHL_CTL)); printk("tvp5150: Operation mode controls = 0x%02x\n", - tvp5150_read(c, TVP5150_OP_MODE_CTL)); + tvp5150_read(sd, TVP5150_OP_MODE_CTL)); printk("tvp5150: Miscellaneous controls = 0x%02x\n", - tvp5150_read(c, TVP5150_MISC_CTL)); + tvp5150_read(sd, TVP5150_MISC_CTL)); printk("tvp5150: Autoswitch mask= 0x%02x\n", - tvp5150_read(c, TVP5150_AUTOSW_MSK)); + tvp5150_read(sd, TVP5150_AUTOSW_MSK)); printk("tvp5150: Color killer threshold control = 0x%02x\n", - tvp5150_read(c, TVP5150_COLOR_KIL_THSH_CTL)); + tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL)); printk("tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n", - tvp5150_read(c, TVP5150_LUMA_PROC_CTL_1), - tvp5150_read(c, TVP5150_LUMA_PROC_CTL_2), - tvp5150_read(c, TVP5150_LUMA_PROC_CTL_3)); + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1), + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2), + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3)); printk("tvp5150: Brightness control = 0x%02x\n", - tvp5150_read(c, TVP5150_BRIGHT_CTL)); + tvp5150_read(sd, TVP5150_BRIGHT_CTL)); printk("tvp5150: Color saturation control = 0x%02x\n", - tvp5150_read(c, TVP5150_SATURATION_CTL)); + tvp5150_read(sd, TVP5150_SATURATION_CTL)); printk("tvp5150: Hue control = 0x%02x\n", - tvp5150_read(c, TVP5150_HUE_CTL)); + tvp5150_read(sd, TVP5150_HUE_CTL)); printk("tvp5150: Contrast control = 0x%02x\n", - tvp5150_read(c, TVP5150_CONTRAST_CTL)); + tvp5150_read(sd, TVP5150_CONTRAST_CTL)); printk("tvp5150: Outputs and data rates select = 0x%02x\n", - tvp5150_read(c, TVP5150_DATA_RATE_SEL)); + tvp5150_read(sd, TVP5150_DATA_RATE_SEL)); printk("tvp5150: Configuration shared pins = 0x%02x\n", - tvp5150_read(c, TVP5150_CONF_SHARED_PIN)); + tvp5150_read(sd, TVP5150_CONF_SHARED_PIN)); printk("tvp5150: Active video cropping start = 0x%02x%02x\n", - tvp5150_read(c, TVP5150_ACT_VD_CROP_ST_MSB), - tvp5150_read(c, TVP5150_ACT_VD_CROP_ST_LSB)); + tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB), + tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB)); printk("tvp5150: Active video cropping stop = 0x%02x%02x\n", - tvp5150_read(c, TVP5150_ACT_VD_CROP_STP_MSB), - tvp5150_read(c, TVP5150_ACT_VD_CROP_STP_LSB)); + tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB), + tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB)); printk("tvp5150: Genlock/RTC = 0x%02x\n", - tvp5150_read(c, TVP5150_GENLOCK)); + tvp5150_read(sd, TVP5150_GENLOCK)); printk("tvp5150: Horizontal sync start = 0x%02x\n", - tvp5150_read(c, TVP5150_HORIZ_SYNC_START)); + tvp5150_read(sd, TVP5150_HORIZ_SYNC_START)); printk("tvp5150: Vertical blanking start = 0x%02x\n", - tvp5150_read(c, TVP5150_VERT_BLANKING_START)); + tvp5150_read(sd, TVP5150_VERT_BLANKING_START)); printk("tvp5150: Vertical blanking stop = 0x%02x\n", - tvp5150_read(c, TVP5150_VERT_BLANKING_STOP)); + tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP)); printk("tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n", - tvp5150_read(c, TVP5150_CHROMA_PROC_CTL_1), - tvp5150_read(c, TVP5150_CHROMA_PROC_CTL_2)); + tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1), + tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2)); printk("tvp5150: Interrupt reset register B = 0x%02x\n", - tvp5150_read(c, TVP5150_INT_RESET_REG_B)); + tvp5150_read(sd, TVP5150_INT_RESET_REG_B)); printk("tvp5150: Interrupt enable register B = 0x%02x\n", - tvp5150_read(c, TVP5150_INT_ENABLE_REG_B)); + tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B)); printk("tvp5150: Interrupt configuration register B = 0x%02x\n", - tvp5150_read(c, TVP5150_INTT_CONFIG_REG_B)); + tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B)); printk("tvp5150: Video standard = 0x%02x\n", - tvp5150_read(c, TVP5150_VIDEO_STD)); + tvp5150_read(sd, TVP5150_VIDEO_STD)); printk("tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n", - tvp5150_read(c, TVP5150_CB_GAIN_FACT), - tvp5150_read(c, TVP5150_CR_GAIN_FACTOR)); + tvp5150_read(sd, TVP5150_CB_GAIN_FACT), + tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR)); printk("tvp5150: Macrovision on counter = 0x%02x\n", - tvp5150_read(c, TVP5150_MACROVISION_ON_CTR)); + tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR)); printk("tvp5150: Macrovision off counter = 0x%02x\n", - tvp5150_read(c, TVP5150_MACROVISION_OFF_CTR)); + tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR)); printk("tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n", - (tvp5150_read(c, TVP5150_REV_SELECT)&1)?3:4); + (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4); printk("tvp5150: Device ID = %02x%02x\n", - tvp5150_read(c, TVP5150_MSB_DEV_ID), - tvp5150_read(c, TVP5150_LSB_DEV_ID)); + tvp5150_read(sd, TVP5150_MSB_DEV_ID), + tvp5150_read(sd, TVP5150_LSB_DEV_ID)); printk("tvp5150: ROM version = (hex) %02x.%02x\n", - tvp5150_read(c, TVP5150_ROM_MAJOR_VER), - tvp5150_read(c, TVP5150_ROM_MINOR_VER)); + tvp5150_read(sd, TVP5150_ROM_MAJOR_VER), + tvp5150_read(sd, TVP5150_ROM_MINOR_VER)); printk("tvp5150: Vertical line count = 0x%02x%02x\n", - tvp5150_read(c, TVP5150_VERT_LN_COUNT_MSB), - tvp5150_read(c, TVP5150_VERT_LN_COUNT_LSB)); + tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB), + tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB)); printk("tvp5150: Interrupt status register B = 0x%02x\n", - tvp5150_read(c, TVP5150_INT_STATUS_REG_B)); + tvp5150_read(sd, TVP5150_INT_STATUS_REG_B)); printk("tvp5150: Interrupt active register B = 0x%02x\n", - tvp5150_read(c, TVP5150_INT_ACTIVE_REG_B)); + tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B)); printk("tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n", - tvp5150_read(c, TVP5150_STATUS_REG_1), - tvp5150_read(c, TVP5150_STATUS_REG_2), - tvp5150_read(c, TVP5150_STATUS_REG_3), - tvp5150_read(c, TVP5150_STATUS_REG_4), - tvp5150_read(c, TVP5150_STATUS_REG_5)); + tvp5150_read(sd, TVP5150_STATUS_REG_1), + tvp5150_read(sd, TVP5150_STATUS_REG_2), + tvp5150_read(sd, TVP5150_STATUS_REG_3), + tvp5150_read(sd, TVP5150_STATUS_REG_4), + tvp5150_read(sd, TVP5150_STATUS_REG_5)); - dump_reg_range(c,"Teletext filter 1", TVP5150_TELETEXT_FIL1_INI, - TVP5150_TELETEXT_FIL1_END,8); - dump_reg_range(c,"Teletext filter 2", TVP5150_TELETEXT_FIL2_INI, - TVP5150_TELETEXT_FIL2_END,8); + dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI, + TVP5150_TELETEXT_FIL1_END, 8); + dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI, + TVP5150_TELETEXT_FIL2_END, 8); printk("tvp5150: Teletext filter enable = 0x%02x\n", - tvp5150_read(c, TVP5150_TELETEXT_FIL_ENA)); + tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA)); printk("tvp5150: Interrupt status register A = 0x%02x\n", - tvp5150_read(c, TVP5150_INT_STATUS_REG_A)); + tvp5150_read(sd, TVP5150_INT_STATUS_REG_A)); printk("tvp5150: Interrupt enable register A = 0x%02x\n", - tvp5150_read(c, TVP5150_INT_ENABLE_REG_A)); + tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A)); printk("tvp5150: Interrupt configuration = 0x%02x\n", - tvp5150_read(c, TVP5150_INT_CONF)); + tvp5150_read(sd, TVP5150_INT_CONF)); printk("tvp5150: VDP status register = 0x%02x\n", - tvp5150_read(c, TVP5150_VDP_STATUS_REG)); + tvp5150_read(sd, TVP5150_VDP_STATUS_REG)); printk("tvp5150: FIFO word count = 0x%02x\n", - tvp5150_read(c, TVP5150_FIFO_WORD_COUNT)); + tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT)); printk("tvp5150: FIFO interrupt threshold = 0x%02x\n", - tvp5150_read(c, TVP5150_FIFO_INT_THRESHOLD)); + tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD)); printk("tvp5150: FIFO reset = 0x%02x\n", - tvp5150_read(c, TVP5150_FIFO_RESET)); + tvp5150_read(sd, TVP5150_FIFO_RESET)); printk("tvp5150: Line number interrupt = 0x%02x\n", - tvp5150_read(c, TVP5150_LINE_NUMBER_INT)); + tvp5150_read(sd, TVP5150_LINE_NUMBER_INT)); printk("tvp5150: Pixel alignment register = 0x%02x%02x\n", - tvp5150_read(c, TVP5150_PIX_ALIGN_REG_HIGH), - tvp5150_read(c, TVP5150_PIX_ALIGN_REG_LOW)); + tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH), + tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW)); printk("tvp5150: FIFO output control = 0x%02x\n", - tvp5150_read(c, TVP5150_FIFO_OUT_CTRL)); + tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL)); printk("tvp5150: Full field enable = 0x%02x\n", - tvp5150_read(c, TVP5150_FULL_FIELD_ENA)); + tvp5150_read(sd, TVP5150_FULL_FIELD_ENA)); printk("tvp5150: Full field mode register = 0x%02x\n", - tvp5150_read(c, TVP5150_FULL_FIELD_MODE_REG)); + tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG)); - dump_reg_range(c,"CC data", TVP5150_CC_DATA_INI, - TVP5150_CC_DATA_END,8); + dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI, + TVP5150_CC_DATA_END, 8); - dump_reg_range(c,"WSS data", TVP5150_WSS_DATA_INI, - TVP5150_WSS_DATA_END,8); + dump_reg_range(sd, "WSS data", TVP5150_WSS_DATA_INI, + TVP5150_WSS_DATA_END, 8); - dump_reg_range(c,"VPS data", TVP5150_VPS_DATA_INI, - TVP5150_VPS_DATA_END,8); + dump_reg_range(sd, "VPS data", TVP5150_VPS_DATA_INI, + TVP5150_VPS_DATA_END, 8); - dump_reg_range(c,"VITC data", TVP5150_VITC_DATA_INI, - TVP5150_VITC_DATA_END,10); + dump_reg_range(sd, "VITC data", TVP5150_VITC_DATA_INI, + TVP5150_VITC_DATA_END, 10); - dump_reg_range(c,"Line mode", TVP5150_LINE_MODE_INI, - TVP5150_LINE_MODE_END,8); + dump_reg_range(sd, "Line mode", TVP5150_LINE_MODE_INI, + TVP5150_LINE_MODE_END, 8); + return 0; } /**************************************************************************** Basic functions ****************************************************************************/ -static inline void tvp5150_selmux(struct i2c_client *c) +static inline void tvp5150_selmux(struct v4l2_subdev *sd) { int opmode=0; - struct tvp5150 *decoder = i2c_get_clientdata(c); + struct tvp5150 *decoder = to_tvp5150(sd); int input = 0; unsigned char val; @@ -309,23 +306,23 @@ static inline void tvp5150_selmux(struct i2c_client *c) break; } - tvp5150_dbg( 1, "Selecting video route: route input=%i, output=%i " + v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i " "=> tvp5150 input=%i, opmode=%i\n", decoder->route.input,decoder->route.output, input, opmode ); - tvp5150_write(c, TVP5150_OP_MODE_CTL, opmode); - tvp5150_write(c, TVP5150_VD_IN_SRC_SEL_1, input); + tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode); + tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input); /* Svideo should enable YCrCb output and disable GPCL output * For Composite and TV, it should be the reverse */ - val = tvp5150_read(c, TVP5150_MISC_CTL); + val = tvp5150_read(sd, TVP5150_MISC_CTL); if (decoder->route.input == TVP5150_SVIDEO) val = (val & ~0x40) | 0x10; else val = (val & ~0x10) | 0x40; - tvp5150_write(c, TVP5150_MISC_CTL, val); + tvp5150_write(sd, TVP5150_MISC_CTL, val); }; struct i2c_reg_value { @@ -593,35 +590,35 @@ static struct i2c_vbi_ram_value vbi_ram_default[] = { (u16)-1 } }; -static int tvp5150_write_inittab(struct i2c_client *c, +static int tvp5150_write_inittab(struct v4l2_subdev *sd, const struct i2c_reg_value *regs) { while (regs->reg != 0xff) { - tvp5150_write(c, regs->reg, regs->value); + tvp5150_write(sd, regs->reg, regs->value); regs++; } return 0; } -static int tvp5150_vdp_init(struct i2c_client *c, +static int tvp5150_vdp_init(struct v4l2_subdev *sd, const struct i2c_vbi_ram_value *regs) { unsigned int i; /* Disable Full Field */ - tvp5150_write(c, TVP5150_FULL_FIELD_ENA, 0); + tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); /* Before programming, Line mode should be at 0xff */ - for (i=TVP5150_LINE_MODE_INI; i<=TVP5150_LINE_MODE_END; i++) - tvp5150_write(c, i, 0xff); + for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) + tvp5150_write(sd, i, 0xff); /* Load Ram Table */ - while (regs->reg != (u16)-1 ) { - tvp5150_write(c, TVP5150_CONF_RAM_ADDR_HIGH,regs->reg>>8); - tvp5150_write(c, TVP5150_CONF_RAM_ADDR_LOW,regs->reg); + while (regs->reg != (u16)-1) { + tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8); + tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg); - for (i=0;i<16;i++) - tvp5150_write(c, TVP5150_VDP_CONF_RAM_DATA,regs->values[i]); + for (i = 0; i < 16; i++) + tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]); regs++; } @@ -629,11 +626,13 @@ static int tvp5150_vdp_init(struct i2c_client *c, } /* Fills VBI capabilities based on i2c_vbi_ram_value struct */ -static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs, +static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap) { + const struct i2c_vbi_ram_value *regs = vbi_ram_default; int line; + v4l2_dbg(1, debug, sd, "VIDIOC_G_SLICED_VBI_CAP\n"); memset(cap, 0, sizeof *cap); while (regs->reg != (u16)-1 ) { @@ -644,6 +643,7 @@ static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs, regs++; } + return 0; } /* Set vbi processing @@ -659,18 +659,18 @@ static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs, * LSB = field1 * MSB = field2 */ -static int tvp5150_set_vbi(struct i2c_client *c, +static int tvp5150_set_vbi(struct v4l2_subdev *sd, const struct i2c_vbi_ram_value *regs, unsigned int type,u8 flags, int line, const int fields) { - struct tvp5150 *decoder = i2c_get_clientdata(c); - v4l2_std_id std=decoder->norm; + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std = decoder->norm; u8 reg; int pos=0; if (std == V4L2_STD_ALL) { - tvp5150_err("VBI can't be configured without knowing number of lines\n"); + v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); return 0; } else if (std & V4L2_STD_625_50) { /* Don't follow NTSC Line number convension */ @@ -698,163 +698,186 @@ static int tvp5150_set_vbi(struct i2c_client *c, reg=((line-6)<<1)+TVP5150_LINE_MODE_INI; if (fields&1) { - tvp5150_write(c, reg, type); + tvp5150_write(sd, reg, type); } if (fields&2) { - tvp5150_write(c, reg+1, type); + tvp5150_write(sd, reg+1, type); } return type; } -static int tvp5150_get_vbi(struct i2c_client *c, +static int tvp5150_get_vbi(struct v4l2_subdev *sd, const struct i2c_vbi_ram_value *regs, int line) { - struct tvp5150 *decoder = i2c_get_clientdata(c); - v4l2_std_id std=decoder->norm; + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std = decoder->norm; u8 reg; - int pos, type=0; + int pos, type = 0; if (std == V4L2_STD_ALL) { - tvp5150_err("VBI can't be configured without knowing number of lines\n"); + v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); return 0; } else if (std & V4L2_STD_625_50) { /* Don't follow NTSC Line number convension */ line += 3; } - if (line<6||line>27) + if (line < 6 || line > 27) return 0; - reg=((line-6)<<1)+TVP5150_LINE_MODE_INI; + reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI; - pos=tvp5150_read(c, reg)&0x0f; - if (pos<0x0f) - type=regs[pos].type.vbi_type; + pos = tvp5150_read(sd, reg) & 0x0f; + if (pos < 0x0f) + type = regs[pos].type.vbi_type; - pos=tvp5150_read(c, reg+1)&0x0f; - if (pos<0x0f) - type|=regs[pos].type.vbi_type; + pos = tvp5150_read(sd, reg + 1) & 0x0f; + if (pos < 0x0f) + type |= regs[pos].type.vbi_type; return type; } -static int tvp5150_set_std(struct i2c_client *c, v4l2_std_id std) + +static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std) { - struct tvp5150 *decoder = i2c_get_clientdata(c); - int fmt=0; + struct tvp5150 *decoder = to_tvp5150(sd); + int fmt = 0; - decoder->norm=std; + decoder->norm = std; /* First tests should be against specific std */ if (std == V4L2_STD_ALL) { - fmt=0; /* Autodetect mode */ + fmt = 0; /* Autodetect mode */ } else if (std & V4L2_STD_NTSC_443) { - fmt=0xa; + fmt = 0xa; } else if (std & V4L2_STD_PAL_M) { - fmt=0x6; - } else if (std & (V4L2_STD_PAL_N| V4L2_STD_PAL_Nc)) { - fmt=0x8; + fmt = 0x6; + } else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) { + fmt = 0x8; } else { /* Then, test against generic ones */ - if (std & V4L2_STD_NTSC) { - fmt=0x2; - } else if (std & V4L2_STD_PAL) { - fmt=0x4; - } else if (std & V4L2_STD_SECAM) { - fmt=0xc; - } + if (std & V4L2_STD_NTSC) + fmt = 0x2; + else if (std & V4L2_STD_PAL) + fmt = 0x4; + else if (std & V4L2_STD_SECAM) + fmt = 0xc; } - tvp5150_dbg(1,"Set video std register to %d.\n",fmt); - tvp5150_write(c, TVP5150_VIDEO_STD, fmt); - + v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt); + tvp5150_write(sd, TVP5150_VIDEO_STD, fmt); return 0; } -static inline void tvp5150_reset(struct i2c_client *c) +static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + + if (decoder->norm == std) + return 0; + + return tvp5150_set_std(sd, std); +} + +static int tvp5150_reset(struct v4l2_subdev *sd, u32 val) { + struct tvp5150 *decoder = to_tvp5150(sd); u8 msb_id, lsb_id, msb_rom, lsb_rom; - struct tvp5150 *decoder = i2c_get_clientdata(c); - msb_id=tvp5150_read(c,TVP5150_MSB_DEV_ID); - lsb_id=tvp5150_read(c,TVP5150_LSB_DEV_ID); - msb_rom=tvp5150_read(c,TVP5150_ROM_MAJOR_VER); - lsb_rom=tvp5150_read(c,TVP5150_ROM_MINOR_VER); + msb_id = tvp5150_read(sd, TVP5150_MSB_DEV_ID); + lsb_id = tvp5150_read(sd, TVP5150_LSB_DEV_ID); + msb_rom = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER); + lsb_rom = tvp5150_read(sd, TVP5150_ROM_MINOR_VER); - if ((msb_rom==4)&&(lsb_rom==0)) { /* Is TVP5150AM1 */ - tvp5150_info("tvp%02x%02xam1 detected.\n",msb_id, lsb_id); + if (msb_rom == 4 && lsb_rom == 0) { /* Is TVP5150AM1 */ + v4l2_info(sd, "tvp%02x%02xam1 detected.\n", msb_id, lsb_id); /* ITU-T BT.656.4 timing */ - tvp5150_write(c,TVP5150_REV_SELECT,0); + tvp5150_write(sd, TVP5150_REV_SELECT, 0); } else { - if ((msb_rom==3)||(lsb_rom==0x21)) { /* Is TVP5150A */ - tvp5150_info("tvp%02x%02xa detected.\n",msb_id, lsb_id); + if (msb_rom == 3 || lsb_rom == 0x21) { /* Is TVP5150A */ + v4l2_info(sd, "tvp%02x%02xa detected.\n", msb_id, lsb_id); } else { - tvp5150_info("*** unknown tvp%02x%02x chip detected.\n",msb_id,lsb_id); - tvp5150_info("*** Rom ver is %d.%d\n",msb_rom,lsb_rom); + v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n", + msb_id, lsb_id); + v4l2_info(sd, "*** Rom ver is %d.%d\n", msb_rom, lsb_rom); } } /* Initializes TVP5150 to its default values */ - tvp5150_write_inittab(c, tvp5150_init_default); + tvp5150_write_inittab(sd, tvp5150_init_default); /* Initializes VDP registers */ - tvp5150_vdp_init(c, vbi_ram_default); + tvp5150_vdp_init(sd, vbi_ram_default); /* Selects decoder input */ - tvp5150_selmux(c); + tvp5150_selmux(sd); /* Initializes TVP5150 to stream enabled values */ - tvp5150_write_inittab(c, tvp5150_init_enable); + tvp5150_write_inittab(sd, tvp5150_init_enable); /* Initialize image preferences */ - tvp5150_write(c, TVP5150_BRIGHT_CTL, decoder->bright); - tvp5150_write(c, TVP5150_CONTRAST_CTL, decoder->contrast); - tvp5150_write(c, TVP5150_SATURATION_CTL, decoder->contrast); - tvp5150_write(c, TVP5150_HUE_CTL, decoder->hue); + tvp5150_write(sd, TVP5150_BRIGHT_CTL, decoder->bright); + tvp5150_write(sd, TVP5150_CONTRAST_CTL, decoder->contrast); + tvp5150_write(sd, TVP5150_SATURATION_CTL, decoder->contrast); + tvp5150_write(sd, TVP5150_HUE_CTL, decoder->hue); - tvp5150_set_std(c, decoder->norm); + tvp5150_set_std(sd, decoder->norm); + return 0; }; -static int tvp5150_get_ctrl(struct i2c_client *c, struct v4l2_control *ctrl) +static int tvp5150_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { -/* struct tvp5150 *decoder = i2c_get_clientdata(c); */ + v4l2_dbg(1, debug, sd, "VIDIOC_G_CTRL called\n"); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - ctrl->value = tvp5150_read(c, TVP5150_BRIGHT_CTL); + ctrl->value = tvp5150_read(sd, TVP5150_BRIGHT_CTL); return 0; case V4L2_CID_CONTRAST: - ctrl->value = tvp5150_read(c, TVP5150_CONTRAST_CTL); + ctrl->value = tvp5150_read(sd, TVP5150_CONTRAST_CTL); return 0; case V4L2_CID_SATURATION: - ctrl->value = tvp5150_read(c, TVP5150_SATURATION_CTL); + ctrl->value = tvp5150_read(sd, TVP5150_SATURATION_CTL); return 0; case V4L2_CID_HUE: - ctrl->value = tvp5150_read(c, TVP5150_HUE_CTL); + ctrl->value = tvp5150_read(sd, TVP5150_HUE_CTL); return 0; } return -EINVAL; } -static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl) +static int tvp5150_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { -/* struct tvp5150 *decoder = i2c_get_clientdata(c); */ + u8 i, n; + n = ARRAY_SIZE(tvp5150_qctrl); + + for (i = 0; i < n; i++) { + if (ctrl->id != tvp5150_qctrl[i].id) + continue; + if (ctrl->value < tvp5150_qctrl[i].minimum || + ctrl->value > tvp5150_qctrl[i].maximum) + return -ERANGE; + v4l2_dbg(1, debug, sd, "VIDIOC_S_CTRL: id=%d, value=%d\n", + ctrl->id, ctrl->value); + break; + } switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - tvp5150_write(c, TVP5150_BRIGHT_CTL, ctrl->value); + tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->value); return 0; case V4L2_CID_CONTRAST: - tvp5150_write(c, TVP5150_CONTRAST_CTL, ctrl->value); + tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->value); return 0; case V4L2_CID_SATURATION: - tvp5150_write(c, TVP5150_SATURATION_CTL, ctrl->value); + tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->value); return 0; case V4L2_CID_HUE: - tvp5150_write(c, TVP5150_HUE_CTL, ctrl->value); + tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->value); return 0; } return -EINVAL; @@ -863,227 +886,210 @@ static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl) /**************************************************************************** I2C Command ****************************************************************************/ -static int tvp5150_command(struct i2c_client *c, - unsigned int cmd, void *arg) -{ - struct tvp5150 *decoder = i2c_get_clientdata(c); - switch (cmd) { +static int tvp5150_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct tvp5150 *decoder = to_tvp5150(sd); - case 0: - case VIDIOC_INT_RESET: - tvp5150_reset(c); - break; - case VIDIOC_INT_G_VIDEO_ROUTING: - { - struct v4l2_routing *route = arg; + decoder->route = *route; + tvp5150_selmux(sd); + return 0; +} - *route = decoder->route; - break; +static int tvp5150_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct v4l2_sliced_vbi_format *svbi; + int i; + + /* raw vbi */ + if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + /* this is for capturing 36 raw vbi lines + if there's a way to cut off the beginning 2 vbi lines + with the tvp5150 then the vbi line count could be lowered + to 17 lines/field again, although I couldn't find a register + which could do that cropping */ + if (fmt->fmt.vbi.sample_format == V4L2_PIX_FMT_GREY) + tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); + if (fmt->fmt.vbi.count[0] == 18 && fmt->fmt.vbi.count[1] == 18) { + tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00); + tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01); + } + return 0; } - case VIDIOC_INT_S_VIDEO_ROUTING: - { - struct v4l2_routing *route = arg; + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + if (svbi->service_set != 0) { + for (i = 0; i <= 23; i++) { + svbi->service_lines[1][i] = 0; + svbi->service_lines[0][i] = + tvp5150_set_vbi(sd, vbi_ram_default, + svbi->service_lines[0][i], 0xf0, i, 3); + } + /* Enables FIFO */ + tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1); + } else { + /* Disables FIFO*/ + tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0); - decoder->route = *route; - tvp5150_selmux(c); - break; + /* Disable Full Field */ + tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); + + /* Disable Line modes */ + for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) + tvp5150_write(sd, i, 0xff); } - case VIDIOC_S_STD: - if (decoder->norm == *(v4l2_std_id *)arg) - break; - return tvp5150_set_std(c, *(v4l2_std_id *)arg); - case VIDIOC_G_STD: - *(v4l2_std_id *)arg = decoder->norm; - break; + return 0; +} - case VIDIOC_G_SLICED_VBI_CAP: - { - struct v4l2_sliced_vbi_cap *cap = arg; - tvp5150_dbg(1, "VIDIOC_G_SLICED_VBI_CAP\n"); +static int tvp5150_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct v4l2_sliced_vbi_format *svbi; + int i, mask = 0; - tvp5150_vbi_get_cap(vbi_ram_default, cap); - break; + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + memset(svbi, 0, sizeof(*svbi)); + + for (i = 0; i <= 23; i++) { + svbi->service_lines[0][i] = + tvp5150_get_vbi(sd, vbi_ram_default, i); + mask |= svbi->service_lines[0][i]; } - case VIDIOC_S_FMT: - { - struct v4l2_format *fmt; - struct v4l2_sliced_vbi_format *svbi; - int i; - - fmt = arg; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - if (svbi->service_set != 0) { - for (i = 0; i <= 23; i++) { - svbi->service_lines[1][i] = 0; - - svbi->service_lines[0][i]=tvp5150_set_vbi(c, - vbi_ram_default, - svbi->service_lines[0][i],0xf0,i,3); - } - /* Enables FIFO */ - tvp5150_write(c, TVP5150_FIFO_OUT_CTRL,1); - } else { - /* Disables FIFO*/ - tvp5150_write(c, TVP5150_FIFO_OUT_CTRL,0); + svbi->service_set = mask; + return 0; +} - /* Disable Full Field */ - tvp5150_write(c, TVP5150_FULL_FIELD_ENA, 0); - /* Disable Line modes */ - for (i=TVP5150_LINE_MODE_INI; i<=TVP5150_LINE_MODE_END; i++) - tvp5150_write(c, i, 0xff); - } - break; - } - case VIDIOC_G_FMT: - { - struct v4l2_format *fmt; - struct v4l2_sliced_vbi_format *svbi; +static int tvp5150_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_chip_ident *chip) +{ + int rev; + struct i2c_client *client = v4l2_get_subdevdata(sd); - int i, mask=0; + rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 | + tvp5150_read(sd, TVP5150_ROM_MINOR_VER); - fmt = arg; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - memset(svbi, 0, sizeof(*svbi)); + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150, + rev); +} - for (i = 0; i <= 23; i++) { - svbi->service_lines[0][i]=tvp5150_get_vbi(c, - vbi_ram_default,i); - mask|=svbi->service_lines[0][i]; - } - svbi->service_set=mask; - break; - } #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(c, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = tvp5150_read(c, reg->reg & 0xff); - else - tvp5150_write(c, reg->reg & 0xff, reg->val & 0xff); - break; - } -#endif +static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_LOG_STATUS: - dump_reg(c); - break; + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = tvp5150_read(sd, reg->reg & 0xff); + return 0; +} - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = arg; - int status = tvp5150_read(c, 0x88); +static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0; - break; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif - tvp5150_dbg(1, "VIDIOC_QUERYCTRL called\n"); +static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + int status = tvp5150_read(sd, 0x88); - for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++) - if (qc->id && qc->id == tvp5150_qctrl[i].id) { - memcpy(qc, &(tvp5150_qctrl[i]), - sizeof(*qc)); - return 0; - } + vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0; + return 0; +} - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - tvp5150_dbg(1, "VIDIOC_G_CTRL called\n"); +static int tvp5150_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + int i; - return tvp5150_get_ctrl(c, ctrl); - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - u8 i, n; - n = ARRAY_SIZE(tvp5150_qctrl); - for (i = 0; i < n; i++) - if (ctrl->id == tvp5150_qctrl[i].id) { - if (ctrl->value < - tvp5150_qctrl[i].minimum - || ctrl->value > - tvp5150_qctrl[i].maximum) - return -ERANGE; - tvp5150_dbg(1, - "VIDIOC_S_CTRL: id=%d, value=%d\n", - ctrl->id, ctrl->value); - return tvp5150_set_ctrl(c, ctrl); - } - return -EINVAL; + v4l2_dbg(1, debug, sd, "VIDIOC_QUERYCTRL called\n"); + + for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++) + if (qc->id && qc->id == tvp5150_qctrl[i].id) { + memcpy(qc, &(tvp5150_qctrl[i]), + sizeof(*qc)); + return 0; } - default: - return -EINVAL; - } + return -EINVAL; +} - return 0; +static int tvp5150_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); } +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tvp5150_core_ops = { + .log_status = tvp5150_log_status, + .g_ctrl = tvp5150_g_ctrl, + .s_ctrl = tvp5150_s_ctrl, + .queryctrl = tvp5150_queryctrl, + .reset = tvp5150_reset, + .g_chip_ident = tvp5150_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = tvp5150_g_register, + .s_register = tvp5150_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { + .s_std = tvp5150_s_std, + .g_tuner = tvp5150_g_tuner, +}; + +static const struct v4l2_subdev_video_ops tvp5150_video_ops = { + .s_routing = tvp5150_s_routing, + .g_fmt = tvp5150_g_fmt, + .s_fmt = tvp5150_s_fmt, + .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap, +}; + +static const struct v4l2_subdev_ops tvp5150_ops = { + .core = &tvp5150_core_ops, + .tuner = &tvp5150_tuner_ops, + .video = &tvp5150_video_ops, +}; + + /**************************************************************************** I2C Client & Driver ****************************************************************************/ -static struct i2c_driver driver; - -static struct i2c_client client_template = { - .name = "(unset)", - .driver = &driver, -}; -static int tvp5150_detect_client(struct i2c_adapter *adapter, - int address, int kind) +static int tvp5150_probe(struct i2c_client *c, + const struct i2c_device_id *id) { - struct i2c_client *c; struct tvp5150 *core; - int rv; - - if (debug) - printk( KERN_INFO - "tvp5150.c: detecting tvp5150 client on address 0x%x\n", - address << 1); - - client_template.adapter = adapter; - client_template.addr = address; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality - (adapter, + if (!i2c_check_functionality(c->adapter, I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return 0; - - c = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!c) - return -ENOMEM; - memcpy(c, &client_template, sizeof(struct i2c_client)); + return -EIO; core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL); if (!core) { - kfree(c); return -ENOMEM; } - i2c_set_clientdata(c, core); - - rv = i2c_attach_client(c); + sd = &core->sd; + v4l2_i2c_subdev_init(sd, c, &tvp5150_ops); + v4l_info(c, "chip found @ 0x%02x (%s)\n", + c->addr << 1, c->adapter->name); core->norm = V4L2_STD_ALL; /* Default is autodetect */ core->route.input = TVP5150_COMPOSITE1; @@ -1093,69 +1099,38 @@ static int tvp5150_detect_client(struct i2c_adapter *adapter, core->hue = 0; core->sat = 128; - if (rv) { - kfree(c); - kfree(core); - return rv; - } - if (debug > 1) - dump_reg(c); + tvp5150_log_status(sd); return 0; } -static int tvp5150_attach_adapter(struct i2c_adapter *adapter) +static int tvp5150_remove(struct i2c_client *c) { - if (debug) - printk( KERN_INFO - "tvp5150.c: starting probe for adapter %s (0x%x)\n", - adapter->name, adapter->id); - return i2c_probe(adapter, &addr_data, &tvp5150_detect_client); -} - -static int tvp5150_detach_client(struct i2c_client *c) -{ - struct tvp5150 *decoder = i2c_get_clientdata(c); - int err; + struct v4l2_subdev *sd = i2c_get_clientdata(c); - tvp5150_dbg(1, + v4l2_dbg(1, debug, sd, "tvp5150.c: removing tvp5150 adapter on address 0x%x\n", c->addr << 1); - err = i2c_detach_client(c); - if (err) { - return err; - } - - kfree(decoder); - kfree(c); - + v4l2_device_unregister_subdev(sd); + kfree(to_tvp5150(sd)); return 0; } /* ----------------------------------------------------------------------- */ -static struct i2c_driver driver = { - .driver = { - .name = "tvp5150", - }, - .id = I2C_DRIVERID_TVP5150, - - .attach_adapter = tvp5150_attach_adapter, - .detach_client = tvp5150_detach_client, +static const struct i2c_device_id tvp5150_id[] = { + { "tvp5150", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tvp5150_id); +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "tvp5150", + .driverid = I2C_DRIVERID_TVP5150, .command = tvp5150_command, + .probe = tvp5150_probe, + .remove = tvp5150_remove, + .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, + .id_table = tvp5150_id, }; - -static int __init tvp5150_init(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit tvp5150_exit(void) -{ - i2c_del_driver(&driver); -} - -module_init(tvp5150_init); -module_exit(tvp5150_exit); diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c new file mode 100644 index 0000000..d5cdc4b --- /dev/null +++ b/drivers/media/video/tw9910.c @@ -0,0 +1,951 @@ +/* + * tw9910 Video Driver + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * Based on ov772x driver, + * + * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com> + * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/videodev2.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-common.h> +#include <media/soc_camera.h> +#include <media/tw9910.h> + +#define GET_ID(val) ((val & 0xF8) >> 3) +#define GET_ReV(val) (val & 0x07) + +/* + * register offset + */ +#define ID 0x00 /* Product ID Code Register */ +#define STATUS1 0x01 /* Chip Status Register I */ +#define INFORM 0x02 /* Input Format */ +#define OPFORM 0x03 /* Output Format Control Register */ +#define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */ +#define OUTCTR1 0x05 /* Output Control I */ +#define ACNTL1 0x06 /* Analog Control Register 1 */ +#define CROP_HI 0x07 /* Cropping Register, High */ +#define VDELAY_LO 0x08 /* Vertical Delay Register, Low */ +#define VACTIVE_LO 0x09 /* Vertical Active Register, Low */ +#define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */ +#define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */ +#define CNTRL1 0x0C /* Control Register I */ +#define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */ +#define SCALE_HI 0x0E /* Scaling Register, High */ +#define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */ +#define BRIGHT 0x10 /* BRIGHTNESS Control Register */ +#define CONTRAST 0x11 /* CONTRAST Control Register */ +#define SHARPNESS 0x12 /* SHARPNESS Control Register I */ +#define SAT_U 0x13 /* Chroma (U) Gain Register */ +#define SAT_V 0x14 /* Chroma (V) Gain Register */ +#define HUE 0x15 /* Hue Control Register */ +#define CORING1 0x17 +#define CORING2 0x18 /* Coring and IF compensation */ +#define VBICNTL 0x19 /* VBI Control Register */ +#define ACNTL2 0x1A /* Analog Control 2 */ +#define OUTCTR2 0x1B /* Output Control 2 */ +#define SDT 0x1C /* Standard Selection */ +#define SDTR 0x1D /* Standard Recognition */ +#define TEST 0x1F /* Test Control Register */ +#define CLMPG 0x20 /* Clamping Gain */ +#define IAGC 0x21 /* Individual AGC Gain */ +#define AGCGAIN 0x22 /* AGC Gain */ +#define PEAKWT 0x23 /* White Peak Threshold */ +#define CLMPL 0x24 /* Clamp level */ +#define SYNCT 0x25 /* Sync Amplitude */ +#define MISSCNT 0x26 /* Sync Miss Count Register */ +#define PCLAMP 0x27 /* Clamp Position Register */ +#define VCNTL1 0x28 /* Vertical Control I */ +#define VCNTL2 0x29 /* Vertical Control II */ +#define CKILL 0x2A /* Color Killer Level Control */ +#define COMB 0x2B /* Comb Filter Control */ +#define LDLY 0x2C /* Luma Delay and H Filter Control */ +#define MISC1 0x2D /* Miscellaneous Control I */ +#define LOOP 0x2E /* LOOP Control Register */ +#define MISC2 0x2F /* Miscellaneous Control II */ +#define MVSN 0x30 /* Macrovision Detection */ +#define STATUS2 0x31 /* Chip STATUS II */ +#define HFREF 0x32 /* H monitor */ +#define CLMD 0x33 /* CLAMP MODE */ +#define IDCNTL 0x34 /* ID Detection Control */ +#define CLCNTL1 0x35 /* Clamp Control I */ +#define ANAPLLCTL 0x4C +#define VBIMIN 0x4D +#define HSLOWCTL 0x4E +#define WSS3 0x4F +#define FILLDATA 0x50 +#define SDID 0x51 +#define DID 0x52 +#define WSS1 0x53 +#define WSS2 0x54 +#define VVBI 0x55 +#define LCTL6 0x56 +#define LCTL7 0x57 +#define LCTL8 0x58 +#define LCTL9 0x59 +#define LCTL10 0x5A +#define LCTL11 0x5B +#define LCTL12 0x5C +#define LCTL13 0x5D +#define LCTL14 0x5E +#define LCTL15 0x5F +#define LCTL16 0x60 +#define LCTL17 0x61 +#define LCTL18 0x62 +#define LCTL19 0x63 +#define LCTL20 0x64 +#define LCTL21 0x65 +#define LCTL22 0x66 +#define LCTL23 0x67 +#define LCTL24 0x68 +#define LCTL25 0x69 +#define LCTL26 0x6A +#define HSGEGIN 0x6B +#define HSEND 0x6C +#define OVSDLY 0x6D +#define OVSEND 0x6E +#define VBIDELAY 0x6F + +/* + * register detail + */ + +/* INFORM */ +#define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */ +#define FC27_FF 0x00 /* 0 : Square pixel mode. */ + /* Must use 24.54MHz for 60Hz field rate */ + /* source or 29.5MHz for 50Hz field rate */ +#define IFSEL_S 0x10 /* 01 : S-video decoding */ +#define IFSEL_C 0x00 /* 00 : Composite video decoding */ + /* Y input video selection */ +#define YSEL_M0 0x00 /* 00 : Mux0 selected */ +#define YSEL_M1 0x04 /* 01 : Mux1 selected */ +#define YSEL_M2 0x08 /* 10 : Mux2 selected */ +#define YSEL_M3 0x10 /* 11 : Mux3 selected */ + +/* OPFORM */ +#define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */ + /* 1 : ITU-R-656 compatible data sequence format */ +#define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */ + /* 1 : 16-bit YCrCb 4:2:2 output format.*/ +#define LLCMODE 0x20 /* 1 : LLC output mode. */ + /* 0 : free-run output mode */ +#define AINC 0x10 /* Serial interface auto-indexing control */ + /* 0 : auto-increment */ + /* 1 : non-auto */ +#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */ + /* 0 : Vertical out ctrl by HACTIVE and DVALID */ +#define OEN 0x04 /* Output Enable together with TRI_SEL. */ + +/* OUTCTR1 */ +#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */ +#define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */ + /* VS pin output control */ +#define VSSL_VSYNC 0x00 /* 0 : VSYNC */ +#define VSSL_VACT 0x10 /* 1 : VACT */ +#define VSSL_FIELD 0x20 /* 2 : FIELD */ +#define VSSL_VVALID 0x30 /* 3 : VVALID */ +#define VSSL_ZERO 0x70 /* 7 : 0 */ +#define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */ +#define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/ + /* HS pin output control */ +#define HSSL_HACT 0x00 /* 0 : HACT */ +#define HSSL_HSYNC 0x01 /* 1 : HSYNC */ +#define HSSL_DVALID 0x02 /* 2 : DVALID */ +#define HSSL_HLOCK 0x03 /* 3 : HLOCK */ +#define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */ +#define HSSL_ZERO 0x07 /* 7 : 0 */ + +/* ACNTL1 */ +#define SRESET 0x80 /* resets the device to its default state + * but all register content remain unchanged. + * This bit is self-resetting. + */ + +/* VBICNTL */ +/* RTSEL : control the real time signal +* output from the MPOUT pin +*/ +#define RTSEL_MASK 0x07 +#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */ +#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */ +#define RTSEL_SLOCK 0x02 /* 0010 = S-lock */ +#define RTSEL_VLOCK 0x03 /* 0011 = V-lock */ +#define RTSEL_MONO 0x04 /* 0100 = MONO */ +#define RTSEL_DET50 0x05 /* 0101 = DET50 */ +#define RTSEL_FIELD 0x06 /* 0110 = FIELD */ +#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */ + +/* + * structure + */ + +struct regval_list { + unsigned char reg_num; + unsigned char value; +}; + +struct tw9910_scale_ctrl { + char *name; + unsigned short width; + unsigned short height; + u16 hscale; + u16 vscale; +}; + +struct tw9910_cropping_ctrl { + u16 vdelay; + u16 vactive; + u16 hdelay; + u16 hactive; +}; + +struct tw9910_hsync_ctrl { + u16 start; + u16 end; +}; + +struct tw9910_priv { + struct tw9910_video_info *info; + struct i2c_client *client; + struct soc_camera_device icd; + const struct tw9910_scale_ctrl *scale; +}; + +/* + * register settings + */ + +#define ENDMARKER { 0xff, 0xff } + +static const struct regval_list tw9910_default_regs[] = +{ + { OPFORM, 0x00 }, + { OUTCTR1, VSP_LO | VSSL_VVALID | HSP_HI | HSSL_HSYNC }, + ENDMARKER, +}; + +static const struct soc_camera_data_format tw9910_color_fmt[] = { + { + .name = "VYUY", + .fourcc = V4L2_PIX_FMT_VYUY, + .depth = 16, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + } +}; + +static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = { + { + .name = "NTSC SQ", + .width = 640, + .height = 480, + .hscale = 0x0100, + .vscale = 0x0100, + }, + { + .name = "NTSC CCIR601", + .width = 720, + .height = 480, + .hscale = 0x0100, + .vscale = 0x0100, + }, + { + .name = "NTSC SQ (CIF)", + .width = 320, + .height = 240, + .hscale = 0x0200, + .vscale = 0x0200, + }, + { + .name = "NTSC CCIR601 (CIF)", + .width = 360, + .height = 240, + .hscale = 0x0200, + .vscale = 0x0200, + }, + { + .name = "NTSC SQ (QCIF)", + .width = 160, + .height = 120, + .hscale = 0x0400, + .vscale = 0x0400, + }, + { + .name = "NTSC CCIR601 (QCIF)", + .width = 180, + .height = 120, + .hscale = 0x0400, + .vscale = 0x0400, + }, +}; + +static const struct tw9910_scale_ctrl tw9910_pal_scales[] = { + { + .name = "PAL SQ", + .width = 768, + .height = 576, + .hscale = 0x0100, + .vscale = 0x0100, + }, + { + .name = "PAL CCIR601", + .width = 720, + .height = 576, + .hscale = 0x0100, + .vscale = 0x0100, + }, + { + .name = "PAL SQ (CIF)", + .width = 384, + .height = 288, + .hscale = 0x0200, + .vscale = 0x0200, + }, + { + .name = "PAL CCIR601 (CIF)", + .width = 360, + .height = 288, + .hscale = 0x0200, + .vscale = 0x0200, + }, + { + .name = "PAL SQ (QCIF)", + .width = 192, + .height = 144, + .hscale = 0x0400, + .vscale = 0x0400, + }, + { + .name = "PAL CCIR601 (QCIF)", + .width = 180, + .height = 144, + .hscale = 0x0400, + .vscale = 0x0400, + }, +}; + +static const struct tw9910_cropping_ctrl tw9910_cropping_ctrl = { + .vdelay = 0x0012, + .vactive = 0x00F0, + .hdelay = 0x0010, + .hactive = 0x02D0, +}; + +static const struct tw9910_hsync_ctrl tw9910_hsync_ctrl = { + .start = 0x0260, + .end = 0x0300, +}; + +/* + * general function + */ +static int tw9910_set_scale(struct i2c_client *client, + const struct tw9910_scale_ctrl *scale) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, SCALE_HI, + (scale->vscale & 0x0F00) >> 4 | + (scale->hscale & 0x0F00) >> 8); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, HSCALE_LO, + scale->hscale & 0x00FF); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, VSCALE_LO, + scale->vscale & 0x00FF); + + return ret; +} + +static int tw9910_set_cropping(struct i2c_client *client, + const struct tw9910_cropping_ctrl *cropping) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, CROP_HI, + (cropping->vdelay & 0x0300) >> 2 | + (cropping->vactive & 0x0300) >> 4 | + (cropping->hdelay & 0x0300) >> 6 | + (cropping->hactive & 0x0300) >> 8); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, VDELAY_LO, + cropping->vdelay & 0x00FF); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, VACTIVE_LO, + cropping->vactive & 0x00FF); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, HDELAY_LO, + cropping->hdelay & 0x00FF); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, HACTIVE_LO, + cropping->hactive & 0x00FF); + + return ret; +} + +static int tw9910_set_hsync(struct i2c_client *client, + const struct tw9910_hsync_ctrl *hsync) +{ + int ret; + + /* bit 10 - 3 */ + ret = i2c_smbus_write_byte_data(client, HSGEGIN, + (hsync->start & 0x07F8) >> 3); + if (ret < 0) + return ret; + + /* bit 10 - 3 */ + ret = i2c_smbus_write_byte_data(client, HSEND, + (hsync->end & 0x07F8) >> 3); + if (ret < 0) + return ret; + + /* bit 2 - 0 */ + ret = i2c_smbus_read_byte_data(client, HSLOWCTL); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, HSLOWCTL, + (ret & 0x88) | + (hsync->start & 0x0007) << 4 | + (hsync->end & 0x0007)); + + return ret; +} + +static int tw9910_write_array(struct i2c_client *client, + const struct regval_list *vals) +{ + while (vals->reg_num != 0xff) { + int ret = i2c_smbus_write_byte_data(client, + vals->reg_num, + vals->value); + if (ret < 0) + return ret; + vals++; + } + return 0; +} + +static int tw9910_mask_set(struct i2c_client *client, u8 command, + u8 mask, u8 set) +{ + s32 val = i2c_smbus_read_byte_data(client, command); + + val &= ~mask; + val |= set; + + return i2c_smbus_write_byte_data(client, command, val); +} + +static void tw9910_reset(struct i2c_client *client) +{ + i2c_smbus_write_byte_data(client, ACNTL1, SRESET); + msleep(1); +} + +static const struct tw9910_scale_ctrl* +tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height) +{ + const struct tw9910_scale_ctrl *scale; + const struct tw9910_scale_ctrl *ret = NULL; + v4l2_std_id norm = icd->vdev->current_norm; + __u32 diff = 0xffffffff, tmp; + int size, i; + + if (norm & V4L2_STD_NTSC) { + scale = tw9910_ntsc_scales; + size = ARRAY_SIZE(tw9910_ntsc_scales); + } else if (norm & V4L2_STD_PAL) { + scale = tw9910_pal_scales; + size = ARRAY_SIZE(tw9910_pal_scales); + } else { + return NULL; + } + + for (i = 0; i < size; i++) { + tmp = abs(width - scale[i].width) + + abs(height - scale[i].height); + if (tmp < diff) { + diff = tmp; + ret = scale + i; + } + } + + return ret; +} + +/* + * soc_camera_ops function + */ +static int tw9910_init(struct soc_camera_device *icd) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + int ret = 0; + + if (priv->info->link.power) { + ret = priv->info->link.power(&priv->client->dev, 1); + if (ret < 0) + return ret; + } + + if (priv->info->link.reset) + ret = priv->info->link.reset(&priv->client->dev); + + return ret; +} + +static int tw9910_release(struct soc_camera_device *icd) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + int ret = 0; + + if (priv->info->link.power) + ret = priv->info->link.power(&priv->client->dev, 0); + + return ret; +} + +static int tw9910_start_capture(struct soc_camera_device *icd) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + + if (!priv->scale) { + dev_err(&icd->dev, "norm select error\n"); + return -EPERM; + } + + dev_dbg(&icd->dev, "%s %dx%d\n", + priv->scale->name, + priv->scale->width, + priv->scale->height); + + return 0; +} + +static int tw9910_stop_capture(struct soc_camera_device *icd) +{ + return 0; +} + +static int tw9910_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + return 0; +} + +static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + struct soc_camera_link *icl = priv->client->dev.platform_data; + unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +static int tw9910_get_chip_id(struct soc_camera_device *icd, + struct v4l2_chip_ident *id) +{ + id->ident = V4L2_IDENT_TW9910; + id->revision = 0; + + return 0; +} + +static int tw9910_set_std(struct soc_camera_device *icd, + v4l2_std_id *a) +{ + int ret = -EINVAL; + + if (*a & (V4L2_STD_NTSC | V4L2_STD_PAL)) + ret = 0; + + return ret; +} + +static int tw9910_enum_input(struct soc_camera_device *icd, + struct v4l2_input *inp) +{ + inp->type = V4L2_INPUT_TYPE_TUNER; + inp->std = V4L2_STD_UNKNOWN; + strcpy(inp->name, "Video"); + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int tw9910_get_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + int ret; + + if (reg->reg > 0xff) + return -EINVAL; + + ret = i2c_smbus_read_byte_data(priv->client, reg->reg); + if (ret < 0) + return ret; + + /* ret = int + * reg->val = __u64 + */ + reg->val = (__u64)ret; + + return 0; +} + +static int tw9910_set_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + + if (reg->reg > 0xff || + reg->val > 0xff) + return -EINVAL; + + return i2c_smbus_write_byte_data(priv->client, reg->reg, reg->val); +} +#endif + +static int tw9910_set_fmt(struct soc_camera_device *icd, __u32 pixfmt, + struct v4l2_rect *rect) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + int ret = -EINVAL; + u8 val; + + /* + * select suitable norm + */ + priv->scale = tw9910_select_norm(icd, rect->width, rect->height); + if (!priv->scale) + goto tw9910_set_fmt_error; + + /* + * reset hardware + */ + tw9910_reset(priv->client); + ret = tw9910_write_array(priv->client, tw9910_default_regs); + if (ret < 0) + goto tw9910_set_fmt_error; + + /* + * set bus width + */ + val = 0x00; + if (SOCAM_DATAWIDTH_16 == priv->info->buswidth) + val = LEN; + + ret = tw9910_mask_set(priv->client, OPFORM, LEN, val); + if (ret < 0) + goto tw9910_set_fmt_error; + + /* + * select MPOUT behavior + */ + switch (priv->info->mpout) { + case TW9910_MPO_VLOSS: + val = RTSEL_VLOSS; break; + case TW9910_MPO_HLOCK: + val = RTSEL_HLOCK; break; + case TW9910_MPO_SLOCK: + val = RTSEL_SLOCK; break; + case TW9910_MPO_VLOCK: + val = RTSEL_VLOCK; break; + case TW9910_MPO_MONO: + val = RTSEL_MONO; break; + case TW9910_MPO_DET50: + val = RTSEL_DET50; break; + case TW9910_MPO_FIELD: + val = RTSEL_FIELD; break; + case TW9910_MPO_RTCO: + val = RTSEL_RTCO; break; + default: + val = 0; + } + + ret = tw9910_mask_set(priv->client, VBICNTL, RTSEL_MASK, val); + if (ret < 0) + goto tw9910_set_fmt_error; + + /* + * set scale + */ + ret = tw9910_set_scale(priv->client, priv->scale); + if (ret < 0) + goto tw9910_set_fmt_error; + + /* + * set cropping + */ + ret = tw9910_set_cropping(priv->client, &tw9910_cropping_ctrl); + if (ret < 0) + goto tw9910_set_fmt_error; + + /* + * set hsync + */ + ret = tw9910_set_hsync(priv->client, &tw9910_hsync_ctrl); + if (ret < 0) + goto tw9910_set_fmt_error; + + return ret; + +tw9910_set_fmt_error: + + tw9910_reset(priv->client); + priv->scale = NULL; + + return ret; +} + +static int tw9910_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + const struct tw9910_scale_ctrl *scale; + + if (V4L2_FIELD_ANY == pix->field) { + pix->field = V4L2_FIELD_INTERLACED; + } else if (V4L2_FIELD_INTERLACED != pix->field) { + dev_err(&icd->dev, "Field type invalid.\n"); + return -EINVAL; + } + + /* + * select suitable norm + */ + scale = tw9910_select_norm(icd, pix->width, pix->height); + if (!scale) + return -EINVAL; + + pix->width = scale->width; + pix->height = scale->height; + + return 0; +} + +static int tw9910_video_probe(struct soc_camera_device *icd) +{ + struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + s32 val; + int ret; + + /* + * We must have a parent by now. And it cannot be a wrong one. + * So this entire test is completely redundant. + */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) + return -ENODEV; + + /* + * tw9910 only use 8 or 16 bit bus width + */ + if (SOCAM_DATAWIDTH_16 != priv->info->buswidth && + SOCAM_DATAWIDTH_8 != priv->info->buswidth) { + dev_err(&icd->dev, "bus width error\n"); + return -ENODEV; + } + + icd->formats = tw9910_color_fmt; + icd->num_formats = ARRAY_SIZE(tw9910_color_fmt); + + /* + * check and show Product ID + */ + val = i2c_smbus_read_byte_data(priv->client, ID); + if (0x0B != GET_ID(val) || + 0x00 != GET_ReV(val)) { + dev_err(&icd->dev, + "Product ID error %x:%x\n", GET_ID(val), GET_ReV(val)); + return -ENODEV; + } + + dev_info(&icd->dev, + "tw9910 Product ID %0x:%0x\n", GET_ID(val), GET_ReV(val)); + + ret = soc_camera_video_start(icd); + if (ret < 0) + return ret; + + icd->vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL; + icd->vdev->current_norm = V4L2_STD_NTSC; + + return ret; +} + +static void tw9910_video_remove(struct soc_camera_device *icd) +{ + soc_camera_video_stop(icd); +} + +static struct soc_camera_ops tw9910_ops = { + .owner = THIS_MODULE, + .probe = tw9910_video_probe, + .remove = tw9910_video_remove, + .init = tw9910_init, + .release = tw9910_release, + .start_capture = tw9910_start_capture, + .stop_capture = tw9910_stop_capture, + .set_fmt = tw9910_set_fmt, + .try_fmt = tw9910_try_fmt, + .set_bus_param = tw9910_set_bus_param, + .query_bus_param = tw9910_query_bus_param, + .get_chip_id = tw9910_get_chip_id, + .set_std = tw9910_set_std, + .enum_input = tw9910_enum_input, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .get_register = tw9910_get_register, + .set_register = tw9910_set_register, +#endif +}; + +/* + * i2c_driver function + */ + +static int tw9910_probe(struct i2c_client *client, + const struct i2c_device_id *did) + +{ + struct tw9910_priv *priv; + struct tw9910_video_info *info; + struct soc_camera_device *icd; + const struct tw9910_scale_ctrl *scale; + int i, ret; + + info = client->dev.platform_data; + if (!info) + return -EINVAL; + + if (!i2c_check_functionality(to_i2c_adapter(client->dev.parent), + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "I2C-Adapter doesn't support " + "I2C_FUNC_SMBUS_BYTE_DATA\n"); + return -EIO; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->info = info; + priv->client = client; + i2c_set_clientdata(client, priv); + + icd = &priv->icd; + icd->ops = &tw9910_ops; + icd->control = &client->dev; + icd->iface = info->link.bus_id; + + /* + * set width and height + */ + icd->width_max = tw9910_ntsc_scales[0].width; /* set default */ + icd->width_min = tw9910_ntsc_scales[0].width; + icd->height_max = tw9910_ntsc_scales[0].height; + icd->height_min = tw9910_ntsc_scales[0].height; + + scale = tw9910_ntsc_scales; + for (i = 0; i < ARRAY_SIZE(tw9910_ntsc_scales); i++) { + icd->width_max = max(scale[i].width, icd->width_max); + icd->width_min = min(scale[i].width, icd->width_min); + icd->height_max = max(scale[i].height, icd->height_max); + icd->height_min = min(scale[i].height, icd->height_min); + } + scale = tw9910_pal_scales; + for (i = 0; i < ARRAY_SIZE(tw9910_pal_scales); i++) { + icd->width_max = max(scale[i].width, icd->width_max); + icd->width_min = min(scale[i].width, icd->width_min); + icd->height_max = max(scale[i].height, icd->height_max); + icd->height_min = min(scale[i].height, icd->height_min); + } + + ret = soc_camera_device_register(icd); + + if (ret) { + i2c_set_clientdata(client, NULL); + kfree(priv); + } + + return ret; +} + +static int tw9910_remove(struct i2c_client *client) +{ + struct tw9910_priv *priv = i2c_get_clientdata(client); + + soc_camera_device_unregister(&priv->icd); + i2c_set_clientdata(client, NULL); + kfree(priv); + return 0; +} + +static const struct i2c_device_id tw9910_id[] = { + { "tw9910", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tw9910_id); + +static struct i2c_driver tw9910_i2c_driver = { + .driver = { + .name = "tw9910", + }, + .probe = tw9910_probe, + .remove = tw9910_remove, + .id_table = tw9910_id, +}; + +/* + * module function + */ +static int __init tw9910_module_init(void) +{ + return i2c_add_driver(&tw9910_i2c_driver); +} + +static void __exit tw9910_module_exit(void) +{ + i2c_del_driver(&tw9910_i2c_driver); +} + +module_init(tw9910_module_init); +module_exit(tw9910_module_exit); + +MODULE_DESCRIPTION("SoC Camera driver for tw9910"); +MODULE_AUTHOR("Kuninori Morimoto"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c index b462887..7a609a3 100644 --- a/drivers/media/video/upd64031a.c +++ b/drivers/media/video/upd64031a.c @@ -26,7 +26,7 @@ #include <linux/kernel.h> #include <linux/i2c.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> #include <media/upd64031a.h> @@ -62,6 +62,7 @@ enum { }; struct upd64031a_state { + struct v4l2_subdev sd; u8 regs[TOT_REGS]; u8 gr_mode; u8 direct_3dycs_connect; @@ -69,6 +70,11 @@ struct upd64031a_state { u8 ext_vert_sync; }; +static inline struct upd64031a_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct upd64031a_state, sd); +} + static u8 upd64031a_init[] = { 0x00, 0xb8, 0x48, 0xd2, 0xe6, 0x03, 0x10, 0x0b, 0xaf, 0x7f, @@ -78,8 +84,9 @@ static u8 upd64031a_init[] = { /* ------------------------------------------------------------------------ */ -static u8 upd64031a_read(struct i2c_client *client, u8 reg) +static u8 upd64031a_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 buf[2]; if (reg >= sizeof(buf)) @@ -90,106 +97,127 @@ static u8 upd64031a_read(struct i2c_client *client, u8 reg) /* ------------------------------------------------------------------------ */ -static void upd64031a_write(struct i2c_client *client, u8 reg, u8 val) +static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 buf[2]; buf[0] = reg; buf[1] = val; - v4l_dbg(1, debug, client, "write reg: %02X val: %02X\n", reg, val); + v4l2_dbg(1, debug, sd, "write reg: %02X val: %02X\n", reg, val); if (i2c_master_send(client, buf, 2) != 2) - v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val); + v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); } /* ------------------------------------------------------------------------ */ /* The input changed due to new input or channel changed */ -static void upd64031a_change(struct i2c_client *client) +static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) { - struct upd64031a_state *state = i2c_get_clientdata(client); + struct upd64031a_state *state = to_state(sd); u8 reg = state->regs[R00]; - v4l_dbg(1, debug, client, "changed input or channel\n"); - upd64031a_write(client, R00, reg | 0x10); - upd64031a_write(client, R00, reg & ~0x10); + v4l2_dbg(1, debug, sd, "changed input or channel\n"); + upd64031a_write(sd, R00, reg | 0x10); + upd64031a_write(sd, R00, reg & ~0x10); + return 0; } /* ------------------------------------------------------------------------ */ +static int upd64031a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct upd64031a_state *state = to_state(sd); + u8 r00, r05, r08; + + state->gr_mode = (route->input & 3) << 6; + state->direct_3dycs_connect = (route->input & 0xc) << 4; + state->ext_comp_sync = + (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1; + state->ext_vert_sync = + (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2; + r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; + r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | + state->ext_comp_sync | state->ext_vert_sync; + r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) | + state->direct_3dycs_connect; + upd64031a_write(sd, R00, r00); + upd64031a_write(sd, R05, r05); + upd64031a_write(sd, R08, r08); + return upd64031a_s_frequency(sd, NULL); +} + +static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64031A, 0); +} + +static int upd64031a_log_status(struct v4l2_subdev *sd) +{ + v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n", + upd64031a_read(sd, 0), upd64031a_read(sd, 1)); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = upd64031a_read(sd, reg->reg & 0xff); + return 0; +} + +static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + upd64031a_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + static int upd64031a_command(struct i2c_client *client, unsigned cmd, void *arg) { - struct upd64031a_state *state = i2c_get_clientdata(client); - struct v4l2_routing *route = arg; - - switch (cmd) { - case VIDIOC_S_FREQUENCY: - upd64031a_change(client); - break; - - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = (state->gr_mode >> 6) | - (state->direct_3dycs_connect >> 4) | - (state->ext_comp_sync >> 1) | - (state->ext_vert_sync >> 2); - route->output = 0; - break; - - case VIDIOC_INT_S_VIDEO_ROUTING: - { - u8 r00, r05, r08; - - state->gr_mode = (route->input & 3) << 6; - state->direct_3dycs_connect = (route->input & 0xc) << 4; - state->ext_comp_sync = - (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1; - state->ext_vert_sync = - (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2; - r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; - r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | - state->ext_comp_sync | state->ext_vert_sync; - r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) | - state->direct_3dycs_connect; - upd64031a_write(client, R00, r00); - upd64031a_write(client, R05, r05); - upd64031a_write(client, R08, r08); - upd64031a_change(client); - break; - } - - case VIDIOC_LOG_STATUS: - v4l_info(client, "Status: SA00=0x%02x SA01=0x%02x\n", - upd64031a_read(client, 0), upd64031a_read(client, 1)); - break; + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ +static const struct v4l2_subdev_core_ops upd64031a_core_ops = { + .log_status = upd64031a_log_status, + .g_chip_ident = upd64031a_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) { - reg->val = upd64031a_read(client, reg->reg & 0xff); - break; - } - upd64031a_write(client, reg->reg & 0xff, reg->val & 0xff); - break; - } + .g_register = upd64031a_g_register, + .s_register = upd64031a_s_register, #endif +}; - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, - V4L2_IDENT_UPD64031A, 0); +static const struct v4l2_subdev_tuner_ops upd64031a_tuner_ops = { + .s_frequency = upd64031a_s_frequency, +}; - default: - break; - } - return 0; -} +static const struct v4l2_subdev_video_ops upd64031a_video_ops = { + .s_routing = upd64031a_s_routing, +}; + +static const struct v4l2_subdev_ops upd64031a_ops = { + .core = &upd64031a_core_ops, + .tuner = &upd64031a_tuner_ops, + .video = &upd64031a_video_ops, +}; /* ------------------------------------------------------------------------ */ @@ -199,6 +227,7 @@ static int upd64031a_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct upd64031a_state *state; + struct v4l2_subdev *sd; int i; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -210,19 +239,23 @@ static int upd64031a_probe(struct i2c_client *client, state = kmalloc(sizeof(struct upd64031a_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; - i2c_set_clientdata(client, state); + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &upd64031a_ops); memcpy(state->regs, upd64031a_init, sizeof(state->regs)); state->gr_mode = UPD64031A_GR_ON << 6; state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4; state->ext_comp_sync = state->ext_vert_sync = 0; for (i = 0; i < TOT_REGS; i++) - upd64031a_write(client, i, state->regs[i]); + upd64031a_write(sd, i, state->regs[i]); return 0; } static int upd64031a_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c index 9521ce0..58412cb 100644 --- a/drivers/media/video/upd64083.c +++ b/drivers/media/video/upd64083.c @@ -26,7 +26,7 @@ #include <linux/kernel.h> #include <linux/i2c.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> #include <media/upd64083.h> @@ -51,11 +51,17 @@ enum { }; struct upd64083_state { + struct v4l2_subdev sd; u8 mode; u8 ext_y_adc; u8 regs[TOT_REGS]; }; +static inline struct upd64083_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct upd64083_state, sd); +} + /* Initial values when used in combination with the NEC upd64031a ghost reduction chip. */ static u8 upd64083_init[] = { @@ -68,34 +74,24 @@ static u8 upd64083_init[] = { /* ------------------------------------------------------------------------ */ -static void upd64083_log_status(struct i2c_client *client) -{ - u8 buf[7]; - - i2c_master_recv(client, buf, 7); - v4l_info(client, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x " - "SA04=%02x SA05=%02x SA06=%02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); -} - -/* ------------------------------------------------------------------------ */ - -static void upd64083_write(struct i2c_client *client, u8 reg, u8 val) +static void upd64083_write(struct v4l2_subdev *sd, u8 reg, u8 val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 buf[2]; buf[0] = reg; buf[1] = val; - v4l_dbg(1, debug, client, "write reg: %02x val: %02x\n", reg, val); + v4l2_dbg(1, debug, sd, "write reg: %02x val: %02x\n", reg, val); if (i2c_master_send(client, buf, 2) != 2) - v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val); + v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); } /* ------------------------------------------------------------------------ */ #ifdef CONFIG_VIDEO_ADV_DEBUG -static u8 upd64083_read(struct i2c_client *client, u8 reg) +static u8 upd64083_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 buf[7]; if (reg >= sizeof(buf)) @@ -107,67 +103,94 @@ static u8 upd64083_read(struct i2c_client *client, u8 reg) /* ------------------------------------------------------------------------ */ -static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg) +static int upd64083_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct upd64083_state *state = i2c_get_clientdata(client); - struct v4l2_routing *route = arg; - - switch (cmd) { - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = (state->mode >> 6) | (state->ext_y_adc >> 3); - route->output = 0; - break; - - case VIDIOC_INT_S_VIDEO_ROUTING: - { - u8 r00, r02; - - if (route->input > 7 || (route->input & 6) == 6) - return -EINVAL; - state->mode = (route->input & 3) << 6; - state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3; - r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; - r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; - upd64083_write(client, R00, r00); - upd64083_write(client, R02, r02); - break; - } - - case VIDIOC_LOG_STATUS: - upd64083_log_status(client); - break; + struct upd64083_state *state = to_state(sd); + u8 r00, r02; + + if (route->input > 7 || (route->input & 6) == 6) + return -EINVAL; + state->mode = (route->input & 3) << 6; + state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3; + r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; + r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; + upd64083_write(sd, R00, r00); + upd64083_write(sd, R02, r02); + return 0; +} #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; +static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = upd64083_read(sd, reg->reg & 0xff); + return 0; +} - if (!v4l2_chip_match_i2c_client(client, +static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) { - reg->val = upd64083_read(client, reg->reg & 0xff); - break; - } - upd64083_write(client, reg->reg & 0xff, reg->val & 0xff); - break; - } + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} #endif - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, - V4L2_IDENT_UPD64083, 0); +static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64083, 0); +} - default: - break; - } +static int upd64083_log_status(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[7]; + i2c_master_recv(client, buf, 7); + v4l2_info(sd, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x " + "SA04=%02x SA05=%02x SA06=%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); return 0; } +static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops upd64083_core_ops = { + .log_status = upd64083_log_status, + .g_chip_ident = upd64083_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = upd64083_g_register, + .s_register = upd64083_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops upd64083_video_ops = { + .s_routing = upd64083_s_routing, +}; + +static const struct v4l2_subdev_ops upd64083_ops = { + .core = &upd64083_core_ops, + .video = &upd64083_video_ops, +}; + /* ------------------------------------------------------------------------ */ /* i2c implementation */ @@ -176,6 +199,7 @@ static int upd64083_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct upd64083_state *state; + struct v4l2_subdev *sd; int i; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -187,19 +211,23 @@ static int upd64083_probe(struct i2c_client *client, state = kmalloc(sizeof(struct upd64083_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; - i2c_set_clientdata(client, state); + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &upd64083_ops); /* Initially assume that a ghost reduction chip is present */ state->mode = 0; /* YCS mode */ state->ext_y_adc = (1 << 5); memcpy(state->regs, upd64083_init, TOT_REGS); for (i = 0; i < TOT_REGS; i++) - upd64083_write(client, i, state->regs[i]); + upd64083_write(sd, i, state->regs[i]); return 0; } static int upd64083_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/drivers/media/video/usbvideo/ibmcam.c b/drivers/media/video/usbvideo/ibmcam.c index c710bcd..f8d85dd 100644 --- a/drivers/media/video/usbvideo/ibmcam.c +++ b/drivers/media/video/usbvideo/ibmcam.c @@ -3779,11 +3779,11 @@ static int ibmcam_probe(struct usb_interface *intf, const struct usb_device_id * err("Alternate settings have different endpoint addresses!"); return -ENODEV; } - if ((endpoint->bmAttributes & 0x03) != 0x01) { + if (usb_endpoint_type(endpoint) != USB_ENDPOINT_XFER_ISOC) { err("Interface %d. has non-ISO endpoint!", ifnum); return -ENODEV; } - if ((endpoint->bEndpointAddress & 0x80) == 0) { + if (usb_endpoint_dir_out(endpoint)) { err("Interface %d. has ISO OUT endpoint!", ifnum); return -ENODEV; } diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c index da27a52..90f0ce6 100644 --- a/drivers/media/video/usbvideo/konicawc.c +++ b/drivers/media/video/usbvideo/konicawc.c @@ -823,12 +823,12 @@ static int konicawc_probe(struct usb_interface *intf, const struct usb_device_id err("Alternate settings have different endpoint addresses!"); return -ENODEV; } - if ((endpoint->bmAttributes & 0x03) != 0x01) { + if (usb_endpoint_type(endpoint) != USB_ENDPOINT_XFER_ISOC) { err("Interface %d. has non-ISO endpoint!", interface->desc.bInterfaceNumber); return -ENODEV; } - if ((endpoint->bEndpointAddress & 0x80) == 0) { + if (usb_endpoint_dir_out(endpoint)) { err("Interface %d. has ISO OUT endpoint!", interface->desc.bInterfaceNumber); return -ENODEV; diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c index 4459b8a..fd112f0 100644 --- a/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/drivers/media/video/usbvideo/quickcam_messenger.c @@ -447,7 +447,7 @@ static int qcm_sensor_init(struct uvd *uvd) CHECK_RET(ret, qcm_stv_setw(uvd->dev, 0x15c1, cpu_to_le16(ISOC_PACKET_SIZE))); CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x15c3, 0x08)); - CHECK_RET(ret, ret = qcm_stv_setb(uvd->dev, 0x143f, 0x01)); + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143f, 0x01)); CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00)); @@ -955,8 +955,7 @@ static int qcm_probe(struct usb_interface *intf, for (j=0; j < interface->desc.bNumEndpoints; j++) { endpoint = &interface->endpoint[j].desc; - if ((endpoint->bEndpointAddress & - USB_ENDPOINT_DIR_MASK) != USB_DIR_IN) + if (usb_endpoint_dir_out(endpoint)) continue; /* not input then not good */ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); @@ -965,9 +964,7 @@ static int qcm_probe(struct usb_interface *intf, continue; /* 0 pkt size is not what we want */ } - if ((endpoint->bmAttributes & - USB_ENDPOINT_XFERTYPE_MASK) == - USB_ENDPOINT_XFER_ISOC) { + if (usb_endpoint_xfer_isoc(endpoint)) { video_ep = endpoint->bEndpointAddress; /* break out of the search */ goto good_videoep; diff --git a/drivers/media/video/usbvideo/ultracam.c b/drivers/media/video/usbvideo/ultracam.c index 9714baa..839a082 100644 --- a/drivers/media/video/usbvideo/ultracam.c +++ b/drivers/media/video/usbvideo/ultracam.c @@ -556,12 +556,12 @@ static int ultracam_probe(struct usb_interface *intf, const struct usb_device_id err("Alternate settings have different endpoint addresses!"); return -ENODEV; } - if ((endpoint->bmAttributes & 0x03) != 0x01) { + if (usb_endpoint_type(endpoint) != USB_ENDPOINT_XFER_ISOC) { err("Interface %d. has non-ISO endpoint!", interface->desc.bInterfaceNumber); return -ENODEV; } - if ((endpoint->bEndpointAddress & 0x80) == 0) { + if (usb_endpoint_dir_out(endpoint)) { err("Interface %d. has ISO OUT endpoint!", interface->desc.bInterfaceNumber); return -ENODEV; diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c index 7c575bb..148a1f9 100644 --- a/drivers/media/video/usbvideo/usbvideo.c +++ b/drivers/media/video/usbvideo/usbvideo.c @@ -1123,7 +1123,7 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file) if (uvd->debug > 1) dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev); - if (0 < usbvideo_ClientIncModCount(uvd)) + if (usbvideo_ClientIncModCount(uvd) < 0) return -ENODEV; mutex_lock(&uvd->lock); @@ -1281,8 +1281,7 @@ static int usbvideo_v4l_close(struct inode *inode, struct file *file) * History: * 22-Jan-2000 Corrected VIDIOCSPICT to reject unsupported settings. */ -static int usbvideo_v4l_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int usbvideo_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct uvd *uvd = file->private_data; @@ -1505,7 +1504,7 @@ static int usbvideo_v4l_do_ioctl(struct inode *inode, struct file *file, static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, usbvideo_v4l_do_ioctl); + return video_usercopy(file, cmd, arg, usbvideo_v4l_do_ioctl); } /* diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c index 8e2d58b..4602597 100644 --- a/drivers/media/video/usbvideo/vicam.c +++ b/drivers/media/video/usbvideo/vicam.c @@ -844,8 +844,7 @@ vicam_probe( struct usb_interface *intf, const struct usb_device_id *id) interface->desc.bInterfaceNumber, (unsigned) (interface->desc.bNumEndpoints)); endpoint = &interface->endpoint[0].desc; - if ((endpoint->bEndpointAddress & 0x80) && - ((endpoint->bmAttributes & 3) == 0x02)) { + if (usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ bulkEndpoint = endpoint->bEndpointAddress; } else { diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index d185b57..85661b1 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -523,7 +523,7 @@ static int vidioc_querycap (struct file *file, void *priv, strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString, sizeof(vc->card)); - strlcpy(vc->bus_info, usbvision->dev->dev.bus_id, + strlcpy(vc->bus_info, dev_name(&usbvision->dev->dev), sizeof(vc->bus_info)); vc->version = USBVISION_DRIVER_VERSION; vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | @@ -1278,7 +1278,7 @@ static int usbvision_vbi_close(struct inode *inode, struct file *file) return -ENODEV; } -static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file, +static int usbvision_do_vbi_ioctl(struct file *file, unsigned int cmd, void *arg) { /* TODO */ @@ -1288,7 +1288,7 @@ static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file, static int usbvision_vbi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, usbvision_do_vbi_ioctl); + return video_usercopy(file, cmd, arg, usbvision_do_vbi_ioctl); } @@ -1679,7 +1679,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf, interface = &dev->actconfig->interface[ifnum]->altsetting[0]; } endpoint = &interface->endpoint[1].desc; - if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != + if (usb_endpoint_type(endpoint) != USB_ENDPOINT_XFER_ISOC) { err("%s: interface %d. has non-ISO endpoint!", __func__, ifnum); @@ -1687,8 +1687,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf, __func__, endpoint->bmAttributes); return -ENODEV; } - if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == - USB_DIR_OUT) { + if (usb_endpoint_dir_out(endpoint)) { err("%s: interface %d. has ISO OUT endpoint!", __func__, ifnum); return -ENODEV; diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index f16aafe..2208165 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -327,6 +327,31 @@ static struct uvc_menu_info exposure_auto_controls[] = { { 8, "Aperture Priority Mode" }, }; +static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, + __u8 query, const __u8 *data) +{ + __s8 zoom = (__s8)data[0]; + + switch (query) { + case GET_CUR: + return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]); + + case GET_MIN: + case GET_MAX: + case GET_RES: + case GET_DEF: + default: + return data[2]; + } +} + +static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping, + __s32 value, __u8 *data) +{ + data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff; + data[2] = min(abs(value), 0xff); +} + static struct uvc_control_mapping uvc_ctrl_mappings[] = { { .id = V4L2_CID_BRIGHTNESS, @@ -532,6 +557,38 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, }, + { + .id = V4L2_CID_ZOOM_ABSOLUTE, + .name = "Zoom, Absolute", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_ZOOM_ABSOLUTE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_ZOOM_CONTINUOUS, + .name = "Zoom, Continuous", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_ZOOM_RELATIVE_CONTROL, + .size = 0, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .get = uvc_ctrl_get_zoom, + .set = uvc_ctrl_set_zoom, + }, + { + .id = V4L2_CID_PRIVACY, + .name = "Privacy", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_PRIVACY_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, }; /* ------------------------------------------------------------------------ @@ -543,18 +600,23 @@ static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) return ctrl->data + id * ctrl->info->size; } -static inline int uvc_get_bit(const __u8 *data, int bit) +static inline int uvc_test_bit(const __u8 *data, int bit) { return (data[bit >> 3] >> (bit & 7)) & 1; } +static inline void uvc_clear_bit(__u8 *data, int bit) +{ + data[bit >> 3] &= ~(1 << (bit & 7)); +} + /* Extract the bit string specified by mapping->offset and mapping->size * from the little-endian data stored at 'data' and return the result as * a signed 32bit integer. Sign extension will be performed if the mapping * references a signed data type. */ -static __s32 uvc_get_le_value(const __u8 *data, - struct uvc_control_mapping *mapping) +static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping, + __u8 query, const __u8 *data) { int bits = mapping->size; int offset = mapping->offset; @@ -583,8 +645,8 @@ static __s32 uvc_get_le_value(const __u8 *data, /* Set the bit string specified by mapping->offset and mapping->size * in the little-endian data stored at 'data' to the value 'value'. */ -static void uvc_set_le_value(__s32 value, __u8 *data, - struct uvc_control_mapping *mapping) +static void uvc_set_le_value(struct uvc_control_mapping *mapping, + __s32 value, __u8 *data) { int bits = mapping->size; int offset = mapping->offset; @@ -736,7 +798,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->default_value = uvc_get_le_value(data, mapping); + v4l2_ctrl->default_value = mapping->get(mapping, GET_DEF, data); } switch (mapping->v4l2_type) { @@ -772,21 +834,21 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->minimum = uvc_get_le_value(data, mapping); + v4l2_ctrl->minimum = mapping->get(mapping, GET_MIN, data); } if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->maximum = uvc_get_le_value(data, mapping); + v4l2_ctrl->maximum = mapping->get(mapping, GET_MAX, data); } if (ctrl->info->flags & UVC_CONTROL_GET_RES) { if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->step = uvc_get_le_value(data, mapping); + v4l2_ctrl->step = mapping->get(mapping, GET_RES, data); } ret = 0; @@ -923,8 +985,8 @@ int uvc_ctrl_get(struct uvc_video_device *video, ctrl->loaded = 1; } - xctrl->value = uvc_get_le_value( - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); + xctrl->value = mapping->get(mapping, GET_CUR, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { menu = mapping->menu_info; @@ -980,8 +1042,8 @@ int uvc_ctrl_set(struct uvc_video_device *video, ctrl->info->size); } - uvc_set_le_value(value, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); + mapping->set(mapping, value, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); ctrl->dirty = 1; ctrl->modified = 1; @@ -1257,6 +1319,11 @@ int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping) struct uvc_control_mapping *map; int ret = -EINVAL; + if (mapping->get == NULL) + mapping->get = uvc_get_le_value; + if (mapping->set == NULL) + mapping->set = uvc_set_le_value; + if (mapping->id & ~V4L2_CTRL_ID_MASK) { uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with " "invalid control id 0x%08x\n", mapping->name, @@ -1306,6 +1373,51 @@ end: } /* + * Prune an entity of its bogus controls. This currently includes processing + * unit auto controls for which no corresponding manual control is available. + * Such auto controls make little sense if any, and are known to crash at + * least the SiGma Micro webcam. + */ +static void +uvc_ctrl_prune_entity(struct uvc_entity *entity) +{ + static const struct { + u8 idx_manual; + u8 idx_auto; + } blacklist[] = { + { 2, 11 }, /* Hue */ + { 6, 12 }, /* White Balance Temperature */ + { 7, 13 }, /* White Balance Component */ + }; + + u8 *controls; + unsigned int size; + unsigned int i; + + if (UVC_ENTITY_TYPE(entity) != VC_PROCESSING_UNIT) + return; + + controls = entity->processing.bmControls; + size = entity->processing.bControlSize; + + for (i = 0; i < ARRAY_SIZE(blacklist); ++i) { + if (blacklist[i].idx_auto >= 8 * size || + blacklist[i].idx_manual >= 8 * size) + continue; + + if (!uvc_test_bit(controls, blacklist[i].idx_auto) || + uvc_test_bit(controls, blacklist[i].idx_manual)) + continue; + + uvc_trace(UVC_TRACE_CONTROL, "Auto control %u/%u has no " + "matching manual control, removing it.\n", entity->id, + blacklist[i].idx_auto); + + uvc_clear_bit(controls, blacklist[i].idx_auto); + } +} + +/* * Initialize device controls. */ int uvc_ctrl_init_device(struct uvc_device *dev) @@ -1331,6 +1443,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev) bControlSize = entity->camera.bControlSize; } + if (dev->quirks & UVC_QUIRK_PRUNE_CONTROLS) + uvc_ctrl_prune_entity(entity); + for (i = 0; i < bControlSize; ++i) ncontrols += hweight8(bmControls[i]); @@ -1345,7 +1460,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev) ctrl = entity->controls; for (i = 0; i < bControlSize * 8; ++i) { - if (uvc_get_bit(bmControls, i) == 0) + if (uvc_test_bit(bmControls, i) == 0) continue; ctrl->entity = entity; diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index d7ad060..89d8bd1 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -12,8 +12,8 @@ */ /* - * This driver aims to support video input devices compliant with the 'USB - * Video Class' specification. + * This driver aims to support video input and ouput devices compliant with the + * 'USB Video Class' specification. * * The driver doesn't support the deprecated v4l1 interface. It implements the * mmap capture method only, and doesn't do any image format conversion in @@ -32,6 +32,7 @@ #include <linux/vmalloc.h> #include <linux/wait.h> #include <asm/atomic.h> +#include <asm/unaligned.h> #include <media/v4l2-common.h> @@ -43,6 +44,7 @@ #define DRIVER_VERSION "v0.1.0" #endif +unsigned int uvc_no_drop_param; static unsigned int uvc_quirks_param; unsigned int uvc_trace_param; @@ -288,8 +290,10 @@ static int uvc_parse_format(struct uvc_device *dev, struct uvc_format_desc *fmtdesc; struct uvc_frame *frame; const unsigned char *start = buffer; + unsigned char *_buffer; unsigned int interval; unsigned int i, n; + int _buflen; __u8 ftype; format->type = buffer[2]; @@ -410,12 +414,20 @@ static int uvc_parse_format(struct uvc_device *dev, buflen -= buffer[0]; buffer += buffer[0]; + /* Count the number of frame descriptors to test the bFrameIndex + * field when parsing the descriptors. We can't rely on the + * bNumFrameDescriptors field as some cameras don't initialize it + * properly. + */ + for (_buflen = buflen, _buffer = buffer; + _buflen > 2 && _buffer[2] == ftype; + _buflen -= _buffer[0], _buffer += _buffer[0]) + format->nframes++; + /* Parse the frame descriptors. Only uncompressed, MJPEG and frame * based formats have frame descriptors. */ while (buflen > 2 && buffer[2] == ftype) { - frame = &format->frame[format->nframes]; - if (ftype != VS_FRAME_FRAME_BASED) n = buflen > 25 ? buffer[25] : 0; else @@ -430,22 +442,32 @@ static int uvc_parse_format(struct uvc_device *dev, return -EINVAL; } + if (buffer[3] - 1 >= format->nframes) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d frame index %u out of range\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, + buffer[3]); + return -EINVAL; + } + + frame = &format->frame[buffer[3] - 1]; + frame->bFrameIndex = buffer[3]; frame->bmCapabilities = buffer[4]; - frame->wWidth = le16_to_cpup((__le16 *)&buffer[5]); - frame->wHeight = le16_to_cpup((__le16 *)&buffer[7]); - frame->dwMinBitRate = le32_to_cpup((__le32 *)&buffer[9]); - frame->dwMaxBitRate = le32_to_cpup((__le32 *)&buffer[13]); + frame->wWidth = get_unaligned_le16(&buffer[5]); + frame->wHeight = get_unaligned_le16(&buffer[7]); + frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); + frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); if (ftype != VS_FRAME_FRAME_BASED) { frame->dwMaxVideoFrameBufferSize = - le32_to_cpup((__le32 *)&buffer[17]); + get_unaligned_le32(&buffer[17]); frame->dwDefaultFrameInterval = - le32_to_cpup((__le32 *)&buffer[21]); + get_unaligned_le32(&buffer[21]); frame->bFrameIntervalType = buffer[25]; } else { frame->dwMaxVideoFrameBufferSize = 0; frame->dwDefaultFrameInterval = - le32_to_cpup((__le32 *)&buffer[17]); + get_unaligned_le32(&buffer[17]); frame->bFrameIntervalType = buffer[21]; } frame->dwFrameInterval = *intervals; @@ -468,7 +490,7 @@ static int uvc_parse_format(struct uvc_device *dev, * some other divisions by zero which could happen. */ for (i = 0; i < n; ++i) { - interval = le32_to_cpup((__le32 *)&buffer[26+4*i]); + interval = get_unaligned_le32(&buffer[26+4*i]); *(*intervals)++ = interval ? interval : 1; } @@ -486,7 +508,6 @@ static int uvc_parse_format(struct uvc_device *dev, 10000000/frame->dwDefaultFrameInterval, (100000000/frame->dwDefaultFrameInterval)%10); - format->nframes++; buflen -= buffer[0]; buffer += buffer[0]; } @@ -588,46 +609,55 @@ static int uvc_parse_streaming(struct uvc_device *dev, } /* Parse the header descriptor. */ - if (buffer[2] == VS_OUTPUT_HEADER) { + switch (buffer[2]) { + case VS_OUTPUT_HEADER: + streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + size = 9; + break; + + case VS_INPUT_HEADER: + streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + size = 13; + break; + + default: uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " - "%d OUTPUT HEADER descriptor is not supported.\n", - dev->udev->devnum, alts->desc.bInterfaceNumber); + "%d HEADER descriptor not found.\n", dev->udev->devnum, + alts->desc.bInterfaceNumber); goto error; - } else if (buffer[2] == VS_INPUT_HEADER) { - p = buflen >= 5 ? buffer[3] : 0; - n = buflen >= 12 ? buffer[12] : 0; + } - if (buflen < 13 + p*n || buffer[2] != VS_INPUT_HEADER) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d INPUT HEADER descriptor is " - "invalid.\n", dev->udev->devnum, - alts->desc.bInterfaceNumber); - goto error; - } + p = buflen >= 4 ? buffer[3] : 0; + n = buflen >= size ? buffer[size-1] : 0; + + if (buflen < size + p*n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d HEADER descriptor is invalid.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); + goto error; + } - streaming->header.bNumFormats = p; - streaming->header.bEndpointAddress = buffer[6]; + streaming->header.bNumFormats = p; + streaming->header.bEndpointAddress = buffer[6]; + if (buffer[2] == VS_INPUT_HEADER) { streaming->header.bmInfo = buffer[7]; streaming->header.bTerminalLink = buffer[8]; streaming->header.bStillCaptureMethod = buffer[9]; streaming->header.bTriggerSupport = buffer[10]; streaming->header.bTriggerUsage = buffer[11]; - streaming->header.bControlSize = n; - - streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL); - if (streaming->header.bmaControls == NULL) { - ret = -ENOMEM; - goto error; - } - - memcpy(streaming->header.bmaControls, &buffer[13], p*n); } else { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " - "%d HEADER descriptor not found.\n", dev->udev->devnum, - alts->desc.bInterfaceNumber); + streaming->header.bTerminalLink = buffer[7]; + } + streaming->header.bControlSize = n; + + streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL); + if (streaming->header.bmaControls == NULL) { + ret = -ENOMEM; goto error; } + memcpy(streaming->header.bmaControls, &buffer[size], p*n); + buflen -= buffer[0]; buffer += buffer[0]; @@ -813,8 +843,7 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, unit->type = VC_EXTENSION_UNIT; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; - unit->extension.bNrInPins = - le16_to_cpup((__le16 *)&buffer[21]); + unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; memcpy(unit->extension.baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; @@ -858,8 +887,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - dev->uvc_version = le16_to_cpup((__le16 *)&buffer[3]); - dev->clock_frequency = le32_to_cpup((__le32 *)&buffer[7]); + dev->uvc_version = get_unaligned_le16(&buffer[3]); + dev->clock_frequency = get_unaligned_le32(&buffer[7]); /* Parse all USB Video Streaming interfaces. */ for (i = 0; i < n; ++i) { @@ -886,7 +915,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, /* Make sure the terminal type MSB is not null, otherwise it * could be confused with a unit. */ - type = le16_to_cpup((__le16 *)&buffer[4]); + type = get_unaligned_le16(&buffer[4]); if ((type & 0xff00) == 0) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d INPUT_TERMINAL %d has invalid " @@ -928,11 +957,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, term->camera.bControlSize = n; term->camera.bmControls = (__u8 *)term + sizeof *term; term->camera.wObjectiveFocalLengthMin = - le16_to_cpup((__le16 *)&buffer[8]); + get_unaligned_le16(&buffer[8]); term->camera.wObjectiveFocalLengthMax = - le16_to_cpup((__le16 *)&buffer[10]); + get_unaligned_le16(&buffer[10]); term->camera.wOcularFocalLength = - le16_to_cpup((__le16 *)&buffer[12]); + get_unaligned_le16(&buffer[12]); memcpy(term->camera.bmControls, &buffer[15], n); } else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) { term->media.bControlSize = n; @@ -968,7 +997,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, /* Make sure the terminal type MSB is not null, otherwise it * could be confused with a unit. */ - type = le16_to_cpup((__le16 *)&buffer[4]); + type = get_unaligned_le16(&buffer[4]); if ((type & 0xff00) == 0) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d OUTPUT_TERMINAL %d has invalid " @@ -1042,7 +1071,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, unit->type = buffer[2]; unit->processing.bSourceID = buffer[4]; unit->processing.wMaxMultiplier = - le16_to_cpup((__le16 *)&buffer[5]); + get_unaligned_le16(&buffer[5]); unit->processing.bControlSize = buffer[7]; unit->processing.bmControls = (__u8 *)unit + sizeof *unit; memcpy(unit->processing.bmControls, &buffer[8], n); @@ -1077,8 +1106,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, unit->type = buffer[2]; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; - unit->extension.bNrInPins = - le16_to_cpup((__le16 *)&buffer[21]); + unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; memcpy(unit->extension.baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; @@ -1128,8 +1156,13 @@ next_descriptor: buffer += buffer[0]; } - /* Check if the optional status endpoint is present. */ - if (alts->desc.bNumEndpoints == 1) { + /* Check if the optional status endpoint is present. Built-in iSight + * webcams have an interrupt endpoint but spit proprietary data that + * don't conform to the UVC status endpoint messages. Don't try to + * handle the interrupt endpoint for those cameras. + */ + if (alts->desc.bNumEndpoints == 1 && + !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) { struct usb_host_endpoint *ep = &alts->endpoint[0]; struct usb_endpoint_descriptor *desc = &ep->desc; @@ -1234,6 +1267,26 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, list_add_tail(&entity->chain, &video->iterms); break; + case TT_STREAMING: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- IT %d\n", entity->id); + + if (!UVC_ENTITY_IS_ITERM(entity)) { + uvc_trace(UVC_TRACE_DESCR, "Unsupported input " + "terminal %u.\n", entity->id); + return -1; + } + + if (video->sterm != NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found multiple streaming " + "entities in chain.\n"); + return -1; + } + + list_add_tail(&entity->chain, &video->iterms); + video->sterm = entity; + break; + default: uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type " "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity)); @@ -1344,6 +1397,10 @@ static int uvc_scan_chain(struct uvc_video_device *video) entity = video->oterm; uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id); + + if (UVC_ENTITY_TYPE(entity) == TT_STREAMING) + video->sterm = entity; + id = entity->output.bSourceID; while (id != 0) { prev = entity; @@ -1372,8 +1429,11 @@ static int uvc_scan_chain(struct uvc_video_device *video) return id; } - /* Initialize the video buffers queue. */ - uvc_queue_init(&video->queue); + if (video->sterm == NULL) { + uvc_trace(UVC_TRACE_DESCR, "No streaming entity found in " + "chain.\n"); + return -1; + } return 0; } @@ -1384,7 +1444,8 @@ static int uvc_scan_chain(struct uvc_video_device *video) * The driver currently supports a single video device per control interface * only. The terminal and units must match the following structure: * - * ITT_CAMERA -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING + * ITT_* -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING + * TT_STREAMING -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> OTT_* * * The Extension Units, if present, must have a single input pin. The * Processing Unit and Extension Units can be in any order. Additional @@ -1401,7 +1462,7 @@ static int uvc_register_video(struct uvc_device *dev) list_for_each_entry(term, &dev->entities, list) { struct uvc_streaming *streaming; - if (UVC_ENTITY_TYPE(term) != TT_STREAMING) + if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term)) continue; memset(&dev->video, 0, sizeof dev->video); @@ -1414,7 +1475,8 @@ static int uvc_register_video(struct uvc_device *dev) continue; list_for_each_entry(streaming, &dev->streaming, list) { - if (streaming->header.bTerminalLink == term->id) { + if (streaming->header.bTerminalLink == + dev->video.sterm->id) { dev->video.streaming = streaming; found = 1; break; @@ -1440,6 +1502,9 @@ static int uvc_register_video(struct uvc_device *dev) printk(" -> %d).\n", dev->video.oterm->id); } + /* Initialize the video buffers queue. */ + uvc_queue_init(&dev->video.queue, dev->video.streaming->type); + /* Initialize the streaming interface with default streaming * parameters. */ @@ -1707,24 +1772,6 @@ static int uvc_reset_resume(struct usb_interface *intf) * though they are compliant. */ static struct usb_device_id uvc_ids[] = { - /* ALi M5606 (Clevo M540SR) */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0402, - .idProduct = 0x5606, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Creative Live! Optia */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x041e, - .idProduct = 0x4057, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Microsoft Lifecam NX-6000 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1810,15 +1857,6 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Silicon Motion SM371 */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x090c, - .idProduct = 0xb371, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* MT6227 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1837,6 +1875,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Syntek (Samsung Q310) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x5931, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* Asus F9SG */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1855,6 +1902,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Lenovo Thinkpad SL500 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x17ef, + .idProduct = 0x480b, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* Ecamm Pico iMage */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1884,106 +1940,8 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX - | UVC_QUIRK_IGNORE_SELECTOR_UNIT}, - /* Acer OEM Webcam - Unknown vendor */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0100, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Packard Bell OEM Webcam - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0101, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Acer Crystal Eye webcam - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0102, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Compaq Presario B1200 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0104, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Acer Travelmate 7720 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0105, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Medion Akoya Mini E1210 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0141, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Acer OrbiCam - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0200, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Fujitsu Amilo SI2636 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0202, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Advent 4211 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0203, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0300, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Clevo M570TU - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0303, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, + | UVC_QUIRK_IGNORE_SELECTOR_UNIT + | UVC_QUIRK_PRUNE_CONTROLS }, /* Generic USB Video Class */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, {} @@ -2029,6 +1987,8 @@ static void __exit uvc_cleanup(void) module_init(uvc_init); module_exit(uvc_cleanup); +module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(quirks, "Forced device quirks"); module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 5646a6a..4254634 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -79,12 +79,13 @@ * */ -void uvc_queue_init(struct uvc_video_queue *queue) +void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type) { mutex_init(&queue->mutex); spin_lock_init(&queue->irqlock); INIT_LIST_HEAD(&queue->mainqueue); INIT_LIST_HEAD(&queue->irqqueue); + queue->type = type; } /* @@ -132,7 +133,7 @@ int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers, queue->buffer[i].buf.index = i; queue->buffer[i].buf.m.offset = i * bufsize; queue->buffer[i].buf.length = buflength; - queue->buffer[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + queue->buffer[i].buf.type = queue->type; queue->buffer[i].buf.sequence = 0; queue->buffer[i].buf.field = V4L2_FIELD_NONE; queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP; @@ -226,7 +227,7 @@ int uvc_queue_buffer(struct uvc_video_queue *queue, uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index); - if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + if (v4l2_buf->type != queue->type || v4l2_buf->memory != V4L2_MEMORY_MMAP) { uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) " "and/or memory (%u).\n", v4l2_buf->type, @@ -249,6 +250,13 @@ int uvc_queue_buffer(struct uvc_video_queue *queue, goto done; } + if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + v4l2_buf->bytesused > buf->buf.length) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n"); + ret = -EINVAL; + goto done; + } + spin_lock_irqsave(&queue->irqlock, flags); if (queue->flags & UVC_QUEUE_DISCONNECTED) { spin_unlock_irqrestore(&queue->irqlock, flags); @@ -256,7 +264,11 @@ int uvc_queue_buffer(struct uvc_video_queue *queue, goto done; } buf->state = UVC_BUF_STATE_QUEUED; - buf->buf.bytesused = 0; + if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + buf->buf.bytesused = 0; + else + buf->buf.bytesused = v4l2_buf->bytesused; + list_add_tail(&buf->stream, &queue->mainqueue); list_add_tail(&buf->queue, &queue->irqqueue); spin_unlock_irqrestore(&queue->irqlock, flags); @@ -289,7 +301,7 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf; int ret = 0; - if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + if (v4l2_buf->type != queue->type || v4l2_buf->memory != V4L2_MEMORY_MMAP) { uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) " "and/or memory (%u).\n", v4l2_buf->type, @@ -397,6 +409,7 @@ int uvc_queue_enable(struct uvc_video_queue *queue, int enable) } queue->sequence = 0; queue->flags |= UVC_QUEUE_STREAMING; + queue->buf_used = 0; } else { uvc_queue_cancel(queue, 0); INIT_LIST_HEAD(&queue->mainqueue); diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 758dfef..afcc693 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -110,7 +110,7 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video, int ret = 0; __u8 *fcc; - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (fmt->type != video->streaming->type) return -EINVAL; fcc = (__u8 *)&fmt->fmt.pix.pixelformat; @@ -216,7 +216,7 @@ static int uvc_v4l2_get_format(struct uvc_video_device *video, struct uvc_format *format = video->streaming->cur_format; struct uvc_frame *frame = video->streaming->cur_frame; - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (fmt->type != video->streaming->type) return -EINVAL; if (format == NULL || frame == NULL) @@ -242,7 +242,7 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video, struct uvc_frame *frame; int ret; - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (fmt->type != video->streaming->type) return -EINVAL; if (uvc_queue_streaming(&video->queue)) @@ -252,9 +252,6 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video, if (ret < 0) return ret; - if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) - return ret; - memcpy(&video->streaming->ctrl, &probe, sizeof probe); video->streaming->cur_format = format; video->streaming->cur_frame = frame; @@ -267,7 +264,7 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video, { uint32_t numerator, denominator; - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (parm->type != video->streaming->type) return -EINVAL; numerator = video->streaming->ctrl.dwFrameInterval; @@ -275,13 +272,21 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video, uvc_simplify_fraction(&numerator, &denominator, 8, 333); memset(parm, 0, sizeof *parm); - parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - parm->parm.capture.capturemode = 0; - parm->parm.capture.timeperframe.numerator = numerator; - parm->parm.capture.timeperframe.denominator = denominator; - parm->parm.capture.extendedmode = 0; - parm->parm.capture.readbuffers = 0; + parm->type = video->streaming->type; + + if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.capturemode = 0; + parm->parm.capture.timeperframe.numerator = numerator; + parm->parm.capture.timeperframe.denominator = denominator; + parm->parm.capture.extendedmode = 0; + parm->parm.capture.readbuffers = 0; + } else { + parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.output.outputmode = 0; + parm->parm.output.timeperframe.numerator = numerator; + parm->parm.output.timeperframe.denominator = denominator; + } return 0; } @@ -291,42 +296,45 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, { struct uvc_frame *frame = video->streaming->cur_frame; struct uvc_streaming_control probe; + struct v4l2_fract timeperframe; uint32_t interval; int ret; - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (parm->type != video->streaming->type) return -EINVAL; if (uvc_queue_streaming(&video->queue)) return -EBUSY; + if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + timeperframe = parm->parm.capture.timeperframe; + else + timeperframe = parm->parm.output.timeperframe; + memcpy(&probe, &video->streaming->ctrl, sizeof probe); - interval = uvc_fraction_to_interval( - parm->parm.capture.timeperframe.numerator, - parm->parm.capture.timeperframe.denominator); + interval = uvc_fraction_to_interval(timeperframe.numerator, + timeperframe.denominator); uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n", - parm->parm.capture.timeperframe.numerator, - parm->parm.capture.timeperframe.denominator, - interval); + timeperframe.numerator, timeperframe.denominator, interval); probe.dwFrameInterval = uvc_try_frame_interval(frame, interval); /* Probe the device with the new settings. */ if ((ret = uvc_probe_video(video, &probe)) < 0) return ret; - /* Commit the new settings. */ - if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) - return ret; - memcpy(&video->streaming->ctrl, &probe, sizeof probe); /* Return the actual frame period. */ - parm->parm.capture.timeperframe.numerator = probe.dwFrameInterval; - parm->parm.capture.timeperframe.denominator = 10000000; - uvc_simplify_fraction(&parm->parm.capture.timeperframe.numerator, - &parm->parm.capture.timeperframe.denominator, - 8, 333); + timeperframe.numerator = probe.dwFrameInterval; + timeperframe.denominator = 10000000; + uvc_simplify_fraction(&timeperframe.numerator, + &timeperframe.denominator, 8, 333); + + if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + parm->parm.capture.timeperframe = timeperframe; + else + parm->parm.output.timeperframe = timeperframe; return 0; } @@ -464,17 +472,13 @@ static int uvc_v4l2_release(struct inode *inode, struct file *file) return 0; } -static int __uvc_v4l2_do_ioctl(struct file *file, - unsigned int cmd, void *arg) +static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); struct uvc_video_device *video = video_get_drvdata(vdev); struct uvc_fh *handle = (struct uvc_fh *)file->private_data; int ret = 0; - if (uvc_trace_param & UVC_TRACE_IOCTL) - v4l_printk_ioctl(cmd); - switch (cmd) { /* Query capabilities */ case VIDIOC_QUERYCAP: @@ -487,8 +491,12 @@ static int __uvc_v4l2_do_ioctl(struct file *file, strncpy(cap->bus_info, video->dev->udev->bus->bus_name, sizeof cap->bus_info); cap->version = DRIVER_VERSION_NUMBER; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE - | V4L2_CAP_STREAMING; + if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_STREAMING; + else + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT + | V4L2_CAP_STREAMING; break; } @@ -666,7 +674,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file, struct v4l2_fmtdesc *fmt = arg; struct uvc_format *format; - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + if (fmt->type != video->streaming->type || fmt->index >= video->streaming->nformats) return -EINVAL; @@ -805,7 +813,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file, struct v4l2_cropcap *ccap = arg; struct uvc_frame *frame = video->streaming->cur_frame; - if (ccap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (ccap->type != video->streaming->type) return -EINVAL; ccap->bounds.left = 0; @@ -831,7 +839,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file, unsigned int bufsize = video->streaming->ctrl.dwMaxVideoFrameSize; - if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + if (rb->type != video->streaming->type || rb->memory != V4L2_MEMORY_MMAP) return -EINVAL; @@ -851,7 +859,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file, { struct v4l2_buffer *buf = arg; - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (buf->type != video->streaming->type) return -EINVAL; if (!uvc_has_privileges(handle)) @@ -877,7 +885,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file, { int *type = arg; - if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (*type != video->streaming->type) return -EINVAL; if (!uvc_has_privileges(handle)) @@ -892,7 +900,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file, { int *type = arg; - if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (*type != video->streaming->type) return -EINVAL; if (!uvc_has_privileges(handle)) @@ -925,7 +933,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - info = kmalloc(sizeof *info, GFP_KERNEL); + info = kzalloc(sizeof *info, GFP_KERNEL); if (info == NULL) return -ENOMEM; @@ -952,7 +960,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - map = kmalloc(sizeof *map, GFP_KERNEL); + map = kzalloc(sizeof *map, GFP_KERNEL); if (map == NULL) return -ENOMEM; @@ -979,7 +987,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file, default: if ((ret = v4l_compat_translate_ioctl(file, cmd, arg, - __uvc_v4l2_do_ioctl)) == -ENOIOCTLCMD) + uvc_v4l2_do_ioctl)) == -ENOIOCTLCMD) uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd); return ret; @@ -988,17 +996,16 @@ static int __uvc_v4l2_do_ioctl(struct file *file, return ret; } -static int uvc_v4l2_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - return __uvc_v4l2_do_ioctl(file, cmd, arg); -} - static int uvc_v4l2_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_ioctl\n"); - return video_usercopy(inode, file, cmd, arg, uvc_v4l2_do_ioctl); + if (uvc_trace_param & UVC_TRACE_IOCTL) { + uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl("); + v4l_printk_ioctl(cmd); + printk(")\n"); + } + + return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); } static ssize_t uvc_v4l2_read(struct file *file, char __user *data, diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index b7bb238..e7c3199 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -36,15 +36,22 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, { __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE; unsigned int pipe; - int ret; pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0) : usb_sndctrlpipe(dev->udev, 0); type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT; - ret = usb_control_msg(dev->udev, pipe, query, type, cs << 8, + return usb_control_msg(dev->udev, pipe, query, type, cs << 8, unit << 8 | intfnum, data, size, timeout); +} + +int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size) +{ + int ret; + ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size, + UVC_CTRL_CONTROL_TIMEOUT); if (ret != size) { uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u " "(unit %u) : %d (exp. %u).\n", query, cs, unit, ret, @@ -55,13 +62,6 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, return 0; } -int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, - __u8 intfnum, __u8 cs, void *data, __u16 size) -{ - return __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size, - UVC_CTRL_CONTROL_TIMEOUT); -} - static void uvc_fixup_buffer_size(struct uvc_video_device *video, struct uvc_streaming_control *ctrl) { @@ -102,8 +102,36 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum, probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, UVC_CTRL_STREAMING_TIMEOUT); - if (ret < 0) + + if ((query == GET_MIN || query == GET_MAX) && ret == 2) { + /* Some cameras, mostly based on Bison Electronics chipsets, + * answer a GET_MIN or GET_MAX request with the wCompQuality + * field only. + */ + uvc_warn_once(video->dev, UVC_WARN_MINMAX, "UVC non " + "compliance - GET_MIN/MAX(PROBE) incorrectly " + "supported. Enabling workaround.\n"); + memset(ctrl, 0, sizeof ctrl); + ctrl->wCompQuality = le16_to_cpup((__le16 *)data); + ret = 0; + goto out; + } else if (query == GET_DEF && probe == 1) { + /* Many cameras don't support the GET_DEF request on their + * video probe control. Warn once and return, the caller will + * fall back to GET_CUR. + */ + uvc_warn_once(video->dev, UVC_WARN_PROBE_DEF, "UVC non " + "compliance - GET_DEF(PROBE) not supported. " + "Enabling workaround.\n"); + ret = -EIO; + goto out; + } else if (ret != size) { + uvc_printk(KERN_ERR, "Failed to query (%u) UVC %s control : " + "%d (exp. %u).\n", query, probe ? "probe" : "commit", + ret, size); + ret = -EIO; goto out; + } ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]); ctrl->bFormatIndex = data[2]; @@ -114,14 +142,11 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]); ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]); ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]); - ctrl->dwMaxVideoFrameSize = - le32_to_cpu(get_unaligned((__le32 *)&data[18])); - ctrl->dwMaxPayloadTransferSize = - le32_to_cpu(get_unaligned((__le32 *)&data[22])); + ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]); + ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]); if (size == 34) { - ctrl->dwClockFrequency = - le32_to_cpu(get_unaligned((__le32 *)&data[26])); + ctrl->dwClockFrequency = get_unaligned_le32(&data[26]); ctrl->bmFramingInfo = data[30]; ctrl->bPreferedVersion = data[31]; ctrl->bMinVersion = data[32]; @@ -138,13 +163,14 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, * Try to get the value from the format and frame descriptor. */ uvc_fixup_buffer_size(video, ctrl); + ret = 0; out: kfree(data); return ret; } -int uvc_set_video_ctrl(struct uvc_video_device *video, +static int uvc_set_video_ctrl(struct uvc_video_device *video, struct uvc_streaming_control *ctrl, int probe) { __u8 *data; @@ -168,14 +194,11 @@ int uvc_set_video_ctrl(struct uvc_video_device *video, /* Note: Some of the fields below are not required for IN devices (see * UVC spec, 4.3.1.1), but we still copy them in case support for OUT * devices is added in the future. */ - put_unaligned(cpu_to_le32(ctrl->dwMaxVideoFrameSize), - (__le32 *)&data[18]); - put_unaligned(cpu_to_le32(ctrl->dwMaxPayloadTransferSize), - (__le32 *)&data[22]); + put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]); + put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]); if (size == 34) { - put_unaligned(cpu_to_le32(ctrl->dwClockFrequency), - (__le32 *)&data[26]); + put_unaligned_le32(ctrl->dwClockFrequency, &data[26]); data[30] = ctrl->bmFramingInfo; data[31] = ctrl->bPreferedVersion; data[32] = ctrl->bMinVersion; @@ -186,6 +209,12 @@ int uvc_set_video_ctrl(struct uvc_video_device *video, video->streaming->intfnum, probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, UVC_CTRL_STREAMING_TIMEOUT); + if (ret != size) { + uvc_printk(KERN_ERR, "Failed to set UVC %s control : " + "%d (exp. %u).\n", probe ? "probe" : "commit", + ret, size); + ret = -EIO; + } kfree(data); return ret; @@ -252,6 +281,12 @@ done: return ret; } +int uvc_commit_video(struct uvc_video_device *video, + struct uvc_streaming_control *probe) +{ + return uvc_set_video_ctrl(video, probe, 0); +} + /* ------------------------------------------------------------------------ * Video codecs */ @@ -333,7 +368,7 @@ static int uvc_video_decode_start(struct uvc_video_device *video, /* Synchronize to the input stream by waiting for the FID bit to be * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE. - * queue->last_fid is initialized to -1, so the first isochronous + * video->last_fid is initialized to -1, so the first isochronous * frame will always be in sync. * * If the device doesn't toggle the FID bit, invert video->last_fid @@ -360,7 +395,7 @@ static int uvc_video_decode_start(struct uvc_video_device *video, * last payload can be lost anyway). We thus must check if the FID has * been toggled. * - * queue->last_fid is initialized to -1, so the first isochronous + * video->last_fid is initialized to -1, so the first isochronous * frame will never trigger an end of frame detection. * * Empty buffers (bytesused == 0) don't trigger end of frame detection @@ -418,6 +453,34 @@ static void uvc_video_decode_end(struct uvc_video_device *video, } } +static int uvc_video_encode_header(struct uvc_video_device *video, + struct uvc_buffer *buf, __u8 *data, int len) +{ + data[0] = 2; /* Header length */ + data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF + | (video->last_fid & UVC_STREAM_FID); + return 2; +} + +static int uvc_video_encode_data(struct uvc_video_device *video, + struct uvc_buffer *buf, __u8 *data, int len) +{ + struct uvc_video_queue *queue = &video->queue; + unsigned int nbytes; + void *mem; + + /* Copy video data to the URB buffer. */ + mem = queue->mem + buf->buf.m.offset + queue->buf_used; + nbytes = min((unsigned int)len, buf->buf.bytesused - queue->buf_used); + nbytes = min(video->bulk.max_payload_size - video->bulk.payload_size, + nbytes); + memcpy(data, mem, nbytes); + + queue->buf_used += nbytes; + + return nbytes; +} + /* ------------------------------------------------------------------------ * URB handling */ @@ -477,7 +540,7 @@ static void uvc_video_decode_bulk(struct urb *urb, /* If the URB is the first of its payload, decode and save the * header. */ - if (video->bulk.header_size == 0) { + if (video->bulk.header_size == 0 && !video->bulk.skip_payload) { do { ret = uvc_video_decode_start(video, buf, mem, len); if (ret == -EAGAIN) @@ -487,14 +550,13 @@ static void uvc_video_decode_bulk(struct urb *urb, /* If an error occured skip the rest of the payload. */ if (ret < 0 || buf == NULL) { video->bulk.skip_payload = 1; - return; - } + } else { + memcpy(video->bulk.header, mem, ret); + video->bulk.header_size = ret; - video->bulk.header_size = ret; - memcpy(video->bulk.header, mem, video->bulk.header_size); - - mem += ret; - len -= ret; + mem += ret; + len -= ret; + } } /* The buffer queue might have been cancelled while a bulk transfer @@ -525,6 +587,48 @@ static void uvc_video_decode_bulk(struct urb *urb, } } +static void uvc_video_encode_bulk(struct urb *urb, + struct uvc_video_device *video, struct uvc_buffer *buf) +{ + u8 *mem = urb->transfer_buffer; + int len = video->urb_size, ret; + + if (buf == NULL) { + urb->transfer_buffer_length = 0; + return; + } + + /* If the URB is the first of its payload, add the header. */ + if (video->bulk.header_size == 0) { + ret = uvc_video_encode_header(video, buf, mem, len); + video->bulk.header_size = ret; + video->bulk.payload_size += ret; + mem += ret; + len -= ret; + } + + /* Process video data. */ + ret = uvc_video_encode_data(video, buf, mem, len); + + video->bulk.payload_size += ret; + len -= ret; + + if (buf->buf.bytesused == video->queue.buf_used || + video->bulk.payload_size == video->bulk.max_payload_size) { + if (buf->buf.bytesused == video->queue.buf_used) { + video->queue.buf_used = 0; + buf->state = UVC_BUF_STATE_DONE; + uvc_queue_next_buffer(&video->queue, buf); + video->last_fid ^= UVC_STREAM_FID; + } + + video->bulk.header_size = 0; + video->bulk.payload_size = 0; + } + + urb->transfer_buffer_length = video->urb_size - len; +} + static void uvc_video_complete(struct urb *urb) { struct uvc_video_device *video = urb->context; @@ -722,7 +826,15 @@ static int uvc_init_video_bulk(struct uvc_video_device *video, if (uvc_alloc_urb_buffers(video, size) < 0) return -ENOMEM; - pipe = usb_rcvbulkpipe(video->dev->udev, ep->desc.bEndpointAddress); + if (usb_endpoint_dir_in(&ep->desc)) + pipe = usb_rcvbulkpipe(video->dev->udev, + ep->desc.bEndpointAddress); + else + pipe = usb_sndbulkpipe(video->dev->udev, + ep->desc.bEndpointAddress); + + if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + size = 0; for (i = 0; i < UVC_URBS; ++i) { urb = usb_alloc_urb(0, gfp_flags); @@ -854,7 +966,7 @@ int uvc_video_resume(struct uvc_video_device *video) video->frozen = 0; - if ((ret = uvc_set_video_ctrl(video, &video->streaming->ctrl, 0)) < 0) { + if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) { uvc_queue_enable(&video->queue, 0); return ret; } @@ -935,23 +1047,30 @@ int uvc_video_init(struct uvc_video_device *video) break; } - /* Commit the default settings. */ probe->bFormatIndex = format->index; probe->bFrameIndex = frame->bFrameIndex; - if ((ret = uvc_set_video_ctrl(video, probe, 0)) < 0) - return ret; video->streaming->cur_format = format; video->streaming->cur_frame = frame; atomic_set(&video->active, 0); /* Select the video decoding function */ - if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) - video->decode = uvc_video_decode_isight; - else if (video->streaming->intf->num_altsetting > 1) - video->decode = uvc_video_decode_isoc; - else - video->decode = uvc_video_decode_bulk; + if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) + video->decode = uvc_video_decode_isight; + else if (video->streaming->intf->num_altsetting > 1) + video->decode = uvc_video_decode_isoc; + else + video->decode = uvc_video_decode_bulk; + } else { + if (video->streaming->intf->num_altsetting == 1) + video->decode = uvc_video_encode_bulk; + else { + uvc_printk(KERN_INFO, "Isochronous endpoints are not " + "supported for video output devices.\n"); + return -EINVAL; + } + } return 0; } @@ -971,7 +1090,8 @@ int uvc_video_enable(struct uvc_video_device *video, int enable) return 0; } - if (video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) + if ((video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) || + uvc_no_drop_param) video->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE; else video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; @@ -979,6 +1099,10 @@ int uvc_video_enable(struct uvc_video_device *video, int enable) if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) return ret; + /* Commit the streaming parameters. */ + if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) + return ret; + return uvc_init_video(video, GFP_KERNEL); } diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 9a6bc1a..896b791 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -4,7 +4,6 @@ #include <linux/kernel.h> #include <linux/videodev2.h> - /* * Dynamic controls */ @@ -316,6 +315,7 @@ struct uvc_xu_control { #define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008 #define UVC_QUIRK_STREAM_NO_FID 0x00000010 #define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 +#define UVC_QUIRK_PRUNE_CONTROLS 0x00000040 /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 @@ -383,6 +383,11 @@ struct uvc_control_mapping { struct uvc_menu_info *menu_info; __u32 menu_count; + + __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query, + const __u8 *data); + void (*set) (struct uvc_control_mapping *mapping, __s32 value, + __u8 *data); }; struct uvc_control { @@ -523,6 +528,7 @@ struct uvc_streaming { __u16 maxpsize; struct uvc_streaming_header header; + enum v4l2_buf_type type; unsigned int nformats; struct uvc_format *format; @@ -558,12 +564,15 @@ struct uvc_buffer { #define UVC_QUEUE_DROP_INCOMPLETE (1 << 2) struct uvc_video_queue { + enum v4l2_buf_type type; + void *mem; unsigned int flags; __u32 sequence; unsigned int count; unsigned int buf_size; + unsigned int buf_used; struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS]; struct mutex mutex; /* protects buffers and mainqueue */ spinlock_t irqlock; /* protects irqqueue */ @@ -578,8 +587,9 @@ struct uvc_video_device { atomic_t active; unsigned int frozen : 1; - struct list_head iterms; - struct uvc_entity *oterm; + struct list_head iterms; /* Input terminals */ + struct uvc_entity *oterm; /* Output terminal */ + struct uvc_entity *sterm; /* USB streaming terminal */ struct uvc_entity *processing; struct uvc_entity *selector; struct list_head extensions; @@ -617,6 +627,7 @@ enum uvc_device_state { struct uvc_device { struct usb_device *udev; struct usb_interface *intf; + unsigned long warnings; __u32 quirks; int intfnum; char name[32]; @@ -679,6 +690,10 @@ struct uvc_driver { #define UVC_TRACE_SUSPEND (1 << 8) #define UVC_TRACE_STATUS (1 << 9) +#define UVC_WARN_MINMAX 0 +#define UVC_WARN_PROBE_DEF 1 + +extern unsigned int uvc_no_drop_param; extern unsigned int uvc_trace_param; #define uvc_trace(flag, msg...) \ @@ -687,6 +702,12 @@ extern unsigned int uvc_trace_param; printk(KERN_DEBUG "uvcvideo: " msg); \ } while (0) +#define uvc_warn_once(dev, warn, msg...) \ + do { \ + if (!test_and_set_bit(warn, &dev->warnings)) \ + printk(KERN_INFO "uvcvideo: " msg); \ + } while (0) + #define uvc_printk(level, msg...) \ printk(level "uvcvideo: " msg) @@ -709,7 +730,8 @@ extern struct uvc_driver uvc_driver; extern void uvc_delete(struct kref *kref); /* Video buffers queue management. */ -extern void uvc_queue_init(struct uvc_video_queue *queue); +extern void uvc_queue_init(struct uvc_video_queue *queue, + enum v4l2_buf_type type); extern int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers, unsigned int buflength); extern int uvc_free_buffers(struct uvc_video_queue *queue); @@ -740,10 +762,10 @@ extern int uvc_video_resume(struct uvc_video_device *video); extern int uvc_video_enable(struct uvc_video_device *video, int enable); extern int uvc_probe_video(struct uvc_video_device *video, struct uvc_streaming_control *probe); +extern int uvc_commit_video(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl); extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, __u8 intfnum, __u8 cs, void *data, __u16 size); -extern int uvc_set_video_ctrl(struct uvc_video_device *video, - struct uvc_streaming_control *ctrl, int probe); /* Status */ extern int uvc_status_init(struct uvc_device *dev); diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 846763d..c676b0b 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -28,7 +28,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Author: Alan Cox, <alan@redhat.com> + * Author: Alan Cox, <alan@lxorguk.ukuu.org.uk> * * Fixes: */ @@ -58,6 +58,7 @@ #include <asm/div64.h> #define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <linux/videodev2.h> @@ -320,6 +321,19 @@ const char **v4l2_ctrl_get_menu(u32 id) "Private packet, IVTV format", NULL }; + static const char *camera_power_line_frequency[] = { + "Disabled", + "50 Hz", + "60 Hz", + NULL + }; + static const char *camera_exposure_auto[] = { + "Auto Mode", + "Manual Mode", + "Shutter Priority Mode", + "Aperture Priority Mode", + NULL + }; switch (id) { case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: @@ -352,6 +366,10 @@ const char **v4l2_ctrl_get_menu(u32 id) return mpeg_stream_type; case V4L2_CID_MPEG_STREAM_VBI_FMT: return mpeg_stream_vbi_fmt; + case V4L2_CID_POWER_LINE_FREQUENCY: + return camera_power_line_frequency; + case V4L2_CID_EXPOSURE_AUTO: + return camera_exposure_auto; default: return NULL; } @@ -363,17 +381,37 @@ const char *v4l2_ctrl_get_name(u32 id) { switch (id) { /* USER controls */ - case V4L2_CID_USER_CLASS: return "User Controls"; - case V4L2_CID_AUDIO_VOLUME: return "Volume"; - case V4L2_CID_AUDIO_MUTE: return "Mute"; - case V4L2_CID_AUDIO_BALANCE: return "Balance"; - case V4L2_CID_AUDIO_BASS: return "Bass"; - case V4L2_CID_AUDIO_TREBLE: return "Treble"; - case V4L2_CID_AUDIO_LOUDNESS: return "Loudness"; - case V4L2_CID_BRIGHTNESS: return "Brightness"; - case V4L2_CID_CONTRAST: return "Contrast"; - case V4L2_CID_SATURATION: return "Saturation"; - case V4L2_CID_HUE: return "Hue"; + case V4L2_CID_USER_CLASS: return "User Controls"; + case V4L2_CID_AUDIO_VOLUME: return "Volume"; + case V4L2_CID_AUDIO_MUTE: return "Mute"; + case V4L2_CID_AUDIO_BALANCE: return "Balance"; + case V4L2_CID_AUDIO_BASS: return "Bass"; + case V4L2_CID_AUDIO_TREBLE: return "Treble"; + case V4L2_CID_AUDIO_LOUDNESS: return "Loudness"; + case V4L2_CID_BRIGHTNESS: return "Brightness"; + case V4L2_CID_CONTRAST: return "Contrast"; + case V4L2_CID_SATURATION: return "Saturation"; + case V4L2_CID_HUE: return "Hue"; + case V4L2_CID_BLACK_LEVEL: return "Black Level"; + case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic"; + case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance"; + case V4L2_CID_RED_BALANCE: return "Red Balance"; + case V4L2_CID_BLUE_BALANCE: return "Blue Balance"; + case V4L2_CID_GAMMA: return "Gamma"; + case V4L2_CID_EXPOSURE: return "Exposure"; + case V4L2_CID_AUTOGAIN: return "Gain, Automatic"; + case V4L2_CID_GAIN: return "Gain"; + case V4L2_CID_HFLIP: return "Horizontal Flip"; + case V4L2_CID_VFLIP: return "Vertical Flip"; + case V4L2_CID_HCENTER: return "Horizontal Center"; + case V4L2_CID_VCENTER: return "Vertical Center"; + case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency"; + case V4L2_CID_HUE_AUTO: return "Hue, Automatic"; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature"; + case V4L2_CID_SHARPNESS: return "Sharpness"; + case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation"; + case V4L2_CID_CHROMA_AGC: return "Chroma AGC"; + case V4L2_CID_COLOR_KILLER: return "Color Killer"; /* MPEG controls */ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; @@ -410,6 +448,25 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID"; case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format"; + /* CAMERA controls */ + case V4L2_CID_CAMERA_CLASS: return "Camera Controls"; + case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure"; + case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute"; + case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate"; + case V4L2_CID_PAN_RELATIVE: return "Pan, Relative"; + case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative"; + case V4L2_CID_PAN_RESET: return "Pan, Reset"; + case V4L2_CID_TILT_RESET: return "Tilt, Reset"; + case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute"; + case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute"; + case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute"; + case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative"; + case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic"; + case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute"; + case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative"; + case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous"; + case V4L2_CID_PRIVACY: return "Privacy"; + default: return NULL; } @@ -428,14 +485,22 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste switch (qctrl->id) { case V4L2_CID_AUDIO_MUTE: case V4L2_CID_AUDIO_LOUDNESS: + case V4L2_CID_AUTO_WHITE_BALANCE: + case V4L2_CID_AUTOGAIN: + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + case V4L2_CID_HUE_AUTO: case V4L2_CID_MPEG_AUDIO_MUTE: case V4L2_CID_MPEG_VIDEO_MUTE: case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: case V4L2_CID_MPEG_VIDEO_PULLDOWN: + case V4L2_CID_EXPOSURE_AUTO_PRIORITY: + case V4L2_CID_PRIVACY: qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; min = 0; max = step = 1; break; + case V4L2_CID_POWER_LINE_FREQUENCY: case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: case V4L2_CID_MPEG_AUDIO_ENCODING: case V4L2_CID_MPEG_AUDIO_L1_BITRATE: @@ -451,10 +516,12 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: case V4L2_CID_MPEG_STREAM_TYPE: case V4L2_CID_MPEG_STREAM_VBI_FMT: + case V4L2_CID_EXPOSURE_AUTO: qctrl->type = V4L2_CTRL_TYPE_MENU; step = 1; break; case V4L2_CID_USER_CLASS: + case V4L2_CID_CAMERA_CLASS: case V4L2_CID_MPEG_CLASS: qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS; qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; @@ -801,4 +868,116 @@ int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver return err != -ENOMEM ? 0 : err; } EXPORT_SYMBOL(v4l2_i2c_attach); + +void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, + const struct v4l2_subdev_ops *ops) +{ + v4l2_subdev_init(sd, ops); + /* the owner is the same as the i2c_client's driver owner */ + sd->owner = client->driver->driver.owner; + /* i2c_client and v4l2_subdev point to one another */ + v4l2_set_subdevdata(sd, client); + i2c_set_clientdata(client, sd); + /* initialize name */ + snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", + client->driver->driver.name, i2c_adapter_id(client->adapter), + client->addr); +} +EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init); + + + +/* Load an i2c sub-device. It assumes that i2c_get_adapdata(adapter) + returns the v4l2_device and that i2c_get_clientdata(client) + returns the v4l2_subdev. */ +struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter, + const char *module_name, const char *client_type, u8 addr) +{ + struct v4l2_device *dev = i2c_get_adapdata(adapter); + struct v4l2_subdev *sd = NULL; + struct i2c_client *client; + struct i2c_board_info info; + + BUG_ON(!dev); +#ifdef MODULE + if (module_name) + request_module(module_name); +#endif + /* Setup the i2c board info with the device type and + the device address. */ + memset(&info, 0, sizeof(info)); + strlcpy(info.type, client_type, sizeof(info.type)); + info.addr = addr; + + /* Create the i2c client */ + client = i2c_new_device(adapter, &info); + /* Note: it is possible in the future that + c->driver is NULL if the driver is still being loaded. + We need better support from the kernel so that we + can easily wait for the load to finish. */ + if (client == NULL || client->driver == NULL) + return NULL; + + /* Lock the module so we can safely get the v4l2_subdev pointer */ + if (!try_module_get(client->driver->driver.owner)) + return NULL; + sd = i2c_get_clientdata(client); + + /* Register with the v4l2_device which increases the module's + use count as well. */ + if (v4l2_device_register_subdev(dev, sd)) + sd = NULL; + /* Decrease the module use count to match the first try_module_get. */ + module_put(client->driver->driver.owner); + return sd; + +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); + +/* Probe and load an i2c sub-device. It assumes that i2c_get_adapdata(adapter) + returns the v4l2_device and that i2c_get_clientdata(client) + returns the v4l2_subdev. */ +struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, + const char *module_name, const char *client_type, + const unsigned short *addrs) +{ + struct v4l2_device *dev = i2c_get_adapdata(adapter); + struct v4l2_subdev *sd = NULL; + struct i2c_client *client = NULL; + struct i2c_board_info info; + + BUG_ON(!dev); +#ifdef MODULE + if (module_name) + request_module(module_name); +#endif + /* Setup the i2c board info with the device type and + the device address. */ + memset(&info, 0, sizeof(info)); + strlcpy(info.type, client_type, sizeof(info.type)); + + /* Probe and create the i2c client */ + client = i2c_new_probed_device(adapter, &info, addrs); + /* Note: it is possible in the future that + c->driver is NULL if the driver is still being loaded. + We need better support from the kernel so that we + can easily wait for the load to finish. */ + if (client == NULL || client->driver == NULL) + return NULL; + + /* Lock the module so we can safely get the v4l2_subdev pointer */ + if (!try_module_get(client->driver->driver.owner)) + return NULL; + sd = i2c_get_clientdata(client); + + /* Register with the v4l2_device which increases the module's + use count as well. */ + if (v4l2_device_register_subdev(dev, sd)) + sd = NULL; + /* Decrease the module use count to match the first try_module_get. */ + module_put(client->driver->driver.owner); + return sd; +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev); + #endif diff --git a/drivers/media/video/compat_ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 0ea85a0..d0e1bd3 100644 --- a/drivers/media/video/compat_ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -7,12 +7,14 @@ * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs * Copyright (C) 2003 Pavel Machek (pavel@suse.cz) * Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be) + * Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> * * These routines maintain argument size conversion between 32bit and 64bit * ioctls. */ #include <linux/compat.h> +#define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include <linux/videodev.h> #include <linux/videodev2.h> #include <linux/module.h> @@ -32,7 +34,7 @@ struct video_tuner32 { static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user *up) { - if(!access_ok(VERIFY_READ, up, sizeof(struct video_tuner32)) || + if (!access_ok(VERIFY_READ, up, sizeof(struct video_tuner32)) || get_user(kp->tuner, &up->tuner) || copy_from_user(kp->name, up->name, 32) || get_user(kp->rangelow, &up->rangelow) || @@ -46,7 +48,7 @@ static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user *up) { - if(!access_ok(VERIFY_WRITE, up, sizeof(struct video_tuner32)) || + if (!access_ok(VERIFY_WRITE, up, sizeof(struct video_tuner32)) || put_user(kp->tuner, &up->tuner) || copy_to_user(up->name, kp->name, 32) || put_user(kp->rangelow, &up->rangelow) || @@ -58,7 +60,6 @@ static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user return 0; } - struct video_buffer32 { compat_caddr_t base; compat_int_t height, width, depth, bytesperline; @@ -88,7 +89,7 @@ static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 __u { u32 tmp = (u32)((unsigned long)kp->base); - if(!access_ok(VERIFY_WRITE, up, sizeof(struct video_buffer32)) || + if (!access_ok(VERIFY_WRITE, up, sizeof(struct video_buffer32)) || put_user(tmp, &up->base) || put_user(kp->height, &up->height) || put_user(kp->width, &up->width) || @@ -99,7 +100,7 @@ static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 __u } struct video_clip32 { - s32 x, y, width, height; /* Its really s32 in videodev.h */ + s32 x, y, width, height; /* It's really s32 in videodev.h */ compat_caddr_t next; }; @@ -108,29 +109,76 @@ struct video_window32 { compat_caddr_t clips; compat_int_t clipcount; }; -#endif -static int native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static int get_video_window32(struct video_window *kp, struct video_window32 __user *up) { - int ret = -ENOIOCTLCMD; + struct video_clip __user *uclips; + struct video_clip __user *kclips; + compat_caddr_t p; + int nclips; - if (file->f_op->unlocked_ioctl) - ret = file->f_op->unlocked_ioctl(file, cmd, arg); - else if (file->f_op->ioctl) { - lock_kernel(); - ret = file->f_op->ioctl(file->f_path.dentry->d_inode, file, cmd, arg); - unlock_kernel(); + if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32))) + return -EFAULT; + + if (get_user(nclips, &up->clipcount)) + return -EFAULT; + + if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32)) || + get_user(kp->x, &up->x) || + get_user(kp->y, &up->y) || + get_user(kp->width, &up->width) || + get_user(kp->height, &up->height) || + get_user(kp->chromakey, &up->chromakey) || + get_user(kp->flags, &up->flags) || + get_user(kp->clipcount, &up->clipcount)) + return -EFAULT; + + nclips = kp->clipcount; + kp->clips = NULL; + + if (nclips == 0) + return 0; + if (get_user(p, &up->clips)) + return -EFAULT; + uclips = compat_ptr(p); + + /* If nclips < 0, then it is a clipping bitmap of size + VIDEO_CLIPMAP_SIZE */ + if (nclips < 0) { + if (!access_ok(VERIFY_READ, uclips, VIDEO_CLIPMAP_SIZE)) + return -EFAULT; + kp->clips = compat_alloc_user_space(VIDEO_CLIPMAP_SIZE); + if (copy_in_user(kp->clips, uclips, VIDEO_CLIPMAP_SIZE)) + return -EFAULT; + return 0; } - return ret; -} + /* Otherwise it is an array of video_clip structs. */ + if (!access_ok(VERIFY_READ, uclips, nclips * sizeof(struct video_clip))) + return -EFAULT; + kp->clips = compat_alloc_user_space(nclips * sizeof(struct video_clip)); + kclips = kp->clips; + while (nclips--) { + int err; + + err = copy_in_user(&kclips->x, &uclips->x, sizeof(kclips->x)); + err |= copy_in_user(&kclips->y, &uclips->y, sizeof(kclips->y)); + err |= copy_in_user(&kclips->width, &uclips->width, sizeof(kclips->width)); + err |= copy_in_user(&kclips->height, &uclips->height, sizeof(kclips->height)); + kclips->next = NULL; + if (err) + return -EFAULT; + kclips++; + uclips++; + } + return 0; +} -#ifdef CONFIG_VIDEO_V4L1_COMPAT /* You get back everything except the clips... */ static int put_video_window32(struct video_window *kp, struct video_window32 __user *up) { - if(!access_ok(VERIFY_WRITE, up, sizeof(struct video_window32)) || + if (!access_ok(VERIFY_WRITE, up, sizeof(struct video_window32)) || put_user(kp->x, &up->x) || put_user(kp->y, &up->y) || put_user(kp->width, &up->width) || @@ -141,16 +189,61 @@ static int put_video_window32(struct video_window *kp, struct video_window32 __u return -EFAULT; return 0; } + +struct video_code32 { + char loadwhat[16]; /* name or tag of file being passed */ + compat_int_t datasize; + unsigned char *data; +}; + +static int get_microcode32(struct video_code *kp, struct video_code32 __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct video_code32)) || + copy_from_user(kp->loadwhat, up->loadwhat, sizeof(up->loadwhat)) || + get_user(kp->datasize, &up->datasize) || + copy_from_user(kp->data, up->data, up->datasize)) + return -EFAULT; + return 0; +} + +#define VIDIOCGTUNER32 _IOWR('v', 4, struct video_tuner32) +#define VIDIOCSTUNER32 _IOW('v', 5, struct video_tuner32) +#define VIDIOCGWIN32 _IOR('v', 9, struct video_window32) +#define VIDIOCSWIN32 _IOW('v', 10, struct video_window32) +#define VIDIOCGFBUF32 _IOR('v', 11, struct video_buffer32) +#define VIDIOCSFBUF32 _IOW('v', 12, struct video_buffer32) +#define VIDIOCGFREQ32 _IOR('v', 14, u32) +#define VIDIOCSFREQ32 _IOW('v', 15, u32) +#define VIDIOCSMICROCODE32 _IOW('v', 27, struct video_code32) + +#define VIDIOCCAPTURE32 _IOW('v', 8, s32) +#define VIDIOCSYNC32 _IOW('v', 18, s32) +#define VIDIOCSWRITEMODE32 _IOW('v', 25, s32) + #endif -struct v4l2_clip32 +static int native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + int ret = -ENOIOCTLCMD; + + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, cmd, arg); + else if (file->f_op->ioctl) { + lock_kernel(); + ret = file->f_op->ioctl(file->f_path.dentry->d_inode, file, cmd, arg); + unlock_kernel(); + } + + return ret; +} + + +struct v4l2_clip32 { struct v4l2_rect c; compat_caddr_t next; }; -struct v4l2_window32 -{ +struct v4l2_window32 { struct v4l2_rect w; enum v4l2_field field; __u32 chromakey; @@ -231,15 +324,28 @@ static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vb return 0; } -struct v4l2_format32 +static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up) +{ + if (copy_from_user(kp, up, sizeof(struct v4l2_sliced_vbi_format))) + return -EFAULT; + return 0; +} + +static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up) { + if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format))) + return -EFAULT; + return 0; +} + +struct v4l2_format32 { enum v4l2_buf_type type; - union - { - struct v4l2_pix_format pix; // V4L2_BUF_TYPE_VIDEO_CAPTURE - struct v4l2_window32 win; // V4L2_BUF_TYPE_VIDEO_OVERLAY - struct v4l2_vbi_format vbi; // V4L2_BUF_TYPE_VBI_CAPTURE - __u8 raw_data[200]; // user-defined + union { + struct v4l2_pix_format pix; + struct v4l2_window32 win; + struct v4l2_vbi_format vbi; + struct v4l2_sliced_vbi_format sliced; + __u8 raw_data[200]; /* user-defined */ } fmt; }; @@ -250,52 +356,62 @@ static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user return -EFAULT; switch (kp->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: return get_v4l2_window32(&kp->fmt.win, &up->fmt.win); case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi); + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced); + case V4L2_BUF_TYPE_PRIVATE: + if (copy_from_user(kp, up, sizeof(kp->fmt.raw_data))) + return -EFAULT; + return 0; + case 0: + return -EINVAL; default: - printk("compat_ioctl : unexpected VIDIOC_FMT type %d\n", + printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n", kp->type); - return -ENXIO; + return -EINVAL; } } static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) { - if(!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) || + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) || put_user(kp->type, &up->type)) return -EFAULT; switch (kp->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: return put_v4l2_window32(&kp->fmt.win, &up->fmt.win); case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi); + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced); + case V4L2_BUF_TYPE_PRIVATE: + if (copy_to_user(up, kp, sizeof(up->fmt.raw_data))) + return -EFAULT; + return 0; + case 0: + return -EINVAL; default: - return -ENXIO; + printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n", + kp->type); + return -EINVAL; } } -static inline int get_v4l2_standard(struct v4l2_standard *kp, struct v4l2_standard __user *up) -{ - if (copy_from_user(kp, up, sizeof(struct v4l2_standard))) - return -EFAULT; - return 0; - -} - -static inline int put_v4l2_standard(struct v4l2_standard *kp, struct v4l2_standard __user *up) -{ - if (copy_to_user(up, kp, sizeof(struct v4l2_standard))) - return -EFAULT; - return 0; -} - -struct v4l2_standard32 -{ +struct v4l2_standard32 { __u32 index; __u32 id[2]; /* __u64 would get the alignment wrong */ __u8 name[24]; @@ -315,7 +431,7 @@ static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up) { - if(!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) || + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) || put_user(kp->index, &up->index) || copy_to_user(up->id, &kp->id, sizeof(__u64)) || copy_to_user(up->name, kp->name, 24) || @@ -326,23 +442,7 @@ static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 return 0; } -static inline int get_v4l2_tuner(struct v4l2_tuner *kp, struct v4l2_tuner __user *up) -{ - if (copy_from_user(kp, up, sizeof(struct v4l2_tuner))) - return -EFAULT; - return 0; - -} - -static inline int put_v4l2_tuner(struct v4l2_tuner *kp, struct v4l2_tuner __user *up) -{ - if (copy_to_user(up, kp, sizeof(struct v4l2_tuner))) - return -EFAULT; - return 0; -} - -struct v4l2_buffer32 -{ +struct v4l2_buffer32 { __u32 index; enum v4l2_buf_type type; __u32 bytesused; @@ -373,7 +473,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user get_user(kp->memory, &up->memory) || get_user(kp->input, &up->input)) return -EFAULT; - switch(kp->memory) { + switch (kp->memory) { case V4L2_MEMORY_MMAP: break; case V4L2_MEMORY_USERPTR: @@ -388,7 +488,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user } break; case V4L2_MEMORY_OVERLAY: - if(get_user(kp->m.offset, &up->m.offset)) + if (get_user(kp->m.offset, &up->m.offset)) return -EFAULT; break; } @@ -404,7 +504,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user put_user(kp->memory, &up->memory) || put_user(kp->input, &up->input)) return -EFAULT; - switch(kp->memory) { + switch (kp->memory) { case V4L2_MEMORY_MMAP: if (put_user(kp->length, &up->length) || put_user(kp->m.offset, &up->m.offset)) @@ -431,8 +531,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user return 0; } -struct v4l2_framebuffer32 -{ +struct v4l2_framebuffer32 { __u32 capability; __u32 flags; compat_caddr_t base; @@ -457,7 +556,7 @@ static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame { u32 tmp = (u32)((unsigned long)kp->base); - if(!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) || + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) || put_user(tmp, &up->base) || put_user(kp->capability, &up->capability) || put_user(kp->flags, &up->flags)) @@ -466,150 +565,145 @@ static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame return 0; } -static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input __user *up) +struct v4l2_input32 { + __u32 index; /* Which input */ + __u8 name[32]; /* Label */ + __u32 type; /* Type of input */ + __u32 audioset; /* Associated audios (bitfield) */ + __u32 tuner; /* Associated tuner */ + v4l2_std_id std; + __u32 status; + __u32 reserved[4]; +} __attribute__ ((packed)); + +/* The 64-bit v4l2_input struct has extra padding at the end of the struct. + Otherwise it is identical to the 32-bit version. */ +static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up) { - if (copy_from_user(kp, up, sizeof(struct v4l2_input) - 4)) + if (copy_from_user(kp, up, sizeof(struct v4l2_input32))) return -EFAULT; return 0; } -static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input __user *up) +static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up) { - if (copy_to_user(up, kp, sizeof(struct v4l2_input) - 4)) + if (copy_to_user(up, kp, sizeof(struct v4l2_input32))) return -EFAULT; return 0; } -static inline int get_v4l2_input(struct v4l2_input *kp, struct v4l2_input __user *up) -{ - if (copy_from_user(kp, up, sizeof(struct v4l2_input))) - return -EFAULT; - return 0; -} - -static inline int put_v4l2_input(struct v4l2_input *kp, struct v4l2_input __user *up) -{ - if (copy_to_user(up, kp, sizeof(struct v4l2_input))) - return -EFAULT; - return 0; -} - -#ifdef CONFIG_VIDEO_V4L1_COMPAT -struct video_code32 -{ - char loadwhat[16]; /* name or tag of file being passed */ - compat_int_t datasize; - unsigned char *data; +struct v4l2_ext_controls32 { + __u32 ctrl_class; + __u32 count; + __u32 error_idx; + __u32 reserved[2]; + compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */ }; -static inline int microcode32(struct video_code *kp, struct video_code32 __user *up) +static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) { - if(!access_ok(VERIFY_READ, up, sizeof(struct video_code32)) || - copy_from_user(kp->loadwhat, up->loadwhat, sizeof (up->loadwhat)) || - get_user(kp->datasize, &up->datasize) || - copy_from_user(kp->data, up->data, up->datasize)) + struct v4l2_ext_control __user *ucontrols; + struct v4l2_ext_control __user *kcontrols; + int n; + compat_caddr_t p; + + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) || + get_user(kp->ctrl_class, &up->ctrl_class) || + get_user(kp->count, &up->count) || + get_user(kp->error_idx, &up->error_idx) || + copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved))) return -EFAULT; + n = kp->count; + if (n == 0) { + kp->controls = NULL; + return 0; + } + if (get_user(p, &up->controls)) + return -EFAULT; + ucontrols = compat_ptr(p); + if (!access_ok(VERIFY_READ, ucontrols, n * sizeof(struct v4l2_ext_control))) + return -EFAULT; + kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control)); + kp->controls = kcontrols; + while (--n >= 0) { + if (copy_in_user(&kcontrols->id, &ucontrols->id, sizeof(__u32))) + return -EFAULT; + if (copy_in_user(&kcontrols->reserved2, &ucontrols->reserved2, sizeof(ucontrols->reserved2))) + return -EFAULT; + /* Note: if the void * part of the union ever becomes relevant + then we need to know the type of the control in order to do + the right thing here. Luckily, that is not yet an issue. */ + if (copy_in_user(&kcontrols->value, &ucontrols->value, sizeof(ucontrols->value))) + return -EFAULT; + ucontrols++; + kcontrols++; + } return 0; } -#define VIDIOCGTUNER32 _IOWR('v',4, struct video_tuner32) -#define VIDIOCSTUNER32 _IOW('v',5, struct video_tuner32) -#define VIDIOCGWIN32 _IOR('v',9, struct video_window32) -#define VIDIOCSWIN32 _IOW('v',10, struct video_window32) -#define VIDIOCGFBUF32 _IOR('v',11, struct video_buffer32) -#define VIDIOCSFBUF32 _IOW('v',12, struct video_buffer32) -#define VIDIOCGFREQ32 _IOR('v',14, u32) -#define VIDIOCSFREQ32 _IOW('v',15, u32) -#define VIDIOCSMICROCODE32 _IOW('v',27, struct video_code32) - -#endif - -/* VIDIOC_ENUMINPUT32 is VIDIOC_ENUMINPUT minus 4 bytes of padding alignement */ -#define VIDIOC_ENUMINPUT32 VIDIOC_ENUMINPUT - _IOC(0, 0, 0, 4) -#define VIDIOC_G_FMT32 _IOWR ('V', 4, struct v4l2_format32) -#define VIDIOC_S_FMT32 _IOWR ('V', 5, struct v4l2_format32) -#define VIDIOC_QUERYBUF32 _IOWR ('V', 9, struct v4l2_buffer32) -#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32) -#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32) -/* VIDIOC_OVERLAY is now _IOW, but was _IOWR */ -#define VIDIOC_OVERLAY32 _IOWR ('V', 14, compat_int_t) -#define VIDIOC_QBUF32 _IOWR ('V', 15, struct v4l2_buffer32) -#define VIDIOC_DQBUF32 _IOWR ('V', 17, struct v4l2_buffer32) -#define VIDIOC_STREAMON32 _IOW ('V', 18, compat_int_t) -#define VIDIOC_STREAMOFF32 _IOW ('V', 19, compat_int_t) -#define VIDIOC_ENUMSTD32 _IOWR ('V', 25, struct v4l2_standard32) -/* VIDIOC_S_CTRL is now _IOWR, but was _IOW */ -#define VIDIOC_S_CTRL32 _IOW ('V', 28, struct v4l2_control) -#define VIDIOC_G_INPUT32 _IOR ('V', 38, compat_int_t) -#define VIDIOC_S_INPUT32 _IOWR ('V', 39, compat_int_t) -#define VIDIOC_TRY_FMT32 _IOWR ('V', 64, struct v4l2_format32) - -#ifdef CONFIG_VIDEO_V4L1_COMPAT -enum { - MaxClips = (~0U-sizeof(struct video_window))/sizeof(struct video_clip) -}; - -static int do_set_window(struct file *file, unsigned int cmd, unsigned long arg) +static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) { - struct video_window32 __user *up = compat_ptr(arg); - struct video_window __user *vw; - struct video_clip __user *p; - int nclips; - u32 n; - - if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32))) - return -EFAULT; + struct v4l2_ext_control __user *ucontrols; + struct v4l2_ext_control __user *kcontrols = kp->controls; + int n = kp->count; + compat_caddr_t p; + + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) || + put_user(kp->ctrl_class, &up->ctrl_class) || + put_user(kp->count, &up->count) || + put_user(kp->error_idx, &up->error_idx) || + copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved))) + return -EFAULT; + if (!kp->count) + return 0; - if (get_user(nclips, &up->clipcount)) + if (get_user(p, &up->controls)) return -EFAULT; - - /* Peculiar interface... */ - if (nclips < 0) - nclips = VIDEO_CLIPMAP_SIZE; - - if (nclips > MaxClips) - return -ENOMEM; - - vw = compat_alloc_user_space(sizeof(struct video_window) + - nclips * sizeof(struct video_clip)); - - p = nclips ? (struct video_clip __user *)(vw + 1) : NULL; - - if (get_user(n, &up->x) || put_user(n, &vw->x) || - get_user(n, &up->y) || put_user(n, &vw->y) || - get_user(n, &up->width) || put_user(n, &vw->width) || - get_user(n, &up->height) || put_user(n, &vw->height) || - get_user(n, &up->chromakey) || put_user(n, &vw->chromakey) || - get_user(n, &up->flags) || put_user(n, &vw->flags) || - get_user(n, &up->clipcount) || put_user(n, &vw->clipcount) || - get_user(n, &up->clips) || put_user(p, &vw->clips)) + ucontrols = compat_ptr(p); + if (!access_ok(VERIFY_WRITE, ucontrols, n * sizeof(struct v4l2_ext_control))) return -EFAULT; - if (nclips) { - struct video_clip32 __user *u = compat_ptr(n); - int i; - if (!u) - return -EINVAL; - for (i = 0; i < nclips; i++, u++, p++) { - s32 v; - if (!access_ok(VERIFY_READ, u, sizeof(struct video_clip32)) || - !access_ok(VERIFY_WRITE, p, sizeof(struct video_clip32)) || - get_user(v, &u->x) || - put_user(v, &p->x) || - get_user(v, &u->y) || - put_user(v, &p->y) || - get_user(v, &u->width) || - put_user(v, &p->width) || - get_user(v, &u->height) || - put_user(v, &p->height) || - put_user(NULL, &p->next)) - return -EFAULT; - } + while (--n >= 0) { + if (copy_in_user(&ucontrols->id, &kcontrols->id, sizeof(__u32))) + return -EFAULT; + if (copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2, + sizeof(ucontrols->reserved2))) + return -EFAULT; + /* Note: if the void * part of the union ever becomes relevant + then we need to know the type of the control in order to do + the right thing here. Luckily, that is not yet an issue. */ + if (copy_in_user(&ucontrols->value, &kcontrols->value, sizeof(ucontrols->value))) + return -EFAULT; + ucontrols++; + kcontrols++; } - - return native_ioctl(file, VIDIOCSWIN, (unsigned long)vw); + return 0; } + +#define VIDIOC_G_FMT32 _IOWR('V', 4, struct v4l2_format32) +#define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32) +#define VIDIOC_QUERYBUF32 _IOWR('V', 9, struct v4l2_buffer32) +#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32) +#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32) +#define VIDIOC_QBUF32 _IOWR('V', 15, struct v4l2_buffer32) +#define VIDIOC_DQBUF32 _IOWR('V', 17, struct v4l2_buffer32) +#define VIDIOC_ENUMSTD32 _IOWR('V', 25, struct v4l2_standard32) +#define VIDIOC_ENUMINPUT32 _IOWR('V', 26, struct v4l2_input32) +#define VIDIOC_TRY_FMT32 _IOWR('V', 64, struct v4l2_format32) +#define VIDIOC_G_EXT_CTRLS32 _IOWR('V', 71, struct v4l2_ext_controls32) +#define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32) +#define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32) + +#define VIDIOC_OVERLAY32 _IOW ('V', 14, s32) +#ifdef __OLD_VIDIOC_ +#define VIDIOC_OVERLAY32_OLD _IOWR('V', 14, s32) #endif +#define VIDIOC_STREAMON32 _IOW ('V', 18, s32) +#define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32) +#define VIDIOC_G_INPUT32 _IOR ('V', 38, s32) +#define VIDIOC_S_INPUT32 _IOWR('V', 39, s32) +#define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32) +#define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32) static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -624,53 +718,60 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg struct v4l2_format v2f; struct v4l2_buffer v2b; struct v4l2_framebuffer v2fb; - struct v4l2_standard v2s; struct v4l2_input v2i; - struct v4l2_tuner v2t; + struct v4l2_standard v2s; + struct v4l2_ext_controls v2ecs; unsigned long vx; + int vi; } karg; void __user *up = compat_ptr(arg); int compatible_arg = 1; int err = 0; - int realcmd = cmd; /* First, convert the command. */ - switch(cmd) { + switch (cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGTUNER32: realcmd = cmd = VIDIOCGTUNER; break; - case VIDIOCSTUNER32: realcmd = cmd = VIDIOCSTUNER; break; - case VIDIOCGWIN32: realcmd = cmd = VIDIOCGWIN; break; - case VIDIOCGFBUF32: realcmd = cmd = VIDIOCGFBUF; break; - case VIDIOCSFBUF32: realcmd = cmd = VIDIOCSFBUF; break; - case VIDIOCGFREQ32: realcmd = cmd = VIDIOCGFREQ; break; - case VIDIOCSFREQ32: realcmd = cmd = VIDIOCSFREQ; break; - case VIDIOCSMICROCODE32: realcmd = cmd = VIDIOCSMICROCODE; break; + case VIDIOCGTUNER32: cmd = VIDIOCGTUNER; break; + case VIDIOCSTUNER32: cmd = VIDIOCSTUNER; break; + case VIDIOCGWIN32: cmd = VIDIOCGWIN; break; + case VIDIOCSWIN32: cmd = VIDIOCSWIN; break; + case VIDIOCGFBUF32: cmd = VIDIOCGFBUF; break; + case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break; + case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break; + case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break; + case VIDIOCSMICROCODE32: cmd = VIDIOCSMICROCODE; break; #endif - case VIDIOC_G_FMT32: realcmd = cmd = VIDIOC_G_FMT; break; - case VIDIOC_S_FMT32: realcmd = cmd = VIDIOC_S_FMT; break; - case VIDIOC_QUERYBUF32: realcmd = cmd = VIDIOC_QUERYBUF; break; - case VIDIOC_QBUF32: realcmd = cmd = VIDIOC_QBUF; break; - case VIDIOC_DQBUF32: realcmd = cmd = VIDIOC_DQBUF; break; - case VIDIOC_STREAMON32: realcmd = cmd = VIDIOC_STREAMON; break; - case VIDIOC_STREAMOFF32: realcmd = cmd = VIDIOC_STREAMOFF; break; - case VIDIOC_G_FBUF32: realcmd = cmd = VIDIOC_G_FBUF; break; - case VIDIOC_S_FBUF32: realcmd = cmd = VIDIOC_S_FBUF; break; - case VIDIOC_OVERLAY32: realcmd = cmd = VIDIOC_OVERLAY; break; - case VIDIOC_ENUMSTD32: realcmd = VIDIOC_ENUMSTD; break; - case VIDIOC_ENUMINPUT32: realcmd = VIDIOC_ENUMINPUT; break; - case VIDIOC_S_CTRL32: realcmd = cmd = VIDIOC_S_CTRL; break; - case VIDIOC_G_INPUT32: realcmd = cmd = VIDIOC_G_INPUT; break; - case VIDIOC_S_INPUT32: realcmd = cmd = VIDIOC_S_INPUT; break; - case VIDIOC_TRY_FMT32: realcmd = cmd = VIDIOC_TRY_FMT; break; - }; - - switch(cmd) { + case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break; + case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break; + case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break; + case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break; + case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break; + case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break; + case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break; + case VIDIOC_ENUMSTD32: cmd = VIDIOC_ENUMSTD; break; + case VIDIOC_ENUMINPUT32: cmd = VIDIOC_ENUMINPUT; break; + case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break; + case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break; + case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break; + case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break; + case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break; +#ifdef __OLD_VIDIOC_ + case VIDIOC_OVERLAY32_OLD: cmd = VIDIOC_OVERLAY; break; +#endif + case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break; + case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; + case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break; + case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break; + case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break; + case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break; + } + + switch (cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT case VIDIOCSTUNER: case VIDIOCGTUNER: err = get_video_tuner32(&karg.vt, up); compatible_arg = 0; - break; case VIDIOCSFBUF: @@ -678,19 +779,42 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg compatible_arg = 0; break; + case VIDIOCSWIN: + err = get_video_window32(&karg.vw, up); + compatible_arg = 0; + break; + + case VIDIOCGWIN: + case VIDIOCGFBUF: + case VIDIOCGFREQ: + compatible_arg = 0; + break; + + case VIDIOCSMICROCODE: + err = get_microcode32(&karg.vc, up); + compatible_arg = 0; + break; case VIDIOCSFREQ: + err = get_user(karg.vx, (u32 __user *)up); + compatible_arg = 0; + break; + + case VIDIOCCAPTURE: + case VIDIOCSYNC: + case VIDIOCSWRITEMODE: #endif - case VIDIOC_S_INPUT: case VIDIOC_OVERLAY: case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: - err = get_user(karg.vx, (u32 __user *)up); - compatible_arg = 1; + case VIDIOC_S_INPUT: + case VIDIOC_S_OUTPUT: + err = get_user(karg.vi, (s32 __user *)up); + compatible_arg = 0; break; - case VIDIOC_S_FBUF: - err = get_v4l2_framebuffer32(&karg.v2fb, up); + case VIDIOC_G_INPUT: + case VIDIOC_G_OUTPUT: compatible_arg = 0; break; @@ -708,122 +832,108 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg compatible_arg = 0; break; - case VIDIOC_ENUMSTD: - err = get_v4l2_standard(&karg.v2s, up); + case VIDIOC_S_FBUF: + err = get_v4l2_framebuffer32(&karg.v2fb, up); compatible_arg = 0; break; - case VIDIOC_ENUMSTD32: - err = get_v4l2_standard32(&karg.v2s, up); + case VIDIOC_G_FBUF: compatible_arg = 0; break; - case VIDIOC_ENUMINPUT: - err = get_v4l2_input(&karg.v2i, up); + case VIDIOC_ENUMSTD: + err = get_v4l2_standard32(&karg.v2s, up); compatible_arg = 0; break; - case VIDIOC_ENUMINPUT32: + case VIDIOC_ENUMINPUT: err = get_v4l2_input32(&karg.v2i, up); compatible_arg = 0; break; - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - err = get_v4l2_tuner(&karg.v2t, up); + case VIDIOC_G_EXT_CTRLS: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + err = get_v4l2_ext_controls32(&karg.v2ecs, up); compatible_arg = 0; break; + } + if (err) + return err; -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGWIN: - case VIDIOCGFBUF: - case VIDIOCGFREQ: -#endif - case VIDIOC_G_FBUF: - case VIDIOC_G_INPUT: - compatible_arg = 0; - break; -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCSMICROCODE: - err = microcode32(&karg.vc, up); - compatible_arg = 0; - break; -#endif - }; - if(err) - goto out; - - if(compatible_arg) - err = native_ioctl(file, realcmd, (unsigned long)up); + if (compatible_arg) + err = native_ioctl(file, cmd, (unsigned long)up); else { mm_segment_t old_fs = get_fs(); set_fs(KERNEL_DS); - err = native_ioctl(file, realcmd, (unsigned long) &karg); + err = native_ioctl(file, cmd, (unsigned long)&karg); set_fs(old_fs); } - if(err == 0) { - switch(cmd) { + + /* Special case: even after an error we need to put the + results back for these ioctls since the error_idx will + contain information on which control failed. */ + switch (cmd) { + case VIDIOC_G_EXT_CTRLS: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + if (put_v4l2_ext_controls32(&karg.v2ecs, up)) + err = -EFAULT; + break; + } + if (err) + return err; + + switch (cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGTUNER: - err = put_video_tuner32(&karg.vt, up); - break; + case VIDIOCGTUNER: + err = put_video_tuner32(&karg.vt, up); + break; - case VIDIOCGWIN: - err = put_video_window32(&karg.vw, up); - break; + case VIDIOCGWIN: + err = put_video_window32(&karg.vw, up); + break; - case VIDIOCGFBUF: - err = put_video_buffer32(&karg.vb, up); - break; + case VIDIOCGFBUF: + err = put_video_buffer32(&karg.vb, up); + break; + case VIDIOCGFREQ: + err = put_user(((u32)karg.vx), (u32 __user *)up); + break; #endif - case VIDIOC_G_FBUF: - err = put_v4l2_framebuffer32(&karg.v2fb, up); - break; - - case VIDIOC_G_FMT: - case VIDIOC_S_FMT: - case VIDIOC_TRY_FMT: - err = put_v4l2_format32(&karg.v2f, up); - break; - - case VIDIOC_QUERYBUF: - case VIDIOC_QBUF: - case VIDIOC_DQBUF: - err = put_v4l2_buffer32(&karg.v2b, up); - break; - - case VIDIOC_ENUMSTD: - err = put_v4l2_standard(&karg.v2s, up); - break; - - case VIDIOC_ENUMSTD32: - err = put_v4l2_standard32(&karg.v2s, up); - break; - - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - err = put_v4l2_tuner(&karg.v2t, up); - break; - - case VIDIOC_ENUMINPUT: - err = put_v4l2_input(&karg.v2i, up); - break; - - case VIDIOC_ENUMINPUT32: - err = put_v4l2_input32(&karg.v2i, up); - break; + case VIDIOC_S_INPUT: + case VIDIOC_S_OUTPUT: + case VIDIOC_G_INPUT: + case VIDIOC_G_OUTPUT: + err = put_user(((s32)karg.vi), (s32 __user *)up); + break; -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGFREQ: -#endif - case VIDIOC_G_INPUT: - err = put_user(((u32)karg.vx), (u32 __user *)up); - break; - }; + case VIDIOC_G_FBUF: + err = put_v4l2_framebuffer32(&karg.v2fb, up); + break; + + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_TRY_FMT: + err = put_v4l2_format32(&karg.v2f, up); + break; + + case VIDIOC_QUERYBUF: + case VIDIOC_QBUF: + case VIDIOC_DQBUF: + err = put_v4l2_buffer32(&karg.v2b, up); + break; + + case VIDIOC_ENUMSTD: + err = put_v4l2_standard32(&karg.v2s, up); + break; + + case VIDIOC_ENUMINPUT: + err = put_v4l2_input32(&karg.v2i, up); + break; } -out: return err; } @@ -836,26 +946,48 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCSWIN32: - ret = do_set_window(file, cmd, arg); - break; + case VIDIOCGCAP: + case VIDIOCGCHAN: + case VIDIOCSCHAN: case VIDIOCGTUNER32: case VIDIOCSTUNER32: + case VIDIOCGPICT: + case VIDIOCSPICT: + case VIDIOCCAPTURE32: case VIDIOCGWIN32: + case VIDIOCSWIN32: case VIDIOCGFBUF32: case VIDIOCSFBUF32: + case VIDIOCKEY: case VIDIOCGFREQ32: case VIDIOCSFREQ32: case VIDIOCGAUDIO: case VIDIOCSAUDIO: + case VIDIOCSYNC32: + case VIDIOCMCAPTURE: + case VIDIOCGMBUF: + case VIDIOCGUNIT: + case VIDIOCGCAPTURE: + case VIDIOCSCAPTURE: + case VIDIOCSPLAYMODE: + case VIDIOCSWRITEMODE32: + case VIDIOCGPLAYINFO: + case VIDIOCSMICROCODE32: case VIDIOCGVBIFMT: case VIDIOCSVBIFMT: #endif +#ifdef __OLD_VIDIOC_ + case VIDIOC_OVERLAY32_OLD: + case VIDIOC_S_PARM_OLD: + case VIDIOC_S_CTRL_OLD: + case VIDIOC_G_AUDIO_OLD: + case VIDIOC_G_AUDOUT_OLD: + case VIDIOC_CROPCAP_OLD: +#endif case VIDIOC_QUERYCAP: + case VIDIOC_RESERVED: case VIDIOC_ENUM_FMT: case VIDIOC_G_FMT32: - case VIDIOC_CROPCAP: - case VIDIOC_S_CROP: case VIDIOC_S_FMT32: case VIDIOC_REQBUFS: case VIDIOC_QUERYBUF32: @@ -870,43 +1002,56 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_S_PARM: case VIDIOC_G_STD: case VIDIOC_S_STD: - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - case VIDIOC_ENUMSTD: case VIDIOC_ENUMSTD32: - case VIDIOC_ENUMINPUT: case VIDIOC_ENUMINPUT32: case VIDIOC_G_CTRL: case VIDIOC_S_CTRL: - case VIDIOC_S_CTRL32: - case VIDIOC_S_FREQUENCY: - case VIDIOC_G_FREQUENCY: + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + case VIDIOC_G_AUDIO: + case VIDIOC_S_AUDIO: case VIDIOC_QUERYCTRL: + case VIDIOC_QUERYMENU: case VIDIOC_G_INPUT32: case VIDIOC_S_INPUT32: + case VIDIOC_G_OUTPUT32: + case VIDIOC_S_OUTPUT32: + case VIDIOC_ENUMOUTPUT: + case VIDIOC_G_AUDOUT: + case VIDIOC_S_AUDOUT: + case VIDIOC_G_MODULATOR: + case VIDIOC_S_MODULATOR: + case VIDIOC_S_FREQUENCY: + case VIDIOC_G_FREQUENCY: + case VIDIOC_CROPCAP: + case VIDIOC_G_CROP: + case VIDIOC_S_CROP: + case VIDIOC_G_JPEGCOMP: + case VIDIOC_S_JPEGCOMP: + case VIDIOC_QUERYSTD: case VIDIOC_TRY_FMT32: - case VIDIOC_S_HW_FREQ_SEEK: + case VIDIOC_ENUMAUDIO: + case VIDIOC_ENUMAUDOUT: + case VIDIOC_G_PRIORITY: + case VIDIOC_S_PRIORITY: + case VIDIOC_G_SLICED_VBI_CAP: + case VIDIOC_LOG_STATUS: + case VIDIOC_G_EXT_CTRLS32: + case VIDIOC_S_EXT_CTRLS32: + case VIDIOC_TRY_EXT_CTRLS32: case VIDIOC_ENUM_FRAMESIZES: case VIDIOC_ENUM_FRAMEINTERVALS: + case VIDIOC_G_ENC_INDEX: + case VIDIOC_ENCODER_CMD: + case VIDIOC_TRY_ENCODER_CMD: + case VIDIOC_DBG_S_REGISTER: + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_G_CHIP_IDENT: + case VIDIOC_S_HW_FREQ_SEEK: ret = do_video_ioctl(file, cmd, arg); break; #ifdef CONFIG_VIDEO_V4L1_COMPAT - /* Little v, the video4linux ioctls (conflict?) */ - case VIDIOCGCAP: - case VIDIOCGCHAN: - case VIDIOCSCHAN: - case VIDIOCGPICT: - case VIDIOCSPICT: - case VIDIOCCAPTURE: - case VIDIOCKEY: - case VIDIOCSYNC: - case VIDIOCMCAPTURE: - case VIDIOCGMBUF: - case VIDIOCGUNIT: - case VIDIOCGCAPTURE: - case VIDIOCSCAPTURE: - /* BTTV specific... */ case _IOW('v', BASE_VIDIOCPRIVATE+0, char [256]): case _IOR('v', BASE_VIDIOCPRIVATE+1, char [256]): @@ -921,6 +1066,8 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) #endif default: v4l_print_ioctl("compat_ioctl32", cmd); + printk(KERN_CONT "\n"); + break; } return ret; } diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index ccd6566..7ad6711 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -9,7 +9,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Authors: Alan Cox, <alan@redhat.com> (version 1) + * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1) * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2) * * Fixes: 20000516 Claudio Matsuoka <claudio@conectiva.com> @@ -30,6 +30,7 @@ #include <asm/system.h> #include <media/v4l2-common.h> +#include <media/v4l2-device.h> #define VIDEO_NUM_DEVICES 256 #define VIDEO_NAME "video4linux" @@ -41,17 +42,17 @@ static ssize_t show_index(struct device *cd, struct device_attribute *attr, char *buf) { - struct video_device *vfd = container_of(cd, struct video_device, dev); + struct video_device *vdev = to_video_device(cd); - return sprintf(buf, "%i\n", vfd->index); + return sprintf(buf, "%i\n", vdev->index); } static ssize_t show_name(struct device *cd, struct device_attribute *attr, char *buf) { - struct video_device *vfd = container_of(cd, struct video_device, dev); + struct video_device *vdev = to_video_device(cd); - return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name); + return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name); } static struct device_attribute video_device_attrs[] = { @@ -73,64 +74,64 @@ struct video_device *video_device_alloc(void) } EXPORT_SYMBOL(video_device_alloc); -void video_device_release(struct video_device *vfd) +void video_device_release(struct video_device *vdev) { - kfree(vfd); + kfree(vdev); } EXPORT_SYMBOL(video_device_release); -void video_device_release_empty(struct video_device *vfd) +void video_device_release_empty(struct video_device *vdev) { /* Do nothing */ /* Only valid when the video_device struct is a static. */ } EXPORT_SYMBOL(video_device_release_empty); -/* Called when the last user of the character device is gone. */ -static void v4l2_chardev_release(struct kobject *kobj) +static inline void video_get(struct video_device *vdev) { - struct video_device *vfd = container_of(kobj, struct video_device, cdev.kobj); + get_device(&vdev->dev); +} + +static inline void video_put(struct video_device *vdev) +{ + put_device(&vdev->dev); +} + +/* Called when the last user of the video device exits. */ +static void v4l2_device_release(struct device *cd) +{ + struct video_device *vdev = to_video_device(cd); mutex_lock(&videodev_lock); - if (video_device[vfd->minor] != vfd) { + if (video_device[vdev->minor] != vdev) { mutex_unlock(&videodev_lock); - BUG(); + /* should not happen */ + WARN_ON(1); return; } /* Free up this device for reuse */ - video_device[vfd->minor] = NULL; - clear_bit(vfd->num, video_nums[vfd->vfl_type]); - mutex_unlock(&videodev_lock); + video_device[vdev->minor] = NULL; - /* Release the character device */ - vfd->cdev_release(kobj); - /* Release video_device and perform other - cleanups as needed. */ - if (vfd->release) - vfd->release(vfd); -} + /* Delete the cdev on this minor as well */ + cdev_del(vdev->cdev); + /* Just in case some driver tries to access this from + the release() callback. */ + vdev->cdev = NULL; -/* The new kobj_type for the character device */ -static struct kobj_type v4l2_ktype_cdev_default = { - .release = v4l2_chardev_release, -}; + /* Mark minor as free */ + clear_bit(vdev->num, video_nums[vdev->vfl_type]); -static void video_release(struct device *cd) -{ - struct video_device *vfd = container_of(cd, struct video_device, dev); + mutex_unlock(&videodev_lock); - /* It's now safe to delete the char device. - This will either trigger the v4l2_chardev_release immediately (if - the refcount goes to 0) or later when the last user of the - character device closes it. */ - cdev_del(&vfd->cdev); + /* Release video_device and perform other + cleanups as needed. */ + vdev->release(vdev); } static struct class video_class = { .name = VIDEO_NAME, .dev_attrs = video_device_attrs, - .dev_release = video_release, }; struct video_device *video_devdata(struct file *file) @@ -139,13 +140,163 @@ struct video_device *video_devdata(struct file *file) } EXPORT_SYMBOL(video_devdata); +static ssize_t v4l2_read(struct file *filp, char __user *buf, + size_t sz, loff_t *off) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->read) + return -EINVAL; + if (video_is_unregistered(vdev)) + return -EIO; + return vdev->fops->read(filp, buf, sz, off); +} + +static ssize_t v4l2_write(struct file *filp, const char __user *buf, + size_t sz, loff_t *off) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->write) + return -EINVAL; + if (video_is_unregistered(vdev)) + return -EIO; + return vdev->fops->write(filp, buf, sz, off); +} + +static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->poll || video_is_unregistered(vdev)) + return DEFAULT_POLLMASK; + return vdev->fops->poll(filp, poll); +} + +static int v4l2_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->ioctl) + return -ENOTTY; + /* Allow ioctl to continue even if the device was unregistered. + Things like dequeueing buffers might still be useful. */ + return vdev->fops->ioctl(inode, filp, cmd, arg); +} + +static long v4l2_unlocked_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->unlocked_ioctl) + return -ENOTTY; + /* Allow ioctl to continue even if the device was unregistered. + Things like dequeueing buffers might still be useful. */ + return vdev->fops->unlocked_ioctl(filp, cmd, arg); +} + +#ifdef CONFIG_COMPAT +static long v4l2_compat_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->compat_ioctl) + return -ENOIOCTLCMD; + /* Allow ioctl to continue even if the device was unregistered. + Things like dequeueing buffers might still be useful. */ + return vdev->fops->compat_ioctl(filp, cmd, arg); +} +#endif + +static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->mmap || + video_is_unregistered(vdev)) + return -ENODEV; + return vdev->fops->mmap(filp, vm); +} + +/* Override for the open function */ +static int v4l2_open(struct inode *inode, struct file *filp) +{ + struct video_device *vdev; + int ret; + + /* Check if the video device is available */ + mutex_lock(&videodev_lock); + vdev = video_devdata(filp); + /* return ENODEV if the video device has been removed + already or if it is not registered anymore. */ + if (vdev == NULL || video_is_unregistered(vdev)) { + mutex_unlock(&videodev_lock); + return -ENODEV; + } + /* and increase the device refcount */ + video_get(vdev); + mutex_unlock(&videodev_lock); + ret = vdev->fops->open(inode, filp); + /* decrease the refcount in case of an error */ + if (ret) + video_put(vdev); + return ret; +} + +/* Override for the release function */ +static int v4l2_release(struct inode *inode, struct file *filp) +{ + struct video_device *vdev = video_devdata(filp); + int ret = vdev->fops->release(inode, filp); + + /* decrease the refcount unconditionally since the release() + return value is ignored. */ + video_put(vdev); + return ret; +} + +static const struct file_operations v4l2_unlocked_fops = { + .owner = THIS_MODULE, + .read = v4l2_read, + .write = v4l2_write, + .open = v4l2_open, + .mmap = v4l2_mmap, + .unlocked_ioctl = v4l2_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = v4l2_compat_ioctl, +#endif + .release = v4l2_release, + .poll = v4l2_poll, + .llseek = no_llseek, +}; + +static const struct file_operations v4l2_fops = { + .owner = THIS_MODULE, + .read = v4l2_read, + .write = v4l2_write, + .open = v4l2_open, + .mmap = v4l2_mmap, + .ioctl = v4l2_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = v4l2_compat_ioctl, +#endif + .release = v4l2_release, + .poll = v4l2_poll, + .llseek = no_llseek, +}; + /** * get_index - assign stream number based on parent device - * @vdev: video_device to assign index number to, vdev->dev should be assigned - * @num: -1 if auto assign, requested number otherwise + * @vdev: video_device to assign index number to, vdev->parent should be assigned + * @num: -1 if auto assign, requested number otherwise * + * Note that when this is called the new device has not yet been registered + * in the video_device array. * - * returns -ENFILE if num is already in use, a free index number if + * Returns -ENFILE if num is already in use, a free index number if * successful. */ static int get_index(struct video_device *vdev, int num) @@ -162,9 +313,12 @@ static int get_index(struct video_device *vdev, int num) return -EINVAL; } + /* Some drivers do not set the parent. In that case always return 0. */ + if (vdev->parent == NULL) + return 0; + for (i = 0; i < VIDEO_NUM_DEVICES; i++) { if (video_device[i] != NULL && - video_device[i] != vdev && video_device[i]->parent == vdev->parent) { used |= 1 << video_device[i]->index; } @@ -180,17 +334,15 @@ static int get_index(struct video_device *vdev, int num) return i > max_index ? -ENFILE : i; } -static const struct file_operations video_fops; - -int video_register_device(struct video_device *vfd, int type, int nr) +int video_register_device(struct video_device *vdev, int type, int nr) { - return video_register_device_index(vfd, type, nr, -1); + return video_register_device_index(vdev, type, nr, -1); } EXPORT_SYMBOL(video_register_device); /** * video_register_device_index - register video4linux devices - * @vfd: video device structure we want to register + * @vdev: video device structure we want to register * @type: type of device to register * @nr: which device number (0 == /dev/video0, 1 == /dev/video1, ... * -1 == first free) @@ -214,8 +366,7 @@ EXPORT_SYMBOL(video_register_device); * * %VFL_TYPE_RADIO - A radio card */ - -int video_register_device_index(struct video_device *vfd, int type, int nr, +int video_register_device_index(struct video_device *vdev, int type, int nr, int index) { int i = 0; @@ -223,14 +374,19 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, int minor_offset = 0; int minor_cnt = VIDEO_NUM_DEVICES; const char *name_base; - void *priv = video_get_drvdata(vfd); + void *priv = video_get_drvdata(vdev); - /* the release callback MUST be present */ - BUG_ON(!vfd->release); + /* A minor value of -1 marks this video device as never + having been registered */ + if (vdev) + vdev->minor = -1; - if (vfd == NULL) + /* the release callback MUST be present */ + WARN_ON(!vdev || !vdev->release); + if (!vdev || !vdev->release) return -EINVAL; + /* Part 1: check device type */ switch (type) { case VFL_TYPE_GRABBER: name_base = "video"; @@ -250,8 +406,12 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, return -EINVAL; } - vfd->vfl_type = type; + vdev->vfl_type = type; + vdev->cdev = NULL; + if (vdev->v4l2_dev) + vdev->parent = vdev->v4l2_dev->dev; + /* Part 2: find a free minor, kernel number and device index. */ #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES /* Keep the ranges for the first four types for historical * reasons. @@ -282,10 +442,7 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, } #endif - /* Initialize the character device */ - cdev_init(&vfd->cdev, vfd->fops); - vfd->cdev.owner = vfd->fops->owner; - /* pick a minor number */ + /* Pick a minor number */ mutex_lock(&videodev_lock); nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr); if (nr == minor_cnt) @@ -309,72 +466,92 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, return -ENFILE; } #endif - vfd->minor = i + minor_offset; - vfd->num = nr; + vdev->minor = i + minor_offset; + vdev->num = nr; set_bit(nr, video_nums[type]); - BUG_ON(video_device[vfd->minor]); - video_device[vfd->minor] = vfd; - - ret = get_index(vfd, index); - vfd->index = ret; - + /* Should not happen since we thought this minor was free */ + WARN_ON(video_device[vdev->minor] != NULL); + ret = vdev->index = get_index(vdev, index); mutex_unlock(&videodev_lock); if (ret < 0) { printk(KERN_ERR "%s: get_index failed\n", __func__); - goto fail_minor; + goto cleanup; } - ret = cdev_add(&vfd->cdev, MKDEV(VIDEO_MAJOR, vfd->minor), 1); + /* Part 3: Initialize the character device */ + vdev->cdev = cdev_alloc(); + if (vdev->cdev == NULL) { + ret = -ENOMEM; + goto cleanup; + } + if (vdev->fops->unlocked_ioctl) + vdev->cdev->ops = &v4l2_unlocked_fops; + else + vdev->cdev->ops = &v4l2_fops; + vdev->cdev->owner = vdev->fops->owner; + ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); if (ret < 0) { printk(KERN_ERR "%s: cdev_add failed\n", __func__); - goto fail_minor; + kfree(vdev->cdev); + vdev->cdev = NULL; + goto cleanup; } - /* sysfs class */ - memset(&vfd->dev, 0, sizeof(vfd->dev)); + + /* Part 4: register the device with sysfs */ + memset(&vdev->dev, 0, sizeof(vdev->dev)); /* The memset above cleared the device's drvdata, so put back the copy we made earlier. */ - video_set_drvdata(vfd, priv); - vfd->dev.class = &video_class; - vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor); - if (vfd->parent) - vfd->dev.parent = vfd->parent; - sprintf(vfd->dev.bus_id, "%s%d", name_base, nr); - ret = device_register(&vfd->dev); + video_set_drvdata(vdev, priv); + vdev->dev.class = &video_class; + vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); + if (vdev->parent) + vdev->dev.parent = vdev->parent; + dev_set_name(&vdev->dev, "%s%d", name_base, nr); + ret = device_register(&vdev->dev); if (ret < 0) { printk(KERN_ERR "%s: device_register failed\n", __func__); - goto del_cdev; + goto cleanup; } - /* Remember the cdev's release function */ - vfd->cdev_release = vfd->cdev.kobj.ktype->release; - /* Install our own */ - vfd->cdev.kobj.ktype = &v4l2_ktype_cdev_default; - return 0; + /* Register the release callback that will be called when the last + reference to the device goes away. */ + vdev->dev.release = v4l2_device_release; -del_cdev: - cdev_del(&vfd->cdev); + /* Part 5: Activate this minor. The char device can now be used. */ + mutex_lock(&videodev_lock); + video_device[vdev->minor] = vdev; + mutex_unlock(&videodev_lock); + return 0; -fail_minor: +cleanup: mutex_lock(&videodev_lock); - video_device[vfd->minor] = NULL; - clear_bit(vfd->num, video_nums[type]); + if (vdev->cdev) + cdev_del(vdev->cdev); + clear_bit(vdev->num, video_nums[type]); mutex_unlock(&videodev_lock); - vfd->minor = -1; + /* Mark this video device as never having been registered. */ + vdev->minor = -1; return ret; } EXPORT_SYMBOL(video_register_device_index); /** * video_unregister_device - unregister a video4linux device - * @vfd: the device to unregister + * @vdev: the device to unregister * - * This unregisters the passed device and deassigns the minor - * number. Future open calls will be met with errors. + * This unregisters the passed device. Future open calls will + * be met with errors. */ - -void video_unregister_device(struct video_device *vfd) +void video_unregister_device(struct video_device *vdev) { - device_unregister(&vfd->dev); + /* Check if vdev was ever registered at all */ + if (!vdev || vdev->minor < 0) + return; + + mutex_lock(&videodev_lock); + set_bit(V4L2_FL_UNREGISTERED, &vdev->flags); + mutex_unlock(&videodev_lock); + device_unregister(&vdev->dev); } EXPORT_SYMBOL(video_unregister_device); diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c new file mode 100644 index 0000000..9eefde0 --- /dev/null +++ b/drivers/media/video/v4l2-device.c @@ -0,0 +1,86 @@ +/* + V4L2 device support. + + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/types.h> +#include <linux/ioctl.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> + +int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) +{ + if (dev == NULL || v4l2_dev == NULL) + return -EINVAL; + /* Warn if we apparently re-register a device */ + WARN_ON(dev_get_drvdata(dev)); + INIT_LIST_HEAD(&v4l2_dev->subdevs); + spin_lock_init(&v4l2_dev->lock); + v4l2_dev->dev = dev; + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", + dev->driver->name, dev->bus_id); + dev_set_drvdata(dev, v4l2_dev); + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_device_register); + +void v4l2_device_unregister(struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd, *next; + + if (v4l2_dev == NULL || v4l2_dev->dev == NULL) + return; + dev_set_drvdata(v4l2_dev->dev, NULL); + /* unregister subdevs */ + list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) + v4l2_device_unregister_subdev(sd); + + v4l2_dev->dev = NULL; +} +EXPORT_SYMBOL_GPL(v4l2_device_unregister); + +int v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd) +{ + /* Check for valid input */ + if (dev == NULL || sd == NULL || !sd->name[0]) + return -EINVAL; + /* Warn if we apparently re-register a subdev */ + WARN_ON(sd->dev); + if (!try_module_get(sd->owner)) + return -ENODEV; + sd->dev = dev; + spin_lock(&dev->lock); + list_add_tail(&sd->list, &dev->subdevs); + spin_unlock(&dev->lock); + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); + +void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) +{ + /* return if it isn't registered */ + if (sd == NULL || sd->dev == NULL) + return; + spin_lock(&sd->dev->lock); + list_del(&sd->list); + spin_unlock(&sd->dev->lock); + sd->dev = NULL; + module_put(sd->owner); +} +EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 710e1a4..b063381 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -8,7 +8,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Authors: Alan Cox, <alan@redhat.com> (version 1) + * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1) * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2) */ @@ -393,10 +393,8 @@ video_fix_command(unsigned int cmd) * Obsolete usercopy function - Should be removed soon */ int -video_usercopy(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg, - int (*func)(struct inode *inode, struct file *file, - unsigned int cmd, void *arg)) +video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, + v4l2_kioctl func) { char sbuf[128]; void *mbuf = NULL; @@ -458,7 +456,7 @@ video_usercopy(struct inode *inode, struct file *file, } /* call driver */ - err = func(inode, file, cmd, parg); + err = func(file, cmd, parg); if (err == -ENOIOCTLCMD) err = -EINVAL; if (is_ext_ctrl) { @@ -1481,9 +1479,15 @@ static int __video_do_ioctl(struct file *file, case VIDIOC_G_CROP: { struct v4l2_crop *p = arg; + __u32 type; if (!ops->vidioc_g_crop) break; + + type = p->type; + memset(p, 0, sizeof(*p)); + p->type = type; + dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); ret = ops->vidioc_g_crop(file, fh, p); if (!ret) @@ -1504,10 +1508,16 @@ static int __video_do_ioctl(struct file *file, case VIDIOC_CROPCAP: { struct v4l2_cropcap *p = arg; + __u32 type; /*FIXME: Should also show v4l2_fract pixelaspect */ if (!ops->vidioc_cropcap) break; + + type = p->type; + memset(p, 0, sizeof(*p)); + p->type = type; + dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); ret = ops->vidioc_cropcap(file, fh, p); if (!ret) { @@ -1522,6 +1532,9 @@ static int __video_do_ioctl(struct file *file, if (!ops->vidioc_g_jpegcomp) break; + + memset(p, 0, sizeof(*p)); + ret = ops->vidioc_g_jpegcomp(file, fh, p); if (!ret) dbgarg(cmd, "quality=%d, APPn=%d, " @@ -1749,6 +1762,77 @@ static int __video_do_ioctl(struct file *file, ret = ops->vidioc_s_hw_freq_seek(file, fh, p); break; } + case VIDIOC_ENUM_FRAMESIZES: + { + struct v4l2_frmsizeenum *p = arg; + + if (!ops->vidioc_enum_framesizes) + break; + + memset(p, 0, sizeof(*p)); + + ret = ops->vidioc_enum_framesizes(file, fh, p); + dbgarg(cmd, + "index=%d, pixelformat=%d, type=%d ", + p->index, p->pixel_format, p->type); + switch (p->type) { + case V4L2_FRMSIZE_TYPE_DISCRETE: + dbgarg2("width = %d, height=%d\n", + p->discrete.width, p->discrete.height); + break; + case V4L2_FRMSIZE_TYPE_STEPWISE: + dbgarg2("min %dx%d, max %dx%d, step %dx%d\n", + p->stepwise.min_width, p->stepwise.min_height, + p->stepwise.step_width, p->stepwise.step_height, + p->stepwise.max_width, p->stepwise.max_height); + break; + case V4L2_FRMSIZE_TYPE_CONTINUOUS: + dbgarg2("continuous\n"); + break; + default: + dbgarg2("- Unknown type!\n"); + } + + break; + } + case VIDIOC_ENUM_FRAMEINTERVALS: + { + struct v4l2_frmivalenum *p = arg; + + if (!ops->vidioc_enum_frameintervals) + break; + + memset(p, 0, sizeof(*p)); + + ret = ops->vidioc_enum_frameintervals(file, fh, p); + dbgarg(cmd, + "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ", + p->index, p->pixel_format, + p->width, p->height, p->type); + switch (p->type) { + case V4L2_FRMIVAL_TYPE_DISCRETE: + dbgarg2("fps=%d/%d\n", + p->discrete.numerator, + p->discrete.denominator); + break; + case V4L2_FRMIVAL_TYPE_STEPWISE: + dbgarg2("min=%d/%d, max=%d/%d, step=%d/%d\n", + p->stepwise.min.numerator, + p->stepwise.min.denominator, + p->stepwise.max.numerator, + p->stepwise.max.denominator, + p->stepwise.step.numerator, + p->stepwise.step.denominator); + break; + case V4L2_FRMIVAL_TYPE_CONTINUOUS: + dbgarg2("continuous\n"); + break; + default: + dbgarg2("- Unknown type!\n"); + } + break; + } + default: { if (!ops->vidioc_default) @@ -1768,7 +1852,7 @@ static int __video_do_ioctl(struct file *file, return ret; } -int __video_ioctl2(struct file *file, +long __video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg) { char sbuf[128]; diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c new file mode 100644 index 0000000..e3612f2 --- /dev/null +++ b/drivers/media/video/v4l2-subdev.c @@ -0,0 +1,110 @@ +/* + V4L2 sub-device support. + + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/types.h> +#include <linux/ioctl.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/v4l2-subdev.h> + +int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg) +{ + switch (cmd) { + case VIDIOC_QUERYCTRL: + return v4l2_subdev_call(sd, core, querymenu, arg); + case VIDIOC_G_CTRL: + return v4l2_subdev_call(sd, core, g_ctrl, arg); + case VIDIOC_S_CTRL: + return v4l2_subdev_call(sd, core, s_ctrl, arg); + case VIDIOC_QUERYMENU: + return v4l2_subdev_call(sd, core, queryctrl, arg); + case VIDIOC_LOG_STATUS: + return v4l2_subdev_call(sd, core, log_status); + case VIDIOC_G_CHIP_IDENT: + return v4l2_subdev_call(sd, core, g_chip_ident, arg); + case VIDIOC_INT_S_STANDBY: + return v4l2_subdev_call(sd, core, s_standby, arg ? (*(u32 *)arg) : 0); + case VIDIOC_INT_RESET: + return v4l2_subdev_call(sd, core, reset, arg ? (*(u32 *)arg) : 0); + case VIDIOC_INT_S_GPIO: + return v4l2_subdev_call(sd, core, s_gpio, arg ? (*(u32 *)arg) : 0); + case VIDIOC_INT_INIT: + return v4l2_subdev_call(sd, core, init, arg ? (*(u32 *)arg) : 0); +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER: + return v4l2_subdev_call(sd, core, g_register, arg); + case VIDIOC_DBG_S_REGISTER: + return v4l2_subdev_call(sd, core, s_register, arg); +#endif + + case VIDIOC_INT_S_TUNER_MODE: + return v4l2_subdev_call(sd, tuner, s_mode, *(enum v4l2_tuner_type *)arg); + case AUDC_SET_RADIO: + return v4l2_subdev_call(sd, tuner, s_radio); + case VIDIOC_S_TUNER: + return v4l2_subdev_call(sd, tuner, s_tuner, arg); + case VIDIOC_G_TUNER: + return v4l2_subdev_call(sd, tuner, g_tuner, arg); + case VIDIOC_S_STD: + return v4l2_subdev_call(sd, tuner, s_std, *(v4l2_std_id *)arg); + case VIDIOC_S_FREQUENCY: + return v4l2_subdev_call(sd, tuner, s_frequency, arg); + case VIDIOC_G_FREQUENCY: + return v4l2_subdev_call(sd, tuner, g_frequency, arg); + case TUNER_SET_TYPE_ADDR: + return v4l2_subdev_call(sd, tuner, s_type_addr, arg); + case TUNER_SET_CONFIG: + return v4l2_subdev_call(sd, tuner, s_config, arg); + + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + return v4l2_subdev_call(sd, audio, s_clock_freq, *(u32 *)arg); + case VIDIOC_INT_S_AUDIO_ROUTING: + return v4l2_subdev_call(sd, audio, s_routing, arg); + case VIDIOC_INT_I2S_CLOCK_FREQ: + return v4l2_subdev_call(sd, audio, s_i2s_clock_freq, *(u32 *)arg); + + case VIDIOC_INT_S_VIDEO_ROUTING: + return v4l2_subdev_call(sd, video, s_routing, arg); + case VIDIOC_INT_S_CRYSTAL_FREQ: + return v4l2_subdev_call(sd, video, s_crystal_freq, arg); + case VIDIOC_INT_DECODE_VBI_LINE: + return v4l2_subdev_call(sd, video, decode_vbi_line, arg); + case VIDIOC_INT_S_VBI_DATA: + return v4l2_subdev_call(sd, video, s_vbi_data, arg); + case VIDIOC_INT_G_VBI_DATA: + return v4l2_subdev_call(sd, video, g_vbi_data, arg); + case VIDIOC_G_SLICED_VBI_CAP: + return v4l2_subdev_call(sd, video, g_sliced_vbi_cap, arg); + case VIDIOC_S_FMT: + return v4l2_subdev_call(sd, video, s_fmt, arg); + case VIDIOC_G_FMT: + return v4l2_subdev_call(sd, video, g_fmt, arg); + case VIDIOC_INT_S_STD_OUTPUT: + return v4l2_subdev_call(sd, video, s_std_output, *(v4l2_std_id *)arg); + case VIDIOC_STREAMON: + return v4l2_subdev_call(sd, video, s_stream, 1); + case VIDIOC_STREAMOFF: + return v4l2_subdev_call(sd, video, s_stream, 0); + + default: + return v4l2_subdev_call(sd, core, ioctl, cmd, arg); + } +} +EXPORT_SYMBOL_GPL(v4l2_subdev_command); diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c index 1efc5f3..a72a361 100644 --- a/drivers/media/video/vino.c +++ b/drivers/media/video/vino.c @@ -4237,8 +4237,7 @@ error: return ret; } -static int vino_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vino_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct vino_channel_settings *vcs = video_drvdata(file); @@ -4353,7 +4352,7 @@ static int vino_ioctl(struct inode *inode, struct file *file, if (mutex_lock_interruptible(&vcs->mutex)) return -EINTR; - ret = video_usercopy(inode, file, cmd, arg, vino_do_ioctl); + ret = video_usercopy(file, cmd, arg, vino_do_ioctl); mutex_unlock(&vcs->mutex); diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c index 577956c..f72b859 100644 --- a/drivers/media/video/vp27smpx.c +++ b/drivers/media/video/vp27smpx.c @@ -28,7 +28,7 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> @@ -40,13 +40,20 @@ MODULE_LICENSE("GPL"); /* ----------------------------------------------------------------------- */ struct vp27smpx_state { + struct v4l2_subdev sd; int radio; u32 audmode; }; -static void vp27smpx_set_audmode(struct i2c_client *client, u32 audmode) +static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd) { - struct vp27smpx_state *state = i2c_get_clientdata(client); + return container_of(sd, struct vp27smpx_state, sd); +} + +static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode) +{ + struct vp27smpx_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 data[3] = { 0x00, 0x00, 0x04 }; switch (audmode) { @@ -63,55 +70,89 @@ static void vp27smpx_set_audmode(struct i2c_client *client, u32 audmode) } if (i2c_master_send(client, data, sizeof(data)) != sizeof(data)) - v4l_err(client, "%s: I/O error setting audmode\n", - client->name); + v4l2_err(sd, "I/O error setting audmode\n"); else state->audmode = audmode; } -static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg) +static int vp27smpx_s_radio(struct v4l2_subdev *sd) { - struct vp27smpx_state *state = i2c_get_clientdata(client); - struct v4l2_tuner *vt = arg; + struct vp27smpx_state *state = to_state(sd); - switch (cmd) { - case AUDC_SET_RADIO: - state->radio = 1; - break; + state->radio = 1; + return 0; +} - case VIDIOC_S_STD: - state->radio = 0; - break; +static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct vp27smpx_state *state = to_state(sd); - case VIDIOC_S_TUNER: - if (!state->radio) - vp27smpx_set_audmode(client, vt->audmode); - break; + state->radio = 0; + return 0; +} - case VIDIOC_G_TUNER: - if (state->radio) - break; - vt->audmode = state->audmode; - vt->capability = V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - break; +static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct vp27smpx_state *state = to_state(sd); - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, - V4L2_IDENT_VP27SMPX, 0); + if (!state->radio) + vp27smpx_set_audmode(sd, vt->audmode); + return 0; +} - case VIDIOC_LOG_STATUS: - v4l_info(client, "Audio Mode: %u%s\n", state->audmode, - state->radio ? " (Radio)" : ""); - break; +static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct vp27smpx_state *state = to_state(sd); + + if (state->radio) + return 0; + vt->audmode = state->audmode; + vt->capability = V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + return 0; +} - default: - return -EINVAL; - } +static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0); +} + +static int vp27smpx_log_status(struct v4l2_subdev *sd) +{ + struct vp27smpx_state *state = to_state(sd); + + v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode, + state->radio ? " (Radio)" : ""); return 0; } +static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { + .log_status = vp27smpx_log_status, + .g_chip_ident = vp27smpx_g_chip_ident, +}; + +static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { + .s_radio = vp27smpx_s_radio, + .s_std = vp27smpx_s_std, + .s_tuner = vp27smpx_s_tuner, + .g_tuner = vp27smpx_g_tuner, +}; + +static const struct v4l2_subdev_ops vp27smpx_ops = { + .core = &vp27smpx_core_ops, + .tuner = &vp27smpx_tuner_ops, +}; + /* ----------------------------------------------------------------------- */ /* i2c implementation */ @@ -125,6 +166,7 @@ static int vp27smpx_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct vp27smpx_state *state; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -136,17 +178,21 @@ static int vp27smpx_probe(struct i2c_client *client, state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops); state->audmode = V4L2_TUNER_MODE_STEREO; - i2c_set_clientdata(client, state); /* initialize vp27smpx */ - vp27smpx_set_audmode(client, state->audmode); + vp27smpx_set_audmode(sd, state->audmode); return 0; } static int vp27smpx_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c index b2dbe48..56c570c 100644 --- a/drivers/media/video/w9966.c +++ b/drivers/media/video/w9966.c @@ -727,8 +727,7 @@ static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data) * Video4linux interfacing */ -static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int w9966_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct w9966_dev *cam = video_drvdata(file); @@ -881,7 +880,7 @@ static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file, static int w9966_v4l_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, w9966_v4l_do_ioctl); + return video_usercopy(file, cmd, arg, w9966_v4l_do_ioctl); } // Capture data diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c index 54ac3fe..12a31e7 100644 --- a/drivers/media/video/wm8739.c +++ b/drivers/media/video/wm8739.c @@ -28,7 +28,7 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> @@ -52,6 +52,7 @@ enum { }; struct wm8739_state { + struct v4l2_subdev sd; u32 clock_freq; u8 muted; u16 volume; @@ -60,43 +61,49 @@ struct wm8739_state { u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ }; +static inline struct wm8739_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct wm8739_state, sd); +} + /* ------------------------------------------------------------------------ */ -static int wm8739_write(struct i2c_client *client, int reg, u16 val) +static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int i; if (reg < 0 || reg >= TOT_REGS) { - v4l_err(client, "Invalid register R%d\n", reg); + v4l2_err(sd, "Invalid register R%d\n", reg); return -1; } - v4l_dbg(1, debug, client, "write: %02x %02x\n", reg, val); + v4l2_dbg(1, debug, sd, "write: %02x %02x\n", reg, val); for (i = 0; i < 3; i++) if (i2c_smbus_write_byte_data(client, (reg << 1) | (val >> 8), val & 0xff) == 0) return 0; - v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg); + v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); return -1; } /* write regs to set audio volume etc */ -static void wm8739_set_audio(struct i2c_client *client) +static void wm8739_set_audio(struct v4l2_subdev *sd) { - struct wm8739_state *state = i2c_get_clientdata(client); + struct wm8739_state *state = to_state(sd); u16 mute = state->muted ? 0x80 : 0; /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB * Default setting: 0x17 = 0 dB */ - wm8739_write(client, R0, (state->vol_l & 0x1f) | mute); - wm8739_write(client, R1, (state->vol_r & 0x1f) | mute); + wm8739_write(sd, R0, (state->vol_l & 0x1f) | mute); + wm8739_write(sd, R1, (state->vol_r & 0x1f) | mute); } -static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int wm8739_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct wm8739_state *state = i2c_get_clientdata(client); + struct wm8739_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -117,9 +124,9 @@ static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) return 0; } -static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct wm8739_state *state = i2c_get_clientdata(client); + struct wm8739_state *state = to_state(sd); unsigned int work_l, work_r; switch (ctrl->id) { @@ -147,7 +154,7 @@ static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) state->vol_r = (long)work_r * 31 / 65535; /* set audio volume etc. */ - wm8739_set_audio(client); + wm8739_set_audio(sd); return 0; } @@ -186,77 +193,89 @@ static struct v4l2_queryctrl wm8739_qctrl[] = { /* ------------------------------------------------------------------------ */ -static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg) +static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq) { - struct wm8739_state *state = i2c_get_clientdata(client); - - switch (cmd) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - { - u32 audiofreq = *(u32 *)arg; - - state->clock_freq = audiofreq; - /* de-activate */ - wm8739_write(client, R9, 0x000); - switch (audiofreq) { - case 44100: - /* 256fps, fs=44.1k */ - wm8739_write(client, R8, 0x020); - break; - case 48000: - /* 256fps, fs=48k */ - wm8739_write(client, R8, 0x000); - break; - case 32000: - /* 256fps, fs=32k */ - wm8739_write(client, R8, 0x018); - break; - default: - break; - } - /* activate */ - wm8739_write(client, R9, 0x001); + struct wm8739_state *state = to_state(sd); + + state->clock_freq = audiofreq; + /* de-activate */ + wm8739_write(sd, R9, 0x000); + switch (audiofreq) { + case 44100: + /* 256fps, fs=44.1k */ + wm8739_write(sd, R8, 0x020); + break; + case 48000: + /* 256fps, fs=48k */ + wm8739_write(sd, R8, 0x000); + break; + case 32000: + /* 256fps, fs=32k */ + wm8739_write(sd, R8, 0x018); + break; + default: break; } + /* activate */ + wm8739_write(sd, R9, 0x001); + return 0; +} - case VIDIOC_G_CTRL: - return wm8739_get_ctrl(client, arg); - - case VIDIOC_S_CTRL: - return wm8739_set_ctrl(client, arg); +static int wm8739_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + int i; - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++) - if (qc->id && qc->id == wm8739_qctrl[i].id) { - memcpy(qc, &wm8739_qctrl[i], sizeof(*qc)); - return 0; - } - return -EINVAL; - } + for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++) + if (qc->id && qc->id == wm8739_qctrl[i].id) { + memcpy(qc, &wm8739_qctrl[i], sizeof(*qc)); + return 0; + } + return -EINVAL; +} - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, - arg, V4L2_IDENT_WM8739, 0); +static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_LOG_STATUS: - v4l_info(client, "Frequency: %u Hz\n", state->clock_freq); - v4l_info(client, "Volume L: %02x%s\n", state->vol_l & 0x1f, - state->muted ? " (muted)" : ""); - v4l_info(client, "Volume R: %02x%s\n", state->vol_r & 0x1f, - state->muted ? " (muted)" : ""); - break; + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8739, 0); +} - default: - return -EINVAL; - } +static int wm8739_log_status(struct v4l2_subdev *sd) +{ + struct wm8739_state *state = to_state(sd); + v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq); + v4l2_info(sd, "Volume L: %02x%s\n", state->vol_l & 0x1f, + state->muted ? " (muted)" : ""); + v4l2_info(sd, "Volume R: %02x%s\n", state->vol_r & 0x1f, + state->muted ? " (muted)" : ""); return 0; } +static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops wm8739_core_ops = { + .log_status = wm8739_log_status, + .g_chip_ident = wm8739_g_chip_ident, + .queryctrl = wm8739_queryctrl, + .g_ctrl = wm8739_g_ctrl, + .s_ctrl = wm8739_s_ctrl, +}; + +static const struct v4l2_subdev_audio_ops wm8739_audio_ops = { + .s_clock_freq = wm8739_s_clock_freq, +}; + +static const struct v4l2_subdev_ops wm8739_ops = { + .core = &wm8739_core_ops, + .audio = &wm8739_audio_ops, +}; + /* ------------------------------------------------------------------------ */ /* i2c implementation */ @@ -265,6 +284,7 @@ static int wm8739_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct wm8739_state *state; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -276,6 +296,8 @@ static int wm8739_probe(struct i2c_client *client, state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &wm8739_ops); state->vol_l = 0x17; /* 0dB */ state->vol_r = 0x17; /* 0dB */ state->muted = 0; @@ -283,31 +305,33 @@ static int wm8739_probe(struct i2c_client *client, /* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */ state->volume = ((long)state->vol_l + 1) * 65535 / 31; state->clock_freq = 48000; - i2c_set_clientdata(client, state); /* Initialize wm8739 */ /* reset */ - wm8739_write(client, R15, 0x00); + wm8739_write(sd, R15, 0x00); /* filter setting, high path, offet clear */ - wm8739_write(client, R5, 0x000); + wm8739_write(sd, R5, 0x000); /* ADC, OSC, Power Off mode Disable */ - wm8739_write(client, R6, 0x000); + wm8739_write(sd, R6, 0x000); /* Digital Audio interface format: Enable Master mode, 24 bit, MSB first/left justified */ - wm8739_write(client, R7, 0x049); + wm8739_write(sd, R7, 0x049); /* sampling control: normal, 256fs, 48KHz sampling rate */ - wm8739_write(client, R8, 0x000); + wm8739_write(sd, R8, 0x000); /* activate */ - wm8739_write(client, R9, 0x001); + wm8739_write(sd, R9, 0x001); /* set volume/mute */ - wm8739_set_audio(client); + wm8739_set_audio(sd); return 0; } static int wm8739_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index 48df661..d0220b0 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -32,7 +32,7 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv-legacy.h> @@ -54,16 +54,23 @@ enum { }; struct wm8775_state { + struct v4l2_subdev sd; u8 input; /* Last selected input (0-0xf) */ u8 muted; }; -static int wm8775_write(struct i2c_client *client, int reg, u16 val) +static inline struct wm8775_state *to_state(struct v4l2_subdev *sd) { + return container_of(sd, struct wm8775_state, sd); +} + +static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); int i; if (reg < 0 || reg >= TOT_REGS) { - v4l_err(client, "Invalid register R%d\n", reg); + v4l2_err(sd, "Invalid register R%d\n", reg); return -1; } @@ -71,84 +78,117 @@ static int wm8775_write(struct i2c_client *client, int reg, u16 val) if (i2c_smbus_write_byte_data(client, (reg << 1) | (val >> 8), val & 0xff) == 0) return 0; - v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg); + v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); return -1; } -static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg) +static int wm8775_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct wm8775_state *state = i2c_get_clientdata(client); - struct v4l2_routing *route = arg; - struct v4l2_control *ctrl = arg; - - switch (cmd) { - case VIDIOC_INT_G_AUDIO_ROUTING: - route->input = state->input; - route->output = 0; - break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - /* There are 4 inputs and one output. Zero or more inputs - are multiplexed together to the output. Hence there are - 16 combinations. - If only one input is active (the normal case) then the - input values 1, 2, 4 or 8 should be used. */ - if (route->input > 15) { - v4l_err(client, "Invalid input %d.\n", route->input); - return -EINVAL; - } - state->input = route->input; - if (state->muted) - break; - wm8775_write(client, R21, 0x0c0); - wm8775_write(client, R14, 0x1d4); - wm8775_write(client, R15, 0x1d4); - wm8775_write(client, R21, 0x100 + state->input); - break; - - case VIDIOC_G_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - ctrl->value = state->muted; - break; - - case VIDIOC_S_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - state->muted = ctrl->value; - wm8775_write(client, R21, 0x0c0); - wm8775_write(client, R14, 0x1d4); - wm8775_write(client, R15, 0x1d4); - if (!state->muted) - wm8775_write(client, R21, 0x100 + state->input); - break; - - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, - arg, V4L2_IDENT_WM8775, 0); - - case VIDIOC_LOG_STATUS: - v4l_info(client, "Input: %d%s\n", state->input, - state->muted ? " (muted)" : ""); - break; - - case VIDIOC_S_FREQUENCY: - /* If I remove this, then it can happen that I have no - sound the first time I tune from static to a valid channel. - It's difficult to reproduce and is almost certainly related - to the zero cross detect circuit. */ - wm8775_write(client, R21, 0x0c0); - wm8775_write(client, R14, 0x1d4); - wm8775_write(client, R15, 0x1d4); - wm8775_write(client, R21, 0x100 + state->input); - break; - - default: + struct wm8775_state *state = to_state(sd); + + /* There are 4 inputs and one output. Zero or more inputs + are multiplexed together to the output. Hence there are + 16 combinations. + If only one input is active (the normal case) then the + input values 1, 2, 4 or 8 should be used. */ + if (route->input > 15) { + v4l2_err(sd, "Invalid input %d.\n", route->input); return -EINVAL; } + state->input = route->input; + if (state->muted) + return 0; + wm8775_write(sd, R21, 0x0c0); + wm8775_write(sd, R14, 0x1d4); + wm8775_write(sd, R15, 0x1d4); + wm8775_write(sd, R21, 0x100 + state->input); + return 0; +} + +static int wm8775_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct wm8775_state *state = to_state(sd); + + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + ctrl->value = state->muted; + return 0; +} + +static int wm8775_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct wm8775_state *state = to_state(sd); + + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + state->muted = ctrl->value; + wm8775_write(sd, R21, 0x0c0); + wm8775_write(sd, R14, 0x1d4); + wm8775_write(sd, R15, 0x1d4); + if (!state->muted) + wm8775_write(sd, R21, 0x100 + state->input); + return 0; +} + +static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0); +} + +static int wm8775_log_status(struct v4l2_subdev *sd) +{ + struct wm8775_state *state = to_state(sd); + + v4l2_info(sd, "Input: %d%s\n", state->input, + state->muted ? " (muted)" : ""); return 0; } +static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct wm8775_state *state = to_state(sd); + + /* If I remove this, then it can happen that I have no + sound the first time I tune from static to a valid channel. + It's difficult to reproduce and is almost certainly related + to the zero cross detect circuit. */ + wm8775_write(sd, R21, 0x0c0); + wm8775_write(sd, R14, 0x1d4); + wm8775_write(sd, R15, 0x1d4); + wm8775_write(sd, R21, 0x100 + state->input); + return 0; +} + +static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops wm8775_core_ops = { + .log_status = wm8775_log_status, + .g_chip_ident = wm8775_g_chip_ident, + .g_ctrl = wm8775_g_ctrl, + .s_ctrl = wm8775_s_ctrl, +}; + +static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = { + .s_frequency = wm8775_s_frequency, +}; + +static const struct v4l2_subdev_audio_ops wm8775_audio_ops = { + .s_routing = wm8775_s_routing, +}; + +static const struct v4l2_subdev_ops wm8775_ops = { + .core = &wm8775_core_ops, + .tuner = &wm8775_tuner_ops, + .audio = &wm8775_audio_ops, +}; + /* ----------------------------------------------------------------------- */ /* i2c implementation */ @@ -162,56 +202,61 @@ static int wm8775_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct wm8775_state *state; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - v4l_info(client, "chip found @ 0x%x (%s)\n", + v4l_info(client, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &wm8775_ops); state->input = 2; state->muted = 0; - i2c_set_clientdata(client, state); /* Initialize wm8775 */ /* RESET */ - wm8775_write(client, R23, 0x000); + wm8775_write(sd, R23, 0x000); /* Disable zero cross detect timeout */ - wm8775_write(client, R7, 0x000); + wm8775_write(sd, R7, 0x000); /* Left justified, 24-bit mode */ - wm8775_write(client, R11, 0x021); + wm8775_write(sd, R11, 0x021); /* Master mode, clock ratio 256fs */ - wm8775_write(client, R12, 0x102); + wm8775_write(sd, R12, 0x102); /* Powered up */ - wm8775_write(client, R13, 0x000); + wm8775_write(sd, R13, 0x000); /* ADC gain +2.5dB, enable zero cross */ - wm8775_write(client, R14, 0x1d4); + wm8775_write(sd, R14, 0x1d4); /* ADC gain +2.5dB, enable zero cross */ - wm8775_write(client, R15, 0x1d4); + wm8775_write(sd, R15, 0x1d4); /* ALC Stereo, ALC target level -1dB FS max gain +8dB */ - wm8775_write(client, R16, 0x1bf); + wm8775_write(sd, R16, 0x1bf); /* Enable gain control, use zero cross detection, ALC hold time 42.6 ms */ - wm8775_write(client, R17, 0x185); + wm8775_write(sd, R17, 0x185); /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */ - wm8775_write(client, R18, 0x0a2); + wm8775_write(sd, R18, 0x0a2); /* Enable noise gate, threshold -72dBfs */ - wm8775_write(client, R19, 0x005); + wm8775_write(sd, R19, 0x005); /* Transient window 4ms, lower PGA gain limit -1dB */ - wm8775_write(client, R20, 0x07a); + wm8775_write(sd, R20, 0x07a); /* LRBOTH = 1, use input 2. */ - wm8775_write(client, R21, 0x102); + wm8775_write(sd, R21, 0x102); return 0; } static int wm8775_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c index 9fc5817..9d00e60 100644 --- a/drivers/media/video/zc0301/zc0301_core.c +++ b/drivers/media/video/zc0301/zc0301_core.c @@ -1020,7 +1020,7 @@ zc0301_vidioc_querycap(struct zc0301_device* cam, void __user * arg) strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) - strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, + strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), sizeof(cap.bus_info)); if (copy_to_user(arg, &cap, sizeof(cap))) diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c index fa5f2f8..05f3919 100644 --- a/drivers/media/video/zoran/zoran_card.c +++ b/drivers/media/video/zoran/zoran_card.c @@ -153,12 +153,6 @@ MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver"); MODULE_AUTHOR("Serguei Miridonov"); MODULE_LICENSE("GPL"); -static struct pci_device_id zr36067_pci_tbl[] = { - {PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36057, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {0} -}; -MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl); int zoran_num; /* number of Buzs in use */ struct zoran *zoran[BUZ_MAX]; diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index db11ab9..00b97d9 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -1940,11 +1940,7 @@ zoran_set_input (struct zoran *zr, * ioctl routine */ -static int -zoran_do_ioctl (struct inode *inode, - struct file *file, - unsigned int cmd, - void *arg) +static int zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; @@ -4201,7 +4197,7 @@ zoran_ioctl (struct inode *inode, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, zoran_do_ioctl); + return video_usercopy(file, cmd, arg, zoran_do_ioctl); } static unsigned int |