1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | DVB device driver for cx231xx |
4 | |
5 | Copyright (C) 2008 <srinivasa.deevi at conexant dot com> |
6 | Based on em28xx driver |
7 | |
8 | */ |
9 | |
10 | #include "cx231xx.h" |
11 | #include <linux/kernel.h> |
12 | #include <linux/slab.h> |
13 | |
14 | #include <media/dvbdev.h> |
15 | #include <media/dmxdev.h> |
16 | #include <media/dvb_demux.h> |
17 | #include <media/dvb_net.h> |
18 | #include <media/dvb_frontend.h> |
19 | #include <media/v4l2-common.h> |
20 | #include <media/tuner.h> |
21 | |
22 | #include "xc5000.h" |
23 | #include "s5h1432.h" |
24 | #include "tda18271.h" |
25 | #include "s5h1411.h" |
26 | #include "lgdt3305.h" |
27 | #include "si2165.h" |
28 | #include "si2168.h" |
29 | #include "mb86a20s.h" |
30 | #include "si2157.h" |
31 | #include "lgdt3306a.h" |
32 | #include "r820t.h" |
33 | #include "mn88473.h" |
34 | |
35 | MODULE_DESCRIPTION("driver for cx231xx based DVB cards" ); |
36 | MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>" ); |
37 | MODULE_LICENSE("GPL" ); |
38 | |
39 | static unsigned int debug; |
40 | module_param(debug, int, 0644); |
41 | MODULE_PARM_DESC(debug, "enable debug messages [dvb]" ); |
42 | |
43 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
44 | |
45 | #define CX231XX_DVB_NUM_BUFS 5 |
46 | #define CX231XX_DVB_MAX_PACKETSIZE 564 |
47 | #define CX231XX_DVB_MAX_PACKETS 64 |
48 | #define CX231XX_DVB_MAX_FRONTENDS 2 |
49 | |
50 | struct cx231xx_dvb { |
51 | struct dvb_frontend *frontend[CX231XX_DVB_MAX_FRONTENDS]; |
52 | |
53 | /* feed count management */ |
54 | struct mutex lock; |
55 | int nfeeds; |
56 | |
57 | /* general boilerplate stuff */ |
58 | struct dvb_adapter adapter; |
59 | struct dvb_demux demux; |
60 | struct dmxdev dmxdev; |
61 | struct dmx_frontend fe_hw; |
62 | struct dmx_frontend fe_mem; |
63 | struct dvb_net net; |
64 | struct i2c_client *i2c_client_demod[2]; |
65 | struct i2c_client *i2c_client_tuner; |
66 | }; |
67 | |
68 | static struct s5h1432_config dvico_s5h1432_config = { |
69 | .output_mode = S5H1432_SERIAL_OUTPUT, |
70 | .gpio = S5H1432_GPIO_ON, |
71 | .qam_if = S5H1432_IF_4000, |
72 | .vsb_if = S5H1432_IF_4000, |
73 | .inversion = S5H1432_INVERSION_OFF, |
74 | .status_mode = S5H1432_DEMODLOCKING, |
75 | .mpeg_timing = S5H1432_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, |
76 | }; |
77 | |
78 | static struct tda18271_std_map cnxt_rde253s_tda18271_std_map = { |
79 | .dvbt_6 = { .if_freq = 4000, .agc_mode = 3, .std = 4, |
80 | .if_lvl = 1, .rfagc_top = 0x37, }, |
81 | .dvbt_7 = { .if_freq = 4000, .agc_mode = 3, .std = 5, |
82 | .if_lvl = 1, .rfagc_top = 0x37, }, |
83 | .dvbt_8 = { .if_freq = 4000, .agc_mode = 3, .std = 6, |
84 | .if_lvl = 1, .rfagc_top = 0x37, }, |
85 | }; |
86 | |
87 | static struct tda18271_std_map mb86a20s_tda18271_config = { |
88 | .dvbt_6 = { .if_freq = 4000, .agc_mode = 3, .std = 4, |
89 | .if_lvl = 0, .rfagc_top = 0x37, }, |
90 | }; |
91 | |
92 | static struct tda18271_config cnxt_rde253s_tunerconfig = { |
93 | .std_map = &cnxt_rde253s_tda18271_std_map, |
94 | .gate = TDA18271_GATE_ANALOG, |
95 | }; |
96 | |
97 | static struct s5h1411_config tda18271_s5h1411_config = { |
98 | .output_mode = S5H1411_SERIAL_OUTPUT, |
99 | .gpio = S5H1411_GPIO_OFF, |
100 | .vsb_if = S5H1411_IF_3250, |
101 | .qam_if = S5H1411_IF_4000, |
102 | .inversion = S5H1411_INVERSION_ON, |
103 | .status_mode = S5H1411_DEMODLOCKING, |
104 | .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, |
105 | }; |
106 | static struct s5h1411_config xc5000_s5h1411_config = { |
107 | .output_mode = S5H1411_SERIAL_OUTPUT, |
108 | .gpio = S5H1411_GPIO_OFF, |
109 | .vsb_if = S5H1411_IF_3250, |
110 | .qam_if = S5H1411_IF_3250, |
111 | .inversion = S5H1411_INVERSION_OFF, |
112 | .status_mode = S5H1411_DEMODLOCKING, |
113 | .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, |
114 | }; |
115 | |
116 | static struct lgdt3305_config hcw_lgdt3305_config = { |
117 | .i2c_addr = 0x0e, |
118 | .mpeg_mode = LGDT3305_MPEG_SERIAL, |
119 | .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE, |
120 | .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, |
121 | .deny_i2c_rptr = 1, |
122 | .spectral_inversion = 1, |
123 | .qam_if_khz = 4000, |
124 | .vsb_if_khz = 3250, |
125 | }; |
126 | |
127 | static struct tda18271_std_map hauppauge_tda18271_std_map = { |
128 | .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4, |
129 | .if_lvl = 1, .rfagc_top = 0x58, }, |
130 | .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5, |
131 | .if_lvl = 1, .rfagc_top = 0x58, }, |
132 | }; |
133 | |
134 | static struct tda18271_config hcw_tda18271_config = { |
135 | .std_map = &hauppauge_tda18271_std_map, |
136 | .gate = TDA18271_GATE_DIGITAL, |
137 | }; |
138 | |
139 | static const struct mb86a20s_config pv_mb86a20s_config = { |
140 | .demod_address = 0x10, |
141 | .is_serial = true, |
142 | }; |
143 | |
144 | static struct tda18271_config pv_tda18271_config = { |
145 | .std_map = &mb86a20s_tda18271_config, |
146 | .gate = TDA18271_GATE_DIGITAL, |
147 | .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, |
148 | }; |
149 | |
150 | static const struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = { |
151 | .qam_if_khz = 4000, |
152 | .vsb_if_khz = 3250, |
153 | .spectral_inversion = 1, |
154 | .mpeg_mode = LGDT3306A_MPEG_SERIAL, |
155 | .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE, |
156 | .tpvalid_polarity = LGDT3306A_TP_VALID_HIGH, |
157 | .xtalMHz = 25, |
158 | }; |
159 | |
160 | static struct r820t_config astrometa_t2hybrid_r820t_config = { |
161 | .i2c_addr = 0x3a, /* 0x74 >> 1 */ |
162 | .xtal = 16000000, |
163 | .rafael_chip = CHIP_R828D, |
164 | .max_i2c_msg_len = 2, |
165 | }; |
166 | |
167 | static inline void print_err_status(struct cx231xx *dev, int packet, int status) |
168 | { |
169 | char *errmsg = "Unknown" ; |
170 | |
171 | switch (status) { |
172 | case -ENOENT: |
173 | errmsg = "unlinked synchronously" ; |
174 | break; |
175 | case -ECONNRESET: |
176 | errmsg = "unlinked asynchronously" ; |
177 | break; |
178 | case -ENOSR: |
179 | errmsg = "Buffer error (overrun)" ; |
180 | break; |
181 | case -EPIPE: |
182 | errmsg = "Stalled (device not responding)" ; |
183 | break; |
184 | case -EOVERFLOW: |
185 | errmsg = "Babble (bad cable?)" ; |
186 | break; |
187 | case -EPROTO: |
188 | errmsg = "Bit-stuff error (bad cable?)" ; |
189 | break; |
190 | case -EILSEQ: |
191 | errmsg = "CRC/Timeout (could be anything)" ; |
192 | break; |
193 | case -ETIME: |
194 | errmsg = "Device does not respond" ; |
195 | break; |
196 | } |
197 | if (packet < 0) { |
198 | dev_dbg(dev->dev, |
199 | "URB status %d [%s].\n" , status, errmsg); |
200 | } else { |
201 | dev_dbg(dev->dev, |
202 | "URB packet %d, status %d [%s].\n" , |
203 | packet, status, errmsg); |
204 | } |
205 | } |
206 | |
207 | static inline int dvb_isoc_copy(struct cx231xx *dev, struct urb *urb) |
208 | { |
209 | int i; |
210 | |
211 | if (!dev) |
212 | return 0; |
213 | |
214 | if (dev->state & DEV_DISCONNECTED) |
215 | return 0; |
216 | |
217 | if (urb->status < 0) { |
218 | print_err_status(dev, packet: -1, status: urb->status); |
219 | if (urb->status == -ENOENT) |
220 | return 0; |
221 | } |
222 | |
223 | for (i = 0; i < urb->number_of_packets; i++) { |
224 | int status = urb->iso_frame_desc[i].status; |
225 | |
226 | if (status < 0) { |
227 | print_err_status(dev, packet: i, status); |
228 | if (urb->iso_frame_desc[i].status != -EPROTO) |
229 | continue; |
230 | } |
231 | |
232 | dvb_dmx_swfilter(demux: &dev->dvb->demux, |
233 | buf: urb->transfer_buffer + |
234 | urb->iso_frame_desc[i].offset, |
235 | count: urb->iso_frame_desc[i].actual_length); |
236 | } |
237 | |
238 | return 0; |
239 | } |
240 | |
241 | static inline int dvb_bulk_copy(struct cx231xx *dev, struct urb *urb) |
242 | { |
243 | if (!dev) |
244 | return 0; |
245 | |
246 | if (dev->state & DEV_DISCONNECTED) |
247 | return 0; |
248 | |
249 | if (urb->status < 0) { |
250 | print_err_status(dev, packet: -1, status: urb->status); |
251 | if (urb->status == -ENOENT) |
252 | return 0; |
253 | } |
254 | |
255 | /* Feed the transport payload into the kernel demux */ |
256 | dvb_dmx_swfilter(demux: &dev->dvb->demux, |
257 | buf: urb->transfer_buffer, count: urb->actual_length); |
258 | |
259 | return 0; |
260 | } |
261 | |
262 | static int start_streaming(struct cx231xx_dvb *dvb) |
263 | { |
264 | int rc; |
265 | struct cx231xx *dev = dvb->adapter.priv; |
266 | |
267 | if (dev->USE_ISO) { |
268 | dev_dbg(dev->dev, "DVB transfer mode is ISO.\n" ); |
269 | cx231xx_set_alt_setting(dev, index: INDEX_TS1, alt: 5); |
270 | rc = cx231xx_set_mode(dev, set_mode: CX231XX_DIGITAL_MODE); |
271 | if (rc < 0) |
272 | return rc; |
273 | dev->mode_tv = 1; |
274 | return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS, |
275 | CX231XX_DVB_NUM_BUFS, |
276 | max_pkt_size: dev->ts1_mode.max_pkt_size, |
277 | isoc_copy: dvb_isoc_copy); |
278 | } else { |
279 | dev_dbg(dev->dev, "DVB transfer mode is BULK.\n" ); |
280 | cx231xx_set_alt_setting(dev, index: INDEX_TS1, alt: 0); |
281 | rc = cx231xx_set_mode(dev, set_mode: CX231XX_DIGITAL_MODE); |
282 | if (rc < 0) |
283 | return rc; |
284 | dev->mode_tv = 1; |
285 | return cx231xx_init_bulk(dev, CX231XX_DVB_MAX_PACKETS, |
286 | CX231XX_DVB_NUM_BUFS, |
287 | max_pkt_size: dev->ts1_mode.max_pkt_size, |
288 | bulk_copy: dvb_bulk_copy); |
289 | } |
290 | |
291 | } |
292 | |
293 | static int stop_streaming(struct cx231xx_dvb *dvb) |
294 | { |
295 | struct cx231xx *dev = dvb->adapter.priv; |
296 | |
297 | if (dev->USE_ISO) |
298 | cx231xx_uninit_isoc(dev); |
299 | else |
300 | cx231xx_uninit_bulk(dev); |
301 | |
302 | cx231xx_set_mode(dev, set_mode: CX231XX_SUSPEND); |
303 | |
304 | return 0; |
305 | } |
306 | |
307 | static int start_feed(struct dvb_demux_feed *feed) |
308 | { |
309 | struct dvb_demux *demux = feed->demux; |
310 | struct cx231xx_dvb *dvb = demux->priv; |
311 | int rc, ret; |
312 | |
313 | if (!demux->dmx.frontend) |
314 | return -EINVAL; |
315 | |
316 | mutex_lock(&dvb->lock); |
317 | dvb->nfeeds++; |
318 | rc = dvb->nfeeds; |
319 | |
320 | if (dvb->nfeeds == 1) { |
321 | ret = start_streaming(dvb); |
322 | if (ret < 0) |
323 | rc = ret; |
324 | } |
325 | |
326 | mutex_unlock(lock: &dvb->lock); |
327 | return rc; |
328 | } |
329 | |
330 | static int stop_feed(struct dvb_demux_feed *feed) |
331 | { |
332 | struct dvb_demux *demux = feed->demux; |
333 | struct cx231xx_dvb *dvb = demux->priv; |
334 | int err = 0; |
335 | |
336 | mutex_lock(&dvb->lock); |
337 | dvb->nfeeds--; |
338 | |
339 | if (0 == dvb->nfeeds) |
340 | err = stop_streaming(dvb); |
341 | |
342 | mutex_unlock(lock: &dvb->lock); |
343 | return err; |
344 | } |
345 | |
346 | /* ------------------------------------------------------------------ */ |
347 | static int cx231xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) |
348 | { |
349 | struct cx231xx *dev = fe->dvb->priv; |
350 | |
351 | if (acquire) |
352 | return cx231xx_set_mode(dev, set_mode: CX231XX_DIGITAL_MODE); |
353 | else |
354 | return cx231xx_set_mode(dev, set_mode: CX231XX_SUSPEND); |
355 | } |
356 | |
357 | /* ------------------------------------------------------------------ */ |
358 | |
359 | static struct xc5000_config cnxt_rde250_tunerconfig = { |
360 | .i2c_address = 0x61, |
361 | .if_khz = 4000, |
362 | }; |
363 | static struct xc5000_config cnxt_rdu250_tunerconfig = { |
364 | .i2c_address = 0x61, |
365 | .if_khz = 3250, |
366 | }; |
367 | |
368 | /* ------------------------------------------------------------------ */ |
369 | #if 0 |
370 | static int attach_xc5000(u8 addr, struct cx231xx *dev) |
371 | { |
372 | |
373 | struct dvb_frontend *fe; |
374 | struct xc5000_config cfg; |
375 | |
376 | memset(&cfg, 0, sizeof(cfg)); |
377 | cfg.i2c_adap = cx231xx_get_i2c_adap(dev, dev->board.tuner_i2c_master); |
378 | cfg.i2c_addr = addr; |
379 | |
380 | if (!dev->dvb->frontend[0]) { |
381 | dev_err(dev->dev, "%s/2: dvb frontend not attached. Can't attach xc5000\n" , |
382 | dev->name); |
383 | return -EINVAL; |
384 | } |
385 | |
386 | fe = dvb_attach(xc5000_attach, dev->dvb->frontend[0], &cfg); |
387 | if (!fe) { |
388 | dev_err(dev->dev, "%s/2: xc5000 attach failed\n" , dev->name); |
389 | dvb_frontend_detach(dev->dvb->frontend[0]); |
390 | dev->dvb->frontend[0] = NULL; |
391 | return -EINVAL; |
392 | } |
393 | |
394 | dev_info(dev->dev, "%s/2: xc5000 attached\n" , dev->name); |
395 | |
396 | return 0; |
397 | } |
398 | #endif |
399 | |
400 | int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq) |
401 | { |
402 | if (dev->dvb && dev->dvb->frontend[0]) { |
403 | |
404 | struct dvb_tuner_ops *dops = &dev->dvb->frontend[0]->ops.tuner_ops; |
405 | |
406 | if (dops->set_analog_params != NULL) { |
407 | struct analog_parameters params; |
408 | |
409 | params.frequency = freq; |
410 | params.std = dev->norm; |
411 | params.mode = 0; /* 0- Air; 1 - cable */ |
412 | /*params.audmode = ; */ |
413 | |
414 | /* Set the analog parameters to set the frequency */ |
415 | dops->set_analog_params(dev->dvb->frontend[0], ¶ms); |
416 | } |
417 | |
418 | } |
419 | |
420 | return 0; |
421 | } |
422 | |
423 | int cx231xx_reset_analog_tuner(struct cx231xx *dev) |
424 | { |
425 | int status = 0; |
426 | |
427 | if (dev->dvb && dev->dvb->frontend[0]) { |
428 | |
429 | struct dvb_tuner_ops *dops = &dev->dvb->frontend[0]->ops.tuner_ops; |
430 | |
431 | if (dops->init != NULL && !dev->xc_fw_load_done) { |
432 | |
433 | dev_dbg(dev->dev, |
434 | "Reloading firmware for XC5000\n" ); |
435 | status = dops->init(dev->dvb->frontend[0]); |
436 | if (status == 0) { |
437 | dev->xc_fw_load_done = 1; |
438 | dev_dbg(dev->dev, |
439 | "XC5000 firmware download completed\n" ); |
440 | } else { |
441 | dev->xc_fw_load_done = 0; |
442 | dev_dbg(dev->dev, |
443 | "XC5000 firmware download failed !!!\n" ); |
444 | } |
445 | } |
446 | |
447 | } |
448 | |
449 | return status; |
450 | } |
451 | |
452 | /* ------------------------------------------------------------------ */ |
453 | |
454 | static int register_dvb(struct cx231xx_dvb *dvb, |
455 | struct module *module, |
456 | struct cx231xx *dev, struct device *device) |
457 | { |
458 | int result; |
459 | |
460 | mutex_init(&dvb->lock); |
461 | |
462 | |
463 | /* register adapter */ |
464 | result = dvb_register_adapter(adap: &dvb->adapter, name: dev->name, module, device, |
465 | adapter_nums: adapter_nr); |
466 | if (result < 0) { |
467 | dev_warn(dev->dev, |
468 | "%s: dvb_register_adapter failed (errno = %d)\n" , |
469 | dev->name, result); |
470 | goto fail_adapter; |
471 | } |
472 | dvb_register_media_controller(adap: &dvb->adapter, mdev: dev->media_dev); |
473 | |
474 | /* Ensure all frontends negotiate bus access */ |
475 | dvb->frontend[0]->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl; |
476 | if (dvb->frontend[1]) |
477 | dvb->frontend[1]->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl; |
478 | |
479 | dvb->adapter.priv = dev; |
480 | |
481 | /* register frontend */ |
482 | result = dvb_register_frontend(dvb: &dvb->adapter, fe: dvb->frontend[0]); |
483 | if (result < 0) { |
484 | dev_warn(dev->dev, |
485 | "%s: dvb_register_frontend failed (errno = %d)\n" , |
486 | dev->name, result); |
487 | goto fail_frontend0; |
488 | } |
489 | |
490 | if (dvb->frontend[1]) { |
491 | result = dvb_register_frontend(dvb: &dvb->adapter, fe: dvb->frontend[1]); |
492 | if (result < 0) { |
493 | dev_warn(dev->dev, |
494 | "%s: 2nd dvb_register_frontend failed (errno = %d)\n" , |
495 | dev->name, result); |
496 | goto fail_frontend1; |
497 | } |
498 | |
499 | /* MFE lock */ |
500 | dvb->adapter.mfe_shared = 1; |
501 | } |
502 | |
503 | /* register demux stuff */ |
504 | dvb->demux.dmx.capabilities = |
505 | DMX_TS_FILTERING | DMX_SECTION_FILTERING | |
506 | DMX_MEMORY_BASED_FILTERING; |
507 | dvb->demux.priv = dvb; |
508 | dvb->demux.filternum = 256; |
509 | dvb->demux.feednum = 256; |
510 | dvb->demux.start_feed = start_feed; |
511 | dvb->demux.stop_feed = stop_feed; |
512 | |
513 | result = dvb_dmx_init(demux: &dvb->demux); |
514 | if (result < 0) { |
515 | dev_warn(dev->dev, |
516 | "%s: dvb_dmx_init failed (errno = %d)\n" , |
517 | dev->name, result); |
518 | goto fail_dmx; |
519 | } |
520 | |
521 | dvb->dmxdev.filternum = 256; |
522 | dvb->dmxdev.demux = &dvb->demux.dmx; |
523 | dvb->dmxdev.capabilities = 0; |
524 | result = dvb_dmxdev_init(dmxdev: &dvb->dmxdev, adap: &dvb->adapter); |
525 | if (result < 0) { |
526 | dev_warn(dev->dev, |
527 | "%s: dvb_dmxdev_init failed (errno = %d)\n" , |
528 | dev->name, result); |
529 | goto fail_dmxdev; |
530 | } |
531 | |
532 | dvb->fe_hw.source = DMX_FRONTEND_0; |
533 | result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); |
534 | if (result < 0) { |
535 | dev_warn(dev->dev, |
536 | "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n" , |
537 | dev->name, result); |
538 | goto fail_fe_hw; |
539 | } |
540 | |
541 | dvb->fe_mem.source = DMX_MEMORY_FE; |
542 | result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); |
543 | if (result < 0) { |
544 | dev_warn(dev->dev, |
545 | "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n" , |
546 | dev->name, result); |
547 | goto fail_fe_mem; |
548 | } |
549 | |
550 | result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); |
551 | if (result < 0) { |
552 | dev_warn(dev->dev, |
553 | "%s: connect_frontend failed (errno = %d)\n" , |
554 | dev->name, result); |
555 | goto fail_fe_conn; |
556 | } |
557 | |
558 | /* register network adapter */ |
559 | dvb_net_init(adap: &dvb->adapter, dvbnet: &dvb->net, dmxdemux: &dvb->demux.dmx); |
560 | result = dvb_create_media_graph(adap: &dvb->adapter, |
561 | create_rf_connector: dev->tuner_type == TUNER_ABSENT); |
562 | if (result < 0) |
563 | goto fail_create_graph; |
564 | |
565 | return 0; |
566 | |
567 | fail_create_graph: |
568 | dvb_net_release(dvbnet: &dvb->net); |
569 | fail_fe_conn: |
570 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); |
571 | fail_fe_mem: |
572 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); |
573 | fail_fe_hw: |
574 | dvb_dmxdev_release(dmxdev: &dvb->dmxdev); |
575 | fail_dmxdev: |
576 | dvb_dmx_release(demux: &dvb->demux); |
577 | fail_dmx: |
578 | if (dvb->frontend[1]) |
579 | dvb_unregister_frontend(fe: dvb->frontend[1]); |
580 | dvb_unregister_frontend(fe: dvb->frontend[0]); |
581 | fail_frontend1: |
582 | if (dvb->frontend[1]) |
583 | dvb_frontend_detach(fe: dvb->frontend[1]); |
584 | fail_frontend0: |
585 | dvb_frontend_detach(fe: dvb->frontend[0]); |
586 | dvb_unregister_adapter(adap: &dvb->adapter); |
587 | fail_adapter: |
588 | return result; |
589 | } |
590 | |
591 | static void unregister_dvb(struct cx231xx_dvb *dvb) |
592 | { |
593 | dvb_net_release(dvbnet: &dvb->net); |
594 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); |
595 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); |
596 | dvb_dmxdev_release(dmxdev: &dvb->dmxdev); |
597 | dvb_dmx_release(demux: &dvb->demux); |
598 | if (dvb->frontend[1]) |
599 | dvb_unregister_frontend(fe: dvb->frontend[1]); |
600 | dvb_unregister_frontend(fe: dvb->frontend[0]); |
601 | if (dvb->frontend[1]) |
602 | dvb_frontend_detach(fe: dvb->frontend[1]); |
603 | dvb_frontend_detach(fe: dvb->frontend[0]); |
604 | dvb_unregister_adapter(adap: &dvb->adapter); |
605 | |
606 | /* remove I2C tuner */ |
607 | dvb_module_release(client: dvb->i2c_client_tuner); |
608 | dvb->i2c_client_tuner = NULL; |
609 | /* remove I2C demod(s) */ |
610 | dvb_module_release(client: dvb->i2c_client_demod[1]); |
611 | dvb->i2c_client_demod[1] = NULL; |
612 | dvb_module_release(client: dvb->i2c_client_demod[0]); |
613 | dvb->i2c_client_demod[0] = NULL; |
614 | } |
615 | |
616 | static int dvb_init(struct cx231xx *dev) |
617 | { |
618 | int result; |
619 | struct cx231xx_dvb *dvb; |
620 | struct i2c_adapter *tuner_i2c; |
621 | struct i2c_adapter *demod_i2c; |
622 | struct i2c_client *client; |
623 | struct i2c_adapter *adapter; |
624 | |
625 | if (!dev->board.has_dvb) { |
626 | /* This device does not support the extension */ |
627 | return 0; |
628 | } |
629 | |
630 | dvb = kzalloc(size: sizeof(struct cx231xx_dvb), GFP_KERNEL); |
631 | |
632 | if (dvb == NULL) { |
633 | dev_info(dev->dev, |
634 | "cx231xx_dvb: memory allocation failed\n" ); |
635 | return -ENOMEM; |
636 | } |
637 | dev->dvb = dvb; |
638 | dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq; |
639 | dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner; |
640 | |
641 | tuner_i2c = cx231xx_get_i2c_adap(dev, i2c_port: dev->board.tuner_i2c_master); |
642 | demod_i2c = cx231xx_get_i2c_adap(dev, i2c_port: dev->board.demod_i2c_master); |
643 | mutex_lock(&dev->lock); |
644 | cx231xx_set_mode(dev, set_mode: CX231XX_DIGITAL_MODE); |
645 | cx231xx_demod_reset(dev); |
646 | /* init frontend */ |
647 | switch (dev->model) { |
648 | case CX231XX_BOARD_CNXT_CARRAERA: |
649 | case CX231XX_BOARD_CNXT_RDE_250: |
650 | |
651 | dev->dvb->frontend[0] = dvb_attach(s5h1432_attach, |
652 | &dvico_s5h1432_config, |
653 | demod_i2c); |
654 | |
655 | if (!dev->dvb->frontend[0]) { |
656 | dev_err(dev->dev, |
657 | "Failed to attach s5h1432 front end\n" ); |
658 | result = -EINVAL; |
659 | goto out_free; |
660 | } |
661 | |
662 | /* define general-purpose callback pointer */ |
663 | dvb->frontend[0]->callback = cx231xx_tuner_callback; |
664 | |
665 | if (!dvb_attach(xc5000_attach, dev->dvb->frontend[0], |
666 | tuner_i2c, |
667 | &cnxt_rde250_tunerconfig)) { |
668 | result = -EINVAL; |
669 | goto out_free; |
670 | } |
671 | |
672 | break; |
673 | case CX231XX_BOARD_CNXT_SHELBY: |
674 | case CX231XX_BOARD_CNXT_RDU_250: |
675 | |
676 | dev->dvb->frontend[0] = dvb_attach(s5h1411_attach, |
677 | &xc5000_s5h1411_config, |
678 | demod_i2c); |
679 | |
680 | if (!dev->dvb->frontend[0]) { |
681 | dev_err(dev->dev, |
682 | "Failed to attach s5h1411 front end\n" ); |
683 | result = -EINVAL; |
684 | goto out_free; |
685 | } |
686 | |
687 | /* define general-purpose callback pointer */ |
688 | dvb->frontend[0]->callback = cx231xx_tuner_callback; |
689 | |
690 | if (!dvb_attach(xc5000_attach, dev->dvb->frontend[0], |
691 | tuner_i2c, |
692 | &cnxt_rdu250_tunerconfig)) { |
693 | result = -EINVAL; |
694 | goto out_free; |
695 | } |
696 | break; |
697 | case CX231XX_BOARD_CNXT_RDE_253S: |
698 | |
699 | dev->dvb->frontend[0] = dvb_attach(s5h1432_attach, |
700 | &dvico_s5h1432_config, |
701 | demod_i2c); |
702 | |
703 | if (!dev->dvb->frontend[0]) { |
704 | dev_err(dev->dev, |
705 | "Failed to attach s5h1432 front end\n" ); |
706 | result = -EINVAL; |
707 | goto out_free; |
708 | } |
709 | |
710 | /* define general-purpose callback pointer */ |
711 | dvb->frontend[0]->callback = cx231xx_tuner_callback; |
712 | |
713 | if (!dvb_attach(tda18271_attach, dev->dvb->frontend[0], |
714 | dev->board.tuner_addr, tuner_i2c, |
715 | &cnxt_rde253s_tunerconfig)) { |
716 | result = -EINVAL; |
717 | goto out_free; |
718 | } |
719 | break; |
720 | case CX231XX_BOARD_CNXT_RDU_253S: |
721 | case CX231XX_BOARD_KWORLD_UB445_USB_HYBRID: |
722 | |
723 | dev->dvb->frontend[0] = dvb_attach(s5h1411_attach, |
724 | &tda18271_s5h1411_config, |
725 | demod_i2c); |
726 | |
727 | if (!dev->dvb->frontend[0]) { |
728 | dev_err(dev->dev, |
729 | "Failed to attach s5h1411 front end\n" ); |
730 | result = -EINVAL; |
731 | goto out_free; |
732 | } |
733 | |
734 | /* define general-purpose callback pointer */ |
735 | dvb->frontend[0]->callback = cx231xx_tuner_callback; |
736 | |
737 | if (!dvb_attach(tda18271_attach, dev->dvb->frontend[0], |
738 | dev->board.tuner_addr, tuner_i2c, |
739 | &cnxt_rde253s_tunerconfig)) { |
740 | result = -EINVAL; |
741 | goto out_free; |
742 | } |
743 | break; |
744 | case CX231XX_BOARD_HAUPPAUGE_EXETER: |
745 | |
746 | dev_info(dev->dev, |
747 | "%s: looking for tuner / demod on i2c bus: %d\n" , |
748 | __func__, i2c_adapter_id(tuner_i2c)); |
749 | |
750 | dev->dvb->frontend[0] = dvb_attach(lgdt3305_attach, |
751 | &hcw_lgdt3305_config, |
752 | demod_i2c); |
753 | |
754 | if (!dev->dvb->frontend[0]) { |
755 | dev_err(dev->dev, |
756 | "Failed to attach LG3305 front end\n" ); |
757 | result = -EINVAL; |
758 | goto out_free; |
759 | } |
760 | |
761 | /* define general-purpose callback pointer */ |
762 | dvb->frontend[0]->callback = cx231xx_tuner_callback; |
763 | |
764 | dvb_attach(tda18271_attach, dev->dvb->frontend[0], |
765 | dev->board.tuner_addr, tuner_i2c, |
766 | &hcw_tda18271_config); |
767 | break; |
768 | |
769 | case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx: |
770 | { |
771 | struct si2165_platform_data si2165_pdata = {}; |
772 | |
773 | /* attach demod */ |
774 | si2165_pdata.fe = &dev->dvb->frontend[0]; |
775 | si2165_pdata.chip_mode = SI2165_MODE_PLL_XTAL; |
776 | si2165_pdata.ref_freq_hz = 16000000; |
777 | |
778 | /* perform probe/init/attach */ |
779 | client = dvb_module_probe(module_name: "si2165" , NULL, adap: demod_i2c, |
780 | addr: dev->board.demod_addr, |
781 | platform_data: &si2165_pdata); |
782 | if (!client) { |
783 | result = -ENODEV; |
784 | goto out_free; |
785 | } |
786 | dvb->i2c_client_demod[0] = client; |
787 | |
788 | dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL; |
789 | |
790 | /* define general-purpose callback pointer */ |
791 | dvb->frontend[0]->callback = cx231xx_tuner_callback; |
792 | |
793 | dvb_attach(tda18271_attach, dev->dvb->frontend[0], |
794 | dev->board.tuner_addr, tuner_i2c, |
795 | &hcw_tda18271_config); |
796 | |
797 | dev->cx231xx_reset_analog_tuner = NULL; |
798 | break; |
799 | } |
800 | case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: |
801 | { |
802 | struct si2165_platform_data si2165_pdata = {}; |
803 | struct si2157_config si2157_config = {}; |
804 | |
805 | /* attach demod */ |
806 | si2165_pdata.fe = &dev->dvb->frontend[0]; |
807 | si2165_pdata.chip_mode = SI2165_MODE_PLL_EXT; |
808 | si2165_pdata.ref_freq_hz = 24000000; |
809 | |
810 | /* perform probe/init/attach */ |
811 | client = dvb_module_probe(module_name: "si2165" , NULL, adap: demod_i2c, |
812 | addr: dev->board.demod_addr, |
813 | platform_data: &si2165_pdata); |
814 | if (!client) { |
815 | result = -ENODEV; |
816 | goto out_free; |
817 | } |
818 | dvb->i2c_client_demod[0] = client; |
819 | |
820 | dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL; |
821 | |
822 | /* define general-purpose callback pointer */ |
823 | dvb->frontend[0]->callback = cx231xx_tuner_callback; |
824 | |
825 | /* attach tuner */ |
826 | si2157_config.fe = dev->dvb->frontend[0]; |
827 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
828 | si2157_config.mdev = dev->media_dev; |
829 | #endif |
830 | si2157_config.if_port = 1; |
831 | si2157_config.inversion = true; |
832 | |
833 | /* perform probe/init/attach */ |
834 | client = dvb_module_probe(module_name: "si2157" , NULL, adap: tuner_i2c, |
835 | addr: dev->board.tuner_addr, |
836 | platform_data: &si2157_config); |
837 | if (!client) { |
838 | result = -ENODEV; |
839 | goto out_free; |
840 | } |
841 | dev->cx231xx_reset_analog_tuner = NULL; |
842 | |
843 | dev->dvb->i2c_client_tuner = client; |
844 | break; |
845 | } |
846 | case CX231XX_BOARD_HAUPPAUGE_955Q: |
847 | { |
848 | struct si2157_config si2157_config = {}; |
849 | struct lgdt3306a_config lgdt3306a_config = {}; |
850 | |
851 | lgdt3306a_config = hauppauge_955q_lgdt3306a_config; |
852 | lgdt3306a_config.fe = &dev->dvb->frontend[0]; |
853 | lgdt3306a_config.i2c_adapter = &adapter; |
854 | |
855 | /* perform probe/init/attach */ |
856 | client = dvb_module_probe(module_name: "lgdt3306a" , NULL, adap: demod_i2c, |
857 | addr: dev->board.demod_addr, |
858 | platform_data: &lgdt3306a_config); |
859 | if (!client) { |
860 | result = -ENODEV; |
861 | goto out_free; |
862 | } |
863 | dvb->i2c_client_demod[0] = client; |
864 | |
865 | dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL; |
866 | |
867 | /* define general-purpose callback pointer */ |
868 | dvb->frontend[0]->callback = cx231xx_tuner_callback; |
869 | |
870 | /* attach tuner */ |
871 | si2157_config.fe = dev->dvb->frontend[0]; |
872 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
873 | si2157_config.mdev = dev->media_dev; |
874 | #endif |
875 | si2157_config.if_port = 1; |
876 | si2157_config.inversion = true; |
877 | |
878 | /* perform probe/init/attach */ |
879 | client = dvb_module_probe(module_name: "si2157" , NULL, adap: tuner_i2c, |
880 | addr: dev->board.tuner_addr, |
881 | platform_data: &si2157_config); |
882 | if (!client) { |
883 | result = -ENODEV; |
884 | goto out_free; |
885 | } |
886 | dev->cx231xx_reset_analog_tuner = NULL; |
887 | |
888 | dev->dvb->i2c_client_tuner = client; |
889 | break; |
890 | } |
891 | case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: |
892 | case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID: |
893 | |
894 | dev_info(dev->dev, |
895 | "%s: looking for demod on i2c bus: %d\n" , |
896 | __func__, i2c_adapter_id(tuner_i2c)); |
897 | |
898 | dev->dvb->frontend[0] = dvb_attach(mb86a20s_attach, |
899 | &pv_mb86a20s_config, |
900 | demod_i2c); |
901 | |
902 | if (!dev->dvb->frontend[0]) { |
903 | dev_err(dev->dev, |
904 | "Failed to attach mb86a20s demod\n" ); |
905 | result = -EINVAL; |
906 | goto out_free; |
907 | } |
908 | |
909 | /* define general-purpose callback pointer */ |
910 | dvb->frontend[0]->callback = cx231xx_tuner_callback; |
911 | |
912 | dvb_attach(tda18271_attach, dev->dvb->frontend[0], |
913 | dev->board.tuner_addr, tuner_i2c, |
914 | &pv_tda18271_config); |
915 | break; |
916 | |
917 | case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD: |
918 | { |
919 | struct si2157_config si2157_config = {}; |
920 | struct si2168_config si2168_config = {}; |
921 | |
922 | /* attach demodulator chip */ |
923 | si2168_config.ts_mode = SI2168_TS_SERIAL; /* from *.inf file */ |
924 | si2168_config.fe = &dev->dvb->frontend[0]; |
925 | si2168_config.i2c_adapter = &adapter; |
926 | si2168_config.ts_clock_inv = true; |
927 | |
928 | /* perform probe/init/attach */ |
929 | client = dvb_module_probe(module_name: "si2168" , NULL, adap: demod_i2c, |
930 | addr: dev->board.demod_addr, |
931 | platform_data: &si2168_config); |
932 | if (!client) { |
933 | result = -ENODEV; |
934 | goto out_free; |
935 | } |
936 | dvb->i2c_client_demod[0] = client; |
937 | |
938 | /* attach tuner chip */ |
939 | si2157_config.fe = dev->dvb->frontend[0]; |
940 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
941 | si2157_config.mdev = dev->media_dev; |
942 | #endif |
943 | si2157_config.if_port = 1; |
944 | si2157_config.inversion = false; |
945 | |
946 | /* perform probe/init/attach */ |
947 | client = dvb_module_probe(module_name: "si2157" , NULL, adap: tuner_i2c, |
948 | addr: dev->board.tuner_addr, |
949 | platform_data: &si2157_config); |
950 | if (!client) { |
951 | result = -ENODEV; |
952 | goto out_free; |
953 | } |
954 | dev->cx231xx_reset_analog_tuner = NULL; |
955 | dev->dvb->i2c_client_tuner = client; |
956 | break; |
957 | } |
958 | case CX231XX_BOARD_ASTROMETA_T2HYBRID: |
959 | { |
960 | struct mn88473_config mn88473_config = {}; |
961 | |
962 | /* attach demodulator chip */ |
963 | mn88473_config.i2c_wr_max = 16; |
964 | mn88473_config.xtal = 25000000; |
965 | mn88473_config.fe = &dev->dvb->frontend[0]; |
966 | |
967 | /* perform probe/init/attach */ |
968 | client = dvb_module_probe(module_name: "mn88473" , NULL, adap: demod_i2c, |
969 | addr: dev->board.demod_addr, |
970 | platform_data: &mn88473_config); |
971 | if (!client) { |
972 | result = -ENODEV; |
973 | goto out_free; |
974 | } |
975 | dvb->i2c_client_demod[0] = client; |
976 | |
977 | /* define general-purpose callback pointer */ |
978 | dvb->frontend[0]->callback = cx231xx_tuner_callback; |
979 | |
980 | /* attach tuner chip */ |
981 | dvb_attach(r820t_attach, dev->dvb->frontend[0], |
982 | tuner_i2c, |
983 | &astrometa_t2hybrid_r820t_config); |
984 | break; |
985 | } |
986 | case CX231XX_BOARD_HAUPPAUGE_935C: |
987 | { |
988 | struct si2157_config si2157_config = {}; |
989 | struct si2168_config si2168_config = {}; |
990 | |
991 | /* attach demodulator chip */ |
992 | si2168_config.ts_mode = SI2168_TS_SERIAL; |
993 | si2168_config.fe = &dev->dvb->frontend[0]; |
994 | si2168_config.i2c_adapter = &adapter; |
995 | si2168_config.ts_clock_inv = true; |
996 | |
997 | /* perform probe/init/attach */ |
998 | client = dvb_module_probe(module_name: "si2168" , NULL, adap: demod_i2c, |
999 | addr: dev->board.demod_addr, |
1000 | platform_data: &si2168_config); |
1001 | if (!client) { |
1002 | result = -ENODEV; |
1003 | goto out_free; |
1004 | } |
1005 | dvb->i2c_client_demod[0] = client; |
1006 | dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL; |
1007 | |
1008 | /* define general-purpose callback pointer */ |
1009 | dvb->frontend[0]->callback = cx231xx_tuner_callback; |
1010 | |
1011 | /* attach tuner */ |
1012 | si2157_config.fe = dev->dvb->frontend[0]; |
1013 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
1014 | si2157_config.mdev = dev->media_dev; |
1015 | #endif |
1016 | si2157_config.if_port = 1; |
1017 | si2157_config.inversion = true; |
1018 | |
1019 | /* perform probe/init/attach */ |
1020 | client = dvb_module_probe(module_name: "si2157" , NULL, adap: tuner_i2c, |
1021 | addr: dev->board.tuner_addr, |
1022 | platform_data: &si2157_config); |
1023 | if (!client) { |
1024 | result = -ENODEV; |
1025 | goto out_free; |
1026 | } |
1027 | dev->cx231xx_reset_analog_tuner = NULL; |
1028 | dev->dvb->i2c_client_tuner = client; |
1029 | break; |
1030 | } |
1031 | case CX231XX_BOARD_HAUPPAUGE_975: |
1032 | { |
1033 | struct i2c_adapter *adapter2; |
1034 | struct si2157_config si2157_config = {}; |
1035 | struct lgdt3306a_config lgdt3306a_config = {}; |
1036 | struct si2168_config si2168_config = {}; |
1037 | |
1038 | /* attach first demodulator chip */ |
1039 | lgdt3306a_config = hauppauge_955q_lgdt3306a_config; |
1040 | lgdt3306a_config.fe = &dev->dvb->frontend[0]; |
1041 | lgdt3306a_config.i2c_adapter = &adapter; |
1042 | |
1043 | /* perform probe/init/attach */ |
1044 | client = dvb_module_probe(module_name: "lgdt3306a" , NULL, adap: demod_i2c, |
1045 | addr: dev->board.demod_addr, |
1046 | platform_data: &lgdt3306a_config); |
1047 | if (!client) { |
1048 | result = -ENODEV; |
1049 | goto out_free; |
1050 | } |
1051 | dvb->i2c_client_demod[0] = client; |
1052 | |
1053 | /* attach second demodulator chip */ |
1054 | si2168_config.ts_mode = SI2168_TS_SERIAL; |
1055 | si2168_config.fe = &dev->dvb->frontend[1]; |
1056 | si2168_config.i2c_adapter = &adapter2; |
1057 | si2168_config.ts_clock_inv = true; |
1058 | |
1059 | /* perform probe/init/attach */ |
1060 | client = dvb_module_probe(module_name: "si2168" , NULL, adap: adapter, |
1061 | addr: dev->board.demod_addr2, |
1062 | platform_data: &si2168_config); |
1063 | if (!client) { |
1064 | result = -ENODEV; |
1065 | goto out_free; |
1066 | } |
1067 | dvb->i2c_client_demod[1] = client; |
1068 | dvb->frontend[1]->id = 1; |
1069 | |
1070 | /* define general-purpose callback pointer */ |
1071 | dvb->frontend[0]->callback = cx231xx_tuner_callback; |
1072 | dvb->frontend[1]->callback = cx231xx_tuner_callback; |
1073 | |
1074 | /* attach tuner */ |
1075 | si2157_config.fe = dev->dvb->frontend[0]; |
1076 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
1077 | si2157_config.mdev = dev->media_dev; |
1078 | #endif |
1079 | si2157_config.if_port = 1; |
1080 | si2157_config.inversion = true; |
1081 | |
1082 | /* perform probe/init/attach */ |
1083 | client = dvb_module_probe(module_name: "si2157" , NULL, adap: adapter, |
1084 | addr: dev->board.tuner_addr, |
1085 | platform_data: &si2157_config); |
1086 | if (!client) { |
1087 | result = -ENODEV; |
1088 | goto out_free; |
1089 | } |
1090 | dev->cx231xx_reset_analog_tuner = NULL; |
1091 | dvb->i2c_client_tuner = client; |
1092 | |
1093 | dvb->frontend[1]->tuner_priv = dvb->frontend[0]->tuner_priv; |
1094 | |
1095 | memcpy(&dvb->frontend[1]->ops.tuner_ops, |
1096 | &dvb->frontend[0]->ops.tuner_ops, |
1097 | sizeof(struct dvb_tuner_ops)); |
1098 | break; |
1099 | } |
1100 | default: |
1101 | dev_err(dev->dev, |
1102 | "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n" , |
1103 | dev->name); |
1104 | break; |
1105 | } |
1106 | if (!dvb->frontend[0]) { |
1107 | dev_err(dev->dev, |
1108 | "%s/2: frontend initialization failed\n" , dev->name); |
1109 | result = -EINVAL; |
1110 | goto out_free; |
1111 | } |
1112 | |
1113 | /* register everything */ |
1114 | result = register_dvb(dvb, THIS_MODULE, dev, device: dev->dev); |
1115 | |
1116 | if (result < 0) |
1117 | goto out_free; |
1118 | |
1119 | |
1120 | dev_info(dev->dev, "Successfully loaded cx231xx-dvb\n" ); |
1121 | |
1122 | ret: |
1123 | cx231xx_set_mode(dev, set_mode: CX231XX_SUSPEND); |
1124 | mutex_unlock(lock: &dev->lock); |
1125 | return result; |
1126 | |
1127 | out_free: |
1128 | /* remove I2C tuner */ |
1129 | dvb_module_release(client: dvb->i2c_client_tuner); |
1130 | dvb->i2c_client_tuner = NULL; |
1131 | /* remove I2C demod(s) */ |
1132 | dvb_module_release(client: dvb->i2c_client_demod[1]); |
1133 | dvb->i2c_client_demod[1] = NULL; |
1134 | dvb_module_release(client: dvb->i2c_client_demod[0]); |
1135 | dvb->i2c_client_demod[0] = NULL; |
1136 | kfree(objp: dvb); |
1137 | dev->dvb = NULL; |
1138 | goto ret; |
1139 | } |
1140 | |
1141 | static int dvb_fini(struct cx231xx *dev) |
1142 | { |
1143 | if (!dev->board.has_dvb) { |
1144 | /* This device does not support the extension */ |
1145 | return 0; |
1146 | } |
1147 | |
1148 | if (dev->dvb) { |
1149 | unregister_dvb(dvb: dev->dvb); |
1150 | kfree(objp: dev->dvb); |
1151 | dev->dvb = NULL; |
1152 | } |
1153 | |
1154 | return 0; |
1155 | } |
1156 | |
1157 | static struct cx231xx_ops dvb_ops = { |
1158 | .id = CX231XX_DVB, |
1159 | .name = "Cx231xx dvb Extension" , |
1160 | .init = dvb_init, |
1161 | .fini = dvb_fini, |
1162 | }; |
1163 | |
1164 | static int __init cx231xx_dvb_register(void) |
1165 | { |
1166 | return cx231xx_register_extension(dev: &dvb_ops); |
1167 | } |
1168 | |
1169 | static void __exit cx231xx_dvb_unregister(void) |
1170 | { |
1171 | cx231xx_unregister_extension(dev: &dvb_ops); |
1172 | } |
1173 | |
1174 | module_init(cx231xx_dvb_register); |
1175 | module_exit(cx231xx_dvb_unregister); |
1176 | |