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