Full support for Ginger Console
[linux-ginger.git] / drivers / media / dvb / b2c2 / flexcop-fe-tuner.c
blob850a6c606750240f502f82bdf25ca1ce3b675dc0
1 /*
2 * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
3 * flexcop-fe-tuner.c - methods for frontend attachment and DiSEqC controlling
4 * see flexcop.c for copyright information
5 */
6 #include <media/tuner.h>
7 #include "flexcop.h"
8 #include "mt312.h"
9 #include "stv0299.h"
10 #include "s5h1420.h"
11 #include "itd1000.h"
12 #include "cx24113.h"
13 #include "cx24123.h"
14 #include "isl6421.h"
15 #include "mt352.h"
16 #include "bcm3510.h"
17 #include "nxt200x.h"
18 #include "dvb-pll.h"
19 #include "lgdt330x.h"
20 #include "tuner-simple.h"
21 #include "stv0297.h"
24 /* Can we use the specified front-end? Remember that if we are compiled
25 * into the kernel we can't call code that's in modules. */
26 #define FE_SUPPORTED(fe) (defined(CONFIG_DVB_##fe) || \
27 (defined(CONFIG_DVB_##fe##_MODULE) && defined(MODULE)))
29 /* lnb control */
30 #if FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299)
31 static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
33 struct flexcop_device *fc = fe->dvb->priv;
34 flexcop_ibi_value v;
35 deb_tuner("polarity/voltage = %u\n", voltage);
37 v = fc->read_ibi_reg(fc, misc_204);
38 switch (voltage) {
39 case SEC_VOLTAGE_OFF:
40 v.misc_204.ACPI1_sig = 1;
41 break;
42 case SEC_VOLTAGE_13:
43 v.misc_204.ACPI1_sig = 0;
44 v.misc_204.LNB_L_H_sig = 0;
45 break;
46 case SEC_VOLTAGE_18:
47 v.misc_204.ACPI1_sig = 0;
48 v.misc_204.LNB_L_H_sig = 1;
49 break;
50 default:
51 err("unknown SEC_VOLTAGE value");
52 return -EINVAL;
54 return fc->write_ibi_reg(fc, misc_204, v);
56 #endif
58 #if FE_SUPPORTED(S5H1420) || FE_SUPPORTED(STV0299) || FE_SUPPORTED(MT312)
59 static int flexcop_sleep(struct dvb_frontend* fe)
61 struct flexcop_device *fc = fe->dvb->priv;
62 if (fc->fe_sleep)
63 return fc->fe_sleep(fe);
64 return 0;
66 #endif
68 /* SkyStar2 DVB-S rev 2.3 */
69 #if FE_SUPPORTED(MT312) && FE_SUPPORTED(PLL)
70 static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
72 /* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */
73 struct flexcop_device *fc = fe->dvb->priv;
74 flexcop_ibi_value v;
75 u16 ax;
76 v.raw = 0;
77 deb_tuner("tone = %u\n",tone);
79 switch (tone) {
80 case SEC_TONE_ON:
81 ax = 0x01ff;
82 break;
83 case SEC_TONE_OFF:
84 ax = 0;
85 break;
86 default:
87 err("unknown SEC_TONE value");
88 return -EINVAL;
91 v.lnb_switch_freq_200.LNB_CTLPrescaler_sig = 1; /* divide by 2 */
92 v.lnb_switch_freq_200.LNB_CTLHighCount_sig = ax;
93 v.lnb_switch_freq_200.LNB_CTLLowCount_sig = ax == 0 ? 0x1ff : ax;
94 return fc->write_ibi_reg(fc,lnb_switch_freq_200,v);
97 static void flexcop_diseqc_send_bit(struct dvb_frontend* fe, int data)
99 flexcop_set_tone(fe, SEC_TONE_ON);
100 udelay(data ? 500 : 1000);
101 flexcop_set_tone(fe, SEC_TONE_OFF);
102 udelay(data ? 1000 : 500);
105 static void flexcop_diseqc_send_byte(struct dvb_frontend* fe, int data)
107 int i, par = 1, d;
108 for (i = 7; i >= 0; i--) {
109 d = (data >> i) & 1;
110 par ^= d;
111 flexcop_diseqc_send_bit(fe, d);
113 flexcop_diseqc_send_bit(fe, par);
116 static int flexcop_send_diseqc_msg(struct dvb_frontend *fe,
117 int len, u8 *msg, unsigned long burst)
119 int i;
121 flexcop_set_tone(fe, SEC_TONE_OFF);
122 mdelay(16);
124 for (i = 0; i < len; i++)
125 flexcop_diseqc_send_byte(fe,msg[i]);
126 mdelay(16);
128 if (burst != -1) {
129 if (burst)
130 flexcop_diseqc_send_byte(fe, 0xff);
131 else {
132 flexcop_set_tone(fe, SEC_TONE_ON);
133 mdelay(12);
134 udelay(500);
135 flexcop_set_tone(fe, SEC_TONE_OFF);
137 msleep(20);
139 return 0;
142 static int flexcop_diseqc_send_master_cmd(struct dvb_frontend *fe,
143 struct dvb_diseqc_master_cmd *cmd)
145 return flexcop_send_diseqc_msg(fe, cmd->msg_len, cmd->msg, 0);
148 static int flexcop_diseqc_send_burst(struct dvb_frontend *fe,
149 fe_sec_mini_cmd_t minicmd)
151 return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd);
154 static struct mt312_config skystar23_samsung_tbdu18132_config = {
155 .demod_address = 0x0e,
158 static int skystar2_rev23_attach(struct flexcop_device *fc,
159 struct i2c_adapter *i2c)
161 struct dvb_frontend_ops *ops;
163 fc->fe = dvb_attach(mt312_attach, &skystar23_samsung_tbdu18132_config, i2c);
164 if (!fc->fe)
165 return 0;
167 if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c,
168 DVB_PLL_SAMSUNG_TBDU18132))
169 return 0;
171 ops = &fc->fe->ops;
172 ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
173 ops->diseqc_send_burst = flexcop_diseqc_send_burst;
174 ops->set_tone = flexcop_set_tone;
175 ops->set_voltage = flexcop_set_voltage;
176 fc->fe_sleep = ops->sleep;
177 ops->sleep = flexcop_sleep;
178 return 1;
180 #else
181 #define skystar2_rev23_attach NULL
182 #endif
184 /* SkyStar2 DVB-S rev 2.6 */
185 #if FE_SUPPORTED(STV0299) && FE_SUPPORTED(PLL)
186 static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe,
187 u32 srate, u32 ratio)
189 u8 aclk = 0;
190 u8 bclk = 0;
192 if (srate < 1500000) {
193 aclk = 0xb7; bclk = 0x47;
194 } else if (srate < 3000000) {
195 aclk = 0xb7; bclk = 0x4b;
196 } else if (srate < 7000000) {
197 aclk = 0xb7; bclk = 0x4f;
198 } else if (srate < 14000000) {
199 aclk = 0xb7; bclk = 0x53;
200 } else if (srate < 30000000) {
201 aclk = 0xb6; bclk = 0x53;
202 } else if (srate < 45000000) {
203 aclk = 0xb4; bclk = 0x51;
206 stv0299_writereg(fe, 0x13, aclk);
207 stv0299_writereg(fe, 0x14, bclk);
208 stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
209 stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
210 stv0299_writereg(fe, 0x21, ratio & 0xf0);
211 return 0;
214 static u8 samsung_tbmu24112_inittab[] = {
215 0x01, 0x15,
216 0x02, 0x30,
217 0x03, 0x00,
218 0x04, 0x7D,
219 0x05, 0x35,
220 0x06, 0x02,
221 0x07, 0x00,
222 0x08, 0xC3,
223 0x0C, 0x00,
224 0x0D, 0x81,
225 0x0E, 0x23,
226 0x0F, 0x12,
227 0x10, 0x7E,
228 0x11, 0x84,
229 0x12, 0xB9,
230 0x13, 0x88,
231 0x14, 0x89,
232 0x15, 0xC9,
233 0x16, 0x00,
234 0x17, 0x5C,
235 0x18, 0x00,
236 0x19, 0x00,
237 0x1A, 0x00,
238 0x1C, 0x00,
239 0x1D, 0x00,
240 0x1E, 0x00,
241 0x1F, 0x3A,
242 0x20, 0x2E,
243 0x21, 0x80,
244 0x22, 0xFF,
245 0x23, 0xC1,
246 0x28, 0x00,
247 0x29, 0x1E,
248 0x2A, 0x14,
249 0x2B, 0x0F,
250 0x2C, 0x09,
251 0x2D, 0x05,
252 0x31, 0x1F,
253 0x32, 0x19,
254 0x33, 0xFE,
255 0x34, 0x93,
256 0xff, 0xff,
259 static struct stv0299_config samsung_tbmu24112_config = {
260 .demod_address = 0x68,
261 .inittab = samsung_tbmu24112_inittab,
262 .mclk = 88000000UL,
263 .invert = 0,
264 .skip_reinit = 0,
265 .lock_output = STV0299_LOCKOUTPUT_LK,
266 .volt13_op0_op1 = STV0299_VOLT13_OP1,
267 .min_delay_ms = 100,
268 .set_symbol_rate = samsung_tbmu24112_set_symbol_rate,
271 static int skystar2_rev26_attach(struct flexcop_device *fc,
272 struct i2c_adapter *i2c)
274 fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, i2c);
275 if (!fc->fe)
276 return 0;
278 if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c,
279 DVB_PLL_SAMSUNG_TBMU24112))
280 return 0;
282 fc->fe->ops.set_voltage = flexcop_set_voltage;
283 fc->fe_sleep = fc->fe->ops.sleep;
284 fc->fe->ops.sleep = flexcop_sleep;
285 return 1;
288 #else
289 #define skystar2_rev26_attach NULL
290 #endif
292 /* SkyStar2 DVB-S rev 2.7 */
293 #if FE_SUPPORTED(S5H1420) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_ITD1000)
294 static struct s5h1420_config skystar2_rev2_7_s5h1420_config = {
295 .demod_address = 0x53,
296 .invert = 1,
297 .repeated_start_workaround = 1,
298 .serial_mpeg = 1,
301 static struct itd1000_config skystar2_rev2_7_itd1000_config = {
302 .i2c_address = 0x61,
305 static int skystar2_rev27_attach(struct flexcop_device *fc,
306 struct i2c_adapter *i2c)
308 flexcop_ibi_value r108;
309 struct i2c_adapter *i2c_tuner;
311 /* enable no_base_addr - no repeated start when reading */
312 fc->fc_i2c_adap[0].no_base_addr = 1;
313 fc->fe = dvb_attach(s5h1420_attach, &skystar2_rev2_7_s5h1420_config,
314 i2c);
315 if (!fc->fe)
316 goto fail;
318 i2c_tuner = s5h1420_get_tuner_i2c_adapter(fc->fe);
319 if (!i2c_tuner)
320 goto fail;
322 fc->fe_sleep = fc->fe->ops.sleep;
323 fc->fe->ops.sleep = flexcop_sleep;
325 /* enable no_base_addr - no repeated start when reading */
326 fc->fc_i2c_adap[2].no_base_addr = 1;
327 if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap,
328 0x08, 1, 1)) {
329 err("ISL6421 could NOT be attached");
330 goto fail_isl;
332 info("ISL6421 successfully attached");
334 /* the ITD1000 requires a lower i2c clock - is it a problem ? */
335 r108.raw = 0x00000506;
336 fc->write_ibi_reg(fc, tw_sm_c_108, r108);
337 if (!dvb_attach(itd1000_attach, fc->fe, i2c_tuner,
338 &skystar2_rev2_7_itd1000_config)) {
339 err("ITD1000 could NOT be attached");
340 /* Should i2c clock be restored? */
341 goto fail_isl;
343 info("ITD1000 successfully attached");
345 return 1;
347 fail_isl:
348 fc->fc_i2c_adap[2].no_base_addr = 0;
349 fail:
350 /* for the next devices we need it again */
351 fc->fc_i2c_adap[0].no_base_addr = 0;
352 return 0;
354 #else
355 #define skystar2_rev27_attach NULL
356 #endif
358 /* SkyStar2 rev 2.8 */
359 #if FE_SUPPORTED(CX24123) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_CX24113)
360 static struct cx24123_config skystar2_rev2_8_cx24123_config = {
361 .demod_address = 0x55,
362 .dont_use_pll = 1,
363 .agc_callback = cx24113_agc_callback,
366 static const struct cx24113_config skystar2_rev2_8_cx24113_config = {
367 .i2c_addr = 0x54,
368 .xtal_khz = 10111,
371 static int skystar2_rev28_attach(struct flexcop_device *fc,
372 struct i2c_adapter *i2c)
374 struct i2c_adapter *i2c_tuner;
376 fc->fe = dvb_attach(cx24123_attach, &skystar2_rev2_8_cx24123_config,
377 i2c);
378 if (!fc->fe)
379 return 0;
381 i2c_tuner = cx24123_get_tuner_i2c_adapter(fc->fe);
382 if (!i2c_tuner)
383 return 0;
385 if (!dvb_attach(cx24113_attach, fc->fe, &skystar2_rev2_8_cx24113_config,
386 i2c_tuner)) {
387 err("CX24113 could NOT be attached");
388 return 0;
390 info("CX24113 successfully attached");
392 fc->fc_i2c_adap[2].no_base_addr = 1;
393 if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap,
394 0x08, 0, 0)) {
395 err("ISL6421 could NOT be attached");
396 fc->fc_i2c_adap[2].no_base_addr = 0;
397 return 0;
399 info("ISL6421 successfully attached");
400 /* TODO on i2c_adap[1] addr 0x11 (EEPROM) there seems to be an
401 * IR-receiver (PIC16F818) - but the card has no input for that ??? */
402 return 1;
404 #else
405 #define skystar2_rev28_attach NULL
406 #endif
408 /* AirStar DVB-T */
409 #if FE_SUPPORTED(MT352) && FE_SUPPORTED(PLL)
410 static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe)
412 static u8 mt352_clock_config[] = { 0x89, 0x18, 0x2d };
413 static u8 mt352_reset[] = { 0x50, 0x80 };
414 static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 };
415 static u8 mt352_agc_cfg[] = { 0x67, 0x28, 0xa1 };
416 static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
418 mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
419 udelay(2000);
420 mt352_write(fe, mt352_reset, sizeof(mt352_reset));
421 mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
422 mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
423 mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
424 return 0;
427 static struct mt352_config samsung_tdtc9251dh0_config = {
428 .demod_address = 0x0f,
429 .demod_init = samsung_tdtc9251dh0_demod_init,
432 static int airstar_dvbt_attach(struct flexcop_device *fc,
433 struct i2c_adapter *i2c)
435 fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, i2c);
436 if (!fc->fe)
437 return 0;
439 return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL,
440 DVB_PLL_SAMSUNG_TDTC9251DH0);
442 #else
443 #define airstar_dvbt_attach NULL
444 #endif
446 /* AirStar ATSC 1st generation */
447 #if FE_SUPPORTED(BCM3510)
448 static int flexcop_fe_request_firmware(struct dvb_frontend *fe,
449 const struct firmware **fw, char* name)
451 struct flexcop_device *fc = fe->dvb->priv;
452 return request_firmware(fw, name, fc->dev);
455 static struct bcm3510_config air2pc_atsc_first_gen_config = {
456 .demod_address = 0x0f,
457 .request_firmware = flexcop_fe_request_firmware,
460 static int airstar_atsc1_attach(struct flexcop_device *fc,
461 struct i2c_adapter *i2c)
463 fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, i2c);
464 return fc->fe != NULL;
466 #else
467 #define airstar_atsc1_attach NULL
468 #endif
470 /* AirStar ATSC 2nd generation */
471 #if FE_SUPPORTED(NXT200X) && FE_SUPPORTED(PLL)
472 static struct nxt200x_config samsung_tbmv_config = {
473 .demod_address = 0x0a,
476 static int airstar_atsc2_attach(struct flexcop_device *fc,
477 struct i2c_adapter *i2c)
479 fc->fe = dvb_attach(nxt200x_attach, &samsung_tbmv_config, i2c);
480 if (!fc->fe)
481 return 0;
483 return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL,
484 DVB_PLL_SAMSUNG_TBMV);
486 #else
487 #define airstar_atsc2_attach NULL
488 #endif
490 /* AirStar ATSC 3rd generation */
491 #if FE_SUPPORTED(LGDT330X)
492 static struct lgdt330x_config air2pc_atsc_hd5000_config = {
493 .demod_address = 0x59,
494 .demod_chip = LGDT3303,
495 .serial_mpeg = 0x04,
496 .clock_polarity_flip = 1,
499 static int airstar_atsc3_attach(struct flexcop_device *fc,
500 struct i2c_adapter *i2c)
502 fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, i2c);
503 if (!fc->fe)
504 return 0;
506 return !!dvb_attach(simple_tuner_attach, fc->fe, i2c, 0x61,
507 TUNER_LG_TDVS_H06XF);
509 #else
510 #define airstar_atsc3_attach NULL
511 #endif
513 /* CableStar2 DVB-C */
514 #if FE_SUPPORTED(STV0297) && FE_SUPPORTED(PLL)
515 static u8 alps_tdee4_stv0297_inittab[] = {
516 0x80, 0x01,
517 0x80, 0x00,
518 0x81, 0x01,
519 0x81, 0x00,
520 0x00, 0x48,
521 0x01, 0x58,
522 0x03, 0x00,
523 0x04, 0x00,
524 0x07, 0x00,
525 0x08, 0x00,
526 0x30, 0xff,
527 0x31, 0x9d,
528 0x32, 0xff,
529 0x33, 0x00,
530 0x34, 0x29,
531 0x35, 0x55,
532 0x36, 0x80,
533 0x37, 0x6e,
534 0x38, 0x9c,
535 0x40, 0x1a,
536 0x41, 0xfe,
537 0x42, 0x33,
538 0x43, 0x00,
539 0x44, 0xff,
540 0x45, 0x00,
541 0x46, 0x00,
542 0x49, 0x04,
543 0x4a, 0x51,
544 0x4b, 0xf8,
545 0x52, 0x30,
546 0x53, 0x06,
547 0x59, 0x06,
548 0x5a, 0x5e,
549 0x5b, 0x04,
550 0x61, 0x49,
551 0x62, 0x0a,
552 0x70, 0xff,
553 0x71, 0x04,
554 0x72, 0x00,
555 0x73, 0x00,
556 0x74, 0x0c,
557 0x80, 0x20,
558 0x81, 0x00,
559 0x82, 0x30,
560 0x83, 0x00,
561 0x84, 0x04,
562 0x85, 0x22,
563 0x86, 0x08,
564 0x87, 0x1b,
565 0x88, 0x00,
566 0x89, 0x00,
567 0x90, 0x00,
568 0x91, 0x04,
569 0xa0, 0x86,
570 0xa1, 0x00,
571 0xa2, 0x00,
572 0xb0, 0x91,
573 0xb1, 0x0b,
574 0xc0, 0x5b,
575 0xc1, 0x10,
576 0xc2, 0x12,
577 0xd0, 0x02,
578 0xd1, 0x00,
579 0xd2, 0x00,
580 0xd3, 0x00,
581 0xd4, 0x02,
582 0xd5, 0x00,
583 0xde, 0x00,
584 0xdf, 0x01,
585 0xff, 0xff,
588 static struct stv0297_config alps_tdee4_stv0297_config = {
589 .demod_address = 0x1c,
590 .inittab = alps_tdee4_stv0297_inittab,
593 static int cablestar2_attach(struct flexcop_device *fc,
594 struct i2c_adapter *i2c)
596 fc->fc_i2c_adap[0].no_base_addr = 1;
597 fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c);
598 if (!fc->fe)
599 goto fail;
601 /* This tuner doesn't use the stv0297's I2C gate, but instead the
602 * tuner is connected to a different flexcop I2C adapter. */
603 if (fc->fe->ops.i2c_gate_ctrl)
604 fc->fe->ops.i2c_gate_ctrl(fc->fe, 0);
605 fc->fe->ops.i2c_gate_ctrl = NULL;
607 if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61,
608 &fc->fc_i2c_adap[2].i2c_adap, DVB_PLL_TDEE4))
609 goto fail;
611 return 1;
613 fail:
614 /* Reset for next frontend to try */
615 fc->fc_i2c_adap[0].no_base_addr = 0;
616 return 0;
618 #else
619 #define cablestar2_attach NULL
620 #endif
622 static struct {
623 flexcop_device_type_t type;
624 int (*attach)(struct flexcop_device *, struct i2c_adapter *);
625 } flexcop_frontends[] = {
626 { FC_SKY_REV27, skystar2_rev27_attach },
627 { FC_SKY_REV28, skystar2_rev28_attach },
628 { FC_SKY_REV26, skystar2_rev26_attach },
629 { FC_AIR_DVBT, airstar_dvbt_attach },
630 { FC_AIR_ATSC2, airstar_atsc2_attach },
631 { FC_AIR_ATSC3, airstar_atsc3_attach },
632 { FC_AIR_ATSC1, airstar_atsc1_attach },
633 { FC_CABLE, cablestar2_attach },
634 { FC_SKY_REV23, skystar2_rev23_attach },
637 /* try to figure out the frontend */
638 int flexcop_frontend_init(struct flexcop_device *fc)
640 int i;
641 for (i = 0; i < ARRAY_SIZE(flexcop_frontends); i++) {
642 if (!flexcop_frontends[i].attach)
643 continue;
644 /* type needs to be set before, because of some workarounds
645 * done based on the probed card type */
646 fc->dev_type = flexcop_frontends[i].type;
647 if (flexcop_frontends[i].attach(fc, &fc->fc_i2c_adap[0].i2c_adap))
648 goto fe_found;
649 /* Clean up partially attached frontend */
650 if (fc->fe) {
651 dvb_frontend_detach(fc->fe);
652 fc->fe = NULL;
655 fc->dev_type = FC_UNK;
656 err("no frontend driver found for this B2C2/FlexCop adapter");
657 return -ENODEV;
659 fe_found:
660 info("found '%s' .", fc->fe->ops.info.name);
661 if (dvb_register_frontend(&fc->dvb_adapter, fc->fe)) {
662 err("frontend registration failed!");
663 dvb_frontend_detach(fc->fe);
664 fc->fe = NULL;
665 return -EINVAL;
667 fc->init_state |= FC_STATE_FE_INIT;
668 return 0;
671 void flexcop_frontend_exit(struct flexcop_device *fc)
673 if (fc->init_state & FC_STATE_FE_INIT) {
674 dvb_unregister_frontend(fc->fe);
675 dvb_frontend_detach(fc->fe);
677 fc->init_state &= ~FC_STATE_FE_INIT;