diff options
author | Mauro Carvalho Chehab <mchehab@infradead.org> | 2007-10-23 15:24:06 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-01-25 19:01:05 -0200 |
commit | 215b95baf969c6f895969f0a4ae0479954fba7cd (patch) | |
tree | 67e02c71a7d306cb82221e674325a27221ce5e06 /drivers/media/video/tuner-xc2028.c | |
parent | 3b20532c791399182bd04f0fcc70b60a95637fa5 (diff) | |
download | lwn-215b95baf969c6f895969f0a4ae0479954fba7cd.tar.gz lwn-215b95baf969c6f895969f0a4ae0479954fba7cd.zip |
V4L/DVB (6430): Convert tuner-xc2028 driver to the newer hybrid approach
This changeset converts tuner-xc2028 to the newer hybrid approach. It also
prevents creating twice the xc3028 private struct by both DVB and V4L parts.
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/tuner-xc2028.c')
-rw-r--r-- | drivers/media/video/tuner-xc2028.c | 398 |
1 files changed, 205 insertions, 193 deletions
diff --git a/drivers/media/video/tuner-xc2028.c b/drivers/media/video/tuner-xc2028.c index 0e68002a4a04..e4c371896de4 100644 --- a/drivers/media/video/tuner-xc2028.c +++ b/drivers/media/video/tuner-xc2028.c @@ -13,16 +13,15 @@ #include <linux/delay.h> #include <media/tuner.h> #include <linux/mutex.h> -#include "tuner-driver.h" +#include "tuner-i2c.h" #include "tuner-xc2028.h" #include <linux/dvb/frontend.h> #include "dvb_frontend.h" -/* digital TV standards */ -#define V4L2_STD_DTV_6MHZ ((v4l2_std_id)0x04000000) -#define V4L2_STD_DTV_7MHZ ((v4l2_std_id)0x08000000) -#define V4L2_STD_DTV_8MHZ ((v4l2_std_id)0x10000000) +#define PREFIX "xc2028 " + +static LIST_HEAD(xc2028_list); /* Firmwares used on tm5600/tm6000 + xc2028/xc3028 */ @@ -40,6 +39,15 @@ static const char *firmware_DK = "tm_xc3028_DK_PAL_MTS.fw"; static const char *firmware_MN = "tm_xc3028_MN_BTSC.fw"; struct xc2028_data { + struct list_head xc2028_list; + struct tuner_i2c_props i2c_props; + int (*tuner_callback) (void *dev, + int command, int arg); + struct device *dev; + void *video_dev; + int count; + u32 frequency; + v4l2_std_id firm_type; /* video stds supported by current firmware */ fe_bandwidth_t bandwidth; /* Firmware bandwidth: @@ -48,61 +56,64 @@ struct xc2028_data { were loaded? */ enum tuner_mode mode; struct i2c_client *i2c_client; - - struct mutex lock; + + struct mutex lock; }; -#define i2c_send(rc,c,buf,size) \ -if (size != (rc = i2c_master_send(c, buf, size))) \ - tuner_warn("i2c output error: rc = %d (should be %d)\n", \ +#define i2c_send(rc, priv, buf, size) \ +if (size != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size))) \ + tuner_info("i2c output error: rc = %d (should be %d)\n", \ rc, (int)size); -#define i2c_rcv(rc,c,buf,size) \ -if (size != (rc = i2c_master_recv(c, buf, size))) \ - tuner_warn("i2c input error: rc = %d (should be %d)\n", \ +#define i2c_rcv(rc, priv, buf, size) \ +if (size != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size))) \ + tuner_info("i2c input error: rc = %d (should be %d)\n", \ rc, (int)size); -#define send_seq(c, data...) \ +#define send_seq(priv, data...) \ { int rc; \ - const static u8 _val[] = data; \ + static u8 _val[] = data; \ if (sizeof(_val) != \ - (rc = i2c_master_send \ - (c, _val, sizeof(_val)))) { \ - printk(KERN_ERR "Error on line %d: %d\n",__LINE__,rc); \ - return; \ + (rc = tuner_i2c_xfer_send (&priv->i2c_props, \ + _val, sizeof(_val)))) { \ + tuner_info("Error on line %d: %d\n",__LINE__,rc); \ + return -EINVAL; \ } \ msleep (10); \ } -static int xc2028_get_reg(struct i2c_client *c, u16 reg) +static int xc2028_get_reg(struct xc2028_data *priv, u16 reg) { int rc; unsigned char buf[1]; - struct tuner *t = i2c_get_clientdata(c); + + tuner_info("%s called\n", __FUNCTION__); buf[0]= reg; - i2c_send(rc, c, buf, sizeof(buf)); + i2c_send(rc, priv, buf, sizeof(buf)); if (rc<0) return rc; - i2c_rcv(rc, c, buf, 2); + i2c_rcv(rc, priv, buf, 2); if (rc<0) return rc; return (buf[1])|(buf[0]<<8); } -static int load_firmware (struct i2c_client *c, const char *name) +static int load_firmware (struct dvb_frontend *fe, const char *name) { + struct xc2028_data *priv = fe->tuner_priv; const struct firmware *fw=NULL; - struct tuner *t = i2c_get_clientdata(c); unsigned char *p, *endp; int len=0, rc=0; static const char firmware_ver[] = "tm6000/xcv v1"; - tuner_info("xc2028: Loading firmware %s\n", name); - rc = request_firmware(&fw, name, &c->dev); + tuner_info("%s called\n", __FUNCTION__); + + tuner_info("Loading firmware %s\n", name); + rc = request_firmware(&fw, name, priv->dev); if (rc < 0) { if (rc==-ENOENT) tuner_info("Error: firmware %s not found.\n", name); @@ -138,7 +149,7 @@ static int load_firmware (struct i2c_client *c, const char *name) while(p<endp) { if ((*p) & 0x80) { /* Special callback command received */ - rc = t->tuner_callback(c->adapter->algo_data, + rc = priv->tuner_callback(priv->video_dev, XC2028_TUNER_RESET, (*p)&0x7f); if (rc<0) { tuner_info("Error at RESET code %d\n", @@ -162,7 +173,7 @@ static int load_firmware (struct i2c_client *c, const char *name) goto err; } - i2c_send(rc, c, p, len); + i2c_send(rc, priv, p, len); if (rc<0) goto err; p+=len; @@ -179,171 +190,173 @@ err: return rc; } -static int check_firmware(struct i2c_client *c, enum tuner_mode new_mode, +static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode, + v4l2_std_id std, fe_bandwidth_t bandwidth) { + struct xc2028_data *priv = fe->tuner_priv; int rc, version; - struct tuner *t = i2c_get_clientdata(c); - struct xc2028_data *xc2028 = t->priv; const char *name; int change_digital_bandwidth; - if (!t->tuner_callback) { - printk(KERN_ERR "xc2028: need tuner_callback to load firmware\n"); - return -EINVAL; - } + tuner_info("%s called\n", __FUNCTION__); - printk(KERN_INFO "xc2028: I am in mode %u and I should switch to mode %i\n", - xc2028->mode, new_mode); + tuner_info( "I am in mode %u and I should switch to mode %i\n", + priv->mode, new_mode); /* first of all, determine whether we have switched the mode */ - if(new_mode != xc2028->mode) { - xc2028->mode = new_mode; - xc2028->need_load_generic = 1; + if(new_mode != priv->mode) { + priv->mode = new_mode; + priv->need_load_generic = 1; } - change_digital_bandwidth = (xc2028->mode == T_DIGITAL_TV - && bandwidth != xc2028->bandwidth) ? 1 : 0; - tuner_info("xc2028: old bandwidth %u, new bandwidth %u\n", xc2028->bandwidth, + change_digital_bandwidth = (priv->mode == T_DIGITAL_TV + && bandwidth != priv->bandwidth) ? 1 : 0; + tuner_info("old bandwidth %u, new bandwidth %u\n", priv->bandwidth, bandwidth); - if (xc2028->need_load_generic) { - if (xc2028->bandwidth==8) + if (priv->need_load_generic) { + if (priv->bandwidth==8) name = firmware_8MHZ_INIT0; else name = firmware_INIT0; /* Reset is needed before loading firmware */ - rc = t->tuner_callback(c->adapter->algo_data, - XC2028_TUNER_RESET, 0); + rc = priv->tuner_callback(priv->video_dev, + XC2028_TUNER_RESET, 0); if (rc<0) return rc; - rc = load_firmware(c,name); + rc = load_firmware(fe,name); if (rc<0) return rc; - xc2028->need_load_generic=0; - xc2028->firm_type=0; - if(xc2028->mode == T_DIGITAL_TV) { + priv->need_load_generic=0; + priv->firm_type=0; + if(priv->mode == T_DIGITAL_TV) { change_digital_bandwidth=1; } } - tuner_info("xc2028: I should change bandwidth %u\n", + tuner_info("I should change bandwidth %u\n", change_digital_bandwidth); + /* FIXME: t->std makes no sense here */ if (change_digital_bandwidth) { switch(bandwidth) { case BANDWIDTH_8_MHZ: - t->std = V4L2_STD_DTV_8MHZ; + std = V4L2_STD_DTV_8MHZ; break; case BANDWIDTH_7_MHZ: - t->std = V4L2_STD_DTV_7MHZ; + std = V4L2_STD_DTV_7MHZ; break; case BANDWIDTH_6_MHZ: - t->std = V4L2_STD_DTV_6MHZ; + std = V4L2_STD_DTV_6MHZ; break; default: tuner_info("error: bandwidth not supported.\n"); }; - xc2028->bandwidth = bandwidth; + priv->bandwidth = bandwidth; } - if (xc2028->firm_type & t->std) { + if (priv->firm_type & std) { tuner_info("xc3028: no need to load a std-specific firmware.\n"); return 0; } - rc = load_firmware(c,firmware_INIT1); + rc = load_firmware(fe,firmware_INIT1); - if (t->std & V4L2_STD_MN) + if (std & V4L2_STD_MN) name=firmware_MN; - else if (t->std & V4L2_STD_DTV_6MHZ) + else if (std & V4L2_STD_DTV_6MHZ) name=firmware_6M; - else if (t->std & V4L2_STD_DTV_7MHZ) + else if (std & V4L2_STD_DTV_7MHZ) name=firmware_7M; - else if (t->std & V4L2_STD_DTV_8MHZ) + else if (std & V4L2_STD_DTV_8MHZ) name=firmware_8M; - else if (t->std & V4L2_STD_PAL_B) + else if (std & V4L2_STD_PAL_B) name=firmware_B; else name=firmware_DK; - tuner_info("xc2028: loading firmware named %s.\n", name); - rc = load_firmware(c, name); + tuner_info("loading firmware named %s.\n", name); + rc = load_firmware(fe, name); if (rc<0) return rc; - version = xc2028_get_reg(c, 0x4); + version = xc2028_get_reg(priv, 0x4); tuner_info("Firmware version is %d.%d\n", (version>>4)&0x0f,(version)&0x0f); - xc2028->firm_type=t->std; + priv->firm_type=std; return 0; } -static int xc2028_signal(struct i2c_client *c) +static int xc2028_signal(struct dvb_frontend *fe, u16 *strength) { - struct tuner *t = i2c_get_clientdata(c); - struct xc2028_data *xc2028 = t->priv; + struct xc2028_data *priv = fe->tuner_priv; int frq_lock, signal=0; - mutex_lock(&xc2028->lock); + tuner_info("%s called\n", __FUNCTION__); - printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__); + mutex_lock(&priv->lock); - frq_lock = xc2028_get_reg(c, 0x2); + *strength = 0; + + frq_lock = xc2028_get_reg(priv, 0x2); if (frq_lock<=0) goto ret; /* Frequency is locked. Return signal quality */ - signal = xc2028_get_reg(c, 0x40); + signal = xc2028_get_reg(priv, 0x40); if(signal<=0) { signal=frq_lock; } ret: - mutex_unlock(&xc2028->lock); + mutex_unlock(&priv->lock); + + *strength = signal; - return signal; + return 0; } #define DIV 15625 -static void generic_set_tv_freq(struct i2c_client *c, u32 freq /* in Hz */, - enum tuner_mode new_mode, fe_bandwidth_t bandwidth) +static int generic_set_tv_freq(struct dvb_frontend *fe, u32 freq /* in Hz */, + enum tuner_mode new_mode, + v4l2_std_id std, + fe_bandwidth_t bandwidth) { - int rc; + struct xc2028_data *priv = fe->tuner_priv; + int rc=-EINVAL; unsigned char buf[5]; - struct tuner *t = i2c_get_clientdata(c); u32 div, offset = 0; + tuner_info("%s called\n", __FUNCTION__); + /* HACK: It seems that specific firmware need to be reloaded when freq is changed */ - struct xc2028_data *xc2028 = t->priv; - mutex_lock(&xc2028->lock); + mutex_lock(&priv->lock); - xc2028->firm_type=0; + priv->firm_type=0; /* Reset GPIO 1 */ - if (t->tuner_callback) { - rc = t->tuner_callback( c->adapter->algo_data, - XC2028_TUNER_RESET, 0); - if (rc<0) - goto ret; - } + rc = priv->tuner_callback(priv->video_dev, XC2028_TUNER_RESET, 0); + if (rc<0) + goto ret; + msleep(10); - printk("xc3028: should set frequency %d kHz)\n", freq / 1000); + tuner_info("should set frequency %d kHz)\n", freq / 1000); - if (check_firmware(c, new_mode, bandwidth)<0) + if (check_firmware(fe, new_mode, std, bandwidth)<0) goto ret; if(new_mode == T_DIGITAL_TV) @@ -352,13 +365,10 @@ static void generic_set_tv_freq(struct i2c_client *c, u32 freq /* in Hz */, div = (freq - offset + DIV/2)/DIV; /* CMD= Set frequency */ - send_seq(c, {0x00, 0x02, 0x00, 0x00}); - if (t->tuner_callback) { - rc = t->tuner_callback( c->adapter->algo_data, - XC2028_RESET_CLK, 1); - if (rc<0) - goto ret; - } + send_seq(priv, {0x00, 0x02, 0x00, 0x00}); + rc = priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1); + if (rc<0) + goto ret; msleep(10); @@ -368,121 +378,82 @@ static void generic_set_tv_freq(struct i2c_client *c, u32 freq /* in Hz */, buf[3]= 0xff & (div); buf[4]= 0; - i2c_send(rc, c, buf, sizeof(buf)); + i2c_send(rc, priv, buf, sizeof(buf)); if (rc<0) goto ret; msleep(100); + priv->frequency=freq; + printk("divider= %02x %02x %02x %02x (freq=%d.%02d)\n", buf[1],buf[2],buf[3],buf[4], - freq / 16, freq % 16 * 100 / 16); + freq / 1000000, (freq%1000000)/10000); -ret: - mutex_unlock(&xc2028->lock); -} + rc=0; +ret: + mutex_unlock(&priv->lock); -static void set_tv_freq(struct i2c_client *c, unsigned int freq) -{ - printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__); - - generic_set_tv_freq(c, freq * 62500l, T_ANALOG_TV, - BANDWIDTH_8_MHZ /* unimportant */); + return rc; } -static void xc2028_release(struct i2c_client *c) +static int xc2028_set_tv_freq(struct dvb_frontend *fe, + struct analog_parameters *p) { - struct tuner *t = i2c_get_clientdata(c); + struct xc2028_data *priv = fe->tuner_priv; - kfree(t->priv); - t->priv = NULL; -} - -static struct tuner_operations tea5767_tuner_ops = { - .set_tv_freq = set_tv_freq, - .has_signal = xc2028_signal, - .release = xc2028_release, -// .is_stereo = xc2028_stereo, -}; + tuner_info("%s called\n", __FUNCTION__); + return generic_set_tv_freq(fe, 62500l*p->frequency, T_ANALOG_TV, + p->std, + BANDWIDTH_8_MHZ /* NOT USED */); +} -static int init=0; - -int xc2028_tuner_init(struct i2c_client *c) +static int xc2028_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) { - struct tuner *t = i2c_get_clientdata(c); - int version = xc2028_get_reg(c, 0x4); - int prd_id = xc2028_get_reg(c, 0x8); - struct xc2028_data *xc2028; + struct xc2028_data *priv = fe->tuner_priv; - tuner_info("Xcv2028/3028 init called!\n"); + tuner_info("%s called\n", __FUNCTION__); - if (init) { - printk (KERN_ERR "Module already initialized!\n"); - return 0; + /* FIXME: Only OFDM implemented */ + if (fe->ops.info.type != FE_OFDM) { + tuner_info ("DTV type not implemented.\n"); + return -EINVAL; } - init++; - - xc2028 = kzalloc(sizeof(*xc2028), GFP_KERNEL); - if (!xc2028) - return -ENOMEM; - t->priv = xc2028; - xc2028->bandwidth=BANDWIDTH_6_MHZ; - xc2028->need_load_generic=1; - xc2028->mode = T_UNINITIALIZED; + return generic_set_tv_freq(fe, p->frequency, T_DIGITAL_TV, + 0, /* NOT USED */ + p->u.ofdm.bandwidth); - mutex_init(&xc2028->lock); - - /* FIXME: Check where t->priv will be freed */ - - if (version<0) - version=0; - - if (prd_id<0) - prd_id=0; - - strlcpy(c->name, "xc2028", sizeof(c->name)); - tuner_info("type set to %d (%s, hw ver=%d.%d, fw ver=%d.%d, id=0x%04x)\n", - t->type, c->name, - (version>>12)&0x0f,(version>>8)&0x0f, - (version>>4)&0x0f,(version)&0x0f, prd_id); - - memcpy(&t->ops, &tea5767_tuner_ops, sizeof(struct tuner_operations)); - - return 0; } -static int xc3028_set_params(struct dvb_frontend *fe, - struct dvb_frontend_parameters *p) +static int xc2028_dvb_release(struct dvb_frontend *fe) { - struct i2c_client *c = fe->tuner_priv; + struct xc2028_data *priv = fe->tuner_priv; + + tuner_info("%s called\n", __FUNCTION__); - printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__); + priv->count--; - generic_set_tv_freq(c, p->frequency, T_DIGITAL_TV, - p->u.ofdm.bandwidth); + if (!priv->count) + kfree (priv); return 0; } -static int xc3028_dvb_release(struct dvb_frontend *fe) +static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency) { - printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__); + struct xc2028_data *priv = fe->tuner_priv; - fe->tuner_priv = NULL; + tuner_info("%s called\n", __FUNCTION__); - return 0; -} - -static int xc3028_dvb_init(struct dvb_frontend *fe) -{ - printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__); + *frequency = priv->frequency; return 0; } -static const struct dvb_tuner_ops xc3028_dvb_tuner_ops = { +static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = { .info = { .name = "Xceive XC3028", .frequency_min = 42000000, @@ -490,33 +461,74 @@ static const struct dvb_tuner_ops xc3028_dvb_tuner_ops = { .frequency_step = 50000, }, - .release = xc3028_dvb_release, - .init = xc3028_dvb_init, + .set_analog_params = xc2028_set_tv_freq, + .release = xc2028_dvb_release, + .get_frequency = xc2028_get_frequency, + .get_rf_strength = xc2028_signal, + .set_params = xc2028_set_params, // int (*sleep)(struct dvb_frontend *fe); - - /** This is for simple PLLs - set all parameters in one go. */ - .set_params = xc3028_set_params, - - /** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */ -// int (*calc_regs)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p, u8 *buf, int buf_len); - -// int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency); // int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); - // int (*get_status)(struct dvb_frontend *fe, u32 *status); }; -int xc2028_attach(struct i2c_client *c, struct dvb_frontend *fe) +int xc2028_attach(struct dvb_frontend *fe, struct i2c_adapter* i2c_adap, + u8 i2c_addr, struct device *dev, void *video_dev, + int (*tuner_callback) (void *dev, int command,int arg)) { - fe->tuner_priv = c; + struct xc2028_data *priv; - memcpy(&fe->ops.tuner_ops, &xc3028_dvb_tuner_ops, sizeof(fe->ops.tuner_ops)); + printk( KERN_INFO PREFIX "Xcv2028/3028 init called!\n"); - return 0; -} + if (NULL == dev) + return -ENODEV; + + if (NULL == video_dev) + return -ENODEV; + + if (!tuner_callback) { + printk( KERN_ERR PREFIX "No tuner callback!\n"); + return -EINVAL; + } + + list_for_each_entry(priv, &xc2028_list, xc2028_list) { + if (priv->dev == dev) { + dev = NULL; + priv->count++; + } + } + + if (dev) { + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + fe->tuner_priv = priv; + priv->bandwidth=BANDWIDTH_6_MHZ; + priv->need_load_generic=1; + priv->mode = T_UNINITIALIZED; + priv->i2c_props.addr = i2c_addr; + priv->i2c_props.adap = i2c_adap; + priv->dev = dev; + priv->video_dev = video_dev; + priv->tuner_callback = tuner_callback; + + mutex_init(&priv->lock); + + list_add_tail(&priv->xc2028_list,&xc2028_list); + } + + memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops, + sizeof(xc2028_dvb_tuner_ops)); + + tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner"); + + return 0; +} EXPORT_SYMBOL(xc2028_attach); +MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); +MODULE_LICENSE("GPL"); |