[media] NXP TDA18212HN silicon tuner driver
authorAntti Palosaari <crope@iki.fi>
Sat, 9 Apr 2011 23:07:30 +0000 (20:07 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Fri, 20 May 2011 12:26:57 +0000 (09:26 -0300)
New silicon tuner driver.

Signed-off-by: Antti Palosaari <crope@iki.fi>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/common/tuners/Kconfig
drivers/media/common/tuners/Makefile
drivers/media/common/tuners/tda18212.c [new file with mode: 0644]
drivers/media/common/tuners/tda18212.h [new file with mode: 0644]
drivers/media/common/tuners/tda18212_priv.h [new file with mode: 0644]

index 6fc79f1..22d3ca3 100644 (file)
@@ -186,4 +186,12 @@ config MEDIA_TUNER_TDA18218
        default m if MEDIA_TUNER_CUSTOMISE
        help
          NXP TDA18218 silicon tuner driver.
+
+config MEDIA_TUNER_TDA18212
+       tristate "NXP TDA18212 silicon tuner"
+       depends on VIDEO_MEDIA && I2C
+       default m if MEDIA_TUNER_CUSTOMISE
+       help
+         NXP TDA18212 silicon tuner driver.
+
 endmenu
index 96da03d..2cb4f53 100644 (file)
@@ -25,6 +25,7 @@ obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o
 obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o
 obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
 obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
+obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o
 
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/common/tuners/tda18212.c b/drivers/media/common/tuners/tda18212.c
new file mode 100644 (file)
index 0000000..1f1db20
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * NXP TDA18212HN silicon tuner driver
+ *
+ * Copyright (C) 2011 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.
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "tda18212_priv.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+/* write multiple registers */
+static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
+       int len)
+{
+       int ret;
+       u8 buf[len+1];
+       struct i2c_msg msg[1] = {
+               {
+                       .addr = priv->cfg->i2c_address,
+                       .flags = 0,
+                       .len = sizeof(buf),
+                       .buf = buf,
+               }
+       };
+
+       buf[0] = reg;
+       memcpy(&buf[1], val, len);
+
+       ret = i2c_transfer(priv->i2c, msg, 1);
+       if (ret == 1) {
+               ret = 0;
+       } else {
+               warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len);
+               ret = -EREMOTEIO;
+       }
+       return ret;
+}
+
+/* read multiple registers */
+static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
+       int len)
+{
+       int ret;
+       u8 buf[len];
+       struct i2c_msg msg[2] = {
+               {
+                       .addr = priv->cfg->i2c_address,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = &reg,
+               }, {
+                       .addr = priv->cfg->i2c_address,
+                       .flags = I2C_M_RD,
+                       .len = sizeof(buf),
+                       .buf = buf,
+               }
+       };
+
+       ret = i2c_transfer(priv->i2c, msg, 2);
+       if (ret == 2) {
+               memcpy(val, buf, len);
+               ret = 0;
+       } else {
+               warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len);
+               ret = -EREMOTEIO;
+       }
+
+       return ret;
+}
+
+/* write single register */
+static int tda18212_wr_reg(struct tda18212_priv *priv, u8 reg, u8 val)
+{
+       return tda18212_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+static int tda18212_rd_reg(struct tda18212_priv *priv, u8 reg, u8 *val)
+{
+       return tda18212_rd_regs(priv, reg, val, 1);
+}
+
+#if 0 /* keep, useful when developing driver */
+static void tda18212_dump_regs(struct tda18212_priv *priv)
+{
+       int i;
+       u8 buf[256];
+
+       #define TDA18212_RD_LEN 32
+       for (i = 0; i < sizeof(buf); i += TDA18212_RD_LEN)
+               tda18212_rd_regs(priv, i, &buf[i], TDA18212_RD_LEN);
+
+       print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 32, 1, buf,
+               sizeof(buf), true);
+
+       return;
+}
+#endif
+
+static int tda18212_set_params(struct dvb_frontend *fe,
+       struct dvb_frontend_parameters *p)
+{
+       struct tda18212_priv *priv = fe->tuner_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       int ret, i;
+       u32 if_khz;
+       u8 buf[9];
+       static const u8 bw_params[][3] = {
+               /*  0f    13    23 */
+               { 0xb3, 0x20, 0x03 }, /* DVB-T 6 MHz */
+               { 0xb3, 0x31, 0x01 }, /* DVB-T 7 MHz */
+               { 0xb3, 0x22, 0x01 }, /* DVB-T 8 MHz */
+               { 0x92, 0x53, 0x03 }, /* DVB-C */
+       };
+
+       dbg("%s: delsys=%d RF=%d BW=%d", __func__,
+               c->delivery_system, c->frequency, c->bandwidth_hz);
+
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+       switch (c->delivery_system) {
+       case SYS_DVBT:
+               switch (c->bandwidth_hz) {
+               case 6000000:
+                       if_khz = priv->cfg->if_dvbt_6;
+                       i = 0;
+                       break;
+               case 7000000:
+                       if_khz = priv->cfg->if_dvbt_7;
+                       i = 1;
+                       break;
+               case 8000000:
+                       if_khz = priv->cfg->if_dvbt_8;
+                       i = 2;
+                       break;
+               default:
+                       ret = -EINVAL;
+                       goto error;
+               }
+               break;
+       case SYS_DVBC_ANNEX_AC:
+               if_khz = priv->cfg->if_dvbc;
+               i = 3;
+               break;
+       default:
+               ret = -EINVAL;
+               goto error;
+       }
+
+       ret = tda18212_wr_reg(priv, 0x23, bw_params[i][2]);
+       if (ret)
+               goto error;
+
+       ret = tda18212_wr_reg(priv, 0x06, 0x00);
+       if (ret)
+               goto error;
+
+       ret = tda18212_wr_reg(priv, 0x0f, bw_params[i][0]);
+       if (ret)
+               goto error;
+
+       buf[0] = 0x02;
+       buf[1] = bw_params[i][1];
+       buf[2] = 0x03; /* default value */
+       buf[3] = if_khz / 50;
+       buf[4] = ((c->frequency / 1000) >> 16) & 0xff;
+       buf[5] = ((c->frequency / 1000) >>  8) & 0xff;
+       buf[6] = ((c->frequency / 1000) >>  0) & 0xff;
+       buf[7] = 0xc1;
+       buf[8] = 0x01;
+       ret = tda18212_wr_regs(priv, 0x12, buf, sizeof(buf));
+       if (ret)
+               goto error;
+
+exit:
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+       return ret;
+
+error:
+       dbg("%s: failed:%d", __func__, ret);
+       goto exit;
+}
+
+static int tda18212_release(struct dvb_frontend *fe)
+{
+       kfree(fe->tuner_priv);
+       fe->tuner_priv = NULL;
+       return 0;
+}
+
+static const struct dvb_tuner_ops tda18212_tuner_ops = {
+       .info = {
+               .name           = "NXP TDA18212",
+
+               .frequency_min  =  48000000,
+               .frequency_max  = 864000000,
+               .frequency_step =      1000,
+       },
+
+       .release       = tda18212_release,
+
+       .set_params    = tda18212_set_params,
+};
+
+struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
+       struct i2c_adapter *i2c, struct tda18212_config *cfg)
+{
+       struct tda18212_priv *priv = NULL;
+       int ret;
+       u8 val;
+
+       priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return NULL;
+
+       priv->cfg = cfg;
+       priv->i2c = i2c;
+       fe->tuner_priv = priv;
+
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+       /* check if the tuner is there */
+       ret = tda18212_rd_reg(priv, 0x00, &val);
+
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+       dbg("%s: ret:%d chip ID:%02x", __func__, ret, val);
+       if (ret || val != 0xc7) {
+               kfree(priv);
+               return NULL;
+       }
+
+       info("NXP TDA18212HN successfully identified.");
+
+       memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops,
+               sizeof(struct dvb_tuner_ops));
+
+       return fe;
+}
+EXPORT_SYMBOL(tda18212_attach);
+
+MODULE_DESCRIPTION("NXP TDA18212HN silicon tuner driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tda18212.h b/drivers/media/common/tuners/tda18212.h
new file mode 100644 (file)
index 0000000..83b497f
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * NXP TDA18212HN silicon tuner driver
+ *
+ * Copyright (C) 2011 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.
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TDA18212_H
+#define TDA18212_H
+
+#include "dvb_frontend.h"
+
+struct tda18212_config {
+       u8 i2c_address;
+
+       u16 if_dvbt_6;
+       u16 if_dvbt_7;
+       u16 if_dvbt_8;
+       u16 if_dvbc;
+};
+
+#if defined(CONFIG_MEDIA_TUNER_TDA18212) || \
+       (defined(CONFIG_MEDIA_TUNER_TDA18212_MODULE) && defined(MODULE))
+extern struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
+       struct i2c_adapter *i2c, struct tda18212_config *cfg);
+#else
+static inline struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
+       struct i2c_adapter *i2c, struct tda18212_config *cfg)
+{
+       printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+       return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/common/tuners/tda18212_priv.h b/drivers/media/common/tuners/tda18212_priv.h
new file mode 100644 (file)
index 0000000..9adff93
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * NXP TDA18212HN silicon tuner driver
+ *
+ * Copyright (C) 2011 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.
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TDA18212_PRIV_H
+#define TDA18212_PRIV_H
+
+#include "tda18212.h"
+
+#define LOG_PREFIX "tda18212"
+
+#undef dbg
+#define dbg(f, arg...) \
+       if (debug) \
+               printk(KERN_INFO   LOG_PREFIX": " f "\n" , ## arg)
+#undef err
+#define err(f, arg...)  printk(KERN_ERR     LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO    LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+struct tda18212_priv {
+       struct tda18212_config *cfg;
+       struct i2c_adapter *i2c;
+};
+
+#endif