diff options
Diffstat (limited to 'drivers/media/tuners/m88ts2022.c')
-rw-r--r-- | drivers/media/tuners/m88ts2022.c | 579 |
1 files changed, 0 insertions, 579 deletions
diff --git a/drivers/media/tuners/m88ts2022.c b/drivers/media/tuners/m88ts2022.c deleted file mode 100644 index 066e5431da93..000000000000 --- a/drivers/media/tuners/m88ts2022.c +++ /dev/null | |||
@@ -1,579 +0,0 @@ | |||
1 | /* | ||
2 | * Montage M88TS2022 silicon tuner driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Antti Palosaari <crope@iki.fi> | ||
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 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * Some calculations are taken from existing TS2020 driver. | ||
17 | */ | ||
18 | |||
19 | #include "m88ts2022_priv.h" | ||
20 | |||
21 | static int m88ts2022_cmd(struct m88ts2022_dev *dev, int op, int sleep, u8 reg, | ||
22 | u8 mask, u8 val, u8 *reg_val) | ||
23 | { | ||
24 | int ret, i; | ||
25 | unsigned int utmp; | ||
26 | struct m88ts2022_reg_val reg_vals[] = { | ||
27 | {0x51, 0x1f - op}, | ||
28 | {0x51, 0x1f}, | ||
29 | {0x50, 0x00 + op}, | ||
30 | {0x50, 0x00}, | ||
31 | }; | ||
32 | |||
33 | for (i = 0; i < 2; i++) { | ||
34 | dev_dbg(&dev->client->dev, | ||
35 | "i=%d op=%02x reg=%02x mask=%02x val=%02x\n", | ||
36 | i, op, reg, mask, val); | ||
37 | |||
38 | for (i = 0; i < ARRAY_SIZE(reg_vals); i++) { | ||
39 | ret = regmap_write(dev->regmap, reg_vals[i].reg, | ||
40 | reg_vals[i].val); | ||
41 | if (ret) | ||
42 | goto err; | ||
43 | } | ||
44 | |||
45 | usleep_range(sleep * 1000, sleep * 10000); | ||
46 | |||
47 | ret = regmap_read(dev->regmap, reg, &utmp); | ||
48 | if (ret) | ||
49 | goto err; | ||
50 | |||
51 | if ((utmp & mask) != val) | ||
52 | break; | ||
53 | } | ||
54 | |||
55 | if (reg_val) | ||
56 | *reg_val = utmp; | ||
57 | err: | ||
58 | return ret; | ||
59 | } | ||
60 | |||
61 | static int m88ts2022_set_params(struct dvb_frontend *fe) | ||
62 | { | ||
63 | struct m88ts2022_dev *dev = fe->tuner_priv; | ||
64 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | ||
65 | int ret; | ||
66 | unsigned int utmp, frequency_khz, frequency_offset_khz, f_3db_hz; | ||
67 | unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n, gdiv28; | ||
68 | u8 buf[3], u8tmp, cap_code, lpf_gm, lpf_mxdiv, div_max, div_min; | ||
69 | u16 u16tmp; | ||
70 | |||
71 | dev_dbg(&dev->client->dev, | ||
72 | "frequency=%d symbol_rate=%d rolloff=%d\n", | ||
73 | c->frequency, c->symbol_rate, c->rolloff); | ||
74 | /* | ||
75 | * Integer-N PLL synthesizer | ||
76 | * kHz is used for all calculations to keep calculations within 32-bit | ||
77 | */ | ||
78 | f_ref_khz = DIV_ROUND_CLOSEST(dev->cfg.clock, 1000); | ||
79 | div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000); | ||
80 | |||
81 | if (c->symbol_rate < 5000000) | ||
82 | frequency_offset_khz = 3000; /* 3 MHz */ | ||
83 | else | ||
84 | frequency_offset_khz = 0; | ||
85 | |||
86 | frequency_khz = c->frequency + frequency_offset_khz; | ||
87 | |||
88 | if (frequency_khz < 1103000) { | ||
89 | div_out = 4; | ||
90 | u8tmp = 0x1b; | ||
91 | } else { | ||
92 | div_out = 2; | ||
93 | u8tmp = 0x0b; | ||
94 | } | ||
95 | |||
96 | buf[0] = u8tmp; | ||
97 | buf[1] = 0x40; | ||
98 | ret = regmap_bulk_write(dev->regmap, 0x10, buf, 2); | ||
99 | if (ret) | ||
100 | goto err; | ||
101 | |||
102 | f_vco_khz = frequency_khz * div_out; | ||
103 | pll_n = f_vco_khz * div_ref / f_ref_khz; | ||
104 | pll_n += pll_n % 2; | ||
105 | dev->frequency_khz = pll_n * f_ref_khz / div_ref / div_out; | ||
106 | |||
107 | if (pll_n < 4095) | ||
108 | u16tmp = pll_n - 1024; | ||
109 | else if (pll_n < 6143) | ||
110 | u16tmp = pll_n + 1024; | ||
111 | else | ||
112 | u16tmp = pll_n + 3072; | ||
113 | |||
114 | buf[0] = (u16tmp >> 8) & 0x3f; | ||
115 | buf[1] = (u16tmp >> 0) & 0xff; | ||
116 | buf[2] = div_ref - 8; | ||
117 | ret = regmap_bulk_write(dev->regmap, 0x01, buf, 3); | ||
118 | if (ret) | ||
119 | goto err; | ||
120 | |||
121 | dev_dbg(&dev->client->dev, | ||
122 | "frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n", | ||
123 | dev->frequency_khz, dev->frequency_khz - c->frequency, | ||
124 | f_vco_khz, pll_n, div_ref, div_out); | ||
125 | |||
126 | ret = m88ts2022_cmd(dev, 0x10, 5, 0x15, 0x40, 0x00, NULL); | ||
127 | if (ret) | ||
128 | goto err; | ||
129 | |||
130 | ret = regmap_read(dev->regmap, 0x14, &utmp); | ||
131 | if (ret) | ||
132 | goto err; | ||
133 | |||
134 | utmp &= 0x7f; | ||
135 | if (utmp < 64) { | ||
136 | ret = regmap_update_bits(dev->regmap, 0x10, 0x80, 0x80); | ||
137 | if (ret) | ||
138 | goto err; | ||
139 | |||
140 | ret = regmap_write(dev->regmap, 0x11, 0x6f); | ||
141 | if (ret) | ||
142 | goto err; | ||
143 | |||
144 | ret = m88ts2022_cmd(dev, 0x10, 5, 0x15, 0x40, 0x00, NULL); | ||
145 | if (ret) | ||
146 | goto err; | ||
147 | } | ||
148 | |||
149 | ret = regmap_read(dev->regmap, 0x14, &utmp); | ||
150 | if (ret) | ||
151 | goto err; | ||
152 | |||
153 | utmp &= 0x1f; | ||
154 | if (utmp > 19) { | ||
155 | ret = regmap_update_bits(dev->regmap, 0x10, 0x02, 0x00); | ||
156 | if (ret) | ||
157 | goto err; | ||
158 | } | ||
159 | |||
160 | ret = m88ts2022_cmd(dev, 0x08, 5, 0x3c, 0xff, 0x00, NULL); | ||
161 | if (ret) | ||
162 | goto err; | ||
163 | |||
164 | ret = regmap_write(dev->regmap, 0x25, 0x00); | ||
165 | if (ret) | ||
166 | goto err; | ||
167 | |||
168 | ret = regmap_write(dev->regmap, 0x27, 0x70); | ||
169 | if (ret) | ||
170 | goto err; | ||
171 | |||
172 | ret = regmap_write(dev->regmap, 0x41, 0x09); | ||
173 | if (ret) | ||
174 | goto err; | ||
175 | |||
176 | ret = regmap_write(dev->regmap, 0x08, 0x0b); | ||
177 | if (ret) | ||
178 | goto err; | ||
179 | |||
180 | /* filters */ | ||
181 | gdiv28 = DIV_ROUND_CLOSEST(f_ref_khz * 1694U, 1000000U); | ||
182 | |||
183 | ret = regmap_write(dev->regmap, 0x04, gdiv28); | ||
184 | if (ret) | ||
185 | goto err; | ||
186 | |||
187 | ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); | ||
188 | if (ret) | ||
189 | goto err; | ||
190 | |||
191 | cap_code = u8tmp & 0x3f; | ||
192 | |||
193 | ret = regmap_write(dev->regmap, 0x41, 0x0d); | ||
194 | if (ret) | ||
195 | goto err; | ||
196 | |||
197 | ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); | ||
198 | if (ret) | ||
199 | goto err; | ||
200 | |||
201 | u8tmp &= 0x3f; | ||
202 | cap_code = (cap_code + u8tmp) / 2; | ||
203 | gdiv28 = gdiv28 * 207 / (cap_code * 2 + 151); | ||
204 | div_max = gdiv28 * 135 / 100; | ||
205 | div_min = gdiv28 * 78 / 100; | ||
206 | div_max = clamp_val(div_max, 0U, 63U); | ||
207 | |||
208 | f_3db_hz = mult_frac(c->symbol_rate, 135, 200); | ||
209 | f_3db_hz += 2000000U + (frequency_offset_khz * 1000U); | ||
210 | f_3db_hz = clamp(f_3db_hz, 7000000U, 40000000U); | ||
211 | |||
212 | #define LPF_COEFF 3200U | ||
213 | lpf_gm = DIV_ROUND_CLOSEST(f_3db_hz * gdiv28, LPF_COEFF * f_ref_khz); | ||
214 | lpf_gm = clamp_val(lpf_gm, 1U, 23U); | ||
215 | |||
216 | lpf_mxdiv = DIV_ROUND_CLOSEST(lpf_gm * LPF_COEFF * f_ref_khz, f_3db_hz); | ||
217 | if (lpf_mxdiv < div_min) | ||
218 | lpf_mxdiv = DIV_ROUND_CLOSEST(++lpf_gm * LPF_COEFF * f_ref_khz, f_3db_hz); | ||
219 | lpf_mxdiv = clamp_val(lpf_mxdiv, 0U, div_max); | ||
220 | |||
221 | ret = regmap_write(dev->regmap, 0x04, lpf_mxdiv); | ||
222 | if (ret) | ||
223 | goto err; | ||
224 | |||
225 | ret = regmap_write(dev->regmap, 0x06, lpf_gm); | ||
226 | if (ret) | ||
227 | goto err; | ||
228 | |||
229 | ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); | ||
230 | if (ret) | ||
231 | goto err; | ||
232 | |||
233 | cap_code = u8tmp & 0x3f; | ||
234 | |||
235 | ret = regmap_write(dev->regmap, 0x41, 0x09); | ||
236 | if (ret) | ||
237 | goto err; | ||
238 | |||
239 | ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); | ||
240 | if (ret) | ||
241 | goto err; | ||
242 | |||
243 | u8tmp &= 0x3f; | ||
244 | cap_code = (cap_code + u8tmp) / 2; | ||
245 | |||
246 | u8tmp = cap_code | 0x80; | ||
247 | ret = regmap_write(dev->regmap, 0x25, u8tmp); | ||
248 | if (ret) | ||
249 | goto err; | ||
250 | |||
251 | ret = regmap_write(dev->regmap, 0x27, 0x30); | ||
252 | if (ret) | ||
253 | goto err; | ||
254 | |||
255 | ret = regmap_write(dev->regmap, 0x08, 0x09); | ||
256 | if (ret) | ||
257 | goto err; | ||
258 | |||
259 | ret = m88ts2022_cmd(dev, 0x01, 20, 0x21, 0xff, 0x00, NULL); | ||
260 | if (ret) | ||
261 | goto err; | ||
262 | err: | ||
263 | if (ret) | ||
264 | dev_dbg(&dev->client->dev, "failed=%d\n", ret); | ||
265 | |||
266 | return ret; | ||
267 | } | ||
268 | |||
269 | static int m88ts2022_init(struct dvb_frontend *fe) | ||
270 | { | ||
271 | struct m88ts2022_dev *dev = fe->tuner_priv; | ||
272 | int ret, i; | ||
273 | u8 u8tmp; | ||
274 | static const struct m88ts2022_reg_val reg_vals[] = { | ||
275 | {0x7d, 0x9d}, | ||
276 | {0x7c, 0x9a}, | ||
277 | {0x7a, 0x76}, | ||
278 | {0x3b, 0x01}, | ||
279 | {0x63, 0x88}, | ||
280 | {0x61, 0x85}, | ||
281 | {0x22, 0x30}, | ||
282 | {0x30, 0x40}, | ||
283 | {0x20, 0x23}, | ||
284 | {0x24, 0x02}, | ||
285 | {0x12, 0xa0}, | ||
286 | }; | ||
287 | |||
288 | dev_dbg(&dev->client->dev, "\n"); | ||
289 | |||
290 | ret = regmap_write(dev->regmap, 0x00, 0x01); | ||
291 | if (ret) | ||
292 | goto err; | ||
293 | |||
294 | ret = regmap_write(dev->regmap, 0x00, 0x03); | ||
295 | if (ret) | ||
296 | goto err; | ||
297 | |||
298 | switch (dev->cfg.clock_out) { | ||
299 | case M88TS2022_CLOCK_OUT_DISABLED: | ||
300 | u8tmp = 0x60; | ||
301 | break; | ||
302 | case M88TS2022_CLOCK_OUT_ENABLED: | ||
303 | u8tmp = 0x70; | ||
304 | ret = regmap_write(dev->regmap, 0x05, dev->cfg.clock_out_div); | ||
305 | if (ret) | ||
306 | goto err; | ||
307 | break; | ||
308 | case M88TS2022_CLOCK_OUT_ENABLED_XTALOUT: | ||
309 | u8tmp = 0x6c; | ||
310 | break; | ||
311 | default: | ||
312 | goto err; | ||
313 | } | ||
314 | |||
315 | ret = regmap_write(dev->regmap, 0x42, u8tmp); | ||
316 | if (ret) | ||
317 | goto err; | ||
318 | |||
319 | if (dev->cfg.loop_through) | ||
320 | u8tmp = 0xec; | ||
321 | else | ||
322 | u8tmp = 0x6c; | ||
323 | |||
324 | ret = regmap_write(dev->regmap, 0x62, u8tmp); | ||
325 | if (ret) | ||
326 | goto err; | ||
327 | |||
328 | for (i = 0; i < ARRAY_SIZE(reg_vals); i++) { | ||
329 | ret = regmap_write(dev->regmap, reg_vals[i].reg, reg_vals[i].val); | ||
330 | if (ret) | ||
331 | goto err; | ||
332 | } | ||
333 | err: | ||
334 | if (ret) | ||
335 | dev_dbg(&dev->client->dev, "failed=%d\n", ret); | ||
336 | return ret; | ||
337 | } | ||
338 | |||
339 | static int m88ts2022_sleep(struct dvb_frontend *fe) | ||
340 | { | ||
341 | struct m88ts2022_dev *dev = fe->tuner_priv; | ||
342 | int ret; | ||
343 | |||
344 | dev_dbg(&dev->client->dev, "\n"); | ||
345 | |||
346 | ret = regmap_write(dev->regmap, 0x00, 0x00); | ||
347 | if (ret) | ||
348 | goto err; | ||
349 | err: | ||
350 | if (ret) | ||
351 | dev_dbg(&dev->client->dev, "failed=%d\n", ret); | ||
352 | return ret; | ||
353 | } | ||
354 | |||
355 | static int m88ts2022_get_frequency(struct dvb_frontend *fe, u32 *frequency) | ||
356 | { | ||
357 | struct m88ts2022_dev *dev = fe->tuner_priv; | ||
358 | |||
359 | dev_dbg(&dev->client->dev, "\n"); | ||
360 | |||
361 | *frequency = dev->frequency_khz; | ||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static int m88ts2022_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) | ||
366 | { | ||
367 | struct m88ts2022_dev *dev = fe->tuner_priv; | ||
368 | |||
369 | dev_dbg(&dev->client->dev, "\n"); | ||
370 | |||
371 | *frequency = 0; /* Zero-IF */ | ||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | static int m88ts2022_get_rf_strength(struct dvb_frontend *fe, u16 *strength) | ||
376 | { | ||
377 | struct m88ts2022_dev *dev = fe->tuner_priv; | ||
378 | int ret; | ||
379 | u16 gain, u16tmp; | ||
380 | unsigned int utmp, gain1, gain2, gain3; | ||
381 | |||
382 | ret = regmap_read(dev->regmap, 0x3d, &utmp); | ||
383 | if (ret) | ||
384 | goto err; | ||
385 | |||
386 | gain1 = (utmp >> 0) & 0x1f; | ||
387 | gain1 = clamp(gain1, 0U, 15U); | ||
388 | |||
389 | ret = regmap_read(dev->regmap, 0x21, &utmp); | ||
390 | if (ret) | ||
391 | goto err; | ||
392 | |||
393 | gain2 = (utmp >> 0) & 0x1f; | ||
394 | gain2 = clamp(gain2, 2U, 16U); | ||
395 | |||
396 | ret = regmap_read(dev->regmap, 0x66, &utmp); | ||
397 | if (ret) | ||
398 | goto err; | ||
399 | |||
400 | gain3 = (utmp >> 3) & 0x07; | ||
401 | gain3 = clamp(gain3, 0U, 6U); | ||
402 | |||
403 | gain = gain1 * 265 + gain2 * 338 + gain3 * 285; | ||
404 | |||
405 | /* scale value to 0x0000-0xffff */ | ||
406 | u16tmp = (0xffff - gain); | ||
407 | u16tmp = clamp_val(u16tmp, 59000U, 61500U); | ||
408 | |||
409 | *strength = (u16tmp - 59000) * 0xffff / (61500 - 59000); | ||
410 | err: | ||
411 | if (ret) | ||
412 | dev_dbg(&dev->client->dev, "failed=%d\n", ret); | ||
413 | return ret; | ||
414 | } | ||
415 | |||
416 | static const struct dvb_tuner_ops m88ts2022_tuner_ops = { | ||
417 | .info = { | ||
418 | .name = "Montage M88TS2022", | ||
419 | .frequency_min = 950000, | ||
420 | .frequency_max = 2150000, | ||
421 | }, | ||
422 | |||
423 | .init = m88ts2022_init, | ||
424 | .sleep = m88ts2022_sleep, | ||
425 | .set_params = m88ts2022_set_params, | ||
426 | |||
427 | .get_frequency = m88ts2022_get_frequency, | ||
428 | .get_if_frequency = m88ts2022_get_if_frequency, | ||
429 | .get_rf_strength = m88ts2022_get_rf_strength, | ||
430 | }; | ||
431 | |||
432 | static int m88ts2022_probe(struct i2c_client *client, | ||
433 | const struct i2c_device_id *id) | ||
434 | { | ||
435 | struct m88ts2022_config *cfg = client->dev.platform_data; | ||
436 | struct dvb_frontend *fe = cfg->fe; | ||
437 | struct m88ts2022_dev *dev; | ||
438 | int ret; | ||
439 | u8 u8tmp; | ||
440 | unsigned int utmp; | ||
441 | static const struct regmap_config regmap_config = { | ||
442 | .reg_bits = 8, | ||
443 | .val_bits = 8, | ||
444 | }; | ||
445 | |||
446 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||
447 | if (!dev) { | ||
448 | ret = -ENOMEM; | ||
449 | dev_err(&client->dev, "kzalloc() failed\n"); | ||
450 | goto err; | ||
451 | } | ||
452 | |||
453 | memcpy(&dev->cfg, cfg, sizeof(struct m88ts2022_config)); | ||
454 | dev->client = client; | ||
455 | dev->regmap = devm_regmap_init_i2c(client, ®map_config); | ||
456 | if (IS_ERR(dev->regmap)) { | ||
457 | ret = PTR_ERR(dev->regmap); | ||
458 | goto err; | ||
459 | } | ||
460 | |||
461 | /* check if the tuner is there */ | ||
462 | ret = regmap_read(dev->regmap, 0x00, &utmp); | ||
463 | if (ret) | ||
464 | goto err; | ||
465 | |||
466 | if ((utmp & 0x03) == 0x00) { | ||
467 | ret = regmap_write(dev->regmap, 0x00, 0x01); | ||
468 | if (ret) | ||
469 | goto err; | ||
470 | |||
471 | usleep_range(2000, 50000); | ||
472 | } | ||
473 | |||
474 | ret = regmap_write(dev->regmap, 0x00, 0x03); | ||
475 | if (ret) | ||
476 | goto err; | ||
477 | |||
478 | usleep_range(2000, 50000); | ||
479 | |||
480 | ret = regmap_read(dev->regmap, 0x00, &utmp); | ||
481 | if (ret) | ||
482 | goto err; | ||
483 | |||
484 | dev_dbg(&dev->client->dev, "chip_id=%02x\n", utmp); | ||
485 | |||
486 | switch (utmp) { | ||
487 | case 0xc3: | ||
488 | case 0x83: | ||
489 | break; | ||
490 | default: | ||
491 | ret = -ENODEV; | ||
492 | goto err; | ||
493 | } | ||
494 | |||
495 | switch (dev->cfg.clock_out) { | ||
496 | case M88TS2022_CLOCK_OUT_DISABLED: | ||
497 | u8tmp = 0x60; | ||
498 | break; | ||
499 | case M88TS2022_CLOCK_OUT_ENABLED: | ||
500 | u8tmp = 0x70; | ||
501 | ret = regmap_write(dev->regmap, 0x05, dev->cfg.clock_out_div); | ||
502 | if (ret) | ||
503 | goto err; | ||
504 | break; | ||
505 | case M88TS2022_CLOCK_OUT_ENABLED_XTALOUT: | ||
506 | u8tmp = 0x6c; | ||
507 | break; | ||
508 | default: | ||
509 | ret = -EINVAL; | ||
510 | goto err; | ||
511 | } | ||
512 | |||
513 | ret = regmap_write(dev->regmap, 0x42, u8tmp); | ||
514 | if (ret) | ||
515 | goto err; | ||
516 | |||
517 | if (dev->cfg.loop_through) | ||
518 | u8tmp = 0xec; | ||
519 | else | ||
520 | u8tmp = 0x6c; | ||
521 | |||
522 | ret = regmap_write(dev->regmap, 0x62, u8tmp); | ||
523 | if (ret) | ||
524 | goto err; | ||
525 | |||
526 | /* sleep */ | ||
527 | ret = regmap_write(dev->regmap, 0x00, 0x00); | ||
528 | if (ret) | ||
529 | goto err; | ||
530 | |||
531 | dev_info(&dev->client->dev, "Montage M88TS2022 successfully identified\n"); | ||
532 | |||
533 | fe->tuner_priv = dev; | ||
534 | memcpy(&fe->ops.tuner_ops, &m88ts2022_tuner_ops, | ||
535 | sizeof(struct dvb_tuner_ops)); | ||
536 | |||
537 | i2c_set_clientdata(client, dev); | ||
538 | return 0; | ||
539 | err: | ||
540 | dev_dbg(&client->dev, "failed=%d\n", ret); | ||
541 | kfree(dev); | ||
542 | return ret; | ||
543 | } | ||
544 | |||
545 | static int m88ts2022_remove(struct i2c_client *client) | ||
546 | { | ||
547 | struct m88ts2022_dev *dev = i2c_get_clientdata(client); | ||
548 | struct dvb_frontend *fe = dev->cfg.fe; | ||
549 | |||
550 | dev_dbg(&client->dev, "\n"); | ||
551 | |||
552 | memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); | ||
553 | fe->tuner_priv = NULL; | ||
554 | kfree(dev); | ||
555 | |||
556 | return 0; | ||
557 | } | ||
558 | |||
559 | static const struct i2c_device_id m88ts2022_id[] = { | ||
560 | {"m88ts2022", 0}, | ||
561 | {} | ||
562 | }; | ||
563 | MODULE_DEVICE_TABLE(i2c, m88ts2022_id); | ||
564 | |||
565 | static struct i2c_driver m88ts2022_driver = { | ||
566 | .driver = { | ||
567 | .owner = THIS_MODULE, | ||
568 | .name = "m88ts2022", | ||
569 | }, | ||
570 | .probe = m88ts2022_probe, | ||
571 | .remove = m88ts2022_remove, | ||
572 | .id_table = m88ts2022_id, | ||
573 | }; | ||
574 | |||
575 | module_i2c_driver(m88ts2022_driver); | ||
576 | |||
577 | MODULE_DESCRIPTION("Montage M88TS2022 silicon tuner driver"); | ||
578 | MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); | ||
579 | MODULE_LICENSE("GPL"); | ||