aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/common
diff options
context:
space:
mode:
authorAntti Palosaari <crope@iki.fi>2010-08-13 02:41:02 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-10-20 23:04:42 -0400
commit51ff2e2c8eaf0cc25c024849743613680b87778c (patch)
tree41ed14bd29d7f42449994f00c6c94d4a819699b8 /drivers/media/common
parentfba32e0c70364f46a3d9ff36af5c85f1bd32ac7a (diff)
V4L/DVB: NXP TDA18218 silicon tuner driver
Signed-off-by: Antti Palosaari <crope@iki.fi> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/common')
-rw-r--r--drivers/media/common/tuners/Kconfig7
-rw-r--r--drivers/media/common/tuners/Makefile1
-rw-r--r--drivers/media/common/tuners/tda18218.c334
-rw-r--r--drivers/media/common/tuners/tda18218.h45
-rw-r--r--drivers/media/common/tuners/tda18218_priv.h106
5 files changed, 493 insertions, 0 deletions
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig
index b3ed5daaacf2..2385e6cca635 100644
--- a/drivers/media/common/tuners/Kconfig
+++ b/drivers/media/common/tuners/Kconfig
@@ -179,4 +179,11 @@ config MEDIA_TUNER_MAX2165
179 help 179 help
180 A driver for the silicon tuner MAX2165 from Maxim. 180 A driver for the silicon tuner MAX2165 from Maxim.
181 181
182config MEDIA_TUNER_TDA18218
183 tristate "NXP TDA18218 silicon tuner"
184 depends on VIDEO_MEDIA && I2C
185 default m if MEDIA_TUNER_CUSTOMISE
186 help
187 NXP TDA18218 silicon tuner driver.
188
182endif # MEDIA_TUNER_CUSTOMISE 189endif # MEDIA_TUNER_CUSTOMISE
diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile
index a5438523f30d..96da03d349ca 100644
--- a/drivers/media/common/tuners/Makefile
+++ b/drivers/media/common/tuners/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o
24obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o 24obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o
25obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o 25obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o
26obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o 26obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
27obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
27 28
28EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core 29EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
29EXTRA_CFLAGS += -Idrivers/media/dvb/frontends 30EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/common/tuners/tda18218.c b/drivers/media/common/tuners/tda18218.c
new file mode 100644
index 000000000000..8da1fdeddaa7
--- /dev/null
+++ b/drivers/media/common/tuners/tda18218.c
@@ -0,0 +1,334 @@
1/*
2 * NXP TDA18218HN silicon tuner driver
3 *
4 * Copyright (C) 2010 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
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21#include "tda18218.h"
22#include "tda18218_priv.h"
23
24static int debug;
25module_param(debug, int, 0644);
26MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
27
28/* write multiple registers */
29static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
30{
31 int ret;
32 u8 buf[1+len], quotient, remainder, i, msg_len, msg_len_max;
33 struct i2c_msg msg[1] = {
34 {
35 .addr = priv->cfg->i2c_address,
36 .flags = 0,
37 .buf = buf,
38 }
39 };
40
41 msg_len_max = priv->cfg->i2c_wr_max - 1;
42 quotient = len / msg_len_max;
43 remainder = len % msg_len_max;
44 msg_len = msg_len_max;
45 for (i = 0; (i <= quotient && remainder); i++) {
46 if (i == quotient) /* set len of the last msg */
47 msg_len = remainder;
48
49 msg[0].len = msg_len + 1;
50 buf[0] = reg + i * msg_len_max;
51 memcpy(&buf[1], &val[i * msg_len_max], msg_len);
52
53 ret = i2c_transfer(priv->i2c, msg, 1);
54 if (ret != 1)
55 break;
56 }
57
58 if (ret == 1) {
59 ret = 0;
60 } else {
61 warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len);
62 ret = -EREMOTEIO;
63 }
64
65 return ret;
66}
67
68/* read multiple registers */
69static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
70{
71 int ret;
72 u8 buf[reg+len]; /* we must start read always from reg 0x00 */
73 struct i2c_msg msg[2] = {
74 {
75 .addr = priv->cfg->i2c_address,
76 .flags = 0,
77 .len = 1,
78 .buf = "\x00",
79 }, {
80 .addr = priv->cfg->i2c_address,
81 .flags = I2C_M_RD,
82 .len = sizeof(buf),
83 .buf = buf,
84 }
85 };
86
87 ret = i2c_transfer(priv->i2c, msg, 2);
88 if (ret == 2) {
89 memcpy(val, &buf[reg], len);
90 ret = 0;
91 } else {
92 warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len);
93 ret = -EREMOTEIO;
94 }
95
96 return ret;
97}
98
99/* write single register */
100static int tda18218_wr_reg(struct tda18218_priv *priv, u8 reg, u8 val)
101{
102 return tda18218_wr_regs(priv, reg, &val, 1);
103}
104
105/* read single register */
106
107static int tda18218_rd_reg(struct tda18218_priv *priv, u8 reg, u8 *val)
108{
109 return tda18218_rd_regs(priv, reg, val, 1);
110}
111
112static int tda18218_set_params(struct dvb_frontend *fe,
113 struct dvb_frontend_parameters *params)
114{
115 struct tda18218_priv *priv = fe->tuner_priv;
116 int ret;
117 u8 buf[3], i, BP_Filter, LP_Fc;
118 u32 LO_Frac;
119 /* TODO: find out correct AGC algorithm */
120 u8 agc[][2] = {
121 { R20_AGC11, 0x60 },
122 { R23_AGC21, 0x02 },
123 { R20_AGC11, 0xa0 },
124 { R23_AGC21, 0x09 },
125 { R20_AGC11, 0xe0 },
126 { R23_AGC21, 0x0c },
127 { R20_AGC11, 0x40 },
128 { R23_AGC21, 0x01 },
129 { R20_AGC11, 0x80 },
130 { R23_AGC21, 0x08 },
131 { R20_AGC11, 0xc0 },
132 { R23_AGC21, 0x0b },
133 { R24_AGC22, 0x1c },
134 { R24_AGC22, 0x0c },
135 };
136
137 if (fe->ops.i2c_gate_ctrl)
138 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
139
140 /* low-pass filter cut-off frequency */
141 switch (params->u.ofdm.bandwidth) {
142 case BANDWIDTH_6_MHZ:
143 LP_Fc = 0;
144 LO_Frac = params->frequency + 4000000;
145 break;
146 case BANDWIDTH_7_MHZ:
147 LP_Fc = 1;
148 LO_Frac = params->frequency + 3500000;
149 break;
150 case BANDWIDTH_8_MHZ:
151 default:
152 LP_Fc = 2;
153 LO_Frac = params->frequency + 4000000;
154 break;
155 }
156
157 /* band-pass filter */
158 if (LO_Frac < 188000000)
159 BP_Filter = 3;
160 else if (LO_Frac < 253000000)
161 BP_Filter = 4;
162 else if (LO_Frac < 343000000)
163 BP_Filter = 5;
164 else
165 BP_Filter = 6;
166
167 buf[0] = (priv->regs[R1A_IF1] & ~7) | BP_Filter; /* BP_Filter */
168 buf[1] = (priv->regs[R1B_IF2] & ~3) | LP_Fc; /* LP_Fc */
169 buf[2] = priv->regs[R1C_AGC2B];
170 ret = tda18218_wr_regs(priv, R1A_IF1, buf, 3);
171 if (ret)
172 goto error;
173
174 buf[0] = (LO_Frac / 1000) >> 12; /* LO_Frac_0 */
175 buf[1] = (LO_Frac / 1000) >> 4; /* LO_Frac_1 */
176 buf[2] = (LO_Frac / 1000) << 4 |
177 (priv->regs[R0C_MD5] & 0x0f); /* LO_Frac_2 */
178 ret = tda18218_wr_regs(priv, R0A_MD3, buf, 3);
179 if (ret)
180 goto error;
181
182 buf[0] = priv->regs[R0F_MD8] | (1 << 6); /* Freq_prog_Start */
183 ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
184 if (ret)
185 goto error;
186
187 buf[0] = priv->regs[R0F_MD8] & ~(1 << 6); /* Freq_prog_Start */
188 ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
189 if (ret)
190 goto error;
191
192 /* trigger AGC */
193 for (i = 0; i < ARRAY_SIZE(agc); i++) {
194 ret = tda18218_wr_reg(priv, agc[i][0], agc[i][1]);
195 if (ret)
196 goto error;
197 }
198
199error:
200 if (fe->ops.i2c_gate_ctrl)
201 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
202
203 if (ret)
204 dbg("%s: failed ret:%d", __func__, ret);
205
206 return ret;
207}
208
209static int tda18218_sleep(struct dvb_frontend *fe)
210{
211 struct tda18218_priv *priv = fe->tuner_priv;
212 int ret;
213
214 if (fe->ops.i2c_gate_ctrl)
215 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
216
217 /* standby */
218 ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
219
220 if (fe->ops.i2c_gate_ctrl)
221 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
222
223 if (ret)
224 dbg("%s: failed ret:%d", __func__, ret);
225
226 return ret;
227}
228
229static int tda18218_init(struct dvb_frontend *fe)
230{
231 struct tda18218_priv *priv = fe->tuner_priv;
232 int ret;
233
234 /* TODO: calibrations */
235
236 if (fe->ops.i2c_gate_ctrl)
237 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
238
239 ret = tda18218_wr_regs(priv, R00_ID, priv->regs, TDA18218_NUM_REGS);
240
241 if (fe->ops.i2c_gate_ctrl)
242 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
243
244 if (ret)
245 dbg("%s: failed ret:%d", __func__, ret);
246
247 return ret;
248}
249
250static int tda18218_release(struct dvb_frontend *fe)
251{
252 kfree(fe->tuner_priv);
253 fe->tuner_priv = NULL;
254 return 0;
255}
256
257static const struct dvb_tuner_ops tda18218_tuner_ops = {
258 .info = {
259 .name = "NXP TDA18218",
260
261 .frequency_min = 174000000,
262 .frequency_max = 864000000,
263 .frequency_step = 1000,
264 },
265
266 .release = tda18218_release,
267 .init = tda18218_init,
268 .sleep = tda18218_sleep,
269
270 .set_params = tda18218_set_params,
271};
272
273struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
274 struct i2c_adapter *i2c, struct tda18218_config *cfg)
275{
276 struct tda18218_priv *priv = NULL;
277 u8 val;
278 int ret;
279 /* chip default registers values */
280 static u8 def_regs[] = {
281 0xc0, 0x88, 0x00, 0x8e, 0x03, 0x00, 0x00, 0xd0, 0x00, 0x40,
282 0x00, 0x00, 0x07, 0xff, 0x84, 0x09, 0x00, 0x13, 0x00, 0x00,
283 0x01, 0x84, 0x09, 0xf0, 0x19, 0x0a, 0x8e, 0x69, 0x98, 0x01,
284 0x00, 0x58, 0x10, 0x40, 0x8c, 0x00, 0x0c, 0x48, 0x85, 0xc9,
285 0xa7, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x00, 0x39, 0x00,
286 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf6
287 };
288
289 priv = kzalloc(sizeof(struct tda18218_priv), GFP_KERNEL);
290 if (priv == NULL)
291 return NULL;
292
293 priv->cfg = cfg;
294 priv->i2c = i2c;
295 fe->tuner_priv = priv;
296
297 if (fe->ops.i2c_gate_ctrl)
298 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
299
300 /* check if the tuner is there */
301 ret = tda18218_rd_reg(priv, R00_ID, &val);
302 dbg("%s: ret:%d chip ID:%02x", __func__, ret, val);
303 if (ret || val != def_regs[R00_ID]) {
304 kfree(priv);
305 return NULL;
306 }
307
308 info("NXP TDA18218HN successfully identified.");
309
310 memcpy(&fe->ops.tuner_ops, &tda18218_tuner_ops,
311 sizeof(struct dvb_tuner_ops));
312 memcpy(priv->regs, def_regs, sizeof(def_regs));
313
314 /* loop-through enabled chip default register values */
315 if (priv->cfg->loop_through) {
316 priv->regs[R17_PD1] = 0xb0;
317 priv->regs[R18_PD2] = 0x59;
318 }
319
320 /* standby */
321 ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
322 if (ret)
323 dbg("%s: failed ret:%d", __func__, ret);
324
325 if (fe->ops.i2c_gate_ctrl)
326 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
327
328 return fe;
329}
330EXPORT_SYMBOL(tda18218_attach);
331
332MODULE_DESCRIPTION("NXP TDA18218HN silicon tuner driver");
333MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
334MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tda18218.h b/drivers/media/common/tuners/tda18218.h
new file mode 100644
index 000000000000..b4180d180029
--- /dev/null
+++ b/drivers/media/common/tuners/tda18218.h
@@ -0,0 +1,45 @@
1/*
2 * NXP TDA18218HN silicon tuner driver
3 *
4 * Copyright (C) 2010 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
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21#ifndef TDA18218_H
22#define TDA18218_H
23
24#include "dvb_frontend.h"
25
26struct tda18218_config {
27 u8 i2c_address;
28 u8 i2c_wr_max;
29 u8 loop_through:1;
30};
31
32#if defined(CONFIG_MEDIA_TUNER_TDA18218) || \
33 (defined(CONFIG_MEDIA_TUNER_TDA18218_MODULE) && defined(MODULE))
34extern struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
35 struct i2c_adapter *i2c, struct tda18218_config *cfg);
36#else
37static inline struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
38 struct i2c_adapter *i2c, struct tda18218_config *cfg)
39{
40 printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
41 return NULL;
42}
43#endif
44
45#endif
diff --git a/drivers/media/common/tuners/tda18218_priv.h b/drivers/media/common/tuners/tda18218_priv.h
new file mode 100644
index 000000000000..904e5365c78c
--- /dev/null
+++ b/drivers/media/common/tuners/tda18218_priv.h
@@ -0,0 +1,106 @@
1/*
2 * NXP TDA18218HN silicon tuner driver
3 *
4 * Copyright (C) 2010 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
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21#ifndef TDA18218_PRIV_H
22#define TDA18218_PRIV_H
23
24#define LOG_PREFIX "tda18218"
25
26#undef dbg
27#define dbg(f, arg...) \
28 if (debug) \
29 printk(KERN_DEBUG LOG_PREFIX": " f "\n" , ## arg)
30#undef err
31#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
32#undef info
33#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
34#undef warn
35#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
36
37#define R00_ID 0x00 /* ID byte */
38#define R01_R1 0x01 /* Read byte 1 */
39#define R02_R2 0x02 /* Read byte 2 */
40#define R03_R3 0x03 /* Read byte 3 */
41#define R04_R4 0x04 /* Read byte 4 */
42#define R05_R5 0x05 /* Read byte 5 */
43#define R06_R6 0x06 /* Read byte 6 */
44#define R07_MD1 0x07 /* Main divider byte 1 */
45#define R08_PSM1 0x08 /* PSM byte 1 */
46#define R09_MD2 0x09 /* Main divider byte 2 */
47#define R0A_MD3 0x0a /* Main divider byte 1 */
48#define R0B_MD4 0x0b /* Main divider byte 4 */
49#define R0C_MD5 0x0c /* Main divider byte 5 */
50#define R0D_MD6 0x0d /* Main divider byte 6 */
51#define R0E_MD7 0x0e /* Main divider byte 7 */
52#define R0F_MD8 0x0f /* Main divider byte 8 */
53#define R10_CD1 0x10 /* Call divider byte 1 */
54#define R11_CD2 0x11 /* Call divider byte 2 */
55#define R12_CD3 0x12 /* Call divider byte 3 */
56#define R13_CD4 0x13 /* Call divider byte 4 */
57#define R14_CD5 0x14 /* Call divider byte 5 */
58#define R15_CD6 0x15 /* Call divider byte 6 */
59#define R16_CD7 0x16 /* Call divider byte 7 */
60#define R17_PD1 0x17 /* Power-down byte 1 */
61#define R18_PD2 0x18 /* Power-down byte 2 */
62#define R19_XTOUT 0x19 /* XTOUT byte */
63#define R1A_IF1 0x1a /* IF byte 1 */
64#define R1B_IF2 0x1b /* IF byte 2 */
65#define R1C_AGC2B 0x1c /* AGC2b byte */
66#define R1D_PSM2 0x1d /* PSM byte 2 */
67#define R1E_PSM3 0x1e /* PSM byte 3 */
68#define R1F_PSM4 0x1f /* PSM byte 4 */
69#define R20_AGC11 0x20 /* AGC1 byte 1 */
70#define R21_AGC12 0x21 /* AGC1 byte 2 */
71#define R22_AGC13 0x22 /* AGC1 byte 3 */
72#define R23_AGC21 0x23 /* AGC2 byte 1 */
73#define R24_AGC22 0x24 /* AGC2 byte 2 */
74#define R25_AAGC 0x25 /* Analog AGC byte */
75#define R26_RC 0x26 /* RC byte */
76#define R27_RSSI 0x27 /* RSSI byte */
77#define R28_IRCAL1 0x28 /* IR CAL byte 1 */
78#define R29_IRCAL2 0x29 /* IR CAL byte 2 */
79#define R2A_IRCAL3 0x2a /* IR CAL byte 3 */
80#define R2B_IRCAL4 0x2b /* IR CAL byte 4 */
81#define R2C_RFCAL1 0x2c /* RF CAL byte 1 */
82#define R2D_RFCAL2 0x2d /* RF CAL byte 2 */
83#define R2E_RFCAL3 0x2e /* RF CAL byte 3 */
84#define R2F_RFCAL4 0x2f /* RF CAL byte 4 */
85#define R30_RFCAL5 0x30 /* RF CAL byte 5 */
86#define R31_RFCAL6 0x31 /* RF CAL byte 6 */
87#define R32_RFCAL7 0x32 /* RF CAL byte 7 */
88#define R33_RFCAL8 0x33 /* RF CAL byte 8 */
89#define R34_RFCAL9 0x34 /* RF CAL byte 9 */
90#define R35_RFCAL10 0x35 /* RF CAL byte 10 */
91#define R36_RFCALRAM1 0x36 /* RF CAL RAM byte 1 */
92#define R37_RFCALRAM2 0x37 /* RF CAL RAM byte 2 */
93#define R38_MARGIN 0x38 /* Margin byte */
94#define R39_FMAX1 0x39 /* Fmax byte 1 */
95#define R3A_FMAX2 0x3a /* Fmax byte 2 */
96
97#define TDA18218_NUM_REGS 59
98
99struct tda18218_priv {
100 struct tda18218_config *cfg;
101 struct i2c_adapter *i2c;
102
103 u8 regs[TDA18218_NUM_REGS];
104};
105
106#endif