Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003     Montage Technology TS2020 - Silicon Tuner driver
0004     Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com>
0005 
0006     Copyright (C) 2009-2012 TurboSight.com
0007 
0008  */
0009 
0010 #include <media/dvb_frontend.h>
0011 #include "ts2020.h"
0012 #include <linux/regmap.h>
0013 #include <linux/math64.h>
0014 
0015 #define TS2020_XTAL_FREQ   27000 /* in kHz */
0016 #define FREQ_OFFSET_LOW_SYM_RATE 3000
0017 
0018 struct ts2020_priv {
0019     struct i2c_client *client;
0020     struct mutex regmap_mutex;
0021     struct regmap_config regmap_config;
0022     struct regmap *regmap;
0023     struct dvb_frontend *fe;
0024     struct delayed_work stat_work;
0025     int (*get_agc_pwm)(struct dvb_frontend *fe, u8 *_agc_pwm);
0026     /* i2c details */
0027     struct i2c_adapter *i2c;
0028     int i2c_address;
0029     bool loop_through:1;
0030     u8 clk_out:2;
0031     u8 clk_out_div:5;
0032     bool dont_poll:1;
0033     u32 frequency_div; /* LO output divider switch frequency */
0034     u32 frequency_khz; /* actual used LO frequency */
0035 #define TS2020_M88TS2020 0
0036 #define TS2020_M88TS2022 1
0037     u8 tuner;
0038 };
0039 
0040 struct ts2020_reg_val {
0041     u8 reg;
0042     u8 val;
0043 };
0044 
0045 static void ts2020_stat_work(struct work_struct *work);
0046 
0047 static void ts2020_release(struct dvb_frontend *fe)
0048 {
0049     struct ts2020_priv *priv = fe->tuner_priv;
0050     struct i2c_client *client = priv->client;
0051 
0052     dev_dbg(&client->dev, "\n");
0053 
0054     i2c_unregister_device(client);
0055 }
0056 
0057 static int ts2020_sleep(struct dvb_frontend *fe)
0058 {
0059     struct ts2020_priv *priv = fe->tuner_priv;
0060     int ret;
0061     u8 u8tmp;
0062 
0063     if (priv->tuner == TS2020_M88TS2020)
0064         u8tmp = 0x0a; /* XXX: probably wrong */
0065     else
0066         u8tmp = 0x00;
0067 
0068     ret = regmap_write(priv->regmap, u8tmp, 0x00);
0069     if (ret < 0)
0070         return ret;
0071 
0072     /* stop statistics polling */
0073     if (!priv->dont_poll)
0074         cancel_delayed_work_sync(&priv->stat_work);
0075     return 0;
0076 }
0077 
0078 static int ts2020_init(struct dvb_frontend *fe)
0079 {
0080     struct dtv_frontend_properties *c = &fe->dtv_property_cache;
0081     struct ts2020_priv *priv = fe->tuner_priv;
0082     int i;
0083     u8 u8tmp;
0084 
0085     if (priv->tuner == TS2020_M88TS2020) {
0086         regmap_write(priv->regmap, 0x42, 0x73);
0087         regmap_write(priv->regmap, 0x05, priv->clk_out_div);
0088         regmap_write(priv->regmap, 0x20, 0x27);
0089         regmap_write(priv->regmap, 0x07, 0x02);
0090         regmap_write(priv->regmap, 0x11, 0xff);
0091         regmap_write(priv->regmap, 0x60, 0xf9);
0092         regmap_write(priv->regmap, 0x08, 0x01);
0093         regmap_write(priv->regmap, 0x00, 0x41);
0094     } else {
0095         static const struct ts2020_reg_val reg_vals[] = {
0096             {0x7d, 0x9d},
0097             {0x7c, 0x9a},
0098             {0x7a, 0x76},
0099             {0x3b, 0x01},
0100             {0x63, 0x88},
0101             {0x61, 0x85},
0102             {0x22, 0x30},
0103             {0x30, 0x40},
0104             {0x20, 0x23},
0105             {0x24, 0x02},
0106             {0x12, 0xa0},
0107         };
0108 
0109         regmap_write(priv->regmap, 0x00, 0x01);
0110         regmap_write(priv->regmap, 0x00, 0x03);
0111 
0112         switch (priv->clk_out) {
0113         case TS2020_CLK_OUT_DISABLED:
0114             u8tmp = 0x60;
0115             break;
0116         case TS2020_CLK_OUT_ENABLED:
0117             u8tmp = 0x70;
0118             regmap_write(priv->regmap, 0x05, priv->clk_out_div);
0119             break;
0120         case TS2020_CLK_OUT_ENABLED_XTALOUT:
0121             u8tmp = 0x6c;
0122             break;
0123         default:
0124             u8tmp = 0x60;
0125             break;
0126         }
0127 
0128         regmap_write(priv->regmap, 0x42, u8tmp);
0129 
0130         if (priv->loop_through)
0131             u8tmp = 0xec;
0132         else
0133             u8tmp = 0x6c;
0134 
0135         regmap_write(priv->regmap, 0x62, u8tmp);
0136 
0137         for (i = 0; i < ARRAY_SIZE(reg_vals); i++)
0138             regmap_write(priv->regmap, reg_vals[i].reg,
0139                      reg_vals[i].val);
0140     }
0141 
0142     /* Initialise v5 stats here */
0143     c->strength.len = 1;
0144     c->strength.stat[0].scale = FE_SCALE_DECIBEL;
0145     c->strength.stat[0].uvalue = 0;
0146 
0147     /* Start statistics polling by invoking the work function */
0148     ts2020_stat_work(&priv->stat_work.work);
0149     return 0;
0150 }
0151 
0152 static int ts2020_tuner_gate_ctrl(struct dvb_frontend *fe, u8 offset)
0153 {
0154     struct ts2020_priv *priv = fe->tuner_priv;
0155     int ret;
0156     ret = regmap_write(priv->regmap, 0x51, 0x1f - offset);
0157     ret |= regmap_write(priv->regmap, 0x51, 0x1f);
0158     ret |= regmap_write(priv->regmap, 0x50, offset);
0159     ret |= regmap_write(priv->regmap, 0x50, 0x00);
0160     msleep(20);
0161     return ret;
0162 }
0163 
0164 static int ts2020_set_tuner_rf(struct dvb_frontend *fe)
0165 {
0166     struct ts2020_priv *dev = fe->tuner_priv;
0167     int ret;
0168     unsigned int utmp;
0169 
0170     ret = regmap_read(dev->regmap, 0x3d, &utmp);
0171     if (ret)
0172         return ret;
0173 
0174     utmp &= 0x7f;
0175     if (utmp < 0x16)
0176         utmp = 0xa1;
0177     else if (utmp == 0x16)
0178         utmp = 0x99;
0179     else
0180         utmp = 0xf9;
0181 
0182     regmap_write(dev->regmap, 0x60, utmp);
0183     ret = ts2020_tuner_gate_ctrl(fe, 0x08);
0184 
0185     return ret;
0186 }
0187 
0188 static int ts2020_set_params(struct dvb_frontend *fe)
0189 {
0190     struct dtv_frontend_properties *c = &fe->dtv_property_cache;
0191     struct ts2020_priv *priv = fe->tuner_priv;
0192     int ret;
0193     unsigned int utmp;
0194     u32 f3db, gdiv28;
0195     u16 u16tmp, value, lpf_coeff;
0196     u8 buf[3], reg10, lpf_mxdiv, mlpf_max, mlpf_min, nlpf;
0197     unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n;
0198     unsigned int frequency_khz = c->frequency;
0199 
0200     /*
0201      * Integer-N PLL synthesizer
0202      * kHz is used for all calculations to keep calculations within 32-bit
0203      */
0204     f_ref_khz = TS2020_XTAL_FREQ;
0205     div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000);
0206 
0207     /* select LO output divider */
0208     if (frequency_khz < priv->frequency_div) {
0209         div_out = 4;
0210         reg10 = 0x10;
0211     } else {
0212         div_out = 2;
0213         reg10 = 0x00;
0214     }
0215 
0216     f_vco_khz = frequency_khz * div_out;
0217     pll_n = f_vco_khz * div_ref / f_ref_khz;
0218     pll_n += pll_n % 2;
0219     priv->frequency_khz = pll_n * f_ref_khz / div_ref / div_out;
0220 
0221     pr_debug("frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n",
0222          priv->frequency_khz, priv->frequency_khz - c->frequency,
0223          f_vco_khz, pll_n, div_ref, div_out);
0224 
0225     if (priv->tuner == TS2020_M88TS2020) {
0226         lpf_coeff = 2766;
0227         reg10 |= 0x01;
0228         ret = regmap_write(priv->regmap, 0x10, reg10);
0229     } else {
0230         lpf_coeff = 3200;
0231         reg10 |= 0x0b;
0232         ret = regmap_write(priv->regmap, 0x10, reg10);
0233         ret |= regmap_write(priv->regmap, 0x11, 0x40);
0234     }
0235 
0236     u16tmp = pll_n - 1024;
0237     buf[0] = (u16tmp >> 8) & 0xff;
0238     buf[1] = (u16tmp >> 0) & 0xff;
0239     buf[2] = div_ref - 8;
0240 
0241     ret |= regmap_write(priv->regmap, 0x01, buf[0]);
0242     ret |= regmap_write(priv->regmap, 0x02, buf[1]);
0243     ret |= regmap_write(priv->regmap, 0x03, buf[2]);
0244 
0245     ret |= ts2020_tuner_gate_ctrl(fe, 0x10);
0246     if (ret < 0)
0247         return -ENODEV;
0248 
0249     ret |= ts2020_tuner_gate_ctrl(fe, 0x08);
0250 
0251     /* Tuner RF */
0252     if (priv->tuner == TS2020_M88TS2020)
0253         ret |= ts2020_set_tuner_rf(fe);
0254 
0255     gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000;
0256     ret |= regmap_write(priv->regmap, 0x04, gdiv28 & 0xff);
0257     ret |= ts2020_tuner_gate_ctrl(fe, 0x04);
0258     if (ret < 0)
0259         return -ENODEV;
0260 
0261     if (priv->tuner == TS2020_M88TS2022) {
0262         ret = regmap_write(priv->regmap, 0x25, 0x00);
0263         ret |= regmap_write(priv->regmap, 0x27, 0x70);
0264         ret |= regmap_write(priv->regmap, 0x41, 0x09);
0265         ret |= regmap_write(priv->regmap, 0x08, 0x0b);
0266         if (ret < 0)
0267             return -ENODEV;
0268     }
0269 
0270     regmap_read(priv->regmap, 0x26, &utmp);
0271     value = utmp;
0272 
0273     f3db = (c->bandwidth_hz / 1000 / 2) + 2000;
0274     f3db += FREQ_OFFSET_LOW_SYM_RATE; /* FIXME: ~always too wide filter */
0275     f3db = clamp(f3db, 7000U, 40000U);
0276 
0277     gdiv28 = gdiv28 * 207 / (value * 2 + 151);
0278     mlpf_max = gdiv28 * 135 / 100;
0279     mlpf_min = gdiv28 * 78 / 100;
0280     if (mlpf_max > 63)
0281         mlpf_max = 63;
0282 
0283     nlpf = (f3db * gdiv28 * 2 / lpf_coeff /
0284         (TS2020_XTAL_FREQ / 1000)  + 1) / 2;
0285     if (nlpf > 23)
0286         nlpf = 23;
0287     if (nlpf < 1)
0288         nlpf = 1;
0289 
0290     lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000)
0291         * lpf_coeff * 2  / f3db + 1) / 2;
0292 
0293     if (lpf_mxdiv < mlpf_min) {
0294         nlpf++;
0295         lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000)
0296             * lpf_coeff * 2  / f3db + 1) / 2;
0297     }
0298 
0299     if (lpf_mxdiv > mlpf_max)
0300         lpf_mxdiv = mlpf_max;
0301 
0302     ret = regmap_write(priv->regmap, 0x04, lpf_mxdiv);
0303     ret |= regmap_write(priv->regmap, 0x06, nlpf);
0304 
0305     ret |= ts2020_tuner_gate_ctrl(fe, 0x04);
0306 
0307     ret |= ts2020_tuner_gate_ctrl(fe, 0x01);
0308 
0309     msleep(80);
0310 
0311     return (ret < 0) ? -EINVAL : 0;
0312 }
0313 
0314 static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency)
0315 {
0316     struct ts2020_priv *priv = fe->tuner_priv;
0317 
0318     *frequency = priv->frequency_khz;
0319     return 0;
0320 }
0321 
0322 static int ts2020_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
0323 {
0324     *frequency = 0; /* Zero-IF */
0325     return 0;
0326 }
0327 
0328 /*
0329  * Get the tuner gain.
0330  * @fe: The front end for which we're determining the gain
0331  * @v_agc: The voltage of the AGC from the demodulator (0-2600mV)
0332  * @_gain: Where to store the gain (in 0.001dB units)
0333  *
0334  * Returns 0 or a negative error code.
0335  */
0336 static int ts2020_read_tuner_gain(struct dvb_frontend *fe, unsigned v_agc,
0337                   __s64 *_gain)
0338 {
0339     struct ts2020_priv *priv = fe->tuner_priv;
0340     unsigned long gain1, gain2, gain3;
0341     unsigned utmp;
0342     int ret;
0343 
0344     /* Read the RF gain */
0345     ret = regmap_read(priv->regmap, 0x3d, &utmp);
0346     if (ret < 0)
0347         return ret;
0348     gain1 = utmp & 0x1f;
0349 
0350     /* Read the baseband gain */
0351     ret = regmap_read(priv->regmap, 0x21, &utmp);
0352     if (ret < 0)
0353         return ret;
0354     gain2 = utmp & 0x1f;
0355 
0356     switch (priv->tuner) {
0357     case TS2020_M88TS2020:
0358         gain1 = clamp_t(long, gain1, 0, 15);
0359         gain2 = clamp_t(long, gain2, 0, 13);
0360         v_agc = clamp_t(long, v_agc, 400, 1100);
0361 
0362         *_gain = -((__s64)gain1 * 2330 +
0363                gain2 * 3500 +
0364                v_agc * 24 / 10 * 10 +
0365                10000);
0366         /* gain in range -19600 to -116850 in units of 0.001dB */
0367         break;
0368 
0369     case TS2020_M88TS2022:
0370         ret = regmap_read(priv->regmap, 0x66, &utmp);
0371         if (ret < 0)
0372             return ret;
0373         gain3 = (utmp >> 3) & 0x07;
0374 
0375         gain1 = clamp_t(long, gain1, 0, 15);
0376         gain2 = clamp_t(long, gain2, 2, 16);
0377         gain3 = clamp_t(long, gain3, 0, 6);
0378         v_agc = clamp_t(long, v_agc, 600, 1600);
0379 
0380         *_gain = -((__s64)gain1 * 2650 +
0381                gain2 * 3380 +
0382                gain3 * 2850 +
0383                v_agc * 176 / 100 * 10 -
0384                30000);
0385         /* gain in range -47320 to -158950 in units of 0.001dB */
0386         break;
0387     }
0388 
0389     return 0;
0390 }
0391 
0392 /*
0393  * Get the AGC information from the demodulator and use that to calculate the
0394  * tuner gain.
0395  */
0396 static int ts2020_get_tuner_gain(struct dvb_frontend *fe, __s64 *_gain)
0397 {
0398     struct ts2020_priv *priv = fe->tuner_priv;
0399     int v_agc = 0, ret;
0400     u8 agc_pwm;
0401 
0402     /* Read the AGC PWM rate from the demodulator */
0403     if (priv->get_agc_pwm) {
0404         ret = priv->get_agc_pwm(fe, &agc_pwm);
0405         if (ret < 0)
0406             return ret;
0407 
0408         switch (priv->tuner) {
0409         case TS2020_M88TS2020:
0410             v_agc = (int)agc_pwm * 20 - 1166;
0411             break;
0412         case TS2020_M88TS2022:
0413             v_agc = (int)agc_pwm * 16 - 670;
0414             break;
0415         }
0416 
0417         if (v_agc < 0)
0418             v_agc = 0;
0419     }
0420 
0421     return ts2020_read_tuner_gain(fe, v_agc, _gain);
0422 }
0423 
0424 /*
0425  * Gather statistics on a regular basis
0426  */
0427 static void ts2020_stat_work(struct work_struct *work)
0428 {
0429     struct ts2020_priv *priv = container_of(work, struct ts2020_priv,
0430                            stat_work.work);
0431     struct i2c_client *client = priv->client;
0432     struct dtv_frontend_properties *c = &priv->fe->dtv_property_cache;
0433     int ret;
0434 
0435     dev_dbg(&client->dev, "\n");
0436 
0437     ret = ts2020_get_tuner_gain(priv->fe, &c->strength.stat[0].svalue);
0438     if (ret < 0)
0439         goto err;
0440 
0441     c->strength.stat[0].scale = FE_SCALE_DECIBEL;
0442 
0443     if (!priv->dont_poll)
0444         schedule_delayed_work(&priv->stat_work, msecs_to_jiffies(2000));
0445     return;
0446 err:
0447     dev_dbg(&client->dev, "failed=%d\n", ret);
0448 }
0449 
0450 /*
0451  * Read TS2020 signal strength in v3 format.
0452  */
0453 static int ts2020_read_signal_strength(struct dvb_frontend *fe,
0454                        u16 *_signal_strength)
0455 {
0456     struct dtv_frontend_properties *c = &fe->dtv_property_cache;
0457     struct ts2020_priv *priv = fe->tuner_priv;
0458     unsigned strength;
0459     __s64 gain;
0460 
0461     if (priv->dont_poll)
0462         ts2020_stat_work(&priv->stat_work.work);
0463 
0464     if (c->strength.stat[0].scale == FE_SCALE_NOT_AVAILABLE) {
0465         *_signal_strength = 0;
0466         return 0;
0467     }
0468 
0469     gain = c->strength.stat[0].svalue;
0470 
0471     /* Calculate the signal strength based on the total gain of the tuner */
0472     if (gain < -85000)
0473         /* 0%: no signal or weak signal */
0474         strength = 0;
0475     else if (gain < -65000)
0476         /* 0% - 60%: weak signal */
0477         strength = 0 + div64_s64((85000 + gain) * 3, 1000);
0478     else if (gain < -45000)
0479         /* 60% - 90%: normal signal */
0480         strength = 60 + div64_s64((65000 + gain) * 3, 2000);
0481     else
0482         /* 90% - 99%: strong signal */
0483         strength = 90 + div64_s64((45000 + gain), 5000);
0484 
0485     *_signal_strength = strength * 65535 / 100;
0486     return 0;
0487 }
0488 
0489 static const struct dvb_tuner_ops ts2020_tuner_ops = {
0490     .info = {
0491         .name = "TS2020",
0492         .frequency_min_hz =  950 * MHz,
0493         .frequency_max_hz = 2150 * MHz
0494     },
0495     .init = ts2020_init,
0496     .release = ts2020_release,
0497     .sleep = ts2020_sleep,
0498     .set_params = ts2020_set_params,
0499     .get_frequency = ts2020_get_frequency,
0500     .get_if_frequency = ts2020_get_if_frequency,
0501     .get_rf_strength = ts2020_read_signal_strength,
0502 };
0503 
0504 struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe,
0505                     const struct ts2020_config *config,
0506                     struct i2c_adapter *i2c)
0507 {
0508     struct i2c_client *client;
0509     struct i2c_board_info board_info;
0510 
0511     /* This is only used by ts2020_probe() so can be on the stack */
0512     struct ts2020_config pdata;
0513 
0514     memcpy(&pdata, config, sizeof(pdata));
0515     pdata.fe = fe;
0516     pdata.attach_in_use = true;
0517 
0518     memset(&board_info, 0, sizeof(board_info));
0519     strscpy(board_info.type, "ts2020", I2C_NAME_SIZE);
0520     board_info.addr = config->tuner_address;
0521     board_info.platform_data = &pdata;
0522     client = i2c_new_client_device(i2c, &board_info);
0523     if (!i2c_client_has_driver(client))
0524         return NULL;
0525 
0526     return fe;
0527 }
0528 EXPORT_SYMBOL(ts2020_attach);
0529 
0530 /*
0531  * We implement own regmap locking due to legacy DVB attach which uses frontend
0532  * gate control callback to control I2C bus access. We can open / close gate and
0533  * serialize whole open / I2C-operation / close sequence at the same.
0534  */
0535 static void ts2020_regmap_lock(void *__dev)
0536 {
0537     struct ts2020_priv *dev = __dev;
0538 
0539     mutex_lock(&dev->regmap_mutex);
0540     if (dev->fe->ops.i2c_gate_ctrl)
0541         dev->fe->ops.i2c_gate_ctrl(dev->fe, 1);
0542 }
0543 
0544 static void ts2020_regmap_unlock(void *__dev)
0545 {
0546     struct ts2020_priv *dev = __dev;
0547 
0548     if (dev->fe->ops.i2c_gate_ctrl)
0549         dev->fe->ops.i2c_gate_ctrl(dev->fe, 0);
0550     mutex_unlock(&dev->regmap_mutex);
0551 }
0552 
0553 static int ts2020_probe(struct i2c_client *client,
0554         const struct i2c_device_id *id)
0555 {
0556     struct ts2020_config *pdata = client->dev.platform_data;
0557     struct dvb_frontend *fe = pdata->fe;
0558     struct ts2020_priv *dev;
0559     int ret;
0560     u8 u8tmp;
0561     unsigned int utmp;
0562     char *chip_str;
0563 
0564     dev = kzalloc(sizeof(*dev), GFP_KERNEL);
0565     if (!dev) {
0566         ret = -ENOMEM;
0567         goto err;
0568     }
0569 
0570     /* create regmap */
0571     mutex_init(&dev->regmap_mutex);
0572     dev->regmap_config.reg_bits = 8;
0573     dev->regmap_config.val_bits = 8;
0574     dev->regmap_config.lock = ts2020_regmap_lock;
0575     dev->regmap_config.unlock = ts2020_regmap_unlock;
0576     dev->regmap_config.lock_arg = dev;
0577     dev->regmap = regmap_init_i2c(client, &dev->regmap_config);
0578     if (IS_ERR(dev->regmap)) {
0579         ret = PTR_ERR(dev->regmap);
0580         goto err_kfree;
0581     }
0582 
0583     dev->i2c = client->adapter;
0584     dev->i2c_address = client->addr;
0585     dev->loop_through = pdata->loop_through;
0586     dev->clk_out = pdata->clk_out;
0587     dev->clk_out_div = pdata->clk_out_div;
0588     dev->dont_poll = pdata->dont_poll;
0589     dev->frequency_div = pdata->frequency_div;
0590     dev->fe = fe;
0591     dev->get_agc_pwm = pdata->get_agc_pwm;
0592     fe->tuner_priv = dev;
0593     dev->client = client;
0594     INIT_DELAYED_WORK(&dev->stat_work, ts2020_stat_work);
0595 
0596     /* check if the tuner is there */
0597     ret = regmap_read(dev->regmap, 0x00, &utmp);
0598     if (ret)
0599         goto err_regmap_exit;
0600 
0601     if ((utmp & 0x03) == 0x00) {
0602         ret = regmap_write(dev->regmap, 0x00, 0x01);
0603         if (ret)
0604             goto err_regmap_exit;
0605 
0606         usleep_range(2000, 50000);
0607     }
0608 
0609     ret = regmap_write(dev->regmap, 0x00, 0x03);
0610     if (ret)
0611         goto err_regmap_exit;
0612 
0613     usleep_range(2000, 50000);
0614 
0615     ret = regmap_read(dev->regmap, 0x00, &utmp);
0616     if (ret)
0617         goto err_regmap_exit;
0618 
0619     dev_dbg(&client->dev, "chip_id=%02x\n", utmp);
0620 
0621     switch (utmp) {
0622     case 0x01:
0623     case 0x41:
0624     case 0x81:
0625         dev->tuner = TS2020_M88TS2020;
0626         chip_str = "TS2020";
0627         if (!dev->frequency_div)
0628             dev->frequency_div = 1060000;
0629         break;
0630     case 0xc3:
0631     case 0x83:
0632         dev->tuner = TS2020_M88TS2022;
0633         chip_str = "TS2022";
0634         if (!dev->frequency_div)
0635             dev->frequency_div = 1103000;
0636         break;
0637     default:
0638         ret = -ENODEV;
0639         goto err_regmap_exit;
0640     }
0641 
0642     if (dev->tuner == TS2020_M88TS2022) {
0643         switch (dev->clk_out) {
0644         case TS2020_CLK_OUT_DISABLED:
0645             u8tmp = 0x60;
0646             break;
0647         case TS2020_CLK_OUT_ENABLED:
0648             u8tmp = 0x70;
0649             ret = regmap_write(dev->regmap, 0x05, dev->clk_out_div);
0650             if (ret)
0651                 goto err_regmap_exit;
0652             break;
0653         case TS2020_CLK_OUT_ENABLED_XTALOUT:
0654             u8tmp = 0x6c;
0655             break;
0656         default:
0657             ret = -EINVAL;
0658             goto err_regmap_exit;
0659         }
0660 
0661         ret = regmap_write(dev->regmap, 0x42, u8tmp);
0662         if (ret)
0663             goto err_regmap_exit;
0664 
0665         if (dev->loop_through)
0666             u8tmp = 0xec;
0667         else
0668             u8tmp = 0x6c;
0669 
0670         ret = regmap_write(dev->regmap, 0x62, u8tmp);
0671         if (ret)
0672             goto err_regmap_exit;
0673     }
0674 
0675     /* sleep */
0676     ret = regmap_write(dev->regmap, 0x00, 0x00);
0677     if (ret)
0678         goto err_regmap_exit;
0679 
0680     dev_info(&client->dev,
0681          "Montage Technology %s successfully identified\n", chip_str);
0682 
0683     memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops,
0684             sizeof(struct dvb_tuner_ops));
0685     if (!pdata->attach_in_use)
0686         fe->ops.tuner_ops.release = NULL;
0687 
0688     i2c_set_clientdata(client, dev);
0689     return 0;
0690 err_regmap_exit:
0691     regmap_exit(dev->regmap);
0692 err_kfree:
0693     kfree(dev);
0694 err:
0695     dev_dbg(&client->dev, "failed=%d\n", ret);
0696     return ret;
0697 }
0698 
0699 static int ts2020_remove(struct i2c_client *client)
0700 {
0701     struct ts2020_priv *dev = i2c_get_clientdata(client);
0702 
0703     dev_dbg(&client->dev, "\n");
0704 
0705     /* stop statistics polling */
0706     if (!dev->dont_poll)
0707         cancel_delayed_work_sync(&dev->stat_work);
0708 
0709     regmap_exit(dev->regmap);
0710     kfree(dev);
0711     return 0;
0712 }
0713 
0714 static const struct i2c_device_id ts2020_id_table[] = {
0715     {"ts2020", 0},
0716     {"ts2022", 0},
0717     {}
0718 };
0719 MODULE_DEVICE_TABLE(i2c, ts2020_id_table);
0720 
0721 static struct i2c_driver ts2020_driver = {
0722     .driver = {
0723         .name   = "ts2020",
0724     },
0725     .probe      = ts2020_probe,
0726     .remove     = ts2020_remove,
0727     .id_table   = ts2020_id_table,
0728 };
0729 
0730 module_i2c_driver(ts2020_driver);
0731 
0732 MODULE_AUTHOR("Konstantin Dimitrov <kosio.dimitrov@gmail.com>");
0733 MODULE_DESCRIPTION("Montage Technology TS2020 - Silicon tuner driver module");
0734 MODULE_LICENSE("GPL");