linux/drivers/media/tuners/mc44s803.c
<<
>>
Prefs
   1/*
   2 *  Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner
   3 *
   4 *  Copyright (c) 2009 Jochen Friedrich <jochen@scram.de>
   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#include <linux/module.h>
  19#include <linux/delay.h>
  20#include <linux/dvb/frontend.h>
  21#include <linux/i2c.h>
  22#include <linux/slab.h>
  23
  24#include <media/dvb_frontend.h>
  25
  26#include "mc44s803.h"
  27#include "mc44s803_priv.h"
  28
  29#define mc_printk(level, format, arg...)        \
  30        printk(level "mc44s803: " format , ## arg)
  31
  32/* Writes a single register */
  33static int mc44s803_writereg(struct mc44s803_priv *priv, u32 val)
  34{
  35        u8 buf[3];
  36        struct i2c_msg msg = {
  37                .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 3
  38        };
  39
  40        buf[0] = (val & 0xff0000) >> 16;
  41        buf[1] = (val & 0xff00) >> 8;
  42        buf[2] = (val & 0xff);
  43
  44        if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
  45                mc_printk(KERN_WARNING, "I2C write failed\n");
  46                return -EREMOTEIO;
  47        }
  48        return 0;
  49}
  50
  51/* Reads a single register */
  52static int mc44s803_readreg(struct mc44s803_priv *priv, u8 reg, u32 *val)
  53{
  54        u32 wval;
  55        u8 buf[3];
  56        int ret;
  57        struct i2c_msg msg[] = {
  58                { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD,
  59                  .buf = buf, .len = 3 },
  60        };
  61
  62        wval = MC44S803_REG_SM(MC44S803_REG_DATAREG, MC44S803_ADDR) |
  63               MC44S803_REG_SM(reg, MC44S803_D);
  64
  65        ret = mc44s803_writereg(priv, wval);
  66        if (ret)
  67                return ret;
  68
  69        if (i2c_transfer(priv->i2c, msg, 1) != 1) {
  70                mc_printk(KERN_WARNING, "I2C read failed\n");
  71                return -EREMOTEIO;
  72        }
  73
  74        *val = (buf[0] << 16) | (buf[1] << 8) | buf[2];
  75
  76        return 0;
  77}
  78
  79static void mc44s803_release(struct dvb_frontend *fe)
  80{
  81        struct mc44s803_priv *priv = fe->tuner_priv;
  82
  83        fe->tuner_priv = NULL;
  84        kfree(priv);
  85}
  86
  87static int mc44s803_init(struct dvb_frontend *fe)
  88{
  89        struct mc44s803_priv *priv = fe->tuner_priv;
  90        u32 val;
  91        int err;
  92
  93        if (fe->ops.i2c_gate_ctrl)
  94                fe->ops.i2c_gate_ctrl(fe, 1);
  95
  96/* Reset chip */
  97        val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR) |
  98              MC44S803_REG_SM(1, MC44S803_RS);
  99
 100        err = mc44s803_writereg(priv, val);
 101        if (err)
 102                goto exit;
 103
 104        val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR);
 105
 106        err = mc44s803_writereg(priv, val);
 107        if (err)
 108                goto exit;
 109
 110/* Power Up and Start Osc */
 111
 112        val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) |
 113              MC44S803_REG_SM(0xC0, MC44S803_REFOSC) |
 114              MC44S803_REG_SM(1, MC44S803_OSCSEL);
 115
 116        err = mc44s803_writereg(priv, val);
 117        if (err)
 118                goto exit;
 119
 120        val = MC44S803_REG_SM(MC44S803_REG_POWER, MC44S803_ADDR) |
 121              MC44S803_REG_SM(0x200, MC44S803_POWER);
 122
 123        err = mc44s803_writereg(priv, val);
 124        if (err)
 125                goto exit;
 126
 127        msleep(10);
 128
 129        val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) |
 130              MC44S803_REG_SM(0x40, MC44S803_REFOSC) |
 131              MC44S803_REG_SM(1, MC44S803_OSCSEL);
 132
 133        err = mc44s803_writereg(priv, val);
 134        if (err)
 135                goto exit;
 136
 137        msleep(20);
 138
 139/* Setup Mixer */
 140
 141        val = MC44S803_REG_SM(MC44S803_REG_MIXER, MC44S803_ADDR) |
 142              MC44S803_REG_SM(1, MC44S803_TRI_STATE) |
 143              MC44S803_REG_SM(0x7F, MC44S803_MIXER_RES);
 144
 145        err = mc44s803_writereg(priv, val);
 146        if (err)
 147                goto exit;
 148
 149/* Setup Cirquit Adjust */
 150
 151        val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) |
 152              MC44S803_REG_SM(1, MC44S803_G1) |
 153              MC44S803_REG_SM(1, MC44S803_G3) |
 154              MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) |
 155              MC44S803_REG_SM(1, MC44S803_G6) |
 156              MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) |
 157              MC44S803_REG_SM(0x3, MC44S803_LP) |
 158              MC44S803_REG_SM(1, MC44S803_CLRF) |
 159              MC44S803_REG_SM(1, MC44S803_CLIF);
 160
 161        err = mc44s803_writereg(priv, val);
 162        if (err)
 163                goto exit;
 164
 165        val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) |
 166              MC44S803_REG_SM(1, MC44S803_G1) |
 167              MC44S803_REG_SM(1, MC44S803_G3) |
 168              MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) |
 169              MC44S803_REG_SM(1, MC44S803_G6) |
 170              MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) |
 171              MC44S803_REG_SM(0x3, MC44S803_LP);
 172
 173        err = mc44s803_writereg(priv, val);
 174        if (err)
 175                goto exit;
 176
 177/* Setup Digtune */
 178
 179        val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
 180              MC44S803_REG_SM(3, MC44S803_XOD);
 181
 182        err = mc44s803_writereg(priv, val);
 183        if (err)
 184                goto exit;
 185
 186/* Setup AGC */
 187
 188        val = MC44S803_REG_SM(MC44S803_REG_LNAAGC, MC44S803_ADDR) |
 189              MC44S803_REG_SM(1, MC44S803_AT1) |
 190              MC44S803_REG_SM(1, MC44S803_AT2) |
 191              MC44S803_REG_SM(1, MC44S803_AGC_AN_DIG) |
 192              MC44S803_REG_SM(1, MC44S803_AGC_READ_EN) |
 193              MC44S803_REG_SM(1, MC44S803_LNA0);
 194
 195        err = mc44s803_writereg(priv, val);
 196        if (err)
 197                goto exit;
 198
 199        if (fe->ops.i2c_gate_ctrl)
 200                fe->ops.i2c_gate_ctrl(fe, 0);
 201        return 0;
 202
 203exit:
 204        if (fe->ops.i2c_gate_ctrl)
 205                fe->ops.i2c_gate_ctrl(fe, 0);
 206
 207        mc_printk(KERN_WARNING, "I/O Error\n");
 208        return err;
 209}
 210
 211static int mc44s803_set_params(struct dvb_frontend *fe)
 212{
 213        struct mc44s803_priv *priv = fe->tuner_priv;
 214        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 215        u32 r1, r2, n1, n2, lo1, lo2, freq, val;
 216        int err;
 217
 218        priv->frequency = c->frequency;
 219
 220        r1 = MC44S803_OSC / 1000000;
 221        r2 = MC44S803_OSC /  100000;
 222
 223        n1 = (c->frequency + MC44S803_IF1 + 500000) / 1000000;
 224        freq = MC44S803_OSC / r1 * n1;
 225        lo1 = ((60 * n1) + (r1 / 2)) / r1;
 226        freq = freq - c->frequency;
 227
 228        n2 = (freq - MC44S803_IF2 + 50000) / 100000;
 229        lo2 = ((60 * n2) + (r2 / 2)) / r2;
 230
 231        if (fe->ops.i2c_gate_ctrl)
 232                fe->ops.i2c_gate_ctrl(fe, 1);
 233
 234        val = MC44S803_REG_SM(MC44S803_REG_REFDIV, MC44S803_ADDR) |
 235              MC44S803_REG_SM(r1-1, MC44S803_R1) |
 236              MC44S803_REG_SM(r2-1, MC44S803_R2) |
 237              MC44S803_REG_SM(1, MC44S803_REFBUF_EN);
 238
 239        err = mc44s803_writereg(priv, val);
 240        if (err)
 241                goto exit;
 242
 243        val = MC44S803_REG_SM(MC44S803_REG_LO1, MC44S803_ADDR) |
 244              MC44S803_REG_SM(n1-2, MC44S803_LO1);
 245
 246        err = mc44s803_writereg(priv, val);
 247        if (err)
 248                goto exit;
 249
 250        val = MC44S803_REG_SM(MC44S803_REG_LO2, MC44S803_ADDR) |
 251              MC44S803_REG_SM(n2-2, MC44S803_LO2);
 252
 253        err = mc44s803_writereg(priv, val);
 254        if (err)
 255                goto exit;
 256
 257        val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
 258              MC44S803_REG_SM(1, MC44S803_DA) |
 259              MC44S803_REG_SM(lo1, MC44S803_LO_REF) |
 260              MC44S803_REG_SM(1, MC44S803_AT);
 261
 262        err = mc44s803_writereg(priv, val);
 263        if (err)
 264                goto exit;
 265
 266        val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
 267              MC44S803_REG_SM(2, MC44S803_DA) |
 268              MC44S803_REG_SM(lo2, MC44S803_LO_REF) |
 269              MC44S803_REG_SM(1, MC44S803_AT);
 270
 271        err = mc44s803_writereg(priv, val);
 272        if (err)
 273                goto exit;
 274
 275        if (fe->ops.i2c_gate_ctrl)
 276                fe->ops.i2c_gate_ctrl(fe, 0);
 277
 278        return 0;
 279
 280exit:
 281        if (fe->ops.i2c_gate_ctrl)
 282                fe->ops.i2c_gate_ctrl(fe, 0);
 283
 284        mc_printk(KERN_WARNING, "I/O Error\n");
 285        return err;
 286}
 287
 288static int mc44s803_get_frequency(struct dvb_frontend *fe, u32 *frequency)
 289{
 290        struct mc44s803_priv *priv = fe->tuner_priv;
 291        *frequency = priv->frequency;
 292        return 0;
 293}
 294
 295static int mc44s803_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 296{
 297        *frequency = MC44S803_IF2; /* 36.125 MHz */
 298        return 0;
 299}
 300
 301static const struct dvb_tuner_ops mc44s803_tuner_ops = {
 302        .info = {
 303                .name              = "Freescale MC44S803",
 304                .frequency_min_hz  =   48 * MHz,
 305                .frequency_max_hz  = 1000 * MHz,
 306                .frequency_step_hz =  100 * kHz,
 307        },
 308
 309        .release       = mc44s803_release,
 310        .init          = mc44s803_init,
 311        .set_params    = mc44s803_set_params,
 312        .get_frequency = mc44s803_get_frequency,
 313        .get_if_frequency = mc44s803_get_if_frequency,
 314};
 315
 316/* This functions tries to identify a MC44S803 tuner by reading the ID
 317   register. This is hasty. */
 318struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe,
 319         struct i2c_adapter *i2c, struct mc44s803_config *cfg)
 320{
 321        struct mc44s803_priv *priv;
 322        u32 reg;
 323        u8 id;
 324        int ret;
 325
 326        reg = 0;
 327
 328        priv = kzalloc(sizeof(struct mc44s803_priv), GFP_KERNEL);
 329        if (priv == NULL)
 330                return NULL;
 331
 332        priv->cfg = cfg;
 333        priv->i2c = i2c;
 334        priv->fe  = fe;
 335
 336        if (fe->ops.i2c_gate_ctrl)
 337                fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
 338
 339        ret = mc44s803_readreg(priv, MC44S803_REG_ID, &reg);
 340        if (ret)
 341                goto error;
 342
 343        id = MC44S803_REG_MS(reg, MC44S803_ID);
 344
 345        if (id != 0x14) {
 346                mc_printk(KERN_ERR, "unsupported ID (%x should be 0x14)\n",
 347                          id);
 348                goto error;
 349        }
 350
 351        mc_printk(KERN_INFO, "successfully identified (ID = %x)\n", id);
 352        memcpy(&fe->ops.tuner_ops, &mc44s803_tuner_ops,
 353               sizeof(struct dvb_tuner_ops));
 354
 355        fe->tuner_priv = priv;
 356
 357        if (fe->ops.i2c_gate_ctrl)
 358                fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
 359
 360        return fe;
 361
 362error:
 363        if (fe->ops.i2c_gate_ctrl)
 364                fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
 365
 366        kfree(priv);
 367        return NULL;
 368}
 369EXPORT_SYMBOL(mc44s803_attach);
 370
 371MODULE_AUTHOR("Jochen Friedrich");
 372MODULE_DESCRIPTION("Freescale MC44S803 silicon tuner driver");
 373MODULE_LICENSE("GPL");
 374