linux/drivers/media/tuners/tda18212.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * NXP TDA18212HN silicon tuner driver
   4 *
   5 * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
   6 */
   7
   8#include "tda18212.h"
   9#include <linux/regmap.h>
  10
  11struct tda18212_dev {
  12        struct tda18212_config cfg;
  13        struct i2c_client *client;
  14        struct regmap *regmap;
  15
  16        u32 if_frequency;
  17};
  18
  19static int tda18212_set_params(struct dvb_frontend *fe)
  20{
  21        struct tda18212_dev *dev = fe->tuner_priv;
  22        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
  23        int ret, i;
  24        u32 if_khz;
  25        u8 buf[9];
  26        #define DVBT_6   0
  27        #define DVBT_7   1
  28        #define DVBT_8   2
  29        #define DVBT2_6  3
  30        #define DVBT2_7  4
  31        #define DVBT2_8  5
  32        #define DVBC_6   6
  33        #define DVBC_8   7
  34        #define ATSC_VSB 8
  35        #define ATSC_QAM 9
  36        static const u8 bw_params[][3] = {
  37                     /* reg:   0f    13    23 */
  38                [DVBT_6]  = { 0xb3, 0x20, 0x03 },
  39                [DVBT_7]  = { 0xb3, 0x31, 0x01 },
  40                [DVBT_8]  = { 0xb3, 0x22, 0x01 },
  41                [DVBT2_6] = { 0xbc, 0x20, 0x03 },
  42                [DVBT2_7] = { 0xbc, 0x72, 0x03 },
  43                [DVBT2_8] = { 0xbc, 0x22, 0x01 },
  44                [DVBC_6]  = { 0x92, 0x50, 0x03 },
  45                [DVBC_8]  = { 0x92, 0x53, 0x03 },
  46                [ATSC_VSB] = { 0x7d, 0x20, 0x63 },
  47                [ATSC_QAM] = { 0x7d, 0x20, 0x63 },
  48        };
  49
  50        dev_dbg(&dev->client->dev,
  51                        "delivery_system=%d frequency=%d bandwidth_hz=%d\n",
  52                        c->delivery_system, c->frequency,
  53                        c->bandwidth_hz);
  54
  55        if (fe->ops.i2c_gate_ctrl)
  56                fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
  57
  58        switch (c->delivery_system) {
  59        case SYS_ATSC:
  60                if_khz = dev->cfg.if_atsc_vsb;
  61                i = ATSC_VSB;
  62                break;
  63        case SYS_DVBC_ANNEX_B:
  64                if_khz = dev->cfg.if_atsc_qam;
  65                i = ATSC_QAM;
  66                break;
  67        case SYS_DVBT:
  68                switch (c->bandwidth_hz) {
  69                case 6000000:
  70                        if_khz = dev->cfg.if_dvbt_6;
  71                        i = DVBT_6;
  72                        break;
  73                case 7000000:
  74                        if_khz = dev->cfg.if_dvbt_7;
  75                        i = DVBT_7;
  76                        break;
  77                case 8000000:
  78                        if_khz = dev->cfg.if_dvbt_8;
  79                        i = DVBT_8;
  80                        break;
  81                default:
  82                        ret = -EINVAL;
  83                        goto error;
  84                }
  85                break;
  86        case SYS_DVBT2:
  87                switch (c->bandwidth_hz) {
  88                case 6000000:
  89                        if_khz = dev->cfg.if_dvbt2_6;
  90                        i = DVBT2_6;
  91                        break;
  92                case 7000000:
  93                        if_khz = dev->cfg.if_dvbt2_7;
  94                        i = DVBT2_7;
  95                        break;
  96                case 8000000:
  97                        if_khz = dev->cfg.if_dvbt2_8;
  98                        i = DVBT2_8;
  99                        break;
 100                default:
 101                        ret = -EINVAL;
 102                        goto error;
 103                }
 104                break;
 105        case SYS_DVBC_ANNEX_A:
 106        case SYS_DVBC_ANNEX_C:
 107                if_khz = dev->cfg.if_dvbc;
 108                i = DVBC_8;
 109                break;
 110        default:
 111                ret = -EINVAL;
 112                goto error;
 113        }
 114
 115        ret = regmap_write(dev->regmap, 0x23, bw_params[i][2]);
 116        if (ret)
 117                goto error;
 118
 119        ret = regmap_write(dev->regmap, 0x06, 0x00);
 120        if (ret)
 121                goto error;
 122
 123        ret = regmap_write(dev->regmap, 0x0f, bw_params[i][0]);
 124        if (ret)
 125                goto error;
 126
 127        buf[0] = 0x02;
 128        buf[1] = bw_params[i][1];
 129        buf[2] = 0x03; /* default value */
 130        buf[3] = DIV_ROUND_CLOSEST(if_khz, 50);
 131        buf[4] = ((c->frequency / 1000) >> 16) & 0xff;
 132        buf[5] = ((c->frequency / 1000) >>  8) & 0xff;
 133        buf[6] = ((c->frequency / 1000) >>  0) & 0xff;
 134        buf[7] = 0xc1;
 135        buf[8] = 0x01;
 136        ret = regmap_bulk_write(dev->regmap, 0x12, buf, sizeof(buf));
 137        if (ret)
 138                goto error;
 139
 140        /* actual IF rounded as it is on register */
 141        dev->if_frequency = buf[3] * 50 * 1000;
 142
 143exit:
 144        if (fe->ops.i2c_gate_ctrl)
 145                fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
 146
 147        return ret;
 148
 149error:
 150        dev_dbg(&dev->client->dev, "failed=%d\n", ret);
 151        goto exit;
 152}
 153
 154static int tda18212_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 155{
 156        struct tda18212_dev *dev = fe->tuner_priv;
 157
 158        *frequency = dev->if_frequency;
 159
 160        return 0;
 161}
 162
 163static const struct dvb_tuner_ops tda18212_tuner_ops = {
 164        .info = {
 165                .name              = "NXP TDA18212",
 166
 167                .frequency_min_hz  =  48 * MHz,
 168                .frequency_max_hz  = 864 * MHz,
 169                .frequency_step_hz =   1 * kHz,
 170        },
 171
 172        .set_params    = tda18212_set_params,
 173        .get_if_frequency = tda18212_get_if_frequency,
 174};
 175
 176static int tda18212_probe(struct i2c_client *client,
 177                const struct i2c_device_id *id)
 178{
 179        struct tda18212_config *cfg = client->dev.platform_data;
 180        struct dvb_frontend *fe = cfg->fe;
 181        struct tda18212_dev *dev;
 182        int ret;
 183        unsigned int chip_id;
 184        char *version;
 185        static const struct regmap_config regmap_config = {
 186                .reg_bits = 8,
 187                .val_bits = 8,
 188        };
 189
 190        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 191        if (dev == NULL) {
 192                ret = -ENOMEM;
 193                dev_err(&client->dev, "kzalloc() failed\n");
 194                goto err;
 195        }
 196
 197        memcpy(&dev->cfg, cfg, sizeof(struct tda18212_config));
 198        dev->client = client;
 199        dev->regmap = devm_regmap_init_i2c(client, &regmap_config);
 200        if (IS_ERR(dev->regmap)) {
 201                ret = PTR_ERR(dev->regmap);
 202                goto err;
 203        }
 204
 205        /* check if the tuner is there */
 206        if (fe->ops.i2c_gate_ctrl)
 207                fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
 208
 209        ret = regmap_read(dev->regmap, 0x00, &chip_id);
 210        dev_dbg(&dev->client->dev, "chip_id=%02x\n", chip_id);
 211
 212        if (fe->ops.i2c_gate_ctrl)
 213                fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
 214
 215        if (ret)
 216                goto err;
 217
 218        switch (chip_id) {
 219        case 0xc7:
 220                version = "M"; /* master */
 221                break;
 222        case 0x47:
 223                version = "S"; /* slave */
 224                break;
 225        default:
 226                ret = -ENODEV;
 227                goto err;
 228        }
 229
 230        dev_info(&dev->client->dev,
 231                        "NXP TDA18212HN/%s successfully identified\n", version);
 232
 233        fe->tuner_priv = dev;
 234        memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops,
 235                        sizeof(struct dvb_tuner_ops));
 236        i2c_set_clientdata(client, dev);
 237
 238        return 0;
 239err:
 240        dev_dbg(&client->dev, "failed=%d\n", ret);
 241        kfree(dev);
 242        return ret;
 243}
 244
 245static int tda18212_remove(struct i2c_client *client)
 246{
 247        struct tda18212_dev *dev = i2c_get_clientdata(client);
 248        struct dvb_frontend *fe = dev->cfg.fe;
 249
 250        dev_dbg(&client->dev, "\n");
 251
 252        memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
 253        fe->tuner_priv = NULL;
 254        kfree(dev);
 255
 256        return 0;
 257}
 258
 259static const struct i2c_device_id tda18212_id[] = {
 260        {"tda18212", 0},
 261        {}
 262};
 263MODULE_DEVICE_TABLE(i2c, tda18212_id);
 264
 265static struct i2c_driver tda18212_driver = {
 266        .driver = {
 267                .name   = "tda18212",
 268        },
 269        .probe          = tda18212_probe,
 270        .remove         = tda18212_remove,
 271        .id_table       = tda18212_id,
 272};
 273
 274module_i2c_driver(tda18212_driver);
 275
 276MODULE_DESCRIPTION("NXP TDA18212HN silicon tuner driver");
 277MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
 278MODULE_LICENSE("GPL");
 279