|
@@ -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 "
|
|
|
"default firmware name\n");
|
|
|
|
|
|
-static LIST_HEAD(xc2028_list);
|
|
|
+static LIST_HEAD(hybrid_tuner_instance_list);
|
|
|
static DEFINE_MUTEX(xc2028_list_mutex);
|
|
|
|
|
|
/* struct for storing firmware table */
|
|
@@ -68,12 +68,11 @@ struct firmware_properties {
|
|
|
};
|
|
|
|
|
|
struct xc2028_data {
|
|
|
- struct list_head xc2028_list;
|
|
|
+ struct list_head hybrid_tuner_instance_list;
|
|
|
struct tuner_i2c_props i2c_props;
|
|
|
int (*tuner_callback) (void *dev,
|
|
|
int command, int arg);
|
|
|
void *video_dev;
|
|
|
- int count;
|
|
|
__u32 frequency;
|
|
|
|
|
|
struct firmware_description *firm;
|
|
@@ -1072,20 +1071,19 @@ static int xc2028_dvb_release(struct dvb_frontend *fe)
|
|
|
|
|
|
mutex_lock(&xc2028_list_mutex);
|
|
|
|
|
|
- priv->count--;
|
|
|
-
|
|
|
- if (!priv->count) {
|
|
|
- list_del(&priv->xc2028_list);
|
|
|
-
|
|
|
+ /* only perform final cleanup if this is the last instance */
|
|
|
+ if (hybrid_tuner_report_instance_count(priv) == 1) {
|
|
|
kfree(priv->ctrl.fname);
|
|
|
-
|
|
|
free_firmware(priv);
|
|
|
- kfree(priv);
|
|
|
- fe->tuner_priv = NULL;
|
|
|
}
|
|
|
|
|
|
+ if (priv)
|
|
|
+ hybrid_tuner_release_state(priv);
|
|
|
+
|
|
|
mutex_unlock(&xc2028_list_mutex);
|
|
|
|
|
|
+ fe->tuner_priv = NULL;
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1150,7 +1148,7 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
|
|
|
struct xc2028_config *cfg)
|
|
|
{
|
|
|
struct xc2028_data *priv;
|
|
|
- void *video_dev;
|
|
|
+ int instance;
|
|
|
|
|
|
if (debug)
|
|
|
printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n");
|
|
@@ -1163,48 +1161,40 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
|
|
|
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);
|
|
|
|
|
|
- list_for_each_entry(priv, &xc2028_list, xc2028_list) {
|
|
|
- if (&priv->i2c_props.adap->dev == &cfg->i2c_adap->dev) {
|
|
|
- video_dev = NULL;
|
|
|
- if (debug)
|
|
|
- printk(KERN_DEBUG "xc2028: reusing device\n");
|
|
|
-
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (video_dev) {
|
|
|
- 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;
|
|
|
+ instance = hybrid_tuner_request_state(struct xc2028_data, priv,
|
|
|
+ hybrid_tuner_instance_list,
|
|
|
+ cfg->i2c_adap, cfg->i2c_addr,
|
|
|
+ "xc2028");
|
|
|
+ switch (instance) {
|
|
|
+ case 0:
|
|
|
+ /* memory allocation failure */
|
|
|
+ goto fail;
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ /* new tuner instance */
|
|
|
priv->tuner_callback = cfg->callback;
|
|
|
priv->ctrl.max_len = 13;
|
|
|
|
|
|
mutex_init(&priv->lock);
|
|
|
|
|
|
- list_add_tail(&priv->xc2028_list, &xc2028_list);
|
|
|
- }
|
|
|
-
|
|
|
- fe->tuner_priv = priv;
|
|
|
- priv->count++;
|
|
|
+ /* analog side (tuner-core) uses i2c_adap->algo_data.
|
|
|
+ * digital side is not guaranteed to have algo_data defined.
|
|
|
+ *
|
|
|
+ * digital side will always have fe->dvb defined.
|
|
|
+ * 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)
|
|
|
- printk(KERN_DEBUG "xc2028: usage count is %i\n", priv->count);
|
|
|
+ fe->tuner_priv = priv;
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ /* existing tuner instance */
|
|
|
+ fe->tuner_priv = priv;
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
|
|
|
sizeof(xc2028_dvb_tuner_ops));
|
|
@@ -1217,6 +1207,11 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
|
|
|
mutex_unlock(&xc2028_list_mutex);
|
|
|
|
|
|
return fe;
|
|
|
+fail:
|
|
|
+ mutex_unlock(&xc2028_list_mutex);
|
|
|
+
|
|
|
+ xc2028_dvb_release(fe);
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
EXPORT_SYMBOL(xc2028_attach);
|