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

V4L/DVB (6468): tda8290: auto-detect tda8290 or tda8295

Consolidate tda8290_attach() and tda8295_attach() into a single function,
tda829x_attach(), which will detect chip combinations tda8290 or tda8295 with
tda8275, tda8275a or tda18271.
Signed-off-by: default avatarMichael Krufky <mkrufky@linuxtv.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 393bf557
...@@ -41,7 +41,13 @@ struct tda8290_priv { ...@@ -41,7 +41,13 @@ struct tda8290_priv {
unsigned char tda8290_easy_mode; unsigned char tda8290_easy_mode;
unsigned char tda827x_addr; unsigned char tda827x_addr;
unsigned char tda827x_ver;
unsigned char ver;
#define TDA8290 1
#define TDA8295 2
#define TDA8275 4
#define TDA8275A 8
#define TDA18271 16
struct tda827x_config cfg; struct tda827x_config cfg;
...@@ -136,7 +142,7 @@ static void set_audio(struct dvb_frontend *fe) ...@@ -136,7 +142,7 @@ static void set_audio(struct dvb_frontend *fe)
mode = "xx"; mode = "xx";
} }
tuner_dbg("setting tda8290 to system %s\n", mode); tuner_dbg("setting tda829x to system %s\n", mode);
} }
static void tda8290_set_freq(struct dvb_frontend *fe, unsigned int freq) static void tda8290_set_freq(struct dvb_frontend *fe, unsigned int freq)
...@@ -429,7 +435,7 @@ static void tda8290_standby(struct dvb_frontend *fe) ...@@ -429,7 +435,7 @@ static void tda8290_standby(struct dvb_frontend *fe)
struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, .buf=cb1, .len = 2}; struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, .buf=cb1, .len = 2};
tda8290_i2c_bridge(fe, 1); tda8290_i2c_bridge(fe, 1);
if (priv->tda827x_ver != 0) if (priv->ver & TDA8275A)
cb1[1] = 0x90; cb1[1] = 0x90;
i2c_transfer(priv->i2c_props.adap, &msg, 1); i2c_transfer(priv->i2c_props.adap, &msg, 1);
tda8290_i2c_bridge(fe, 0); tda8290_i2c_bridge(fe, 0);
...@@ -498,7 +504,7 @@ static void tda8290_init_tuner(struct dvb_frontend *fe) ...@@ -498,7 +504,7 @@ static void tda8290_init_tuner(struct dvb_frontend *fe)
0x0c, 0x04, 0x20, 0xFF, 0x00, 0x00, 0x4b }; 0x0c, 0x04, 0x20, 0xFF, 0x00, 0x00, 0x4b };
struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0,
.buf=tda8275_init, .len = 14}; .buf=tda8275_init, .len = 14};
if (priv->tda827x_ver != 0) if (priv->ver & TDA8275A)
msg.buf = tda8275a_init; msg.buf = tda8275a_init;
tda8290_i2c_bridge(fe, 1); tda8290_i2c_bridge(fe, 1);
...@@ -517,48 +523,25 @@ static void tda829x_release(struct dvb_frontend *fe) ...@@ -517,48 +523,25 @@ static void tda829x_release(struct dvb_frontend *fe)
fe->analog_demod_priv = NULL; fe->analog_demod_priv = NULL;
} }
static struct analog_tuner_ops tda8290_tuner_ops = { static int tda829x_find_tuner(struct dvb_frontend *fe)
.set_tv_freq = tda8290_set_freq,
.set_radio_freq = tda8290_set_freq,
.has_signal = tda8290_has_signal,
.standby = tda8290_standby,
.release = tda829x_release,
.i2c_gate_ctrl = tda8290_i2c_bridge,
};
static struct analog_tuner_ops tda8295_tuner_ops = {
.set_tv_freq = tda8295_set_freq,
.set_radio_freq = tda8295_set_freq,
.has_signal = tda8295_has_signal,
.standby = tda8295_standby,
.release = tda829x_release,
.i2c_gate_ctrl = tda8295_i2c_bridge,
};
int tda8290_attach(struct tuner *t)
{ {
struct tda8290_priv *priv = NULL; struct tda8290_priv *priv = fe->analog_demod_priv;
u8 data; struct analog_tuner_ops *ops = fe->ops.analog_demod_ops;
struct tuner *t = priv->t;
int i, ret, tuners_found; int i, ret, tuners_found;
u32 tuner_addrs; u32 tuner_addrs;
struct i2c_msg msg = {.flags=I2C_M_RD, .buf=&data, .len = 1}; u8 data;
struct i2c_msg msg = { .flags = I2C_M_RD, .buf = &data, .len = 1 };
priv = kzalloc(sizeof(struct tda8290_priv), GFP_KERNEL); if (NULL == ops)
if (priv == NULL) return -EINVAL;
return -ENOMEM;
t->fe.analog_demod_priv = priv;
priv->i2c_props.addr = t->i2c.addr; ops->i2c_gate_ctrl(fe, 1);
priv->i2c_props.adap = t->i2c.adapter;
priv->cfg.config = &t->config;
priv->cfg.tuner_callback = t->tuner_callback;
priv->t = t;
tda8290_i2c_bridge(&t->fe, 1);
/* probe for tuner chip */ /* probe for tuner chip */
tuners_found = 0; tuners_found = 0;
tuner_addrs = 0; tuner_addrs = 0;
for (i=0x60; i<= 0x63; i++) { for (i = 0x60; i <= 0x63; i++) {
msg.addr = i; msg.addr = i;
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
if (ret == 1) { if (ret == 1) {
...@@ -570,20 +553,23 @@ int tda8290_attach(struct tuner *t) ...@@ -570,20 +553,23 @@ int tda8290_attach(struct tuner *t)
behind the bridge and we choose the highest address that doesn't behind the bridge and we choose the highest address that doesn't
give a response now give a response now
*/ */
tda8290_i2c_bridge(&t->fe, 0);
if(tuners_found > 1) ops->i2c_gate_ctrl(fe, 0);
if (tuners_found > 1)
for (i = 0; i < tuners_found; i++) { for (i = 0; i < tuners_found; i++) {
msg.addr = tuner_addrs & 0xff; msg.addr = tuner_addrs & 0xff;
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
if(ret == 1) if (ret == 1)
tuner_addrs = tuner_addrs >> 8; tuner_addrs = tuner_addrs >> 8;
else else
break; break;
} }
if (tuner_addrs == 0) { if (tuner_addrs == 0) {
tuner_addrs = 0x61; tuner_addrs = 0x60;
tuner_info("could not clearly identify tuner address, defaulting to %x\n", tuner_info("could not clearly identify tuner address, "
tuner_addrs); "defaulting to %x\n", tuner_addrs);
} else { } else {
tuner_addrs = tuner_addrs & 0xff; tuner_addrs = tuner_addrs & 0xff;
tuner_info("setting tuner address to %x\n", tuner_addrs); tuner_info("setting tuner address to %x\n", tuner_addrs);
...@@ -591,19 +577,24 @@ int tda8290_attach(struct tuner *t) ...@@ -591,19 +577,24 @@ int tda8290_attach(struct tuner *t)
priv->tda827x_addr = tuner_addrs; priv->tda827x_addr = tuner_addrs;
msg.addr = tuner_addrs; msg.addr = tuner_addrs;
tda8290_i2c_bridge(&t->fe, 1); ops->i2c_gate_ctrl(fe, 1);
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
if( ret != 1)
tuner_warn("TDA827x access failed!\n");
if ((data & 0x3c) == 0) { if (ret != 1) {
strlcpy(t->i2c.name, "tda8290+75", sizeof(t->i2c.name)); tuner_warn("tuner access failed!\n");
priv->tda827x_ver = 0; return -EREMOTEIO;
} else {
strlcpy(t->i2c.name, "tda8290+75a", sizeof(t->i2c.name));
priv->tda827x_ver = 2;
} }
if (data == 0x83) {
priv->ver |= TDA18271;
tda18271_attach(&t->fe, priv->tda827x_addr,
priv->i2c_props.adap);
} else {
if ((data & 0x3c) == 0)
priv->ver |= TDA8275;
else
priv->ver |= TDA8275A;
tda827x_attach(&t->fe, priv->tda827x_addr, tda827x_attach(&t->fe, priv->tda827x_addr,
priv->i2c_props.adap, &priv->cfg); priv->i2c_props.adap, &priv->cfg);
...@@ -612,106 +603,106 @@ int tda8290_attach(struct tuner *t) ...@@ -612,106 +603,106 @@ int tda8290_attach(struct tuner *t)
*/ */
if (t->fe.ops.tuner_ops.sleep) if (t->fe.ops.tuner_ops.sleep)
t->fe.ops.tuner_ops.sleep(&t->fe); t->fe.ops.tuner_ops.sleep(&t->fe);
}
ops->i2c_gate_ctrl(fe, 0);
t->fe.ops.analog_demod_ops = &tda8290_tuner_ops; switch (priv->ver) {
case TDA8290 | TDA8275:
tuner_info("type set to %s\n", t->i2c.name); strlcpy(t->i2c.name, "tda8290+75", sizeof(t->i2c.name));
break;
t->mode = V4L2_TUNER_ANALOG_TV; case TDA8295 | TDA8275:
strlcpy(t->i2c.name, "tda8295+75", sizeof(t->i2c.name));
break;
case TDA8290 | TDA8275A:
strlcpy(t->i2c.name, "tda8290+75a", sizeof(t->i2c.name));
break;
case TDA8295 | TDA8275A:
strlcpy(t->i2c.name, "tda8295+75a", sizeof(t->i2c.name));
break;
case TDA8290 | TDA18271:
strlcpy(t->i2c.name, "tda8290+18271", sizeof(t->i2c.name));
break;
case TDA8295 | TDA18271:
strlcpy(t->i2c.name, "tda8295+18271", sizeof(t->i2c.name));
break;
default:
return -EINVAL;
}
tda8290_init_tuner(&t->fe);
tda8290_init_if(&t->fe);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(tda8290_attach);
int tda8295_attach(struct tuner *t) static struct analog_tuner_ops tda8290_tuner_ops = {
.set_tv_freq = tda8290_set_freq,
.set_radio_freq = tda8290_set_freq,
.has_signal = tda8290_has_signal,
.standby = tda8290_standby,
.release = tda829x_release,
.i2c_gate_ctrl = tda8290_i2c_bridge,
};
static struct analog_tuner_ops tda8295_tuner_ops = {
.set_tv_freq = tda8295_set_freq,
.set_radio_freq = tda8295_set_freq,
.has_signal = tda8295_has_signal,
.standby = tda8295_standby,
.release = tda829x_release,
.i2c_gate_ctrl = tda8295_i2c_bridge,
};
int tda829x_attach(struct tuner *t)
{ {
struct dvb_frontend *fe = &t->fe;
struct tda8290_priv *priv = NULL; struct tda8290_priv *priv = NULL;
u8 data;
int i, ret, tuners_found; unsigned char tda8290_id[] = { 0x1f, 0x00 };
u32 tuner_addrs; #define TDA8290_ID 0x89
struct i2c_msg msg = { .flags = I2C_M_RD, .buf = &data, .len = 1 }; unsigned char tda8295_id[] = { 0x2f, 0x00 };
#define TDA8295_ID 0x8a
priv = kzalloc(sizeof(struct tda8290_priv), GFP_KERNEL); priv = kzalloc(sizeof(struct tda8290_priv), GFP_KERNEL);
if (priv == NULL) if (priv == NULL)
return -ENOMEM; return -ENOMEM;
t->fe.analog_demod_priv = priv; fe->analog_demod_priv = priv;
priv->i2c_props.addr = t->i2c.addr; priv->i2c_props.addr = t->i2c.addr;
priv->i2c_props.adap = t->i2c.adapter; priv->i2c_props.adap = t->i2c.adapter;
priv->cfg.config = &t->config;
priv->cfg.tuner_callback = t->tuner_callback;
priv->t = t; priv->t = t;
tda8295_i2c_bridge(&t->fe, 1); /* detect tda8290 */
/* probe for tuner chip */ tuner_i2c_xfer_send(&priv->i2c_props, &tda8290_id[0], 1);
tuners_found = 0; tuner_i2c_xfer_recv(&priv->i2c_props, &tda8290_id[1], 1);
tuner_addrs = 0; if (tda8290_id[1] == TDA8290_ID) {
for (i = 0x60; i <= 0x63; i++) { priv->ver = TDA8290;
msg.addr = i; fe->ops.analog_demod_ops = &tda8290_tuner_ops;
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
if (ret == 1) {
tuners_found++;
tuner_addrs = (tuner_addrs << 8) + i;
}
} }
/* if there is more than one tuner, we expect the right one is
behind the bridge and we choose the highest address that doesn't /* detect tda8295 */
give a response now tuner_i2c_xfer_send(&priv->i2c_props, &tda8295_id[0], 1);
*/ tuner_i2c_xfer_recv(&priv->i2c_props, &tda8295_id[1], 1);
tda8295_i2c_bridge(&t->fe, 0); if (tda8295_id[1] == TDA8295_ID) {
if (tuners_found > 1) priv->ver = TDA8295;
for (i = 0; i < tuners_found; i++) { fe->ops.analog_demod_ops = &tda8295_tuner_ops;
msg.addr = tuner_addrs & 0xff;
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
if (ret == 1)
tuner_addrs = tuner_addrs >> 8;
else
break;
}
if (tuner_addrs == 0) {
tuner_addrs = 0x60;
tuner_info("could not clearly identify tuner address, "
"defaulting to %x\n", tuner_addrs);
} else {
tuner_addrs = tuner_addrs & 0xff;
tuner_info("setting tuner address to %x\n", tuner_addrs);
} }
priv->tda827x_addr = tuner_addrs;
msg.addr = tuner_addrs;
tda8295_i2c_bridge(&t->fe, 1); if (tda829x_find_tuner(fe) < 0)
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); return -EINVAL;
tda8295_i2c_bridge(&t->fe, 0);
if (ret != 1)
tuner_warn("TDA827x access failed!\n");
if ((data & 0x3c) == 0) {
strlcpy(t->i2c.name, "tda8295+18271", sizeof(t->i2c.name));
tda18271_attach(&t->fe, priv->tda827x_addr,
priv->i2c_props.adap);
priv->tda827x_ver = 4;
} else {
strlcpy(t->i2c.name, "tda8295+75a", sizeof(t->i2c.name));
tda827x_attach(&t->fe, priv->tda827x_addr,
priv->i2c_props.adap, &priv->cfg);
/* FIXME: tda827x module doesn't probe the tuner until if (priv->ver & TDA8290) {
* tda827x_initial_sleep is called tda8290_init_tuner(fe);
*/ tda8290_init_if(fe);
if (t->fe.ops.tuner_ops.sleep) } else if (priv->ver & TDA8295)
t->fe.ops.tuner_ops.sleep(&t->fe); tda8295_init_if(fe);
priv->tda827x_ver = 2;
}
priv->tda827x_ver |= 1; /* signifies 8295 vs 8290 */
tuner_info("type set to %s\n", t->i2c.name);
t->fe.ops.analog_demod_ops = &tda8295_tuner_ops; tuner_info("type set to %s\n", t->i2c.name);
t->mode = V4L2_TUNER_ANALOG_TV; t->mode = V4L2_TUNER_ANALOG_TV;
tda8295_init_if(&t->fe);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(tda8295_attach); EXPORT_SYMBOL_GPL(tda829x_attach);
int tda8290_probe(struct tuner *t) int tda8290_probe(struct tuner *t)
{ {
...@@ -745,7 +736,7 @@ int tda8290_probe(struct tuner *t) ...@@ -745,7 +736,7 @@ int tda8290_probe(struct tuner *t)
} }
EXPORT_SYMBOL_GPL(tda8290_probe); EXPORT_SYMBOL_GPL(tda8290_probe);
MODULE_DESCRIPTION("Philips TDA8290 + TDA8275 / TDA8275a tuner driver"); MODULE_DESCRIPTION("Philips/NXP TDA8290/TDA8295 analog IF demodulator driver");
MODULE_AUTHOR("Gerd Knorr, Hartmut Hackmann, Michael Krufky"); MODULE_AUTHOR("Gerd Knorr, Hartmut Hackmann, Michael Krufky");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
......
...@@ -23,8 +23,7 @@ ...@@ -23,8 +23,7 @@
#if defined(CONFIG_TUNER_TDA8290) || (defined(CONFIG_TUNER_TDA8290_MODULE) && defined(MODULE)) #if defined(CONFIG_TUNER_TDA8290) || (defined(CONFIG_TUNER_TDA8290_MODULE) && defined(MODULE))
extern int tda8290_probe(struct tuner *t); extern int tda8290_probe(struct tuner *t);
extern int tda8290_attach(struct tuner *t); extern int tda829x_attach(struct tuner *t);
extern int tda8295_attach(struct tuner *t);
#else #else
static inline int tda8290_probe(struct tuner *t) static inline int tda8290_probe(struct tuner *t)
{ {
...@@ -32,14 +31,7 @@ static inline int tda8290_probe(struct tuner *t) ...@@ -32,14 +31,7 @@ static inline int tda8290_probe(struct tuner *t)
return -EINVAL; return -EINVAL;
} }
static inline int tda8290_attach(struct tuner *t) static inline int tda829x_attach(struct tuner *t)
{
printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
__FUNCTION__);
return -EINVAL;
}
static inline int tda8295_attach(struct tuner *t)
{ {
printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n", printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
__FUNCTION__); __FUNCTION__);
......
...@@ -295,13 +295,9 @@ static void set_type(struct i2c_client *c, unsigned int type, ...@@ -295,13 +295,9 @@ static void set_type(struct i2c_client *c, unsigned int type,
microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr); microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
break; break;
case TUNER_PHILIPS_TDA8290: case TUNER_PHILIPS_TDA8290:
{
tda8290_attach(t);
break;
}
case TUNER_PHILIPS_TDA8295: case TUNER_PHILIPS_TDA8295:
{ {
tda8295_attach(t); tda829x_attach(t);
break; break;
} }
case TUNER_TEA5767: case TUNER_TEA5767:
......
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