aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/common/tuners/tuner-xc2028.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/common/tuners/tuner-xc2028.c')
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.c87
1 files changed, 41 insertions, 46 deletions
diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
index 9e9003cffc7f..0cbde17bfbb7 100644
--- a/drivers/media/common/tuners/tuner-xc2028.c
+++ b/drivers/media/common/tuners/tuner-xc2028.c
@@ -46,7 +46,7 @@ module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);
46MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the " 46MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the "
47 "default firmware name\n"); 47 "default firmware name\n");
48 48
49static LIST_HEAD(xc2028_list); 49static LIST_HEAD(hybrid_tuner_instance_list);
50static DEFINE_MUTEX(xc2028_list_mutex); 50static DEFINE_MUTEX(xc2028_list_mutex);
51 51
52/* struct for storing firmware table */ 52/* struct for storing firmware table */
@@ -68,12 +68,11 @@ struct firmware_properties {
68}; 68};
69 69
70struct xc2028_data { 70struct xc2028_data {
71 struct list_head xc2028_list; 71 struct list_head hybrid_tuner_instance_list;
72 struct tuner_i2c_props i2c_props; 72 struct tuner_i2c_props i2c_props;
73 int (*tuner_callback) (void *dev, 73 int (*tuner_callback) (void *dev,
74 int command, int arg); 74 int command, int arg);
75 void *video_dev; 75 void *video_dev;
76 int count;
77 __u32 frequency; 76 __u32 frequency;
78 77
79 struct firmware_description *firm; 78 struct firmware_description *firm;
@@ -1072,20 +1071,19 @@ static int xc2028_dvb_release(struct dvb_frontend *fe)
1072 1071
1073 mutex_lock(&xc2028_list_mutex); 1072 mutex_lock(&xc2028_list_mutex);
1074 1073
1075 priv->count--; 1074 /* only perform final cleanup if this is the last instance */
1076 1075 if (hybrid_tuner_report_instance_count(priv) == 1) {
1077 if (!priv->count) {
1078 list_del(&priv->xc2028_list);
1079
1080 kfree(priv->ctrl.fname); 1076 kfree(priv->ctrl.fname);
1081
1082 free_firmware(priv); 1077 free_firmware(priv);
1083 kfree(priv);
1084 fe->tuner_priv = NULL;
1085 } 1078 }
1086 1079
1080 if (priv)
1081 hybrid_tuner_release_state(priv);
1082
1087 mutex_unlock(&xc2028_list_mutex); 1083 mutex_unlock(&xc2028_list_mutex);
1088 1084
1085 fe->tuner_priv = NULL;
1086
1089 return 0; 1087 return 0;
1090} 1088}
1091 1089
@@ -1150,7 +1148,7 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
1150 struct xc2028_config *cfg) 1148 struct xc2028_config *cfg)
1151{ 1149{
1152 struct xc2028_data *priv; 1150 struct xc2028_data *priv;
1153 void *video_dev; 1151 int instance;
1154 1152
1155 if (debug) 1153 if (debug)
1156 printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n"); 1154 printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n");
@@ -1163,48 +1161,40 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
1163 return NULL; 1161 return NULL;
1164 } 1162 }
1165 1163
1166 video_dev = cfg->i2c_adap->algo_data;
1167
1168 if (debug)
1169 printk(KERN_DEBUG "xc2028: video_dev =%p\n", video_dev);
1170
1171 mutex_lock(&xc2028_list_mutex); 1164 mutex_lock(&xc2028_list_mutex);
1172 1165
1173 list_for_each_entry(priv, &xc2028_list, xc2028_list) { 1166 instance = hybrid_tuner_request_state(struct xc2028_data, priv,
1174 if (&priv->i2c_props.adap->dev == &cfg->i2c_adap->dev) { 1167 hybrid_tuner_instance_list,
1175 video_dev = NULL; 1168 cfg->i2c_adap, cfg->i2c_addr,
1176 if (debug) 1169 "xc2028");
1177 printk(KERN_DEBUG "xc2028: reusing device\n"); 1170 switch (instance) {
1178 1171 case 0:
1179 break; 1172 /* memory allocation failure */
1180 } 1173 goto fail;
1181 } 1174 break;
1182 1175 case 1:
1183 if (video_dev) { 1176 /* new tuner instance */
1184 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
1185 if (priv == NULL) {
1186 mutex_unlock(&xc2028_list_mutex);
1187 return NULL;
1188 }
1189
1190 priv->i2c_props.addr = cfg->i2c_addr;
1191 priv->i2c_props.adap = cfg->i2c_adap;
1192 priv->i2c_props.name = "xc2028";
1193
1194 priv->video_dev = video_dev;
1195 priv->tuner_callback = cfg->callback; 1177 priv->tuner_callback = cfg->callback;
1196 priv->ctrl.max_len = 13; 1178 priv->ctrl.max_len = 13;
1197 1179
1198 mutex_init(&priv->lock); 1180 mutex_init(&priv->lock);
1199 1181
1200 list_add_tail(&priv->xc2028_list, &xc2028_list); 1182 /* analog side (tuner-core) uses i2c_adap->algo_data.
1201 } 1183 * digital side is not guaranteed to have algo_data defined.
1202 1184 *
1203 fe->tuner_priv = priv; 1185 * digital side will always have fe->dvb defined.
1204 priv->count++; 1186 * analog side (tuner-core) doesn't (yet) define fe->dvb.
1187 */
1188 priv->video_dev = ((fe->dvb) && (fe->dvb->priv)) ?
1189 fe->dvb->priv : cfg->i2c_adap->algo_data;
1205 1190
1206 if (debug) 1191 fe->tuner_priv = priv;
1207 printk(KERN_DEBUG "xc2028: usage count is %i\n", priv->count); 1192 break;
1193 case 2:
1194 /* existing tuner instance */
1195 fe->tuner_priv = priv;
1196 break;
1197 }
1208 1198
1209 memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops, 1199 memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
1210 sizeof(xc2028_dvb_tuner_ops)); 1200 sizeof(xc2028_dvb_tuner_ops));
@@ -1217,6 +1207,11 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
1217 mutex_unlock(&xc2028_list_mutex); 1207 mutex_unlock(&xc2028_list_mutex);
1218 1208
1219 return fe; 1209 return fe;
1210fail:
1211 mutex_unlock(&xc2028_list_mutex);
1212
1213 xc2028_dvb_release(fe);
1214 return NULL;
1220} 1215}
1221 1216
1222EXPORT_SYMBOL(xc2028_attach); 1217EXPORT_SYMBOL(xc2028_attach);