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