1*ccae7af2SMauro Carvalho Chehab /* 2*ccae7af2SMauro Carvalho Chehab * Infineon TUA 9001 silicon tuner driver 3*ccae7af2SMauro Carvalho Chehab * 4*ccae7af2SMauro Carvalho Chehab * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> 5*ccae7af2SMauro Carvalho Chehab * 6*ccae7af2SMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 7*ccae7af2SMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 8*ccae7af2SMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 9*ccae7af2SMauro Carvalho Chehab * (at your option) any later version. 10*ccae7af2SMauro Carvalho Chehab * 11*ccae7af2SMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 12*ccae7af2SMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 13*ccae7af2SMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14*ccae7af2SMauro Carvalho Chehab * GNU General Public License for more details. 15*ccae7af2SMauro Carvalho Chehab * 16*ccae7af2SMauro Carvalho Chehab * You should have received a copy of the GNU General Public License along 17*ccae7af2SMauro Carvalho Chehab * with this program; if not, write to the Free Software Foundation, Inc., 18*ccae7af2SMauro Carvalho Chehab * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19*ccae7af2SMauro Carvalho Chehab */ 20*ccae7af2SMauro Carvalho Chehab 21*ccae7af2SMauro Carvalho Chehab #include "tua9001.h" 22*ccae7af2SMauro Carvalho Chehab #include "tua9001_priv.h" 23*ccae7af2SMauro Carvalho Chehab 24*ccae7af2SMauro Carvalho Chehab /* write register */ 25*ccae7af2SMauro Carvalho Chehab static int tua9001_wr_reg(struct tua9001_priv *priv, u8 reg, u16 val) 26*ccae7af2SMauro Carvalho Chehab { 27*ccae7af2SMauro Carvalho Chehab int ret; 28*ccae7af2SMauro Carvalho Chehab u8 buf[3] = { reg, (val >> 8) & 0xff, (val >> 0) & 0xff }; 29*ccae7af2SMauro Carvalho Chehab struct i2c_msg msg[1] = { 30*ccae7af2SMauro Carvalho Chehab { 31*ccae7af2SMauro Carvalho Chehab .addr = priv->cfg->i2c_addr, 32*ccae7af2SMauro Carvalho Chehab .flags = 0, 33*ccae7af2SMauro Carvalho Chehab .len = sizeof(buf), 34*ccae7af2SMauro Carvalho Chehab .buf = buf, 35*ccae7af2SMauro Carvalho Chehab } 36*ccae7af2SMauro Carvalho Chehab }; 37*ccae7af2SMauro Carvalho Chehab 38*ccae7af2SMauro Carvalho Chehab ret = i2c_transfer(priv->i2c, msg, 1); 39*ccae7af2SMauro Carvalho Chehab if (ret == 1) { 40*ccae7af2SMauro Carvalho Chehab ret = 0; 41*ccae7af2SMauro Carvalho Chehab } else { 42*ccae7af2SMauro Carvalho Chehab printk(KERN_WARNING "%s: I2C wr failed=%d reg=%02x\n", 43*ccae7af2SMauro Carvalho Chehab __func__, ret, reg); 44*ccae7af2SMauro Carvalho Chehab ret = -EREMOTEIO; 45*ccae7af2SMauro Carvalho Chehab } 46*ccae7af2SMauro Carvalho Chehab 47*ccae7af2SMauro Carvalho Chehab return ret; 48*ccae7af2SMauro Carvalho Chehab } 49*ccae7af2SMauro Carvalho Chehab 50*ccae7af2SMauro Carvalho Chehab static int tua9001_release(struct dvb_frontend *fe) 51*ccae7af2SMauro Carvalho Chehab { 52*ccae7af2SMauro Carvalho Chehab kfree(fe->tuner_priv); 53*ccae7af2SMauro Carvalho Chehab fe->tuner_priv = NULL; 54*ccae7af2SMauro Carvalho Chehab 55*ccae7af2SMauro Carvalho Chehab return 0; 56*ccae7af2SMauro Carvalho Chehab } 57*ccae7af2SMauro Carvalho Chehab 58*ccae7af2SMauro Carvalho Chehab static int tua9001_init(struct dvb_frontend *fe) 59*ccae7af2SMauro Carvalho Chehab { 60*ccae7af2SMauro Carvalho Chehab struct tua9001_priv *priv = fe->tuner_priv; 61*ccae7af2SMauro Carvalho Chehab int ret = 0; 62*ccae7af2SMauro Carvalho Chehab u8 i; 63*ccae7af2SMauro Carvalho Chehab struct reg_val data[] = { 64*ccae7af2SMauro Carvalho Chehab { 0x1e, 0x6512 }, 65*ccae7af2SMauro Carvalho Chehab { 0x25, 0xb888 }, 66*ccae7af2SMauro Carvalho Chehab { 0x39, 0x5460 }, 67*ccae7af2SMauro Carvalho Chehab { 0x3b, 0x00c0 }, 68*ccae7af2SMauro Carvalho Chehab { 0x3a, 0xf000 }, 69*ccae7af2SMauro Carvalho Chehab { 0x08, 0x0000 }, 70*ccae7af2SMauro Carvalho Chehab { 0x32, 0x0030 }, 71*ccae7af2SMauro Carvalho Chehab { 0x41, 0x703a }, 72*ccae7af2SMauro Carvalho Chehab { 0x40, 0x1c78 }, 73*ccae7af2SMauro Carvalho Chehab { 0x2c, 0x1c00 }, 74*ccae7af2SMauro Carvalho Chehab { 0x36, 0xc013 }, 75*ccae7af2SMauro Carvalho Chehab { 0x37, 0x6f18 }, 76*ccae7af2SMauro Carvalho Chehab { 0x27, 0x0008 }, 77*ccae7af2SMauro Carvalho Chehab { 0x2a, 0x0001 }, 78*ccae7af2SMauro Carvalho Chehab { 0x34, 0x0a40 }, 79*ccae7af2SMauro Carvalho Chehab }; 80*ccae7af2SMauro Carvalho Chehab 81*ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 82*ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */ 83*ccae7af2SMauro Carvalho Chehab 84*ccae7af2SMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(data); i++) { 85*ccae7af2SMauro Carvalho Chehab ret = tua9001_wr_reg(priv, data[i].reg, data[i].val); 86*ccae7af2SMauro Carvalho Chehab if (ret) 87*ccae7af2SMauro Carvalho Chehab break; 88*ccae7af2SMauro Carvalho Chehab } 89*ccae7af2SMauro Carvalho Chehab 90*ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 91*ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */ 92*ccae7af2SMauro Carvalho Chehab 93*ccae7af2SMauro Carvalho Chehab if (ret < 0) 94*ccae7af2SMauro Carvalho Chehab pr_debug("%s: failed=%d\n", __func__, ret); 95*ccae7af2SMauro Carvalho Chehab 96*ccae7af2SMauro Carvalho Chehab return ret; 97*ccae7af2SMauro Carvalho Chehab } 98*ccae7af2SMauro Carvalho Chehab 99*ccae7af2SMauro Carvalho Chehab static int tua9001_set_params(struct dvb_frontend *fe) 100*ccae7af2SMauro Carvalho Chehab { 101*ccae7af2SMauro Carvalho Chehab struct tua9001_priv *priv = fe->tuner_priv; 102*ccae7af2SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 103*ccae7af2SMauro Carvalho Chehab int ret, i; 104*ccae7af2SMauro Carvalho Chehab u16 val; 105*ccae7af2SMauro Carvalho Chehab u32 frequency; 106*ccae7af2SMauro Carvalho Chehab struct reg_val data[2]; 107*ccae7af2SMauro Carvalho Chehab 108*ccae7af2SMauro Carvalho Chehab pr_debug("%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", 109*ccae7af2SMauro Carvalho Chehab __func__, c->delivery_system, c->frequency, 110*ccae7af2SMauro Carvalho Chehab c->bandwidth_hz); 111*ccae7af2SMauro Carvalho Chehab 112*ccae7af2SMauro Carvalho Chehab switch (c->delivery_system) { 113*ccae7af2SMauro Carvalho Chehab case SYS_DVBT: 114*ccae7af2SMauro Carvalho Chehab switch (c->bandwidth_hz) { 115*ccae7af2SMauro Carvalho Chehab case 8000000: 116*ccae7af2SMauro Carvalho Chehab val = 0x0000; 117*ccae7af2SMauro Carvalho Chehab break; 118*ccae7af2SMauro Carvalho Chehab case 7000000: 119*ccae7af2SMauro Carvalho Chehab val = 0x1000; 120*ccae7af2SMauro Carvalho Chehab break; 121*ccae7af2SMauro Carvalho Chehab case 6000000: 122*ccae7af2SMauro Carvalho Chehab val = 0x2000; 123*ccae7af2SMauro Carvalho Chehab break; 124*ccae7af2SMauro Carvalho Chehab case 5000000: 125*ccae7af2SMauro Carvalho Chehab val = 0x3000; 126*ccae7af2SMauro Carvalho Chehab break; 127*ccae7af2SMauro Carvalho Chehab default: 128*ccae7af2SMauro Carvalho Chehab ret = -EINVAL; 129*ccae7af2SMauro Carvalho Chehab goto err; 130*ccae7af2SMauro Carvalho Chehab } 131*ccae7af2SMauro Carvalho Chehab break; 132*ccae7af2SMauro Carvalho Chehab default: 133*ccae7af2SMauro Carvalho Chehab ret = -EINVAL; 134*ccae7af2SMauro Carvalho Chehab goto err; 135*ccae7af2SMauro Carvalho Chehab } 136*ccae7af2SMauro Carvalho Chehab 137*ccae7af2SMauro Carvalho Chehab data[0].reg = 0x04; 138*ccae7af2SMauro Carvalho Chehab data[0].val = val; 139*ccae7af2SMauro Carvalho Chehab 140*ccae7af2SMauro Carvalho Chehab frequency = (c->frequency - 150000000); 141*ccae7af2SMauro Carvalho Chehab frequency /= 100; 142*ccae7af2SMauro Carvalho Chehab frequency *= 48; 143*ccae7af2SMauro Carvalho Chehab frequency /= 10000; 144*ccae7af2SMauro Carvalho Chehab 145*ccae7af2SMauro Carvalho Chehab data[1].reg = 0x1f; 146*ccae7af2SMauro Carvalho Chehab data[1].val = frequency; 147*ccae7af2SMauro Carvalho Chehab 148*ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 149*ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */ 150*ccae7af2SMauro Carvalho Chehab 151*ccae7af2SMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(data); i++) { 152*ccae7af2SMauro Carvalho Chehab ret = tua9001_wr_reg(priv, data[i].reg, data[i].val); 153*ccae7af2SMauro Carvalho Chehab if (ret < 0) 154*ccae7af2SMauro Carvalho Chehab break; 155*ccae7af2SMauro Carvalho Chehab } 156*ccae7af2SMauro Carvalho Chehab 157*ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 158*ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */ 159*ccae7af2SMauro Carvalho Chehab 160*ccae7af2SMauro Carvalho Chehab err: 161*ccae7af2SMauro Carvalho Chehab if (ret < 0) 162*ccae7af2SMauro Carvalho Chehab pr_debug("%s: failed=%d\n", __func__, ret); 163*ccae7af2SMauro Carvalho Chehab 164*ccae7af2SMauro Carvalho Chehab return ret; 165*ccae7af2SMauro Carvalho Chehab } 166*ccae7af2SMauro Carvalho Chehab 167*ccae7af2SMauro Carvalho Chehab static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 168*ccae7af2SMauro Carvalho Chehab { 169*ccae7af2SMauro Carvalho Chehab *frequency = 0; /* Zero-IF */ 170*ccae7af2SMauro Carvalho Chehab 171*ccae7af2SMauro Carvalho Chehab return 0; 172*ccae7af2SMauro Carvalho Chehab } 173*ccae7af2SMauro Carvalho Chehab 174*ccae7af2SMauro Carvalho Chehab static const struct dvb_tuner_ops tua9001_tuner_ops = { 175*ccae7af2SMauro Carvalho Chehab .info = { 176*ccae7af2SMauro Carvalho Chehab .name = "Infineon TUA 9001", 177*ccae7af2SMauro Carvalho Chehab 178*ccae7af2SMauro Carvalho Chehab .frequency_min = 170000000, 179*ccae7af2SMauro Carvalho Chehab .frequency_max = 862000000, 180*ccae7af2SMauro Carvalho Chehab .frequency_step = 0, 181*ccae7af2SMauro Carvalho Chehab }, 182*ccae7af2SMauro Carvalho Chehab 183*ccae7af2SMauro Carvalho Chehab .release = tua9001_release, 184*ccae7af2SMauro Carvalho Chehab 185*ccae7af2SMauro Carvalho Chehab .init = tua9001_init, 186*ccae7af2SMauro Carvalho Chehab .set_params = tua9001_set_params, 187*ccae7af2SMauro Carvalho Chehab 188*ccae7af2SMauro Carvalho Chehab .get_if_frequency = tua9001_get_if_frequency, 189*ccae7af2SMauro Carvalho Chehab }; 190*ccae7af2SMauro Carvalho Chehab 191*ccae7af2SMauro Carvalho Chehab struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, 192*ccae7af2SMauro Carvalho Chehab struct i2c_adapter *i2c, struct tua9001_config *cfg) 193*ccae7af2SMauro Carvalho Chehab { 194*ccae7af2SMauro Carvalho Chehab struct tua9001_priv *priv = NULL; 195*ccae7af2SMauro Carvalho Chehab 196*ccae7af2SMauro Carvalho Chehab priv = kzalloc(sizeof(struct tua9001_priv), GFP_KERNEL); 197*ccae7af2SMauro Carvalho Chehab if (priv == NULL) 198*ccae7af2SMauro Carvalho Chehab return NULL; 199*ccae7af2SMauro Carvalho Chehab 200*ccae7af2SMauro Carvalho Chehab priv->cfg = cfg; 201*ccae7af2SMauro Carvalho Chehab priv->i2c = i2c; 202*ccae7af2SMauro Carvalho Chehab 203*ccae7af2SMauro Carvalho Chehab printk(KERN_INFO "Infineon TUA 9001 successfully attached."); 204*ccae7af2SMauro Carvalho Chehab 205*ccae7af2SMauro Carvalho Chehab memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops, 206*ccae7af2SMauro Carvalho Chehab sizeof(struct dvb_tuner_ops)); 207*ccae7af2SMauro Carvalho Chehab 208*ccae7af2SMauro Carvalho Chehab fe->tuner_priv = priv; 209*ccae7af2SMauro Carvalho Chehab return fe; 210*ccae7af2SMauro Carvalho Chehab } 211*ccae7af2SMauro Carvalho Chehab EXPORT_SYMBOL(tua9001_attach); 212*ccae7af2SMauro Carvalho Chehab 213*ccae7af2SMauro Carvalho Chehab MODULE_DESCRIPTION("Infineon TUA 9001 silicon tuner driver"); 214*ccae7af2SMauro Carvalho Chehab MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 215*ccae7af2SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 216