blob: e29cc2bc113a91604f03b009a7f2877dd4a7d0e2 [file] [log] [blame]
Antti Palosaari26eb7042011-04-09 20:07:30 -03001/*
2 * NXP TDA18212HN silicon tuner driver
3 *
4 * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
Joe Perches2b507632011-07-31 04:30:10 -030021#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22
23#include "tda18212.h"
24
25struct tda18212_priv {
26 struct tda18212_config *cfg;
27 struct i2c_adapter *i2c;
28};
29
30#define dbg(fmt, arg...) \
31do { \
32 if (debug) \
33 pr_info("%s: " fmt, __func__, ##arg); \
34} while (0)
Antti Palosaari26eb7042011-04-09 20:07:30 -030035
36static int debug;
37module_param(debug, int, 0644);
38MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
39
40/* write multiple registers */
41static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
42 int len)
43{
44 int ret;
45 u8 buf[len+1];
46 struct i2c_msg msg[1] = {
47 {
48 .addr = priv->cfg->i2c_address,
49 .flags = 0,
50 .len = sizeof(buf),
51 .buf = buf,
52 }
53 };
54
55 buf[0] = reg;
56 memcpy(&buf[1], val, len);
57
58 ret = i2c_transfer(priv->i2c, msg, 1);
59 if (ret == 1) {
60 ret = 0;
61 } else {
Joe Perches2b507632011-07-31 04:30:10 -030062 pr_warn("i2c wr failed ret:%d reg:%02x len:%d\n",
63 ret, reg, len);
Antti Palosaari26eb7042011-04-09 20:07:30 -030064 ret = -EREMOTEIO;
65 }
66 return ret;
67}
68
69/* read multiple registers */
70static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
71 int len)
72{
73 int ret;
74 u8 buf[len];
75 struct i2c_msg msg[2] = {
76 {
77 .addr = priv->cfg->i2c_address,
78 .flags = 0,
79 .len = 1,
80 .buf = &reg,
81 }, {
82 .addr = priv->cfg->i2c_address,
83 .flags = I2C_M_RD,
84 .len = sizeof(buf),
85 .buf = buf,
86 }
87 };
88
89 ret = i2c_transfer(priv->i2c, msg, 2);
90 if (ret == 2) {
91 memcpy(val, buf, len);
92 ret = 0;
93 } else {
Joe Perches2b507632011-07-31 04:30:10 -030094 pr_warn("i2c rd failed ret:%d reg:%02x len:%d\n",
95 ret, reg, len);
Antti Palosaari26eb7042011-04-09 20:07:30 -030096 ret = -EREMOTEIO;
97 }
98
99 return ret;
100}
101
102/* write single register */
103static int tda18212_wr_reg(struct tda18212_priv *priv, u8 reg, u8 val)
104{
105 return tda18212_wr_regs(priv, reg, &val, 1);
106}
107
108/* read single register */
109static int tda18212_rd_reg(struct tda18212_priv *priv, u8 reg, u8 *val)
110{
111 return tda18212_rd_regs(priv, reg, val, 1);
112}
113
114#if 0 /* keep, useful when developing driver */
115static void tda18212_dump_regs(struct tda18212_priv *priv)
116{
117 int i;
118 u8 buf[256];
119
120 #define TDA18212_RD_LEN 32
121 for (i = 0; i < sizeof(buf); i += TDA18212_RD_LEN)
122 tda18212_rd_regs(priv, i, &buf[i], TDA18212_RD_LEN);
123
124 print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 32, 1, buf,
125 sizeof(buf), true);
126
127 return;
128}
129#endif
130
131static int tda18212_set_params(struct dvb_frontend *fe,
132 struct dvb_frontend_parameters *p)
133{
134 struct tda18212_priv *priv = fe->tuner_priv;
135 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
136 int ret, i;
137 u32 if_khz;
138 u8 buf[9];
139 static const u8 bw_params[][3] = {
140 /* 0f 13 23 */
141 { 0xb3, 0x20, 0x03 }, /* DVB-T 6 MHz */
142 { 0xb3, 0x31, 0x01 }, /* DVB-T 7 MHz */
143 { 0xb3, 0x22, 0x01 }, /* DVB-T 8 MHz */
144 { 0x92, 0x53, 0x03 }, /* DVB-C */
145 };
146
Joe Perches2b507632011-07-31 04:30:10 -0300147 dbg("delsys=%d RF=%d BW=%d\n",
148 c->delivery_system, c->frequency, c->bandwidth_hz);
Antti Palosaari26eb7042011-04-09 20:07:30 -0300149
150 if (fe->ops.i2c_gate_ctrl)
151 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
152
153 switch (c->delivery_system) {
154 case SYS_DVBT:
155 switch (c->bandwidth_hz) {
156 case 6000000:
157 if_khz = priv->cfg->if_dvbt_6;
158 i = 0;
159 break;
160 case 7000000:
161 if_khz = priv->cfg->if_dvbt_7;
162 i = 1;
163 break;
164 case 8000000:
165 if_khz = priv->cfg->if_dvbt_8;
166 i = 2;
167 break;
168 default:
169 ret = -EINVAL;
170 goto error;
171 }
172 break;
173 case SYS_DVBC_ANNEX_AC:
174 if_khz = priv->cfg->if_dvbc;
175 i = 3;
176 break;
177 default:
178 ret = -EINVAL;
179 goto error;
180 }
181
182 ret = tda18212_wr_reg(priv, 0x23, bw_params[i][2]);
183 if (ret)
184 goto error;
185
186 ret = tda18212_wr_reg(priv, 0x06, 0x00);
187 if (ret)
188 goto error;
189
190 ret = tda18212_wr_reg(priv, 0x0f, bw_params[i][0]);
191 if (ret)
192 goto error;
193
194 buf[0] = 0x02;
195 buf[1] = bw_params[i][1];
196 buf[2] = 0x03; /* default value */
197 buf[3] = if_khz / 50;
198 buf[4] = ((c->frequency / 1000) >> 16) & 0xff;
199 buf[5] = ((c->frequency / 1000) >> 8) & 0xff;
200 buf[6] = ((c->frequency / 1000) >> 0) & 0xff;
201 buf[7] = 0xc1;
202 buf[8] = 0x01;
203 ret = tda18212_wr_regs(priv, 0x12, buf, sizeof(buf));
204 if (ret)
205 goto error;
206
207exit:
208 if (fe->ops.i2c_gate_ctrl)
209 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
210
211 return ret;
212
213error:
Joe Perches2b507632011-07-31 04:30:10 -0300214 dbg("failed:%d\n", ret);
Antti Palosaari26eb7042011-04-09 20:07:30 -0300215 goto exit;
216}
217
218static int tda18212_release(struct dvb_frontend *fe)
219{
220 kfree(fe->tuner_priv);
221 fe->tuner_priv = NULL;
222 return 0;
223}
224
225static const struct dvb_tuner_ops tda18212_tuner_ops = {
226 .info = {
227 .name = "NXP TDA18212",
228
229 .frequency_min = 48000000,
230 .frequency_max = 864000000,
231 .frequency_step = 1000,
232 },
233
234 .release = tda18212_release,
235
236 .set_params = tda18212_set_params,
237};
238
239struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
240 struct i2c_adapter *i2c, struct tda18212_config *cfg)
241{
242 struct tda18212_priv *priv = NULL;
243 int ret;
244 u8 val;
245
246 priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL);
247 if (priv == NULL)
248 return NULL;
249
250 priv->cfg = cfg;
251 priv->i2c = i2c;
252 fe->tuner_priv = priv;
253
254 if (fe->ops.i2c_gate_ctrl)
255 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
256
257 /* check if the tuner is there */
258 ret = tda18212_rd_reg(priv, 0x00, &val);
259
260 if (fe->ops.i2c_gate_ctrl)
261 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
262
Joe Perches2b507632011-07-31 04:30:10 -0300263 dbg("ret:%d chip ID:%02x\n", ret, val);
Antti Palosaari26eb7042011-04-09 20:07:30 -0300264 if (ret || val != 0xc7) {
265 kfree(priv);
266 return NULL;
267 }
268
Joe Perches2b507632011-07-31 04:30:10 -0300269 pr_info("NXP TDA18212HN successfully identified\n");
Antti Palosaari26eb7042011-04-09 20:07:30 -0300270
271 memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops,
272 sizeof(struct dvb_tuner_ops));
273
274 return fe;
275}
276EXPORT_SYMBOL(tda18212_attach);
277
278MODULE_DESCRIPTION("NXP TDA18212HN silicon tuner driver");
279MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
280MODULE_LICENSE("GPL");