| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 1 | /* | 
|  | 2 | *  Driver for Quantek QT1010 silicon tuner | 
|  | 3 | * | 
|  | 4 | *  Copyright (C) 2006 Antti Palosaari <crope@iki.fi> | 
|  | 5 | *                     Aapo Tahkola <aet@rasterburn.org> | 
|  | 6 | * | 
|  | 7 | *  This program is free software; you can redistribute it and/or modify | 
|  | 8 | *  it under the terms of the GNU General Public License as published by | 
|  | 9 | *  the Free Software Foundation; either version 2 of the License, or | 
|  | 10 | *  (at your option) any later version. | 
|  | 11 | * | 
|  | 12 | *  This program is distributed in the hope that it will be useful, | 
|  | 13 | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 14 | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 15 | *  GNU General Public License for more details. | 
|  | 16 | * | 
|  | 17 | *  You should have received a copy of the GNU General Public License | 
|  | 18 | *  along with this program; if not, write to the Free Software | 
|  | 19 | *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|  | 20 | */ | 
|  | 21 | #include "qt1010.h" | 
|  | 22 | #include "qt1010_priv.h" | 
|  | 23 |  | 
|  | 24 | static int debug; | 
|  | 25 | module_param(debug, int, 0644); | 
|  | 26 | MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); | 
|  | 27 |  | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 28 | #define dprintk(args...) \ | 
|  | 29 | do { \ | 
|  | 30 | if (debug) printk(KERN_DEBUG "QT1010: " args); \ | 
|  | 31 | } while (0) | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 32 |  | 
|  | 33 | /* read single register */ | 
|  | 34 | static int qt1010_readreg(struct qt1010_priv *priv, u8 reg, u8 *val) | 
|  | 35 | { | 
|  | 36 | struct i2c_msg msg[2] = { | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 37 | { .addr = priv->cfg->i2c_address, | 
|  | 38 | .flags = 0, .buf = ®, .len = 1 }, | 
|  | 39 | { .addr = priv->cfg->i2c_address, | 
|  | 40 | .flags = I2C_M_RD, .buf = val, .len = 1 }, | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 41 | }; | 
|  | 42 |  | 
|  | 43 | if (i2c_transfer(priv->i2c, msg, 2) != 2) { | 
|  | 44 | printk(KERN_WARNING "qt1010 I2C read failed\n"); | 
|  | 45 | return -EREMOTEIO; | 
|  | 46 | } | 
|  | 47 | return 0; | 
|  | 48 | } | 
|  | 49 |  | 
|  | 50 | /* write single register */ | 
|  | 51 | static int qt1010_writereg(struct qt1010_priv *priv, u8 reg, u8 val) | 
|  | 52 | { | 
|  | 53 | u8 buf[2] = { reg, val }; | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 54 | struct i2c_msg msg = { .addr = priv->cfg->i2c_address, | 
|  | 55 | .flags = 0, .buf = buf, .len = 2 }; | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 56 |  | 
|  | 57 | if (i2c_transfer(priv->i2c, &msg, 1) != 1) { | 
|  | 58 | printk(KERN_WARNING "qt1010 I2C write failed\n"); | 
|  | 59 | return -EREMOTEIO; | 
|  | 60 | } | 
|  | 61 | return 0; | 
|  | 62 | } | 
|  | 63 |  | 
|  | 64 | /* dump all registers */ | 
|  | 65 | static void qt1010_dump_regs(struct qt1010_priv *priv) | 
|  | 66 | { | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 67 | u8 reg, val; | 
|  | 68 |  | 
|  | 69 | for (reg = 0; ; reg++) { | 
|  | 70 | if (reg % 16 == 0) { | 
|  | 71 | if (reg) | 
| Jan Nikitenko | 458f9aa | 2009-06-18 08:11:57 -0300 | [diff] [blame] | 72 | printk(KERN_CONT "\n"); | 
|  | 73 | printk(KERN_DEBUG "%02x:", reg); | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 74 | } | 
|  | 75 | if (qt1010_readreg(priv, reg, &val) == 0) | 
| Jan Nikitenko | 458f9aa | 2009-06-18 08:11:57 -0300 | [diff] [blame] | 76 | printk(KERN_CONT " %02x", val); | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 77 | else | 
| Jan Nikitenko | 458f9aa | 2009-06-18 08:11:57 -0300 | [diff] [blame] | 78 | printk(KERN_CONT " --"); | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 79 | if (reg == 0x2f) | 
|  | 80 | break; | 
|  | 81 | } | 
| Jan Nikitenko | 458f9aa | 2009-06-18 08:11:57 -0300 | [diff] [blame] | 82 | printk(KERN_CONT "\n"); | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 83 | } | 
|  | 84 |  | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 85 | static int qt1010_set_params(struct dvb_frontend *fe, | 
|  | 86 | struct dvb_frontend_parameters *params) | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 87 | { | 
|  | 88 | struct qt1010_priv *priv; | 
|  | 89 | int err; | 
|  | 90 | u32 freq, div, mod1, mod2; | 
|  | 91 | u8 i, tmpval, reg05; | 
|  | 92 | qt1010_i2c_oper_t rd[48] = { | 
|  | 93 | { QT1010_WR, 0x01, 0x80 }, | 
|  | 94 | { QT1010_WR, 0x02, 0x3f }, | 
|  | 95 | { QT1010_WR, 0x05, 0xff }, /* 02 c write */ | 
|  | 96 | { QT1010_WR, 0x06, 0x44 }, | 
|  | 97 | { QT1010_WR, 0x07, 0xff }, /* 04 c write */ | 
|  | 98 | { QT1010_WR, 0x08, 0x08 }, | 
|  | 99 | { QT1010_WR, 0x09, 0xff }, /* 06 c write */ | 
|  | 100 | { QT1010_WR, 0x0a, 0xff }, /* 07 c write */ | 
|  | 101 | { QT1010_WR, 0x0b, 0xff }, /* 08 c write */ | 
|  | 102 | { QT1010_WR, 0x0c, 0xe1 }, | 
|  | 103 | { QT1010_WR, 0x1a, 0xff }, /* 10 c write */ | 
|  | 104 | { QT1010_WR, 0x1b, 0x00 }, | 
|  | 105 | { QT1010_WR, 0x1c, 0x89 }, | 
|  | 106 | { QT1010_WR, 0x11, 0xff }, /* 13 c write */ | 
|  | 107 | { QT1010_WR, 0x12, 0xff }, /* 14 c write */ | 
|  | 108 | { QT1010_WR, 0x22, 0xff }, /* 15 c write */ | 
|  | 109 | { QT1010_WR, 0x1e, 0x00 }, | 
|  | 110 | { QT1010_WR, 0x1e, 0xd0 }, | 
|  | 111 | { QT1010_RD, 0x22, 0xff }, /* 16 c read */ | 
|  | 112 | { QT1010_WR, 0x1e, 0x00 }, | 
|  | 113 | { QT1010_RD, 0x05, 0xff }, /* 20 c read */ | 
|  | 114 | { QT1010_RD, 0x22, 0xff }, /* 21 c read */ | 
|  | 115 | { QT1010_WR, 0x23, 0xd0 }, | 
|  | 116 | { QT1010_WR, 0x1e, 0x00 }, | 
|  | 117 | { QT1010_WR, 0x1e, 0xe0 }, | 
|  | 118 | { QT1010_RD, 0x23, 0xff }, /* 25 c read */ | 
|  | 119 | { QT1010_RD, 0x23, 0xff }, /* 26 c read */ | 
|  | 120 | { QT1010_WR, 0x1e, 0x00 }, | 
|  | 121 | { QT1010_WR, 0x24, 0xd0 }, | 
|  | 122 | { QT1010_WR, 0x1e, 0x00 }, | 
|  | 123 | { QT1010_WR, 0x1e, 0xf0 }, | 
|  | 124 | { QT1010_RD, 0x24, 0xff }, /* 31 c read */ | 
|  | 125 | { QT1010_WR, 0x1e, 0x00 }, | 
|  | 126 | { QT1010_WR, 0x14, 0x7f }, | 
|  | 127 | { QT1010_WR, 0x15, 0x7f }, | 
|  | 128 | { QT1010_WR, 0x05, 0xff }, /* 35 c write */ | 
|  | 129 | { QT1010_WR, 0x06, 0x00 }, | 
|  | 130 | { QT1010_WR, 0x15, 0x1f }, | 
|  | 131 | { QT1010_WR, 0x16, 0xff }, | 
|  | 132 | { QT1010_WR, 0x18, 0xff }, | 
|  | 133 | { QT1010_WR, 0x1f, 0xff }, /* 40 c write */ | 
|  | 134 | { QT1010_WR, 0x20, 0xff }, /* 41 c write */ | 
|  | 135 | { QT1010_WR, 0x21, 0x53 }, | 
|  | 136 | { QT1010_WR, 0x25, 0xff }, /* 43 c write */ | 
|  | 137 | { QT1010_WR, 0x26, 0x15 }, | 
|  | 138 | { QT1010_WR, 0x00, 0xff }, /* 45 c write */ | 
|  | 139 | { QT1010_WR, 0x02, 0x00 }, | 
|  | 140 | { QT1010_WR, 0x01, 0x00 } | 
|  | 141 | }; | 
|  | 142 |  | 
|  | 143 | #define FREQ1 32000000 /* 32 MHz */ | 
|  | 144 | #define FREQ2  4000000 /* 4 MHz Quartz oscillator in the stick? */ | 
|  | 145 |  | 
|  | 146 | priv = fe->tuner_priv; | 
|  | 147 | freq = params->frequency; | 
|  | 148 | div = (freq + QT1010_OFFSET) / QT1010_STEP; | 
|  | 149 | freq = (div * QT1010_STEP) - QT1010_OFFSET; | 
|  | 150 | mod1 = (freq + QT1010_OFFSET) % FREQ1; | 
|  | 151 | mod2 = (freq + QT1010_OFFSET) % FREQ2; | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 152 | priv->bandwidth = | 
|  | 153 | (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 154 | priv->frequency = freq; | 
|  | 155 |  | 
| Antti Palosaari | 705d41e | 2007-01-27 16:41:35 -0300 | [diff] [blame] | 156 | if (fe->ops.i2c_gate_ctrl) | 
|  | 157 | fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ | 
|  | 158 |  | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 159 | /* reg 05 base value */ | 
|  | 160 | if      (freq < 290000000) reg05 = 0x14; /* 290 MHz */ | 
|  | 161 | else if (freq < 610000000) reg05 = 0x34; /* 610 MHz */ | 
|  | 162 | else if (freq < 802000000) reg05 = 0x54; /* 802 MHz */ | 
|  | 163 | else                       reg05 = 0x74; | 
|  | 164 |  | 
|  | 165 | /* 0x5 */ | 
|  | 166 | rd[2].val = reg05; | 
|  | 167 |  | 
|  | 168 | /* 07 - set frequency: 32 MHz scale */ | 
|  | 169 | rd[4].val = (freq + QT1010_OFFSET) / FREQ1; | 
|  | 170 |  | 
|  | 171 | /* 09 - changes every 8/24 MHz */ | 
|  | 172 | if (mod1 < 8000000) rd[6].val = 0x1d; | 
|  | 173 | else                rd[6].val = 0x1c; | 
|  | 174 |  | 
|  | 175 | /* 0a - set frequency: 4 MHz scale (max 28 MHz) */ | 
|  | 176 | if      (mod1 < 1*FREQ2) rd[7].val = 0x09; /*  +0 MHz */ | 
|  | 177 | else if (mod1 < 2*FREQ2) rd[7].val = 0x08; /*  +4 MHz */ | 
|  | 178 | else if (mod1 < 3*FREQ2) rd[7].val = 0x0f; /*  +8 MHz */ | 
|  | 179 | else if (mod1 < 4*FREQ2) rd[7].val = 0x0e; /* +12 MHz */ | 
|  | 180 | else if (mod1 < 5*FREQ2) rd[7].val = 0x0d; /* +16 MHz */ | 
|  | 181 | else if (mod1 < 6*FREQ2) rd[7].val = 0x0c; /* +20 MHz */ | 
|  | 182 | else if (mod1 < 7*FREQ2) rd[7].val = 0x0b; /* +24 MHz */ | 
|  | 183 | else                     rd[7].val = 0x0a; /* +28 MHz */ | 
|  | 184 |  | 
|  | 185 | /* 0b - changes every 2/2 MHz */ | 
|  | 186 | if (mod2 < 2000000) rd[8].val = 0x45; | 
|  | 187 | else                rd[8].val = 0x44; | 
|  | 188 |  | 
|  | 189 | /* 1a - set frequency: 125 kHz scale (max 3875 kHz)*/ | 
|  | 190 | tmpval = 0x78; /* byte, overflows intentionally */ | 
|  | 191 | rd[10].val = tmpval-((mod2/QT1010_STEP)*0x08); | 
|  | 192 |  | 
|  | 193 | /* 11 */ | 
|  | 194 | rd[13].val = 0xfd; /* TODO: correct value calculation */ | 
|  | 195 |  | 
|  | 196 | /* 12 */ | 
|  | 197 | rd[14].val = 0x91; /* TODO: correct value calculation */ | 
|  | 198 |  | 
|  | 199 | /* 22 */ | 
|  | 200 | if      (freq < 450000000) rd[15].val = 0xd0; /* 450 MHz */ | 
|  | 201 | else if (freq < 482000000) rd[15].val = 0xd1; /* 482 MHz */ | 
|  | 202 | else if (freq < 514000000) rd[15].val = 0xd4; /* 514 MHz */ | 
|  | 203 | else if (freq < 546000000) rd[15].val = 0xd7; /* 546 MHz */ | 
|  | 204 | else if (freq < 610000000) rd[15].val = 0xda; /* 610 MHz */ | 
|  | 205 | else                       rd[15].val = 0xd0; | 
|  | 206 |  | 
|  | 207 | /* 05 */ | 
|  | 208 | rd[35].val = (reg05 & 0xf0); | 
|  | 209 |  | 
|  | 210 | /* 1f */ | 
|  | 211 | if      (mod1 <  8000000) tmpval = 0x00; | 
|  | 212 | else if (mod1 < 12000000) tmpval = 0x01; | 
|  | 213 | else if (mod1 < 16000000) tmpval = 0x02; | 
|  | 214 | else if (mod1 < 24000000) tmpval = 0x03; | 
|  | 215 | else if (mod1 < 28000000) tmpval = 0x04; | 
|  | 216 | else                      tmpval = 0x05; | 
|  | 217 | rd[40].val = (priv->reg1f_init_val + 0x0e + tmpval); | 
|  | 218 |  | 
|  | 219 | /* 20 */ | 
|  | 220 | if      (mod1 <  8000000) tmpval = 0x00; | 
|  | 221 | else if (mod1 < 12000000) tmpval = 0x01; | 
|  | 222 | else if (mod1 < 20000000) tmpval = 0x02; | 
|  | 223 | else if (mod1 < 24000000) tmpval = 0x03; | 
|  | 224 | else if (mod1 < 28000000) tmpval = 0x04; | 
|  | 225 | else                      tmpval = 0x05; | 
|  | 226 | rd[41].val = (priv->reg20_init_val + 0x0d + tmpval); | 
|  | 227 |  | 
|  | 228 | /* 25 */ | 
|  | 229 | rd[43].val = priv->reg25_init_val; | 
|  | 230 |  | 
|  | 231 | /* 00 */ | 
|  | 232 | rd[45].val = 0x92; /* TODO: correct value calculation */ | 
|  | 233 |  | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 234 | dprintk("freq:%u 05:%02x 07:%02x 09:%02x 0a:%02x 0b:%02x " \ | 
|  | 235 | "1a:%02x 11:%02x 12:%02x 22:%02x 05:%02x 1f:%02x " \ | 
|  | 236 | "20:%02x 25:%02x 00:%02x", \ | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 237 | freq, rd[2].val, rd[4].val, rd[6].val, rd[7].val, rd[8].val, \ | 
|  | 238 | rd[10].val, rd[13].val, rd[14].val, rd[15].val, rd[35].val, \ | 
|  | 239 | rd[40].val, rd[41].val, rd[43].val, rd[45].val); | 
|  | 240 |  | 
| Michael Krufky | 47e76c5 | 2007-02-13 17:53:46 -0300 | [diff] [blame] | 241 | for (i = 0; i < ARRAY_SIZE(rd); i++) { | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 242 | if (rd[i].oper == QT1010_WR) { | 
|  | 243 | err = qt1010_writereg(priv, rd[i].reg, rd[i].val); | 
|  | 244 | } else { /* read is required to proper locking */ | 
|  | 245 | err = qt1010_readreg(priv, rd[i].reg, &tmpval); | 
|  | 246 | } | 
|  | 247 | if (err) return err; | 
|  | 248 | } | 
|  | 249 |  | 
|  | 250 | if (debug) | 
|  | 251 | qt1010_dump_regs(priv); | 
|  | 252 |  | 
| Antti Palosaari | 705d41e | 2007-01-27 16:41:35 -0300 | [diff] [blame] | 253 | if (fe->ops.i2c_gate_ctrl) | 
|  | 254 | fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ | 
|  | 255 |  | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 256 | return 0; | 
|  | 257 | } | 
|  | 258 |  | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 259 | static int qt1010_init_meas1(struct qt1010_priv *priv, | 
|  | 260 | u8 oper, u8 reg, u8 reg_init_val, u8 *retval) | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 261 | { | 
|  | 262 | u8 i, val1, val2; | 
|  | 263 | int err; | 
|  | 264 |  | 
|  | 265 | qt1010_i2c_oper_t i2c_data[] = { | 
|  | 266 | { QT1010_WR, reg, reg_init_val }, | 
|  | 267 | { QT1010_WR, 0x1e, 0x00 }, | 
|  | 268 | { QT1010_WR, 0x1e, oper }, | 
|  | 269 | { QT1010_RD, reg, 0xff } | 
|  | 270 | }; | 
|  | 271 |  | 
| Michael Krufky | 47e76c5 | 2007-02-13 17:53:46 -0300 | [diff] [blame] | 272 | for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 273 | if (i2c_data[i].oper == QT1010_WR) { | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 274 | err = qt1010_writereg(priv, i2c_data[i].reg, | 
|  | 275 | i2c_data[i].val); | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 276 | } else { | 
|  | 277 | err = qt1010_readreg(priv, i2c_data[i].reg, &val2); | 
|  | 278 | } | 
|  | 279 | if (err) return err; | 
|  | 280 | } | 
|  | 281 |  | 
|  | 282 | do { | 
|  | 283 | val1 = val2; | 
|  | 284 | err = qt1010_readreg(priv, reg, &val2); | 
|  | 285 | if (err) return err; | 
|  | 286 | dprintk("compare reg:%02x %02x %02x", reg, val1, val2); | 
|  | 287 | } while (val1 != val2); | 
|  | 288 | *retval = val1; | 
|  | 289 |  | 
|  | 290 | return qt1010_writereg(priv, 0x1e, 0x00); | 
|  | 291 | } | 
|  | 292 |  | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 293 | static u8 qt1010_init_meas2(struct qt1010_priv *priv, | 
|  | 294 | u8 reg_init_val, u8 *retval) | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 295 | { | 
|  | 296 | u8 i, val; | 
|  | 297 | int err; | 
|  | 298 | qt1010_i2c_oper_t i2c_data[] = { | 
|  | 299 | { QT1010_WR, 0x07, reg_init_val }, | 
|  | 300 | { QT1010_WR, 0x22, 0xd0 }, | 
|  | 301 | { QT1010_WR, 0x1e, 0x00 }, | 
|  | 302 | { QT1010_WR, 0x1e, 0xd0 }, | 
|  | 303 | { QT1010_RD, 0x22, 0xff }, | 
|  | 304 | { QT1010_WR, 0x1e, 0x00 }, | 
|  | 305 | { QT1010_WR, 0x22, 0xff } | 
|  | 306 | }; | 
| Michael Krufky | 47e76c5 | 2007-02-13 17:53:46 -0300 | [diff] [blame] | 307 | for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 308 | if (i2c_data[i].oper == QT1010_WR) { | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 309 | err = qt1010_writereg(priv, i2c_data[i].reg, | 
|  | 310 | i2c_data[i].val); | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 311 | } else { | 
|  | 312 | err = qt1010_readreg(priv, i2c_data[i].reg, &val); | 
|  | 313 | } | 
|  | 314 | if (err) return err; | 
|  | 315 | } | 
|  | 316 | *retval = val; | 
|  | 317 | return 0; | 
|  | 318 | } | 
|  | 319 |  | 
|  | 320 | static int qt1010_init(struct dvb_frontend *fe) | 
|  | 321 | { | 
|  | 322 | struct qt1010_priv *priv = fe->tuner_priv; | 
|  | 323 | struct dvb_frontend_parameters params; | 
| Marco Schluessler | b79ea69 | 2007-02-13 16:46:13 -0300 | [diff] [blame] | 324 | int err = 0; | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 325 | u8 i, tmpval, *valptr = NULL; | 
|  | 326 |  | 
|  | 327 | qt1010_i2c_oper_t i2c_data[] = { | 
|  | 328 | { QT1010_WR, 0x01, 0x80 }, | 
|  | 329 | { QT1010_WR, 0x0d, 0x84 }, | 
|  | 330 | { QT1010_WR, 0x0e, 0xb7 }, | 
|  | 331 | { QT1010_WR, 0x2a, 0x23 }, | 
|  | 332 | { QT1010_WR, 0x2c, 0xdc }, | 
|  | 333 | { QT1010_M1, 0x25, 0x40 }, /* get reg 25 init value */ | 
|  | 334 | { QT1010_M1, 0x81, 0xff }, /* get reg 25 init value */ | 
|  | 335 | { QT1010_WR, 0x2b, 0x70 }, | 
|  | 336 | { QT1010_WR, 0x2a, 0x23 }, | 
|  | 337 | { QT1010_M1, 0x26, 0x08 }, | 
|  | 338 | { QT1010_M1, 0x82, 0xff }, | 
|  | 339 | { QT1010_WR, 0x05, 0x14 }, | 
|  | 340 | { QT1010_WR, 0x06, 0x44 }, | 
|  | 341 | { QT1010_WR, 0x07, 0x28 }, | 
|  | 342 | { QT1010_WR, 0x08, 0x0b }, | 
|  | 343 | { QT1010_WR, 0x11, 0xfd }, | 
|  | 344 | { QT1010_M1, 0x22, 0x0d }, | 
|  | 345 | { QT1010_M1, 0xd0, 0xff }, | 
|  | 346 | { QT1010_WR, 0x06, 0x40 }, | 
|  | 347 | { QT1010_WR, 0x16, 0xf0 }, | 
|  | 348 | { QT1010_WR, 0x02, 0x38 }, | 
|  | 349 | { QT1010_WR, 0x03, 0x18 }, | 
|  | 350 | { QT1010_WR, 0x20, 0xe0 }, | 
|  | 351 | { QT1010_M1, 0x1f, 0x20 }, /* get reg 1f init value */ | 
|  | 352 | { QT1010_M1, 0x84, 0xff }, /* get reg 1f init value */ | 
|  | 353 | { QT1010_RD, 0x20, 0x20 }, /* get reg 20 init value */ | 
|  | 354 | { QT1010_WR, 0x03, 0x19 }, | 
|  | 355 | { QT1010_WR, 0x02, 0x3f }, | 
|  | 356 | { QT1010_WR, 0x21, 0x53 }, | 
|  | 357 | { QT1010_RD, 0x21, 0xff }, | 
|  | 358 | { QT1010_WR, 0x11, 0xfd }, | 
|  | 359 | { QT1010_WR, 0x05, 0x34 }, | 
|  | 360 | { QT1010_WR, 0x06, 0x44 }, | 
|  | 361 | { QT1010_WR, 0x08, 0x08 } | 
|  | 362 | }; | 
|  | 363 |  | 
| Antti Palosaari | 705d41e | 2007-01-27 16:41:35 -0300 | [diff] [blame] | 364 | if (fe->ops.i2c_gate_ctrl) | 
|  | 365 | fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ | 
|  | 366 |  | 
| Michael Krufky | 47e76c5 | 2007-02-13 17:53:46 -0300 | [diff] [blame] | 367 | for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 368 | switch (i2c_data[i].oper) { | 
|  | 369 | case QT1010_WR: | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 370 | err = qt1010_writereg(priv, i2c_data[i].reg, | 
|  | 371 | i2c_data[i].val); | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 372 | break; | 
|  | 373 | case QT1010_RD: | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 374 | if (i2c_data[i].val == 0x20) | 
|  | 375 | valptr = &priv->reg20_init_val; | 
|  | 376 | else | 
|  | 377 | valptr = &tmpval; | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 378 | err = qt1010_readreg(priv, i2c_data[i].reg, valptr); | 
|  | 379 | break; | 
|  | 380 | case QT1010_M1: | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 381 | if (i2c_data[i].val == 0x25) | 
|  | 382 | valptr = &priv->reg25_init_val; | 
|  | 383 | else if (i2c_data[i].val == 0x1f) | 
|  | 384 | valptr = &priv->reg1f_init_val; | 
|  | 385 | else | 
|  | 386 | valptr = &tmpval; | 
|  | 387 | err = qt1010_init_meas1(priv, i2c_data[i+1].reg, | 
|  | 388 | i2c_data[i].reg, | 
|  | 389 | i2c_data[i].val, valptr); | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 390 | i++; | 
|  | 391 | break; | 
|  | 392 | } | 
|  | 393 | if (err) return err; | 
|  | 394 | } | 
|  | 395 |  | 
|  | 396 | for (i = 0x31; i < 0x3a; i++) /* 0x31 - 0x39 */ | 
|  | 397 | if ((err = qt1010_init_meas2(priv, i, &tmpval))) | 
|  | 398 | return err; | 
|  | 399 |  | 
|  | 400 | params.frequency = 545000000; /* Sigmatek DVB-110 545000000 */ | 
|  | 401 | /* MSI Megasky 580 GL861 533000000 */ | 
|  | 402 | return qt1010_set_params(fe, ¶ms); | 
|  | 403 | } | 
|  | 404 |  | 
|  | 405 | static int qt1010_release(struct dvb_frontend *fe) | 
|  | 406 | { | 
|  | 407 | kfree(fe->tuner_priv); | 
|  | 408 | fe->tuner_priv = NULL; | 
|  | 409 | return 0; | 
|  | 410 | } | 
|  | 411 |  | 
|  | 412 | static int qt1010_get_frequency(struct dvb_frontend *fe, u32 *frequency) | 
|  | 413 | { | 
|  | 414 | struct qt1010_priv *priv = fe->tuner_priv; | 
|  | 415 | *frequency = priv->frequency; | 
|  | 416 | return 0; | 
|  | 417 | } | 
|  | 418 |  | 
|  | 419 | static int qt1010_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) | 
|  | 420 | { | 
|  | 421 | struct qt1010_priv *priv = fe->tuner_priv; | 
|  | 422 | *bandwidth = priv->bandwidth; | 
|  | 423 | return 0; | 
|  | 424 | } | 
|  | 425 |  | 
|  | 426 | static const struct dvb_tuner_ops qt1010_tuner_ops = { | 
|  | 427 | .info = { | 
|  | 428 | .name           = "Quantek QT1010", | 
|  | 429 | .frequency_min  = QT1010_MIN_FREQ, | 
|  | 430 | .frequency_max  = QT1010_MAX_FREQ, | 
|  | 431 | .frequency_step = QT1010_STEP, | 
|  | 432 | }, | 
|  | 433 |  | 
|  | 434 | .release       = qt1010_release, | 
|  | 435 | .init          = qt1010_init, | 
|  | 436 | /* TODO: implement sleep */ | 
|  | 437 |  | 
|  | 438 | .set_params    = qt1010_set_params, | 
|  | 439 | .get_frequency = qt1010_get_frequency, | 
|  | 440 | .get_bandwidth = qt1010_get_bandwidth | 
|  | 441 | }; | 
|  | 442 |  | 
|  | 443 | struct dvb_frontend * qt1010_attach(struct dvb_frontend *fe, | 
|  | 444 | struct i2c_adapter *i2c, | 
|  | 445 | struct qt1010_config *cfg) | 
|  | 446 | { | 
|  | 447 | struct qt1010_priv *priv = NULL; | 
|  | 448 | u8 id; | 
|  | 449 |  | 
|  | 450 | priv = kzalloc(sizeof(struct qt1010_priv), GFP_KERNEL); | 
|  | 451 | if (priv == NULL) | 
|  | 452 | return NULL; | 
|  | 453 |  | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 454 | priv->cfg = cfg; | 
|  | 455 | priv->i2c = i2c; | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 456 |  | 
| Antti Palosaari | 705d41e | 2007-01-27 16:41:35 -0300 | [diff] [blame] | 457 | if (fe->ops.i2c_gate_ctrl) | 
|  | 458 | fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ | 
|  | 459 |  | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 460 |  | 
|  | 461 | /* Try to detect tuner chip. Probably this is not correct register. */ | 
|  | 462 | if (qt1010_readreg(priv, 0x29, &id) != 0 || (id != 0x39)) { | 
|  | 463 | kfree(priv); | 
|  | 464 | return NULL; | 
|  | 465 | } | 
|  | 466 |  | 
| Antti Palosaari | 705d41e | 2007-01-27 16:41:35 -0300 | [diff] [blame] | 467 | if (fe->ops.i2c_gate_ctrl) | 
|  | 468 | fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ | 
|  | 469 |  | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 470 | printk(KERN_INFO "Quantek QT1010 successfully identified.\n"); | 
| Michael Krufky | f6982d5 | 2007-02-13 18:26:26 -0300 | [diff] [blame] | 471 | memcpy(&fe->ops.tuner_ops, &qt1010_tuner_ops, | 
|  | 472 | sizeof(struct dvb_tuner_ops)); | 
| Antti Palosaari | cbdc80e | 2007-01-21 15:56:10 -0300 | [diff] [blame] | 473 |  | 
|  | 474 | fe->tuner_priv = priv; | 
|  | 475 | return fe; | 
|  | 476 | } | 
|  | 477 | EXPORT_SYMBOL(qt1010_attach); | 
|  | 478 |  | 
|  | 479 | MODULE_DESCRIPTION("Quantek QT1010 silicon tuner driver"); | 
|  | 480 | MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); | 
|  | 481 | MODULE_AUTHOR("Aapo Tahkola <aet@rasterburn.org>"); | 
|  | 482 | MODULE_VERSION("0.1"); | 
|  | 483 | MODULE_LICENSE("GPL"); |