@@ -282,4 +282,11 @@ config MEDIA_TUNER_QM1D1C0042
default m if !MEDIA_SUBDRV_AUTOSELECT
help
Sharp QM1D1C0042 trellis coded 8PSK tuner driver.
+
+config MEDIA_TUNER_TUA6034
+ tristate "Infineon TUA6034 tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Infineon TUA6034 3-band ditigal TV tuner driver.
endmenu
@@ -42,6 +42,7 @@ obj-$(CONFIG_MEDIA_TUNER_R820T) += r820t.o
obj-$(CONFIG_MEDIA_TUNER_MXL301RF) += mxl301rf.o
obj-$(CONFIG_MEDIA_TUNER_QM1D1C0042) += qm1d1c0042.o
obj-$(CONFIG_MEDIA_TUNER_M88RS6000T) += m88rs6000t.o
+obj-$(CONFIG_MEDIA_TUNER_TUA6034) += tua6034.o
ccflags-y += -I$(srctree)/drivers/media/dvb-core
ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
new file mode 100644
@@ -0,0 +1,464 @@
+/*
+ * Infineon TUA6034 terrestial tuner driver
+ *
+ * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
+ *
+ * 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 version 2.
+ *
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include "dvb_i2c.h"
+#include "tua6034.h"
+
+#define TUA6034_XTL_FREQ 4000000
+
+struct tua6034_state {
+ struct tua6034_config cfg;
+ struct i2c_client *i2c;
+
+ /* current parameters (except frequency divider) */
+ u8 ctrl_byte;
+ u8 band_sw_byte;
+ u8 aux_byte;
+};
+
+struct def_param {
+ /* for auto "band" selection */
+ u32 band_sw_freq[4]; /* fmin, flow_mid, fmid_high, fmax */
+
+ /* for auto "CP current" selection */
+ /* band:[low, mid, high] x cp:[50uA/125uA,125uA/250uA,250uA/650uA] */
+ u32 cp_sw_freq[3][3];
+ u32 if_freq;
+ u32 if_bw;
+ enum tua6034_ref_div ref_div;
+};
+
+/* per-DELSYS default configuration */
+static const struct def_param def_cfgs[] = {
+ /* ISDB-T */
+ {
+ .band_sw_freq = {90000000, 170000000, 470000000, 900000000},
+ .cp_sw_freq = {
+ {0, 0, 170000000},
+ {0, 270000000, 370000000},
+ {0, 0, 670000000}
+ },
+
+ .if_freq = 57000000,
+ .if_bw = 6000000,
+ .ref_div = TUA6034_REF_DIV_1_28,
+ },
+
+ /* DVB-T */
+ {
+ .band_sw_freq = {44250000, 157500000, 443000000, 900000000},
+ .cp_sw_freq = {
+ {0, 140500000, 157500000},
+ {0, 443000000, 0},
+ {0, 802000000, 900000000}
+ },
+
+ .if_freq = 36125000,
+ .if_bw = 8000000,
+ .ref_div = TUA6034_REF_DIV_1_24,
+ },
+
+ /* ATSC */
+ {
+ .band_sw_freq = {50000000, 160000000, 454000000, 900000000},
+ .cp_sw_freq = {
+ {0, 160000000, 0},
+ {0, 454000000, 0},
+ {0, 900000000, 0}
+ },
+
+ .if_freq = 44000000,
+ .if_bw = 6000000,
+ .ref_div = TUA6034_REF_DIV_1_64,
+ },
+};
+
+
+static int raw_write(struct tua6034_state *state, const u8 *buf, int len)
+{
+ int ret;
+
+ ret = i2c_master_send(state->i2c, buf, len);
+ if (ret >= 0 && ret < len)
+ ret = -EIO;
+ return (ret == len) ? 0 : ret;
+}
+
+static int raw_read(struct tua6034_state *state, u8 *val)
+{
+ int ret;
+
+ ret = i2c_master_recv(state->i2c, val, 1);
+ if (ret >= 0 && ret < 1)
+ ret = -EIO;
+ return (ret == 1) ? 0 : ret;
+}
+
+static int calc_ctrl_bytes(struct tua6034_state *state, u32 freq)
+{
+ enum tua6034_ref_div rdiv;
+ enum tua6034_band band;
+ enum tua6034_cp_cur cp_cur;
+ const struct def_param *def;
+ int i, j;
+
+ switch (state->cfg.cfg_mode) {
+ case TUA6034_CONFIG_DFLT_ISDBT:
+ case TUA6034_CONFIG_DFLT_DVBT:
+ case TUA6034_CONFIG_DFLT_ATSC:
+ /* user configured band,cp_cur. no auto selection */
+ if (state->cfg.band != TUA6034_BAND_ALL &&
+ state->cfg.cp_cur != TUA6034_CP_DEF) {
+ band = state->cfg.band;
+ cp_cur = state->cfg.cp_cur;
+ break;
+ }
+
+ def = &def_cfgs[state->cfg.cfg_mode];
+ for (i = 0; i < ARRAY_SIZE(def->band_sw_freq); i++)
+ if (freq < def->band_sw_freq[i])
+ break;
+ if (i == 0 || i >= ARRAY_SIZE(def->band_sw_freq))
+ goto invalid_param;
+
+ band = (i == 1) ? TUA6034_BAND_LOW :
+ (i == 2) ? TUA6034_BAND_MID : TUA6034_BAND_HIGH;
+ if (state->cfg.band != TUA6034_BAND_ALL &&
+ state->cfg.band != band)
+ goto invalid_param;
+
+ if (state->cfg.cp_cur != TUA6034_CP_DEF) {
+ cp_cur = state->cfg.cp_cur;
+ break;
+ }
+
+ for (j = 0; j < 3; j++)
+ if (freq < def->cp_sw_freq[i - 1][j])
+ break;
+ cp_cur = j + 1;
+ break;
+
+ case TUA6034_CONFIG_MANNUAL:
+ band = state->cfg.band;
+ cp_cur = state->cfg.cp_cur;
+ break;
+ default:
+ goto invalid_param;
+ }
+
+ rdiv = state->cfg.ref_div;
+ /* check constraints */
+ if ((rdiv == TUA6034_REF_DIV_1_80 || rdiv == TUA6034_REF_DIV_1_128) &&
+ (cp_cur == TUA6034_CP_125uA || cp_cur == TUA6034_CP_650uA)) {
+ if (state->cfg.cfg_mode == TUA6034_CONFIG_MANNUAL)
+ goto invalid_param;
+ cp_cur = TUA6034_CP_250uA;
+ }
+
+ state->ctrl_byte = 0x80;
+ if (cp_cur == TUA6034_CP_250uA || cp_cur == TUA6034_CP_650uA)
+ state->ctrl_byte |= 0x40;
+
+ if (cp_cur == TUA6034_CP_125uA || cp_cur == TUA6034_CP_650uA ||
+ rdiv == TUA6034_REF_DIV_1_32 || rdiv == TUA6034_REF_DIV_1_28)
+ state->ctrl_byte |= 0x30;
+
+ if (cp_cur == TUA6034_CP_125uA || cp_cur == TUA6034_CP_650uA)
+ state->ctrl_byte |= 0x08;
+ if (rdiv == TUA6034_REF_DIV_1_24 || rdiv == TUA6034_REF_DIV_1_64)
+ state->ctrl_byte |= 0x04;
+ if (rdiv == TUA6034_REF_DIV_1_128 ||
+ rdiv == TUA6034_REF_DIV_1_64 || rdiv == TUA6034_REF_DIV_1_28)
+ state->ctrl_byte |= 0x02;
+
+ state->band_sw_byte = band & 0x03;
+
+ state->aux_byte = (state->cfg.agc_tc != TUA6034_AGC_TC_LONG) << 7;
+ state->aux_byte |= (state->cfg.agc_tkov - 1) << 4;
+
+ return 0;
+
+invalid_param:
+ dev_warn(&state->i2c->dev, "invalid tuning parameters.");
+ return -EINVAL;
+}
+
+/* tuner_ops */
+
+static int tua6034_set_config(struct dvb_frontend *fe, void *priv_cfg)
+{
+ struct tua6034_state *state;
+ struct tua6034_config *cfg;
+ const struct def_param *def = NULL;
+ u32 fmin, fmax;
+
+ state = fe->tuner_priv;
+ cfg = priv_cfg;
+
+ state->cfg.agc_tc = cfg->agc_tc;
+ if (cfg->agc_tkov == TUA6034_AGC_TKOV_DEF)
+ state->cfg.agc_tkov = TUA6034_AGC_112dBuV;
+ else
+ state->cfg.agc_tkov = cfg->agc_tkov;
+ state->cfg.ports = cfg->ports;
+
+ state->cfg.cfg_mode = cfg->cfg_mode;
+ switch (cfg->cfg_mode) {
+ case TUA6034_CONFIG_DFLT_ISDBT:
+ case TUA6034_CONFIG_DFLT_DVBT:
+ case TUA6034_CONFIG_DFLT_ATSC:
+ def = &def_cfgs[cfg->cfg_mode];
+
+ state->cfg.if_freq = (cfg->if_freq != 0) ?
+ cfg->if_freq : def->if_freq;
+
+ state->cfg.if_bw = (cfg->if_bw != 0) ?
+ cfg->if_bw : def->if_bw;
+
+ state->cfg.ref_div = (cfg->ref_div != 0) ?
+ cfg->ref_div : def->ref_div;
+ break;
+
+ case TUA6034_CONFIG_MANNUAL:
+ if (cfg->if_freq == 0 || cfg->if_bw == 0 ||
+ cfg->ref_div == 0 || cfg->band == 0 ||
+ cfg->band == TUA6034_BAND_ALL || cfg->cp_cur == 0)
+ return -EINVAL;
+ state->cfg.if_freq = cfg->if_freq;
+ state->cfg.if_bw = cfg->if_bw;
+ state->cfg.ref_div = cfg->ref_div;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ if ((state->cfg.ref_div == TUA6034_REF_DIV_1_80 ||
+ state->cfg.ref_div == TUA6034_REF_DIV_1_128) &&
+ (state->cfg.cp_cur == TUA6034_CP_125uA ||
+ state->cfg.cp_cur == TUA6034_CP_650uA))
+ return -EINVAL;
+
+ if (cfg->band == TUA6034_BAND_NONE)
+ return -EINVAL;
+ state->cfg.band = cfg->band;
+
+ state->cfg.cp_cur = cfg->cp_cur;
+
+ fe->ops.tuner_ops.info.frequency_step =
+ TUA6034_XTL_FREQ / state->cfg.ref_div;
+ if (state->cfg.cfg_mode == TUA6034_CONFIG_MANNUAL) {
+ fmin = 44000000;
+ fmax = 863000000;
+ } else {
+ switch (state->cfg.band) {
+ case TUA6034_BAND_ALL:
+ fmin = def->band_sw_freq[0];
+ fmax = def->band_sw_freq[3];
+ break;
+ case TUA6034_BAND_HIGH:
+ fmin = def->band_sw_freq[2];
+ fmax = def->band_sw_freq[3];
+ break;
+ case TUA6034_BAND_LOW:
+ fmin = def->band_sw_freq[0];
+ fmax = def->band_sw_freq[1];
+ break;
+ case TUA6034_BAND_MID:
+ fmin = def->band_sw_freq[1];
+ fmax = def->band_sw_freq[2];
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ fe->ops.tuner_ops.info.frequency_min = fmin;
+ fe->ops.tuner_ops.info.frequency_max = fmax;
+
+ return 0;
+}
+
+static int tua6034_get_if_freq(struct dvb_frontend *fe, u32 *freq)
+{
+ struct tua6034_state *state;
+
+ if (!freq)
+ return 0;
+
+ state = fe->tuner_priv;
+ *freq = state->cfg.if_freq;
+ return 0;
+}
+
+static int tua6034_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
+{
+ struct tua6034_state *state;
+
+ if (!bw)
+ return 0;
+
+ state = fe->tuner_priv;
+ if (state->cfg.cfg_mode == TUA6034_CONFIG_DFLT_DVBT &&
+ state->cfg.band == TUA6034_BAND_LOW)
+ *bw = 7000000;
+ else
+ *bw = state->cfg.if_bw;
+ return 0;
+}
+
+static int tua6034_set_params(struct dvb_frontend *fe)
+{
+ struct tua6034_state *state;
+ u8 cmd[4];
+ u32 freq;
+ u32 div;
+ int ret;
+
+ state = fe->tuner_priv;
+ freq = fe->dtv_property_cache.frequency + state->cfg.if_freq;
+ div = mult_frac(freq, state->cfg.ref_div, TUA6034_XTL_FREQ);
+ cmd[0] = (div >> 8) & 0x7f;
+ cmd[1] = div & 0xff;
+
+ ret = calc_ctrl_bytes(state, fe->dtv_property_cache.frequency);
+ if (ret < 0)
+ goto failed;
+ cmd[2] = state->ctrl_byte;
+ cmd[3] = state->band_sw_byte;
+ cmd[3] |= (state->cfg.ports << 2);
+ ret = raw_write(state, cmd, 4);
+ if (ret < 0)
+ goto failed;
+
+ usleep_range(50, 200);
+ cmd[2] = (state->ctrl_byte & 0xc7) | 0x18;
+ cmd[3] = state->aux_byte;
+ ret = raw_write(state, cmd, 4);
+ if (ret < 0)
+ goto failed;
+
+ return 0;
+
+failed:
+ dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
+ __func__, fe->dvb->num, fe->id);
+ return ret;
+}
+
+
+static int tua6034_sleep(struct dvb_frontend *fe)
+{
+ struct tua6034_state *state;
+ u8 cmd[2];
+ int ret;
+
+ state = fe->tuner_priv;
+ cmd[0] = state->ctrl_byte | 0x01;
+ /* set band_sw_byte to 0x03(BAND_NONE) to sleep */
+ cmd[1] = 0x03;
+ ret = raw_write(state, cmd, 2);
+ if (ret < 0)
+ dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
+ __func__, fe->dvb->num, fe->id);
+ return 0;
+}
+
+
+static int tua6034_init(struct dvb_frontend *fe)
+{
+ struct tua6034_state *state;
+ u8 rbuf;
+ int i, ret;
+
+ state = fe->tuner_priv;
+
+ /* wait for POR bit to be cleared */
+ for (i = 5; i > 0; i--) {
+ ret = raw_read(state, &rbuf);
+ if (ret < 0 || !(rbuf & 0x80))
+ break;
+ if (i == 1)
+ ret = -ENODEV;
+ else
+ msleep(100);
+ }
+ if (ret < 0) {
+ dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
+ __func__, fe->dvb->num, fe->id);
+ return ret;
+ }
+ return 0;
+}
+
+/* I2C driver functions */
+
+static const struct dvb_tuner_ops tua6034_ops = {
+ .info = {
+ .name = "Infineon TUA6034",
+ /* .frequency_{min,max} are set by set_config() */
+ },
+
+ .init = tua6034_init,
+ .sleep = tua6034_sleep,
+
+ .set_params = tua6034_set_params,
+ .set_config = tua6034_set_config,
+ .get_bandwidth = tua6034_get_bandwidth,
+ .get_if_frequency = tua6034_get_if_freq,
+
+};
+
+
+static int tua6034_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tua6034_state *state;
+ struct dvb_i2c_tuner_config *cfg;
+ struct dvb_frontend *fe;
+ int ret;
+
+
+ cfg = client->dev.platform_data;
+ fe = cfg->fe;
+ state = fe->tuner_priv;
+ state->i2c = client;
+
+ ret = tua6034_set_config(fe, (void *) cfg->devcfg.priv_cfg);
+ if (ret < 0)
+ return ret;
+
+ dev_info(&client->dev, "Infineon TUA6034 attached.\n");
+ return 0;
+}
+
+static const struct i2c_device_id tua6034_id[] = {
+ {"tua6034", 0},
+ {}
+};
+
+static const struct dvb_i2c_module_param tua6034_param = {
+ .ops.tuner_ops = &tua6034_ops,
+ .priv_probe = tua6034_probe,
+
+ .priv_size = sizeof(struct tua6034_state),
+ .is_tuner = true,
+};
+
+DEFINE_DVB_I2C_MODULE(tua6034, tua6034_id, tua6034_param);
+
+MODULE_DESCRIPTION("Infineon TUA6034 terrestial tuner");
+MODULE_AUTHOR("Akihiro TSUKADA");
+MODULE_LICENSE("GPL");
new file mode 100644
@@ -0,0 +1,113 @@
+/*
+ * Infineon TUA6034 terrestial tuner driver
+ *
+ * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
+ *
+ * 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 version 2.
+ *
+ *
+ * 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.
+ */
+
+#ifndef TUA6034_H
+#define TUA6034_H
+
+#include "dvb_frontend.h"
+
+/* AGC timeconstant (and current) */
+enum tua6034_agc_timeconst {
+ TUA6034_AGC_TC_LONG, /* 2sec, 300nA with 160nF on AGC pin */
+ TUA6034_AGC_TC_SHORT, /* 50msec, 9uA with 160nF on AGC pin */
+
+ TUA6034_AGC_TC_DEF = 0, /* long */
+};
+
+/* AGC takeover point */
+enum tua6034_agc_takeover {
+ TUA6034_AGC_TKOV_DEF, /* (112dBuV) */
+ TUA6034_AGC_118dBuV,
+ TUA6034_AGC_115dBuV,
+ TUA6034_AGC_112dBuV,
+ TUA6034_AGC_109dBuV,
+ TUA6034_AGC_106dBuV,
+ TUA6034_AGC_103dBuV,
+ TUA6034_AGC_HIGHZ, /* use external AGC */
+ TUA6034_AGC_DISABLED,
+};
+
+/* use per delivery system default for config parameters */
+enum tua6034_config_def {
+ /* use default config parameters for ISDB-T */
+ TUA6034_CONFIG_DFLT_ISDBT,
+
+ /* use default config parameters for DVB-T */
+ TUA6034_CONFIG_DFLT_DVBT,
+
+ /* use default config parameters for ATSC */
+ TUA6034_CONFIG_DFLT_ATSC,
+
+ /* do not use delsys specific defaults */
+ TUA6034_CONFIG_MANNUAL,
+
+ TUA6034_CONFIG_DFLT_DEF = 0, /* ISDB-T */
+};
+
+/* reference frequency divider */
+enum tua6034_ref_div {
+ TUA6034_REF_DIV_DEF, /* 142.86kHz */
+ /* in normal mode only */
+ TUA6034_REF_DIV_1_80 = 80, /* 50kHz @ 4MHz XSTL */
+ TUA6034_REF_DIV_1_128 = 128, /* 31.25kHz */
+ /* in both modes OK */
+ TUA6034_REF_DIV_1_24 = 24, /* 166.67kHz */
+ TUA6034_REF_DIV_1_64 = 64, /* 62.5kHz */
+ /* in extended mode only */
+ TUA6034_REF_DIV_1_32 = 32, /* 125kHz */
+ TUA6034_REF_DIV_1_28 = 28, /* 142.86kHz */
+};
+
+enum tua6034_band {
+ TUA6034_BAND_HIGH,
+ TUA6034_BAND_LOW,
+ TUA6034_BAND_MID,
+ TUA6034_BAND_NONE, /* stand-by mode */
+ /* all bands supported, and auto-selected by frequency */
+ TUA6034_BAND_ALL,
+
+ TUA6034_BAND_DEF = 0, /* band HIGH */
+};
+
+/* charge pump current */
+enum tua6034_cp_cur {
+ TUA6034_CP_DEF, /* 250uA */
+ TUA6034_CP_50uA,
+ TUA6034_CP_125uA, /* extended mode only */
+ TUA6034_CP_250uA,
+ TUA6034_CP_650uA, /* extended mode only */
+};
+
+
+/* any member can be set to 0, to use the default value */
+struct tua6034_config {
+ enum tua6034_agc_timeconst agc_tc;
+ enum tua6034_agc_takeover agc_tkov;
+#define TUA6034_PORT3_ON 0x01
+#define TUA6034_PORT4_ON 0x02
+ u8 ports;
+
+ enum tua6034_config_def cfg_mode;
+
+ /* the following params have default values from cfg_mode */
+ u32 if_freq;
+ u32 if_bw;
+ enum tua6034_ref_div ref_div;
+ enum tua6034_band band;
+ enum tua6034_cp_cur cp_cur;
+};
+
+#endif /* TUA6034_H */