1ccae7af2SMauro Carvalho Chehab /* 2ccae7af2SMauro Carvalho Chehab * Infineon TUA9001 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 17ccae7af2SMauro Carvalho Chehab #include "tua9001_priv.h" 18ccae7af2SMauro Carvalho Chehab 19ccae7af2SMauro Carvalho Chehab /* write register */ 20*465433faSAntti Palosaari static int tua9001_wr_reg(struct tua9001_dev *dev, u8 reg, u16 val) 21ccae7af2SMauro Carvalho Chehab { 22*465433faSAntti Palosaari struct i2c_client *client = dev->client; 23ccae7af2SMauro Carvalho Chehab int ret; 24ccae7af2SMauro Carvalho Chehab u8 buf[3] = { reg, (val >> 8) & 0xff, (val >> 0) & 0xff }; 25ccae7af2SMauro Carvalho Chehab struct i2c_msg msg[1] = { 26ccae7af2SMauro Carvalho Chehab { 27*465433faSAntti Palosaari .addr = client->addr, 28ccae7af2SMauro Carvalho Chehab .flags = 0, 29ccae7af2SMauro Carvalho Chehab .len = sizeof(buf), 30ccae7af2SMauro Carvalho Chehab .buf = buf, 31ccae7af2SMauro Carvalho Chehab } 32ccae7af2SMauro Carvalho Chehab }; 33ccae7af2SMauro Carvalho Chehab 34*465433faSAntti Palosaari ret = i2c_transfer(client->adapter, msg, 1); 35ccae7af2SMauro Carvalho Chehab if (ret == 1) { 36ccae7af2SMauro Carvalho Chehab ret = 0; 37ccae7af2SMauro Carvalho Chehab } else { 38*465433faSAntti Palosaari dev_warn(&client->dev, "i2c wr failed=%d reg=%02x\n", ret, reg); 39ccae7af2SMauro Carvalho Chehab ret = -EREMOTEIO; 40ccae7af2SMauro Carvalho Chehab } 41ccae7af2SMauro Carvalho Chehab 42ccae7af2SMauro Carvalho Chehab return ret; 43ccae7af2SMauro Carvalho Chehab } 44ccae7af2SMauro Carvalho Chehab 45ccae7af2SMauro Carvalho Chehab static int tua9001_init(struct dvb_frontend *fe) 46ccae7af2SMauro Carvalho Chehab { 47*465433faSAntti Palosaari struct tua9001_dev *dev = fe->tuner_priv; 48*465433faSAntti Palosaari struct i2c_client *client = dev->client; 49*465433faSAntti Palosaari int ret, i; 50*465433faSAntti Palosaari static const struct tua9001_reg_val data[] = { 51ccae7af2SMauro Carvalho Chehab {0x1e, 0x6512}, 52ccae7af2SMauro Carvalho Chehab {0x25, 0xb888}, 53ccae7af2SMauro Carvalho Chehab {0x39, 0x5460}, 54ccae7af2SMauro Carvalho Chehab {0x3b, 0x00c0}, 55ccae7af2SMauro Carvalho Chehab {0x3a, 0xf000}, 56ccae7af2SMauro Carvalho Chehab {0x08, 0x0000}, 57ccae7af2SMauro Carvalho Chehab {0x32, 0x0030}, 58ccae7af2SMauro Carvalho Chehab {0x41, 0x703a}, 59ccae7af2SMauro Carvalho Chehab {0x40, 0x1c78}, 60ccae7af2SMauro Carvalho Chehab {0x2c, 0x1c00}, 61ccae7af2SMauro Carvalho Chehab {0x36, 0xc013}, 62ccae7af2SMauro Carvalho Chehab {0x37, 0x6f18}, 63ccae7af2SMauro Carvalho Chehab {0x27, 0x0008}, 64ccae7af2SMauro Carvalho Chehab {0x2a, 0x0001}, 65ccae7af2SMauro Carvalho Chehab {0x34, 0x0a40}, 66ccae7af2SMauro Carvalho Chehab }; 67ccae7af2SMauro Carvalho Chehab 68*465433faSAntti Palosaari dev_dbg(&client->dev, "\n"); 69e6211c7cSAntti Palosaari 7089054e37SAntti Palosaari if (fe->callback) { 71*465433faSAntti Palosaari ret = fe->callback(client->adapter, 72*465433faSAntti Palosaari DVB_FRONTEND_COMPONENT_TUNER, 7389054e37SAntti Palosaari TUA9001_CMD_RESETN, 0); 74*465433faSAntti Palosaari if (ret) 7589054e37SAntti Palosaari goto err; 7689054e37SAntti Palosaari } 7789054e37SAntti Palosaari 78ccae7af2SMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(data); i++) { 79*465433faSAntti Palosaari ret = tua9001_wr_reg(dev, data[i].reg, data[i].val); 80*465433faSAntti Palosaari if (ret) 8196676239SAntti Palosaari goto err; 82ccae7af2SMauro Carvalho Chehab } 83*465433faSAntti Palosaari return 0; 8489054e37SAntti Palosaari err: 85*465433faSAntti Palosaari dev_dbg(&client->dev, "failed=%d\n", ret); 8689054e37SAntti Palosaari return ret; 8789054e37SAntti Palosaari } 8889054e37SAntti Palosaari 8989054e37SAntti Palosaari static int tua9001_sleep(struct dvb_frontend *fe) 9089054e37SAntti Palosaari { 91*465433faSAntti Palosaari struct tua9001_dev *dev = fe->tuner_priv; 92*465433faSAntti Palosaari struct i2c_client *client = dev->client; 93*465433faSAntti Palosaari int ret; 9489054e37SAntti Palosaari 95*465433faSAntti Palosaari dev_dbg(&client->dev, "\n"); 96e6211c7cSAntti Palosaari 97*465433faSAntti Palosaari if (fe->callback) { 98*465433faSAntti Palosaari ret = fe->callback(client->adapter, 99*465433faSAntti Palosaari DVB_FRONTEND_COMPONENT_TUNER, 10089054e37SAntti Palosaari TUA9001_CMD_RESETN, 1); 101*465433faSAntti Palosaari if (ret) 102*465433faSAntti Palosaari goto err; 103*465433faSAntti Palosaari } 104*465433faSAntti Palosaari return 0; 105*465433faSAntti Palosaari err: 106*465433faSAntti Palosaari dev_dbg(&client->dev, "failed=%d\n", ret); 107ccae7af2SMauro Carvalho Chehab return ret; 108ccae7af2SMauro Carvalho Chehab } 109ccae7af2SMauro Carvalho Chehab 110ccae7af2SMauro Carvalho Chehab static int tua9001_set_params(struct dvb_frontend *fe) 111ccae7af2SMauro Carvalho Chehab { 112*465433faSAntti Palosaari struct tua9001_dev *dev = fe->tuner_priv; 113*465433faSAntti Palosaari struct i2c_client *client = dev->client; 114ccae7af2SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 115*465433faSAntti Palosaari int ret, i; 116ccae7af2SMauro Carvalho Chehab u16 val; 117ccae7af2SMauro Carvalho Chehab u32 frequency; 118*465433faSAntti Palosaari struct tua9001_reg_val data[2]; 119ccae7af2SMauro Carvalho Chehab 120*465433faSAntti Palosaari dev_dbg(&client->dev, 121*465433faSAntti Palosaari "delivery_system=%u frequency=%u bandwidth_hz=%u\n", 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) { 161*465433faSAntti Palosaari ret = fe->callback(client->adapter, 162*465433faSAntti Palosaari DVB_FRONTEND_COMPONENT_TUNER, 16389054e37SAntti Palosaari TUA9001_CMD_RXEN, 0); 164*465433faSAntti Palosaari if (ret) 16596676239SAntti Palosaari goto err; 16689054e37SAntti Palosaari } 16789054e37SAntti Palosaari 168ccae7af2SMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(data); i++) { 169*465433faSAntti Palosaari ret = tua9001_wr_reg(dev, data[i].reg, data[i].val); 170*465433faSAntti Palosaari if (ret) 17196676239SAntti Palosaari goto err; 172ccae7af2SMauro Carvalho Chehab } 173ccae7af2SMauro Carvalho Chehab 17489054e37SAntti Palosaari if (fe->callback) { 175*465433faSAntti Palosaari ret = fe->callback(client->adapter, 176*465433faSAntti Palosaari DVB_FRONTEND_COMPONENT_TUNER, 17789054e37SAntti Palosaari TUA9001_CMD_RXEN, 1); 178*465433faSAntti Palosaari if (ret) 17996676239SAntti Palosaari goto err; 18089054e37SAntti Palosaari } 181*465433faSAntti Palosaari return 0; 182ccae7af2SMauro Carvalho Chehab err: 183*465433faSAntti Palosaari dev_dbg(&client->dev, "failed=%d\n", ret); 184ccae7af2SMauro Carvalho Chehab return ret; 185ccae7af2SMauro Carvalho Chehab } 186ccae7af2SMauro Carvalho Chehab 187ccae7af2SMauro Carvalho Chehab static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 188ccae7af2SMauro Carvalho Chehab { 189*465433faSAntti Palosaari struct tua9001_dev *dev = fe->tuner_priv; 190*465433faSAntti Palosaari struct i2c_client *client = dev->client; 191e6211c7cSAntti Palosaari 192*465433faSAntti Palosaari dev_dbg(&client->dev, "\n"); 193e6211c7cSAntti Palosaari 194ccae7af2SMauro Carvalho Chehab *frequency = 0; /* Zero-IF */ 195ccae7af2SMauro Carvalho Chehab return 0; 196ccae7af2SMauro Carvalho Chehab } 197ccae7af2SMauro Carvalho Chehab 198ccae7af2SMauro Carvalho Chehab static const struct dvb_tuner_ops tua9001_tuner_ops = { 199ccae7af2SMauro Carvalho Chehab .info = { 200ccae7af2SMauro Carvalho Chehab .name = "Infineon TUA9001", 201ccae7af2SMauro Carvalho Chehab .frequency_min = 170000000, 202ccae7af2SMauro Carvalho Chehab .frequency_max = 862000000, 203ccae7af2SMauro Carvalho Chehab }, 204ccae7af2SMauro Carvalho Chehab 205ccae7af2SMauro Carvalho Chehab .init = tua9001_init, 20689054e37SAntti Palosaari .sleep = tua9001_sleep, 207ccae7af2SMauro Carvalho Chehab .set_params = tua9001_set_params, 208ccae7af2SMauro Carvalho Chehab 209ccae7af2SMauro Carvalho Chehab .get_if_frequency = tua9001_get_if_frequency, 210ccae7af2SMauro Carvalho Chehab }; 211ccae7af2SMauro Carvalho Chehab 212fc851c66SAntti Palosaari static int tua9001_probe(struct i2c_client *client, 213fc851c66SAntti Palosaari const struct i2c_device_id *id) 214fc851c66SAntti Palosaari { 215*465433faSAntti Palosaari struct tua9001_dev *dev; 216fc851c66SAntti Palosaari struct tua9001_platform_data *pdata = client->dev.platform_data; 217fc851c66SAntti Palosaari struct dvb_frontend *fe = pdata->dvb_frontend; 218fc851c66SAntti Palosaari int ret; 219fc851c66SAntti Palosaari 220fc851c66SAntti Palosaari dev = kzalloc(sizeof(*dev), GFP_KERNEL); 221fc851c66SAntti Palosaari if (!dev) { 222fc851c66SAntti Palosaari ret = -ENOMEM; 223fc851c66SAntti Palosaari goto err; 224fc851c66SAntti Palosaari } 225fc851c66SAntti Palosaari 226fc851c66SAntti Palosaari dev->client = client; 227fc851c66SAntti Palosaari dev->fe = pdata->dvb_frontend; 228fc851c66SAntti Palosaari 229fc851c66SAntti Palosaari if (fe->callback) { 230fc851c66SAntti Palosaari ret = fe->callback(client->adapter, 231fc851c66SAntti Palosaari DVB_FRONTEND_COMPONENT_TUNER, 232fc851c66SAntti Palosaari TUA9001_CMD_CEN, 1); 233fc851c66SAntti Palosaari if (ret) 234fc851c66SAntti Palosaari goto err_kfree; 235fc851c66SAntti Palosaari 236fc851c66SAntti Palosaari ret = fe->callback(client->adapter, 237fc851c66SAntti Palosaari DVB_FRONTEND_COMPONENT_TUNER, 238fc851c66SAntti Palosaari TUA9001_CMD_RXEN, 0); 239fc851c66SAntti Palosaari if (ret) 240fc851c66SAntti Palosaari goto err_kfree; 241fc851c66SAntti Palosaari 242fc851c66SAntti Palosaari ret = fe->callback(client->adapter, 243fc851c66SAntti Palosaari DVB_FRONTEND_COMPONENT_TUNER, 244fc851c66SAntti Palosaari TUA9001_CMD_RESETN, 1); 245fc851c66SAntti Palosaari if (ret) 246fc851c66SAntti Palosaari goto err_kfree; 247fc851c66SAntti Palosaari } 248fc851c66SAntti Palosaari 249fc851c66SAntti Palosaari fe->tuner_priv = dev; 250fc851c66SAntti Palosaari memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops, 251fc851c66SAntti Palosaari sizeof(struct dvb_tuner_ops)); 252fc851c66SAntti Palosaari i2c_set_clientdata(client, dev); 253fc851c66SAntti Palosaari 254fc851c66SAntti Palosaari dev_info(&client->dev, "Infineon TUA9001 successfully attached\n"); 255fc851c66SAntti Palosaari return 0; 256fc851c66SAntti Palosaari err_kfree: 257fc851c66SAntti Palosaari kfree(dev); 258fc851c66SAntti Palosaari err: 259fc851c66SAntti Palosaari dev_dbg(&client->dev, "failed=%d\n", ret); 260fc851c66SAntti Palosaari return ret; 261fc851c66SAntti Palosaari } 262fc851c66SAntti Palosaari 263fc851c66SAntti Palosaari static int tua9001_remove(struct i2c_client *client) 264fc851c66SAntti Palosaari { 265*465433faSAntti Palosaari struct tua9001_dev *dev = i2c_get_clientdata(client); 266fc851c66SAntti Palosaari struct dvb_frontend *fe = dev->fe; 267fc851c66SAntti Palosaari int ret; 268fc851c66SAntti Palosaari 269fc851c66SAntti Palosaari dev_dbg(&client->dev, "\n"); 270fc851c66SAntti Palosaari 271fc851c66SAntti Palosaari if (fe->callback) { 272fc851c66SAntti Palosaari ret = fe->callback(client->adapter, 273fc851c66SAntti Palosaari DVB_FRONTEND_COMPONENT_TUNER, 274fc851c66SAntti Palosaari TUA9001_CMD_CEN, 0); 275fc851c66SAntti Palosaari if (ret) 276fc851c66SAntti Palosaari goto err_kfree; 277fc851c66SAntti Palosaari } 278fc851c66SAntti Palosaari kfree(dev); 279fc851c66SAntti Palosaari return 0; 280fc851c66SAntti Palosaari err_kfree: 281fc851c66SAntti Palosaari kfree(dev); 282fc851c66SAntti Palosaari dev_dbg(&client->dev, "failed=%d\n", ret); 283fc851c66SAntti Palosaari return ret; 284fc851c66SAntti Palosaari } 285fc851c66SAntti Palosaari 286fc851c66SAntti Palosaari static const struct i2c_device_id tua9001_id_table[] = { 287fc851c66SAntti Palosaari {"tua9001", 0}, 288fc851c66SAntti Palosaari {} 289fc851c66SAntti Palosaari }; 290fc851c66SAntti Palosaari MODULE_DEVICE_TABLE(i2c, tua9001_id_table); 291fc851c66SAntti Palosaari 292fc851c66SAntti Palosaari static struct i2c_driver tua9001_driver = { 293fc851c66SAntti Palosaari .driver = { 294fc851c66SAntti Palosaari .owner = THIS_MODULE, 295fc851c66SAntti Palosaari .name = "tua9001", 296fc851c66SAntti Palosaari .suppress_bind_attrs = true, 297fc851c66SAntti Palosaari }, 298fc851c66SAntti Palosaari .probe = tua9001_probe, 299fc851c66SAntti Palosaari .remove = tua9001_remove, 300fc851c66SAntti Palosaari .id_table = tua9001_id_table, 301fc851c66SAntti Palosaari }; 302fc851c66SAntti Palosaari 303fc851c66SAntti Palosaari module_i2c_driver(tua9001_driver); 304fc851c66SAntti Palosaari 305ccae7af2SMauro Carvalho Chehab MODULE_DESCRIPTION("Infineon TUA9001 silicon tuner driver"); 306ccae7af2SMauro Carvalho Chehab MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 307ccae7af2SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 308