flexcop-fe-tuner.c revision e4671b6bc0b5b488adc5acbcfcbfa6661abec94e
1/*
2 * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
3 *
4 * flexcop-fe-tuner.c - methods for attaching a frontend and controlling DiSEqC.
5 *
6 * see flexcop.c for copyright information.
7 */
8#include <media/tuner.h>
9
10#include "flexcop.h"
11
12#include "stv0299.h"
13#include "mt352.h"
14#include "nxt200x.h"
15#include "bcm3510.h"
16#include "stv0297.h"
17#include "mt312.h"
18#include "lgdt330x.h"
19#include "dvb-pll.h"
20#include "tuner-simple.h"
21
22#include "s5h1420.h"
23#include "itd1000.h"
24
25#include "cx24123.h"
26#include "cx24113.h"
27
28#include "isl6421.h"
29
30/* lnb control */
31
32static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
33{
34	struct flexcop_device *fc = fe->dvb->priv;
35	flexcop_ibi_value v;
36	deb_tuner("polarity/voltage = %u\n", voltage);
37
38	v = fc->read_ibi_reg(fc, misc_204);
39	switch (voltage) {
40		case SEC_VOLTAGE_OFF:
41			v.misc_204.ACPI1_sig = 1;
42			break;
43		case SEC_VOLTAGE_13:
44			v.misc_204.ACPI1_sig = 0;
45			v.misc_204.LNB_L_H_sig = 0;
46			break;
47		case SEC_VOLTAGE_18:
48			v.misc_204.ACPI1_sig = 0;
49			v.misc_204.LNB_L_H_sig = 1;
50			break;
51		default:
52			err("unknown SEC_VOLTAGE value");
53			return -EINVAL;
54	}
55	return fc->write_ibi_reg(fc, misc_204, v);
56}
57
58static int flexcop_sleep(struct dvb_frontend* fe)
59{
60	struct flexcop_device *fc = fe->dvb->priv;
61/*	flexcop_ibi_value v = fc->read_ibi_reg(fc,misc_204); */
62
63	if (fc->fe_sleep)
64		return fc->fe_sleep(fe);
65
66/*	v.misc_204.ACPI3_sig = 1;
67	fc->write_ibi_reg(fc,misc_204,v);*/
68
69	return 0;
70}
71
72static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
73{
74	/* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */
75	struct flexcop_device *fc = fe->dvb->priv;
76	flexcop_ibi_value v;
77	u16 ax;
78	v.raw = 0;
79
80	deb_tuner("tone = %u\n",tone);
81
82	switch (tone) {
83		case SEC_TONE_ON:
84			ax = 0x01ff;
85			break;
86		case SEC_TONE_OFF:
87			ax = 0;
88			break;
89		default:
90			err("unknown SEC_TONE value");
91			return -EINVAL;
92	}
93
94	v.lnb_switch_freq_200.LNB_CTLPrescaler_sig = 1; /* divide by 2 */
95
96	v.lnb_switch_freq_200.LNB_CTLHighCount_sig = ax;
97	v.lnb_switch_freq_200.LNB_CTLLowCount_sig  = ax == 0 ? 0x1ff : ax;
98
99	return fc->write_ibi_reg(fc,lnb_switch_freq_200,v);
100}
101
102static void flexcop_diseqc_send_bit(struct dvb_frontend* fe, int data)
103{
104	flexcop_set_tone(fe, SEC_TONE_ON);
105	udelay(data ? 500 : 1000);
106	flexcop_set_tone(fe, SEC_TONE_OFF);
107	udelay(data ? 1000 : 500);
108}
109
110static void flexcop_diseqc_send_byte(struct dvb_frontend* fe, int data)
111{
112	int i, par = 1, d;
113
114	for (i = 7; i >= 0; i--) {
115		d = (data >> i) & 1;
116		par ^= d;
117		flexcop_diseqc_send_bit(fe, d);
118	}
119
120	flexcop_diseqc_send_bit(fe, par);
121}
122
123static int flexcop_send_diseqc_msg(struct dvb_frontend* fe, int len, u8 *msg, unsigned long burst)
124{
125	int i;
126
127	flexcop_set_tone(fe, SEC_TONE_OFF);
128	mdelay(16);
129
130	for (i = 0; i < len; i++)
131		flexcop_diseqc_send_byte(fe,msg[i]);
132
133	mdelay(16);
134
135	if (burst != -1) {
136		if (burst)
137			flexcop_diseqc_send_byte(fe, 0xff);
138		else {
139			flexcop_set_tone(fe, SEC_TONE_ON);
140			udelay(12500);
141			flexcop_set_tone(fe, SEC_TONE_OFF);
142		}
143		msleep(20);
144	}
145	return 0;
146}
147
148static int flexcop_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
149{
150	return flexcop_send_diseqc_msg(fe, cmd->msg_len, cmd->msg, 0);
151}
152
153static int flexcop_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
154{
155	return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd);
156}
157
158/* dvb-s stv0299 */
159static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio)
160{
161	u8 aclk = 0;
162	u8 bclk = 0;
163
164	if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
165	else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
166	else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
167	else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
168	else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
169	else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
170
171	stv0299_writereg (fe, 0x13, aclk);
172	stv0299_writereg (fe, 0x14, bclk);
173	stv0299_writereg (fe, 0x1f, (ratio >> 16) & 0xff);
174	stv0299_writereg (fe, 0x20, (ratio >>  8) & 0xff);
175	stv0299_writereg (fe, 0x21, (ratio      ) & 0xf0);
176
177	return 0;
178}
179
180static int samsung_tbmu24112_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params)
181{
182	u8 buf[4];
183	u32 div;
184	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
185	struct flexcop_device *fc = fe->dvb->priv;
186
187	div = params->frequency / 125;
188
189	buf[0] = (div >> 8) & 0x7f;
190	buf[1] = div & 0xff;
191	buf[2] = 0x84;  /* 0xC4 */
192	buf[3] = 0x08;
193
194	if (params->frequency < 1500000)
195		buf[3] |= 0x10;
196
197	if (fe->ops.i2c_gate_ctrl)
198		fe->ops.i2c_gate_ctrl(fe, 1);
199	if (i2c_transfer(&fc->fc_i2c_adap[0].i2c_adap, &msg, 1) != 1)
200		return -EIO;
201	return 0;
202}
203
204static u8 samsung_tbmu24112_inittab[] = {
205	     0x01, 0x15,
206	     0x02, 0x30,
207	     0x03, 0x00,
208	     0x04, 0x7D,
209	     0x05, 0x35,
210	     0x06, 0x02,
211	     0x07, 0x00,
212	     0x08, 0xC3,
213	     0x0C, 0x00,
214	     0x0D, 0x81,
215	     0x0E, 0x23,
216	     0x0F, 0x12,
217	     0x10, 0x7E,
218	     0x11, 0x84,
219	     0x12, 0xB9,
220	     0x13, 0x88,
221	     0x14, 0x89,
222	     0x15, 0xC9,
223	     0x16, 0x00,
224	     0x17, 0x5C,
225	     0x18, 0x00,
226	     0x19, 0x00,
227	     0x1A, 0x00,
228	     0x1C, 0x00,
229	     0x1D, 0x00,
230	     0x1E, 0x00,
231	     0x1F, 0x3A,
232	     0x20, 0x2E,
233	     0x21, 0x80,
234	     0x22, 0xFF,
235	     0x23, 0xC1,
236	     0x28, 0x00,
237	     0x29, 0x1E,
238	     0x2A, 0x14,
239	     0x2B, 0x0F,
240	     0x2C, 0x09,
241	     0x2D, 0x05,
242	     0x31, 0x1F,
243	     0x32, 0x19,
244	     0x33, 0xFE,
245	     0x34, 0x93,
246	     0xff, 0xff,
247};
248
249static struct stv0299_config samsung_tbmu24112_config = {
250	.demod_address = 0x68,
251	.inittab = samsung_tbmu24112_inittab,
252	.mclk = 88000000UL,
253	.invert = 0,
254	.skip_reinit = 0,
255	.lock_output = STV0299_LOCKOUTPUT_LK,
256	.volt13_op0_op1 = STV0299_VOLT13_OP1,
257	.min_delay_ms = 100,
258	.set_symbol_rate = samsung_tbmu24112_set_symbol_rate,
259};
260
261/* dvb-t mt352 */
262static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
263{
264	static u8 mt352_clock_config [] = { 0x89, 0x18, 0x2d };
265	static u8 mt352_reset [] = { 0x50, 0x80 };
266	static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
267	static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0xa1 };
268	static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
269
270	mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
271	udelay(2000);
272	mt352_write(fe, mt352_reset, sizeof(mt352_reset));
273	mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
274
275	mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
276	mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
277
278	return 0;
279}
280
281static int samsung_tdtc9251dh0_calc_regs(struct dvb_frontend* fe, struct dvb_frontend_parameters *params, u8* pllbuf, int buf_len)
282{
283	u32 div;
284	unsigned char bs = 0;
285
286	if (buf_len < 5)
287		return -EINVAL;
288
289	#define IF_FREQUENCYx6 217    /* 6 * 36.16666666667MHz */
290	div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
291
292	if (params->frequency >= 48000000 && params->frequency <= 154000000) bs = 0x09;
293	if (params->frequency >= 161000000 && params->frequency <= 439000000) bs = 0x0a;
294	if (params->frequency >= 447000000 && params->frequency <= 863000000) bs = 0x08;
295
296	pllbuf[0] = 0x61;
297	pllbuf[1] = div >> 8;
298	pllbuf[2] = div & 0xff;
299	pllbuf[3] = 0xcc;
300	pllbuf[4] = bs;
301
302	return 5;
303}
304
305static struct mt352_config samsung_tdtc9251dh0_config = {
306	.demod_address = 0x0f,
307	.demod_init    = samsung_tdtc9251dh0_demod_init,
308};
309
310static int flexcop_fe_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
311{
312	struct flexcop_device *fc = fe->dvb->priv;
313	return request_firmware(fw, name, fc->dev);
314}
315
316static struct lgdt330x_config air2pc_atsc_hd5000_config = {
317	.demod_address       = 0x59,
318	.demod_chip          = LGDT3303,
319	.serial_mpeg         = 0x04,
320	.clock_polarity_flip = 1,
321};
322
323static struct nxt200x_config samsung_tbmv_config = {
324	.demod_address    = 0x0a,
325};
326
327static struct bcm3510_config air2pc_atsc_first_gen_config = {
328	.demod_address    = 0x0f,
329	.request_firmware = flexcop_fe_request_firmware,
330};
331
332static int skystar23_samsung_tbdu18132_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params)
333{
334	u8 buf[4];
335	u32 div;
336	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
337	struct flexcop_device *fc = fe->dvb->priv;
338
339	div = (params->frequency + (125/2)) / 125;
340
341	buf[0] = (div >> 8) & 0x7f;
342	buf[1] = (div >> 0) & 0xff;
343	buf[2] = 0x84 | ((div >> 10) & 0x60);
344	buf[3] = 0x80;
345
346	if (params->frequency < 1550000)
347		buf[3] |= 0x02;
348
349	if (fe->ops.i2c_gate_ctrl)
350		fe->ops.i2c_gate_ctrl(fe, 1);
351	if (i2c_transfer(&fc->fc_i2c_adap[0].i2c_adap, &msg, 1) != 1)
352		return -EIO;
353	return 0;
354}
355
356static struct mt312_config skystar23_samsung_tbdu18132_config = {
357
358	.demod_address = 0x0e,
359};
360
361static int alps_tdee4_stv0297_tuner_set_params(struct dvb_frontend* fe,
362					       struct dvb_frontend_parameters *fep)
363{
364	struct flexcop_device *fc = fe->dvb->priv;
365	u8 buf[4];
366	u16 div;
367	int ret;
368
369/*  62.5 kHz * 10 */
370#define REF_FREQ    625
371#define FREQ_OFFSET 36125
372
373	div = ((fep->frequency/1000 + FREQ_OFFSET ) * 10)  / REF_FREQ; // 4 MHz = 4000 KHz
374
375	buf[0] = (u8)( div >> 8) & 0x7f;
376	buf[1] = (u8)        div & 0xff;
377
378/* F(osc) = N * Reference Freq. (62.5 kHz)
379 * byte 2 :  0 N14 N13 N12 N11 N10 N9  N8
380 * byte 3 : N7 N6  N5  N4  N3  N2  N1  N0
381 * byte 4 : 1  *   *   AGD R3  R2  R1  R0
382 * byte 5 : C1 *   RE  RTS BS4 BS3 BS2 BS1
383 * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95 */
384	buf[2] = 0x95;
385
386// Range(MHz)  C1 *  RE RTS BS4 BS3 BS2 BS1  Byte 5
387//  47 - 153   0  *  0   0   0   0   0   1   0x01
388// 153 - 430   0  *  0   0   0   0   1   0   0x02
389// 430 - 822   0  *  0   0   1   0   0   0   0x08
390// 822 - 862   1  *  0   0   1   0   0   0   0x88
391
392	     if (fep->frequency <= 153000000) buf[3] = 0x01;
393	else if (fep->frequency <= 430000000) buf[3] = 0x02;
394	else if (fep->frequency <= 822000000) buf[3] = 0x08;
395	else buf[3] = 0x88;
396
397	if (fe->ops.i2c_gate_ctrl)
398		fe->ops.i2c_gate_ctrl(fe, 0);
399	deb_tuner("tuner buffer for %d Hz: %x %x %x %x\n",fep->frequency, buf[0],buf[1],buf[2],buf[3]);
400	ret = fc->i2c_request(&fc->fc_i2c_adap[2],
401		FC_WRITE, 0x61, buf[0], &buf[1], 3);
402	deb_tuner("tuner write returned: %d\n",ret);
403
404	return ret;
405}
406
407static u8 alps_tdee4_stv0297_inittab[] = {
408	0x80, 0x01,
409	0x80, 0x00,
410	0x81, 0x01,
411	0x81, 0x00,
412	0x00, 0x48,
413	0x01, 0x58,
414	0x03, 0x00,
415	0x04, 0x00,
416	0x07, 0x00,
417	0x08, 0x00,
418	0x30, 0xff,
419	0x31, 0x9d,
420	0x32, 0xff,
421	0x33, 0x00,
422	0x34, 0x29,
423	0x35, 0x55,
424	0x36, 0x80,
425	0x37, 0x6e,
426	0x38, 0x9c,
427	0x40, 0x1a,
428	0x41, 0xfe,
429	0x42, 0x33,
430	0x43, 0x00,
431	0x44, 0xff,
432	0x45, 0x00,
433	0x46, 0x00,
434	0x49, 0x04,
435	0x4a, 0x51,
436	0x4b, 0xf8,
437	0x52, 0x30,
438	0x53, 0x06,
439	0x59, 0x06,
440	0x5a, 0x5e,
441	0x5b, 0x04,
442	0x61, 0x49,
443	0x62, 0x0a,
444	0x70, 0xff,
445	0x71, 0x04,
446	0x72, 0x00,
447	0x73, 0x00,
448	0x74, 0x0c,
449	0x80, 0x20,
450	0x81, 0x00,
451	0x82, 0x30,
452	0x83, 0x00,
453	0x84, 0x04,
454	0x85, 0x22,
455	0x86, 0x08,
456	0x87, 0x1b,
457	0x88, 0x00,
458	0x89, 0x00,
459	0x90, 0x00,
460	0x91, 0x04,
461	0xa0, 0x86,
462	0xa1, 0x00,
463	0xa2, 0x00,
464	0xb0, 0x91,
465	0xb1, 0x0b,
466	0xc0, 0x5b,
467	0xc1, 0x10,
468	0xc2, 0x12,
469	0xd0, 0x02,
470	0xd1, 0x00,
471	0xd2, 0x00,
472	0xd3, 0x00,
473	0xd4, 0x02,
474	0xd5, 0x00,
475	0xde, 0x00,
476	0xdf, 0x01,
477	0xff, 0xff,
478};
479
480static struct stv0297_config alps_tdee4_stv0297_config = {
481	.demod_address = 0x1c,
482	.inittab = alps_tdee4_stv0297_inittab,
483//	.invert = 1,
484//	.pll_set = alps_tdee4_stv0297_pll_set,
485};
486
487
488/* SkyStar2 rev2.7 (a/u) */
489static struct s5h1420_config skystar2_rev2_7_s5h1420_config = {
490	.demod_address = 0x53,
491	.invert = 1,
492	.repeated_start_workaround = 1,
493};
494
495static struct itd1000_config skystar2_rev2_7_itd1000_config = {
496	.i2c_address = 0x61,
497};
498
499/* SkyStar2 rev2.8 */
500static struct cx24123_config skystar2_rev2_8_cx24123_config = {
501	.demod_address = 0x55,
502	.dont_use_pll = 1,
503	.agc_callback = cx24113_agc_callback,
504};
505
506static const struct cx24113_config skystar2_rev2_8_cx24113_config = {
507	.i2c_addr = 0x54,
508	.xtal_khz = 10111,
509};
510
511/* try to figure out the frontend, each card/box can have on of the following list */
512int flexcop_frontend_init(struct flexcop_device *fc)
513{
514	struct dvb_frontend_ops *ops;
515	struct i2c_adapter *i2c = &fc->fc_i2c_adap[0].i2c_adap;
516	struct i2c_adapter *i2c_tuner;
517
518	/* enable no_base_addr - no repeated start when reading */
519	fc->fc_i2c_adap[0].no_base_addr = 1;
520	fc->fe = dvb_attach(s5h1420_attach, &skystar2_rev2_7_s5h1420_config, i2c);
521	if (fc->fe != NULL) {
522		flexcop_ibi_value r108;
523		i2c_tuner = s5h1420_get_tuner_i2c_adapter(fc->fe);
524		ops = &fc->fe->ops;
525
526		fc->fe_sleep = ops->sleep;
527		ops->sleep   = flexcop_sleep;
528
529		fc->dev_type = FC_SKY_REV27;
530
531		/* enable no_base_addr - no repeated start when reading */
532		fc->fc_i2c_adap[2].no_base_addr = 1;
533		if (dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap, 0x08, 1, 1) == NULL)
534			err("ISL6421 could NOT be attached");
535		else
536			info("ISL6421 successfully attached");
537
538		/* the ITD1000 requires a lower i2c clock - it slows down the stuff for everyone - but is it a problem ? */
539		r108.raw = 0x00000506;
540		fc->write_ibi_reg(fc, tw_sm_c_108, r108);
541		if (i2c_tuner) {
542			if (dvb_attach(itd1000_attach, fc->fe, i2c_tuner, &skystar2_rev2_7_itd1000_config) == NULL)
543				err("ITD1000 could NOT be attached");
544			else
545				info("ITD1000 successfully attached");
546		}
547		goto fe_found;
548	}
549	fc->fc_i2c_adap[0].no_base_addr = 0; /* for the next devices we need it again */
550
551	/* try the sky v2.8 (cx24123, isl6421) */
552	fc->fe = dvb_attach(cx24123_attach,
553		&skystar2_rev2_8_cx24123_config, i2c);
554	if (fc->fe != NULL) {
555		i2c_tuner = cx24123_get_tuner_i2c_adapter(fc->fe);
556		if (i2c_tuner != NULL) {
557			if (dvb_attach(cx24113_attach, fc->fe,
558					&skystar2_rev2_8_cx24113_config,
559					i2c_tuner) == NULL)
560				err("CX24113 could NOT be attached");
561			else
562				info("CX24113 successfully attached");
563		}
564
565		fc->dev_type = FC_SKY_REV28;
566
567		fc->fc_i2c_adap[2].no_base_addr = 1;
568		if (dvb_attach(isl6421_attach, fc->fe,
569		       &fc->fc_i2c_adap[2].i2c_adap, 0x08, 0, 0) == NULL)
570			err("ISL6421 could NOT be attached");
571		else
572			info("ISL6421 successfully attached");
573
574		/* TODO on i2c_adap[1] addr 0x11 (EEPROM) there seems to be an
575		 * IR-receiver (PIC16F818) - but the card has no input for
576		 * that ??? */
577
578		goto fe_found;
579    }
580
581	/* try the sky v2.6 (stv0299/Samsung tbmu24112(sl1935)) */
582	fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, i2c);
583	if (fc->fe != NULL) {
584		ops = &fc->fe->ops;
585
586		ops->tuner_ops.set_params = samsung_tbmu24112_tuner_set_params;
587
588		ops->set_voltage = flexcop_set_voltage;
589
590		fc->fe_sleep = ops->sleep;
591		ops->sleep = flexcop_sleep;
592
593		fc->dev_type = FC_SKY;
594		goto fe_found;
595	}
596
597	/* try the air dvb-t (mt352/Samsung tdtc9251dh0(??)) */
598	fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, i2c);
599	if (fc->fe != NULL) {
600		fc->dev_type = FC_AIR_DVB;
601		fc->fe->ops.tuner_ops.calc_regs = samsung_tdtc9251dh0_calc_regs;
602		goto fe_found;
603	}
604
605	/* try the air atsc 2nd generation (nxt2002) */
606	fc->fe = dvb_attach(nxt200x_attach, &samsung_tbmv_config, i2c);
607	if (fc->fe != NULL) {
608		fc->dev_type = FC_AIR_ATSC2;
609		dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, DVB_PLL_SAMSUNG_TBMV);
610		goto fe_found;
611	}
612
613	fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, i2c);
614	if (fc->fe != NULL) {
615		fc->dev_type = FC_AIR_ATSC3;
616		dvb_attach(simple_tuner_attach, fc->fe, i2c, 0x61,
617				TUNER_LG_TDVS_H06XF);
618		goto fe_found;
619	}
620
621	/* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */
622	fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, i2c);
623	if (fc->fe != NULL) {
624		fc->dev_type = FC_AIR_ATSC1;
625		goto fe_found;
626	}
627
628	/* try the cable dvb (stv0297) */
629	fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c);
630	if (fc->fe != NULL) {
631		fc->dev_type = FC_CABLE;
632		fc->fe->ops.tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params;
633		goto fe_found;
634	}
635
636	/* try the sky v2.3 (vp310/Samsung tbdu18132(tsa5059)) */
637	fc->fe = dvb_attach(mt312_attach,
638		&skystar23_samsung_tbdu18132_config, i2c);
639	if (fc->fe != NULL) {
640		ops = &fc->fe->ops;
641
642		ops->tuner_ops.set_params = skystar23_samsung_tbdu18132_tuner_set_params;
643
644		ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
645		ops->diseqc_send_burst      = flexcop_diseqc_send_burst;
646		ops->set_tone               = flexcop_set_tone;
647		ops->set_voltage            = flexcop_set_voltage;
648
649		fc->fe_sleep                = ops->sleep;
650		ops->sleep                  = flexcop_sleep;
651
652		fc->dev_type                = FC_SKY_OLD;
653		goto fe_found;
654	}
655
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		ops = &fc->fe->ops;
664		if (ops->release != NULL)
665			ops->release(fc->fe);
666		fc->fe = NULL;
667		return -EINVAL;
668	}
669	fc->init_state |= FC_STATE_FE_INIT;
670	return 0;
671}
672
673void flexcop_frontend_exit(struct flexcop_device *fc)
674{
675	if (fc->init_state & FC_STATE_FE_INIT) {
676		dvb_unregister_frontend(fc->fe);
677		dvb_frontend_detach(fc->fe);
678	}
679
680	fc->init_state &= ~FC_STATE_FE_INIT;
681}
682