Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
flexcop-fe-tuner.c
Go to the documentation of this file.
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"
22 
23 
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)))
28 
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)
32 {
33  struct flexcop_device *fc = fe->dvb->priv;
35  deb_tuner("polarity/voltage = %u\n", voltage);
36 
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;
53  }
54  return fc->write_ibi_reg(fc, misc_204, v);
55 }
56 #endif
57 
58 #if FE_SUPPORTED(S5H1420) || FE_SUPPORTED(STV0299) || FE_SUPPORTED(MT312)
59 static int flexcop_sleep(struct dvb_frontend* fe)
60 {
61  struct flexcop_device *fc = fe->dvb->priv;
62  if (fc->fe_sleep)
63  return fc->fe_sleep(fe);
64  return 0;
65 }
66 #endif
67 
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)
71 {
72 /* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */
73  struct flexcop_device *fc = fe->dvb->priv;
75  u16 ax;
76  v.raw = 0;
77  deb_tuner("tone = %u\n",tone);
78 
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;
89  }
90 
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);
95 }
96 
97 static void flexcop_diseqc_send_bit(struct dvb_frontend* fe, int data)
98 {
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);
103 }
104 
105 static void flexcop_diseqc_send_byte(struct dvb_frontend* fe, int data)
106 {
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);
112  }
113  flexcop_diseqc_send_bit(fe, par);
114 }
115 
116 static int flexcop_send_diseqc_msg(struct dvb_frontend *fe,
117  int len, u8 *msg, unsigned long burst)
118 {
119  int i;
120 
121  flexcop_set_tone(fe, SEC_TONE_OFF);
122  mdelay(16);
123 
124  for (i = 0; i < len; i++)
125  flexcop_diseqc_send_byte(fe,msg[i]);
126  mdelay(16);
127 
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);
136  }
137  msleep(20);
138  }
139  return 0;
140 }
141 
142 static int flexcop_diseqc_send_master_cmd(struct dvb_frontend *fe,
143  struct dvb_diseqc_master_cmd *cmd)
144 {
145  return flexcop_send_diseqc_msg(fe, cmd->msg_len, cmd->msg, 0);
146 }
147 
148 static int flexcop_diseqc_send_burst(struct dvb_frontend *fe,
149  fe_sec_mini_cmd_t minicmd)
150 {
151  return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd);
152 }
153 
154 static struct mt312_config skystar23_samsung_tbdu18132_config = {
155  .demod_address = 0x0e,
156 };
157 
158 static int skystar2_rev23_attach(struct flexcop_device *fc,
159  struct i2c_adapter *i2c)
160 {
161  struct dvb_frontend_ops *ops;
162 
163  fc->fe = dvb_attach(mt312_attach, &skystar23_samsung_tbdu18132_config, i2c);
164  if (!fc->fe)
165  return 0;
166 
167  if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c,
169  return 0;
170 
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;
179 }
180 #else
181 #define skystar2_rev23_attach NULL
182 #endif
183 
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)
188 {
189  u8 aclk = 0;
190  u8 bclk = 0;
191 
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;
204  }
205 
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;
212 }
213 
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,
257 };
258 
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,
269 };
270 
271 static int skystar2_rev26_attach(struct flexcop_device *fc,
272  struct i2c_adapter *i2c)
273 {
274  fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, i2c);
275  if (!fc->fe)
276  return 0;
277 
278  if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c,
280  return 0;
281 
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;
286 
287 }
288 #else
289 #define skystar2_rev26_attach NULL
290 #endif
291 
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,
299 };
300 
301 static struct itd1000_config skystar2_rev2_7_itd1000_config = {
302  .i2c_address = 0x61,
303 };
304 
305 static int skystar2_rev27_attach(struct flexcop_device *fc,
306  struct i2c_adapter *i2c)
307 {
308  flexcop_ibi_value r108;
309  struct i2c_adapter *i2c_tuner;
310 
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;
317 
318  i2c_tuner = s5h1420_get_tuner_i2c_adapter(fc->fe);
319  if (!i2c_tuner)
320  goto fail;
321 
322  fc->fe_sleep = fc->fe->ops.sleep;
323  fc->fe->ops.sleep = flexcop_sleep;
324 
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;
331  }
332  info("ISL6421 successfully attached");
333 
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;
342  }
343  info("ITD1000 successfully attached");
344 
345  return 1;
346 
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;
353 }
354 #else
355 #define skystar2_rev27_attach NULL
356 #endif
357 
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,
364 };
365 
366 static const struct cx24113_config skystar2_rev2_8_cx24113_config = {
367  .i2c_addr = 0x54,
368  .xtal_khz = 10111,
369 };
370 
371 static int skystar2_rev28_attach(struct flexcop_device *fc,
372  struct i2c_adapter *i2c)
373 {
374  struct i2c_adapter *i2c_tuner;
375 
376  fc->fe = dvb_attach(cx24123_attach, &skystar2_rev2_8_cx24123_config,
377  i2c);
378  if (!fc->fe)
379  return 0;
380 
381  i2c_tuner = cx24123_get_tuner_i2c_adapter(fc->fe);
382  if (!i2c_tuner)
383  return 0;
384 
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;
389  }
390  info("CX24113 successfully attached");
391 
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;
398  }
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;
403 }
404 #else
405 #define skystar2_rev28_attach NULL
406 #endif
407 
408 /* AirStar DVB-T */
409 #if FE_SUPPORTED(MT352) && FE_SUPPORTED(PLL)
410 static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe)
411 {
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 };
417 
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;
425 }
426 
427 static struct mt352_config samsung_tdtc9251dh0_config = {
428  .demod_address = 0x0f,
429  .demod_init = samsung_tdtc9251dh0_demod_init,
430 };
431 
432 static int airstar_dvbt_attach(struct flexcop_device *fc,
433  struct i2c_adapter *i2c)
434 {
435  fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, i2c);
436  if (!fc->fe)
437  return 0;
438 
439  return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL,
441 }
442 #else
443 #define airstar_dvbt_attach NULL
444 #endif
445 
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)
450 {
451  struct flexcop_device *fc = fe->dvb->priv;
452  return request_firmware(fw, name, fc->dev);
453 }
454 
455 static struct bcm3510_config air2pc_atsc_first_gen_config = {
456  .demod_address = 0x0f,
457  .request_firmware = flexcop_fe_request_firmware,
458 };
459 
460 static int airstar_atsc1_attach(struct flexcop_device *fc,
461  struct i2c_adapter *i2c)
462 {
463  fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, i2c);
464  return fc->fe != NULL;
465 }
466 #else
467 #define airstar_atsc1_attach NULL
468 #endif
469 
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,
474 };
475 
476 static int airstar_atsc2_attach(struct flexcop_device *fc,
477  struct i2c_adapter *i2c)
478 {
479  fc->fe = dvb_attach(nxt200x_attach, &samsung_tbmv_config, i2c);
480  if (!fc->fe)
481  return 0;
482 
483  return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL,
485 }
486 #else
487 #define airstar_atsc2_attach NULL
488 #endif
489 
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,
497 };
498 
499 static int airstar_atsc3_attach(struct flexcop_device *fc,
500  struct i2c_adapter *i2c)
501 {
502  fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, i2c);
503  if (!fc->fe)
504  return 0;
505 
506  return !!dvb_attach(simple_tuner_attach, fc->fe, i2c, 0x61,
507  TUNER_LG_TDVS_H06XF);
508 }
509 #else
510 #define airstar_atsc3_attach NULL
511 #endif
512 
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,
586 };
587 
588 static struct stv0297_config alps_tdee4_stv0297_config = {
589  .demod_address = 0x1c,
590  .inittab = alps_tdee4_stv0297_inittab,
591 };
592 
593 static int cablestar2_attach(struct flexcop_device *fc,
594  struct i2c_adapter *i2c)
595 {
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;
600 
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;
606 
607  if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61,
608  &fc->fc_i2c_adap[2].i2c_adap, DVB_PLL_TDEE4))
609  goto fail;
610 
611  return 1;
612 
613 fail:
614  /* Reset for next frontend to try */
615  fc->fc_i2c_adap[0].no_base_addr = 0;
616  return 0;
617 }
618 #else
619 #define cablestar2_attach NULL
620 #endif
621 
622 static struct {
624  int (*attach)(struct flexcop_device *, struct i2c_adapter *);
625 } flexcop_frontends[] = {
635 };
636 
637 /* try to figure out the frontend */
639 {
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;
653  }
654  }
655  fc->dev_type = FC_UNK;
656  err("no frontend driver found for this B2C2/FlexCop adapter");
657  return -ENODEV;
658 
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;
666  }
668  return 0;
669 }
670 
672 {
673  if (fc->init_state & FC_STATE_FE_INIT) {
675  dvb_frontend_detach(fc->fe);
676  }
678 }