aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/common/tuners/mc44s803.c
diff options
context:
space:
mode:
authorJochen Friedrich <jochen@scram.de>2009-02-02 12:50:09 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-03-30 11:42:39 -0400
commitb72dbaefbdcdfc9b69fc3861b9de0a6240f5cc8a (patch)
treea5e6e497c9acae0f6418ae8a0ac4203d74f9f656 /drivers/media/common/tuners/mc44s803.c
parent812b1f9d54a5f75066f8a5c92166a979c48c98c6 (diff)
V4L/DVB (10452): Add Freescale MC44S803 tuner driver
Add Freescale MC44S803 tuner driver. Signed-off-by: Jochen Friedrich <jochen@scram.de> Signed-off-by: Antti Palosaari <crope@iki.fi> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/common/tuners/mc44s803.c')
-rw-r--r--drivers/media/common/tuners/mc44s803.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/drivers/media/common/tuners/mc44s803.c b/drivers/media/common/tuners/mc44s803.c
new file mode 100644
index 000000000000..20c4485ce16a
--- /dev/null
+++ b/drivers/media/common/tuners/mc44s803.c
@@ -0,0 +1,371 @@
1/*
2 * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner
3 *
4 * Copyright (c) 2009 Jochen Friedrich <jochen@scram.de>
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 *
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
20 */
21
22#include <linux/module.h>
23#include <linux/delay.h>
24#include <linux/dvb/frontend.h>
25#include <linux/i2c.h>
26
27#include "dvb_frontend.h"
28
29#include "mc44s803.h"
30#include "mc44s803_priv.h"
31
32#define mc_printk(level, format, arg...) \
33 printk(level "mc44s803: " format , ## arg)
34
35/* Writes a single register */
36static int mc44s803_writereg(struct mc44s803_priv *priv, u32 val)
37{
38 u8 buf[3];
39 struct i2c_msg msg = {
40 .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 3
41 };
42
43 buf[0] = (val & 0xff0000) >> 16;
44 buf[1] = (val & 0xff00) >> 8;
45 buf[2] = (val & 0xff);
46
47 if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
48 mc_printk(KERN_WARNING, "I2C write failed\n");
49 return -EREMOTEIO;
50 }
51 return 0;
52}
53
54/* Reads a single register */
55static int mc44s803_readreg(struct mc44s803_priv *priv, u8 reg, u32 *val)
56{
57 u32 wval;
58 u8 buf[3];
59 int ret;
60 struct i2c_msg msg[] = {
61 { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD,
62 .buf = buf, .len = 3 },
63 };
64
65 wval = MC44S803_REG_SM(MC44S803_REG_DATAREG, MC44S803_ADDR) |
66 MC44S803_REG_SM(reg, MC44S803_D);
67
68 ret = mc44s803_writereg(priv, wval);
69 if (ret)
70 return ret;
71
72 if (i2c_transfer(priv->i2c, msg, 1) != 1) {
73 mc_printk(KERN_WARNING, "I2C read failed\n");
74 return -EREMOTEIO;
75 }
76
77 *val = (buf[0] << 16) | (buf[1] << 8) | buf[2];
78
79 return 0;
80}
81
82static int mc44s803_release(struct dvb_frontend *fe)
83{
84 struct mc44s803_priv *priv = fe->tuner_priv;
85
86 fe->tuner_priv = NULL;
87 kfree(priv);
88
89 return 0;
90}
91
92static int mc44s803_init(struct dvb_frontend *fe)
93{
94 struct mc44s803_priv *priv = fe->tuner_priv;
95 u32 val;
96 int err;
97
98 if (fe->ops.i2c_gate_ctrl)
99 fe->ops.i2c_gate_ctrl(fe, 1);
100
101/* Reset chip */
102 val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR) |
103 MC44S803_REG_SM(1, MC44S803_RS);
104
105 err = mc44s803_writereg(priv, val);
106 if (err)
107 goto exit;
108
109 val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR);
110
111 err = mc44s803_writereg(priv, val);
112 if (err)
113 goto exit;
114
115/* Power Up and Start Osc */
116
117 val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) |
118 MC44S803_REG_SM(0xC0, MC44S803_REFOSC) |
119 MC44S803_REG_SM(1, MC44S803_OSCSEL);
120
121 err = mc44s803_writereg(priv, val);
122 if (err)
123 goto exit;
124
125 val = MC44S803_REG_SM(MC44S803_REG_POWER, MC44S803_ADDR) |
126 MC44S803_REG_SM(0x200, MC44S803_POWER);
127
128 err = mc44s803_writereg(priv, val);
129 if (err)
130 goto exit;
131
132 msleep(10);
133
134 val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) |
135 MC44S803_REG_SM(0x40, MC44S803_REFOSC) |
136 MC44S803_REG_SM(1, MC44S803_OSCSEL);
137
138 err = mc44s803_writereg(priv, val);
139 if (err)
140 goto exit;
141
142 msleep(20);
143
144/* Setup Mixer */
145
146 val = MC44S803_REG_SM(MC44S803_REG_MIXER, MC44S803_ADDR) |
147 MC44S803_REG_SM(1, MC44S803_TRI_STATE) |
148 MC44S803_REG_SM(0x7F, MC44S803_MIXER_RES);
149
150 err = mc44s803_writereg(priv, val);
151 if (err)
152 goto exit;
153
154/* Setup Cirquit Adjust */
155
156 val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) |
157 MC44S803_REG_SM(1, MC44S803_G1) |
158 MC44S803_REG_SM(1, MC44S803_G3) |
159 MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) |
160 MC44S803_REG_SM(1, MC44S803_G6) |
161 MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) |
162 MC44S803_REG_SM(0x3, MC44S803_LP) |
163 MC44S803_REG_SM(1, MC44S803_CLRF) |
164 MC44S803_REG_SM(1, MC44S803_CLIF);
165
166 err = mc44s803_writereg(priv, val);
167 if (err)
168 goto exit;
169
170 val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) |
171 MC44S803_REG_SM(1, MC44S803_G1) |
172 MC44S803_REG_SM(1, MC44S803_G3) |
173 MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) |
174 MC44S803_REG_SM(1, MC44S803_G6) |
175 MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) |
176 MC44S803_REG_SM(0x3, MC44S803_LP);
177
178 err = mc44s803_writereg(priv, val);
179 if (err)
180 goto exit;
181
182/* Setup Digtune */
183
184 val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
185 MC44S803_REG_SM(3, MC44S803_XOD);
186
187 err = mc44s803_writereg(priv, val);
188 if (err)
189 goto exit;
190
191/* Setup AGC */
192
193 val = MC44S803_REG_SM(MC44S803_REG_LNAAGC, MC44S803_ADDR) |
194 MC44S803_REG_SM(1, MC44S803_AT1) |
195 MC44S803_REG_SM(1, MC44S803_AT2) |
196 MC44S803_REG_SM(1, MC44S803_AGC_AN_DIG) |
197 MC44S803_REG_SM(1, MC44S803_AGC_READ_EN) |
198 MC44S803_REG_SM(1, MC44S803_LNA0);
199
200 err = mc44s803_writereg(priv, val);
201 if (err)
202 goto exit;
203
204 if (fe->ops.i2c_gate_ctrl)
205 fe->ops.i2c_gate_ctrl(fe, 0);
206 return 0;
207
208exit:
209 if (fe->ops.i2c_gate_ctrl)
210 fe->ops.i2c_gate_ctrl(fe, 0);
211
212 mc_printk(KERN_WARNING, "I/O Error\n");
213 return err;
214}
215
216static int mc44s803_set_params(struct dvb_frontend *fe,
217 struct dvb_frontend_parameters *params)
218{
219 struct mc44s803_priv *priv = fe->tuner_priv;
220 u32 r1, r2, n1, n2, lo1, lo2, freq, val;
221 int err;
222
223 priv->frequency = params->frequency;
224
225 r1 = MC44S803_OSC / 1000000;
226 r2 = MC44S803_OSC / 100000;
227
228 n1 = (params->frequency + MC44S803_IF1 + 500000) / 1000000;
229 freq = MC44S803_OSC / r1 * n1;
230 lo1 = ((60 * n1) + (r1 / 2)) / r1;
231 freq = freq - params->frequency;
232
233 n2 = (freq - MC44S803_IF2 + 50000) / 100000;
234 lo2 = ((60 * n2) + (r2 / 2)) / r2;
235
236 if (fe->ops.i2c_gate_ctrl)
237 fe->ops.i2c_gate_ctrl(fe, 1);
238
239 val = MC44S803_REG_SM(MC44S803_REG_REFDIV, MC44S803_ADDR) |
240 MC44S803_REG_SM(r1-1, MC44S803_R1) |
241 MC44S803_REG_SM(r2-1, MC44S803_R2) |
242 MC44S803_REG_SM(1, MC44S803_REFBUF_EN);
243
244 err = mc44s803_writereg(priv, val);
245 if (err)
246 goto exit;
247
248 val = MC44S803_REG_SM(MC44S803_REG_LO1, MC44S803_ADDR) |
249 MC44S803_REG_SM(n1-2, MC44S803_LO1);
250
251 err = mc44s803_writereg(priv, val);
252 if (err)
253 goto exit;
254
255 val = MC44S803_REG_SM(MC44S803_REG_LO2, MC44S803_ADDR) |
256 MC44S803_REG_SM(n2-2, MC44S803_LO2);
257
258 err = mc44s803_writereg(priv, val);
259 if (err)
260 goto exit;
261
262 val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
263 MC44S803_REG_SM(1, MC44S803_DA) |
264 MC44S803_REG_SM(lo1, MC44S803_LO_REF) |
265 MC44S803_REG_SM(1, MC44S803_AT);
266
267 err = mc44s803_writereg(priv, val);
268 if (err)
269 goto exit;
270
271 val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
272 MC44S803_REG_SM(2, MC44S803_DA) |
273 MC44S803_REG_SM(lo2, MC44S803_LO_REF) |
274 MC44S803_REG_SM(1, MC44S803_AT);
275
276 err = mc44s803_writereg(priv, val);
277 if (err)
278 goto exit;
279
280 if (fe->ops.i2c_gate_ctrl)
281 fe->ops.i2c_gate_ctrl(fe, 0);
282
283 return 0;
284
285exit:
286 if (fe->ops.i2c_gate_ctrl)
287 fe->ops.i2c_gate_ctrl(fe, 0);
288
289 mc_printk(KERN_WARNING, "I/O Error\n");
290 return err;
291}
292
293static int mc44s803_get_frequency(struct dvb_frontend *fe, u32 *frequency)
294{
295 struct mc44s803_priv *priv = fe->tuner_priv;
296 *frequency = priv->frequency;
297 return 0;
298}
299
300static const struct dvb_tuner_ops mc44s803_tuner_ops = {
301 .info = {
302 .name = "Freescale MC44S803",
303 .frequency_min = 48000000,
304 .frequency_max = 1000000000,
305 .frequency_step = 100000,
306 },
307
308 .release = mc44s803_release,
309 .init = mc44s803_init,
310 .set_params = mc44s803_set_params,
311 .get_frequency = mc44s803_get_frequency
312};
313
314/* This functions tries to identify a MC44S803 tuner by reading the ID
315 register. This is hasty. */
316struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe,
317 struct i2c_adapter *i2c, struct mc44s803_config *cfg)
318{
319 struct mc44s803_priv *priv;
320 u32 reg;
321 u8 id;
322 int ret;
323
324 reg = 0;
325
326 priv = kzalloc(sizeof(struct mc44s803_priv), GFP_KERNEL);
327 if (priv == NULL)
328 return NULL;
329
330 priv->cfg = cfg;
331 priv->i2c = i2c;
332 priv->fe = fe;
333
334 if (fe->ops.i2c_gate_ctrl)
335 fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
336
337 ret = mc44s803_readreg(priv, MC44S803_REG_ID, &reg);
338 if (ret)
339 goto error;
340
341 id = MC44S803_REG_MS(reg, MC44S803_ID);
342
343 if (id != 0x14) {
344 mc_printk(KERN_ERR, "unsupported ID "
345 "(%x should be 0x14)\n", id);
346 goto error;
347 }
348
349 mc_printk(KERN_INFO, "successfully identified (ID = %x)\n", id);
350 memcpy(&fe->ops.tuner_ops, &mc44s803_tuner_ops,
351 sizeof(struct dvb_tuner_ops));
352
353 fe->tuner_priv = priv;
354
355 if (fe->ops.i2c_gate_ctrl)
356 fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
357
358 return fe;
359
360error:
361 if (fe->ops.i2c_gate_ctrl)
362 fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
363
364 kfree(priv);
365 return NULL;
366}
367EXPORT_SYMBOL(mc44s803_attach);
368
369MODULE_AUTHOR("Jochen Friedrich");
370MODULE_DESCRIPTION("Freescale MC44S803 silicon tuner driver");
371MODULE_LICENSE("GPL");