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;
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)
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;
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 
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,
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)
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,
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)
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, false)) {
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, false)) {
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,
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)
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,
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)
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 {
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 */
638 int 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 
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 	}
667 	fc->init_state |= FC_STATE_FE_INIT;
668 	return 0;
669 }
670 
671 void 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