1 /* 2 * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" 3 * 4 * Copyright (c) 2006 Olivier DANET <odanet@caramail.com> 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 /* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ 19 20 #include <linux/module.h> 21 #include <linux/delay.h> 22 #include <linux/dvb/frontend.h> 23 #include <linux/i2c.h> 24 #include <linux/slab.h> 25 26 #include <media/dvb_frontend.h> 27 28 #include "mt2060.h" 29 #include "mt2060_priv.h" 30 31 static int debug; 32 module_param(debug, int, 0644); 33 MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); 34 35 #define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2060: " args); printk("\n"); }} while (0) 36 37 // Reads a single register 38 static int mt2060_readreg(struct mt2060_priv *priv, u8 reg, u8 *val) 39 { 40 struct i2c_msg msg[2] = { 41 { .addr = priv->cfg->i2c_address, .flags = 0, .len = 1 }, 42 { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .len = 1 }, 43 }; 44 int rc = 0; 45 u8 *b; 46 47 b = kmalloc(2, GFP_KERNEL); 48 if (!b) 49 return -ENOMEM; 50 51 b[0] = reg; 52 b[1] = 0; 53 54 msg[0].buf = b; 55 msg[1].buf = b + 1; 56 57 if (i2c_transfer(priv->i2c, msg, 2) != 2) { 58 printk(KERN_WARNING "mt2060 I2C read failed\n"); 59 rc = -EREMOTEIO; 60 } 61 *val = b[1]; 62 kfree(b); 63 64 return rc; 65 } 66 67 // Writes a single register 68 static int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val) 69 { 70 struct i2c_msg msg = { 71 .addr = priv->cfg->i2c_address, .flags = 0, .len = 2 72 }; 73 u8 *buf; 74 int rc = 0; 75 76 buf = kmalloc(2, GFP_KERNEL); 77 if (!buf) 78 return -ENOMEM; 79 80 buf[0] = reg; 81 buf[1] = val; 82 83 msg.buf = buf; 84 85 if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 86 printk(KERN_WARNING "mt2060 I2C write failed\n"); 87 rc = -EREMOTEIO; 88 } 89 kfree(buf); 90 return rc; 91 } 92 93 // Writes a set of consecutive registers 94 static int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len) 95 { 96 int rem, val_len; 97 u8 *xfer_buf; 98 int rc = 0; 99 struct i2c_msg msg = { 100 .addr = priv->cfg->i2c_address, .flags = 0 101 }; 102 103 xfer_buf = kmalloc(16, GFP_KERNEL); 104 if (!xfer_buf) 105 return -ENOMEM; 106 107 msg.buf = xfer_buf; 108 109 for (rem = len - 1; rem > 0; rem -= priv->i2c_max_regs) { 110 val_len = min_t(int, rem, priv->i2c_max_regs); 111 msg.len = 1 + val_len; 112 xfer_buf[0] = buf[0] + len - 1 - rem; 113 memcpy(&xfer_buf[1], &buf[1 + len - 1 - rem], val_len); 114 115 if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 116 printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n", val_len); 117 rc = -EREMOTEIO; 118 break; 119 } 120 } 121 122 kfree(xfer_buf); 123 return rc; 124 } 125 126 // Initialisation sequences 127 // LNABAND=3, NUM1=0x3C, DIV1=0x74, NUM2=0x1080, DIV2=0x49 128 static u8 mt2060_config1[] = { 129 REG_LO1C1, 130 0x3F, 0x74, 0x00, 0x08, 0x93 131 }; 132 133 // FMCG=2, GP2=0, GP1=0 134 static u8 mt2060_config2[] = { 135 REG_MISC_CTRL, 136 0x20, 0x1E, 0x30, 0xff, 0x80, 0xff, 0x00, 0x2c, 0x42 137 }; 138 139 // VGAG=3, V1CSE=1 140 141 #ifdef MT2060_SPURCHECK 142 /* The function below calculates the frequency offset between the output frequency if2 143 and the closer cross modulation subcarrier between lo1 and lo2 up to the tenth harmonic */ 144 static int mt2060_spurcalc(u32 lo1,u32 lo2,u32 if2) 145 { 146 int I,J; 147 int dia,diamin,diff; 148 diamin=1000000; 149 for (I = 1; I < 10; I++) { 150 J = ((2*I*lo1)/lo2+1)/2; 151 diff = I*(int)lo1-J*(int)lo2; 152 if (diff < 0) diff=-diff; 153 dia = (diff-(int)if2); 154 if (dia < 0) dia=-dia; 155 if (diamin > dia) diamin=dia; 156 } 157 return diamin; 158 } 159 160 #define BANDWIDTH 4000 // kHz 161 162 /* Calculates the frequency offset to add to avoid spurs. Returns 0 if no offset is needed */ 163 static int mt2060_spurcheck(u32 lo1,u32 lo2,u32 if2) 164 { 165 u32 Spur,Sp1,Sp2; 166 int I,J; 167 I=0; 168 J=1000; 169 170 Spur=mt2060_spurcalc(lo1,lo2,if2); 171 if (Spur < BANDWIDTH) { 172 /* Potential spurs detected */ 173 dprintk("Spurs before : f_lo1: %d f_lo2: %d (kHz)", 174 (int)lo1,(int)lo2); 175 I=1000; 176 Sp1 = mt2060_spurcalc(lo1+I,lo2+I,if2); 177 Sp2 = mt2060_spurcalc(lo1-I,lo2-I,if2); 178 179 if (Sp1 < Sp2) { 180 J=-J; I=-I; Spur=Sp2; 181 } else 182 Spur=Sp1; 183 184 while (Spur < BANDWIDTH) { 185 I += J; 186 Spur = mt2060_spurcalc(lo1+I,lo2+I,if2); 187 } 188 dprintk("Spurs after : f_lo1: %d f_lo2: %d (kHz)", 189 (int)(lo1+I),(int)(lo2+I)); 190 } 191 return I; 192 } 193 #endif 194 195 #define IF2 36150 // IF2 frequency = 36.150 MHz 196 #define FREF 16000 // Quartz oscillator 16 MHz 197 198 static int mt2060_set_params(struct dvb_frontend *fe) 199 { 200 struct dtv_frontend_properties *c = &fe->dtv_property_cache; 201 struct mt2060_priv *priv; 202 int i=0; 203 u32 freq; 204 u8 lnaband; 205 u32 f_lo1,f_lo2; 206 u32 div1,num1,div2,num2; 207 u8 b[8]; 208 u32 if1; 209 210 priv = fe->tuner_priv; 211 212 if1 = priv->if1_freq; 213 b[0] = REG_LO1B1; 214 b[1] = 0xFF; 215 216 if (fe->ops.i2c_gate_ctrl) 217 fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 218 219 mt2060_writeregs(priv,b,2); 220 221 freq = c->frequency / 1000; /* Hz -> kHz */ 222 223 f_lo1 = freq + if1 * 1000; 224 f_lo1 = (f_lo1 / 250) * 250; 225 f_lo2 = f_lo1 - freq - IF2; 226 // From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise 227 f_lo2 = ((f_lo2 + 25) / 50) * 50; 228 priv->frequency = (f_lo1 - f_lo2 - IF2) * 1000, 229 230 #ifdef MT2060_SPURCHECK 231 // LO-related spurs detection and correction 232 num1 = mt2060_spurcheck(f_lo1,f_lo2,IF2); 233 f_lo1 += num1; 234 f_lo2 += num1; 235 #endif 236 //Frequency LO1 = 16MHz * (DIV1 + NUM1/64 ) 237 num1 = f_lo1 / (FREF / 64); 238 div1 = num1 / 64; 239 num1 &= 0x3f; 240 241 // Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) 242 num2 = f_lo2 * 64 / (FREF / 128); 243 div2 = num2 / 8192; 244 num2 &= 0x1fff; 245 246 if (freq <= 95000) lnaband = 0xB0; else 247 if (freq <= 180000) lnaband = 0xA0; else 248 if (freq <= 260000) lnaband = 0x90; else 249 if (freq <= 335000) lnaband = 0x80; else 250 if (freq <= 425000) lnaband = 0x70; else 251 if (freq <= 480000) lnaband = 0x60; else 252 if (freq <= 570000) lnaband = 0x50; else 253 if (freq <= 645000) lnaband = 0x40; else 254 if (freq <= 730000) lnaband = 0x30; else 255 if (freq <= 810000) lnaband = 0x20; else lnaband = 0x10; 256 257 b[0] = REG_LO1C1; 258 b[1] = lnaband | ((num1 >>2) & 0x0F); 259 b[2] = div1; 260 b[3] = (num2 & 0x0F) | ((num1 & 3) << 4); 261 b[4] = num2 >> 4; 262 b[5] = ((num2 >>12) & 1) | (div2 << 1); 263 264 dprintk("IF1: %dMHz",(int)if1); 265 dprintk("PLL freq=%dkHz f_lo1=%dkHz f_lo2=%dkHz",(int)freq,(int)f_lo1,(int)f_lo2); 266 dprintk("PLL div1=%d num1=%d div2=%d num2=%d",(int)div1,(int)num1,(int)div2,(int)num2); 267 dprintk("PLL [1..5]: %2x %2x %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3],(int)b[4],(int)b[5]); 268 269 mt2060_writeregs(priv,b,6); 270 271 //Waits for pll lock or timeout 272 i = 0; 273 do { 274 mt2060_readreg(priv,REG_LO_STATUS,b); 275 if ((b[0] & 0x88)==0x88) 276 break; 277 msleep(4); 278 i++; 279 } while (i<10); 280 281 if (fe->ops.i2c_gate_ctrl) 282 fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 283 284 return 0; 285 } 286 287 static void mt2060_calibrate(struct mt2060_priv *priv) 288 { 289 u8 b = 0; 290 int i = 0; 291 292 if (mt2060_writeregs(priv,mt2060_config1,sizeof(mt2060_config1))) 293 return; 294 if (mt2060_writeregs(priv,mt2060_config2,sizeof(mt2060_config2))) 295 return; 296 297 /* initialize the clock output */ 298 mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30); 299 300 do { 301 b |= (1 << 6); // FM1SS; 302 mt2060_writereg(priv, REG_LO2C1,b); 303 msleep(20); 304 305 if (i == 0) { 306 b |= (1 << 7); // FM1CA; 307 mt2060_writereg(priv, REG_LO2C1,b); 308 b &= ~(1 << 7); // FM1CA; 309 msleep(20); 310 } 311 312 b &= ~(1 << 6); // FM1SS 313 mt2060_writereg(priv, REG_LO2C1,b); 314 315 msleep(20); 316 i++; 317 } while (i < 9); 318 319 i = 0; 320 while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0) 321 msleep(20); 322 323 if (i <= 10) { 324 mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :) 325 dprintk("calibration was successful: %d", (int)priv->fmfreq); 326 } else 327 dprintk("FMCAL timed out"); 328 } 329 330 static int mt2060_get_frequency(struct dvb_frontend *fe, u32 *frequency) 331 { 332 struct mt2060_priv *priv = fe->tuner_priv; 333 *frequency = priv->frequency; 334 return 0; 335 } 336 337 static int mt2060_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 338 { 339 *frequency = IF2 * 1000; 340 return 0; 341 } 342 343 static int mt2060_init(struct dvb_frontend *fe) 344 { 345 struct mt2060_priv *priv = fe->tuner_priv; 346 int ret; 347 348 if (fe->ops.i2c_gate_ctrl) 349 fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 350 351 if (priv->sleep) { 352 ret = mt2060_writereg(priv, REG_MISC_CTRL, 0x20); 353 if (ret) 354 goto err_i2c_gate_ctrl; 355 } 356 357 ret = mt2060_writereg(priv, REG_VGAG, 358 (priv->cfg->clock_out << 6) | 0x33); 359 360 err_i2c_gate_ctrl: 361 if (fe->ops.i2c_gate_ctrl) 362 fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 363 364 return ret; 365 } 366 367 static int mt2060_sleep(struct dvb_frontend *fe) 368 { 369 struct mt2060_priv *priv = fe->tuner_priv; 370 int ret; 371 372 if (fe->ops.i2c_gate_ctrl) 373 fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 374 375 ret = mt2060_writereg(priv, REG_VGAG, 376 (priv->cfg->clock_out << 6) | 0x30); 377 if (ret) 378 goto err_i2c_gate_ctrl; 379 380 if (priv->sleep) 381 ret = mt2060_writereg(priv, REG_MISC_CTRL, 0xe8); 382 383 err_i2c_gate_ctrl: 384 if (fe->ops.i2c_gate_ctrl) 385 fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 386 387 return ret; 388 } 389 390 static void mt2060_release(struct dvb_frontend *fe) 391 { 392 kfree(fe->tuner_priv); 393 fe->tuner_priv = NULL; 394 } 395 396 static const struct dvb_tuner_ops mt2060_tuner_ops = { 397 .info = { 398 .name = "Microtune MT2060", 399 .frequency_min_hz = 48 * MHz, 400 .frequency_max_hz = 860 * MHz, 401 .frequency_step_hz = 50 * kHz, 402 }, 403 404 .release = mt2060_release, 405 406 .init = mt2060_init, 407 .sleep = mt2060_sleep, 408 409 .set_params = mt2060_set_params, 410 .get_frequency = mt2060_get_frequency, 411 .get_if_frequency = mt2060_get_if_frequency, 412 }; 413 414 /* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */ 415 struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1) 416 { 417 struct mt2060_priv *priv = NULL; 418 u8 id = 0; 419 420 priv = kzalloc(sizeof(struct mt2060_priv), GFP_KERNEL); 421 if (priv == NULL) 422 return NULL; 423 424 priv->cfg = cfg; 425 priv->i2c = i2c; 426 priv->if1_freq = if1; 427 priv->i2c_max_regs = ~0; 428 429 if (fe->ops.i2c_gate_ctrl) 430 fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 431 432 if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) { 433 kfree(priv); 434 return NULL; 435 } 436 437 if (id != PART_REV) { 438 kfree(priv); 439 return NULL; 440 } 441 printk(KERN_INFO "MT2060: successfully identified (IF1 = %d)\n", if1); 442 memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops)); 443 444 fe->tuner_priv = priv; 445 446 mt2060_calibrate(priv); 447 448 if (fe->ops.i2c_gate_ctrl) 449 fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 450 451 return fe; 452 } 453 EXPORT_SYMBOL(mt2060_attach); 454 455 static int mt2060_probe(struct i2c_client *client, 456 const struct i2c_device_id *id) 457 { 458 struct mt2060_platform_data *pdata = client->dev.platform_data; 459 struct dvb_frontend *fe; 460 struct mt2060_priv *dev; 461 int ret; 462 u8 chip_id; 463 464 dev_dbg(&client->dev, "\n"); 465 466 if (!pdata) { 467 dev_err(&client->dev, "Cannot proceed without platform data\n"); 468 ret = -EINVAL; 469 goto err; 470 } 471 472 dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); 473 if (!dev) { 474 ret = -ENOMEM; 475 goto err; 476 } 477 478 fe = pdata->dvb_frontend; 479 dev->config.i2c_address = client->addr; 480 dev->config.clock_out = pdata->clock_out; 481 dev->cfg = &dev->config; 482 dev->i2c = client->adapter; 483 dev->if1_freq = pdata->if1 ? pdata->if1 : 1220; 484 dev->client = client; 485 dev->i2c_max_regs = pdata->i2c_write_max ? pdata->i2c_write_max - 1 : ~0; 486 dev->sleep = true; 487 488 ret = mt2060_readreg(dev, REG_PART_REV, &chip_id); 489 if (ret) { 490 ret = -ENODEV; 491 goto err; 492 } 493 494 dev_dbg(&client->dev, "chip id=%02x\n", chip_id); 495 496 if (chip_id != PART_REV) { 497 ret = -ENODEV; 498 goto err; 499 } 500 501 /* Power on, calibrate, sleep */ 502 ret = mt2060_writereg(dev, REG_MISC_CTRL, 0x20); 503 if (ret) 504 goto err; 505 mt2060_calibrate(dev); 506 ret = mt2060_writereg(dev, REG_MISC_CTRL, 0xe8); 507 if (ret) 508 goto err; 509 510 dev_info(&client->dev, "Microtune MT2060 successfully identified\n"); 511 memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(fe->ops.tuner_ops)); 512 fe->ops.tuner_ops.release = NULL; 513 fe->tuner_priv = dev; 514 i2c_set_clientdata(client, dev); 515 516 return 0; 517 err: 518 dev_dbg(&client->dev, "failed=%d\n", ret); 519 return ret; 520 } 521 522 static int mt2060_remove(struct i2c_client *client) 523 { 524 dev_dbg(&client->dev, "\n"); 525 526 return 0; 527 } 528 529 static const struct i2c_device_id mt2060_id_table[] = { 530 {"mt2060", 0}, 531 {} 532 }; 533 MODULE_DEVICE_TABLE(i2c, mt2060_id_table); 534 535 static struct i2c_driver mt2060_driver = { 536 .driver = { 537 .name = "mt2060", 538 .suppress_bind_attrs = true, 539 }, 540 .probe = mt2060_probe, 541 .remove = mt2060_remove, 542 .id_table = mt2060_id_table, 543 }; 544 545 module_i2c_driver(mt2060_driver); 546 547 MODULE_AUTHOR("Olivier DANET"); 548 MODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver"); 549 MODULE_LICENSE("GPL"); 550