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