linux/drivers/media/tuners/mt2266.c
<<
>>
Prefs
   1/*
   2 *  Driver for Microtune MT2266 "Direct conversion low power broadband tuner"
   3 *
   4 *  Copyright (c) 2007 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 *  GNU General Public License for more details.
  15 */
  16
  17#include <linux/module.h>
  18#include <linux/delay.h>
  19#include <linux/dvb/frontend.h>
  20#include <linux/i2c.h>
  21#include <linux/slab.h>
  22
  23#include <media/dvb_frontend.h>
  24#include "mt2266.h"
  25
  26#define I2C_ADDRESS 0x60
  27
  28#define REG_PART_REV   0
  29#define REG_TUNE       1
  30#define REG_BAND       6
  31#define REG_BANDWIDTH  8
  32#define REG_LOCK       0x12
  33
  34#define PART_REV 0x85
  35
  36struct mt2266_priv {
  37        struct mt2266_config *cfg;
  38        struct i2c_adapter   *i2c;
  39
  40        u32 frequency;
  41        u32 bandwidth;
  42        u8 band;
  43};
  44
  45#define MT2266_VHF 1
  46#define MT2266_UHF 0
  47
  48/* Here, frequencies are expressed in kiloHertz to avoid 32 bits overflows */
  49
  50static int debug;
  51module_param(debug, int, 0644);
  52MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
  53
  54#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2266: " args); printk("\n"); }} while (0)
  55
  56// Reads a single register
  57static int mt2266_readreg(struct mt2266_priv *priv, u8 reg, u8 *val)
  58{
  59        struct i2c_msg msg[2] = {
  60                { .addr = priv->cfg->i2c_address, .flags = 0,        .buf = &reg, .len = 1 },
  61                { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val,  .len = 1 },
  62        };
  63        if (i2c_transfer(priv->i2c, msg, 2) != 2) {
  64                printk(KERN_WARNING "MT2266 I2C read failed\n");
  65                return -EREMOTEIO;
  66        }
  67        return 0;
  68}
  69
  70// Writes a single register
  71static int mt2266_writereg(struct mt2266_priv *priv, u8 reg, u8 val)
  72{
  73        u8 buf[2] = { reg, val };
  74        struct i2c_msg msg = {
  75                .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2
  76        };
  77        if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
  78                printk(KERN_WARNING "MT2266 I2C write failed\n");
  79                return -EREMOTEIO;
  80        }
  81        return 0;
  82}
  83
  84// Writes a set of consecutive registers
  85static int mt2266_writeregs(struct mt2266_priv *priv,u8 *buf, u8 len)
  86{
  87        struct i2c_msg msg = {
  88                .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len
  89        };
  90        if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
  91                printk(KERN_WARNING "MT2266 I2C write failed (len=%i)\n",(int)len);
  92                return -EREMOTEIO;
  93        }
  94        return 0;
  95}
  96
  97// Initialisation sequences
  98static u8 mt2266_init1[] = { REG_TUNE, 0x00, 0x00, 0x28,
  99                                 0x00, 0x52, 0x99, 0x3f };
 100
 101static u8 mt2266_init2[] = {
 102    0x17, 0x6d, 0x71, 0x61, 0xc0, 0xbf, 0xff, 0xdc, 0x00, 0x0a, 0xd4,
 103    0x03, 0x64, 0x64, 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14,
 104    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7f, 0x5e, 0x3f, 0xff, 0xff,
 105    0xff, 0x00, 0x77, 0x0f, 0x2d
 106};
 107
 108static u8 mt2266_init_8mhz[] = { REG_BANDWIDTH, 0x22, 0x22, 0x22, 0x22,
 109                                                0x22, 0x22, 0x22, 0x22 };
 110
 111static u8 mt2266_init_7mhz[] = { REG_BANDWIDTH, 0x32, 0x32, 0x32, 0x32,
 112                                                0x32, 0x32, 0x32, 0x32 };
 113
 114static u8 mt2266_init_6mhz[] = { REG_BANDWIDTH, 0xa7, 0xa7, 0xa7, 0xa7,
 115                                                0xa7, 0xa7, 0xa7, 0xa7 };
 116
 117static u8 mt2266_uhf[] = { 0x1d, 0xdc, 0x00, 0x0a, 0xd4, 0x03, 0x64, 0x64,
 118                           0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14 };
 119
 120static u8 mt2266_vhf[] = { 0x1d, 0xfe, 0x00, 0x00, 0xb4, 0x03, 0xa5, 0xa5,
 121                           0xa5, 0xa5, 0x82, 0xaa, 0xf1, 0x17, 0x80, 0x1f };
 122
 123#define FREF 30000       // Quartz oscillator 30 MHz
 124
 125static int mt2266_set_params(struct dvb_frontend *fe)
 126{
 127        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 128        struct mt2266_priv *priv;
 129        int ret=0;
 130        u32 freq;
 131        u32 tune;
 132        u8  lnaband;
 133        u8  b[10];
 134        int i;
 135        u8 band;
 136
 137        priv = fe->tuner_priv;
 138
 139        freq = priv->frequency / 1000; /* Hz -> kHz */
 140        if (freq < 470000 && freq > 230000)
 141                return -EINVAL; /* Gap between VHF and UHF bands */
 142
 143        priv->frequency = c->frequency;
 144        tune = 2 * freq * (8192/16) / (FREF/16);
 145        band = (freq < 300000) ? MT2266_VHF : MT2266_UHF;
 146        if (band == MT2266_VHF)
 147                tune *= 2;
 148
 149        switch (c->bandwidth_hz) {
 150        case 6000000:
 151                mt2266_writeregs(priv, mt2266_init_6mhz,
 152                                 sizeof(mt2266_init_6mhz));
 153                break;
 154        case 8000000:
 155                mt2266_writeregs(priv, mt2266_init_8mhz,
 156                                 sizeof(mt2266_init_8mhz));
 157                break;
 158        case 7000000:
 159        default:
 160                mt2266_writeregs(priv, mt2266_init_7mhz,
 161                                 sizeof(mt2266_init_7mhz));
 162                break;
 163        }
 164        priv->bandwidth = c->bandwidth_hz;
 165
 166        if (band == MT2266_VHF && priv->band == MT2266_UHF) {
 167                dprintk("Switch from UHF to VHF");
 168                mt2266_writereg(priv, 0x05, 0x04);
 169                mt2266_writereg(priv, 0x19, 0x61);
 170                mt2266_writeregs(priv, mt2266_vhf, sizeof(mt2266_vhf));
 171        } else if (band == MT2266_UHF && priv->band == MT2266_VHF) {
 172                dprintk("Switch from VHF to UHF");
 173                mt2266_writereg(priv, 0x05, 0x52);
 174                mt2266_writereg(priv, 0x19, 0x61);
 175                mt2266_writeregs(priv, mt2266_uhf, sizeof(mt2266_uhf));
 176        }
 177        msleep(10);
 178
 179        if (freq <= 495000)
 180                lnaband = 0xEE;
 181        else if (freq <= 525000)
 182                lnaband = 0xDD;
 183        else if (freq <= 550000)
 184                lnaband = 0xCC;
 185        else if (freq <= 580000)
 186                lnaband = 0xBB;
 187        else if (freq <= 605000)
 188                lnaband = 0xAA;
 189        else if (freq <= 630000)
 190                lnaband = 0x99;
 191        else if (freq <= 655000)
 192                lnaband = 0x88;
 193        else if (freq <= 685000)
 194                lnaband = 0x77;
 195        else if (freq <= 710000)
 196                lnaband = 0x66;
 197        else if (freq <= 735000)
 198                lnaband = 0x55;
 199        else if (freq <= 765000)
 200                lnaband = 0x44;
 201        else if (freq <= 802000)
 202                lnaband = 0x33;
 203        else if (freq <= 840000)
 204                lnaband = 0x22;
 205        else
 206                lnaband = 0x11;
 207
 208        b[0] = REG_TUNE;
 209        b[1] = (tune >> 8) & 0x1F;
 210        b[2] = tune & 0xFF;
 211        b[3] = tune >> 13;
 212        mt2266_writeregs(priv,b,4);
 213
 214        dprintk("set_parms: tune=%d band=%d %s",
 215                (int) tune, (int) lnaband,
 216                (band == MT2266_UHF) ? "UHF" : "VHF");
 217        dprintk("set_parms: [1..3]: %2x %2x %2x",
 218                (int) b[1], (int) b[2], (int)b[3]);
 219
 220        if (band == MT2266_UHF) {
 221                b[0] = 0x05;
 222                b[1] = (priv->band == MT2266_VHF) ? 0x52 : 0x62;
 223                b[2] = lnaband;
 224                mt2266_writeregs(priv, b, 3);
 225        }
 226
 227        /* Wait for pll lock or timeout */
 228        i = 0;
 229        do {
 230                mt2266_readreg(priv,REG_LOCK,b);
 231                if (b[0] & 0x40)
 232                        break;
 233                msleep(10);
 234                i++;
 235        } while (i<10);
 236        dprintk("Lock when i=%i",(int)i);
 237
 238        if (band == MT2266_UHF && priv->band == MT2266_VHF)
 239                mt2266_writereg(priv, 0x05, 0x62);
 240
 241        priv->band = band;
 242
 243        return ret;
 244}
 245
 246static void mt2266_calibrate(struct mt2266_priv *priv)
 247{
 248        mt2266_writereg(priv, 0x11, 0x03);
 249        mt2266_writereg(priv, 0x11, 0x01);
 250        mt2266_writeregs(priv, mt2266_init1, sizeof(mt2266_init1));
 251        mt2266_writeregs(priv, mt2266_init2, sizeof(mt2266_init2));
 252        mt2266_writereg(priv, 0x33, 0x5e);
 253        mt2266_writereg(priv, 0x10, 0x10);
 254        mt2266_writereg(priv, 0x10, 0x00);
 255        mt2266_writeregs(priv, mt2266_init_8mhz, sizeof(mt2266_init_8mhz));
 256        msleep(25);
 257        mt2266_writereg(priv, 0x17, 0x6d);
 258        mt2266_writereg(priv, 0x1c, 0x00);
 259        msleep(75);
 260        mt2266_writereg(priv, 0x17, 0x6d);
 261        mt2266_writereg(priv, 0x1c, 0xff);
 262}
 263
 264static int mt2266_get_frequency(struct dvb_frontend *fe, u32 *frequency)
 265{
 266        struct mt2266_priv *priv = fe->tuner_priv;
 267        *frequency = priv->frequency;
 268        return 0;
 269}
 270
 271static int mt2266_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
 272{
 273        struct mt2266_priv *priv = fe->tuner_priv;
 274        *bandwidth = priv->bandwidth;
 275        return 0;
 276}
 277
 278static int mt2266_init(struct dvb_frontend *fe)
 279{
 280        int ret;
 281        struct mt2266_priv *priv = fe->tuner_priv;
 282        ret = mt2266_writereg(priv, 0x17, 0x6d);
 283        if (ret < 0)
 284                return ret;
 285        ret = mt2266_writereg(priv, 0x1c, 0xff);
 286        if (ret < 0)
 287                return ret;
 288        return 0;
 289}
 290
 291static int mt2266_sleep(struct dvb_frontend *fe)
 292{
 293        struct mt2266_priv *priv = fe->tuner_priv;
 294        mt2266_writereg(priv, 0x17, 0x6d);
 295        mt2266_writereg(priv, 0x1c, 0x00);
 296        return 0;
 297}
 298
 299static void mt2266_release(struct dvb_frontend *fe)
 300{
 301        kfree(fe->tuner_priv);
 302        fe->tuner_priv = NULL;
 303}
 304
 305static const struct dvb_tuner_ops mt2266_tuner_ops = {
 306        .info = {
 307                .name           = "Microtune MT2266",
 308                .frequency_min  = 174000000,
 309                .frequency_max  = 862000000,
 310                .frequency_step =     50000,
 311        },
 312        .release       = mt2266_release,
 313        .init          = mt2266_init,
 314        .sleep         = mt2266_sleep,
 315        .set_params    = mt2266_set_params,
 316        .get_frequency = mt2266_get_frequency,
 317        .get_bandwidth = mt2266_get_bandwidth
 318};
 319
 320struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg)
 321{
 322        struct mt2266_priv *priv = NULL;
 323        u8 id = 0;
 324
 325        priv = kzalloc(sizeof(struct mt2266_priv), GFP_KERNEL);
 326        if (priv == NULL)
 327                return NULL;
 328
 329        priv->cfg      = cfg;
 330        priv->i2c      = i2c;
 331        priv->band     = MT2266_UHF;
 332
 333        if (mt2266_readreg(priv, 0, &id)) {
 334                kfree(priv);
 335                return NULL;
 336        }
 337        if (id != PART_REV) {
 338                kfree(priv);
 339                return NULL;
 340        }
 341        printk(KERN_INFO "MT2266: successfully identified\n");
 342        memcpy(&fe->ops.tuner_ops, &mt2266_tuner_ops, sizeof(struct dvb_tuner_ops));
 343
 344        fe->tuner_priv = priv;
 345        mt2266_calibrate(priv);
 346        return fe;
 347}
 348EXPORT_SYMBOL(mt2266_attach);
 349
 350MODULE_AUTHOR("Olivier DANET");
 351MODULE_DESCRIPTION("Microtune MT2266 silicon tuner driver");
 352MODULE_LICENSE("GPL");
 353