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))
31static 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)
42static 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)
71static 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)
82static 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
109static 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
117static 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
128static 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
154static 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
160static 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
166static struct mt312_config skystar23_samsung_tbdu18132_config = {
167	.demod_address = 0x0e,
168};
169
170static 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)
198static 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
226static 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
271static 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
283static 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)
306static 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
313static struct itd1000_config skystar2_rev2_7_itd1000_config = {
314	.i2c_address = 0x61,
315};
316
317static 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
359fail_isl:
360	fc->fc_i2c_adap[2].no_base_addr = 0;
361fail:
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)
372static 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
378static const struct cx24113_config skystar2_rev2_8_cx24113_config = {
379	.i2c_addr = 0x54,
380	.xtal_khz = 10111,
381};
382
383static 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)
422static 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
439static struct mt352_config samsung_tdtc9251dh0_config = {
440	.demod_address = 0x0f,
441	.demod_init    = samsung_tdtc9251dh0_demod_init,
442};
443
444static 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)
460static struct bcm3510_config air2pc_atsc_first_gen_config = {
461	.demod_address    = 0x0f,
462	.request_firmware = flexcop_fe_request_firmware,
463};
464
465static 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)
477static const struct nxt200x_config samsung_tbmv_config = {
478	.demod_address = 0x0a,
479};
480
481static 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)
497static struct lgdt330x_config air2pc_atsc_hd5000_config = {
498	.demod_chip          = LGDT3303,
499	.serial_mpeg         = 0x04,
500	.clock_polarity_flip = 1,
501};
502
503static 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)
520static 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
593static struct stv0297_config alps_tdee4_stv0297_config = {
594	.demod_address = 0x1c,
595	.inittab = alps_tdee4_stv0297_inittab,
596};
597
598static 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
618fail:
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)
629static 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
637static 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
664static 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 */
681int 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
702fe_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
714void 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