• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/media/dvb/b2c2/
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)
31static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
32{
33	struct flexcop_device *fc = fe->dvb->priv;
34	flexcop_ibi_value v;
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)
59static 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)
70static 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;
74	flexcop_ibi_value v;
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
97static 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
105static 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
116static 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
142static 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
148static 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
154static struct mt312_config skystar23_samsung_tbdu18132_config = {
155	.demod_address = 0x0e,
156};
157
158static 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,
168			DVB_PLL_SAMSUNG_TBDU18132))
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)
186static 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
214static 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
259static 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
271static 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,
279			DVB_PLL_SAMSUNG_TBMU24112))
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)
294static 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
301static struct itd1000_config skystar2_rev2_7_itd1000_config = {
302	.i2c_address = 0x61,
303};
304
305static 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
347fail_isl:
348	fc->fc_i2c_adap[2].no_base_addr = 0;
349fail:
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)
360static 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
366static const struct cx24113_config skystar2_rev2_8_cx24113_config = {
367	.i2c_addr = 0x54,
368	.xtal_khz = 10111,
369};
370
371static 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)
410static 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
427static struct mt352_config samsung_tdtc9251dh0_config = {
428	.demod_address = 0x0f,
429	.demod_init    = samsung_tdtc9251dh0_demod_init,
430};
431
432static 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,
440			    DVB_PLL_SAMSUNG_TDTC9251DH0);
441}
442#else
443#define airstar_dvbt_attach NULL
444#endif
445
446/* AirStar ATSC 1st generation */
447#if FE_SUPPORTED(BCM3510)
448static 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
455static struct bcm3510_config air2pc_atsc_first_gen_config = {
456	.demod_address    = 0x0f,
457	.request_firmware = flexcop_fe_request_firmware,
458};
459
460static 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)
472static struct nxt200x_config samsung_tbmv_config = {
473	.demod_address = 0x0a,
474};
475
476static 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,
484			    DVB_PLL_SAMSUNG_TBMV);
485}
486#else
487#define airstar_atsc2_attach NULL
488#endif
489
490/* AirStar ATSC 3rd generation */
491#if FE_SUPPORTED(LGDT330X)
492static 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
499static 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)
515static 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
588static struct stv0297_config alps_tdee4_stv0297_config = {
589	.demod_address = 0x1c,
590	.inittab = alps_tdee4_stv0297_inittab,
591};
592
593static 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
613fail:
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
622static 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 },
635};
636
637/* try to figure out the frontend */
638int flexcop_frontend_init(struct flexcop_device *fc)
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
659fe_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	}
667	fc->init_state |= FC_STATE_FE_INIT;
668	return 0;
669}
670
671void flexcop_frontend_exit(struct flexcop_device *fc)
672{
673	if (fc->init_state & FC_STATE_FE_INIT) {
674		dvb_unregister_frontend(fc->fe);
675		dvb_frontend_detach(fc->fe);
676	}
677	fc->init_state &= ~FC_STATE_FE_INIT;
678}
679