linux/drivers/media/common/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 "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, struct dvb_frontend_parameters *params)
 126{
 127        struct mt2266_priv *priv;
 128        int ret=0;
 129        u32 freq;
 130        u32 tune;
 131        u8  lnaband;
 132        u8  b[10];
 133        int i;
 134        u8 band;
 135
 136        priv = fe->tuner_priv;
 137
 138        freq = params->frequency / 1000; // Hz -> kHz
 139        if (freq < 470000 && freq > 230000)
 140                return -EINVAL; /* Gap between VHF and UHF bands */
 141        priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
 142        priv->frequency = freq * 1000;
 143
 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 (params->u.ofdm.bandwidth) {
 150        case BANDWIDTH_6_MHZ:
 151                mt2266_writeregs(priv, mt2266_init_6mhz,
 152                                 sizeof(mt2266_init_6mhz));
 153                break;
 154        case BANDWIDTH_7_MHZ:
 155                mt2266_writeregs(priv, mt2266_init_7mhz,
 156                                 sizeof(mt2266_init_7mhz));
 157                break;
 158        case BANDWIDTH_8_MHZ:
 159        default:
 160                mt2266_writeregs(priv, mt2266_init_8mhz,
 161                                 sizeof(mt2266_init_8mhz));
 162                break;
 163        }
 164
 165        if (band == MT2266_VHF && priv->band == MT2266_UHF) {
 166                dprintk("Switch from UHF to VHF");
 167                mt2266_writereg(priv, 0x05, 0x04);
 168                mt2266_writereg(priv, 0x19, 0x61);
 169                mt2266_writeregs(priv, mt2266_vhf, sizeof(mt2266_vhf));
 170        } else if (band == MT2266_UHF && priv->band == MT2266_VHF) {
 171                dprintk("Switch from VHF to UHF");
 172                mt2266_writereg(priv, 0x05, 0x52);
 173                mt2266_writereg(priv, 0x19, 0x61);
 174                mt2266_writeregs(priv, mt2266_uhf, sizeof(mt2266_uhf));
 175        }
 176        msleep(10);
 177
 178        if (freq <= 495000)
 179                lnaband = 0xEE;
 180        else if (freq <= 525000)
 181                lnaband = 0xDD;
 182        else if (freq <= 550000)
 183                lnaband = 0xCC;
 184        else if (freq <= 580000)
 185                lnaband = 0xBB;
 186        else if (freq <= 605000)
 187                lnaband = 0xAA;
 188        else if (freq <= 630000)
 189                lnaband = 0x99;
 190        else if (freq <= 655000)
 191                lnaband = 0x88;
 192        else if (freq <= 685000)
 193                lnaband = 0x77;
 194        else if (freq <= 710000)
 195                lnaband = 0x66;
 196        else if (freq <= 735000)
 197                lnaband = 0x55;
 198        else if (freq <= 765000)
 199                lnaband = 0x44;
 200        else if (freq <= 802000)
 201                lnaband = 0x33;
 202        else if (freq <= 840000)
 203                lnaband = 0x22;
 204        else
 205                lnaband = 0x11;
 206
 207        b[0] = REG_TUNE;
 208        b[1] = (tune >> 8) & 0x1F;
 209        b[2] = tune & 0xFF;
 210        b[3] = tune >> 13;
 211        mt2266_writeregs(priv,b,4);
 212
 213        dprintk("set_parms: tune=%d band=%d %s",
 214                (int) tune, (int) lnaband,
 215                (band == MT2266_UHF) ? "UHF" : "VHF");
 216        dprintk("set_parms: [1..3]: %2x %2x %2x",
 217                (int) b[1], (int) b[2], (int)b[3]);
 218
 219        if (band == MT2266_UHF) {
 220                b[0] = 0x05;
 221                b[1] = (priv->band == MT2266_VHF) ? 0x52 : 0x62;
 222                b[2] = lnaband;
 223                mt2266_writeregs(priv, b, 3);
 224        }
 225
 226        /* Wait for pll lock or timeout */
 227        i = 0;
 228        do {
 229                mt2266_readreg(priv,REG_LOCK,b);
 230                if (b[0] & 0x40)
 231                        break;
 232                msleep(10);
 233                i++;
 234        } while (i<10);
 235        dprintk("Lock when i=%i",(int)i);
 236
 237        if (band == MT2266_UHF && priv->band == MT2266_VHF)
 238                mt2266_writereg(priv, 0x05, 0x62);
 239
 240        priv->band = band;
 241
 242        return ret;
 243}
 244
 245static void mt2266_calibrate(struct mt2266_priv *priv)
 246{
 247        mt2266_writereg(priv, 0x11, 0x03);
 248        mt2266_writereg(priv, 0x11, 0x01);
 249        mt2266_writeregs(priv, mt2266_init1, sizeof(mt2266_init1));
 250        mt2266_writeregs(priv, mt2266_init2, sizeof(mt2266_init2));
 251        mt2266_writereg(priv, 0x33, 0x5e);
 252        mt2266_writereg(priv, 0x10, 0x10);
 253        mt2266_writereg(priv, 0x10, 0x00);
 254        mt2266_writeregs(priv, mt2266_init_8mhz, sizeof(mt2266_init_8mhz));
 255        msleep(25);
 256        mt2266_writereg(priv, 0x17, 0x6d);
 257        mt2266_writereg(priv, 0x1c, 0x00);
 258        msleep(75);
 259        mt2266_writereg(priv, 0x17, 0x6d);
 260        mt2266_writereg(priv, 0x1c, 0xff);
 261}
 262
 263static int mt2266_get_frequency(struct dvb_frontend *fe, u32 *frequency)
 264{
 265        struct mt2266_priv *priv = fe->tuner_priv;
 266        *frequency = priv->frequency;
 267        return 0;
 268}
 269
 270static int mt2266_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
 271{
 272        struct mt2266_priv *priv = fe->tuner_priv;
 273        *bandwidth = priv->bandwidth;
 274        return 0;
 275}
 276
 277static int mt2266_init(struct dvb_frontend *fe)
 278{
 279        int ret;
 280        struct mt2266_priv *priv = fe->tuner_priv;
 281        ret = mt2266_writereg(priv, 0x17, 0x6d);
 282        if (ret < 0)
 283                return ret;
 284        ret = mt2266_writereg(priv, 0x1c, 0xff);
 285        if (ret < 0)
 286                return ret;
 287        return 0;
 288}
 289
 290static int mt2266_sleep(struct dvb_frontend *fe)
 291{
 292        struct mt2266_priv *priv = fe->tuner_priv;
 293        mt2266_writereg(priv, 0x17, 0x6d);
 294        mt2266_writereg(priv, 0x1c, 0x00);
 295        return 0;
 296}
 297
 298static int mt2266_release(struct dvb_frontend *fe)
 299{
 300        kfree(fe->tuner_priv);
 301        fe->tuner_priv = NULL;
 302        return 0;
 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