linux/drivers/media/tuners/m88ts2022.c
Antti Palosaari eb773df5d2 [media] m88ts2022: rename device state (priv => dev)
foo_dev seems to be most correct term for the structure holding data
of each device instance. It is most used term in Kernel and also
examples from book Linux Device Drivers, Third Edition, uses it.

Signed-off-by: Antti Palosaari <crope@iki.fi>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
2014-09-08 10:00:54 -03:00

681 lines
14 KiB
C

/*
* Montage M88TS2022 silicon tuner driver
*
* Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Some calculations are taken from existing TS2020 driver.
*/
#include "m88ts2022_priv.h"
/* write multiple registers */
static int m88ts2022_wr_regs(struct m88ts2022_dev *dev,
u8 reg, const u8 *val, int len)
{
#define MAX_WR_LEN 3
#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1)
int ret;
u8 buf[MAX_WR_XFER_LEN];
struct i2c_msg msg[1] = {
{
.addr = dev->client->addr,
.flags = 0,
.len = 1 + len,
.buf = buf,
}
};
if (WARN_ON(len > MAX_WR_LEN))
return -EINVAL;
buf[0] = reg;
memcpy(&buf[1], val, len);
ret = i2c_transfer(dev->client->adapter, msg, 1);
if (ret == 1) {
ret = 0;
} else {
dev_warn(&dev->client->dev,
"%s: i2c wr failed=%d reg=%02x len=%d\n",
KBUILD_MODNAME, ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
}
/* read multiple registers */
static int m88ts2022_rd_regs(struct m88ts2022_dev *dev, u8 reg,
u8 *val, int len)
{
#define MAX_RD_LEN 1
#define MAX_RD_XFER_LEN (MAX_RD_LEN)
int ret;
u8 buf[MAX_RD_XFER_LEN];
struct i2c_msg msg[2] = {
{
.addr = dev->client->addr,
.flags = 0,
.len = 1,
.buf = &reg,
}, {
.addr = dev->client->addr,
.flags = I2C_M_RD,
.len = len,
.buf = buf,
}
};
if (WARN_ON(len > MAX_RD_LEN))
return -EINVAL;
ret = i2c_transfer(dev->client->adapter, msg, 2);
if (ret == 2) {
memcpy(val, buf, len);
ret = 0;
} else {
dev_warn(&dev->client->dev,
"%s: i2c rd failed=%d reg=%02x len=%d\n",
KBUILD_MODNAME, ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
}
/* write single register */
static int m88ts2022_wr_reg(struct m88ts2022_dev *dev, u8 reg, u8 val)
{
return m88ts2022_wr_regs(dev, reg, &val, 1);
}
/* read single register */
static int m88ts2022_rd_reg(struct m88ts2022_dev *dev, u8 reg, u8 *val)
{
return m88ts2022_rd_regs(dev, reg, val, 1);
}
/* write single register with mask */
static int m88ts2022_wr_reg_mask(struct m88ts2022_dev *dev,
u8 reg, u8 val, u8 mask)
{
int ret;
u8 u8tmp;
/* no need for read if whole reg is written */
if (mask != 0xff) {
ret = m88ts2022_rd_regs(dev, reg, &u8tmp, 1);
if (ret)
return ret;
val &= mask;
u8tmp &= ~mask;
val |= u8tmp;
}
return m88ts2022_wr_regs(dev, reg, &val, 1);
}
static int m88ts2022_cmd(struct dvb_frontend *fe,
int op, int sleep, u8 reg, u8 mask, u8 val, u8 *reg_val)
{
struct m88ts2022_dev *dev = fe->tuner_priv;
int ret, i;
u8 u8tmp;
struct m88ts2022_reg_val reg_vals[] = {
{0x51, 0x1f - op},
{0x51, 0x1f},
{0x50, 0x00 + op},
{0x50, 0x00},
};
for (i = 0; i < 2; i++) {
dev_dbg(&dev->client->dev,
"%s: i=%d op=%02x reg=%02x mask=%02x val=%02x\n",
__func__, i, op, reg, mask, val);
for (i = 0; i < ARRAY_SIZE(reg_vals); i++) {
ret = m88ts2022_wr_reg(dev, reg_vals[i].reg,
reg_vals[i].val);
if (ret)
goto err;
}
usleep_range(sleep * 1000, sleep * 10000);
ret = m88ts2022_rd_reg(dev, reg, &u8tmp);
if (ret)
goto err;
if ((u8tmp & mask) != val)
break;
}
if (reg_val)
*reg_val = u8tmp;
err:
return ret;
}
static int m88ts2022_set_params(struct dvb_frontend *fe)
{
struct m88ts2022_dev *dev = fe->tuner_priv;
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
unsigned int frequency_khz, frequency_offset_khz, f_3db_hz;
unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n, gdiv28;
u8 buf[3], u8tmp, cap_code, lpf_gm, lpf_mxdiv, div_max, div_min;
u16 u16tmp;
dev_dbg(&dev->client->dev,
"%s: frequency=%d symbol_rate=%d rolloff=%d\n",
__func__, c->frequency, c->symbol_rate, c->rolloff);
/*
* Integer-N PLL synthesizer
* kHz is used for all calculations to keep calculations within 32-bit
*/
f_ref_khz = DIV_ROUND_CLOSEST(dev->cfg.clock, 1000);
div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000);
if (c->symbol_rate < 5000000)
frequency_offset_khz = 3000; /* 3 MHz */
else
frequency_offset_khz = 0;
frequency_khz = c->frequency + frequency_offset_khz;
if (frequency_khz < 1103000) {
div_out = 4;
u8tmp = 0x1b;
} else {
div_out = 2;
u8tmp = 0x0b;
}
buf[0] = u8tmp;
buf[1] = 0x40;
ret = m88ts2022_wr_regs(dev, 0x10, buf, 2);
if (ret)
goto err;
f_vco_khz = frequency_khz * div_out;
pll_n = f_vco_khz * div_ref / f_ref_khz;
pll_n += pll_n % 2;
dev->frequency_khz = pll_n * f_ref_khz / div_ref / div_out;
if (pll_n < 4095)
u16tmp = pll_n - 1024;
else if (pll_n < 6143)
u16tmp = pll_n + 1024;
else
u16tmp = pll_n + 3072;
buf[0] = (u16tmp >> 8) & 0x3f;
buf[1] = (u16tmp >> 0) & 0xff;
buf[2] = div_ref - 8;
ret = m88ts2022_wr_regs(dev, 0x01, buf, 3);
if (ret)
goto err;
dev_dbg(&dev->client->dev,
"%s: frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n",
__func__, dev->frequency_khz,
dev->frequency_khz - c->frequency, f_vco_khz, pll_n,
div_ref, div_out);
ret = m88ts2022_cmd(fe, 0x10, 5, 0x15, 0x40, 0x00, NULL);
if (ret)
goto err;
ret = m88ts2022_rd_reg(dev, 0x14, &u8tmp);
if (ret)
goto err;
u8tmp &= 0x7f;
if (u8tmp < 64) {
ret = m88ts2022_wr_reg_mask(dev, 0x10, 0x80, 0x80);
if (ret)
goto err;
ret = m88ts2022_wr_reg(dev, 0x11, 0x6f);
if (ret)
goto err;
ret = m88ts2022_cmd(fe, 0x10, 5, 0x15, 0x40, 0x00, NULL);
if (ret)
goto err;
}
ret = m88ts2022_rd_reg(dev, 0x14, &u8tmp);
if (ret)
goto err;
u8tmp &= 0x1f;
if (u8tmp > 19) {
ret = m88ts2022_wr_reg_mask(dev, 0x10, 0x00, 0x02);
if (ret)
goto err;
}
ret = m88ts2022_cmd(fe, 0x08, 5, 0x3c, 0xff, 0x00, NULL);
if (ret)
goto err;
ret = m88ts2022_wr_reg(dev, 0x25, 0x00);
if (ret)
goto err;
ret = m88ts2022_wr_reg(dev, 0x27, 0x70);
if (ret)
goto err;
ret = m88ts2022_wr_reg(dev, 0x41, 0x09);
if (ret)
goto err;
ret = m88ts2022_wr_reg(dev, 0x08, 0x0b);
if (ret)
goto err;
/* filters */
gdiv28 = DIV_ROUND_CLOSEST(f_ref_khz * 1694U, 1000000U);
ret = m88ts2022_wr_reg(dev, 0x04, gdiv28);
if (ret)
goto err;
ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
if (ret)
goto err;
cap_code = u8tmp & 0x3f;
ret = m88ts2022_wr_reg(dev, 0x41, 0x0d);
if (ret)
goto err;
ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
if (ret)
goto err;
u8tmp &= 0x3f;
cap_code = (cap_code + u8tmp) / 2;
gdiv28 = gdiv28 * 207 / (cap_code * 2 + 151);
div_max = gdiv28 * 135 / 100;
div_min = gdiv28 * 78 / 100;
div_max = clamp_val(div_max, 0U, 63U);
f_3db_hz = mult_frac(c->symbol_rate, 135, 200);
f_3db_hz += 2000000U + (frequency_offset_khz * 1000U);
f_3db_hz = clamp(f_3db_hz, 7000000U, 40000000U);
#define LPF_COEFF 3200U
lpf_gm = DIV_ROUND_CLOSEST(f_3db_hz * gdiv28, LPF_COEFF * f_ref_khz);
lpf_gm = clamp_val(lpf_gm, 1U, 23U);
lpf_mxdiv = DIV_ROUND_CLOSEST(lpf_gm * LPF_COEFF * f_ref_khz, f_3db_hz);
if (lpf_mxdiv < div_min)
lpf_mxdiv = DIV_ROUND_CLOSEST(++lpf_gm * LPF_COEFF * f_ref_khz, f_3db_hz);
lpf_mxdiv = clamp_val(lpf_mxdiv, 0U, div_max);
ret = m88ts2022_wr_reg(dev, 0x04, lpf_mxdiv);
if (ret)
goto err;
ret = m88ts2022_wr_reg(dev, 0x06, lpf_gm);
if (ret)
goto err;
ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
if (ret)
goto err;
cap_code = u8tmp & 0x3f;
ret = m88ts2022_wr_reg(dev, 0x41, 0x09);
if (ret)
goto err;
ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
if (ret)
goto err;
u8tmp &= 0x3f;
cap_code = (cap_code + u8tmp) / 2;
u8tmp = cap_code | 0x80;
ret = m88ts2022_wr_reg(dev, 0x25, u8tmp);
if (ret)
goto err;
ret = m88ts2022_wr_reg(dev, 0x27, 0x30);
if (ret)
goto err;
ret = m88ts2022_wr_reg(dev, 0x08, 0x09);
if (ret)
goto err;
ret = m88ts2022_cmd(fe, 0x01, 20, 0x21, 0xff, 0x00, NULL);
if (ret)
goto err;
err:
if (ret)
dev_dbg(&dev->client->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
static int m88ts2022_init(struct dvb_frontend *fe)
{
struct m88ts2022_dev *dev = fe->tuner_priv;
int ret, i;
u8 u8tmp;
static const struct m88ts2022_reg_val reg_vals[] = {
{0x7d, 0x9d},
{0x7c, 0x9a},
{0x7a, 0x76},
{0x3b, 0x01},
{0x63, 0x88},
{0x61, 0x85},
{0x22, 0x30},
{0x30, 0x40},
{0x20, 0x23},
{0x24, 0x02},
{0x12, 0xa0},
};
dev_dbg(&dev->client->dev, "%s:\n", __func__);
ret = m88ts2022_wr_reg(dev, 0x00, 0x01);
if (ret)
goto err;
ret = m88ts2022_wr_reg(dev, 0x00, 0x03);
if (ret)
goto err;
switch (dev->cfg.clock_out) {
case M88TS2022_CLOCK_OUT_DISABLED:
u8tmp = 0x60;
break;
case M88TS2022_CLOCK_OUT_ENABLED:
u8tmp = 0x70;
ret = m88ts2022_wr_reg(dev, 0x05, dev->cfg.clock_out_div);
if (ret)
goto err;
break;
case M88TS2022_CLOCK_OUT_ENABLED_XTALOUT:
u8tmp = 0x6c;
break;
default:
goto err;
}
ret = m88ts2022_wr_reg(dev, 0x42, u8tmp);
if (ret)
goto err;
if (dev->cfg.loop_through)
u8tmp = 0xec;
else
u8tmp = 0x6c;
ret = m88ts2022_wr_reg(dev, 0x62, u8tmp);
if (ret)
goto err;
for (i = 0; i < ARRAY_SIZE(reg_vals); i++) {
ret = m88ts2022_wr_reg(dev, reg_vals[i].reg, reg_vals[i].val);
if (ret)
goto err;
}
err:
if (ret)
dev_dbg(&dev->client->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
static int m88ts2022_sleep(struct dvb_frontend *fe)
{
struct m88ts2022_dev *dev = fe->tuner_priv;
int ret;
dev_dbg(&dev->client->dev, "%s:\n", __func__);
ret = m88ts2022_wr_reg(dev, 0x00, 0x00);
if (ret)
goto err;
err:
if (ret)
dev_dbg(&dev->client->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
static int m88ts2022_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct m88ts2022_dev *dev = fe->tuner_priv;
dev_dbg(&dev->client->dev, "%s:\n", __func__);
*frequency = dev->frequency_khz;
return 0;
}
static int m88ts2022_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct m88ts2022_dev *dev = fe->tuner_priv;
dev_dbg(&dev->client->dev, "%s:\n", __func__);
*frequency = 0; /* Zero-IF */
return 0;
}
static int m88ts2022_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
{
struct m88ts2022_dev *dev = fe->tuner_priv;
int ret;
u8 u8tmp;
u16 gain, u16tmp;
unsigned int gain1, gain2, gain3;
ret = m88ts2022_rd_reg(dev, 0x3d, &u8tmp);
if (ret)
goto err;
gain1 = (u8tmp >> 0) & 0x1f;
gain1 = clamp(gain1, 0U, 15U);
ret = m88ts2022_rd_reg(dev, 0x21, &u8tmp);
if (ret)
goto err;
gain2 = (u8tmp >> 0) & 0x1f;
gain2 = clamp(gain2, 2U, 16U);
ret = m88ts2022_rd_reg(dev, 0x66, &u8tmp);
if (ret)
goto err;
gain3 = (u8tmp >> 3) & 0x07;
gain3 = clamp(gain3, 0U, 6U);
gain = gain1 * 265 + gain2 * 338 + gain3 * 285;
/* scale value to 0x0000-0xffff */
u16tmp = (0xffff - gain);
u16tmp = clamp_val(u16tmp, 59000U, 61500U);
*strength = (u16tmp - 59000) * 0xffff / (61500 - 59000);
err:
if (ret)
dev_dbg(&dev->client->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
static const struct dvb_tuner_ops m88ts2022_tuner_ops = {
.info = {
.name = "Montage M88TS2022",
.frequency_min = 950000,
.frequency_max = 2150000,
},
.init = m88ts2022_init,
.sleep = m88ts2022_sleep,
.set_params = m88ts2022_set_params,
.get_frequency = m88ts2022_get_frequency,
.get_if_frequency = m88ts2022_get_if_frequency,
.get_rf_strength = m88ts2022_get_rf_strength,
};
static int m88ts2022_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct m88ts2022_config *cfg = client->dev.platform_data;
struct dvb_frontend *fe = cfg->fe;
struct m88ts2022_dev *dev;
int ret;
u8 chip_id, u8tmp;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
ret = -ENOMEM;
dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
goto err;
}
memcpy(&dev->cfg, cfg, sizeof(struct m88ts2022_config));
dev->client = client;
/* check if the tuner is there */
ret = m88ts2022_rd_reg(dev, 0x00, &u8tmp);
if (ret)
goto err;
if ((u8tmp & 0x03) == 0x00) {
ret = m88ts2022_wr_reg(dev, 0x00, 0x01);
if (ret < 0)
goto err;
usleep_range(2000, 50000);
}
ret = m88ts2022_wr_reg(dev, 0x00, 0x03);
if (ret)
goto err;
usleep_range(2000, 50000);
ret = m88ts2022_rd_reg(dev, 0x00, &chip_id);
if (ret)
goto err;
dev_dbg(&dev->client->dev, "%s: chip_id=%02x\n", __func__, chip_id);
switch (chip_id) {
case 0xc3:
case 0x83:
break;
default:
goto err;
}
switch (dev->cfg.clock_out) {
case M88TS2022_CLOCK_OUT_DISABLED:
u8tmp = 0x60;
break;
case M88TS2022_CLOCK_OUT_ENABLED:
u8tmp = 0x70;
ret = m88ts2022_wr_reg(dev, 0x05, dev->cfg.clock_out_div);
if (ret)
goto err;
break;
case M88TS2022_CLOCK_OUT_ENABLED_XTALOUT:
u8tmp = 0x6c;
break;
default:
goto err;
}
ret = m88ts2022_wr_reg(dev, 0x42, u8tmp);
if (ret)
goto err;
if (dev->cfg.loop_through)
u8tmp = 0xec;
else
u8tmp = 0x6c;
ret = m88ts2022_wr_reg(dev, 0x62, u8tmp);
if (ret)
goto err;
/* sleep */
ret = m88ts2022_wr_reg(dev, 0x00, 0x00);
if (ret)
goto err;
dev_info(&dev->client->dev,
"%s: Montage M88TS2022 successfully identified\n",
KBUILD_MODNAME);
fe->tuner_priv = dev;
memcpy(&fe->ops.tuner_ops, &m88ts2022_tuner_ops,
sizeof(struct dvb_tuner_ops));
i2c_set_clientdata(client, dev);
return 0;
err:
dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
kfree(dev);
return ret;
}
static int m88ts2022_remove(struct i2c_client *client)
{
struct m88ts2022_dev *dev = i2c_get_clientdata(client);
struct dvb_frontend *fe = dev->cfg.fe;
dev_dbg(&client->dev, "%s:\n", __func__);
memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
fe->tuner_priv = NULL;
kfree(dev);
return 0;
}
static const struct i2c_device_id m88ts2022_id[] = {
{"m88ts2022", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, m88ts2022_id);
static struct i2c_driver m88ts2022_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "m88ts2022",
},
.probe = m88ts2022_probe,
.remove = m88ts2022_remove,
.id_table = m88ts2022_id,
};
module_i2c_driver(m88ts2022_driver);
MODULE_DESCRIPTION("Montage M88TS2022 silicon tuner driver");
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
MODULE_LICENSE("GPL");