linux/drivers/media/dvb-frontends/tua6100.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Driver for Infineon tua6100 pll.
   4 *
   5 * (c) 2006 Andrew de Quincey
   6 *
   7 * Based on code found in budget-av.c, which has the following:
   8 * Compiled from various sources by Michael Hunold <michael@mihu.de>
   9 *
  10 * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> &
  11 *                               Andrew de Quincey <adq_dvb@lidskialf.net>
  12 *
  13 * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
  14 *
  15 * Copyright (C) 1999-2002 Ralph  Metzler
  16 *                       & Marcus Metzler for convergence integrated media GmbH
  17 */
  18
  19#include <linux/slab.h>
  20#include <linux/module.h>
  21#include <linux/dvb/frontend.h>
  22#include <asm/types.h>
  23
  24#include "tua6100.h"
  25
  26struct tua6100_priv {
  27        /* i2c details */
  28        int i2c_address;
  29        struct i2c_adapter *i2c;
  30        u32 frequency;
  31};
  32
  33static void tua6100_release(struct dvb_frontend *fe)
  34{
  35        kfree(fe->tuner_priv);
  36        fe->tuner_priv = NULL;
  37}
  38
  39static int tua6100_sleep(struct dvb_frontend *fe)
  40{
  41        struct tua6100_priv *priv = fe->tuner_priv;
  42        int ret;
  43        u8 reg0[] = { 0x00, 0x00 };
  44        struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 };
  45
  46        if (fe->ops.i2c_gate_ctrl)
  47                fe->ops.i2c_gate_ctrl(fe, 1);
  48        if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) {
  49                printk("%s: i2c error\n", __func__);
  50        }
  51        if (fe->ops.i2c_gate_ctrl)
  52                fe->ops.i2c_gate_ctrl(fe, 0);
  53
  54        return (ret == 1) ? 0 : ret;
  55}
  56
  57static int tua6100_set_params(struct dvb_frontend *fe)
  58{
  59        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
  60        struct tua6100_priv *priv = fe->tuner_priv;
  61        u32 div;
  62        u32 prediv;
  63        u8 reg0[] = { 0x00, 0x00 };
  64        u8 reg1[] = { 0x01, 0x00, 0x00, 0x00 };
  65        u8 reg2[] = { 0x02, 0x00, 0x00 };
  66        struct i2c_msg msg0 = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 };
  67        struct i2c_msg msg1 = { .addr = priv->i2c_address, .flags = 0, .buf = reg1, .len = 4 };
  68        struct i2c_msg msg2 = { .addr = priv->i2c_address, .flags = 0, .buf = reg2, .len = 3 };
  69
  70#define _R_VAL 4
  71#define _P_VAL 32
  72#define _ri 4000000
  73
  74        // setup register 0
  75        if (c->frequency < 2000000)
  76                reg0[1] = 0x03;
  77        else
  78                reg0[1] = 0x07;
  79
  80        // setup register 1
  81        if (c->frequency < 1630000)
  82                reg1[1] = 0x2c;
  83        else
  84                reg1[1] = 0x0c;
  85
  86        if (_P_VAL == 64)
  87                reg1[1] |= 0x40;
  88        if (c->frequency >= 1525000)
  89                reg1[1] |= 0x80;
  90
  91        // register 2
  92        reg2[1] = (_R_VAL >> 8) & 0x03;
  93        reg2[2] = _R_VAL;
  94        if (c->frequency < 1455000)
  95                reg2[1] |= 0x1c;
  96        else if (c->frequency < 1630000)
  97                reg2[1] |= 0x0c;
  98        else
  99                reg2[1] |= 0x1c;
 100
 101        /*
 102         * The N divisor ratio (note: c->frequency is in kHz, but we
 103         * need it in Hz)
 104         */
 105        prediv = (c->frequency * _R_VAL) / (_ri / 1000);
 106        div = prediv / _P_VAL;
 107        reg1[1] |= (div >> 9) & 0x03;
 108        reg1[2] = div >> 1;
 109        reg1[3] = (div << 7);
 110        priv->frequency = ((div * _P_VAL) * (_ri / 1000)) / _R_VAL;
 111
 112        // Finally, calculate and store the value for A
 113        reg1[3] |= (prediv - (div*_P_VAL)) & 0x7f;
 114
 115#undef _R_VAL
 116#undef _P_VAL
 117#undef _ri
 118
 119        if (fe->ops.i2c_gate_ctrl)
 120                fe->ops.i2c_gate_ctrl(fe, 1);
 121        if (i2c_transfer(priv->i2c, &msg0, 1) != 1)
 122                return -EIO;
 123
 124        if (fe->ops.i2c_gate_ctrl)
 125                fe->ops.i2c_gate_ctrl(fe, 1);
 126        if (i2c_transfer(priv->i2c, &msg2, 1) != 1)
 127                return -EIO;
 128
 129        if (fe->ops.i2c_gate_ctrl)
 130                fe->ops.i2c_gate_ctrl(fe, 1);
 131        if (i2c_transfer(priv->i2c, &msg1, 1) != 1)
 132                return -EIO;
 133
 134        if (fe->ops.i2c_gate_ctrl)
 135                fe->ops.i2c_gate_ctrl(fe, 0);
 136
 137        return 0;
 138}
 139
 140static int tua6100_get_frequency(struct dvb_frontend *fe, u32 *frequency)
 141{
 142        struct tua6100_priv *priv = fe->tuner_priv;
 143        *frequency = priv->frequency;
 144        return 0;
 145}
 146
 147static const struct dvb_tuner_ops tua6100_tuner_ops = {
 148        .info = {
 149                .name = "Infineon TUA6100",
 150                .frequency_min_hz  =  950 * MHz,
 151                .frequency_max_hz  = 2150 * MHz,
 152                .frequency_step_hz =    1 * MHz,
 153        },
 154        .release = tua6100_release,
 155        .sleep = tua6100_sleep,
 156        .set_params = tua6100_set_params,
 157        .get_frequency = tua6100_get_frequency,
 158};
 159
 160struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c)
 161{
 162        struct tua6100_priv *priv = NULL;
 163        u8 b1 [] = { 0x80 };
 164        u8 b2 [] = { 0x00 };
 165        struct i2c_msg msg [] = { { .addr = addr, .flags = 0, .buf = b1, .len = 1 },
 166                                  { .addr = addr, .flags = I2C_M_RD, .buf = b2, .len = 1 } };
 167        int ret;
 168
 169        if (fe->ops.i2c_gate_ctrl)
 170                fe->ops.i2c_gate_ctrl(fe, 1);
 171        ret = i2c_transfer (i2c, msg, 2);
 172        if (fe->ops.i2c_gate_ctrl)
 173                fe->ops.i2c_gate_ctrl(fe, 0);
 174
 175        if (ret != 2)
 176                return NULL;
 177
 178        priv = kzalloc(sizeof(struct tua6100_priv), GFP_KERNEL);
 179        if (priv == NULL)
 180                return NULL;
 181
 182        priv->i2c_address = addr;
 183        priv->i2c = i2c;
 184
 185        memcpy(&fe->ops.tuner_ops, &tua6100_tuner_ops, sizeof(struct dvb_tuner_ops));
 186        fe->tuner_priv = priv;
 187        return fe;
 188}
 189EXPORT_SYMBOL(tua6100_attach);
 190
 191MODULE_DESCRIPTION("DVB tua6100 driver");
 192MODULE_AUTHOR("Andrew de Quincey");
 193MODULE_LICENSE("GPL");
 194