diff options
author | Patrick Boettcher <pb@linuxtv.org> | 2008-12-10 22:28:25 +0100 |
---|---|---|
committer | Patrick Boettcher <pb@linuxtv.org> | 2008-12-10 22:28:25 +0100 |
commit | a9efe23fbe1000bff6e24012a5b580f9a8b93a92 (patch) | |
tree | 0308e2214bcc86e348e5d731d6bf958f1d4bf54f /linux/drivers/media | |
parent | 12583b9f4af4d04cce76742c9859bff4068e6345 (diff) | |
parent | 97b478e31ccddc358a73628542bb2d32983abeac (diff) | |
download | mediapointer-dvb-s2-a9efe23fbe1000bff6e24012a5b580f9a8b93a92.tar.gz mediapointer-dvb-s2-a9efe23fbe1000bff6e24012a5b580f9a8b93a92.tar.bz2 |
sync from main
From: Patrick Boettcher <pb@linuxtv.org>
sync from main
Priority: normal
Signed-off-by: Patrick Boettcher <pb@linuxtv.org>
Diffstat (limited to 'linux/drivers/media')
95 files changed, 2387 insertions, 1591 deletions
diff --git a/linux/drivers/media/common/ir-keymaps.c b/linux/drivers/media/common/ir-keymaps.c index 6c0a80f45..ee9931f56 100644 --- a/linux/drivers/media/common/ir-keymaps.c +++ b/linux/drivers/media/common/ir-keymaps.c @@ -2441,6 +2441,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, diff --git a/linux/drivers/media/common/saa7146_i2c.c b/linux/drivers/media/common/saa7146_i2c.c index ae20d2729..255b14551 100644 --- a/linux/drivers/media/common/saa7146_i2c.c +++ b/linux/drivers/media/common/saa7146_i2c.c @@ -406,6 +406,9 @@ static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, in static struct i2c_algorithm saa7146_algo = { .master_xfer = saa7146_i2c_xfer, .functionality = saa7146_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate) diff --git a/linux/drivers/media/common/tuners/tda827x.c b/linux/drivers/media/common/tuners/tda827x.c index 83a017c53..a14fe817d 100644 --- a/linux/drivers/media/common/tuners/tda827x.c +++ b/linux/drivers/media/common/tuners/tda827x.c @@ -81,10 +81,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); } @@ -200,7 +201,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; @@ -305,7 +306,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; } @@ -592,7 +593,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; @@ -692,7 +693,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/linux/drivers/media/common/tuners/tda8290.c b/linux/drivers/media/common/tuners/tda8290.c index 637849ec8..6eaaacca0 100644 --- a/linux/drivers/media/common/tuners/tda8290.c +++ b/linux/drivers/media/common/tuners/tda8290.c @@ -33,6 +33,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 { @@ -162,9 +165,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) { @@ -201,15 +229,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/linux/drivers/media/common/tuners/tuner-xc2028.c b/linux/drivers/media/common/tuners/tuner-xc2028.c index 7f43272ca..9536e3d3b 100644 --- a/linux/drivers/media/common/tuners/tuner-xc2028.c +++ b/linux/drivers/media/common/tuners/tuner-xc2028.c @@ -32,6 +32,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, @@ -1095,16 +1101,16 @@ static int xc2028_set_params(struct dvb_frontend *fe, T_DIGITAL_TV, type, 0, demod); } -#if 0 -/* This is needed at sleep (S1/S3), but not at fe_standby. Otherwise, - firmware will be loaded on every open() - */ static int xc2028_sleep(struct dvb_frontend *fe) { struct xc2028_data *priv = fe->tuner_priv; int rc = 0; - tuner_dbg("%s called\n", __func__); + /* Avoid firmware reload on slow devices */ + if (no_poweroff) + return 0; + + tuner_dbg("Putting xc2028/3028 into poweroff mode.\n"); mutex_lock(&priv->lock); @@ -1119,7 +1125,6 @@ static int xc2028_sleep(struct dvb_frontend *fe) return rc; } -#endif static int xc2028_dvb_release(struct dvb_frontend *fe) { @@ -1200,9 +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, -#if 0 .sleep = xc2028_sleep, -#endif #if 0 int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); int (*get_status)(struct dvb_frontend *fe, u32 *status); diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index a127a4175..5cded3708 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -628,12 +628,14 @@ int flexcop_frontend_init(struct flexcop_device *fc) } /* try the cable dvb (stv0297) */ + fc->fc_i2c_adap[0].no_base_addr = 1; fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c); if (fc->fe != NULL) { fc->dev_type = FC_CABLE; fc->fe->ops.tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params; goto fe_found; } + fc->fc_i2c_adap[0].no_base_addr = 0; /* try the sky v2.3 (vp310/Samsung tbdu18132(tsa5059)) */ fc->fe = dvb_attach(mt312_attach, diff --git a/linux/drivers/media/dvb/b2c2/flexcop-i2c.c b/linux/drivers/media/dvb/b2c2/flexcop-i2c.c index 43a112ec6..01d27613f 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-i2c.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-i2c.c @@ -47,9 +47,13 @@ static int flexcop_i2c_read4(struct flexcop_i2c_adapter *i2c, int len = r100.tw_sm_c_100.total_bytes, /* remember total_bytes is buflen-1 */ ret; - r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr; ret = flexcop_i2c_operation(i2c->fc, &r100); if (ret != 0) { + deb_i2c("Retrying operation\n"); + r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr; + ret = flexcop_i2c_operation(i2c->fc, &r100); + } + if (ret != 0) { deb_i2c("read failed. %d\n", ret); return ret; } @@ -205,6 +209,9 @@ static u32 flexcop_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm flexcop_algo = { .master_xfer = flexcop_master_xfer, .functionality = flexcop_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; int flexcop_i2c_init(struct flexcop_device *fc) diff --git a/linux/drivers/media/dvb/dm1105/dm1105.c b/linux/drivers/media/dvb/dm1105/dm1105.c index 100a7f661..d7034e6a1 100644 --- a/linux/drivers/media/dvb/dm1105/dm1105.c +++ b/linux/drivers/media/dvb/dm1105/dm1105.c @@ -330,6 +330,9 @@ static u32 functionality(struct i2c_adapter *adap) static struct i2c_algorithm dm1105_algo = { .master_xfer = dm1105_i2c_xfer, .functionality = functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static inline struct dm1105dvb *feed_to_dm1105dvb(struct dvb_demux_feed *feed) diff --git a/linux/drivers/media/dvb/dvb-usb/af9005.c b/linux/drivers/media/dvb/dvb-usb/af9005.c index ca5a0a4d2..fdf592625 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9005.c +++ b/linux/drivers/media/dvb/dvb-usb/af9005.c @@ -500,6 +500,9 @@ static u32 af9005_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm af9005_i2c_algo = { .master_xfer = af9005_i2c_xfer, .functionality = af9005_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; int af9005_send_command(struct dvb_usb_device *d, u8 command, u8 * wbuf, diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c index e6e534e04..f0b57c739 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.c +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -318,6 +318,9 @@ static u32 af9015_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm af9015_i2c_algo = { .master_xfer = af9015_i2c_xfer, .functionality = af9015_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op) diff --git a/linux/drivers/media/dvb/dvb-usb/anysee.c b/linux/drivers/media/dvb/dvb-usb/anysee.c index cd2edbcaa..90a04266a 100644 --- a/linux/drivers/media/dvb/dvb-usb/anysee.c +++ b/linux/drivers/media/dvb/dvb-usb/anysee.c @@ -199,6 +199,9 @@ static u32 anysee_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm anysee_i2c_algo = { .master_xfer = anysee_master_xfer, .functionality = anysee_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static int anysee_mt352_demod_init(struct dvb_frontend *fe) diff --git a/linux/drivers/media/dvb/dvb-usb/au6610.c b/linux/drivers/media/dvb/dvb-usb/au6610.c index eb34cc389..b2400d693 100644 --- a/linux/drivers/media/dvb/dvb-usb/au6610.c +++ b/linux/drivers/media/dvb/dvb-usb/au6610.c @@ -119,6 +119,9 @@ static u32 au6610_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm au6610_i2c_algo = { .master_xfer = au6610_i2c_xfer, .functionality = au6610_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* Callbacks for DVB USB */ diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index 406d7fba3..f467cad8a 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -213,6 +213,9 @@ static u32 cxusb_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm cxusb_i2c_algo = { .master_xfer = cxusb_i2c_xfer, .functionality = cxusb_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff) diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700_core.c b/linux/drivers/media/dvb/dvb-usb/dib0700_core.c index 2fbb7049a..807bff0e1 100644 --- a/linux/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/linux/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -252,6 +252,9 @@ static u32 dib0700_i2c_func(struct i2c_adapter *adapter) struct i2c_algorithm dib0700_i2c_algo = { .master_xfer = dib0700_i2c_xfer, .functionality = dib0700_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c index f28d3ae59..391732788 100644 --- a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -446,13 +446,13 @@ static int stk7700ph_tuner_attach(struct dvb_usb_adapter *adap) == NULL ? -ENODEV : 0; } -#define DEFAULT_RC_INTERVAL 150 +#define DEFAULT_RC_INTERVAL 50 static u8 rc_request[] = { REQUEST_POLL_RC, 0 }; /* Number of keypresses to ignore before start repeating */ -#define RC_REPEAT_DELAY 2 -#define RC_REPEAT_DELAY_V1_20 5 +#define RC_REPEAT_DELAY 6 +#define RC_REPEAT_DELAY_V1_20 10 diff --git a/linux/drivers/media/dvb/dvb-usb/dibusb-common.c b/linux/drivers/media/dvb/dvb-usb/dibusb-common.c index 8ee6cd4da..d774879df 100644 --- a/linux/drivers/media/dvb/dvb-usb/dibusb-common.c +++ b/linux/drivers/media/dvb/dvb-usb/dibusb-common.c @@ -155,6 +155,9 @@ static u32 dibusb_i2c_func(struct i2c_adapter *adapter) struct i2c_algorithm dibusb_i2c_algo = { .master_xfer = dibusb_i2c_xfer, .functionality = dibusb_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; EXPORT_SYMBOL(dibusb_i2c_algo); diff --git a/linux/drivers/media/dvb/dvb-usb/digitv.c b/linux/drivers/media/dvb/dvb-usb/digitv.c index b545cf3ea..3a560dcdd 100644 --- a/linux/drivers/media/dvb/dvb-usb/digitv.c +++ b/linux/drivers/media/dvb/dvb-usb/digitv.c @@ -83,6 +83,9 @@ static u32 digitv_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm digitv_i2c_algo = { .master_xfer = digitv_i2c_xfer, .functionality = digitv_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* Callbacks for DVB USB */ diff --git a/linux/drivers/media/dvb/dvb-usb/dtv5100.c b/linux/drivers/media/dvb/dvb-usb/dtv5100.c index 078ce92ca..e07a805f6 100644 --- a/linux/drivers/media/dvb/dvb-usb/dtv5100.c +++ b/linux/drivers/media/dvb/dvb-usb/dtv5100.c @@ -104,6 +104,9 @@ static u32 dtv5100_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm dtv5100_i2c_algo = { .master_xfer = dtv5100_i2c_xfer, .functionality = dtv5100_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* Callbacks for DVB USB */ diff --git a/linux/drivers/media/dvb/dvb-usb/dw2102.c b/linux/drivers/media/dvb/dvb-usb/dw2102.c index bc5e47a2b..2b8ee587b 100644 --- a/linux/drivers/media/dvb/dvb-usb/dw2102.c +++ b/linux/drivers/media/dvb/dvb-usb/dw2102.c @@ -368,21 +368,33 @@ static u32 dw210x_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm dw2102_i2c_algo = { .master_xfer = dw2102_i2c_transfer, .functionality = dw210x_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_algorithm dw2102_serit_i2c_algo = { .master_xfer = dw2102_serit_i2c_transfer, .functionality = dw210x_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_algorithm dw2102_earda_i2c_algo = { .master_xfer = dw2102_earda_i2c_transfer, .functionality = dw210x_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_algorithm dw2104_i2c_algo = { .master_xfer = dw2104_i2c_transfer, .functionality = dw210x_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) @@ -660,7 +672,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; diff --git a/linux/drivers/media/dvb/dvb-usb/gl861.c b/linux/drivers/media/dvb/dvb-usb/gl861.c index 6f596ed41..41bec1b58 100644 --- a/linux/drivers/media/dvb/dvb-usb/gl861.c +++ b/linux/drivers/media/dvb/dvb-usb/gl861.c @@ -91,6 +91,9 @@ static u32 gl861_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm gl861_i2c_algo = { .master_xfer = gl861_i2c_xfer, .functionality = gl861_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* Callbacks for DVB USB */ diff --git a/linux/drivers/media/dvb/dvb-usb/m920x.c b/linux/drivers/media/dvb/dvb-usb/m920x.c index 54626a0db..51c1c8dba 100644 --- a/linux/drivers/media/dvb/dvb-usb/m920x.c +++ b/linux/drivers/media/dvb/dvb-usb/m920x.c @@ -256,6 +256,9 @@ static u32 m920x_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm m920x_i2c_algo = { .master_xfer = m920x_i2c_xfer, .functionality = m920x_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* pid filter */ diff --git a/linux/drivers/media/dvb/dvb-usb/opera1.c b/linux/drivers/media/dvb/dvb-usb/opera1.c index cba064615..d45ceb83b 100644 --- a/linux/drivers/media/dvb/dvb-usb/opera1.c +++ b/linux/drivers/media/dvb/dvb-usb/opera1.c @@ -167,6 +167,9 @@ static u32 opera1_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm opera1_i2c_algo = { .master_xfer = opera1_i2c_xfer, .functionality = opera1_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static int opera1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) diff --git a/linux/drivers/media/dvb/dvb-usb/ttusb2.c b/linux/drivers/media/dvb/dvb-usb/ttusb2.c index e79a26473..849c59478 100644 --- a/linux/drivers/media/dvb/dvb-usb/ttusb2.c +++ b/linux/drivers/media/dvb/dvb-usb/ttusb2.c @@ -124,6 +124,9 @@ static u32 ttusb2_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm ttusb2_i2c_algo = { .master_xfer = ttusb2_i2c_xfer, .functionality = ttusb2_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* Callbacks for DVB USB */ diff --git a/linux/drivers/media/dvb/frontends/cx24123.c b/linux/drivers/media/dvb/frontends/cx24123.c index fb559369e..c9cfc8393 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.c +++ b/linux/drivers/media/dvb/frontends/cx24123.c @@ -29,6 +29,7 @@ #include "dvb_frontend.h" #include "cx24123.h" +#include "compat.h" #define XTAL 10111000 @@ -1065,6 +1066,9 @@ static u32 cx24123_tuner_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm cx24123_tuner_i2c_algo = { .master_xfer = cx24123_tuner_i2c_tuner_xfer, .functionality = cx24123_tuner_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; struct i2c_adapter * diff --git a/linux/drivers/media/dvb/frontends/dibx000_common.c b/linux/drivers/media/dvb/frontends/dibx000_common.c index a8e3a950c..6f1f41ae3 100644 --- a/linux/drivers/media/dvb/frontends/dibx000_common.c +++ b/linux/drivers/media/dvb/frontends/dibx000_common.c @@ -1,6 +1,7 @@ #include <linux/i2c.h> #include "dibx000_common.h" +#include "compat.h" static int debug; module_param(debug, int, 0644); @@ -107,6 +108,9 @@ static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = { .master_xfer = dibx000_i2c_gated_tuner_xfer, .functionality = dibx000_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; struct i2c_adapter * dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf, int gating) diff --git a/linux/drivers/media/dvb/frontends/s5h1420.c b/linux/drivers/media/dvb/frontends/s5h1420.c index 46d55e393..f357f8233 100644 --- a/linux/drivers/media/dvb/frontends/s5h1420.c +++ b/linux/drivers/media/dvb/frontends/s5h1420.c @@ -37,6 +37,7 @@ #include "dvb_frontend.h" #include "s5h1420.h" #include "s5h1420_priv.h" +#include "compat.h" #define TONE_FREQ 22000 @@ -904,6 +905,9 @@ static int s5h1420_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c static struct i2c_algorithm s5h1420_tuner_i2c_algo = { .master_xfer = s5h1420_tuner_i2c_tuner_xfer, .functionality = s5h1420_tuner_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe) diff --git a/linux/drivers/media/dvb/siano/sms-cards.c b/linux/drivers/media/dvb/siano/sms-cards.c index 6f9b77360..fd62e0b85 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.c +++ b/linux/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), @@ -95,7 +105,22 @@ static struct sms_board sms_boards[] = { [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = { .name = "Hauppauge WinTV MiniStick", .type = SMS_NOVA_B0, - .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-01.fw", + .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/linux/drivers/media/dvb/siano/sms-cards.h b/linux/drivers/media/dvb/siano/sms-cards.h index c8f3da6f9..8e0fe9fd2 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.h +++ b/linux/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/linux/drivers/media/dvb/siano/smscoreapi.c b/linux/drivers/media/dvb/siano/smscoreapi.c index 700a0a6a7..b19dd9c7d 100644 --- a/linux/drivers/media/dvb/siano/smscoreapi.c +++ b/linux/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; @@ -1248,6 +1256,76 @@ static int smscore_map_common_buffer(struct smscore_device_t *coredev, } #endif +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/linux/drivers/media/dvb/siano/smscoreapi.h b/linux/drivers/media/dvb/siano/smscoreapi.h index 3bba565d7..1ab277767 100644 --- a/linux/drivers/media/dvb/siano/smscoreapi.h +++ b/linux/drivers/media/dvb/siano/smscoreapi.h @@ -187,6 +187,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 @@ -481,18 +483,6 @@ struct SMSHOSTLIB_STATISTICS_DVB_ST { }; -struct SMSHOSTLIB_GPIO_CONFIG_ST { - u8 Direction; /* GPIO direction: Input - 0, Output - 1 */ - u8 PullUpDown; /* PullUp/PullDown: None - 0, - * PullDown - 1, PullUp - 2, Keeper - 3 */ - u8 InputCharacteristics; /* Input Characteristics: Normal - 0, - * Schmitt trigger - 1 */ - u8 OutputSlewRate; /* Output Slew Rate: - * Fast slew rate - 0, Slow slew rate - 1 */ - u8 OutputDriving; /* Output driving capability: - * 4mA - 0, 8mA - 1, 12mA - 2, 16mA - 3 */ -}; - struct SMSHOSTLIB_I2C_REQ_ST { u32 DeviceAddress; /* I2c device address */ u32 WriteCount; /* number of bytes to write */ @@ -507,6 +497,32 @@ struct SMSHOSTLIB_I2C_RES_ST { }; #endif +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; @@ -519,7 +535,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; @@ -567,9 +583,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/linux/drivers/media/dvb/siano/smsdvb.c b/linux/drivers/media/dvb/siano/smsdvb.c index f2a6c6f9e..bbc87fe2e 100644 --- a/linux/drivers/media/dvb/siano/smsdvb.c +++ b/linux/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) { @@ -276,6 +300,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; } @@ -311,6 +357,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, @@ -411,6 +461,8 @@ static int smsdvb_hotplug(struct smscore_device_t *coredev, sms_info("success"); + sms_board_setup(coredev); + return 0; client_error: diff --git a/linux/drivers/media/dvb/siano/smsusb.c b/linux/drivers/media/dvb/siano/smsusb.c index a50ea8259..fd191b0ef 100644 --- a/linux/drivers/media/dvb/siano/smsusb.c +++ b/linux/drivers/media/dvb/siano/smsusb.c @@ -436,11 +436,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/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index 8775b78af..086b9987f 100644 --- a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -1657,6 +1657,9 @@ static void frontend_init(struct ttusb* ttusb) static struct i2c_algorithm ttusb_dec_algo = { .master_xfer = master_xfer, .functionality = functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *id) diff --git a/linux/drivers/media/video/au0828/au0828-i2c.c b/linux/drivers/media/video/au0828/au0828-i2c.c index 03b797b9b..d8cef6ccc 100644 --- a/linux/drivers/media/video/au0828/au0828-i2c.c +++ b/linux/drivers/media/video/au0828/au0828-i2c.c @@ -301,6 +301,9 @@ static u32 au0828_functionality(struct i2c_adapter *adap) static struct i2c_algorithm au0828_i2c_algo_template = { .master_xfer = i2c_xfer, .functionality = au0828_functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* ----------------------------------------------------------------------- */ diff --git a/linux/drivers/media/video/bt8xx/bttv-i2c.c b/linux/drivers/media/video/bt8xx/bttv-i2c.c index bcd2cd240..558572589 100644 --- a/linux/drivers/media/video/bt8xx/bttv-i2c.c +++ b/linux/drivers/media/video/bt8xx/bttv-i2c.c @@ -260,6 +260,9 @@ static int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int static const struct i2c_algorithm bttv_algo = { .master_xfer = bttv_i2c_xfer, .functionality = functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* ----------------------------------------------------------------------- */ diff --git a/linux/drivers/media/video/cx18/cx18-av-audio.c b/linux/drivers/media/video/cx18/cx18-av-audio.c index 486cad0c2..fd85b9b2d 100644 --- a/linux/drivers/media/video/cx18/cx18-av-audio.c +++ b/linux/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 diff --git a/linux/drivers/media/video/cx18/cx18-av-core.c b/linux/drivers/media/video/cx18/cx18-av-core.c index 518bd701d..40ea6fde6 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.c +++ b/linux/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 @@ -80,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) { diff --git a/linux/drivers/media/video/cx18/cx18-av-core.h b/linux/drivers/media/video/cx18/cx18-av-core.h index a07988c6f..cf68a6039 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.h +++ b/linux/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 @@ -307,7 +308,6 @@ 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/linux/drivers/media/video/cx18/cx18-av-firmware.c b/linux/drivers/media/video/cx18/cx18-av-firmware.c index 924691dca..c64fd0a05 100644 --- a/linux/drivers/media/video/cx18/cx18-av-firmware.c +++ b/linux/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 @@ -68,8 +69,7 @@ int cx18_av_loadfw(struct cx18 *cx) 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 @@ -80,8 +80,6 @@ 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_WR_RETRIES) break; } diff --git a/linux/drivers/media/video/cx18/cx18-cards.c b/linux/drivers/media/video/cx18/cx18-cards.c index 7345fc09d..23bd871b7 100644 --- a/linux/drivers/media/video/cx18/cx18-cards.c +++ b/linux/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 diff --git a/linux/drivers/media/video/cx18/cx18-cards.h b/linux/drivers/media/video/cx18/cx18-cards.h index 32155f6e6..a54aae9ed 100644 --- a/linux/drivers/media/video/cx18/cx18-cards.h +++ b/linux/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 diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 54f0fde42..57fcce84b 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -75,14 +75,9 @@ 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[] = "-"; @@ -96,18 +91,20 @@ static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS; 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); @@ -152,13 +149,11 @@ 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)); + "(Deprecated) MMIO accesses are now never purposely delayed\n" + "\t\t\tEffectively: 0 ns"); MODULE_PARM_DESC(enc_mpg_buffers, "Encoder MPG Buffers (in MB)\n" "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS)); @@ -375,11 +370,6 @@ static void cx18_process_options(struct cx18 *cx) 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,6 +430,8 @@ 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); @@ -451,11 +443,22 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) spin_lock_init(&cx->lock); + cx->work_queue = create_singlethread_workqueue(cx->name); + if (cx->work_queue == NULL) { + CX18_ERR("Unable to create work hander thread\n"); + return -ENOMEM; + } + + 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; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) - INIT_WORK(&cx->work, cx18_work_handler); + INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler); #else - INIT_WORK(&cx->work, cx18_work_handler, cx); + INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler, + &cx->epu_work_order[i].work); #endif + } /* start counting open_id at 1 */ cx->open_id = 1; @@ -660,12 +663,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); @@ -835,11 +835,11 @@ free_map: free_mem: release_mem_region(cx->base_addr, CX18_MEM_SIZE); free_workqueue: + destroy_workqueue(cx->work_queue); 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); @@ -918,6 +918,15 @@ int cx18_init_on_first_open(struct cx18 *cx) return 0; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +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); +} + +#endif static void cx18_remove(struct pci_dev *pci_dev) { struct cx18 *cx = pci_get_drvdata(pci_dev); @@ -931,11 +940,22 @@ static void cx18_remove(struct pci_dev *pci_dev) /* Interrupts */ cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); + + cx18_halt_firmware(cx); + + cx18_cancel_epu_work_orders(cx); +#else + + flush_workqueue(cx->work_queue); + cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); cx18_halt_firmware(cx); +#endif - flush_scheduled_work(); + destroy_workqueue(cx->work_queue); cx18_streams_cleanup(cx, 1); @@ -949,7 +969,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); } @@ -998,6 +1017,7 @@ static void module_cleanup(void) continue; kfree(cx18_cards[i]); } + } module_init(module_start); diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index ce7680675..94c196a91 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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 @@ -176,7 +174,6 @@ #define CX18_MAX_PGM_INDEX (400) -extern int cx18_retry_mmio; /* enable check & retry of mmio accesses */ extern int cx18_debug; @@ -185,7 +182,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,8 +199,6 @@ 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 */ @@ -219,6 +213,7 @@ struct cx18_buffer { dma_addr_t dma_handle; u32 id; unsigned long b_flags; + unsigned skipped; char *buf; u32 bytesused; @@ -247,6 +242,26 @@ struct cx18_dvb { struct cx18; /* forward reference */ struct cx18_scb; /* forward reference */ + +#define CX18_MAX_MDL_ACKS 2 +#define CX18_MAX_EPU_WORK_ORDERS 70 /* CPU_DE_RELEASE_MDL bursts 63 commands */ + +#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 { @@ -260,7 +275,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 @@ -353,18 +368,6 @@ struct cx18_i2c_algo_callback_data { }; #define CX18_MAX_MMIO_WR_RETRIES 10 -#define CX18_MAX_MMIO_RD_RETRIES 2 - -struct cx18_mmio_stats { - atomic_t retried_write[CX18_MAX_MMIO_WR_RETRIES+1]; - atomic_t retried_read[CX18_MAX_MMIO_RD_RETRIES+1]; -}; - -#define CX18_MAX_MB_ACK_DELAY 100 - -struct cx18_mbox_stats { - atomic_t mb_ack_delay[CX18_MAX_MB_ACK_DELAY+1]; -}; /* Struct to hold info about cx18 cards */ struct cx18 { @@ -388,7 +391,6 @@ struct cx18 { 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; /* codec settings */ @@ -441,7 +443,13 @@ struct cx18 { /* when the current DMA is finished this queue is woken up */ wait_queue_head_t dma_waitq; - struct work_struct work; + u32 sw1_irq_mask; + u32 sw2_irq_mask; + u32 hw2_irq_mask; + + struct workqueue_struct *work_queue; + 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]; @@ -456,10 +464,6 @@ struct cx18 { u32 gpio_val; struct mutex gpio_lock; - /* Statistics */ - struct cx18_mmio_stats mmio_stats; - struct cx18_mbox_stats mbox_stats; - /* v4l2 and User settings */ /* codec settings */ diff --git a/linux/drivers/media/video/cx18/cx18-dvb.c b/linux/drivers/media/video/cx18/cx18-dvb.c index 4845f732e..034e09a37 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.c +++ b/linux/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" @@ -305,26 +304,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_buf_sync_for_device(s, buf); - cx18_enqueue(s, buf, &s->q_free); - - if (s->handle == CX18_INVALID_TASK_HANDLE || - !test_bit(CX18_F_S_STREAMING, &s->s_flags)) - 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/linux/drivers/media/video/cx18/cx18-dvb.h b/linux/drivers/media/video/cx18/cx18-dvb.h index bbdcefc87..bf8d8f6f5 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-fileops.c b/linux/drivers/media/video/cx18/cx18-fileops.c index e884271b8..45b5f402e 100644 --- a/linux/drivers/media/video/cx18/cx18-fileops.c +++ b/linux/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 diff --git a/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c index d9c5f55ab..8eac84314 100644 --- a/linux/drivers/media/video/cx18/cx18-firmware.c +++ b/linux/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 @@ -121,6 +122,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,6 +133,7 @@ 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; } @@ -150,6 +153,7 @@ 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; } @@ -185,6 +189,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; } } @@ -196,6 +201,7 @@ 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); + cx18_setup_page(cx, 0); return size; } diff --git a/linux/drivers/media/video/cx18/cx18-gpio.c b/linux/drivers/media/video/cx18/cx18-gpio.c index 17b7a32fc..1a99329f3 100644 --- a/linux/drivers/media/video/cx18/cx18-gpio.c +++ b/linux/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 @@ -60,8 +61,6 @@ static void gpio_write(struct cx18 *cx) 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); - if (!cx18_retry_mmio) - (void) cx18_read_reg(cx, CX18_REG_GPIO_OUT2); /* sync */ } void cx18_reset_i2c_slaves_gpio(struct cx18 *cx) diff --git a/linux/drivers/media/video/cx18/cx18-gpio.h b/linux/drivers/media/video/cx18/cx18-gpio.h index beb7424b9..39ffccc19 100644 --- a/linux/drivers/media/video/cx18/cx18-gpio.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-i2c.c b/linux/drivers/media/video/cx18/cx18-i2c.c index 1af9ea149..c0a79ecd4 100644 --- a/linux/drivers/media/video/cx18/cx18-i2c.c +++ b/linux/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 @@ -184,9 +185,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) @@ -197,9 +198,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) @@ -433,16 +434,10 @@ int init_cx18_i2c(struct cx18 *cx) } /* courtesy of Steven Toth <stoth@hauppauge.com> */ cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0); - if (!cx18_retry_mmio) - (void) cx18_read_reg(cx, 0xc7001c); /* sync */ mdelay(10); cx18_write_reg_expect(cx, 0x00c000c0, 0xc7001c, 0x000000c0, 0x00c000c0); - if (!cx18_retry_mmio) - (void) cx18_read_reg(cx, 0xc7001c); /* sync */ mdelay(10); cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0); - if (!cx18_retry_mmio) - (void) cx18_read_reg(cx, 0xc7001c); /* sync */ mdelay(10); /* Set to edge-triggered intrs. */ @@ -452,12 +447,12 @@ int init_cx18_i2c(struct cx18 *cx) ~(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/linux/drivers/media/video/cx18/cx18-io.c b/linux/drivers/media/video/cx18/cx18-io.c index 3c6485fce..ec5b3d7bc 100644 --- a/linux/drivers/media/video/cx18/cx18-io.c +++ b/linux/drivers/media/video/cx18/cx18-io.c @@ -24,183 +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_WR_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_RD_RETRIES; i++) - CX18_DEBUG_INFO("retried_read[%d] = %d\n", i, - atomic_read(&cx->mmio_stats.retried_read[i])); - for (i = 0; i <= CX18_MAX_MB_ACK_DELAY; i++) - if (atomic_read(&cx->mbox_stats.mb_ack_delay[i])) - CX18_DEBUG_INFO("mb_ack_delay[%d] = %d\n", i, - atomic_read(&cx->mbox_stats.mb_ack_delay[i])); - return; -} - -void cx18_raw_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr) -{ - int i; - for (i = 0; i < CX18_MAX_MMIO_WR_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_RD_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_RD_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_WR_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_WR_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_WR_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_WR_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_RD_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_RD_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_RD_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; @@ -234,32 +57,28 @@ 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) { - u32 r; - 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_cpu(struct cx18 *cx, u32 val) diff --git a/linux/drivers/media/video/cx18/cx18-io.h b/linux/drivers/media/video/cx18/cx18-io.h index 4486b73fa..2635b3a8c 100644 --- a/linux/drivers/media/video/cx18/cx18-io.h +++ b/linux/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_WR_RETRIES) - i = CX18_MAX_MMIO_WR_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_RD_RETRIES) - i = CX18_MAX_MMIO_RD_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,130 +153,32 @@ 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); diff --git a/linux/drivers/media/video/cx18/cx18-ioctl.c b/linux/drivers/media/video/cx18/cx18-ioctl.c index 20c7650d7..5d9c1146e 100644 --- a/linux/drivers/media/video/cx18/cx18-ioctl.c +++ b/linux/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 @@ -857,7 +858,6 @@ static int cx18_log_status(struct file *file, void *fh) 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/linux/drivers/media/video/cx18/cx18-ioctl.h b/linux/drivers/media/video/cx18/cx18-ioctl.h index 2222f679d..08fe24e95 100644 --- a/linux/drivers/media/video/cx18/cx18-ioctl.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-irq.c b/linux/drivers/media/video/cx18/cx18-irq.c index 3e23fc350..8c22637f4 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.c +++ b/linux/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,127 +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" - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) -void cx18_work_handler(struct work_struct *work) -{ - struct cx18 *cx = container_of(work, struct cx18, work); -#else -void cx18_work_handler(void *arg) -{ - struct cx18 *cx = arg; -#endif - 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, int rpu) -{ - 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, rpu); - 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, rpu); - wake_up(&cx->dma_waitq); - if (s->id != -1) - wake_up(&s->waitq); -} - -static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb, int rpu) -{ - 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, rpu); - 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, CPU); - break; - case CX18_EPU_DEBUG: - epu_debug(cx, &mb, CPU); - 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); - } -} static void xpu_ack(struct cx18 *cx, u32 sw2) { @@ -151,6 +34,14 @@ static void xpu_ack(struct cx18 *cx, u32 sw2) wake_up(&cx->mb_apu_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); +} + #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) irqreturn_t cx18_irq_handler(int irq, void *dev_id, struct pt_regs *regs) #else @@ -158,16 +49,11 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) #endif { 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,6 +66,13 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) 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)) { } @@ -188,11 +81,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)) - schedule_work(&cx->work); - return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE; } diff --git a/linux/drivers/media/video/cx18/cx18-irq.h b/linux/drivers/media/video/cx18/cx18-irq.h index 6472e42c6..b94ca6019 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.h +++ b/linux/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 @@ -36,9 +37,3 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id, struct pt_regs *regs); #else irqreturn_t cx18_irq_handler(int irq, void *dev_id); #endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) -void cx18_work_handler(struct work_struct *work); -#else -void cx18_work_handler(void *arg); -#endif diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index 35f7188d4..f62aee719 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/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,6 +27,10 @@ #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 */ @@ -92,12 +97,187 @@ static const struct cx18_api_info *find_api_info(u32 cmd) return NULL; } -long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu) +static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) +{ + char argstr[MAX_MB_ARGUMENTS*11+1]; + char *p; + int i; + + if (!(cx18_debug & CX18_DBGFLG_API)) + return; + + 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); +} + + +/* + * Functions that run in a work_queue work handling context + */ + +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; + } + + 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 put them back in 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); + continue; + } + + 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); + + dvb_dmx_swfilter(&s->dvb.demux, buf->buf, + buf->bytesused); + + cx18_buf_sync_for_device(s, buf); + cx18_enqueue(s, buf, &s->q_free); + + if (s->handle != CX18_INVALID_TASK_HANDLE && + test_bit(CX18_F_S_STREAMING, &s->s_flags)) + 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 + set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); + } + wake_up(&cx->dma_waitq); + if (s->id != -1) + wake_up(&s->waitq); +} + +static void epu_debug(struct cx18 *cx, struct cx18_epu_work_order *order) +{ + char *p; + char *str = order->str; + + 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); +} + +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); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) +void cx18_epu_work_handler(struct work_struct *work) +{ + struct cx18_epu_work_order *order = + container_of(work, struct cx18_epu_work_order, work); +#else +void cx18_epu_work_handler(void *arg) +{ + struct cx18_epu_work_order *order = arg; +#endif + 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; + u32 ack_irq, req; - switch (rpu) { + switch (order->rpu) { case APU: ack_irq = IRQ_EPU_TO_APU_ACK; ack_mb = &cx->scb->apu2epu_mb; @@ -108,23 +288,187 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu) break; default: CX18_WARN("Unhandled RPU (%d) for command %x ack\n", - rpu, mb->cmd); - return -EINVAL; + 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 void cx18_api_log_ack_delay(struct cx18 *cx, int msecs) +static int epu_dma_done_irq(struct cx18 *cx, struct cx18_epu_work_order *order) { - if (msecs > CX18_MAX_MB_ACK_DELAY) - msecs = CX18_MAX_MB_ACK_DELAY; - atomic_inc(&cx->mbox_stats.mb_ack_delay[msecs]); + 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); @@ -167,8 +511,6 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) } mutex_lock(mb_lock); - cx18_setup_page(cx, SCB_OFFSET); - /* * Wait for an in-use mailbox to complete * @@ -181,7 +523,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) */ state = cx18_readl(cx, xpu_state); req = cx18_readl(cx, &mb->request); - timeout = msecs_to_jiffies(20); /* 1 field at 50 Hz vertical refresh */ + timeout = msecs_to_jiffies(10); ret = wait_event_timeout(*waitq, (ack = cx18_readl(cx, &mb->ack)) == req, timeout); @@ -191,8 +533,8 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) 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 usecs for busy mbox to be acked\n", - jiffies_to_usecs(timeout-ret)); + 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; @@ -206,10 +548,8 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) /* * Notify the XPU and wait for it to send an Ack back - * 21 ms = ~ 0.5 frames at a frame rate of 24 fps - * 42 ms = ~ 1 frame at a frame rate of 24 fps */ - timeout = msecs_to_jiffies((info->flags & API_FAST) ? 21 : 42); + 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); @@ -219,27 +559,19 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) *waitq, cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request), timeout); + if (ret == 0) { /* Timed out */ mutex_unlock(mb_lock); - i = jiffies_to_msecs(timeout); - cx18_api_log_ack_delay(cx, i); - CX18_WARN("sending %s timed out waiting %d msecs for RPU " - "acknowledgement\n", info->name, i); + CX18_DEBUG_WARN("sending %s timed out waiting %d msecs for RPU " + "acknowledgement\n", + info->name, jiffies_to_msecs(timeout)); return -EINVAL; - } else if (ret < 0) { - /* Interrupted */ - mutex_unlock(mb_lock); - CX18_WARN("sending %s was interrupted waiting for RPU" - "acknowledgement\n", info->name); - return -EINTR; } - i = jiffies_to_msecs(timeout-ret); - cx18_api_log_ack_delay(cx, i); if (ret != timeout) CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n", - i, info->name); + jiffies_to_msecs(timeout-ret), info->name); /* Collect data returned by the XPU */ for (i = 0; i < MAX_MB_ARGUMENTS; i++) diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.h b/linux/drivers/media/video/cx18/cx18-mailbox.h index 54758f32d..35104458e 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.h +++ b/linux/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 @@ -37,6 +38,17 @@ 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 { @@ -73,6 +85,13 @@ int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd, 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, int rpu); + +void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) +void cx18_epu_work_handler(struct work_struct *work); +#else +void cx18_epu_work_handler(void *arg); +#endif #endif diff --git a/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c index 174682c25..7b09c9a3e 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.c +++ b/linux/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 @@ -44,83 +45,116 @@ void cx18_queue_init(struct cx18_queue *q) void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, struct cx18_queue *q) { - unsigned long flags = 0; - /* clear the buffer if it is going to be enqueued to the free queue */ if (q == &s->q_free) { buf->bytesused = 0; buf->readpos = 0; buf->b_flags = 0; + buf->skipped = 0; } - spin_lock_irqsave(&s->qlock, flags); + mutex_lock(&s->qlock); list_add_tail(&buf->list, &q->list); atomic_inc(&q->buffers); q->bytesused += buf->bytesused - buf->readpos; - spin_unlock_irqrestore(&s->qlock, flags); + mutex_unlock(&s->qlock); } 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); q->bytesused -= buf->bytesused - buf->readpos; + buf->skipped = 0; } - 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 *ret = NULL; + struct list_head *p, *t; + LIST_HEAD(r); + + mutex_lock(&s->qlock); + list_for_each_safe(p, t, &s->q_free.list) { + buf = list_entry(p, struct cx18_buffer, list); + + if (buf->id != id) { + buf->skipped++; + if (buf->skipped >= atomic_read(&s->q_free.buffers)-1) { + /* buffer must have fallen out of rotation */ + atomic_dec(&s->q_free.buffers); + list_move_tail(&buf->list, &r); + CX18_WARN("Skipped %s, buffer %d, %d " + "times - it must have dropped out of " + "rotation\n", s->name, buf->id, + buf->skipped); + } 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); + if (s->type == CX18_ENC_STREAM_TYPE_TS) { + /* + * TS doesn't use q_full, but for sweeping up lost + * buffers, we want the TS to requeue the buffer just + * before sending the MDL back to the firmware, so we + * pull it off the list here. + */ + list_del_init(&buf->list); + } else { + atomic_inc(&s->q_full.buffers); + s->q_full.bytesused += buf->bytesused; + list_move_tail(&buf->list, &s->q_full.list); + } + + ret = buf; + break; + } + mutex_unlock(&s->qlock); - spin_unlock(&s->qlock); - return buf; + /* Put lost buffers back into firmware transfer rotation */ + while (!list_empty(&r)) { + buf = list_entry(r.next, struct cx18_buffer, list); + list_del_init(r.next); + 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); + CX18_INFO("Returning %s, buffer %d back to transfer rotation\n", + s->name, buf->id); + /* and there was much rejoicing... */ } - spin_unlock(&s->qlock); - CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name); - return NULL; + 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->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) diff --git a/linux/drivers/media/video/cx18/cx18-queue.h b/linux/drivers/media/video/cx18/cx18-queue.h index 7f93bb13c..ff50a2b7e 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.h +++ b/linux/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 @@ -46,7 +47,7 @@ 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/linux/drivers/media/video/cx18/cx18-scb.c b/linux/drivers/media/video/cx18/cx18-scb.c index f56d3772a..ac18bd932 100644 --- a/linux/drivers/media/video/cx18/cx18-scb.c +++ b/linux/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 diff --git a/linux/drivers/media/video/cx18/cx18-scb.h b/linux/drivers/media/video/cx18/cx18-scb.h index 594713bbe..1dc1c431f 100644 --- a/linux/drivers/media/video/cx18/cx18-scb.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index d29a0b61b..f7a7f38d8 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/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 @@ -132,7 +133,7 @@ static void cx18_stream_init(struct cx18 *cx, int type) 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); @@ -622,3 +623,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/linux/drivers/media/video/cx18/cx18-streams.h b/linux/drivers/media/video/cx18/cx18-streams.h index f327e947b..7218b1504 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.h +++ b/linux/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,6 +23,7 @@ */ 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); diff --git a/linux/drivers/media/video/cx18/cx18-version.h b/linux/drivers/media/video/cx18/cx18-version.h index 366cc1472..eb043d599 100644 --- a/linux/drivers/media/video/cx18/cx18-version.h +++ b/linux/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 2 +#define CX18_DRIVER_VERSION_PATCHLEVEL 3 #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/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c index dc3f79638..db1e8bdab 100644 --- a/linux/drivers/media/video/cx23885/cx23885-cards.c +++ b/linux/drivers/media/video/cx23885/cx23885-cards.c @@ -159,6 +159,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); @@ -238,6 +242,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); @@ -391,6 +399,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; @@ -531,6 +540,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) */ @@ -631,6 +641,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 */ @@ -645,6 +656,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/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index a11fdbfb5..990fd217b 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -503,6 +503,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/linux/drivers/media/video/cx23885/cx23885-i2c.c b/linux/drivers/media/video/cx23885/cx23885-i2c.c index ced5648d8..2210b47d2 100644 --- a/linux/drivers/media/video/cx23885/cx23885-i2c.c +++ b/linux/drivers/media/video/cx23885/cx23885-i2c.c @@ -353,6 +353,9 @@ static u32 cx23885_functionality(struct i2c_adapter *adap) static struct i2c_algorithm cx23885_i2c_algo_template = { .master_xfer = i2c_xfer, .functionality = cx23885_functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* ----------------------------------------------------------------------- */ diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h index 26f755354..ad93e0e88 100644 --- a/linux/drivers/media/video/cx23885/cx23885.h +++ b/linux/drivers/media/video/cx23885/cx23885.h @@ -67,6 +67,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/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index 3dd2f6629..f7171586d 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -795,7 +795,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; } @@ -872,7 +871,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/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index b86b0870d..c62455202 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -63,7 +63,7 @@ static int em28xx_isoc_audio_deinit(struct em28xx *dev) dprintk("Stopping isoc\n"); for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { - usb_kill_urb(dev->adev->urb[i]); + usb_unlink_urb(dev->adev->urb[i]); usb_free_urb(dev->adev->urb[i]); dev->adev->urb[i] = NULL; } @@ -80,7 +80,9 @@ static void em28xx_audio_isocirq(struct urb *urb) struct em28xx *dev = urb->context; int i; unsigned int oldptr; +#ifdef NO_PCM_LOCK unsigned long flags; +#endif int period_elapsed = 0; int status; unsigned char *cp; @@ -106,9 +108,26 @@ static void em28xx_audio_isocirq(struct urb *urb) if (!length) continue; +#ifdef NO_PCM_LOCK spin_lock_irqsave(&dev->adev->slock, flags); - +#endif oldptr = dev->adev->hwptr_done_capture; + if (oldptr + length >= runtime->buffer_size) { + unsigned int cnt = + runtime->buffer_size - oldptr; + memcpy(runtime->dma_area + oldptr * stride, cp, + cnt * stride); + memcpy(runtime->dma_area, cp + cnt * stride, + length * stride - cnt * stride); + } else { + memcpy(runtime->dma_area + oldptr * stride, cp, + length * stride); + } + +#ifndef NO_PCM_LOCK + snd_pcm_stream_lock(substream); +#endif + dev->adev->hwptr_done_capture += length; if (dev->adev->hwptr_done_capture >= runtime->buffer_size) @@ -123,19 +142,11 @@ static void em28xx_audio_isocirq(struct urb *urb) period_elapsed = 1; } +#ifdef NO_PCM_LOCK spin_unlock_irqrestore(&dev->adev->slock, flags); - - if (oldptr + length >= runtime->buffer_size) { - unsigned int cnt = - runtime->buffer_size - oldptr; - memcpy(runtime->dma_area + oldptr * stride, cp, - cnt * stride); - memcpy(runtime->dma_area, cp + cnt * stride, - length * stride - cnt * stride); - } else { - memcpy(runtime->dma_area + oldptr * stride, cp, - length * stride); - } +#else + snd_pcm_stream_unlock(substream); +#endif } if (period_elapsed) snd_pcm_period_elapsed(substream); diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 9177ea54b..23d3820a2 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -52,10 +52,87 @@ struct em28xx_hash_table { 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 */ +#if 0 /* Still missing the dvb setup */ +static struct em28xx_reg_seq em2880_msi_digivox_ad_digital[] = { + {EM28XX_R08_GPIO, 0x6a, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; +#endif + +/* Board - EM2870 Kworld 355u + Analog - No input analog */ +#if 0 /* Still missing the dvb setup */ +static struct em28xx_reg_seq em2870_kworld_355u_digital[] = { + {EM2880_R04_GPO, 0x01, 0xff, 10}, + { -1, -1, -1, -1}, +}; +#endif + +/* 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}, +}; + +/* Pinnacle PCTV HD Mini (80e) GPIOs + 0-5: not used + 6: demod reset, active low + 7: LED on, active high */ +static struct em28xx_reg_seq em2874_pinnacle_80e_digital[] = { + {EM28XX_R06_I2C_CLK, 0x45, 0xff, 10}, /*400 KHz*/ + {EM2874_R80_GPIO, 0x80, 0xff, 100},/*Demod reset*/ + {EM2874_R80_GPIO, 0xc0, 0xff, 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, @@ -65,10 +142,10 @@ struct em28xx_board em28xx_boards[] = { [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 = EM28XX_AMUX_LINE_IN, @@ -79,15 +156,15 @@ struct em28xx_board em28xx_boards[] = { } }, }, [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, @@ -96,12 +173,10 @@ struct em28xx_board em28xx_boards[] = { }, [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 = EM28XX_AMUX_LINE_IN, @@ -113,11 +188,10 @@ struct em28xx_board em28xx_boards[] = { }, [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 = EM28XX_AMUX_LINE_IN, @@ -133,11 +207,10 @@ struct em28xx_board em28xx_boards[] = { }, [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 = EM28XX_AMUX_VIDEO, @@ -153,15 +226,13 @@ struct em28xx_board em28xx_boards[] = { }, [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, @@ -175,12 +246,10 @@ 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 = EM28XX_AMUX_LINE_IN, @@ -197,10 +266,9 @@ struct em28xx_board em28xx_boards[] = { [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, @@ -218,12 +286,10 @@ struct em28xx_board em28xx_boards[] = { [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 = EM28XX_AMUX_VIDEO, @@ -240,11 +306,10 @@ struct em28xx_board em28xx_boards[] = { [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 = EM28XX_AMUX_LINE_IN, @@ -261,30 +326,28 @@ struct em28xx_board em28xx_boards[] = { [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, + .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, + .vmux = SAA7115_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = SAA7115_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_PINNACLE_DVC_100] = { .name = "Pinnacle Dazzle DVC 100", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, /* Capture only device */ + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, @@ -295,10 +358,10 @@ struct em28xx_board em28xx_boards[] = { } }, }, [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 = EM28XX_AMUX_VIDEO, @@ -307,12 +370,10 @@ struct em28xx_board em28xx_boards[] = { [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 = EM28XX_AMUX_LINE_IN, @@ -329,14 +390,12 @@ struct em28xx_board em28xx_boards[] = { [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 = EM28XX_AMUX_LINE_IN, @@ -351,11 +410,11 @@ struct em28xx_board em28xx_boards[] = { } }, }, [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 = EM28XX_AMUX_LINE_IN, @@ -367,21 +426,20 @@ struct em28xx_board em28xx_boards[] = { }, [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 = 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 = EM28XX_AMUX_LINE_IN, @@ -394,11 +452,10 @@ struct em28xx_board em28xx_boards[] = { [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 = EM28XX_AMUX_VIDEO, @@ -415,30 +472,38 @@ struct em28xx_board em28xx_boards[] = { [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 = { { + +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, +#endif + .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, } }, }, [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 = EM28XX_AMUX_VIDEO, @@ -454,8 +519,7 @@ struct em28xx_board em28xx_boards[] = { }, [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, @@ -474,11 +538,10 @@ struct em28xx_board em28xx_boards[] = { [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 = EM28XX_AMUX_LINE_IN, @@ -492,10 +555,18 @@ struct em28xx_board em28xx_boards[] = { .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, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = default_digital, +#endif }, [EM2870_BOARD_TERRATEC_XS_MT2060] = { .name = "Terratec Cinergy T XS (MT2060)", @@ -506,172 +577,242 @@ 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, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = default_digital, +#endif }, [EM2870_BOARD_KWORLD_355U] = { .name = "Kworld 355 U DVB-T", .valid = EM28XX_BOARD_NOT_VALIDATED, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = em2870_kworld_355u_digital, +#endif }, [EM2870_BOARD_PINNACLE_PCTV_DVB] = { .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 = { { +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = default_digital, +#endif + .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_LINE_IN, + .gpio = default_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .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 = 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, } }, }, [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, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, +#endif + .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 = 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 = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = { .name = "Hauppauge WinTV HVR 950", - .vchannels = 3, .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 = 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, } }, }, [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = { .name = "Pinnacle PCTV HD Pro Stick", - .vchannels = 3, .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 = 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, } }, }, [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = { .name = "AMD ATI TV Wonder HD 600", - .vchannels = 3, .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 = 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, } }, }, [EM2880_BOARD_TERRATEC_HYBRID_XS] = { - .name = "Terratec Hybrid XS", - .vchannels = 3, - .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 = EM28XX_AMUX_VIDEO, + .gpio = default_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, } }, }, /* maybe there's a reason behind it why Terratec sells the Hybrid XS @@ -679,33 +820,38 @@ struct em28xx_board em28xx_boards[] = { maybe we'll need it lateron */ [EM2880_BOARD_TERRATEC_PRODIGY_XS] = { .name = "Terratec Prodigy XS", - .vchannels = 3, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, - .input = { { +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, +#endif + .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, } }, }, [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, @@ -723,11 +869,10 @@ struct em28xx_board em28xx_boards[] = { [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 = EM28XX_AMUX_VIDEO, @@ -744,9 +889,9 @@ struct em28xx_board em28xx_boards[] = { [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 = EM28XX_AMUX_LINE_IN, @@ -759,11 +904,10 @@ struct em28xx_board em28xx_boards[] = { [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 = EM28XX_AMUX_VIDEO, @@ -780,11 +924,10 @@ struct em28xx_board em28xx_boards[] = { [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 = EM28XX_AMUX_VIDEO, @@ -800,10 +943,9 @@ struct em28xx_board em28xx_boards[] = { }, [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 = EM28XX_AMUX_LINE_IN, @@ -816,11 +958,10 @@ struct em28xx_board em28xx_boards[] = { [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 = EM28XX_AMUX_VIDEO, @@ -836,11 +977,10 @@ struct em28xx_board em28xx_boards[] = { }, [EM2820_BOARD_PROLINK_PLAYTV_USB2] = { .name = "Pixelview Prolink PlayTV USB 2.0", - .vchannels = 3, .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_VIDEO, @@ -857,13 +997,12 @@ 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 = EM28XX_AMUX_VIDEO, @@ -872,50 +1011,64 @@ struct em28xx_board em28xx_boards[] = { [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 = { { +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = em2880_msi_digivox_ad_digital, +#endif + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = em2880_msi_digivox_ad_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2880_msi_digivox_ad_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .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 = { { +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = em2880_msi_digivox_ad_digital, +#endif + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = em2880_msi_digivox_ad_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2880_msi_digivox_ad_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .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 = EM28XX_AMUX_VIDEO, @@ -931,93 +1084,118 @@ struct em28xx_board em28xx_boards[] = { }, [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_LINE_IN, + .gpio = default_analog, }, { /* S-video has not been tested yet */ .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .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 = { { +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = default_digital, +#endif + .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_LINE_IN, + .gpio = default_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .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 = { { +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = default_digital, +#endif + .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_LINE_IN, + .gpio = default_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .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 = { { +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, +#endif + .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, } }, }, [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 = EM28XX_AMUX_VIDEO, @@ -1034,51 +1212,61 @@ struct em28xx_board em28xx_boards[] = { [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 = { { +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, +#endif + .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_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 = EM28XX_AMUX_VIDEO, + .gpio = default_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, } }, }, [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, @@ -1088,15 +1276,21 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + + /* em2874 tuners are DVB only */ + [EM2874_BOARD_PINNACLE_PCTV_80E] = { .name = "Pinnacle PCTV HD Mini", - .vchannels = 0, .tuner_type = TUNER_ABSENT, - .has_dvb = 1, - .ir_codes = ir_codes_pinnacle_pctv_hd, + .has_dvb = 1, + .dvb_gpio = em2874_pinnacle_80e_digital, + .ir_codes = ir_codes_pinnacle_pctv_hd, .decoder = EM28XX_NODECODER, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_FREQ_400_KHZ, #ifdef DJH_DEBUG - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, @@ -1184,8 +1378,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), @@ -1211,84 +1405,6 @@ 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}, -}; - -/* Pinnacle PCTV HD Mini (80e) GPIOs - 0-5: not used - 6: demod reset, active low - 7: LED on, active high */ -static struct em28xx_reg_seq em2874_pinnacle_80e_digital[] = { - {EM28XX_R06_I2C_CLK, 0x45, 0xff, 10}, /*400 KHz*/ - {EM2874_R80_GPIO, 0x80, 0xff, 100},/*Demod reset*/ - {EM2874_R80_GPIO, 0xc0, 0xff, 10}, - { -1, -1, -1, -1}, -}; - -/* * EEPROM hash table for devices with generic USB IDs */ static struct em28xx_hash_table em28xx_eeprom_hash [] = { @@ -1315,28 +1431,15 @@ 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->ir_codes = em28xx_boards[dev->model].ir_codes; - dev->valid = em28xx_boards[dev->model].valid; + memcpy(&dev->board, &em28xx_boards[dev->model], sizeof(dev->board)); } /* Since em28xx_pre_card_setup() requires a proper dev->model, @@ -1346,6 +1449,11 @@ void em28xx_pre_card_setup(struct em28xx *dev) { int rc; + em28xx_set_model(dev); + + em28xx_info("Identified as %s (card=%d)\n", + dev->board.name, dev->model); + /* Set the default GPO/GPIO for legacy devices */ dev->reg_gpo_num = EM2880_R04_GPO; dev->reg_gpio_num = EM28XX_R08_GPIO; @@ -1356,7 +1464,8 @@ void em28xx_pre_card_setup(struct em28xx *dev) rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); if (rc > 0) { dev->chip_id = rc; - switch (rc) { + + switch (dev->chip_id) { case CHIP_ID_EM2750: em28xx_info("chip ID is em2750\n"); break; @@ -1379,7 +1488,7 @@ void em28xx_pre_card_setup(struct em28xx *dev) 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); } } @@ -1388,223 +1497,83 @@ void em28xx_pre_card_setup(struct em28xx *dev) if (rc >= 0) dev->reg_gpo = rc; - em28xx_set_model(dev); - - /* request some modules */ - switch (dev->model) { - case EM2880_BOARD_TERRATEC_PRODIGY_XS: - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_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_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - 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_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - 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_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - 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; + /* 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; - case EM2880_BOARD_MSI_DIGIVOX_AD: - case EM2880_BOARD_MSI_DIGIVOX_AD_II: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - 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; + if (!dev->board.i2c_speed) + dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ; - case EM2750_BOARD_UNKNOWN: - case EM2750_BOARD_DLCW_130: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x0a", 1); - break; + 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_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); /* 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_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d); msleep(10); - em28xx_write_regs(dev, 0x08, "\x6d", 1); - 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_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - 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_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); /* 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_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); /* 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_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - /* 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_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - /* 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_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - /* 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_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - /* enables audio for that device */ - em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1); - break; - - case EM2874_BOARD_PINNACLE_PCTV_80E: - /* Set 400 KHz clock and select secondary i2c bus */ - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_FREQ_400_KHZ); - - dev->digital_gpio = em2874_pinnacle_80e_digital; + /* 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) @@ -1630,6 +1599,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 */ @@ -1814,6 +1784,7 @@ void em28xx_card_setup(struct em28xx *dev) 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; @@ -1829,7 +1800,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) @@ -1839,7 +1810,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: @@ -1862,10 +1833,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"); @@ -1880,13 +1851,13 @@ 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 @@ -1894,3 +1865,4 @@ void em28xx_card_setup(struct em28xx *dev) em28xx_ir_init(dev); } + diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index eb8515895..f62b05eae 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -66,7 +66,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 +75,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 +102,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 +119,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 +142,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 +150,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); @@ -349,7 +353,7 @@ static int em28xx_set_audio_source(struct em28xx *dev) int ret; u8 input; - if (dev->is_em2800) { + if (dev->board.is_em2800) { if (dev->ctl_ainput == EM28XX_AMUX_VIDEO) input = EM2800_AUDIO_SRC_TUNER; else @@ -360,7 +364,7 @@ static int em28xx_set_audio_source(struct em28xx *dev) return ret; } - if (dev->has_msp34xx) + if (dev->board.has_msp34xx) input = EM28XX_AUDIO_SRC_TUNER; else { switch (dev->ctl_ainput) { @@ -399,7 +403,7 @@ struct em28xx_vol_table outputs[] = { int em28xx_audio_analog_set(struct em28xx *dev) { int ret, i; - u8 xclk = 0x07; + u8 xclk; if (!dev->audio_mode.has_audio) return 0; @@ -417,13 +421,11 @@ int em28xx_audio_analog_set(struct em28xx *dev) } } - if (dev->has_12mhz_i2s) - xclk |= 0x20; - + 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); @@ -556,20 +558,20 @@ EXPORT_SYMBOL_GPL(em28xx_audio_setup); int em28xx_colorlevels_set_default(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); - - 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); + 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) @@ -602,17 +604,17 @@ 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); @@ -621,9 +623,9 @@ int em28xx_capture_start(struct em28xx *dev, int start) int em28xx_outfmt_set_yuv422(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); + em28xx_write_reg(dev, EM28XX_R27_OUTFMT, 0x34); + em28xx_write_reg(dev, EM28XX_R10_VINMODE, 0x10); + return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x11); } static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, @@ -660,7 +662,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]; @@ -739,12 +741,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) { @@ -769,22 +773,25 @@ 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); } #if 0 /* Resource is locked */ - if (dev->mode != EM28XX_MODE_UNDEFINED) + if (dev->mode != EM28XX_SUSPEND) return -EINVAL; #endif 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); diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index 714d35c51..8d3ed7dc8 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -162,7 +162,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; } @@ -216,7 +216,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); } /* ------------------------------------------------------------------ */ @@ -394,7 +394,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; } @@ -410,8 +410,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, @@ -467,12 +469,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; @@ -480,7 +482,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/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c index ec3e3b157..7084728eb 100644 --- a/linux/drivers/media/video/em28xx/em28xx-i2c.c +++ b/linux/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); @@ -388,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, @@ -521,6 +523,9 @@ static int attach_inform(struct i2c_client *client) static struct i2c_algorithm em28xx_algo = { .master_xfer = em28xx_i2c_xfer, .functionality = functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_adapter em28xx_adap_template = { diff --git a/linux/drivers/media/video/em28xx/em28xx-input.c b/linux/drivers/media/video/em28xx/em28xx-input.c index 29900920d..d6ef5c461 100644 --- a/linux/drivers/media/video/em28xx/em28xx-input.c +++ b/linux/drivers/media/video/em28xx/em28xx-input.c @@ -345,7 +345,7 @@ int em28xx_ir_init(struct em28xx *dev) u8 ir_config; int err = -ENOMEM; - if (dev->ir_codes == NULL) { + if (dev->board.ir_codes == NULL) { /* No remote control support */ return 0; } @@ -384,7 +384,7 @@ int em28xx_ir_init(struct em28xx *dev) 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->ir_codes); + 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; diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 98e95054e..45d588c3a 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -51,6 +51,24 @@ #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 /* */ diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 5da519bfa..ff960f90c 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -521,8 +521,8 @@ 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 (!dev->board.is_em2800) { + 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); @@ -533,9 +533,9 @@ static int em28xx_config(struct em28xx *dev) #if 1 /* enable vbi capturing */ -/* em28xx_write_regs_req(dev, 0x00, 0x0e, "\xC0", 1); audio register */ -/* em28xx_write_regs_req(dev, 0x00, 0x0f, "\x80", 1); clk register */ - em28xx_write_regs_req(dev, 0x00, 0x11, "\x51", 1); +/* em28xx_write_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); #endif dev->mute = 1; /* maybe not the right place... */ @@ -578,7 +578,7 @@ static void video_mux(struct em28xx *dev, int index) em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); - if (dev->has_msp34xx) { + if (dev->board.has_msp34xx) { if (dev->i2s_speed) { em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed); @@ -752,7 +752,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, 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; @@ -1009,7 +1009,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)); @@ -1039,7 +1039,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, return rc; mutex_lock(&dev->lock); - if (!dev->has_msp34xx) + if (!dev->board.has_msp34xx) rc = em28xx_get_ctrl(dev, ctrl); else rc = -EINVAL; @@ -1067,7 +1067,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; @@ -1758,9 +1758,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; @@ -2103,7 +2106,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, 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; + dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800; em28xx_pre_card_setup(dev); @@ -2163,9 +2166,9 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, video_set_drvdata(dev->vbi_dev, dev); #endif - if (dev->has_msp34xx) { + if (dev->board.has_msp34xx) { /* Send a reset to other chips via gpio */ - errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1); + 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); @@ -2173,7 +2176,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, } msleep(3); - errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1); + 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); @@ -2203,10 +2206,11 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, mutex_unlock(&em28xx_extension_devlist_lock); mutex_unlock(&em28xx_devlist_mutex); + /* Save some power by putting tuner to sleep */ + em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); + return 0; -fail_unreg: - em28xx_release_resources(dev); fail_reg_devices: mutex_unlock(&dev->lock); return retval; @@ -2229,7 +2233,7 @@ static void request_module_async(struct work_struct *work) else if (dev->has_alsa_audio) request_module("em28xx-alsa"); - if (dev->has_dvb) + if (dev->board.has_dvb) request_module("em28xx-dvb"); } @@ -2260,6 +2264,8 @@ static int em28xx_usb_probe(struct usb_interface *interface, 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; @@ -2270,11 +2276,12 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* 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_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; @@ -2303,8 +2310,8 @@ static int em28xx_usb_probe(struct usb_interface *interface, if (!check_interface) { em28xx_err(DRIVER_NAME " video device (%04x:%04x): " "interface %i, class %i found.\n", - udev->descriptor.idVendor, - udev->descriptor.idProduct, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), ifnum, interface->altsetting[0].desc.bInterfaceClass); @@ -2314,14 +2321,42 @@ static int em28xx_usb_probe(struct usb_interface *interface, 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"; } - 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); + 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", @@ -2356,7 +2391,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, uif = udev->actconfig->interface[0]; dev->num_alt = uif->num_altsetting; - em28xx_info("Alternate settings: %i\n", dev->num_alt); + em28xx_videodbg("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); @@ -2372,7 +2407,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, wMaxPacketSize); dev->alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - em28xx_info("Alternate setting %i, max size= %i\n", i, + em28xx_videodbg("Alternate setting %i, max size= %i\n", i, dev->alt_max_pkt_size[i]); } @@ -2388,8 +2423,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, 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); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 151668c60..4de9a8190 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -99,6 +99,7 @@ #define EM2883_BOARD_KWORLD_HYBRID_A316 57 #define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58 #define EM2874_BOARD_PINNACLE_PCTV_80E 59 +#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -161,7 +162,7 @@ #define EM2800_I2C_WRITE_TIMEOUT 20 enum em28xx_mode { - EM28XX_MODE_UNDEFINED, + EM28XX_SUSPEND, EM28XX_ANALOG_MODE, EM28XX_DIGITAL_MODE, }; @@ -305,11 +306,18 @@ enum em28xx_aout { 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]) @@ -317,14 +325,7 @@ struct em28xx_input { 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 { @@ -336,15 +337,21 @@ struct em28xx_board { /* 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]; @@ -425,35 +432,22 @@ struct em28xx { int model; /* index in the device_data struct */ int devno; /* marks the number of this device */ enum em28xx_chip_id chip_id; - unsigned int is_em2800:1; - unsigned int has_msp34xx:1; - unsigned int has_tda9887:1; + + struct em28xx_board board; + unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; unsigned int has_alsa_audio: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 */ struct em28xx_IR *ir; /* Some older em28xx chips needs a waiting time after writing */ unsigned int wait_after_write; - /* GPIO sequences for analog and digital mode */ - struct em28xx_reg_seq *analog_gpio, *digital_gpio; - - /* GPIO sequences for tuner callbacks */ - struct em28xx_reg_seq *tun_analog_gpio, *tun_digital_gpio; - - int video_inputs; /* number of video inputs */ struct list_head devlist; 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 */ @@ -536,9 +530,6 @@ struct em28xx { /* Caches GPO and GPIO registers */ unsigned char reg_gpo, reg_gpio; - /* Infrared remote control support */ - IR_KEYTAB_TYPE *ir_codes; - /* Snapshot button */ char snapshot_button_path[30]; /* path of the input dev */ struct input_dev *sbutton_input_dev; @@ -640,7 +631,7 @@ int em28xx_ir_fini(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) @@ -712,7 +703,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; @@ -720,7 +711,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/linux/drivers/media/video/ivtv/ivtv-i2c.c b/linux/drivers/media/video/ivtv/ivtv-i2c.c index c251ddb2f..fa18eae17 100644 --- a/linux/drivers/media/video/ivtv/ivtv-i2c.c +++ b/linux/drivers/media/video/ivtv/ivtv-i2c.c @@ -550,6 +550,9 @@ static u32 ivtv_functionality(struct i2c_adapter *adap) static struct i2c_algorithm ivtv_algo = { .master_xfer = ivtv_xfer, .functionality = ivtv_functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* template for our-bit banger */ diff --git a/linux/drivers/media/video/mt9m001.c b/linux/drivers/media/video/mt9m001.c index 228183c54..edacba723 100644 --- a/linux/drivers/media/video/mt9m001.c +++ b/linux/drivers/media/video/mt9m001.c @@ -285,8 +285,8 @@ static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) width_flag; } -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 +298,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,8 +325,8 @@ 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; @@ -447,8 +447,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, diff --git a/linux/drivers/media/video/mt9m111.c b/linux/drivers/media/video/mt9m111.c index ceff40edc..1fde94514 100644 --- a/linux/drivers/media/video/mt9m111.c +++ b/linux/drivers/media/video/mt9m111.c @@ -452,8 +452,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,8 +473,8 @@ 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; @@ -597,8 +597,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, diff --git a/linux/drivers/media/video/mt9v022.c b/linux/drivers/media/video/mt9v022.c index bea695a2c..1ca28c087 100644 --- a/linux/drivers/media/video/mt9v022.c +++ b/linux/drivers/media/video/mt9v022.c @@ -337,14 +337,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,8 +400,8 @@ 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; @@ -538,8 +538,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, diff --git a/linux/drivers/media/video/ov772x.c b/linux/drivers/media/video/ov772x.c index 2ddc2ab70..c023bbc84 100644 --- a/linux/drivers/media/video/ov772x.c +++ b/linux/drivers/media/video/ov772x.c @@ -112,34 +112,34 @@ #define AREF7 0x55 /* Analog reference control */ #define UFIX 0x60 /* U channel fixed value output */ #define VFIX 0x61 /* V channel fixed value output */ -#define AW_BB_BLK 0x62 /* AWB option for advanced AWB */ -#define AW_B_CTRL0 0x63 /* AWB control byte 0 */ +#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 AW_B_BIAS 0x68 /* AWB BLC level clip */ -#define AW_BCTRL1 0x69 /* AWB control 1 */ -#define AW_BCTRL2 0x6A /* AWB control 2 */ -#define AW_BCTRL3 0x6B /* AWB control 3 */ -#define AW_BCTRL4 0x6C /* AWB control 4 */ -#define AW_BCTRL5 0x6D /* AWB control 5 */ -#define AW_BCTRL6 0x6E /* AWB control 6 */ -#define AW_BCTRL7 0x6F /* AWB control 7 */ -#define AW_BCTRL8 0x70 /* AWB control 8 */ -#define AW_BCTRL9 0x71 /* AWB control 9 */ -#define AW_BCTRL10 0x72 /* AWB control 10 */ -#define AW_BCTRL11 0x73 /* AWB control 11 */ -#define AW_BCTRL12 0x74 /* AWB control 12 */ -#define AW_BCTRL13 0x75 /* AWB control 13 */ -#define AW_BCTRL14 0x76 /* AWB control 14 */ -#define AW_BCTRL15 0x77 /* AWB control 15 */ -#define AW_BCTRL16 0x78 /* AWB control 16 */ -#define AW_BCTRL17 0x79 /* AWB control 17 */ -#define AW_BCTRL18 0x7A /* AWB control 18 */ -#define AW_BCTRL19 0x7B /* AWB control 19 */ -#define AW_BCTRL20 0x7C /* AWB control 20 */ -#define AW_BCTRL21 0x7D /* AWB control 21 */ +#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 */ @@ -593,12 +593,30 @@ static int ov772x_reset(struct i2c_client *client) static int ov772x_init(struct soc_camera_device *icd) { - return 0; + 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) { - return 0; + 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) @@ -737,9 +755,9 @@ static int ov772x_set_register(struct soc_camera_device *icd, } #endif -static int ov772x_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, - struct v4l2_rect *rect) +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; @@ -760,8 +778,8 @@ static int ov772x_set_fmt_cap(struct soc_camera_device *icd, return ret; } -static int ov772x_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int ov772x_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; struct ov772x_priv *priv; @@ -814,9 +832,6 @@ static int ov772x_video_probe(struct soc_camera_device *icd) icd->formats = ov772x_fmt_lists; icd->num_formats = ARRAY_SIZE(ov772x_fmt_lists); - if (priv->info->link.power) - priv->info->link.power(&priv->client->dev, 1); - /* * check and show product ID and manufacturer ID */ @@ -824,8 +839,8 @@ static int ov772x_video_probe(struct soc_camera_device *icd) ver = i2c_smbus_read_byte_data(priv->client, VER); if (pid != 0x77 || ver != 0x21) { - if (priv->info->link.power) - priv->info->link.power(&priv->client->dev, 0); + dev_err(&icd->dev, + "Product ID error %x:%x\n", pid, ver); return -ENODEV; } @@ -842,13 +857,7 @@ static int ov772x_video_probe(struct soc_camera_device *icd) static void ov772x_video_remove(struct soc_camera_device *icd) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - soc_camera_video_stop(icd); - - if (priv->info->link.power) - priv->info->link.power(&priv->client->dev, 0); - } static struct soc_camera_ops ov772x_ops = { @@ -859,8 +868,8 @@ static struct soc_camera_ops ov772x_ops = { .release = ov772x_release, .start_capture = ov772x_start_capture, .stop_capture = ov772x_stop_capture, - .set_fmt_cap = ov772x_set_fmt_cap, - .try_fmt_cap = ov772x_try_fmt_cap, + .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, diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index edce8fc34..877c8f7b1 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -1013,6 +1013,9 @@ static int pvr2_i2c_detach_inform(struct i2c_client *client) static struct i2c_algorithm pvr2_i2c_algo_template = { .master_xfer = pvr2_i2c_xfer, .functionality = pvr2_i2c_functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_adapter pvr2_i2c_adap_template = { diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index 08723ed84..210a99780 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -770,6 +770,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; @@ -828,12 +831,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; @@ -841,7 +842,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; } @@ -867,7 +867,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: @@ -893,13 +903,14 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) 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 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; @@ -909,28 +920,174 @@ 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) +{ + 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_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 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) { - return icd->ops->set_fmt_cap(icd, pixfmt, rect); + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + const struct soc_camera_data_format *host_fmt, *cam_fmt = NULL; + const struct soc_camera_format_xlate *xlate; + 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; + + 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); + } + + if (ret < 0) + dev_warn(&ici->dev, "Failed to configure for format %x\n", + pixfmt); + + if (pixfmt && !ret) { + icd->buswidth = buswidth; + icd->current_fmt = host_fmt; + } + + return ret; } -static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +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; + + 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 (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; + 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; /* limit to sensor capabilities */ - return icd->ops->try_fmt_cap(icd, f); + return icd->ops->try_fmt(icd, f); } static int pxa_camera_reqbufs(struct soc_camera_file *icf, @@ -1038,13 +1195,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, }; diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index d4c432cb3..54be8314f 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -4645,6 +4645,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); @@ -4775,6 +4812,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, @@ -5692,6 +5735,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, @@ -5979,6 +6028,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: diff --git a/linux/drivers/media/video/saa7134/saa7134-i2c.c b/linux/drivers/media/video/saa7134/saa7134-i2c.c index df97ac987..3f7d4eb80 100644 --- a/linux/drivers/media/video/saa7134/saa7134-i2c.c +++ b/linux/drivers/media/video/saa7134/saa7134-i2c.c @@ -358,6 +358,9 @@ static int attach_inform(struct i2c_client *client) static struct i2c_algorithm saa7134_algo = { .master_xfer = saa7134_i2c_xfer, .functionality = functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_adapter saa7134_adap_template = { diff --git a/linux/drivers/media/video/saa7134/saa7134-input.c b/linux/drivers/media/video/saa7134/saa7134-input.c index 1c4bd413e..272490b92 100644 --- a/linux/drivers/media/video/saa7134/saa7134-input.c +++ b/linux/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/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index 82f9a889a..3bb599673 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -276,6 +276,7 @@ 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 32 #define SAA7134_INPUT_MAX 8 diff --git a/linux/drivers/media/video/sh_mobile_ceu_camera.c b/linux/drivers/media/video/sh_mobile_ceu_camera.c index 2fb8bee0b..e2e816213 100644 --- a/linux/drivers/media/video/sh_mobile_ceu_camera.c +++ b/linux/drivers/media/video/sh_mobile_ceu_camera.c @@ -445,15 +445,46 @@ 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 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); + const struct soc_camera_data_format *cam_fmt; + int ret; + + /* + * TODO: find a suitable supported by the SoC output format, check + * whether the sensor supports one of acceptable input formats. + */ + if (pixfmt) { + cam_fmt = soc_camera_format_by_fourcc(icd, pixfmt); + if (!cam_fmt) + return -EINVAL; + } + + ret = icd->ops->set_fmt(icd, pixfmt, rect); + if (pixfmt && !ret) + icd->current_fmt = 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) { + const struct soc_camera_data_format *cam_fmt; + int ret = sh_mobile_ceu_try_bus_param(icd, f->fmt.pix.pixelformat); + + if (ret < 0) + return ret; + + /* + * TODO: find a suitable supported by the SoC output format, check + * whether the sensor supports one of acceptable input formats. + */ + cam_fmt = soc_camera_format_by_fourcc(icd, f->fmt.pix.pixelformat); + if (!cam_fmt) + return -EINVAL; + /* FIXME: calculate using depth and bus width */ if (f->fmt.pix.height < 4) @@ -467,8 +498,12 @@ 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(cam_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); + return icd->ops->try_fmt(icd, f); } static int sh_mobile_ceu_reqbufs(struct soc_camera_file *icf, @@ -536,12 +571,11 @@ 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, + .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, }; diff --git a/linux/drivers/media/video/soc_camera.c b/linux/drivers/media/video/soc_camera.c index 7a46697c4..4a9c52c37 100644 --- a/linux/drivers/media/video/soc_camera.c +++ b/linux/drivers/media/video/soc_camera.c @@ -36,8 +36,8 @@ 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; @@ -46,52 +46,47 @@ format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc) return icd->formats + i; return NULL; } +EXPORT_SYMBOL(soc_camera_format_by_fourcc); + +const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( + struct soc_camera_device *icd, unsigned int fourcc) +{ + unsigned int i; + + 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); static int soc_camera_try_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); enum v4l2_field field; - const struct soc_camera_data_format *fmt; int ret; WARN_ON(priv != file->private_data); - 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; - } - - dev_dbg(&icd->dev, "fmt: 0x%08x\n", fmt->fourcc); - + /* + * TODO: this might also have to migrate to host-drivers, if anyone + * wishes to support other fields + */ field = f->fmt.pix.field; if (field == V4L2_FIELD_ANY) { - field = V4L2_FIELD_NONE; - } else if (V4L2_FIELD_NONE != field) { + f->fmt.pix.field = V4L2_FIELD_NONE; + } else if (field != V4L2_FIELD_NONE) { dev_err(&icd->dev, "Field type invalid.\n"); return -EINVAL; } - /* test physical bus parameters */ - ret = ici->ops->try_bus_param(icd, f->fmt.pix.pixelformat); - if (ret) - return ret; - /* limit format to hardware capabilities */ - ret = ici->ops->try_fmt_cap(icd, f); - - /* 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; + ret = ici->ops->try_fmt(icd, f); return ret; } @@ -179,6 +174,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; @@ -215,10 +263,12 @@ static int soc_camera_open(struct inode *inode, struct file *file) /* 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; } } @@ -234,6 +284,9 @@ static int soc_camera_open(struct inode *inode, struct file *file) /* All errors are entered with the video_lock held */ eiciadd: + soc_camera_free_user_formats(icd); +eiufmt: + icd->use_count--; module_put(ici->ops->owner); emgi: module_put(icd->ops->owner); @@ -252,8 +305,10 @@ static int soc_camera_close(struct inode *inode, struct file *file) mutex_lock(&video_lock); icd->use_count--; - if (!icd->use_count) + if (!icd->use_count) { ici->ops->remove(icd); + soc_camera_free_user_formats(icd); + } module_put(icd->ops->owner); module_put(ici->ops->owner); mutex_unlock(&video_lock); @@ -266,7 +321,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; @@ -311,7 +366,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, @@ -323,28 +377,20 @@ 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); + __u32 pixfmt = f->fmt.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; @@ -352,15 +398,20 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, 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) + ret = ici->ops->set_fmt(icd, f->fmt.pix.pixelformat, &rect); + if (ret < 0) { return ret; + } else if (!icd->current_fmt || + icd->current_fmt->fourcc != pixfmt) { + dev_err(&ici->dev, + "Host driver hasn't set up current format correctly!\n"); + return -EINVAL; + } - 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) + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n", f->type); @@ -368,11 +419,11 @@ 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); + return ici->ops->set_bus_param(icd, pixfmt); } 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; @@ -380,10 +431,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; @@ -391,7 +442,7 @@ 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; @@ -402,10 +453,9 @@ static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, 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; + f->fmt.pix.bytesperline = f->fmt.pix.width * + DIV_ROUND_UP(icd->current_fmt->depth, 8); + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; dev_dbg(&icd->dev, "current_fmt->fourcc: 0x%08x\n", icd->current_fmt->fourcc); return 0; @@ -575,7 +625,7 @@ static int soc_camera_s_crop(struct file *file, void *fh, if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - ret = ici->ops->set_fmt_cap(icd, 0, &a->c); + ret = ici->ops->set_fmt(icd, 0, &a->c); if (!ret) { icd->width = a->c.width; icd->height = a->c.height; @@ -941,8 +991,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"); diff --git a/linux/drivers/media/video/soc_camera_platform.c b/linux/drivers/media/video/soc_camera_platform.c index bb7a9d480..c23871e4c 100644 --- a/linux/drivers/media/video/soc_camera_platform.c +++ b/linux/drivers/media/video/soc_camera_platform.c @@ -79,14 +79,14 @@ 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); @@ -124,8 +124,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/linux/drivers/media/video/usbvision/usbvision-i2c.c b/linux/drivers/media/video/usbvision/usbvision-i2c.c index 888051b80..58f65e03a 100644 --- a/linux/drivers/media/video/usbvision/usbvision-i2c.c +++ b/linux/drivers/media/video/usbvision/usbvision-i2c.c @@ -198,6 +198,9 @@ static struct i2c_algorithm usbvision_algo = { .master_xfer = usbvision_i2c_xfer, .smbus_xfer = NULL, .functionality = functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; diff --git a/linux/drivers/media/video/w9968cf.c b/linux/drivers/media/video/w9968cf.c index ab3c054c4..5763a4234 100644 --- a/linux/drivers/media/video/w9968cf.c +++ b/linux/drivers/media/video/w9968cf.c @@ -1557,6 +1557,9 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam) static struct i2c_algorithm algo = { .smbus_xfer = w9968cf_i2c_smbus_xfer, .functionality = w9968cf_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_adapter adap = { |