Commit c663d035 authored by Michael Krufky's avatar Michael Krufky Committed by Mauro Carvalho Chehab

V4L/DVB (7944): tuner-xc2028: use hybrid_tuner_request_state

Use a standard method to manage multiple instances of a hybrid tuner.
Signed-off-by: default avatarMichael Krufky <mkrufky@linuxtv.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 9adea1c0
...@@ -46,7 +46,7 @@ module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0); ...@@ -46,7 +46,7 @@ module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);
MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the " MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the "
"default firmware name\n"); "default firmware name\n");
static LIST_HEAD(xc2028_list); static LIST_HEAD(hybrid_tuner_instance_list);
static DEFINE_MUTEX(xc2028_list_mutex); static DEFINE_MUTEX(xc2028_list_mutex);
/* struct for storing firmware table */ /* struct for storing firmware table */
...@@ -68,12 +68,11 @@ struct firmware_properties { ...@@ -68,12 +68,11 @@ struct firmware_properties {
}; };
struct xc2028_data { struct xc2028_data {
struct list_head xc2028_list; struct list_head hybrid_tuner_instance_list;
struct tuner_i2c_props i2c_props; struct tuner_i2c_props i2c_props;
int (*tuner_callback) (void *dev, int (*tuner_callback) (void *dev,
int command, int arg); int command, int arg);
void *video_dev; void *video_dev;
int count;
__u32 frequency; __u32 frequency;
struct firmware_description *firm; struct firmware_description *firm;
...@@ -1072,20 +1071,19 @@ static int xc2028_dvb_release(struct dvb_frontend *fe) ...@@ -1072,20 +1071,19 @@ static int xc2028_dvb_release(struct dvb_frontend *fe)
mutex_lock(&xc2028_list_mutex); mutex_lock(&xc2028_list_mutex);
priv->count--; /* only perform final cleanup if this is the last instance */
if (hybrid_tuner_report_instance_count(priv) == 1) {
if (!priv->count) {
list_del(&priv->xc2028_list);
kfree(priv->ctrl.fname); kfree(priv->ctrl.fname);
free_firmware(priv); free_firmware(priv);
kfree(priv);
fe->tuner_priv = NULL;
} }
if (priv)
hybrid_tuner_release_state(priv);
mutex_unlock(&xc2028_list_mutex); mutex_unlock(&xc2028_list_mutex);
fe->tuner_priv = NULL;
return 0; return 0;
} }
...@@ -1150,7 +1148,7 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, ...@@ -1150,7 +1148,7 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
struct xc2028_config *cfg) struct xc2028_config *cfg)
{ {
struct xc2028_data *priv; struct xc2028_data *priv;
void *video_dev; int instance;
if (debug) if (debug)
printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n"); printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n");
...@@ -1163,48 +1161,40 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, ...@@ -1163,48 +1161,40 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
return NULL; return NULL;
} }
video_dev = cfg->i2c_adap->algo_data;
if (debug)
printk(KERN_DEBUG "xc2028: video_dev =%p\n", video_dev);
mutex_lock(&xc2028_list_mutex); mutex_lock(&xc2028_list_mutex);
list_for_each_entry(priv, &xc2028_list, xc2028_list) { instance = hybrid_tuner_request_state(struct xc2028_data, priv,
if (&priv->i2c_props.adap->dev == &cfg->i2c_adap->dev) { hybrid_tuner_instance_list,
video_dev = NULL; cfg->i2c_adap, cfg->i2c_addr,
if (debug) "xc2028");
printk(KERN_DEBUG "xc2028: reusing device\n"); switch (instance) {
case 0:
break; /* memory allocation failure */
} goto fail;
} break;
case 1:
if (video_dev) { /* new tuner instance */
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (priv == NULL) {
mutex_unlock(&xc2028_list_mutex);
return NULL;
}
priv->i2c_props.addr = cfg->i2c_addr;
priv->i2c_props.adap = cfg->i2c_adap;
priv->i2c_props.name = "xc2028";
priv->video_dev = video_dev;
priv->tuner_callback = cfg->callback; priv->tuner_callback = cfg->callback;
priv->ctrl.max_len = 13; priv->ctrl.max_len = 13;
mutex_init(&priv->lock); mutex_init(&priv->lock);
list_add_tail(&priv->xc2028_list, &xc2028_list); /* analog side (tuner-core) uses i2c_adap->algo_data.
} * digital side is not guaranteed to have algo_data defined.
*
fe->tuner_priv = priv; * digital side will always have fe->dvb defined.
priv->count++; * analog side (tuner-core) doesn't (yet) define fe->dvb.
*/
priv->video_dev = ((fe->dvb) && (fe->dvb->priv)) ?
fe->dvb->priv : cfg->i2c_adap->algo_data;
if (debug) fe->tuner_priv = priv;
printk(KERN_DEBUG "xc2028: usage count is %i\n", priv->count); break;
case 2:
/* existing tuner instance */
fe->tuner_priv = priv;
break;
}
memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops, memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
sizeof(xc2028_dvb_tuner_ops)); sizeof(xc2028_dvb_tuner_ops));
...@@ -1217,6 +1207,11 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, ...@@ -1217,6 +1207,11 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
mutex_unlock(&xc2028_list_mutex); mutex_unlock(&xc2028_list_mutex);
return fe; return fe;
fail:
mutex_unlock(&xc2028_list_mutex);
xc2028_dvb_release(fe);
return NULL;
} }
EXPORT_SYMBOL(xc2028_attach); EXPORT_SYMBOL(xc2028_attach);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment