diff options
Diffstat (limited to 'drivers/media/dvb/frontends/mt2060.c')
-rw-r--r-- | drivers/media/dvb/frontends/mt2060.c | 190 |
1 files changed, 122 insertions, 68 deletions
diff --git a/drivers/media/dvb/frontends/mt2060.c b/drivers/media/dvb/frontends/mt2060.c index 14b4f588eeb7..cc38e7077a0b 100644 --- a/drivers/media/dvb/frontends/mt2060.c +++ b/drivers/media/dvb/frontends/mt2060.c | |||
@@ -19,14 +19,16 @@ | |||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= | 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= |
20 | */ | 20 | */ |
21 | 21 | ||
22 | /* See mt2060_priv.h for details */ | ||
23 | |||
24 | /* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ | 22 | /* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ |
25 | 23 | ||
26 | #include <linux/module.h> | 24 | #include <linux/module.h> |
27 | #include <linux/moduleparam.h> | 25 | #include <linux/moduleparam.h> |
28 | #include <linux/delay.h> | 26 | #include <linux/delay.h> |
29 | #include <linux/dvb/frontend.h> | 27 | #include <linux/dvb/frontend.h> |
28 | #include <linux/i2c.h> | ||
29 | |||
30 | #include "dvb_frontend.h" | ||
31 | |||
30 | #include "mt2060.h" | 32 | #include "mt2060.h" |
31 | #include "mt2060_priv.h" | 33 | #include "mt2060_priv.h" |
32 | 34 | ||
@@ -34,17 +36,17 @@ static int debug=0; | |||
34 | module_param(debug, int, 0644); | 36 | module_param(debug, int, 0644); |
35 | MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); | 37 | MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); |
36 | 38 | ||
37 | #define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "MT2060: " args); printk("\n"); } } while (0) | 39 | #define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2060: " args); printk("\n"); }} while (0) |
38 | 40 | ||
39 | // Reads a single register | 41 | // Reads a single register |
40 | static int mt2060_readreg(struct mt2060_state *state, u8 reg, u8 *val) | 42 | static int mt2060_readreg(struct mt2060_priv *priv, u8 reg, u8 *val) |
41 | { | 43 | { |
42 | struct i2c_msg msg[2] = { | 44 | struct i2c_msg msg[2] = { |
43 | { .addr = state->config->i2c_address, .flags = 0, .buf = ®, .len = 1 }, | 45 | { .addr = priv->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, |
44 | { .addr = state->config->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 }, | 46 | { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 }, |
45 | }; | 47 | }; |
46 | 48 | ||
47 | if (i2c_transfer(state->i2c, msg, 2) != 2) { | 49 | if (i2c_transfer(priv->i2c, msg, 2) != 2) { |
48 | printk(KERN_WARNING "mt2060 I2C read failed\n"); | 50 | printk(KERN_WARNING "mt2060 I2C read failed\n"); |
49 | return -EREMOTEIO; | 51 | return -EREMOTEIO; |
50 | } | 52 | } |
@@ -52,16 +54,14 @@ static int mt2060_readreg(struct mt2060_state *state, u8 reg, u8 *val) | |||
52 | } | 54 | } |
53 | 55 | ||
54 | // Writes a single register | 56 | // Writes a single register |
55 | static int mt2060_writereg(struct mt2060_state *state, u8 reg, u8 val) | 57 | static int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val) |
56 | { | 58 | { |
57 | u8 buf[2]; | 59 | u8 buf[2] = { reg, val }; |
58 | struct i2c_msg msg = { | 60 | struct i2c_msg msg = { |
59 | .addr = state->config->i2c_address, .flags = 0, .buf = buf, .len = 2 | 61 | .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2 |
60 | }; | 62 | }; |
61 | buf[0]=reg; | ||
62 | buf[1]=val; | ||
63 | 63 | ||
64 | if (i2c_transfer(state->i2c, &msg, 1) != 1) { | 64 | if (i2c_transfer(priv->i2c, &msg, 1) != 1) { |
65 | printk(KERN_WARNING "mt2060 I2C write failed\n"); | 65 | printk(KERN_WARNING "mt2060 I2C write failed\n"); |
66 | return -EREMOTEIO; | 66 | return -EREMOTEIO; |
67 | } | 67 | } |
@@ -69,12 +69,12 @@ static int mt2060_writereg(struct mt2060_state *state, u8 reg, u8 val) | |||
69 | } | 69 | } |
70 | 70 | ||
71 | // Writes a set of consecutive registers | 71 | // Writes a set of consecutive registers |
72 | static int mt2060_writeregs(struct mt2060_state *state,u8 *buf, u8 len) | 72 | static int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len) |
73 | { | 73 | { |
74 | struct i2c_msg msg = { | 74 | struct i2c_msg msg = { |
75 | .addr = state->config->i2c_address, .flags = 0, .buf = buf, .len = len | 75 | .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len |
76 | }; | 76 | }; |
77 | if (i2c_transfer(state->i2c, &msg, 1) != 1) { | 77 | if (i2c_transfer(priv->i2c, &msg, 1) != 1) { |
78 | printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n",(int)len); | 78 | printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n",(int)len); |
79 | return -EREMOTEIO; | 79 | return -EREMOTEIO; |
80 | } | 80 | } |
@@ -95,20 +95,6 @@ static u8 mt2060_config2[] = { | |||
95 | }; | 95 | }; |
96 | 96 | ||
97 | // VGAG=3, V1CSE=1 | 97 | // VGAG=3, V1CSE=1 |
98 | static u8 mt2060_config3[] = { | ||
99 | REG_VGAG, | ||
100 | 0x33 | ||
101 | }; | ||
102 | |||
103 | int mt2060_init(struct mt2060_state *state) | ||
104 | { | ||
105 | if (mt2060_writeregs(state,mt2060_config1,sizeof(mt2060_config1))) | ||
106 | return -EREMOTEIO; | ||
107 | if (mt2060_writeregs(state,mt2060_config3,sizeof(mt2060_config3))) | ||
108 | return -EREMOTEIO; | ||
109 | return 0; | ||
110 | } | ||
111 | EXPORT_SYMBOL(mt2060_init); | ||
112 | 98 | ||
113 | #ifdef MT2060_SPURCHECK | 99 | #ifdef MT2060_SPURCHECK |
114 | /* The function below calculates the frequency offset between the output frequency if2 | 100 | /* The function below calculates the frequency offset between the output frequency if2 |
@@ -167,8 +153,9 @@ static int mt2060_spurcheck(u32 lo1,u32 lo2,u32 if2) | |||
167 | #define IF2 36150 // IF2 frequency = 36.150 MHz | 153 | #define IF2 36150 // IF2 frequency = 36.150 MHz |
168 | #define FREF 16000 // Quartz oscillator 16 MHz | 154 | #define FREF 16000 // Quartz oscillator 16 MHz |
169 | 155 | ||
170 | int mt2060_set(struct mt2060_state *state, struct dvb_frontend_parameters *fep) | 156 | static int mt2060_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) |
171 | { | 157 | { |
158 | struct mt2060_priv *priv; | ||
172 | int ret=0; | 159 | int ret=0; |
173 | int i=0; | 160 | int i=0; |
174 | u32 freq; | 161 | u32 freq; |
@@ -178,17 +165,23 @@ int mt2060_set(struct mt2060_state *state, struct dvb_frontend_parameters *fep) | |||
178 | u8 b[8]; | 165 | u8 b[8]; |
179 | u32 if1; | 166 | u32 if1; |
180 | 167 | ||
181 | if1 = state->if1_freq; | 168 | priv = fe->tuner_priv; |
169 | |||
170 | if1 = priv->if1_freq; | ||
182 | b[0] = REG_LO1B1; | 171 | b[0] = REG_LO1B1; |
183 | b[1] = 0xFF; | 172 | b[1] = 0xFF; |
184 | mt2060_writeregs(state,b,2); | ||
185 | 173 | ||
186 | freq = fep->frequency / 1000; // Hz -> kHz | 174 | mt2060_writeregs(priv,b,2); |
187 | 175 | ||
188 | f_lo1 = freq + if1 * 1000; | 176 | freq = params->frequency / 1000; // Hz -> kHz |
189 | f_lo1 = (f_lo1/250)*250; | 177 | priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; |
190 | f_lo2 = f_lo1 - freq - IF2; | 178 | |
191 | f_lo2 = (f_lo2/50)*50; | 179 | f_lo1 = freq + if1 * 1000; |
180 | f_lo1 = (f_lo1 / 250) * 250; | ||
181 | f_lo2 = f_lo1 - freq - IF2; | ||
182 | // From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise | ||
183 | f_lo2 = ((f_lo2 + 25) / 50) * 50; | ||
184 | priv->frequency = (f_lo1 - f_lo2 - IF2) * 1000, | ||
192 | 185 | ||
193 | #ifdef MT2060_SPURCHECK | 186 | #ifdef MT2060_SPURCHECK |
194 | // LO-related spurs detection and correction | 187 | // LO-related spurs detection and correction |
@@ -197,12 +190,14 @@ int mt2060_set(struct mt2060_state *state, struct dvb_frontend_parameters *fep) | |||
197 | f_lo2 += num1; | 190 | f_lo2 += num1; |
198 | #endif | 191 | #endif |
199 | //Frequency LO1 = 16MHz * (DIV1 + NUM1/64 ) | 192 | //Frequency LO1 = 16MHz * (DIV1 + NUM1/64 ) |
200 | div1 = f_lo1 / FREF; | 193 | num1 = f_lo1 / (FREF / 64); |
201 | num1 = (64 * (f_lo1 % FREF) )/FREF; | 194 | div1 = num1 / 64; |
195 | num1 &= 0x3f; | ||
202 | 196 | ||
203 | // Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) | 197 | // Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) |
204 | div2 = f_lo2 / FREF; | 198 | num2 = f_lo2 * 64 / (FREF / 128); |
205 | num2 = (16384 * (f_lo2 % FREF) /FREF +1)/2; | 199 | div2 = num2 / 8192; |
200 | num2 &= 0x1fff; | ||
206 | 201 | ||
207 | if (freq <= 95000) lnaband = 0xB0; else | 202 | if (freq <= 95000) lnaband = 0xB0; else |
208 | if (freq <= 180000) lnaband = 0xA0; else | 203 | if (freq <= 180000) lnaband = 0xA0; else |
@@ -223,85 +218,144 @@ int mt2060_set(struct mt2060_state *state, struct dvb_frontend_parameters *fep) | |||
223 | b[5] = ((num2 >>12) & 1) | (div2 << 1); | 218 | b[5] = ((num2 >>12) & 1) | (div2 << 1); |
224 | 219 | ||
225 | dprintk("IF1: %dMHz",(int)if1); | 220 | dprintk("IF1: %dMHz",(int)if1); |
226 | dprintk("PLL freq: %d f_lo1: %d f_lo2: %d (kHz)",(int)freq,(int)f_lo1,(int)f_lo2); | 221 | dprintk("PLL freq=%dkHz f_lo1=%dkHz f_lo2=%dkHz",(int)freq,(int)f_lo1,(int)f_lo2); |
227 | dprintk("PLL div1: %d num1: %d div2: %d num2: %d",(int)div1,(int)num1,(int)div2,(int)num2); | 222 | dprintk("PLL div1=%d num1=%d div2=%d num2=%d",(int)div1,(int)num1,(int)div2,(int)num2); |
228 | dprintk("PLL [1..5]: %2x %2x %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3],(int)b[4],(int)b[5]); | 223 | dprintk("PLL [1..5]: %2x %2x %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3],(int)b[4],(int)b[5]); |
229 | 224 | ||
230 | mt2060_writeregs(state,b,6); | 225 | mt2060_writeregs(priv,b,6); |
231 | 226 | ||
232 | //Waits for pll lock or timeout | 227 | //Waits for pll lock or timeout |
233 | i=0; | 228 | i = 0; |
234 | do { | 229 | do { |
235 | mt2060_readreg(state,REG_LO_STATUS,b); | 230 | mt2060_readreg(priv,REG_LO_STATUS,b); |
236 | if ((b[0] & 0x88)==0x88) break; | 231 | if ((b[0] & 0x88)==0x88) |
232 | break; | ||
237 | msleep(4); | 233 | msleep(4); |
238 | i++; | 234 | i++; |
239 | } while (i<10); | 235 | } while (i<10); |
240 | 236 | ||
241 | return ret; | 237 | return ret; |
242 | } | 238 | } |
243 | EXPORT_SYMBOL(mt2060_set); | ||
244 | 239 | ||
245 | /* from usbsnoop.log */ | 240 | static void mt2060_calibrate(struct mt2060_priv *priv) |
246 | static void mt2060_calibrate(struct mt2060_state *state) | ||
247 | { | 241 | { |
248 | u8 b = 0; | 242 | u8 b = 0; |
249 | int i = 0; | 243 | int i = 0; |
250 | 244 | ||
251 | if (mt2060_writeregs(state,mt2060_config1,sizeof(mt2060_config1))) | 245 | if (mt2060_writeregs(priv,mt2060_config1,sizeof(mt2060_config1))) |
252 | return; | 246 | return; |
253 | if (mt2060_writeregs(state,mt2060_config2,sizeof(mt2060_config2))) | 247 | if (mt2060_writeregs(priv,mt2060_config2,sizeof(mt2060_config2))) |
254 | return; | 248 | return; |
255 | 249 | ||
256 | do { | 250 | do { |
257 | b |= (1 << 6); // FM1SS; | 251 | b |= (1 << 6); // FM1SS; |
258 | mt2060_writereg(state, REG_LO2C1,b); | 252 | mt2060_writereg(priv, REG_LO2C1,b); |
259 | msleep(20); | 253 | msleep(20); |
260 | 254 | ||
261 | if (i == 0) { | 255 | if (i == 0) { |
262 | b |= (1 << 7); // FM1CA; | 256 | b |= (1 << 7); // FM1CA; |
263 | mt2060_writereg(state, REG_LO2C1,b); | 257 | mt2060_writereg(priv, REG_LO2C1,b); |
264 | b &= ~(1 << 7); // FM1CA; | 258 | b &= ~(1 << 7); // FM1CA; |
265 | msleep(20); | 259 | msleep(20); |
266 | } | 260 | } |
267 | 261 | ||
268 | b &= ~(1 << 6); // FM1SS | 262 | b &= ~(1 << 6); // FM1SS |
269 | mt2060_writereg(state, REG_LO2C1,b); | 263 | mt2060_writereg(priv, REG_LO2C1,b); |
270 | 264 | ||
271 | msleep(20); | 265 | msleep(20); |
272 | i++; | 266 | i++; |
273 | } while (i < 9); | 267 | } while (i < 9); |
274 | 268 | ||
275 | i = 0; | 269 | i = 0; |
276 | while (i++ < 10 && mt2060_readreg(state, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0) | 270 | while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0) |
277 | msleep(20); | 271 | msleep(20); |
278 | 272 | ||
279 | if (i < 10) { | 273 | if (i < 10) { |
280 | mt2060_readreg(state, REG_FM_FREQ, &state->fmfreq); // now find out, what is fmreq used for :) | 274 | mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :) |
281 | dprintk("calibration was successful: %d", state->fmfreq); | 275 | dprintk("calibration was successful: %d", (int)priv->fmfreq); |
282 | } else | 276 | } else |
283 | dprintk("FMCAL timed out"); | 277 | dprintk("FMCAL timed out"); |
284 | } | 278 | } |
285 | 279 | ||
280 | static int mt2060_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8 *buf, int buf_len) | ||
281 | { | ||
282 | return -ENODEV; | ||
283 | } | ||
284 | |||
285 | static int mt2060_get_frequency(struct dvb_frontend *fe, u32 *frequency) | ||
286 | { | ||
287 | struct mt2060_priv *priv = fe->tuner_priv; | ||
288 | *frequency = priv->frequency; | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static int mt2060_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) | ||
293 | { | ||
294 | struct mt2060_priv *priv = fe->tuner_priv; | ||
295 | *bandwidth = priv->bandwidth; | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static int mt2060_sleep(struct dvb_frontend *fe) | ||
300 | { | ||
301 | struct mt2060_priv *priv = fe->tuner_priv; | ||
302 | return mt2060_writereg(priv, REG_VGAG,0x30); | ||
303 | } | ||
304 | |||
305 | static int mt2060_release(struct dvb_frontend *fe) | ||
306 | { | ||
307 | kfree(fe->tuner_priv); | ||
308 | fe->tuner_priv = NULL; | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static const struct dvb_tuner_ops mt2060_tuner_ops = { | ||
313 | .info = { | ||
314 | .name = "Microtune MT2060", | ||
315 | .frequency_min = 48000000, | ||
316 | .frequency_max = 860000000, | ||
317 | .frequency_step = 50000, | ||
318 | }, | ||
319 | |||
320 | .release = mt2060_release, | ||
321 | |||
322 | .sleep = mt2060_sleep, | ||
323 | |||
324 | .set_params = mt2060_set_params, | ||
325 | .calc_regs = mt2060_calc_regs, | ||
326 | .get_frequency = mt2060_get_frequency, | ||
327 | .get_bandwidth = mt2060_get_bandwidth | ||
328 | }; | ||
329 | |||
286 | /* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */ | 330 | /* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */ |
287 | int mt2060_attach(struct mt2060_state *state, struct mt2060_config *config, struct i2c_adapter *i2c,u16 if1) | 331 | int mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1) |
288 | { | 332 | { |
333 | struct mt2060_priv *priv = NULL; | ||
289 | u8 id = 0; | 334 | u8 id = 0; |
290 | memset(state,0,sizeof(struct mt2060_state)); | ||
291 | 335 | ||
292 | state->config = config; | 336 | priv = kzalloc(sizeof(struct mt2060_priv), GFP_KERNEL); |
293 | state->i2c = i2c; | 337 | if (priv == NULL) |
294 | state->if1_freq = if1; | 338 | return -ENOMEM; |
295 | 339 | ||
296 | if (mt2060_readreg(state,REG_PART_REV,&id) != 0) | 340 | priv->cfg = cfg; |
297 | return -ENODEV; | 341 | priv->i2c = i2c; |
342 | priv->if1_freq = if1; | ||
298 | 343 | ||
299 | if (id != PART_REV) | 344 | if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) { |
345 | kfree(priv); | ||
300 | return -ENODEV; | 346 | return -ENODEV; |
347 | } | ||
301 | 348 | ||
349 | if (id != PART_REV) { | ||
350 | kfree(priv); | ||
351 | return -ENODEV; | ||
352 | } | ||
302 | printk(KERN_INFO "MT2060: successfully identified\n"); | 353 | printk(KERN_INFO "MT2060: successfully identified\n"); |
354 | memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops)); | ||
355 | |||
356 | fe->tuner_priv = priv; | ||
303 | 357 | ||
304 | mt2060_calibrate(state); | 358 | mt2060_calibrate(priv); |
305 | 359 | ||
306 | return 0; | 360 | return 0; |
307 | } | 361 | } |