aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/video
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video')
-rw-r--r--drivers/media/video/tuner-core.c65
-rw-r--r--drivers/media/video/tuner-driver.h1
2 files changed, 55 insertions, 11 deletions
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 3ec50dbed74..4f2bd2dd15c 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -585,8 +585,38 @@ static void tuner_status(struct dvb_frontend *fe)
/* ---------------------------------------------------------------------- */
-/* static vars: used only in tuner_attach and tuner_probe */
-static unsigned default_mode_mask;
+LIST_HEAD(tuner_list);
+
+/* Search for existing radio and/or TV tuners on the given I2C adapter.
+ Note that when this function is called from tuner_attach you can be
+ certain no other devices will be added/deleted at the same time, I2C
+ core protects against that. */
+static void tuner_lookup(struct i2c_adapter *adap,
+ struct tuner **radio, struct tuner **tv)
+{
+ struct tuner *pos;
+
+ *radio = NULL;
+ *tv = NULL;
+
+ list_for_each_entry(pos, &tuner_list, list) {
+ int mode_mask;
+
+ if (pos->i2c->adapter != adap ||
+ pos->i2c->driver->id != I2C_DRIVERID_TUNER)
+ continue;
+
+ mode_mask = pos->mode_mask & ~T_STANDBY;
+ if (*radio == NULL && mode_mask == T_RADIO)
+ *radio = pos;
+ /* Note: currently TDA9887 is the only demod-only
+ device. If other devices appear then we need to
+ make this test more general. */
+ else if (*tv == NULL && pos->type != TUNER_TDA9887 &&
+ (pos->mode_mask & (T_ANALOG_TV | T_DIGITAL_TV)))
+ *tv = pos;
+ }
+}
/* During client attach, set_type is called by adapter's attach_inform callback.
set_type must then be completed by tuner_attach.
@@ -595,6 +625,8 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
{
struct i2c_client *client;
struct tuner *t;
+ struct tuner *radio;
+ struct tuner *tv;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (NULL == client)
@@ -638,7 +670,9 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
t->mode_mask = T_RADIO;
t->mode = T_STANDBY;
t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
- default_mode_mask &= ~T_RADIO;
+ tuner_lookup(t->i2c->adapter, &radio, &tv);
+ if (tv)
+ tv->mode_mask &= ~T_RADIO;
goto register_client;
}
@@ -665,7 +699,9 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
t->mode_mask = T_RADIO;
t->mode = T_STANDBY;
t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
- default_mode_mask &= ~T_RADIO;
+ tuner_lookup(t->i2c->adapter, &radio, &tv);
+ if (tv)
+ tv->mode_mask &= ~T_RADIO;
goto register_client;
}
@@ -673,13 +709,21 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
}
}
- /* Initializes only the first adapter found */
- if (default_mode_mask != T_UNINITIALIZED) {
- tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
- t->mode_mask = default_mode_mask;
+ /* Initializes only the first TV tuner on this adapter. Why only the
+ first? Because there are some devices (notably the ones with TI
+ tuners) that have more than one i2c address for the *same* device.
+ Experience shows that, except for just one case, the first
+ address is the right one. The exception is a Russian tuner
+ (ACORP_Y878F). So, the desired behavior is just to enable the
+ first found TV tuner. */
+ tuner_lookup(t->i2c->adapter, &radio, &tv);
+ if (tv == NULL) {
+ t->mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
+ if (radio == NULL)
+ t->mode_mask |= T_RADIO;
+ tuner_dbg("Setting mode_mask to 0x%02x\n", t->mode_mask);
t->tv_freq = 400 * 16; /* Sets freq to VHF High */
t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
- default_mode_mask = T_UNINITIALIZED;
}
/* Should be just before return */
@@ -729,8 +773,6 @@ static int tuner_probe(struct i2c_adapter *adap)
"in i2c probe ignore list!\n");
}
- default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
-
if (adap->class & I2C_CLASS_TV_ANALOG)
return i2c_probe(adap, &addr_data, tuner_attach);
return 0;
@@ -752,6 +794,7 @@ static int tuner_detach(struct i2c_client *client)
if (ops && ops->release)
ops->release(&t->fe);
+ list_del(&t->list);
kfree(t);
kfree(client);
return 0;
diff --git a/drivers/media/video/tuner-driver.h b/drivers/media/video/tuner-driver.h
index 1c60229dcd0..3ff2943ecc1 100644
--- a/drivers/media/video/tuner-driver.h
+++ b/drivers/media/video/tuner-driver.h
@@ -46,6 +46,7 @@ struct analog_tuner_ops {
struct tuner {
/* device */
struct i2c_client *i2c;
+ struct list_head list; /* list of tuners */
unsigned int type; /* chip type */