1ccae7af2SMauro Carvalho Chehab /* 2ccae7af2SMauro Carvalho Chehab * Infineon TUA 9001 silicon tuner driver 3ccae7af2SMauro Carvalho Chehab * 4ccae7af2SMauro Carvalho Chehab * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> 5ccae7af2SMauro Carvalho Chehab * 6ccae7af2SMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 7ccae7af2SMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 8ccae7af2SMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 9ccae7af2SMauro Carvalho Chehab * (at your option) any later version. 10ccae7af2SMauro Carvalho Chehab * 11ccae7af2SMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 12ccae7af2SMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 13ccae7af2SMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14ccae7af2SMauro Carvalho Chehab * GNU General Public License for more details. 15ccae7af2SMauro Carvalho Chehab * 16ccae7af2SMauro Carvalho Chehab * You should have received a copy of the GNU General Public License along 17ccae7af2SMauro Carvalho Chehab * with this program; if not, write to the Free Software Foundation, Inc., 18ccae7af2SMauro Carvalho Chehab * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19ccae7af2SMauro Carvalho Chehab */ 20ccae7af2SMauro Carvalho Chehab 21ccae7af2SMauro Carvalho Chehab #include "tua9001.h" 22ccae7af2SMauro Carvalho Chehab #include "tua9001_priv.h" 23ccae7af2SMauro Carvalho Chehab 24ccae7af2SMauro Carvalho Chehab /* write register */ 25ccae7af2SMauro Carvalho Chehab static int tua9001_wr_reg(struct tua9001_priv *priv, u8 reg, u16 val) 26ccae7af2SMauro Carvalho Chehab { 27ccae7af2SMauro Carvalho Chehab int ret; 28ccae7af2SMauro Carvalho Chehab u8 buf[3] = { reg, (val >> 8) & 0xff, (val >> 0) & 0xff }; 29ccae7af2SMauro Carvalho Chehab struct i2c_msg msg[1] = { 30ccae7af2SMauro Carvalho Chehab { 31fc851c66SAntti Palosaari .addr = priv->i2c_addr, 32ccae7af2SMauro Carvalho Chehab .flags = 0, 33ccae7af2SMauro Carvalho Chehab .len = sizeof(buf), 34ccae7af2SMauro Carvalho Chehab .buf = buf, 35ccae7af2SMauro Carvalho Chehab } 36ccae7af2SMauro Carvalho Chehab }; 37ccae7af2SMauro Carvalho Chehab 38ccae7af2SMauro Carvalho Chehab ret = i2c_transfer(priv->i2c, msg, 1); 39ccae7af2SMauro Carvalho Chehab if (ret == 1) { 40ccae7af2SMauro Carvalho Chehab ret = 0; 41ccae7af2SMauro Carvalho Chehab } else { 42e6211c7cSAntti Palosaari dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x\n", 43e6211c7cSAntti Palosaari KBUILD_MODNAME, ret, reg); 44ccae7af2SMauro Carvalho Chehab ret = -EREMOTEIO; 45ccae7af2SMauro Carvalho Chehab } 46ccae7af2SMauro Carvalho Chehab 47ccae7af2SMauro Carvalho Chehab return ret; 48ccae7af2SMauro Carvalho Chehab } 49ccae7af2SMauro Carvalho Chehab 50ccae7af2SMauro Carvalho Chehab static int tua9001_init(struct dvb_frontend *fe) 51ccae7af2SMauro Carvalho Chehab { 52ccae7af2SMauro Carvalho Chehab struct tua9001_priv *priv = fe->tuner_priv; 53ccae7af2SMauro Carvalho Chehab int ret = 0; 54ccae7af2SMauro Carvalho Chehab u8 i; 55ccae7af2SMauro Carvalho Chehab struct reg_val data[] = { 56ccae7af2SMauro Carvalho Chehab { 0x1e, 0x6512 }, 57ccae7af2SMauro Carvalho Chehab { 0x25, 0xb888 }, 58ccae7af2SMauro Carvalho Chehab { 0x39, 0x5460 }, 59ccae7af2SMauro Carvalho Chehab { 0x3b, 0x00c0 }, 60ccae7af2SMauro Carvalho Chehab { 0x3a, 0xf000 }, 61ccae7af2SMauro Carvalho Chehab { 0x08, 0x0000 }, 62ccae7af2SMauro Carvalho Chehab { 0x32, 0x0030 }, 63ccae7af2SMauro Carvalho Chehab { 0x41, 0x703a }, 64ccae7af2SMauro Carvalho Chehab { 0x40, 0x1c78 }, 65ccae7af2SMauro Carvalho Chehab { 0x2c, 0x1c00 }, 66ccae7af2SMauro Carvalho Chehab { 0x36, 0xc013 }, 67ccae7af2SMauro Carvalho Chehab { 0x37, 0x6f18 }, 68ccae7af2SMauro Carvalho Chehab { 0x27, 0x0008 }, 69ccae7af2SMauro Carvalho Chehab { 0x2a, 0x0001 }, 70ccae7af2SMauro Carvalho Chehab { 0x34, 0x0a40 }, 71ccae7af2SMauro Carvalho Chehab }; 72ccae7af2SMauro Carvalho Chehab 73e6211c7cSAntti Palosaari dev_dbg(&priv->i2c->dev, "%s:\n", __func__); 74e6211c7cSAntti Palosaari 7589054e37SAntti Palosaari if (fe->callback) { 7689054e37SAntti Palosaari ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, 7789054e37SAntti Palosaari TUA9001_CMD_RESETN, 0); 7889054e37SAntti Palosaari if (ret < 0) 7989054e37SAntti Palosaari goto err; 8089054e37SAntti Palosaari } 8189054e37SAntti Palosaari 82ccae7af2SMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(data); i++) { 83ccae7af2SMauro Carvalho Chehab ret = tua9001_wr_reg(priv, data[i].reg, data[i].val); 8489054e37SAntti Palosaari if (ret < 0) 85*96676239SAntti Palosaari goto err; 86ccae7af2SMauro Carvalho Chehab } 8789054e37SAntti Palosaari err: 8889054e37SAntti Palosaari if (ret < 0) 89e6211c7cSAntti Palosaari dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); 9089054e37SAntti Palosaari 9189054e37SAntti Palosaari return ret; 9289054e37SAntti Palosaari } 9389054e37SAntti Palosaari 9489054e37SAntti Palosaari static int tua9001_sleep(struct dvb_frontend *fe) 9589054e37SAntti Palosaari { 9689054e37SAntti Palosaari struct tua9001_priv *priv = fe->tuner_priv; 9789054e37SAntti Palosaari int ret = 0; 9889054e37SAntti Palosaari 99e6211c7cSAntti Palosaari dev_dbg(&priv->i2c->dev, "%s:\n", __func__); 100e6211c7cSAntti Palosaari 10189054e37SAntti Palosaari if (fe->callback) 10289054e37SAntti Palosaari ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, 10389054e37SAntti Palosaari TUA9001_CMD_RESETN, 1); 104ccae7af2SMauro Carvalho Chehab 105ccae7af2SMauro Carvalho Chehab if (ret < 0) 106e6211c7cSAntti Palosaari dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); 107ccae7af2SMauro Carvalho Chehab 108ccae7af2SMauro Carvalho Chehab return ret; 109ccae7af2SMauro Carvalho Chehab } 110ccae7af2SMauro Carvalho Chehab 111ccae7af2SMauro Carvalho Chehab static int tua9001_set_params(struct dvb_frontend *fe) 112ccae7af2SMauro Carvalho Chehab { 113ccae7af2SMauro Carvalho Chehab struct tua9001_priv *priv = fe->tuner_priv; 114ccae7af2SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 11559e54059SMauro Carvalho Chehab int ret = 0, i; 116ccae7af2SMauro Carvalho Chehab u16 val; 117ccae7af2SMauro Carvalho Chehab u32 frequency; 118ccae7af2SMauro Carvalho Chehab struct reg_val data[2]; 119ccae7af2SMauro Carvalho Chehab 120e6211c7cSAntti Palosaari dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d frequency=%d " \ 121e6211c7cSAntti Palosaari "bandwidth_hz=%d\n", __func__, 122e6211c7cSAntti Palosaari c->delivery_system, c->frequency, c->bandwidth_hz); 123ccae7af2SMauro Carvalho Chehab 124ccae7af2SMauro Carvalho Chehab switch (c->delivery_system) { 125ccae7af2SMauro Carvalho Chehab case SYS_DVBT: 126ccae7af2SMauro Carvalho Chehab switch (c->bandwidth_hz) { 127ccae7af2SMauro Carvalho Chehab case 8000000: 128ccae7af2SMauro Carvalho Chehab val = 0x0000; 129ccae7af2SMauro Carvalho Chehab break; 130ccae7af2SMauro Carvalho Chehab case 7000000: 131ccae7af2SMauro Carvalho Chehab val = 0x1000; 132ccae7af2SMauro Carvalho Chehab break; 133ccae7af2SMauro Carvalho Chehab case 6000000: 134ccae7af2SMauro Carvalho Chehab val = 0x2000; 135ccae7af2SMauro Carvalho Chehab break; 136ccae7af2SMauro Carvalho Chehab case 5000000: 137ccae7af2SMauro Carvalho Chehab val = 0x3000; 138ccae7af2SMauro Carvalho Chehab break; 139ccae7af2SMauro Carvalho Chehab default: 140ccae7af2SMauro Carvalho Chehab ret = -EINVAL; 141ccae7af2SMauro Carvalho Chehab goto err; 142ccae7af2SMauro Carvalho Chehab } 143ccae7af2SMauro Carvalho Chehab break; 144ccae7af2SMauro Carvalho Chehab default: 145ccae7af2SMauro Carvalho Chehab ret = -EINVAL; 146ccae7af2SMauro Carvalho Chehab goto err; 147ccae7af2SMauro Carvalho Chehab } 148ccae7af2SMauro Carvalho Chehab 149ccae7af2SMauro Carvalho Chehab data[0].reg = 0x04; 150ccae7af2SMauro Carvalho Chehab data[0].val = val; 151ccae7af2SMauro Carvalho Chehab 152ccae7af2SMauro Carvalho Chehab frequency = (c->frequency - 150000000); 153ccae7af2SMauro Carvalho Chehab frequency /= 100; 154ccae7af2SMauro Carvalho Chehab frequency *= 48; 155ccae7af2SMauro Carvalho Chehab frequency /= 10000; 156ccae7af2SMauro Carvalho Chehab 157ccae7af2SMauro Carvalho Chehab data[1].reg = 0x1f; 158ccae7af2SMauro Carvalho Chehab data[1].val = frequency; 159ccae7af2SMauro Carvalho Chehab 16089054e37SAntti Palosaari if (fe->callback) { 16189054e37SAntti Palosaari ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, 16289054e37SAntti Palosaari TUA9001_CMD_RXEN, 0); 16389054e37SAntti Palosaari if (ret < 0) 164*96676239SAntti Palosaari goto err; 16589054e37SAntti Palosaari } 16689054e37SAntti Palosaari 167ccae7af2SMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(data); i++) { 168ccae7af2SMauro Carvalho Chehab ret = tua9001_wr_reg(priv, data[i].reg, data[i].val); 169ccae7af2SMauro Carvalho Chehab if (ret < 0) 170*96676239SAntti Palosaari goto err; 171ccae7af2SMauro Carvalho Chehab } 172ccae7af2SMauro Carvalho Chehab 17389054e37SAntti Palosaari if (fe->callback) { 17489054e37SAntti Palosaari ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, 17589054e37SAntti Palosaari TUA9001_CMD_RXEN, 1); 17689054e37SAntti Palosaari if (ret < 0) 177*96676239SAntti Palosaari goto err; 17889054e37SAntti Palosaari } 179ccae7af2SMauro Carvalho Chehab err: 180ccae7af2SMauro Carvalho Chehab if (ret < 0) 181e6211c7cSAntti Palosaari dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); 182ccae7af2SMauro Carvalho Chehab 183ccae7af2SMauro Carvalho Chehab return ret; 184ccae7af2SMauro Carvalho Chehab } 185ccae7af2SMauro Carvalho Chehab 186ccae7af2SMauro Carvalho Chehab static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 187ccae7af2SMauro Carvalho Chehab { 188e6211c7cSAntti Palosaari struct tua9001_priv *priv = fe->tuner_priv; 189e6211c7cSAntti Palosaari 190e6211c7cSAntti Palosaari dev_dbg(&priv->i2c->dev, "%s:\n", __func__); 191e6211c7cSAntti Palosaari 192ccae7af2SMauro Carvalho Chehab *frequency = 0; /* Zero-IF */ 193ccae7af2SMauro Carvalho Chehab 194ccae7af2SMauro Carvalho Chehab return 0; 195ccae7af2SMauro Carvalho Chehab } 196ccae7af2SMauro Carvalho Chehab 197ccae7af2SMauro Carvalho Chehab static const struct dvb_tuner_ops tua9001_tuner_ops = { 198ccae7af2SMauro Carvalho Chehab .info = { 199ccae7af2SMauro Carvalho Chehab .name = "Infineon TUA 9001", 200ccae7af2SMauro Carvalho Chehab 201ccae7af2SMauro Carvalho Chehab .frequency_min = 170000000, 202ccae7af2SMauro Carvalho Chehab .frequency_max = 862000000, 203ccae7af2SMauro Carvalho Chehab .frequency_step = 0, 204ccae7af2SMauro Carvalho Chehab }, 205ccae7af2SMauro Carvalho Chehab 206ccae7af2SMauro Carvalho Chehab .init = tua9001_init, 20789054e37SAntti Palosaari .sleep = tua9001_sleep, 208ccae7af2SMauro Carvalho Chehab .set_params = tua9001_set_params, 209ccae7af2SMauro Carvalho Chehab 210ccae7af2SMauro Carvalho Chehab .get_if_frequency = tua9001_get_if_frequency, 211ccae7af2SMauro Carvalho Chehab }; 212ccae7af2SMauro Carvalho Chehab 213fc851c66SAntti Palosaari static int tua9001_probe(struct i2c_client *client, 214fc851c66SAntti Palosaari const struct i2c_device_id *id) 215fc851c66SAntti Palosaari { 216fc851c66SAntti Palosaari struct tua9001_priv *dev; 217fc851c66SAntti Palosaari struct tua9001_platform_data *pdata = client->dev.platform_data; 218fc851c66SAntti Palosaari struct dvb_frontend *fe = pdata->dvb_frontend; 219fc851c66SAntti Palosaari int ret; 220fc851c66SAntti Palosaari 221fc851c66SAntti Palosaari dev = kzalloc(sizeof(*dev), GFP_KERNEL); 222fc851c66SAntti Palosaari if (!dev) { 223fc851c66SAntti Palosaari ret = -ENOMEM; 224fc851c66SAntti Palosaari goto err; 225fc851c66SAntti Palosaari } 226fc851c66SAntti Palosaari 227fc851c66SAntti Palosaari dev->client = client; 228fc851c66SAntti Palosaari dev->i2c_addr = client->addr; 229fc851c66SAntti Palosaari dev->i2c = client->adapter; 230fc851c66SAntti Palosaari dev->fe = pdata->dvb_frontend; 231fc851c66SAntti Palosaari 232fc851c66SAntti Palosaari if (fe->callback) { 233fc851c66SAntti Palosaari ret = fe->callback(client->adapter, 234fc851c66SAntti Palosaari DVB_FRONTEND_COMPONENT_TUNER, 235fc851c66SAntti Palosaari TUA9001_CMD_CEN, 1); 236fc851c66SAntti Palosaari if (ret) 237fc851c66SAntti Palosaari goto err_kfree; 238fc851c66SAntti Palosaari 239fc851c66SAntti Palosaari ret = fe->callback(client->adapter, 240fc851c66SAntti Palosaari DVB_FRONTEND_COMPONENT_TUNER, 241fc851c66SAntti Palosaari TUA9001_CMD_RXEN, 0); 242fc851c66SAntti Palosaari if (ret) 243fc851c66SAntti Palosaari goto err_kfree; 244fc851c66SAntti Palosaari 245fc851c66SAntti Palosaari ret = fe->callback(client->adapter, 246fc851c66SAntti Palosaari DVB_FRONTEND_COMPONENT_TUNER, 247fc851c66SAntti Palosaari TUA9001_CMD_RESETN, 1); 248fc851c66SAntti Palosaari if (ret) 249fc851c66SAntti Palosaari goto err_kfree; 250fc851c66SAntti Palosaari } 251fc851c66SAntti Palosaari 252fc851c66SAntti Palosaari fe->tuner_priv = dev; 253fc851c66SAntti Palosaari memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops, 254fc851c66SAntti Palosaari sizeof(struct dvb_tuner_ops)); 255fc851c66SAntti Palosaari i2c_set_clientdata(client, dev); 256fc851c66SAntti Palosaari 257fc851c66SAntti Palosaari dev_info(&client->dev, "Infineon TUA 9001 successfully attached\n"); 258fc851c66SAntti Palosaari return 0; 259fc851c66SAntti Palosaari err_kfree: 260fc851c66SAntti Palosaari kfree(dev); 261fc851c66SAntti Palosaari err: 262fc851c66SAntti Palosaari dev_dbg(&client->dev, "failed=%d\n", ret); 263fc851c66SAntti Palosaari return ret; 264fc851c66SAntti Palosaari } 265fc851c66SAntti Palosaari 266fc851c66SAntti Palosaari static int tua9001_remove(struct i2c_client *client) 267fc851c66SAntti Palosaari { 268fc851c66SAntti Palosaari struct tua9001_priv *dev = i2c_get_clientdata(client); 269fc851c66SAntti Palosaari struct dvb_frontend *fe = dev->fe; 270fc851c66SAntti Palosaari int ret; 271fc851c66SAntti Palosaari 272fc851c66SAntti Palosaari dev_dbg(&client->dev, "\n"); 273fc851c66SAntti Palosaari 274fc851c66SAntti Palosaari if (fe->callback) { 275fc851c66SAntti Palosaari ret = fe->callback(client->adapter, 276fc851c66SAntti Palosaari DVB_FRONTEND_COMPONENT_TUNER, 277fc851c66SAntti Palosaari TUA9001_CMD_CEN, 0); 278fc851c66SAntti Palosaari if (ret) 279fc851c66SAntti Palosaari goto err_kfree; 280fc851c66SAntti Palosaari } 281fc851c66SAntti Palosaari kfree(dev); 282fc851c66SAntti Palosaari return 0; 283fc851c66SAntti Palosaari err_kfree: 284fc851c66SAntti Palosaari kfree(dev); 285fc851c66SAntti Palosaari dev_dbg(&client->dev, "failed=%d\n", ret); 286fc851c66SAntti Palosaari return ret; 287fc851c66SAntti Palosaari } 288fc851c66SAntti Palosaari 289fc851c66SAntti Palosaari static const struct i2c_device_id tua9001_id_table[] = { 290fc851c66SAntti Palosaari {"tua9001", 0}, 291fc851c66SAntti Palosaari {} 292fc851c66SAntti Palosaari }; 293fc851c66SAntti Palosaari MODULE_DEVICE_TABLE(i2c, tua9001_id_table); 294fc851c66SAntti Palosaari 295fc851c66SAntti Palosaari static struct i2c_driver tua9001_driver = { 296fc851c66SAntti Palosaari .driver = { 297fc851c66SAntti Palosaari .owner = THIS_MODULE, 298fc851c66SAntti Palosaari .name = "tua9001", 299fc851c66SAntti Palosaari .suppress_bind_attrs = true, 300fc851c66SAntti Palosaari }, 301fc851c66SAntti Palosaari .probe = tua9001_probe, 302fc851c66SAntti Palosaari .remove = tua9001_remove, 303fc851c66SAntti Palosaari .id_table = tua9001_id_table, 304fc851c66SAntti Palosaari }; 305fc851c66SAntti Palosaari 306fc851c66SAntti Palosaari module_i2c_driver(tua9001_driver); 307fc851c66SAntti Palosaari 308ccae7af2SMauro Carvalho Chehab MODULE_DESCRIPTION("Infineon TUA 9001 silicon tuner driver"); 309ccae7af2SMauro Carvalho Chehab MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 310ccae7af2SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 311