19a0bf528SMauro Carvalho Chehab /* 29a0bf528SMauro Carvalho Chehab Driver for Philips tda8262/tda8263 DVBS Silicon tuners 39a0bf528SMauro Carvalho Chehab 49a0bf528SMauro Carvalho Chehab (c) 2006 Andrew de Quincey 59a0bf528SMauro Carvalho Chehab 69a0bf528SMauro Carvalho Chehab This program is free software; you can redistribute it and/or modify 79a0bf528SMauro Carvalho Chehab it under the terms of the GNU General Public License as published by 89a0bf528SMauro Carvalho Chehab the Free Software Foundation; either version 2 of the License, or 99a0bf528SMauro Carvalho Chehab (at your option) any later version. 109a0bf528SMauro Carvalho Chehab 119a0bf528SMauro Carvalho Chehab This program is distributed in the hope that it will be useful, 129a0bf528SMauro Carvalho Chehab but WITHOUT ANY WARRANTY; without even the implied warranty of 139a0bf528SMauro Carvalho Chehab MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 149a0bf528SMauro Carvalho Chehab 159a0bf528SMauro Carvalho Chehab GNU General Public License for more details. 169a0bf528SMauro Carvalho Chehab 179a0bf528SMauro Carvalho Chehab You should have received a copy of the GNU General Public License 189a0bf528SMauro Carvalho Chehab along with this program; if not, write to the Free Software 199a0bf528SMauro Carvalho Chehab Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 209a0bf528SMauro Carvalho Chehab 219a0bf528SMauro Carvalho Chehab */ 229a0bf528SMauro Carvalho Chehab 239a0bf528SMauro Carvalho Chehab #include <linux/slab.h> 249a0bf528SMauro Carvalho Chehab #include <linux/module.h> 259a0bf528SMauro Carvalho Chehab #include <linux/dvb/frontend.h> 269a0bf528SMauro Carvalho Chehab #include <asm/types.h> 279a0bf528SMauro Carvalho Chehab 289a0bf528SMauro Carvalho Chehab #include "tda826x.h" 299a0bf528SMauro Carvalho Chehab 309a0bf528SMauro Carvalho Chehab static int debug; 319a0bf528SMauro Carvalho Chehab #define dprintk(args...) \ 329a0bf528SMauro Carvalho Chehab do { \ 339a0bf528SMauro Carvalho Chehab if (debug) printk(KERN_DEBUG "tda826x: " args); \ 349a0bf528SMauro Carvalho Chehab } while (0) 359a0bf528SMauro Carvalho Chehab 369a0bf528SMauro Carvalho Chehab struct tda826x_priv { 379a0bf528SMauro Carvalho Chehab /* i2c details */ 389a0bf528SMauro Carvalho Chehab int i2c_address; 399a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c; 409a0bf528SMauro Carvalho Chehab u8 has_loopthrough:1; 419a0bf528SMauro Carvalho Chehab u32 frequency; 429a0bf528SMauro Carvalho Chehab }; 439a0bf528SMauro Carvalho Chehab 449a0bf528SMauro Carvalho Chehab static int tda826x_release(struct dvb_frontend *fe) 459a0bf528SMauro Carvalho Chehab { 469a0bf528SMauro Carvalho Chehab kfree(fe->tuner_priv); 479a0bf528SMauro Carvalho Chehab fe->tuner_priv = NULL; 489a0bf528SMauro Carvalho Chehab return 0; 499a0bf528SMauro Carvalho Chehab } 509a0bf528SMauro Carvalho Chehab 519a0bf528SMauro Carvalho Chehab static int tda826x_sleep(struct dvb_frontend *fe) 529a0bf528SMauro Carvalho Chehab { 539a0bf528SMauro Carvalho Chehab struct tda826x_priv *priv = fe->tuner_priv; 549a0bf528SMauro Carvalho Chehab int ret; 559a0bf528SMauro Carvalho Chehab u8 buf [] = { 0x00, 0x8d }; 569a0bf528SMauro Carvalho Chehab struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 2 }; 579a0bf528SMauro Carvalho Chehab 589a0bf528SMauro Carvalho Chehab dprintk("%s:\n", __func__); 599a0bf528SMauro Carvalho Chehab 609a0bf528SMauro Carvalho Chehab if (!priv->has_loopthrough) 619a0bf528SMauro Carvalho Chehab buf[1] = 0xad; 629a0bf528SMauro Carvalho Chehab 639a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 649a0bf528SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); 659a0bf528SMauro Carvalho Chehab if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { 669a0bf528SMauro Carvalho Chehab dprintk("%s: i2c error\n", __func__); 679a0bf528SMauro Carvalho Chehab } 689a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 699a0bf528SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); 709a0bf528SMauro Carvalho Chehab 719a0bf528SMauro Carvalho Chehab return (ret == 1) ? 0 : ret; 729a0bf528SMauro Carvalho Chehab } 739a0bf528SMauro Carvalho Chehab 749a0bf528SMauro Carvalho Chehab static int tda826x_set_params(struct dvb_frontend *fe) 759a0bf528SMauro Carvalho Chehab { 769a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *p = &fe->dtv_property_cache; 779a0bf528SMauro Carvalho Chehab struct tda826x_priv *priv = fe->tuner_priv; 789a0bf528SMauro Carvalho Chehab int ret; 799a0bf528SMauro Carvalho Chehab u32 div; 809a0bf528SMauro Carvalho Chehab u32 ksyms; 819a0bf528SMauro Carvalho Chehab u32 bandwidth; 829a0bf528SMauro Carvalho Chehab u8 buf [11]; 839a0bf528SMauro Carvalho Chehab struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 11 }; 849a0bf528SMauro Carvalho Chehab 859a0bf528SMauro Carvalho Chehab dprintk("%s:\n", __func__); 869a0bf528SMauro Carvalho Chehab 879a0bf528SMauro Carvalho Chehab div = (p->frequency + (1000-1)) / 1000; 889a0bf528SMauro Carvalho Chehab 899a0bf528SMauro Carvalho Chehab /* BW = ((1 + RO) * SR/2 + 5) * 1.3 [SR in MSPS, BW in MHz] */ 909a0bf528SMauro Carvalho Chehab /* with R0 = 0.35 and some transformations: */ 919a0bf528SMauro Carvalho Chehab ksyms = p->symbol_rate / 1000; 929a0bf528SMauro Carvalho Chehab bandwidth = (878 * ksyms + 6500000) / 1000000 + 1; 939a0bf528SMauro Carvalho Chehab if (bandwidth < 5) 949a0bf528SMauro Carvalho Chehab bandwidth = 5; 959a0bf528SMauro Carvalho Chehab else if (bandwidth > 36) 969a0bf528SMauro Carvalho Chehab bandwidth = 36; 979a0bf528SMauro Carvalho Chehab 989a0bf528SMauro Carvalho Chehab buf[0] = 0x00; // subaddress 999a0bf528SMauro Carvalho Chehab buf[1] = 0x09; // powerdown RSSI + the magic value 1 1009a0bf528SMauro Carvalho Chehab if (!priv->has_loopthrough) 1019a0bf528SMauro Carvalho Chehab buf[1] |= 0x20; // power down loopthrough if not needed 1029a0bf528SMauro Carvalho Chehab buf[2] = (1<<5) | 0x0b; // 1Mhz + 0.45 VCO 1039a0bf528SMauro Carvalho Chehab buf[3] = div >> 7; 1049a0bf528SMauro Carvalho Chehab buf[4] = div << 1; 1059a0bf528SMauro Carvalho Chehab buf[5] = ((bandwidth - 5) << 3) | 7; /* baseband cut-off */ 1069a0bf528SMauro Carvalho Chehab buf[6] = 0xfe; // baseband gain 9 db + no RF attenuation 1079a0bf528SMauro Carvalho Chehab buf[7] = 0x83; // charge pumps at high, tests off 1089a0bf528SMauro Carvalho Chehab buf[8] = 0x80; // recommended value 4 for AMPVCO + disable ports. 1099a0bf528SMauro Carvalho Chehab buf[9] = 0x1a; // normal caltime + recommended values for SELTH + SELVTL 1109a0bf528SMauro Carvalho Chehab buf[10] = 0xd4; // recommended value 13 for BBIAS + unknown bit set on 1119a0bf528SMauro Carvalho Chehab 1129a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 1139a0bf528SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); 1149a0bf528SMauro Carvalho Chehab if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { 1159a0bf528SMauro Carvalho Chehab dprintk("%s: i2c error\n", __func__); 1169a0bf528SMauro Carvalho Chehab } 1179a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 1189a0bf528SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); 1199a0bf528SMauro Carvalho Chehab 1209a0bf528SMauro Carvalho Chehab priv->frequency = div * 1000; 1219a0bf528SMauro Carvalho Chehab 1229a0bf528SMauro Carvalho Chehab return (ret == 1) ? 0 : ret; 1239a0bf528SMauro Carvalho Chehab } 1249a0bf528SMauro Carvalho Chehab 1259a0bf528SMauro Carvalho Chehab static int tda826x_get_frequency(struct dvb_frontend *fe, u32 *frequency) 1269a0bf528SMauro Carvalho Chehab { 1279a0bf528SMauro Carvalho Chehab struct tda826x_priv *priv = fe->tuner_priv; 1289a0bf528SMauro Carvalho Chehab *frequency = priv->frequency; 1299a0bf528SMauro Carvalho Chehab return 0; 1309a0bf528SMauro Carvalho Chehab } 1319a0bf528SMauro Carvalho Chehab 132*14c4bf3cSJulia Lawall static const struct dvb_tuner_ops tda826x_tuner_ops = { 1339a0bf528SMauro Carvalho Chehab .info = { 1349a0bf528SMauro Carvalho Chehab .name = "Philips TDA826X", 1359a0bf528SMauro Carvalho Chehab .frequency_min = 950000, 1369a0bf528SMauro Carvalho Chehab .frequency_max = 2175000 1379a0bf528SMauro Carvalho Chehab }, 1389a0bf528SMauro Carvalho Chehab .release = tda826x_release, 1399a0bf528SMauro Carvalho Chehab .sleep = tda826x_sleep, 1409a0bf528SMauro Carvalho Chehab .set_params = tda826x_set_params, 1419a0bf528SMauro Carvalho Chehab .get_frequency = tda826x_get_frequency, 1429a0bf528SMauro Carvalho Chehab }; 1439a0bf528SMauro Carvalho Chehab 1449a0bf528SMauro Carvalho Chehab struct dvb_frontend *tda826x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, int has_loopthrough) 1459a0bf528SMauro Carvalho Chehab { 1469a0bf528SMauro Carvalho Chehab struct tda826x_priv *priv = NULL; 1479a0bf528SMauro Carvalho Chehab u8 b1 [] = { 0, 0 }; 1489a0bf528SMauro Carvalho Chehab struct i2c_msg msg[2] = { 1499a0bf528SMauro Carvalho Chehab { .addr = addr, .flags = 0, .buf = NULL, .len = 0 }, 1509a0bf528SMauro Carvalho Chehab { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 } 1519a0bf528SMauro Carvalho Chehab }; 1529a0bf528SMauro Carvalho Chehab int ret; 1539a0bf528SMauro Carvalho Chehab 1549a0bf528SMauro Carvalho Chehab dprintk("%s:\n", __func__); 1559a0bf528SMauro Carvalho Chehab 1569a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 1579a0bf528SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); 1589a0bf528SMauro Carvalho Chehab ret = i2c_transfer (i2c, msg, 2); 1599a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 1609a0bf528SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); 1619a0bf528SMauro Carvalho Chehab 1629a0bf528SMauro Carvalho Chehab if (ret != 2) 1639a0bf528SMauro Carvalho Chehab return NULL; 1649a0bf528SMauro Carvalho Chehab if (!(b1[1] & 0x80)) 1659a0bf528SMauro Carvalho Chehab return NULL; 1669a0bf528SMauro Carvalho Chehab 1679a0bf528SMauro Carvalho Chehab priv = kzalloc(sizeof(struct tda826x_priv), GFP_KERNEL); 1689a0bf528SMauro Carvalho Chehab if (priv == NULL) 1699a0bf528SMauro Carvalho Chehab return NULL; 1709a0bf528SMauro Carvalho Chehab 1719a0bf528SMauro Carvalho Chehab priv->i2c_address = addr; 1729a0bf528SMauro Carvalho Chehab priv->i2c = i2c; 1739a0bf528SMauro Carvalho Chehab priv->has_loopthrough = has_loopthrough; 1749a0bf528SMauro Carvalho Chehab 1759a0bf528SMauro Carvalho Chehab memcpy(&fe->ops.tuner_ops, &tda826x_tuner_ops, sizeof(struct dvb_tuner_ops)); 1769a0bf528SMauro Carvalho Chehab 1779a0bf528SMauro Carvalho Chehab fe->tuner_priv = priv; 1789a0bf528SMauro Carvalho Chehab 1799a0bf528SMauro Carvalho Chehab return fe; 1809a0bf528SMauro Carvalho Chehab } 1819a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(tda826x_attach); 1829a0bf528SMauro Carvalho Chehab 1839a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644); 1849a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); 1859a0bf528SMauro Carvalho Chehab 1869a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("DVB TDA826x driver"); 1879a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Andrew de Quincey"); 1889a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 189