diff options
Diffstat (limited to 'drivers/media/tuners/mt2060.c')
-rw-r--r-- | drivers/media/tuners/mt2060.c | 129 |
1 files changed, 121 insertions, 8 deletions
diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c index 94077ea78dde..2e487f9a2cc3 100644 --- a/drivers/media/tuners/mt2060.c +++ b/drivers/media/tuners/mt2060.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= */ /* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ @@ -71,13 +67,24 @@ static int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val) // Writes a set of consecutive registers static int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len) { + int rem, val_len; + u8 xfer_buf[16]; struct i2c_msg msg = { - .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len + .addr = priv->cfg->i2c_address, .flags = 0, .buf = xfer_buf }; - if (i2c_transfer(priv->i2c, &msg, 1) != 1) { - printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n",(int)len); - return -EREMOTEIO; + + for (rem = len - 1; rem > 0; rem -= priv->i2c_max_regs) { + val_len = min_t(int, rem, priv->i2c_max_regs); + msg.len = 1 + val_len; + xfer_buf[0] = buf[0] + len - 1 - rem; + memcpy(&xfer_buf[1], &buf[1 + len - 1 - rem], val_len); + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n", val_len); + return -EREMOTEIO; + } } + return 0; } @@ -306,9 +313,16 @@ static int mt2060_init(struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + if (priv->sleep) { + ret = mt2060_writereg(priv, REG_MISC_CTRL, 0x20); + if (ret) + goto err_i2c_gate_ctrl; + } + ret = mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x33); +err_i2c_gate_ctrl: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ @@ -325,7 +339,13 @@ static int mt2060_sleep(struct dvb_frontend *fe) ret = mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30); + if (ret) + goto err_i2c_gate_ctrl; + + if (priv->sleep) + ret = mt2060_writereg(priv, REG_MISC_CTRL, 0xe8); +err_i2c_gate_ctrl: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ @@ -369,6 +389,7 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter priv->cfg = cfg; priv->i2c = i2c; priv->if1_freq = if1; + priv->i2c_max_regs = ~0; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ @@ -396,6 +417,98 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter } EXPORT_SYMBOL(mt2060_attach); +static int mt2060_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mt2060_platform_data *pdata = client->dev.platform_data; + struct dvb_frontend *fe; + struct mt2060_priv *dev; + int ret; + u8 chip_id; + + dev_dbg(&client->dev, "\n"); + + if (!pdata) { + dev_err(&client->dev, "Cannot proceed without platform data\n"); + ret = -EINVAL; + goto err; + } + + dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err; + } + + fe = pdata->dvb_frontend; + dev->config.i2c_address = client->addr; + dev->config.clock_out = pdata->clock_out; + dev->cfg = &dev->config; + dev->i2c = client->adapter; + dev->if1_freq = pdata->if1 ? pdata->if1 : 1220; + dev->client = client; + dev->i2c_max_regs = pdata->i2c_write_max ? pdata->i2c_write_max - 1 : ~0; + dev->sleep = true; + + ret = mt2060_readreg(dev, REG_PART_REV, &chip_id); + if (ret) { + ret = -ENODEV; + goto err; + } + + dev_dbg(&client->dev, "chip id=%02x\n", chip_id); + + if (chip_id != PART_REV) { + ret = -ENODEV; + goto err; + } + + /* Power on, calibrate, sleep */ + ret = mt2060_writereg(dev, REG_MISC_CTRL, 0x20); + if (ret) + goto err; + mt2060_calibrate(dev); + ret = mt2060_writereg(dev, REG_MISC_CTRL, 0xe8); + if (ret) + goto err; + + dev_info(&client->dev, "Microtune MT2060 successfully identified\n"); + memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(fe->ops.tuner_ops)); + fe->ops.tuner_ops.release = NULL; + fe->tuner_priv = dev; + i2c_set_clientdata(client, dev); + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mt2060_remove(struct i2c_client *client) +{ + dev_dbg(&client->dev, "\n"); + + return 0; +} + +static const struct i2c_device_id mt2060_id_table[] = { + {"mt2060", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mt2060_id_table); + +static struct i2c_driver mt2060_driver = { + .driver = { + .name = "mt2060", + .suppress_bind_attrs = true, + }, + .probe = mt2060_probe, + .remove = mt2060_remove, + .id_table = mt2060_id_table, +}; + +module_i2c_driver(mt2060_driver); + MODULE_AUTHOR("Olivier DANET"); MODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver"); MODULE_LICENSE("GPL"); |