7b2f301b1d5ca19deb0de3377f37b4f569d988bc
[linux-2.6-block.git] / drivers / media / dvb-frontends / ts2020.c
1 /*
2     Montage Technology TS2020 - Silicon Tuner driver
3     Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com>
4
5     Copyright (C) 2009-2012 TurboSight.com
6
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
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 "dvb_frontend.h"
23 #include "ts2020.h"
24
25 #define TS2020_XTAL_FREQ   27000 /* in kHz */
26 #define FREQ_OFFSET_LOW_SYM_RATE 3000
27
28 struct ts2020_priv {
29         struct i2c_client *client;
30         struct dvb_frontend *fe;
31         /* i2c details */
32         int i2c_address;
33         struct i2c_adapter *i2c;
34         u8 clk_out:2;
35         u8 clk_out_div:5;
36         u32 frequency_div; /* LO output divider switch frequency */
37         u32 frequency_khz; /* actual used LO frequency */
38 #define TS2020_M88TS2020 0
39 #define TS2020_M88TS2022 1
40         u8 tuner;
41         u8 loop_through:1;
42 };
43
44 struct ts2020_reg_val {
45         u8 reg;
46         u8 val;
47 };
48
49 static int ts2020_release(struct dvb_frontend *fe)
50 {
51         struct ts2020_priv *priv = fe->tuner_priv;
52         struct i2c_client *client = priv->client;
53
54         dev_dbg(&client->dev, "\n");
55
56         i2c_unregister_device(client);
57         return 0;
58 }
59
60 static int ts2020_writereg(struct dvb_frontend *fe, int reg, int data)
61 {
62         struct ts2020_priv *priv = fe->tuner_priv;
63         u8 buf[] = { reg, data };
64         struct i2c_msg msg[] = {
65                 {
66                         .addr = priv->i2c_address,
67                         .flags = 0,
68                         .buf = buf,
69                         .len = 2
70                 }
71         };
72         int err;
73
74         if (fe->ops.i2c_gate_ctrl)
75                 fe->ops.i2c_gate_ctrl(fe, 1);
76
77         err = i2c_transfer(priv->i2c, msg, 1);
78         if (err != 1) {
79                 printk(KERN_ERR
80                        "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n",
81                        __func__, err, reg, data);
82                 return -EREMOTEIO;
83         }
84
85         if (fe->ops.i2c_gate_ctrl)
86                 fe->ops.i2c_gate_ctrl(fe, 0);
87
88         return 0;
89 }
90
91 static int ts2020_readreg(struct dvb_frontend *fe, u8 reg)
92 {
93         struct ts2020_priv *priv = fe->tuner_priv;
94         int ret;
95         u8 b0[] = { reg };
96         u8 b1[] = { 0 };
97         struct i2c_msg msg[] = {
98                 {
99                         .addr = priv->i2c_address,
100                         .flags = 0,
101                         .buf = b0,
102                         .len = 1
103                 }, {
104                         .addr = priv->i2c_address,
105                         .flags = I2C_M_RD,
106                         .buf = b1,
107                         .len = 1
108                 }
109         };
110
111         if (fe->ops.i2c_gate_ctrl)
112                 fe->ops.i2c_gate_ctrl(fe, 1);
113
114         ret = i2c_transfer(priv->i2c, msg, 2);
115
116         if (ret != 2) {
117                 printk(KERN_ERR "%s: reg=0x%x(error=%d)\n",
118                        __func__, reg, ret);
119                 return ret;
120         }
121
122         if (fe->ops.i2c_gate_ctrl)
123                 fe->ops.i2c_gate_ctrl(fe, 0);
124
125         return b1[0];
126 }
127
128 static int ts2020_sleep(struct dvb_frontend *fe)
129 {
130         struct ts2020_priv *priv = fe->tuner_priv;
131         u8 u8tmp;
132
133         if (priv->tuner == TS2020_M88TS2020)
134                 u8tmp = 0x0a; /* XXX: probably wrong */
135         else
136                 u8tmp = 0x00;
137
138         return ts2020_writereg(fe, u8tmp, 0x00);
139 }
140
141 static int ts2020_init(struct dvb_frontend *fe)
142 {
143         struct ts2020_priv *priv = fe->tuner_priv;
144         int i;
145         u8 u8tmp;
146
147         if (priv->tuner == TS2020_M88TS2020) {
148                 ts2020_writereg(fe, 0x42, 0x73);
149                 ts2020_writereg(fe, 0x05, priv->clk_out_div);
150                 ts2020_writereg(fe, 0x20, 0x27);
151                 ts2020_writereg(fe, 0x07, 0x02);
152                 ts2020_writereg(fe, 0x11, 0xff);
153                 ts2020_writereg(fe, 0x60, 0xf9);
154                 ts2020_writereg(fe, 0x08, 0x01);
155                 ts2020_writereg(fe, 0x00, 0x41);
156         } else {
157                 static const struct ts2020_reg_val reg_vals[] = {
158                         {0x7d, 0x9d},
159                         {0x7c, 0x9a},
160                         {0x7a, 0x76},
161                         {0x3b, 0x01},
162                         {0x63, 0x88},
163                         {0x61, 0x85},
164                         {0x22, 0x30},
165                         {0x30, 0x40},
166                         {0x20, 0x23},
167                         {0x24, 0x02},
168                         {0x12, 0xa0},
169                 };
170
171                 ts2020_writereg(fe, 0x00, 0x01);
172                 ts2020_writereg(fe, 0x00, 0x03);
173
174                 switch (priv->clk_out) {
175                 case TS2020_CLK_OUT_DISABLED:
176                         u8tmp = 0x60;
177                         break;
178                 case TS2020_CLK_OUT_ENABLED:
179                         u8tmp = 0x70;
180                         ts2020_writereg(fe, 0x05, priv->clk_out_div);
181                         break;
182                 case TS2020_CLK_OUT_ENABLED_XTALOUT:
183                         u8tmp = 0x6c;
184                         break;
185                 default:
186                         u8tmp = 0x60;
187                         break;
188                 }
189
190                 ts2020_writereg(fe, 0x42, u8tmp);
191
192                 if (priv->loop_through)
193                         u8tmp = 0xec;
194                 else
195                         u8tmp = 0x6c;
196
197                 ts2020_writereg(fe, 0x62, u8tmp);
198
199                 for (i = 0; i < ARRAY_SIZE(reg_vals); i++)
200                         ts2020_writereg(fe, reg_vals[i].reg, reg_vals[i].val);
201         }
202
203         return 0;
204 }
205
206 static int ts2020_tuner_gate_ctrl(struct dvb_frontend *fe, u8 offset)
207 {
208         int ret;
209         ret = ts2020_writereg(fe, 0x51, 0x1f - offset);
210         ret |= ts2020_writereg(fe, 0x51, 0x1f);
211         ret |= ts2020_writereg(fe, 0x50, offset);
212         ret |= ts2020_writereg(fe, 0x50, 0x00);
213         msleep(20);
214         return ret;
215 }
216
217 static int ts2020_set_tuner_rf(struct dvb_frontend *fe)
218 {
219         int reg;
220
221         reg = ts2020_readreg(fe, 0x3d);
222         reg &= 0x7f;
223         if (reg < 0x16)
224                 reg = 0xa1;
225         else if (reg == 0x16)
226                 reg = 0x99;
227         else
228                 reg = 0xf9;
229
230         ts2020_writereg(fe, 0x60, reg);
231         reg = ts2020_tuner_gate_ctrl(fe, 0x08);
232
233         return reg;
234 }
235
236 static int ts2020_set_params(struct dvb_frontend *fe)
237 {
238         struct dtv_frontend_properties *c = &fe->dtv_property_cache;
239         struct ts2020_priv *priv = fe->tuner_priv;
240         int ret;
241         u32 f3db, gdiv28;
242         u16 u16tmp, value, lpf_coeff;
243         u8 buf[3], reg10, lpf_mxdiv, mlpf_max, mlpf_min, nlpf;
244         unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n;
245         unsigned int frequency_khz = c->frequency;
246
247         /*
248          * Integer-N PLL synthesizer
249          * kHz is used for all calculations to keep calculations within 32-bit
250          */
251         f_ref_khz = TS2020_XTAL_FREQ;
252         div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000);
253
254         /* select LO output divider */
255         if (frequency_khz < priv->frequency_div) {
256                 div_out = 4;
257                 reg10 = 0x10;
258         } else {
259                 div_out = 2;
260                 reg10 = 0x00;
261         }
262
263         f_vco_khz = frequency_khz * div_out;
264         pll_n = f_vco_khz * div_ref / f_ref_khz;
265         pll_n += pll_n % 2;
266         priv->frequency_khz = pll_n * f_ref_khz / div_ref / div_out;
267
268         pr_debug("frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n",
269                  priv->frequency_khz, priv->frequency_khz - c->frequency,
270                  f_vco_khz, pll_n, div_ref, div_out);
271
272         if (priv->tuner == TS2020_M88TS2020) {
273                 lpf_coeff = 2766;
274                 reg10 |= 0x01;
275                 ret = ts2020_writereg(fe, 0x10, reg10);
276         } else {
277                 lpf_coeff = 3200;
278                 reg10 |= 0x0b;
279                 ret = ts2020_writereg(fe, 0x10, reg10);
280                 ret |= ts2020_writereg(fe, 0x11, 0x40);
281         }
282
283         u16tmp = pll_n - 1024;
284         buf[0] = (u16tmp >> 8) & 0xff;
285         buf[1] = (u16tmp >> 0) & 0xff;
286         buf[2] = div_ref - 8;
287
288         ret |= ts2020_writereg(fe, 0x01, buf[0]);
289         ret |= ts2020_writereg(fe, 0x02, buf[1]);
290         ret |= ts2020_writereg(fe, 0x03, buf[2]);
291
292         ret |= ts2020_tuner_gate_ctrl(fe, 0x10);
293         if (ret < 0)
294                 return -ENODEV;
295
296         ret |= ts2020_tuner_gate_ctrl(fe, 0x08);
297
298         /* Tuner RF */
299         if (priv->tuner == TS2020_M88TS2020)
300                 ret |= ts2020_set_tuner_rf(fe);
301
302         gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000;
303         ret |= ts2020_writereg(fe, 0x04, gdiv28 & 0xff);
304         ret |= ts2020_tuner_gate_ctrl(fe, 0x04);
305         if (ret < 0)
306                 return -ENODEV;
307
308         if (priv->tuner == TS2020_M88TS2022) {
309                 ret = ts2020_writereg(fe, 0x25, 0x00);
310                 ret |= ts2020_writereg(fe, 0x27, 0x70);
311                 ret |= ts2020_writereg(fe, 0x41, 0x09);
312                 ret |= ts2020_writereg(fe, 0x08, 0x0b);
313                 if (ret < 0)
314                         return -ENODEV;
315         }
316
317         value = ts2020_readreg(fe, 0x26);
318
319         f3db = (c->bandwidth_hz / 1000 / 2) + 2000;
320         f3db += FREQ_OFFSET_LOW_SYM_RATE; /* FIXME: ~always too wide filter */
321         f3db = clamp(f3db, 7000U, 40000U);
322
323         gdiv28 = gdiv28 * 207 / (value * 2 + 151);
324         mlpf_max = gdiv28 * 135 / 100;
325         mlpf_min = gdiv28 * 78 / 100;
326         if (mlpf_max > 63)
327                 mlpf_max = 63;
328
329         nlpf = (f3db * gdiv28 * 2 / lpf_coeff /
330                 (TS2020_XTAL_FREQ / 1000)  + 1) / 2;
331         if (nlpf > 23)
332                 nlpf = 23;
333         if (nlpf < 1)
334                 nlpf = 1;
335
336         lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000)
337                 * lpf_coeff * 2  / f3db + 1) / 2;
338
339         if (lpf_mxdiv < mlpf_min) {
340                 nlpf++;
341                 lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000)
342                         * lpf_coeff * 2  / f3db + 1) / 2;
343         }
344
345         if (lpf_mxdiv > mlpf_max)
346                 lpf_mxdiv = mlpf_max;
347
348         ret = ts2020_writereg(fe, 0x04, lpf_mxdiv);
349         ret |= ts2020_writereg(fe, 0x06, nlpf);
350
351         ret |= ts2020_tuner_gate_ctrl(fe, 0x04);
352
353         ret |= ts2020_tuner_gate_ctrl(fe, 0x01);
354
355         msleep(80);
356
357         return (ret < 0) ? -EINVAL : 0;
358 }
359
360 static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency)
361 {
362         struct ts2020_priv *priv = fe->tuner_priv;
363
364         *frequency = priv->frequency_khz;
365         return 0;
366 }
367
368 static int ts2020_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
369 {
370         *frequency = 0; /* Zero-IF */
371         return 0;
372 }
373
374 /* read TS2020 signal strength */
375 static int ts2020_read_signal_strength(struct dvb_frontend *fe,
376                                                 u16 *signal_strength)
377 {
378         u16 sig_reading, sig_strength;
379         u8 rfgain, bbgain;
380
381         rfgain = ts2020_readreg(fe, 0x3d) & 0x1f;
382         bbgain = ts2020_readreg(fe, 0x21) & 0x1f;
383
384         if (rfgain > 15)
385                 rfgain = 15;
386         if (bbgain > 13)
387                 bbgain = 13;
388
389         sig_reading = rfgain * 2 + bbgain * 3;
390
391         sig_strength = 40 + (64 - sig_reading) * 50 / 64 ;
392
393         /* cook the value to be suitable for szap-s2 human readable output */
394         *signal_strength = sig_strength * 1000;
395
396         return 0;
397 }
398
399 static struct dvb_tuner_ops ts2020_tuner_ops = {
400         .info = {
401                 .name = "TS2020",
402                 .frequency_min = 950000,
403                 .frequency_max = 2150000
404         },
405         .init = ts2020_init,
406         .release = ts2020_release,
407         .sleep = ts2020_sleep,
408         .set_params = ts2020_set_params,
409         .get_frequency = ts2020_get_frequency,
410         .get_if_frequency = ts2020_get_if_frequency,
411         .get_rf_strength = ts2020_read_signal_strength,
412 };
413
414 struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe,
415                                         const struct ts2020_config *config,
416                                         struct i2c_adapter *i2c)
417 {
418         struct i2c_client *client;
419         struct i2c_board_info board_info;
420         struct ts2020_config pdata;
421
422         memcpy(&pdata, config, sizeof(pdata));
423         pdata.fe = fe;
424         pdata.attach_in_use = true;
425
426         memset(&board_info, 0, sizeof(board_info));
427         strlcpy(board_info.type, "ts2020", I2C_NAME_SIZE);
428         board_info.addr = config->tuner_address;
429         board_info.platform_data = &pdata;
430         client = i2c_new_device(i2c, &board_info);
431         if (!client || !client->dev.driver)
432                 return NULL;
433
434         return fe;
435 }
436 EXPORT_SYMBOL(ts2020_attach);
437
438 static int ts2020_probe(struct i2c_client *client,
439                 const struct i2c_device_id *id)
440 {
441         struct ts2020_config *pdata = client->dev.platform_data;
442         struct dvb_frontend *fe = pdata->fe;
443         struct ts2020_priv *dev;
444         int ret;
445         u8 u8tmp;
446         unsigned int utmp;
447         char *chip_str;
448
449         dev = kzalloc(sizeof(*dev), GFP_KERNEL);
450         if (!dev) {
451                 ret = -ENOMEM;
452                 goto err;
453         }
454
455         dev->i2c = client->adapter;
456         dev->i2c_address = client->addr;
457         dev->clk_out = pdata->clk_out;
458         dev->clk_out_div = pdata->clk_out_div;
459         dev->frequency_div = pdata->frequency_div;
460         dev->fe = fe;
461         fe->tuner_priv = dev;
462         dev->client = client;
463
464         /* check if the tuner is there */
465         ret = ts2020_readreg(fe, 0x00);
466         if (ret < 0)
467                 goto err;
468         utmp = ret;
469
470         if ((utmp & 0x03) == 0x00) {
471                 ret = ts2020_writereg(fe, 0x00, 0x01);
472                 if (ret)
473                         goto err;
474
475                 usleep_range(2000, 50000);
476         }
477
478         ret = ts2020_writereg(fe, 0x00, 0x03);
479         if (ret)
480                 goto err;
481
482         usleep_range(2000, 50000);
483
484         ret = ts2020_readreg(fe, 0x00);
485         if (ret < 0)
486                 goto err;
487         utmp = ret;
488
489         dev_dbg(&client->dev, "chip_id=%02x\n", utmp);
490
491         switch (utmp) {
492         case 0x01:
493         case 0x41:
494         case 0x81:
495                 dev->tuner = TS2020_M88TS2020;
496                 chip_str = "TS2020";
497                 if (!dev->frequency_div)
498                         dev->frequency_div = 1060000;
499                 break;
500         case 0xc3:
501         case 0x83:
502                 dev->tuner = TS2020_M88TS2022;
503                 chip_str = "TS2022";
504                 if (!dev->frequency_div)
505                         dev->frequency_div = 1103000;
506                 break;
507         default:
508                 ret = -ENODEV;
509                 goto err;
510         }
511
512         if (dev->tuner == TS2020_M88TS2022) {
513                 switch (dev->clk_out) {
514                 case TS2020_CLK_OUT_DISABLED:
515                         u8tmp = 0x60;
516                         break;
517                 case TS2020_CLK_OUT_ENABLED:
518                         u8tmp = 0x70;
519                         ret = ts2020_writereg(fe, 0x05, dev->clk_out_div);
520                         if (ret)
521                                 goto err;
522                         break;
523                 case TS2020_CLK_OUT_ENABLED_XTALOUT:
524                         u8tmp = 0x6c;
525                         break;
526                 default:
527                         ret = -EINVAL;
528                         goto err;
529                 }
530
531                 ret = ts2020_writereg(fe, 0x42, u8tmp);
532                 if (ret)
533                         goto err;
534
535                 if (dev->loop_through)
536                         u8tmp = 0xec;
537                 else
538                         u8tmp = 0x6c;
539
540                 ret = ts2020_writereg(fe, 0x62, u8tmp);
541                 if (ret)
542                         goto err;
543         }
544
545         /* sleep */
546         ret = ts2020_writereg(fe, 0x00, 0x00);
547         if (ret)
548                 goto err;
549
550         dev_info(&client->dev,
551                  "Montage Technology %s successfully identified\n", chip_str);
552
553         memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops,
554                         sizeof(struct dvb_tuner_ops));
555         if (!pdata->attach_in_use)
556                 fe->ops.tuner_ops.release = NULL;
557
558         i2c_set_clientdata(client, dev);
559         return 0;
560 err:
561         dev_dbg(&client->dev, "failed=%d\n", ret);
562         kfree(dev);
563         return ret;
564 }
565
566 static int ts2020_remove(struct i2c_client *client)
567 {
568         struct ts2020_priv *dev = i2c_get_clientdata(client);
569
570         dev_dbg(&client->dev, "\n");
571
572         kfree(dev);
573         return 0;
574 }
575
576 static const struct i2c_device_id ts2020_id_table[] = {
577         {"ts2020", 0},
578         {"ts2022", 0},
579         {}
580 };
581 MODULE_DEVICE_TABLE(i2c, ts2020_id_table);
582
583 static struct i2c_driver ts2020_driver = {
584         .driver = {
585                 .owner  = THIS_MODULE,
586                 .name   = "ts2020",
587         },
588         .probe          = ts2020_probe,
589         .remove         = ts2020_remove,
590         .id_table       = ts2020_id_table,
591 };
592
593 module_i2c_driver(ts2020_driver);
594
595 MODULE_AUTHOR("Konstantin Dimitrov <kosio.dimitrov@gmail.com>");
596 MODULE_DESCRIPTION("Montage Technology TS2020 - Silicon tuner driver module");
597 MODULE_LICENSE("GPL");