Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Driver for Microtune MT2060 "Single chip dual conversion broadband tuner"
0004  *
0005  *  Copyright (c) 2006 Olivier DANET <odanet@caramail.com>
0006  */
0007 
0008 /* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */
0009 
0010 #include <linux/module.h>
0011 #include <linux/delay.h>
0012 #include <linux/dvb/frontend.h>
0013 #include <linux/i2c.h>
0014 #include <linux/slab.h>
0015 
0016 #include <media/dvb_frontend.h>
0017 
0018 #include "mt2060.h"
0019 #include "mt2060_priv.h"
0020 
0021 static int debug;
0022 module_param(debug, int, 0644);
0023 MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
0024 
0025 #define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2060: " args); printk("\n"); }} while (0)
0026 
0027 // Reads a single register
0028 static int mt2060_readreg(struct mt2060_priv *priv, u8 reg, u8 *val)
0029 {
0030     struct i2c_msg msg[2] = {
0031         { .addr = priv->cfg->i2c_address, .flags = 0, .len = 1 },
0032         { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .len = 1 },
0033     };
0034     int rc = 0;
0035     u8 *b;
0036 
0037     b = kmalloc(2, GFP_KERNEL);
0038     if (!b)
0039         return -ENOMEM;
0040 
0041     b[0] = reg;
0042     b[1] = 0;
0043 
0044     msg[0].buf = b;
0045     msg[1].buf = b + 1;
0046 
0047     if (i2c_transfer(priv->i2c, msg, 2) != 2) {
0048         printk(KERN_WARNING "mt2060 I2C read failed\n");
0049         rc = -EREMOTEIO;
0050     }
0051     *val = b[1];
0052     kfree(b);
0053 
0054     return rc;
0055 }
0056 
0057 // Writes a single register
0058 static int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val)
0059 {
0060     struct i2c_msg msg = {
0061         .addr = priv->cfg->i2c_address, .flags = 0, .len = 2
0062     };
0063     u8 *buf;
0064     int rc = 0;
0065 
0066     buf = kmalloc(2, GFP_KERNEL);
0067     if (!buf)
0068         return -ENOMEM;
0069 
0070     buf[0] = reg;
0071     buf[1] = val;
0072 
0073     msg.buf = buf;
0074 
0075     if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
0076         printk(KERN_WARNING "mt2060 I2C write failed\n");
0077         rc = -EREMOTEIO;
0078     }
0079     kfree(buf);
0080     return rc;
0081 }
0082 
0083 // Writes a set of consecutive registers
0084 static int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len)
0085 {
0086     int rem, val_len;
0087     u8 *xfer_buf;
0088     int rc = 0;
0089     struct i2c_msg msg = {
0090         .addr = priv->cfg->i2c_address, .flags = 0
0091     };
0092 
0093     xfer_buf = kmalloc(16, GFP_KERNEL);
0094     if (!xfer_buf)
0095         return -ENOMEM;
0096 
0097     msg.buf = xfer_buf;
0098 
0099     for (rem = len - 1; rem > 0; rem -= priv->i2c_max_regs) {
0100         val_len = min_t(int, rem, priv->i2c_max_regs);
0101         msg.len = 1 + val_len;
0102         xfer_buf[0] = buf[0] + len - 1 - rem;
0103         memcpy(&xfer_buf[1], &buf[1 + len - 1 - rem], val_len);
0104 
0105         if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
0106             printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n", val_len);
0107             rc = -EREMOTEIO;
0108             break;
0109         }
0110     }
0111 
0112     kfree(xfer_buf);
0113     return rc;
0114 }
0115 
0116 // Initialisation sequences
0117 // LNABAND=3, NUM1=0x3C, DIV1=0x74, NUM2=0x1080, DIV2=0x49
0118 static u8 mt2060_config1[] = {
0119     REG_LO1C1,
0120     0x3F,   0x74,   0x00,   0x08,   0x93
0121 };
0122 
0123 // FMCG=2, GP2=0, GP1=0
0124 static u8 mt2060_config2[] = {
0125     REG_MISC_CTRL,
0126     0x20,   0x1E,   0x30,   0xff,   0x80,   0xff,   0x00,   0x2c,   0x42
0127 };
0128 
0129 //  VGAG=3, V1CSE=1
0130 
0131 #ifdef  MT2060_SPURCHECK
0132 /* The function below calculates the frequency offset between the output frequency if2
0133  and the closer cross modulation subcarrier between lo1 and lo2 up to the tenth harmonic */
0134 static int mt2060_spurcalc(u32 lo1,u32 lo2,u32 if2)
0135 {
0136     int I,J;
0137     int dia,diamin,diff;
0138     diamin=1000000;
0139     for (I = 1; I < 10; I++) {
0140         J = ((2*I*lo1)/lo2+1)/2;
0141         diff = I*(int)lo1-J*(int)lo2;
0142         if (diff < 0) diff=-diff;
0143         dia = (diff-(int)if2);
0144         if (dia < 0) dia=-dia;
0145         if (diamin > dia) diamin=dia;
0146     }
0147     return diamin;
0148 }
0149 
0150 #define BANDWIDTH 4000 // kHz
0151 
0152 /* Calculates the frequency offset to add to avoid spurs. Returns 0 if no offset is needed */
0153 static int mt2060_spurcheck(u32 lo1,u32 lo2,u32 if2)
0154 {
0155     u32 Spur,Sp1,Sp2;
0156     int I,J;
0157     I=0;
0158     J=1000;
0159 
0160     Spur=mt2060_spurcalc(lo1,lo2,if2);
0161     if (Spur < BANDWIDTH) {
0162         /* Potential spurs detected */
0163         dprintk("Spurs before : f_lo1: %d  f_lo2: %d  (kHz)",
0164             (int)lo1,(int)lo2);
0165         I=1000;
0166         Sp1 = mt2060_spurcalc(lo1+I,lo2+I,if2);
0167         Sp2 = mt2060_spurcalc(lo1-I,lo2-I,if2);
0168 
0169         if (Sp1 < Sp2) {
0170             J=-J; I=-I; Spur=Sp2;
0171         } else
0172             Spur=Sp1;
0173 
0174         while (Spur < BANDWIDTH) {
0175             I += J;
0176             Spur = mt2060_spurcalc(lo1+I,lo2+I,if2);
0177         }
0178         dprintk("Spurs after  : f_lo1: %d  f_lo2: %d  (kHz)",
0179             (int)(lo1+I),(int)(lo2+I));
0180     }
0181     return I;
0182 }
0183 #endif
0184 
0185 #define IF2  36150       // IF2 frequency = 36.150 MHz
0186 #define FREF 16000       // Quartz oscillator 16 MHz
0187 
0188 static int mt2060_set_params(struct dvb_frontend *fe)
0189 {
0190     struct dtv_frontend_properties *c = &fe->dtv_property_cache;
0191     struct mt2060_priv *priv;
0192     int i=0;
0193     u32 freq;
0194     u8  lnaband;
0195     u32 f_lo1,f_lo2;
0196     u32 div1,num1,div2,num2;
0197     u8  b[8];
0198     u32 if1;
0199 
0200     priv = fe->tuner_priv;
0201 
0202     if1 = priv->if1_freq;
0203     b[0] = REG_LO1B1;
0204     b[1] = 0xFF;
0205 
0206     if (fe->ops.i2c_gate_ctrl)
0207         fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
0208 
0209     mt2060_writeregs(priv,b,2);
0210 
0211     freq = c->frequency / 1000; /* Hz -> kHz */
0212 
0213     f_lo1 = freq + if1 * 1000;
0214     f_lo1 = (f_lo1 / 250) * 250;
0215     f_lo2 = f_lo1 - freq - IF2;
0216     // From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise
0217     f_lo2 = ((f_lo2 + 25) / 50) * 50;
0218     priv->frequency =  (f_lo1 - f_lo2 - IF2) * 1000;
0219 
0220 #ifdef MT2060_SPURCHECK
0221     // LO-related spurs detection and correction
0222     num1   = mt2060_spurcheck(f_lo1,f_lo2,IF2);
0223     f_lo1 += num1;
0224     f_lo2 += num1;
0225 #endif
0226     //Frequency LO1 = 16MHz * (DIV1 + NUM1/64 )
0227     num1 = f_lo1 / (FREF / 64);
0228     div1 = num1 / 64;
0229     num1 &= 0x3f;
0230 
0231     // Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 )
0232     num2 = f_lo2 * 64 / (FREF / 128);
0233     div2 = num2 / 8192;
0234     num2 &= 0x1fff;
0235 
0236     if (freq <=  95000) lnaband = 0xB0; else
0237     if (freq <= 180000) lnaband = 0xA0; else
0238     if (freq <= 260000) lnaband = 0x90; else
0239     if (freq <= 335000) lnaband = 0x80; else
0240     if (freq <= 425000) lnaband = 0x70; else
0241     if (freq <= 480000) lnaband = 0x60; else
0242     if (freq <= 570000) lnaband = 0x50; else
0243     if (freq <= 645000) lnaband = 0x40; else
0244     if (freq <= 730000) lnaband = 0x30; else
0245     if (freq <= 810000) lnaband = 0x20; else lnaband = 0x10;
0246 
0247     b[0] = REG_LO1C1;
0248     b[1] = lnaband | ((num1 >>2) & 0x0F);
0249     b[2] = div1;
0250     b[3] = (num2 & 0x0F)  | ((num1 & 3) << 4);
0251     b[4] = num2 >> 4;
0252     b[5] = ((num2 >>12) & 1) | (div2 << 1);
0253 
0254     dprintk("IF1: %dMHz",(int)if1);
0255     dprintk("PLL freq=%dkHz  f_lo1=%dkHz  f_lo2=%dkHz",(int)freq,(int)f_lo1,(int)f_lo2);
0256     dprintk("PLL div1=%d  num1=%d  div2=%d  num2=%d",(int)div1,(int)num1,(int)div2,(int)num2);
0257     dprintk("PLL [1..5]: %2x %2x %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3],(int)b[4],(int)b[5]);
0258 
0259     mt2060_writeregs(priv,b,6);
0260 
0261     //Waits for pll lock or timeout
0262     i = 0;
0263     do {
0264         mt2060_readreg(priv,REG_LO_STATUS,b);
0265         if ((b[0] & 0x88)==0x88)
0266             break;
0267         msleep(4);
0268         i++;
0269     } while (i<10);
0270 
0271     if (fe->ops.i2c_gate_ctrl)
0272         fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
0273 
0274     return 0;
0275 }
0276 
0277 static void mt2060_calibrate(struct mt2060_priv *priv)
0278 {
0279     u8 b = 0;
0280     int i = 0;
0281 
0282     if (mt2060_writeregs(priv,mt2060_config1,sizeof(mt2060_config1)))
0283         return;
0284     if (mt2060_writeregs(priv,mt2060_config2,sizeof(mt2060_config2)))
0285         return;
0286 
0287     /* initialize the clock output */
0288     mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30);
0289 
0290     do {
0291         b |= (1 << 6); // FM1SS;
0292         mt2060_writereg(priv, REG_LO2C1,b);
0293         msleep(20);
0294 
0295         if (i == 0) {
0296             b |= (1 << 7); // FM1CA;
0297             mt2060_writereg(priv, REG_LO2C1,b);
0298             b &= ~(1 << 7); // FM1CA;
0299             msleep(20);
0300         }
0301 
0302         b &= ~(1 << 6); // FM1SS
0303         mt2060_writereg(priv, REG_LO2C1,b);
0304 
0305         msleep(20);
0306         i++;
0307     } while (i < 9);
0308 
0309     i = 0;
0310     while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0)
0311         msleep(20);
0312 
0313     if (i <= 10) {
0314         mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :)
0315         dprintk("calibration was successful: %d", (int)priv->fmfreq);
0316     } else
0317         dprintk("FMCAL timed out");
0318 }
0319 
0320 static int mt2060_get_frequency(struct dvb_frontend *fe, u32 *frequency)
0321 {
0322     struct mt2060_priv *priv = fe->tuner_priv;
0323     *frequency = priv->frequency;
0324     return 0;
0325 }
0326 
0327 static int mt2060_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
0328 {
0329     *frequency = IF2 * 1000;
0330     return 0;
0331 }
0332 
0333 static int mt2060_init(struct dvb_frontend *fe)
0334 {
0335     struct mt2060_priv *priv = fe->tuner_priv;
0336     int ret;
0337 
0338     if (fe->ops.i2c_gate_ctrl)
0339         fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
0340 
0341     if (priv->sleep) {
0342         ret = mt2060_writereg(priv, REG_MISC_CTRL, 0x20);
0343         if (ret)
0344             goto err_i2c_gate_ctrl;
0345     }
0346 
0347     ret = mt2060_writereg(priv, REG_VGAG,
0348                   (priv->cfg->clock_out << 6) | 0x33);
0349 
0350 err_i2c_gate_ctrl:
0351     if (fe->ops.i2c_gate_ctrl)
0352         fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
0353 
0354     return ret;
0355 }
0356 
0357 static int mt2060_sleep(struct dvb_frontend *fe)
0358 {
0359     struct mt2060_priv *priv = fe->tuner_priv;
0360     int ret;
0361 
0362     if (fe->ops.i2c_gate_ctrl)
0363         fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
0364 
0365     ret = mt2060_writereg(priv, REG_VGAG,
0366                   (priv->cfg->clock_out << 6) | 0x30);
0367     if (ret)
0368         goto err_i2c_gate_ctrl;
0369 
0370     if (priv->sleep)
0371         ret = mt2060_writereg(priv, REG_MISC_CTRL, 0xe8);
0372 
0373 err_i2c_gate_ctrl:
0374     if (fe->ops.i2c_gate_ctrl)
0375         fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
0376 
0377     return ret;
0378 }
0379 
0380 static void mt2060_release(struct dvb_frontend *fe)
0381 {
0382     kfree(fe->tuner_priv);
0383     fe->tuner_priv = NULL;
0384 }
0385 
0386 static const struct dvb_tuner_ops mt2060_tuner_ops = {
0387     .info = {
0388         .name              = "Microtune MT2060",
0389         .frequency_min_hz  =  48 * MHz,
0390         .frequency_max_hz  = 860 * MHz,
0391         .frequency_step_hz =  50 * kHz,
0392     },
0393 
0394     .release       = mt2060_release,
0395 
0396     .init          = mt2060_init,
0397     .sleep         = mt2060_sleep,
0398 
0399     .set_params    = mt2060_set_params,
0400     .get_frequency = mt2060_get_frequency,
0401     .get_if_frequency = mt2060_get_if_frequency,
0402 };
0403 
0404 /* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */
0405 struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1)
0406 {
0407     struct mt2060_priv *priv = NULL;
0408     u8 id = 0;
0409 
0410     priv = kzalloc(sizeof(struct mt2060_priv), GFP_KERNEL);
0411     if (priv == NULL)
0412         return NULL;
0413 
0414     priv->cfg      = cfg;
0415     priv->i2c      = i2c;
0416     priv->if1_freq = if1;
0417     priv->i2c_max_regs = ~0;
0418 
0419     if (fe->ops.i2c_gate_ctrl)
0420         fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
0421 
0422     if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) {
0423         kfree(priv);
0424         return NULL;
0425     }
0426 
0427     if (id != PART_REV) {
0428         kfree(priv);
0429         return NULL;
0430     }
0431     printk(KERN_INFO "MT2060: successfully identified (IF1 = %d)\n", if1);
0432     memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops));
0433 
0434     fe->tuner_priv = priv;
0435 
0436     mt2060_calibrate(priv);
0437 
0438     if (fe->ops.i2c_gate_ctrl)
0439         fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
0440 
0441     return fe;
0442 }
0443 EXPORT_SYMBOL(mt2060_attach);
0444 
0445 static int mt2060_probe(struct i2c_client *client,
0446             const struct i2c_device_id *id)
0447 {
0448     struct mt2060_platform_data *pdata = client->dev.platform_data;
0449     struct dvb_frontend *fe;
0450     struct mt2060_priv *dev;
0451     int ret;
0452     u8 chip_id;
0453 
0454     dev_dbg(&client->dev, "\n");
0455 
0456     if (!pdata) {
0457         dev_err(&client->dev, "Cannot proceed without platform data\n");
0458         ret = -EINVAL;
0459         goto err;
0460     }
0461 
0462     dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
0463     if (!dev) {
0464         ret = -ENOMEM;
0465         goto err;
0466     }
0467 
0468     fe = pdata->dvb_frontend;
0469     dev->config.i2c_address = client->addr;
0470     dev->config.clock_out = pdata->clock_out;
0471     dev->cfg = &dev->config;
0472     dev->i2c = client->adapter;
0473     dev->if1_freq = pdata->if1 ? pdata->if1 : 1220;
0474     dev->client = client;
0475     dev->i2c_max_regs = pdata->i2c_write_max ? pdata->i2c_write_max - 1 : ~0;
0476     dev->sleep = true;
0477 
0478     ret = mt2060_readreg(dev, REG_PART_REV, &chip_id);
0479     if (ret) {
0480         ret = -ENODEV;
0481         goto err;
0482     }
0483 
0484     dev_dbg(&client->dev, "chip id=%02x\n", chip_id);
0485 
0486     if (chip_id != PART_REV) {
0487         ret = -ENODEV;
0488         goto err;
0489     }
0490 
0491     /* Power on, calibrate, sleep */
0492     ret = mt2060_writereg(dev, REG_MISC_CTRL, 0x20);
0493     if (ret)
0494         goto err;
0495     mt2060_calibrate(dev);
0496     ret = mt2060_writereg(dev, REG_MISC_CTRL, 0xe8);
0497     if (ret)
0498         goto err;
0499 
0500     dev_info(&client->dev, "Microtune MT2060 successfully identified\n");
0501     memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(fe->ops.tuner_ops));
0502     fe->ops.tuner_ops.release = NULL;
0503     fe->tuner_priv = dev;
0504     i2c_set_clientdata(client, dev);
0505 
0506     return 0;
0507 err:
0508     dev_dbg(&client->dev, "failed=%d\n", ret);
0509     return ret;
0510 }
0511 
0512 static int mt2060_remove(struct i2c_client *client)
0513 {
0514     dev_dbg(&client->dev, "\n");
0515 
0516     return 0;
0517 }
0518 
0519 static const struct i2c_device_id mt2060_id_table[] = {
0520     {"mt2060", 0},
0521     {}
0522 };
0523 MODULE_DEVICE_TABLE(i2c, mt2060_id_table);
0524 
0525 static struct i2c_driver mt2060_driver = {
0526     .driver = {
0527         .name = "mt2060",
0528         .suppress_bind_attrs = true,
0529     },
0530     .probe      = mt2060_probe,
0531     .remove     = mt2060_remove,
0532     .id_table   = mt2060_id_table,
0533 };
0534 
0535 module_i2c_driver(mt2060_driver);
0536 
0537 MODULE_AUTHOR("Olivier DANET");
0538 MODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver");
0539 MODULE_LICENSE("GPL");