blob: 9f758042ea473b358f4e7f5ff4e67ca23d68e7ea [file] [log] [blame]
Johannes Stezenbach2add87a2005-05-16 21:54:10 -07001/*
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 "flexcop.h"
9
10#include "stv0299.h"
11#include "mt352.h"
Michael Krufky46365f32006-01-23 09:52:39 -020012#include "nxt200x.h"
Johannes Stezenbach55f51ef2005-06-23 22:02:41 -070013#include "bcm3510.h"
Johannes Stezenbach2add87a2005-05-16 21:54:10 -070014#include "stv0297.h"
15#include "mt312.h"
Michael Krufkyc0b11b92005-11-08 21:35:32 -080016#include "lgdt330x.h"
Michael Krufky81ad3422006-04-24 23:21:46 -030017#include "lg_h06xf.h"
Michael Krufkyc0b11b92005-11-08 21:35:32 -080018#include "dvb-pll.h"
Johannes Stezenbach2add87a2005-05-16 21:54:10 -070019
20/* lnb control */
21
22static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
23{
24 struct flexcop_device *fc = fe->dvb->priv;
25 flexcop_ibi_value v;
26 deb_tuner("polarity/voltage = %u\n", voltage);
27
28 v = fc->read_ibi_reg(fc, misc_204);
29 switch (voltage) {
30 case SEC_VOLTAGE_OFF:
31 v.misc_204.ACPI1_sig = 1;
32 break;
33 case SEC_VOLTAGE_13:
34 v.misc_204.ACPI1_sig = 0;
35 v.misc_204.LNB_L_H_sig = 0;
36 break;
37 case SEC_VOLTAGE_18:
38 v.misc_204.ACPI1_sig = 0;
39 v.misc_204.LNB_L_H_sig = 1;
40 break;
41 default:
42 err("unknown SEC_VOLTAGE value");
43 return -EINVAL;
44 }
45 return fc->write_ibi_reg(fc, misc_204, v);
46}
47
48static int flexcop_sleep(struct dvb_frontend* fe)
49{
50 struct flexcop_device *fc = fe->dvb->priv;
51/* flexcop_ibi_value v = fc->read_ibi_reg(fc,misc_204); */
52
53 if (fc->fe_sleep)
54 return fc->fe_sleep(fe);
55
56/* v.misc_204.ACPI3_sig = 1;
57 fc->write_ibi_reg(fc,misc_204,v);*/
58
59 return 0;
60}
61
62static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
63{
64 /* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */
65 struct flexcop_device *fc = fe->dvb->priv;
66 flexcop_ibi_value v;
67 u16 ax;
68 v.raw = 0;
69
70 deb_tuner("tone = %u\n",tone);
71
72 switch (tone) {
73 case SEC_TONE_ON:
74 ax = 0x01ff;
75 break;
76 case SEC_TONE_OFF:
77 ax = 0;
78 break;
79 default:
80 err("unknown SEC_TONE value");
81 return -EINVAL;
82 }
83
84 v.lnb_switch_freq_200.LNB_CTLPrescaler_sig = 1; /* divide by 2 */
85
Johannes Stezenbach958706c2005-05-16 21:54:19 -070086 v.lnb_switch_freq_200.LNB_CTLHighCount_sig = ax;
87 v.lnb_switch_freq_200.LNB_CTLLowCount_sig = ax == 0 ? 0x1ff : ax;
Johannes Stezenbach2add87a2005-05-16 21:54:10 -070088
89 return fc->write_ibi_reg(fc,lnb_switch_freq_200,v);
90}
91
92static void flexcop_diseqc_send_bit(struct dvb_frontend* fe, int data)
93{
94 flexcop_set_tone(fe, SEC_TONE_ON);
95 udelay(data ? 500 : 1000);
96 flexcop_set_tone(fe, SEC_TONE_OFF);
97 udelay(data ? 1000 : 500);
98}
99
100static void flexcop_diseqc_send_byte(struct dvb_frontend* fe, int data)
101{
102 int i, par = 1, d;
103
104 for (i = 7; i >= 0; i--) {
105 d = (data >> i) & 1;
106 par ^= d;
107 flexcop_diseqc_send_bit(fe, d);
108 }
109
110 flexcop_diseqc_send_bit(fe, par);
111}
112
113static int flexcop_send_diseqc_msg(struct dvb_frontend* fe, int len, u8 *msg, unsigned long burst)
114{
115 int i;
116
117 flexcop_set_tone(fe, SEC_TONE_OFF);
118 mdelay(16);
119
120 for (i = 0; i < len; i++)
121 flexcop_diseqc_send_byte(fe,msg[i]);
122
123 mdelay(16);
124
125 if (burst != -1) {
126 if (burst)
127 flexcop_diseqc_send_byte(fe, 0xff);
128 else {
129 flexcop_set_tone(fe, SEC_TONE_ON);
130 udelay(12500);
131 flexcop_set_tone(fe, SEC_TONE_OFF);
132 }
133 msleep(20);
134 }
135 return 0;
136}
137
138static int flexcop_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
139{
140 return flexcop_send_diseqc_msg(fe, cmd->msg_len, cmd->msg, 0);
141}
142
143static int flexcop_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
144{
145 return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd);
146}
147
148/* dvb-s stv0299 */
149static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio)
150{
151 u8 aclk = 0;
152 u8 bclk = 0;
153
154 if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
155 else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
156 else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
157 else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
158 else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
159 else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
160
161 stv0299_writereg (fe, 0x13, aclk);
162 stv0299_writereg (fe, 0x14, bclk);
163 stv0299_writereg (fe, 0x1f, (ratio >> 16) & 0xff);
164 stv0299_writereg (fe, 0x20, (ratio >> 8) & 0xff);
165 stv0299_writereg (fe, 0x21, (ratio ) & 0xf0);
166
167 return 0;
168}
169
Andreas Oberrittercfbfce12005-09-09 13:02:30 -0700170static int samsung_tbmu24112_pll_set(struct dvb_frontend* fe, struct i2c_adapter *i2c, struct dvb_frontend_parameters* params)
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700171{
172 u8 buf[4];
173 u32 div;
174 struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700175
176 div = params->frequency / 125;
177
178 buf[0] = (div >> 8) & 0x7f;
179 buf[1] = div & 0xff;
180 buf[2] = 0x84; /* 0xC4 */
181 buf[3] = 0x08;
182
183 if (params->frequency < 1500000) buf[3] |= 0x10;
184
Andreas Oberrittercfbfce12005-09-09 13:02:30 -0700185 if (i2c_transfer(i2c, &msg, 1) != 1)
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700186 return -EIO;
187 return 0;
188}
189
190static u8 samsung_tbmu24112_inittab[] = {
191 0x01, 0x15,
192 0x02, 0x30,
193 0x03, 0x00,
194 0x04, 0x7D,
195 0x05, 0x35,
196 0x06, 0x02,
197 0x07, 0x00,
198 0x08, 0xC3,
199 0x0C, 0x00,
200 0x0D, 0x81,
201 0x0E, 0x23,
202 0x0F, 0x12,
203 0x10, 0x7E,
204 0x11, 0x84,
205 0x12, 0xB9,
206 0x13, 0x88,
207 0x14, 0x89,
208 0x15, 0xC9,
209 0x16, 0x00,
210 0x17, 0x5C,
211 0x18, 0x00,
212 0x19, 0x00,
213 0x1A, 0x00,
214 0x1C, 0x00,
215 0x1D, 0x00,
216 0x1E, 0x00,
217 0x1F, 0x3A,
218 0x20, 0x2E,
219 0x21, 0x80,
220 0x22, 0xFF,
221 0x23, 0xC1,
222 0x28, 0x00,
223 0x29, 0x1E,
224 0x2A, 0x14,
225 0x2B, 0x0F,
226 0x2C, 0x09,
227 0x2D, 0x05,
228 0x31, 0x1F,
229 0x32, 0x19,
230 0x33, 0xFE,
231 0x34, 0x93,
232 0xff, 0xff,
233};
234
235static struct stv0299_config samsung_tbmu24112_config = {
236 .demod_address = 0x68,
237 .inittab = samsung_tbmu24112_inittab,
238 .mclk = 88000000UL,
239 .invert = 0,
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700240 .skip_reinit = 0,
241 .lock_output = STV0229_LOCKOUTPUT_LK,
242 .volt13_op0_op1 = STV0299_VOLT13_OP1,
243 .min_delay_ms = 100,
244 .set_symbol_rate = samsung_tbmu24112_set_symbol_rate,
245 .pll_set = samsung_tbmu24112_pll_set,
246};
247
248/* dvb-t mt352 */
249static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
250{
251 static u8 mt352_clock_config [] = { 0x89, 0x18, 0x2d };
252 static u8 mt352_reset [] = { 0x50, 0x80 };
253 static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
254 static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0xa1 };
255 static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
256
257 mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
258 udelay(2000);
259 mt352_write(fe, mt352_reset, sizeof(mt352_reset));
260 mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
261
262 mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
263 mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
264
265 return 0;
266}
267
268static int samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf)
269{
270 u32 div;
271 unsigned char bs = 0;
272
273 #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */
274 div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
275
276 if (params->frequency >= 48000000 && params->frequency <= 154000000) bs = 0x09;
277 if (params->frequency >= 161000000 && params->frequency <= 439000000) bs = 0x0a;
278 if (params->frequency >= 447000000 && params->frequency <= 863000000) bs = 0x08;
279
280 pllbuf[0] = 0xc2; /* Note: non-linux standard PLL i2c address */
281 pllbuf[1] = div >> 8;
282 pllbuf[2] = div & 0xff;
283 pllbuf[3] = 0xcc;
284 pllbuf[4] = bs;
285
286 return 0;
287}
288
289static struct mt352_config samsung_tdtc9251dh0_config = {
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700290 .demod_address = 0x0f,
Johannes Stezenbach55f51ef2005-06-23 22:02:41 -0700291 .demod_init = samsung_tdtc9251dh0_demod_init,
292 .pll_set = samsung_tdtc9251dh0_pll_set,
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700293};
294
Johannes Stezenbach55f51ef2005-06-23 22:02:41 -0700295static int flexcop_fe_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700296{
297 struct flexcop_device *fc = fe->dvb->priv;
298 return request_firmware(fw, name, fc->dev);
299}
300
Michael Krufkyc0b11b92005-11-08 21:35:32 -0800301static int lgdt3303_pll_set(struct dvb_frontend* fe,
Michael Krufky50c25ff2006-01-09 15:25:34 -0200302 struct dvb_frontend_parameters* params)
Michael Krufkyc0b11b92005-11-08 21:35:32 -0800303{
304 struct flexcop_device *fc = fe->dvb->priv;
Michael Krufky7c250392006-04-18 17:47:08 -0300305 return lg_h06xf_pll_set(fe, &fc->i2c_adap, params);
Michael Krufkyc0b11b92005-11-08 21:35:32 -0800306}
307
308static struct lgdt330x_config air2pc_atsc_hd5000_config = {
309 .demod_address = 0x59,
310 .demod_chip = LGDT3303,
311 .serial_mpeg = 0x04,
312 .pll_set = lgdt3303_pll_set,
313 .clock_polarity_flip = 1,
314};
315
Michael Krufky46365f32006-01-23 09:52:39 -0200316static struct nxt200x_config samsung_tbmv_config = {
Johannes Stezenbach55f51ef2005-06-23 22:02:41 -0700317 .demod_address = 0x0a,
Michael Krufky46365f32006-01-23 09:52:39 -0200318 .pll_address = 0xc2,
Michael Krufkyf69b5d92006-01-23 09:52:57 -0200319 .pll_desc = &dvb_pll_samsung_tbmv,
Johannes Stezenbach55f51ef2005-06-23 22:02:41 -0700320};
321
322static struct bcm3510_config air2pc_atsc_first_gen_config = {
323 .demod_address = 0x0f,
324 .request_firmware = flexcop_fe_request_firmware,
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700325};
326
327static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
328{
329 u8 buf[4];
330 u32 div;
331 struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
332 struct flexcop_device *fc = fe->dvb->priv;
333
334 div = (params->frequency + (125/2)) / 125;
335
336 buf[0] = (div >> 8) & 0x7f;
337 buf[1] = (div >> 0) & 0xff;
338 buf[2] = 0x84 | ((div >> 10) & 0x60);
339 buf[3] = 0x80;
340
341 if (params->frequency < 1550000)
342 buf[3] |= 0x02;
343
344 if (i2c_transfer(&fc->i2c_adap, &msg, 1) != 1)
345 return -EIO;
346 return 0;
347}
348
349static struct mt312_config skystar23_samsung_tbdu18132_config = {
350
351 .demod_address = 0x0e,
352 .pll_set = skystar23_samsung_tbdu18132_pll_set,
353};
354
Andrew de Quinceydc27a162005-09-09 13:03:07 -0700355
356static u8 alps_tdee4_stv0297_inittab[] = {
357 0x80, 0x01,
358 0x80, 0x00,
359 0x81, 0x01,
360 0x81, 0x00,
361 0x00, 0x09,
362 0x01, 0x69,
363 0x03, 0x00,
364 0x04, 0x00,
365 0x07, 0x00,
366 0x08, 0x00,
367 0x20, 0x00,
368 0x21, 0x40,
369 0x22, 0x00,
370 0x23, 0x00,
371 0x24, 0x40,
372 0x25, 0x88,
373 0x30, 0xff,
374 0x31, 0x00,
375 0x32, 0xff,
376 0x33, 0x00,
377 0x34, 0x50,
378 0x35, 0x7f,
379 0x36, 0x00,
380 0x37, 0x20,
381 0x38, 0x00,
382 0x40, 0x1c,
383 0x41, 0xff,
384 0x42, 0x29,
385 0x43, 0x00,
386 0x44, 0xff,
387 0x45, 0x00,
388 0x46, 0x00,
389 0x49, 0x04,
390 0x4a, 0x00,
391 0x4b, 0xf8,
392 0x52, 0x30,
393 0x55, 0xae,
394 0x56, 0x47,
395 0x57, 0xe1,
396 0x58, 0x3a,
397 0x5a, 0x1e,
398 0x5b, 0x34,
399 0x60, 0x00,
400 0x63, 0x00,
401 0x64, 0x00,
402 0x65, 0x00,
403 0x66, 0x00,
404 0x67, 0x00,
405 0x68, 0x00,
406 0x69, 0x00,
407 0x6a, 0x02,
408 0x6b, 0x00,
409 0x70, 0xff,
410 0x71, 0x00,
411 0x72, 0x00,
412 0x73, 0x00,
413 0x74, 0x0c,
414 0x80, 0x00,
415 0x81, 0x00,
416 0x82, 0x00,
417 0x83, 0x00,
418 0x84, 0x04,
419 0x85, 0x80,
420 0x86, 0x24,
421 0x87, 0x78,
422 0x88, 0x10,
423 0x89, 0x00,
424 0x90, 0x01,
425 0x91, 0x01,
426 0xa0, 0x04,
427 0xa1, 0x00,
428 0xa2, 0x00,
429 0xb0, 0x91,
430 0xb1, 0x0b,
431 0xc0, 0x53,
432 0xc1, 0x70,
433 0xc2, 0x12,
434 0xd0, 0x00,
435 0xd1, 0x00,
436 0xd2, 0x00,
437 0xd3, 0x00,
438 0xd4, 0x00,
439 0xd5, 0x00,
440 0xde, 0x00,
441 0xdf, 0x00,
442 0x61, 0x49,
443 0x62, 0x0b,
444 0x53, 0x08,
445 0x59, 0x08,
446 0xff, 0xff,
447};
448
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700449static struct stv0297_config alps_tdee4_stv0297_config = {
450 .demod_address = 0x1c,
Andrew de Quinceydc27a162005-09-09 13:03:07 -0700451 .inittab = alps_tdee4_stv0297_inittab,
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700452// .invert = 1,
453// .pll_set = alps_tdee4_stv0297_pll_set,
454};
455
456/* try to figure out the frontend, each card/box can have on of the following list */
457int flexcop_frontend_init(struct flexcop_device *fc)
458{
Jesper Juhl6a5bdd32006-01-09 15:25:23 -0200459 struct dvb_frontend_ops *ops;
460
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700461 /* try the sky v2.6 (stv0299/Samsung tbmu24112(sl1935)) */
462 if ((fc->fe = stv0299_attach(&samsung_tbmu24112_config, &fc->i2c_adap)) != NULL) {
Jesper Juhl6a5bdd32006-01-09 15:25:23 -0200463 ops = fc->fe->ops;
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700464
Jesper Juhl6a5bdd32006-01-09 15:25:23 -0200465 ops->set_voltage = flexcop_set_voltage;
466
467 fc->fe_sleep = ops->sleep;
468 ops->sleep = flexcop_sleep;
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700469
470 fc->dev_type = FC_SKY;
471 info("found the stv0299 at i2c address: 0x%02x",samsung_tbmu24112_config.demod_address);
472 } else
473 /* try the air dvb-t (mt352/Samsung tdtc9251dh0(??)) */
474 if ((fc->fe = mt352_attach(&samsung_tdtc9251dh0_config, &fc->i2c_adap)) != NULL ) {
475 fc->dev_type = FC_AIR_DVB;
476 info("found the mt352 at i2c address: 0x%02x",samsung_tdtc9251dh0_config.demod_address);
477 } else
Johannes Stezenbach55f51ef2005-06-23 22:02:41 -0700478 /* try the air atsc 2nd generation (nxt2002) */
Michael Krufky46365f32006-01-23 09:52:39 -0200479 if ((fc->fe = nxt200x_attach(&samsung_tbmv_config, &fc->i2c_adap)) != NULL) {
Johannes Stezenbach55f51ef2005-06-23 22:02:41 -0700480 fc->dev_type = FC_AIR_ATSC2;
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700481 info("found the nxt2002 at i2c address: 0x%02x",samsung_tbmv_config.demod_address);
482 } else
Michael Krufkyc0b11b92005-11-08 21:35:32 -0800483 /* try the air atsc 3nd generation (lgdt3303) */
484 if ((fc->fe = lgdt330x_attach(&air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) {
485 fc->dev_type = FC_AIR_ATSC3;
486 info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address);
487 } else
Johannes Stezenbach55f51ef2005-06-23 22:02:41 -0700488 /* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */
489 if ((fc->fe = bcm3510_attach(&air2pc_atsc_first_gen_config, &fc->i2c_adap)) != NULL) {
490 fc->dev_type = FC_AIR_ATSC1;
491 info("found the bcm3510 at i2c address: 0x%02x",air2pc_atsc_first_gen_config.demod_address);
492 } else
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700493 /* try the cable dvb (stv0297) */
Andrew de Quinceydc27a162005-09-09 13:03:07 -0700494 if ((fc->fe = stv0297_attach(&alps_tdee4_stv0297_config, &fc->i2c_adap)) != NULL) {
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700495 fc->dev_type = FC_CABLE;
496 info("found the stv0297 at i2c address: 0x%02x",alps_tdee4_stv0297_config.demod_address);
497 } else
498 /* try the sky v2.3 (vp310/Samsung tbdu18132(tsa5059)) */
Adrian Bunk805e6602006-02-27 00:07:49 -0300499 if ((fc->fe = vp310_mt312_attach(&skystar23_samsung_tbdu18132_config, &fc->i2c_adap)) != NULL) {
Jesper Juhl6a5bdd32006-01-09 15:25:23 -0200500 ops = fc->fe->ops;
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700501
Jesper Juhl6a5bdd32006-01-09 15:25:23 -0200502 ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
503 ops->diseqc_send_burst = flexcop_diseqc_send_burst;
504 ops->set_tone = flexcop_set_tone;
505 ops->set_voltage = flexcop_set_voltage;
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700506
Jesper Juhl6a5bdd32006-01-09 15:25:23 -0200507 fc->fe_sleep = ops->sleep;
508 ops->sleep = flexcop_sleep;
509
510 fc->dev_type = FC_SKY_OLD;
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700511 info("found the vp310 (aka mt312) at i2c address: 0x%02x",skystar23_samsung_tbdu18132_config.demod_address);
512 }
513
514 if (fc->fe == NULL) {
515 err("no frontend driver found for this B2C2/FlexCop adapter");
516 return -ENODEV;
517 } else {
518 if (dvb_register_frontend(&fc->dvb_adapter, fc->fe)) {
519 err("frontend registration failed!");
Jesper Juhl6a5bdd32006-01-09 15:25:23 -0200520 ops = fc->fe->ops;
521 if (ops->release != NULL)
522 ops->release(fc->fe);
Johannes Stezenbach2add87a2005-05-16 21:54:10 -0700523 fc->fe = NULL;
524 return -EINVAL;
525 }
526 }
527 fc->init_state |= FC_STATE_FE_INIT;
528 return 0;
529}
530
531void flexcop_frontend_exit(struct flexcop_device *fc)
532{
533 if (fc->init_state & FC_STATE_FE_INIT)
534 dvb_unregister_frontend(fc->fe);
535
536 fc->init_state &= ~FC_STATE_FE_INIT;
537}