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