[media] ts2020: Provide DVBv5 API signal strength
[firefly-linux-kernel-4.4.55.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 #include <linux/regmap.h>
25
26 #define TS2020_XTAL_FREQ   27000 /* in kHz */
27 #define FREQ_OFFSET_LOW_SYM_RATE 3000
28
29 struct ts2020_priv {
30         struct i2c_client *client;
31         struct mutex regmap_mutex;
32         struct regmap_config regmap_config;
33         struct regmap *regmap;
34         struct dvb_frontend *fe;
35         struct delayed_work stat_work;
36         int (*get_agc_pwm)(struct dvb_frontend *fe, u8 *_agc_pwm);
37         /* i2c details */
38         struct i2c_adapter *i2c;
39         int i2c_address;
40         u8 clk_out:2;
41         u8 clk_out_div:5;
42         u32 frequency_div; /* LO output divider switch frequency */
43         u32 frequency_khz; /* actual used LO frequency */
44 #define TS2020_M88TS2020 0
45 #define TS2020_M88TS2022 1
46         u8 tuner;
47         u8 loop_through:1;
48 };
49
50 struct ts2020_reg_val {
51         u8 reg;
52         u8 val;
53 };
54
55 static int ts2020_release(struct dvb_frontend *fe)
56 {
57         struct ts2020_priv *priv = fe->tuner_priv;
58         struct i2c_client *client = priv->client;
59
60         dev_dbg(&client->dev, "\n");
61
62         i2c_unregister_device(client);
63         return 0;
64 }
65
66 static int ts2020_sleep(struct dvb_frontend *fe)
67 {
68         struct ts2020_priv *priv = fe->tuner_priv;
69         int ret;
70         u8 u8tmp;
71
72         if (priv->tuner == TS2020_M88TS2020)
73                 u8tmp = 0x0a; /* XXX: probably wrong */
74         else
75                 u8tmp = 0x00;
76
77         ret = regmap_write(priv->regmap, u8tmp, 0x00);
78         if (ret < 0)
79                 return ret;
80
81         /* stop statistics polling */
82         cancel_delayed_work_sync(&priv->stat_work);
83         return 0;
84 }
85
86 static int ts2020_init(struct dvb_frontend *fe)
87 {
88         struct dtv_frontend_properties *c = &fe->dtv_property_cache;
89         struct ts2020_priv *priv = fe->tuner_priv;
90         int i;
91         u8 u8tmp;
92
93         if (priv->tuner == TS2020_M88TS2020) {
94                 regmap_write(priv->regmap, 0x42, 0x73);
95                 regmap_write(priv->regmap, 0x05, priv->clk_out_div);
96                 regmap_write(priv->regmap, 0x20, 0x27);
97                 regmap_write(priv->regmap, 0x07, 0x02);
98                 regmap_write(priv->regmap, 0x11, 0xff);
99                 regmap_write(priv->regmap, 0x60, 0xf9);
100                 regmap_write(priv->regmap, 0x08, 0x01);
101                 regmap_write(priv->regmap, 0x00, 0x41);
102         } else {
103                 static const struct ts2020_reg_val reg_vals[] = {
104                         {0x7d, 0x9d},
105                         {0x7c, 0x9a},
106                         {0x7a, 0x76},
107                         {0x3b, 0x01},
108                         {0x63, 0x88},
109                         {0x61, 0x85},
110                         {0x22, 0x30},
111                         {0x30, 0x40},
112                         {0x20, 0x23},
113                         {0x24, 0x02},
114                         {0x12, 0xa0},
115                 };
116
117                 regmap_write(priv->regmap, 0x00, 0x01);
118                 regmap_write(priv->regmap, 0x00, 0x03);
119
120                 switch (priv->clk_out) {
121                 case TS2020_CLK_OUT_DISABLED:
122                         u8tmp = 0x60;
123                         break;
124                 case TS2020_CLK_OUT_ENABLED:
125                         u8tmp = 0x70;
126                         regmap_write(priv->regmap, 0x05, priv->clk_out_div);
127                         break;
128                 case TS2020_CLK_OUT_ENABLED_XTALOUT:
129                         u8tmp = 0x6c;
130                         break;
131                 default:
132                         u8tmp = 0x60;
133                         break;
134                 }
135
136                 regmap_write(priv->regmap, 0x42, u8tmp);
137
138                 if (priv->loop_through)
139                         u8tmp = 0xec;
140                 else
141                         u8tmp = 0x6c;
142
143                 regmap_write(priv->regmap, 0x62, u8tmp);
144
145                 for (i = 0; i < ARRAY_SIZE(reg_vals); i++)
146                         regmap_write(priv->regmap, reg_vals[i].reg,
147                                      reg_vals[i].val);
148         }
149
150         /* Initialise v5 stats here */
151         c->strength.len = 1;
152         c->strength.stat[0].scale = FE_SCALE_DECIBEL;
153         c->strength.stat[0].uvalue = 0;
154
155         /* Start statistics polling */
156         schedule_delayed_work(&priv->stat_work, 0);
157         return 0;
158 }
159
160 static int ts2020_tuner_gate_ctrl(struct dvb_frontend *fe, u8 offset)
161 {
162         struct ts2020_priv *priv = fe->tuner_priv;
163         int ret;
164         ret = regmap_write(priv->regmap, 0x51, 0x1f - offset);
165         ret |= regmap_write(priv->regmap, 0x51, 0x1f);
166         ret |= regmap_write(priv->regmap, 0x50, offset);
167         ret |= regmap_write(priv->regmap, 0x50, 0x00);
168         msleep(20);
169         return ret;
170 }
171
172 static int ts2020_set_tuner_rf(struct dvb_frontend *fe)
173 {
174         struct ts2020_priv *dev = fe->tuner_priv;
175         int ret;
176         unsigned int utmp;
177
178         ret = regmap_read(dev->regmap, 0x3d, &utmp);
179         utmp &= 0x7f;
180         if (utmp < 0x16)
181                 utmp = 0xa1;
182         else if (utmp == 0x16)
183                 utmp = 0x99;
184         else
185                 utmp = 0xf9;
186
187         regmap_write(dev->regmap, 0x60, utmp);
188         ret = ts2020_tuner_gate_ctrl(fe, 0x08);
189
190         return ret;
191 }
192
193 static int ts2020_set_params(struct dvb_frontend *fe)
194 {
195         struct dtv_frontend_properties *c = &fe->dtv_property_cache;
196         struct ts2020_priv *priv = fe->tuner_priv;
197         int ret;
198         unsigned int utmp;
199         u32 f3db, gdiv28;
200         u16 u16tmp, value, lpf_coeff;
201         u8 buf[3], reg10, lpf_mxdiv, mlpf_max, mlpf_min, nlpf;
202         unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n;
203         unsigned int frequency_khz = c->frequency;
204
205         /*
206          * Integer-N PLL synthesizer
207          * kHz is used for all calculations to keep calculations within 32-bit
208          */
209         f_ref_khz = TS2020_XTAL_FREQ;
210         div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000);
211
212         /* select LO output divider */
213         if (frequency_khz < priv->frequency_div) {
214                 div_out = 4;
215                 reg10 = 0x10;
216         } else {
217                 div_out = 2;
218                 reg10 = 0x00;
219         }
220
221         f_vco_khz = frequency_khz * div_out;
222         pll_n = f_vco_khz * div_ref / f_ref_khz;
223         pll_n += pll_n % 2;
224         priv->frequency_khz = pll_n * f_ref_khz / div_ref / div_out;
225
226         pr_debug("frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n",
227                  priv->frequency_khz, priv->frequency_khz - c->frequency,
228                  f_vco_khz, pll_n, div_ref, div_out);
229
230         if (priv->tuner == TS2020_M88TS2020) {
231                 lpf_coeff = 2766;
232                 reg10 |= 0x01;
233                 ret = regmap_write(priv->regmap, 0x10, reg10);
234         } else {
235                 lpf_coeff = 3200;
236                 reg10 |= 0x0b;
237                 ret = regmap_write(priv->regmap, 0x10, reg10);
238                 ret |= regmap_write(priv->regmap, 0x11, 0x40);
239         }
240
241         u16tmp = pll_n - 1024;
242         buf[0] = (u16tmp >> 8) & 0xff;
243         buf[1] = (u16tmp >> 0) & 0xff;
244         buf[2] = div_ref - 8;
245
246         ret |= regmap_write(priv->regmap, 0x01, buf[0]);
247         ret |= regmap_write(priv->regmap, 0x02, buf[1]);
248         ret |= regmap_write(priv->regmap, 0x03, buf[2]);
249
250         ret |= ts2020_tuner_gate_ctrl(fe, 0x10);
251         if (ret < 0)
252                 return -ENODEV;
253
254         ret |= ts2020_tuner_gate_ctrl(fe, 0x08);
255
256         /* Tuner RF */
257         if (priv->tuner == TS2020_M88TS2020)
258                 ret |= ts2020_set_tuner_rf(fe);
259
260         gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000;
261         ret |= regmap_write(priv->regmap, 0x04, gdiv28 & 0xff);
262         ret |= ts2020_tuner_gate_ctrl(fe, 0x04);
263         if (ret < 0)
264                 return -ENODEV;
265
266         if (priv->tuner == TS2020_M88TS2022) {
267                 ret = regmap_write(priv->regmap, 0x25, 0x00);
268                 ret |= regmap_write(priv->regmap, 0x27, 0x70);
269                 ret |= regmap_write(priv->regmap, 0x41, 0x09);
270                 ret |= regmap_write(priv->regmap, 0x08, 0x0b);
271                 if (ret < 0)
272                         return -ENODEV;
273         }
274
275         regmap_read(priv->regmap, 0x26, &utmp);
276         value = utmp;
277
278         f3db = (c->bandwidth_hz / 1000 / 2) + 2000;
279         f3db += FREQ_OFFSET_LOW_SYM_RATE; /* FIXME: ~always too wide filter */
280         f3db = clamp(f3db, 7000U, 40000U);
281
282         gdiv28 = gdiv28 * 207 / (value * 2 + 151);
283         mlpf_max = gdiv28 * 135 / 100;
284         mlpf_min = gdiv28 * 78 / 100;
285         if (mlpf_max > 63)
286                 mlpf_max = 63;
287
288         nlpf = (f3db * gdiv28 * 2 / lpf_coeff /
289                 (TS2020_XTAL_FREQ / 1000)  + 1) / 2;
290         if (nlpf > 23)
291                 nlpf = 23;
292         if (nlpf < 1)
293                 nlpf = 1;
294
295         lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000)
296                 * lpf_coeff * 2  / f3db + 1) / 2;
297
298         if (lpf_mxdiv < mlpf_min) {
299                 nlpf++;
300                 lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000)
301                         * lpf_coeff * 2  / f3db + 1) / 2;
302         }
303
304         if (lpf_mxdiv > mlpf_max)
305                 lpf_mxdiv = mlpf_max;
306
307         ret = regmap_write(priv->regmap, 0x04, lpf_mxdiv);
308         ret |= regmap_write(priv->regmap, 0x06, nlpf);
309
310         ret |= ts2020_tuner_gate_ctrl(fe, 0x04);
311
312         ret |= ts2020_tuner_gate_ctrl(fe, 0x01);
313
314         msleep(80);
315
316         return (ret < 0) ? -EINVAL : 0;
317 }
318
319 static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency)
320 {
321         struct ts2020_priv *priv = fe->tuner_priv;
322
323         *frequency = priv->frequency_khz;
324         return 0;
325 }
326
327 static int ts2020_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
328 {
329         *frequency = 0; /* Zero-IF */
330         return 0;
331 }
332
333 /*
334  * Get the tuner gain.
335  * @fe: The front end for which we're determining the gain
336  * @v_agc: The voltage of the AGC from the demodulator (0-2600mV)
337  * @_gain: Where to store the gain (in 0.001dB units)
338  *
339  * Returns 0 or a negative error code.
340  */
341 static int ts2020_read_tuner_gain(struct dvb_frontend *fe, unsigned v_agc,
342                                   __s64 *_gain)
343 {
344         struct ts2020_priv *priv = fe->tuner_priv;
345         unsigned long gain1, gain2, gain3;
346         unsigned utmp;
347         int ret;
348
349         /* Read the RF gain */
350         ret = regmap_read(priv->regmap, 0x3d, &utmp);
351         if (ret < 0)
352                 return ret;
353         gain1 = utmp & 0x1f;
354
355         /* Read the baseband gain */
356         ret = regmap_read(priv->regmap, 0x21, &utmp);
357         if (ret < 0)
358                 return ret;
359         gain2 = utmp & 0x1f;
360
361         switch (priv->tuner) {
362         case TS2020_M88TS2020:
363                 gain1 = clamp_t(long, gain1, 0, 15);
364                 gain2 = clamp_t(long, gain2, 0, 13);
365                 v_agc = clamp_t(long, v_agc, 400, 1100);
366
367                 *_gain = -(gain1 * 2330 +
368                            gain2 * 3500 +
369                            v_agc * 24 / 10 * 10 +
370                            10000);
371                 /* gain in range -19600 to -116850 in units of 0.001dB */
372                 break;
373
374         case TS2020_M88TS2022:
375                 ret = regmap_read(priv->regmap, 0x66, &utmp);
376                 if (ret < 0)
377                         return ret;
378                 gain3 = (utmp >> 3) & 0x07;
379
380                 gain1 = clamp_t(long, gain1, 0, 15);
381                 gain2 = clamp_t(long, gain2, 2, 16);
382                 gain3 = clamp_t(long, gain3, 0, 6);
383                 v_agc = clamp_t(long, v_agc, 600, 1600);
384
385                 *_gain = -(gain1 * 2650 +
386                            gain2 * 3380 +
387                            gain3 * 2850 +
388                            v_agc * 176 / 100 * 10 -
389                            30000);
390                 /* gain in range -47320 to -158950 in units of 0.001dB */
391                 break;
392         }
393
394         return 0;
395 }
396
397 /*
398  * Get the AGC information from the demodulator and use that to calculate the
399  * tuner gain.
400  */
401 static int ts2020_get_tuner_gain(struct dvb_frontend *fe, __s64 *_gain)
402 {
403         struct ts2020_priv *priv = fe->tuner_priv;
404         int v_agc = 0, ret;
405         u8 agc_pwm;
406
407         /* Read the AGC PWM rate from the demodulator */
408         if (priv->get_agc_pwm) {
409                 ret = priv->get_agc_pwm(fe, &agc_pwm);
410                 if (ret < 0)
411                         return ret;
412
413                 switch (priv->tuner) {
414                 case TS2020_M88TS2020:
415                         v_agc = (int)agc_pwm * 20 - 1166;
416                         break;
417                 case TS2020_M88TS2022:
418                         v_agc = (int)agc_pwm * 16 - 670;
419                         break;
420                 }
421
422                 if (v_agc < 0)
423                         v_agc = 0;
424         }
425
426         return ts2020_read_tuner_gain(fe, v_agc, _gain);
427 }
428
429 /*
430  * Gather statistics on a regular basis
431  */
432 static void ts2020_stat_work(struct work_struct *work)
433 {
434         struct ts2020_priv *priv = container_of(work, struct ts2020_priv,
435                                                stat_work.work);
436         struct i2c_client *client = priv->client;
437         struct dtv_frontend_properties *c = &priv->fe->dtv_property_cache;
438         int ret;
439
440         dev_dbg(&client->dev, "\n");
441
442         ret = ts2020_get_tuner_gain(priv->fe, &c->strength.stat[0].svalue);
443         if (ret < 0)
444                 goto err;
445
446         c->strength.stat[0].scale = FE_SCALE_DECIBEL;
447
448         schedule_delayed_work(&priv->stat_work, msecs_to_jiffies(2000));
449         return;
450 err:
451         dev_dbg(&client->dev, "failed=%d\n", ret);
452 }
453
454 /*
455  * Read TS2020 signal strength in v3 format.
456  */
457 static int ts2020_read_signal_strength(struct dvb_frontend *fe,
458                                        u16 *_signal_strength)
459 {
460         struct dtv_frontend_properties *c = &fe->dtv_property_cache;
461         unsigned strength;
462         __s64 gain;
463
464         if (c->strength.stat[0].scale == FE_SCALE_NOT_AVAILABLE) {
465                 *_signal_strength = 0;
466                 return 0;
467         }
468
469         gain = c->strength.stat[0].svalue;
470
471         /* Calculate the signal strength based on the total gain of the tuner */
472         if (gain < -85000)
473                 /* 0%: no signal or weak signal */
474                 strength = 0;
475         else if (gain < -65000)
476                 /* 0% - 60%: weak signal */
477                 strength = 0 + (85000 + gain) * 3 / 1000;
478         else if (gain < -45000)
479                 /* 60% - 90%: normal signal */
480                 strength = 60 + (65000 + gain) * 3 / 2000;
481         else
482                 /* 90% - 99%: strong signal */
483                 strength = 90 + (45000 + gain) / 5000;
484
485         *_signal_strength = strength * 65535 / 100;
486         return 0;
487 }
488
489 static struct dvb_tuner_ops ts2020_tuner_ops = {
490         .info = {
491                 .name = "TS2020",
492                 .frequency_min = 950000,
493                 .frequency_max = 2150000
494         },
495         .init = ts2020_init,
496         .release = ts2020_release,
497         .sleep = ts2020_sleep,
498         .set_params = ts2020_set_params,
499         .get_frequency = ts2020_get_frequency,
500         .get_if_frequency = ts2020_get_if_frequency,
501         .get_rf_strength = ts2020_read_signal_strength,
502 };
503
504 struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe,
505                                         const struct ts2020_config *config,
506                                         struct i2c_adapter *i2c)
507 {
508         struct i2c_client *client;
509         struct i2c_board_info board_info;
510
511         /* This is only used by ts2020_probe() so can be on the stack */
512         struct ts2020_config pdata;
513
514         memcpy(&pdata, config, sizeof(pdata));
515         pdata.fe = fe;
516         pdata.attach_in_use = true;
517
518         memset(&board_info, 0, sizeof(board_info));
519         strlcpy(board_info.type, "ts2020", I2C_NAME_SIZE);
520         board_info.addr = config->tuner_address;
521         board_info.platform_data = &pdata;
522         client = i2c_new_device(i2c, &board_info);
523         if (!client || !client->dev.driver)
524                 return NULL;
525
526         return fe;
527 }
528 EXPORT_SYMBOL(ts2020_attach);
529
530 /*
531  * We implement own regmap locking due to legacy DVB attach which uses frontend
532  * gate control callback to control I2C bus access. We can open / close gate and
533  * serialize whole open / I2C-operation / close sequence at the same.
534  */
535 static void ts2020_regmap_lock(void *__dev)
536 {
537         struct ts2020_priv *dev = __dev;
538
539         mutex_lock(&dev->regmap_mutex);
540         if (dev->fe->ops.i2c_gate_ctrl)
541                 dev->fe->ops.i2c_gate_ctrl(dev->fe, 1);
542 }
543
544 static void ts2020_regmap_unlock(void *__dev)
545 {
546         struct ts2020_priv *dev = __dev;
547
548         if (dev->fe->ops.i2c_gate_ctrl)
549                 dev->fe->ops.i2c_gate_ctrl(dev->fe, 0);
550         mutex_unlock(&dev->regmap_mutex);
551 }
552
553 static int ts2020_probe(struct i2c_client *client,
554                 const struct i2c_device_id *id)
555 {
556         struct ts2020_config *pdata = client->dev.platform_data;
557         struct dvb_frontend *fe = pdata->fe;
558         struct ts2020_priv *dev;
559         int ret;
560         u8 u8tmp;
561         unsigned int utmp;
562         char *chip_str;
563
564         dev = kzalloc(sizeof(*dev), GFP_KERNEL);
565         if (!dev) {
566                 ret = -ENOMEM;
567                 goto err;
568         }
569
570         /* create regmap */
571         mutex_init(&dev->regmap_mutex);
572         dev->regmap_config.reg_bits = 8,
573         dev->regmap_config.val_bits = 8,
574         dev->regmap_config.lock = ts2020_regmap_lock,
575         dev->regmap_config.unlock = ts2020_regmap_unlock,
576         dev->regmap_config.lock_arg = dev,
577         dev->regmap = regmap_init_i2c(client, &dev->regmap_config);
578         if (IS_ERR(dev->regmap)) {
579                 ret = PTR_ERR(dev->regmap);
580                 goto err_kfree;
581         }
582
583         dev->i2c = client->adapter;
584         dev->i2c_address = client->addr;
585         dev->clk_out = pdata->clk_out;
586         dev->clk_out_div = pdata->clk_out_div;
587         dev->frequency_div = pdata->frequency_div;
588         dev->fe = fe;
589         dev->get_agc_pwm = pdata->get_agc_pwm;
590         fe->tuner_priv = dev;
591         dev->client = client;
592         INIT_DELAYED_WORK(&dev->stat_work, ts2020_stat_work);
593
594         /* check if the tuner is there */
595         ret = regmap_read(dev->regmap, 0x00, &utmp);
596         if (ret)
597                 goto err_regmap_exit;
598
599         if ((utmp & 0x03) == 0x00) {
600                 ret = regmap_write(dev->regmap, 0x00, 0x01);
601                 if (ret)
602                         goto err_regmap_exit;
603
604                 usleep_range(2000, 50000);
605         }
606
607         ret = regmap_write(dev->regmap, 0x00, 0x03);
608         if (ret)
609                 goto err_regmap_exit;
610
611         usleep_range(2000, 50000);
612
613         ret = regmap_read(dev->regmap, 0x00, &utmp);
614         if (ret)
615                 goto err_regmap_exit;
616
617         dev_dbg(&client->dev, "chip_id=%02x\n", utmp);
618
619         switch (utmp) {
620         case 0x01:
621         case 0x41:
622         case 0x81:
623                 dev->tuner = TS2020_M88TS2020;
624                 chip_str = "TS2020";
625                 if (!dev->frequency_div)
626                         dev->frequency_div = 1060000;
627                 break;
628         case 0xc3:
629         case 0x83:
630                 dev->tuner = TS2020_M88TS2022;
631                 chip_str = "TS2022";
632                 if (!dev->frequency_div)
633                         dev->frequency_div = 1103000;
634                 break;
635         default:
636                 ret = -ENODEV;
637                 goto err_regmap_exit;
638         }
639
640         if (dev->tuner == TS2020_M88TS2022) {
641                 switch (dev->clk_out) {
642                 case TS2020_CLK_OUT_DISABLED:
643                         u8tmp = 0x60;
644                         break;
645                 case TS2020_CLK_OUT_ENABLED:
646                         u8tmp = 0x70;
647                         ret = regmap_write(dev->regmap, 0x05, dev->clk_out_div);
648                         if (ret)
649                                 goto err_regmap_exit;
650                         break;
651                 case TS2020_CLK_OUT_ENABLED_XTALOUT:
652                         u8tmp = 0x6c;
653                         break;
654                 default:
655                         ret = -EINVAL;
656                         goto err_regmap_exit;
657                 }
658
659                 ret = regmap_write(dev->regmap, 0x42, u8tmp);
660                 if (ret)
661                         goto err_regmap_exit;
662
663                 if (dev->loop_through)
664                         u8tmp = 0xec;
665                 else
666                         u8tmp = 0x6c;
667
668                 ret = regmap_write(dev->regmap, 0x62, u8tmp);
669                 if (ret)
670                         goto err_regmap_exit;
671         }
672
673         /* sleep */
674         ret = regmap_write(dev->regmap, 0x00, 0x00);
675         if (ret)
676                 goto err_regmap_exit;
677
678         dev_info(&client->dev,
679                  "Montage Technology %s successfully identified\n", chip_str);
680
681         memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops,
682                         sizeof(struct dvb_tuner_ops));
683         if (!pdata->attach_in_use)
684                 fe->ops.tuner_ops.release = NULL;
685
686         i2c_set_clientdata(client, dev);
687         return 0;
688 err_regmap_exit:
689         regmap_exit(dev->regmap);
690 err_kfree:
691         kfree(dev);
692 err:
693         dev_dbg(&client->dev, "failed=%d\n", ret);
694         return ret;
695 }
696
697 static int ts2020_remove(struct i2c_client *client)
698 {
699         struct ts2020_priv *dev = i2c_get_clientdata(client);
700
701         dev_dbg(&client->dev, "\n");
702
703         regmap_exit(dev->regmap);
704         kfree(dev);
705         return 0;
706 }
707
708 static const struct i2c_device_id ts2020_id_table[] = {
709         {"ts2020", 0},
710         {"ts2022", 0},
711         {}
712 };
713 MODULE_DEVICE_TABLE(i2c, ts2020_id_table);
714
715 static struct i2c_driver ts2020_driver = {
716         .driver = {
717                 .owner  = THIS_MODULE,
718                 .name   = "ts2020",
719         },
720         .probe          = ts2020_probe,
721         .remove         = ts2020_remove,
722         .id_table       = ts2020_id_table,
723 };
724
725 module_i2c_driver(ts2020_driver);
726
727 MODULE_AUTHOR("Konstantin Dimitrov <kosio.dimitrov@gmail.com>");
728 MODULE_DESCRIPTION("Montage Technology TS2020 - Silicon tuner driver module");
729 MODULE_LICENSE("GPL");