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