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