Commit | Line | Data |
---|---|---|
4de2730a PB |
1 | /* |
2 | * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" | |
3 | * | |
4 | * Copyright (c) 2006 Olivier DANET <odanet@caramail.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * | |
15 | * GNU General Public License for more details. | |
4de2730a PB |
16 | */ |
17 | ||
4de2730a PB |
18 | /* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ |
19 | ||
20 | #include <linux/module.h> | |
4de2730a PB |
21 | #include <linux/delay.h> |
22 | #include <linux/dvb/frontend.h> | |
46f73f93 | 23 | #include <linux/i2c.h> |
5a0e3ad6 | 24 | #include <linux/slab.h> |
46f73f93 | 25 | |
fada1935 | 26 | #include <media/dvb_frontend.h> |
46f73f93 | 27 | |
4de2730a PB |
28 | #include "mt2060.h" |
29 | #include "mt2060_priv.h" | |
30 | ||
b7571f8d | 31 | static int debug; |
4de2730a PB |
32 | module_param(debug, int, 0644); |
33 | MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); | |
34 | ||
46f73f93 | 35 | #define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2060: " args); printk("\n"); }} while (0) |
4de2730a PB |
36 | |
37 | // Reads a single register | |
46f73f93 | 38 | static int mt2060_readreg(struct mt2060_priv *priv, u8 reg, u8 *val) |
4de2730a PB |
39 | { |
40 | struct i2c_msg msg[2] = { | |
b4756707 SY |
41 | { .addr = priv->cfg->i2c_address, .flags = 0, .len = 1 }, |
42 | { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .len = 1 }, | |
4de2730a | 43 | }; |
b4756707 SY |
44 | int rc = 0; |
45 | u8 *b; | |
46 | ||
47 | b = kmalloc(2, GFP_KERNEL); | |
48 | if (!b) | |
49 | return -ENOMEM; | |
50 | ||
51 | b[0] = reg; | |
52 | b[1] = 0; | |
53 | ||
54 | msg[0].buf = b; | |
55 | msg[1].buf = b + 1; | |
4de2730a | 56 | |
46f73f93 | 57 | if (i2c_transfer(priv->i2c, msg, 2) != 2) { |
4de2730a | 58 | printk(KERN_WARNING "mt2060 I2C read failed\n"); |
b4756707 | 59 | rc = -EREMOTEIO; |
4de2730a | 60 | } |
b4756707 SY |
61 | *val = b[1]; |
62 | kfree(b); | |
63 | ||
64 | return rc; | |
4de2730a PB |
65 | } |
66 | ||
67 | // Writes a single register | |
46f73f93 | 68 | static int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val) |
4de2730a | 69 | { |
4de2730a | 70 | struct i2c_msg msg = { |
b4756707 | 71 | .addr = priv->cfg->i2c_address, .flags = 0, .len = 2 |
4de2730a | 72 | }; |
b4756707 SY |
73 | u8 *buf; |
74 | int rc = 0; | |
75 | ||
76 | buf = kmalloc(2, GFP_KERNEL); | |
77 | if (!buf) | |
78 | return -ENOMEM; | |
79 | ||
80 | buf[0] = reg; | |
81 | buf[1] = val; | |
82 | ||
83 | msg.buf = buf; | |
4de2730a | 84 | |
46f73f93 | 85 | if (i2c_transfer(priv->i2c, &msg, 1) != 1) { |
4de2730a | 86 | printk(KERN_WARNING "mt2060 I2C write failed\n"); |
b4756707 | 87 | rc = -EREMOTEIO; |
4de2730a | 88 | } |
b4756707 SY |
89 | kfree(buf); |
90 | return rc; | |
4de2730a PB |
91 | } |
92 | ||
93 | // Writes a set of consecutive registers | |
46f73f93 | 94 | static int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len) |
4de2730a | 95 | { |
433c4864 | 96 | int rem, val_len; |
b4756707 SY |
97 | u8 *xfer_buf; |
98 | int rc = 0; | |
4de2730a | 99 | struct i2c_msg msg = { |
b4756707 | 100 | .addr = priv->cfg->i2c_address, .flags = 0 |
4de2730a | 101 | }; |
433c4864 | 102 | |
b4756707 SY |
103 | xfer_buf = kmalloc(16, GFP_KERNEL); |
104 | if (!xfer_buf) | |
105 | return -ENOMEM; | |
106 | ||
107 | msg.buf = xfer_buf; | |
108 | ||
433c4864 AP |
109 | for (rem = len - 1; rem > 0; rem -= priv->i2c_max_regs) { |
110 | val_len = min_t(int, rem, priv->i2c_max_regs); | |
111 | msg.len = 1 + val_len; | |
112 | xfer_buf[0] = buf[0] + len - 1 - rem; | |
113 | memcpy(&xfer_buf[1], &buf[1 + len - 1 - rem], val_len); | |
114 | ||
115 | if (i2c_transfer(priv->i2c, &msg, 1) != 1) { | |
116 | printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n", val_len); | |
b4756707 SY |
117 | rc = -EREMOTEIO; |
118 | break; | |
433c4864 | 119 | } |
4de2730a | 120 | } |
433c4864 | 121 | |
b4756707 SY |
122 | kfree(xfer_buf); |
123 | return rc; | |
4de2730a PB |
124 | } |
125 | ||
126 | // Initialisation sequences | |
127 | // LNABAND=3, NUM1=0x3C, DIV1=0x74, NUM2=0x1080, DIV2=0x49 | |
128 | static u8 mt2060_config1[] = { | |
129 | REG_LO1C1, | |
130 | 0x3F, 0x74, 0x00, 0x08, 0x93 | |
131 | }; | |
132 | ||
133 | // FMCG=2, GP2=0, GP1=0 | |
134 | static u8 mt2060_config2[] = { | |
135 | REG_MISC_CTRL, | |
136 | 0x20, 0x1E, 0x30, 0xff, 0x80, 0xff, 0x00, 0x2c, 0x42 | |
137 | }; | |
138 | ||
139 | // VGAG=3, V1CSE=1 | |
4de2730a PB |
140 | |
141 | #ifdef MT2060_SPURCHECK | |
142 | /* The function below calculates the frequency offset between the output frequency if2 | |
143 | and the closer cross modulation subcarrier between lo1 and lo2 up to the tenth harmonic */ | |
144 | static int mt2060_spurcalc(u32 lo1,u32 lo2,u32 if2) | |
145 | { | |
146 | int I,J; | |
147 | int dia,diamin,diff; | |
148 | diamin=1000000; | |
149 | for (I = 1; I < 10; I++) { | |
150 | J = ((2*I*lo1)/lo2+1)/2; | |
151 | diff = I*(int)lo1-J*(int)lo2; | |
152 | if (diff < 0) diff=-diff; | |
153 | dia = (diff-(int)if2); | |
154 | if (dia < 0) dia=-dia; | |
155 | if (diamin > dia) diamin=dia; | |
156 | } | |
157 | return diamin; | |
158 | } | |
159 | ||
160 | #define BANDWIDTH 4000 // kHz | |
161 | ||
162 | /* Calculates the frequency offset to add to avoid spurs. Returns 0 if no offset is needed */ | |
163 | static int mt2060_spurcheck(u32 lo1,u32 lo2,u32 if2) | |
164 | { | |
165 | u32 Spur,Sp1,Sp2; | |
166 | int I,J; | |
167 | I=0; | |
168 | J=1000; | |
169 | ||
170 | Spur=mt2060_spurcalc(lo1,lo2,if2); | |
171 | if (Spur < BANDWIDTH) { | |
172 | /* Potential spurs detected */ | |
173 | dprintk("Spurs before : f_lo1: %d f_lo2: %d (kHz)", | |
174 | (int)lo1,(int)lo2); | |
175 | I=1000; | |
176 | Sp1 = mt2060_spurcalc(lo1+I,lo2+I,if2); | |
177 | Sp2 = mt2060_spurcalc(lo1-I,lo2-I,if2); | |
178 | ||
179 | if (Sp1 < Sp2) { | |
180 | J=-J; I=-I; Spur=Sp2; | |
181 | } else | |
182 | Spur=Sp1; | |
183 | ||
184 | while (Spur < BANDWIDTH) { | |
185 | I += J; | |
186 | Spur = mt2060_spurcalc(lo1+I,lo2+I,if2); | |
187 | } | |
188 | dprintk("Spurs after : f_lo1: %d f_lo2: %d (kHz)", | |
189 | (int)(lo1+I),(int)(lo2+I)); | |
190 | } | |
191 | return I; | |
192 | } | |
193 | #endif | |
194 | ||
195 | #define IF2 36150 // IF2 frequency = 36.150 MHz | |
196 | #define FREF 16000 // Quartz oscillator 16 MHz | |
197 | ||
14d24d14 | 198 | static int mt2060_set_params(struct dvb_frontend *fe) |
4de2730a | 199 | { |
2676c258 | 200 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
46f73f93 | 201 | struct mt2060_priv *priv; |
4de2730a PB |
202 | int i=0; |
203 | u32 freq; | |
204 | u8 lnaband; | |
205 | u32 f_lo1,f_lo2; | |
206 | u32 div1,num1,div2,num2; | |
207 | u8 b[8]; | |
208 | u32 if1; | |
209 | ||
46f73f93 OD |
210 | priv = fe->tuner_priv; |
211 | ||
212 | if1 = priv->if1_freq; | |
4de2730a PB |
213 | b[0] = REG_LO1B1; |
214 | b[1] = 0xFF; | |
4de2730a | 215 | |
6e623433 AP |
216 | if (fe->ops.i2c_gate_ctrl) |
217 | fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ | |
218 | ||
46f73f93 | 219 | mt2060_writeregs(priv,b,2); |
4de2730a | 220 | |
2676c258 | 221 | freq = c->frequency / 1000; /* Hz -> kHz */ |
46f73f93 OD |
222 | |
223 | f_lo1 = freq + if1 * 1000; | |
224 | f_lo1 = (f_lo1 / 250) * 250; | |
225 | f_lo2 = f_lo1 - freq - IF2; | |
226 | // From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise | |
227 | f_lo2 = ((f_lo2 + 25) / 50) * 50; | |
228 | priv->frequency = (f_lo1 - f_lo2 - IF2) * 1000, | |
4de2730a PB |
229 | |
230 | #ifdef MT2060_SPURCHECK | |
231 | // LO-related spurs detection and correction | |
232 | num1 = mt2060_spurcheck(f_lo1,f_lo2,IF2); | |
233 | f_lo1 += num1; | |
234 | f_lo2 += num1; | |
235 | #endif | |
236 | //Frequency LO1 = 16MHz * (DIV1 + NUM1/64 ) | |
46f73f93 OD |
237 | num1 = f_lo1 / (FREF / 64); |
238 | div1 = num1 / 64; | |
239 | num1 &= 0x3f; | |
4de2730a PB |
240 | |
241 | // Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) | |
46f73f93 OD |
242 | num2 = f_lo2 * 64 / (FREF / 128); |
243 | div2 = num2 / 8192; | |
244 | num2 &= 0x1fff; | |
4de2730a PB |
245 | |
246 | if (freq <= 95000) lnaband = 0xB0; else | |
247 | if (freq <= 180000) lnaband = 0xA0; else | |
248 | if (freq <= 260000) lnaband = 0x90; else | |
249 | if (freq <= 335000) lnaband = 0x80; else | |
250 | if (freq <= 425000) lnaband = 0x70; else | |
251 | if (freq <= 480000) lnaband = 0x60; else | |
252 | if (freq <= 570000) lnaband = 0x50; else | |
253 | if (freq <= 645000) lnaband = 0x40; else | |
254 | if (freq <= 730000) lnaband = 0x30; else | |
255 | if (freq <= 810000) lnaband = 0x20; else lnaband = 0x10; | |
256 | ||
257 | b[0] = REG_LO1C1; | |
258 | b[1] = lnaband | ((num1 >>2) & 0x0F); | |
259 | b[2] = div1; | |
260 | b[3] = (num2 & 0x0F) | ((num1 & 3) << 4); | |
261 | b[4] = num2 >> 4; | |
262 | b[5] = ((num2 >>12) & 1) | (div2 << 1); | |
263 | ||
264 | dprintk("IF1: %dMHz",(int)if1); | |
46f73f93 OD |
265 | dprintk("PLL freq=%dkHz f_lo1=%dkHz f_lo2=%dkHz",(int)freq,(int)f_lo1,(int)f_lo2); |
266 | dprintk("PLL div1=%d num1=%d div2=%d num2=%d",(int)div1,(int)num1,(int)div2,(int)num2); | |
4de2730a PB |
267 | dprintk("PLL [1..5]: %2x %2x %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3],(int)b[4],(int)b[5]); |
268 | ||
46f73f93 | 269 | mt2060_writeregs(priv,b,6); |
4de2730a PB |
270 | |
271 | //Waits for pll lock or timeout | |
46f73f93 | 272 | i = 0; |
4de2730a | 273 | do { |
46f73f93 OD |
274 | mt2060_readreg(priv,REG_LO_STATUS,b); |
275 | if ((b[0] & 0x88)==0x88) | |
276 | break; | |
4de2730a PB |
277 | msleep(4); |
278 | i++; | |
279 | } while (i<10); | |
280 | ||
6e623433 AP |
281 | if (fe->ops.i2c_gate_ctrl) |
282 | fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ | |
283 | ||
4539fc5c | 284 | return 0; |
4de2730a | 285 | } |
4de2730a | 286 | |
46f73f93 | 287 | static void mt2060_calibrate(struct mt2060_priv *priv) |
4de2730a PB |
288 | { |
289 | u8 b = 0; | |
290 | int i = 0; | |
291 | ||
46f73f93 | 292 | if (mt2060_writeregs(priv,mt2060_config1,sizeof(mt2060_config1))) |
4de2730a | 293 | return; |
46f73f93 | 294 | if (mt2060_writeregs(priv,mt2060_config2,sizeof(mt2060_config2))) |
4de2730a PB |
295 | return; |
296 | ||
136cafbf PB |
297 | /* initialize the clock output */ |
298 | mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30); | |
299 | ||
4de2730a PB |
300 | do { |
301 | b |= (1 << 6); // FM1SS; | |
46f73f93 | 302 | mt2060_writereg(priv, REG_LO2C1,b); |
4de2730a PB |
303 | msleep(20); |
304 | ||
305 | if (i == 0) { | |
306 | b |= (1 << 7); // FM1CA; | |
46f73f93 | 307 | mt2060_writereg(priv, REG_LO2C1,b); |
4de2730a PB |
308 | b &= ~(1 << 7); // FM1CA; |
309 | msleep(20); | |
310 | } | |
311 | ||
312 | b &= ~(1 << 6); // FM1SS | |
46f73f93 | 313 | mt2060_writereg(priv, REG_LO2C1,b); |
4de2730a PB |
314 | |
315 | msleep(20); | |
316 | i++; | |
317 | } while (i < 9); | |
318 | ||
319 | i = 0; | |
46f73f93 | 320 | while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0) |
4de2730a PB |
321 | msleep(20); |
322 | ||
7d979a86 | 323 | if (i <= 10) { |
46f73f93 OD |
324 | mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :) |
325 | dprintk("calibration was successful: %d", (int)priv->fmfreq); | |
4de2730a PB |
326 | } else |
327 | dprintk("FMCAL timed out"); | |
328 | } | |
329 | ||
46f73f93 OD |
330 | static int mt2060_get_frequency(struct dvb_frontend *fe, u32 *frequency) |
331 | { | |
332 | struct mt2060_priv *priv = fe->tuner_priv; | |
333 | *frequency = priv->frequency; | |
334 | return 0; | |
335 | } | |
336 | ||
055327c5 AP |
337 | static int mt2060_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) |
338 | { | |
339 | *frequency = IF2 * 1000; | |
340 | return 0; | |
341 | } | |
342 | ||
294d83d7 | 343 | static int mt2060_init(struct dvb_frontend *fe) |
344 | { | |
345 | struct mt2060_priv *priv = fe->tuner_priv; | |
6e623433 AP |
346 | int ret; |
347 | ||
348 | if (fe->ops.i2c_gate_ctrl) | |
349 | fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ | |
350 | ||
e11415c8 AP |
351 | if (priv->sleep) { |
352 | ret = mt2060_writereg(priv, REG_MISC_CTRL, 0x20); | |
353 | if (ret) | |
354 | goto err_i2c_gate_ctrl; | |
355 | } | |
356 | ||
6e623433 AP |
357 | ret = mt2060_writereg(priv, REG_VGAG, |
358 | (priv->cfg->clock_out << 6) | 0x33); | |
359 | ||
e11415c8 | 360 | err_i2c_gate_ctrl: |
6e623433 AP |
361 | if (fe->ops.i2c_gate_ctrl) |
362 | fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ | |
363 | ||
364 | return ret; | |
294d83d7 | 365 | } |
366 | ||
46f73f93 OD |
367 | static int mt2060_sleep(struct dvb_frontend *fe) |
368 | { | |
369 | struct mt2060_priv *priv = fe->tuner_priv; | |
6e623433 AP |
370 | int ret; |
371 | ||
372 | if (fe->ops.i2c_gate_ctrl) | |
373 | fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ | |
374 | ||
375 | ret = mt2060_writereg(priv, REG_VGAG, | |
376 | (priv->cfg->clock_out << 6) | 0x30); | |
e11415c8 AP |
377 | if (ret) |
378 | goto err_i2c_gate_ctrl; | |
379 | ||
380 | if (priv->sleep) | |
381 | ret = mt2060_writereg(priv, REG_MISC_CTRL, 0xe8); | |
6e623433 | 382 | |
e11415c8 | 383 | err_i2c_gate_ctrl: |
6e623433 AP |
384 | if (fe->ops.i2c_gate_ctrl) |
385 | fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ | |
386 | ||
387 | return ret; | |
46f73f93 OD |
388 | } |
389 | ||
f2709c20 MCC |
390 | static void mt2060_release(struct dvb_frontend *fe) |
391 | { | |
392 | kfree(fe->tuner_priv); | |
393 | fe->tuner_priv = NULL; | |
394 | } | |
395 | ||
46f73f93 OD |
396 | static const struct dvb_tuner_ops mt2060_tuner_ops = { |
397 | .info = { | |
a3f90c75 MCC |
398 | .name = "Microtune MT2060", |
399 | .frequency_min_hz = 48 * MHz, | |
400 | .frequency_max_hz = 860 * MHz, | |
401 | .frequency_step_hz = 50 * kHz, | |
46f73f93 OD |
402 | }, |
403 | ||
f2709c20 | 404 | .release = mt2060_release, |
46f73f93 | 405 | |
294d83d7 | 406 | .init = mt2060_init, |
46f73f93 OD |
407 | .sleep = mt2060_sleep, |
408 | ||
409 | .set_params = mt2060_set_params, | |
46f73f93 | 410 | .get_frequency = mt2060_get_frequency, |
055327c5 | 411 | .get_if_frequency = mt2060_get_if_frequency, |
46f73f93 OD |
412 | }; |
413 | ||
4de2730a | 414 | /* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */ |
6958effe | 415 | struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1) |
4de2730a | 416 | { |
46f73f93 | 417 | struct mt2060_priv *priv = NULL; |
4de2730a | 418 | u8 id = 0; |
4de2730a | 419 | |
46f73f93 OD |
420 | priv = kzalloc(sizeof(struct mt2060_priv), GFP_KERNEL); |
421 | if (priv == NULL) | |
6958effe | 422 | return NULL; |
4de2730a | 423 | |
46f73f93 OD |
424 | priv->cfg = cfg; |
425 | priv->i2c = i2c; | |
426 | priv->if1_freq = if1; | |
433c4864 | 427 | priv->i2c_max_regs = ~0; |
4de2730a | 428 | |
6e623433 AP |
429 | if (fe->ops.i2c_gate_ctrl) |
430 | fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ | |
431 | ||
46f73f93 OD |
432 | if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) { |
433 | kfree(priv); | |
6958effe | 434 | return NULL; |
46f73f93 | 435 | } |
4de2730a | 436 | |
46f73f93 OD |
437 | if (id != PART_REV) { |
438 | kfree(priv); | |
6958effe | 439 | return NULL; |
46f73f93 | 440 | } |
b7571f8d | 441 | printk(KERN_INFO "MT2060: successfully identified (IF1 = %d)\n", if1); |
46f73f93 OD |
442 | memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops)); |
443 | ||
444 | fe->tuner_priv = priv; | |
4de2730a | 445 | |
46f73f93 | 446 | mt2060_calibrate(priv); |
4de2730a | 447 | |
6e623433 AP |
448 | if (fe->ops.i2c_gate_ctrl) |
449 | fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ | |
450 | ||
6958effe | 451 | return fe; |
4de2730a PB |
452 | } |
453 | EXPORT_SYMBOL(mt2060_attach); | |
454 | ||
59e8b7aa AP |
455 | static int mt2060_probe(struct i2c_client *client, |
456 | const struct i2c_device_id *id) | |
457 | { | |
458 | struct mt2060_platform_data *pdata = client->dev.platform_data; | |
459 | struct dvb_frontend *fe; | |
460 | struct mt2060_priv *dev; | |
461 | int ret; | |
462 | u8 chip_id; | |
463 | ||
464 | dev_dbg(&client->dev, "\n"); | |
465 | ||
466 | if (!pdata) { | |
467 | dev_err(&client->dev, "Cannot proceed without platform data\n"); | |
468 | ret = -EINVAL; | |
469 | goto err; | |
470 | } | |
471 | ||
472 | dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); | |
473 | if (!dev) { | |
474 | ret = -ENOMEM; | |
475 | goto err; | |
476 | } | |
477 | ||
478 | fe = pdata->dvb_frontend; | |
479 | dev->config.i2c_address = client->addr; | |
480 | dev->config.clock_out = pdata->clock_out; | |
481 | dev->cfg = &dev->config; | |
482 | dev->i2c = client->adapter; | |
483 | dev->if1_freq = pdata->if1 ? pdata->if1 : 1220; | |
484 | dev->client = client; | |
433c4864 | 485 | dev->i2c_max_regs = pdata->i2c_write_max ? pdata->i2c_write_max - 1 : ~0; |
e11415c8 | 486 | dev->sleep = true; |
59e8b7aa AP |
487 | |
488 | ret = mt2060_readreg(dev, REG_PART_REV, &chip_id); | |
489 | if (ret) { | |
490 | ret = -ENODEV; | |
491 | goto err; | |
492 | } | |
493 | ||
494 | dev_dbg(&client->dev, "chip id=%02x\n", chip_id); | |
495 | ||
496 | if (chip_id != PART_REV) { | |
497 | ret = -ENODEV; | |
498 | goto err; | |
499 | } | |
500 | ||
e11415c8 AP |
501 | /* Power on, calibrate, sleep */ |
502 | ret = mt2060_writereg(dev, REG_MISC_CTRL, 0x20); | |
503 | if (ret) | |
504 | goto err; | |
505 | mt2060_calibrate(dev); | |
506 | ret = mt2060_writereg(dev, REG_MISC_CTRL, 0xe8); | |
507 | if (ret) | |
508 | goto err; | |
509 | ||
59e8b7aa AP |
510 | dev_info(&client->dev, "Microtune MT2060 successfully identified\n"); |
511 | memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(fe->ops.tuner_ops)); | |
512 | fe->ops.tuner_ops.release = NULL; | |
513 | fe->tuner_priv = dev; | |
514 | i2c_set_clientdata(client, dev); | |
515 | ||
59e8b7aa AP |
516 | return 0; |
517 | err: | |
518 | dev_dbg(&client->dev, "failed=%d\n", ret); | |
519 | return ret; | |
520 | } | |
521 | ||
522 | static int mt2060_remove(struct i2c_client *client) | |
523 | { | |
524 | dev_dbg(&client->dev, "\n"); | |
525 | ||
526 | return 0; | |
527 | } | |
528 | ||
529 | static const struct i2c_device_id mt2060_id_table[] = { | |
530 | {"mt2060", 0}, | |
531 | {} | |
532 | }; | |
533 | MODULE_DEVICE_TABLE(i2c, mt2060_id_table); | |
534 | ||
535 | static struct i2c_driver mt2060_driver = { | |
536 | .driver = { | |
537 | .name = "mt2060", | |
538 | .suppress_bind_attrs = true, | |
539 | }, | |
540 | .probe = mt2060_probe, | |
541 | .remove = mt2060_remove, | |
542 | .id_table = mt2060_id_table, | |
543 | }; | |
544 | ||
545 | module_i2c_driver(mt2060_driver); | |
546 | ||
4de2730a PB |
547 | MODULE_AUTHOR("Olivier DANET"); |
548 | MODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver"); | |
549 | MODULE_LICENSE("GPL"); |