[PATCH] dvb: b2c2/flexcop driver refactoring part 2: add modular Flexcop driver
[linux-2.6/linux-loongson.git] / drivers / media / dvb / b2c2 / flexcop-fe-tuner.c
blobb3dd16fb5bbdecc8916a3dbba0581ae0e7cd76b4
1 /*
2 * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
4 * flexcop-fe-tuner.c - methods for attaching a frontend and controlling DiSEqC.
6 * see flexcop.c for copyright information.
7 */
8 #include "flexcop.h"
10 #include "stv0299.h"
11 #include "mt352.h"
12 #include "nxt2002.h"
13 #include "stv0297.h"
14 #include "mt312.h"
16 /* lnb control */
18 static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
20 struct flexcop_device *fc = fe->dvb->priv;
21 flexcop_ibi_value v;
22 deb_tuner("polarity/voltage = %u\n", voltage);
24 v = fc->read_ibi_reg(fc, misc_204);
25 switch (voltage) {
26 case SEC_VOLTAGE_OFF:
27 v.misc_204.ACPI1_sig = 1;
28 break;
29 case SEC_VOLTAGE_13:
30 v.misc_204.ACPI1_sig = 0;
31 v.misc_204.LNB_L_H_sig = 0;
32 break;
33 case SEC_VOLTAGE_18:
34 v.misc_204.ACPI1_sig = 0;
35 v.misc_204.LNB_L_H_sig = 1;
36 break;
37 default:
38 err("unknown SEC_VOLTAGE value");
39 return -EINVAL;
41 return fc->write_ibi_reg(fc, misc_204, v);
44 static int flexcop_sleep(struct dvb_frontend* fe)
46 struct flexcop_device *fc = fe->dvb->priv;
47 /* flexcop_ibi_value v = fc->read_ibi_reg(fc,misc_204); */
49 if (fc->fe_sleep)
50 return fc->fe_sleep(fe);
52 /* v.misc_204.ACPI3_sig = 1;
53 fc->write_ibi_reg(fc,misc_204,v);*/
55 return 0;
58 static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
60 /* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */
61 struct flexcop_device *fc = fe->dvb->priv;
62 flexcop_ibi_value v;
63 u16 ax;
64 v.raw = 0;
66 deb_tuner("tone = %u\n",tone);
68 switch (tone) {
69 case SEC_TONE_ON:
70 ax = 0x01ff;
71 break;
72 case SEC_TONE_OFF:
73 ax = 0;
74 break;
75 default:
76 err("unknown SEC_TONE value");
77 return -EINVAL;
80 v.lnb_switch_freq_200.LNB_CTLPrescaler_sig = 1; /* divide by 2 */
82 v.lnb_switch_freq_200.LNB_CTLHighCount_sig =
83 v.lnb_switch_freq_200.LNB_CTLLowCount_sig = ax;
85 return fc->write_ibi_reg(fc,lnb_switch_freq_200,v);
88 static void flexcop_diseqc_send_bit(struct dvb_frontend* fe, int data)
90 flexcop_set_tone(fe, SEC_TONE_ON);
91 udelay(data ? 500 : 1000);
92 flexcop_set_tone(fe, SEC_TONE_OFF);
93 udelay(data ? 1000 : 500);
96 static void flexcop_diseqc_send_byte(struct dvb_frontend* fe, int data)
98 int i, par = 1, d;
100 for (i = 7; i >= 0; i--) {
101 d = (data >> i) & 1;
102 par ^= d;
103 flexcop_diseqc_send_bit(fe, d);
106 flexcop_diseqc_send_bit(fe, par);
109 static int flexcop_send_diseqc_msg(struct dvb_frontend* fe, int len, u8 *msg, unsigned long burst)
111 int i;
113 flexcop_set_tone(fe, SEC_TONE_OFF);
114 mdelay(16);
116 for (i = 0; i < len; i++)
117 flexcop_diseqc_send_byte(fe,msg[i]);
119 mdelay(16);
121 if (burst != -1) {
122 if (burst)
123 flexcop_diseqc_send_byte(fe, 0xff);
124 else {
125 flexcop_set_tone(fe, SEC_TONE_ON);
126 udelay(12500);
127 flexcop_set_tone(fe, SEC_TONE_OFF);
129 msleep(20);
131 return 0;
134 static int flexcop_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
136 return flexcop_send_diseqc_msg(fe, cmd->msg_len, cmd->msg, 0);
139 static int flexcop_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
141 return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd);
144 /* dvb-s stv0299 */
145 static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio)
147 u8 aclk = 0;
148 u8 bclk = 0;
150 if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
151 else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
152 else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
153 else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
154 else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
155 else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
157 stv0299_writereg (fe, 0x13, aclk);
158 stv0299_writereg (fe, 0x14, bclk);
159 stv0299_writereg (fe, 0x1f, (ratio >> 16) & 0xff);
160 stv0299_writereg (fe, 0x20, (ratio >> 8) & 0xff);
161 stv0299_writereg (fe, 0x21, (ratio ) & 0xf0);
163 return 0;
166 static int samsung_tbmu24112_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
168 u8 buf[4];
169 u32 div;
170 struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
171 struct flexcop_device *fc = fe->dvb->priv;
173 div = params->frequency / 125;
175 buf[0] = (div >> 8) & 0x7f;
176 buf[1] = div & 0xff;
177 buf[2] = 0x84; /* 0xC4 */
178 buf[3] = 0x08;
180 if (params->frequency < 1500000) buf[3] |= 0x10;
182 if (i2c_transfer(&fc->i2c_adap, &msg, 1) != 1)
183 return -EIO;
184 return 0;
187 static u8 samsung_tbmu24112_inittab[] = {
188 0x01, 0x15,
189 0x02, 0x30,
190 0x03, 0x00,
191 0x04, 0x7D,
192 0x05, 0x35,
193 0x06, 0x02,
194 0x07, 0x00,
195 0x08, 0xC3,
196 0x0C, 0x00,
197 0x0D, 0x81,
198 0x0E, 0x23,
199 0x0F, 0x12,
200 0x10, 0x7E,
201 0x11, 0x84,
202 0x12, 0xB9,
203 0x13, 0x88,
204 0x14, 0x89,
205 0x15, 0xC9,
206 0x16, 0x00,
207 0x17, 0x5C,
208 0x18, 0x00,
209 0x19, 0x00,
210 0x1A, 0x00,
211 0x1C, 0x00,
212 0x1D, 0x00,
213 0x1E, 0x00,
214 0x1F, 0x3A,
215 0x20, 0x2E,
216 0x21, 0x80,
217 0x22, 0xFF,
218 0x23, 0xC1,
219 0x28, 0x00,
220 0x29, 0x1E,
221 0x2A, 0x14,
222 0x2B, 0x0F,
223 0x2C, 0x09,
224 0x2D, 0x05,
225 0x31, 0x1F,
226 0x32, 0x19,
227 0x33, 0xFE,
228 0x34, 0x93,
229 0xff, 0xff,
232 static struct stv0299_config samsung_tbmu24112_config = {
233 .demod_address = 0x68,
234 .inittab = samsung_tbmu24112_inittab,
235 .mclk = 88000000UL,
236 .invert = 0,
237 .enhanced_tuning = 0,
238 .skip_reinit = 0,
239 .lock_output = STV0229_LOCKOUTPUT_LK,
240 .volt13_op0_op1 = STV0299_VOLT13_OP1,
241 .min_delay_ms = 100,
242 .set_symbol_rate = samsung_tbmu24112_set_symbol_rate,
243 .pll_set = samsung_tbmu24112_pll_set,
246 /* dvb-t mt352 */
247 static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
249 static u8 mt352_clock_config [] = { 0x89, 0x18, 0x2d };
250 static u8 mt352_reset [] = { 0x50, 0x80 };
251 static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
252 static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0xa1 };
253 static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
255 mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
256 udelay(2000);
257 mt352_write(fe, mt352_reset, sizeof(mt352_reset));
258 mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
260 mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
261 mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
263 return 0;
266 static int samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf)
268 u32 div;
269 unsigned char bs = 0;
271 #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */
272 div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
274 if (params->frequency >= 48000000 && params->frequency <= 154000000) bs = 0x09;
275 if (params->frequency >= 161000000 && params->frequency <= 439000000) bs = 0x0a;
276 if (params->frequency >= 447000000 && params->frequency <= 863000000) bs = 0x08;
278 pllbuf[0] = 0xc2; /* Note: non-linux standard PLL i2c address */
279 pllbuf[1] = div >> 8;
280 pllbuf[2] = div & 0xff;
281 pllbuf[3] = 0xcc;
282 pllbuf[4] = bs;
284 return 0;
287 static struct mt352_config samsung_tdtc9251dh0_config = {
289 .demod_address = 0x0f,
290 .demod_init = samsung_tdtc9251dh0_demod_init,
291 .pll_set = samsung_tdtc9251dh0_pll_set,
294 static int nxt2002_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
296 struct flexcop_device *fc = fe->dvb->priv;
297 return request_firmware(fw, name, fc->dev);
300 static struct nxt2002_config samsung_tbmv_config = {
301 .demod_address = 0x0a,
302 .request_firmware = nxt2002_request_firmware,
305 static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
307 u8 buf[4];
308 u32 div;
309 struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
310 struct flexcop_device *fc = fe->dvb->priv;
312 div = (params->frequency + (125/2)) / 125;
314 buf[0] = (div >> 8) & 0x7f;
315 buf[1] = (div >> 0) & 0xff;
316 buf[2] = 0x84 | ((div >> 10) & 0x60);
317 buf[3] = 0x80;
319 if (params->frequency < 1550000)
320 buf[3] |= 0x02;
322 if (i2c_transfer(&fc->i2c_adap, &msg, 1) != 1)
323 return -EIO;
324 return 0;
327 static struct mt312_config skystar23_samsung_tbdu18132_config = {
329 .demod_address = 0x0e,
330 .pll_set = skystar23_samsung_tbdu18132_pll_set,
333 static struct stv0297_config alps_tdee4_stv0297_config = {
334 .demod_address = 0x1c,
335 // .invert = 1,
336 // .pll_set = alps_tdee4_stv0297_pll_set,
339 /* try to figure out the frontend, each card/box can have on of the following list */
340 int flexcop_frontend_init(struct flexcop_device *fc)
342 /* try the sky v2.6 (stv0299/Samsung tbmu24112(sl1935)) */
343 if ((fc->fe = stv0299_attach(&samsung_tbmu24112_config, &fc->i2c_adap)) != NULL) {
344 fc->fe->ops->set_voltage = flexcop_set_voltage;
346 fc->fe_sleep = fc->fe->ops->sleep;
347 fc->fe->ops->sleep = flexcop_sleep;
349 fc->dev_type = FC_SKY;
350 info("found the stv0299 at i2c address: 0x%02x",samsung_tbmu24112_config.demod_address);
351 } else
352 /* try the air dvb-t (mt352/Samsung tdtc9251dh0(??)) */
353 if ((fc->fe = mt352_attach(&samsung_tdtc9251dh0_config, &fc->i2c_adap)) != NULL ) {
354 fc->dev_type = FC_AIR_DVB;
355 info("found the mt352 at i2c address: 0x%02x",samsung_tdtc9251dh0_config.demod_address);
356 } else
357 /* try the air atsc (nxt2002) */
358 if ((fc->fe = nxt2002_attach(&samsung_tbmv_config, &fc->i2c_adap)) != NULL) {
359 fc->dev_type = FC_AIR_ATSC;
360 info("found the nxt2002 at i2c address: 0x%02x",samsung_tbmv_config.demod_address);
361 } else
362 /* try the cable dvb (stv0297) */
363 if ((fc->fe = stv0297_attach(&alps_tdee4_stv0297_config, &fc->i2c_adap, 0xf8)) != NULL) {
364 fc->dev_type = FC_CABLE;
365 info("found the stv0297 at i2c address: 0x%02x",alps_tdee4_stv0297_config.demod_address);
366 } else
367 /* try the sky v2.3 (vp310/Samsung tbdu18132(tsa5059)) */
368 if ((fc->fe = vp310_attach(&skystar23_samsung_tbdu18132_config, &fc->i2c_adap)) != NULL) {
369 fc->fe->ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
370 fc->fe->ops->diseqc_send_burst = flexcop_diseqc_send_burst;
371 fc->fe->ops->set_tone = flexcop_set_tone;
372 fc->fe->ops->set_voltage = flexcop_set_voltage;
374 fc->fe_sleep = fc->fe->ops->sleep;
375 fc->fe->ops->sleep = flexcop_sleep;
377 fc->dev_type = FC_SKY_OLD;
378 info("found the vp310 (aka mt312) at i2c address: 0x%02x",skystar23_samsung_tbdu18132_config.demod_address);
381 if (fc->fe == NULL) {
382 err("no frontend driver found for this B2C2/FlexCop adapter");
383 return -ENODEV;
384 } else {
385 if (dvb_register_frontend(&fc->dvb_adapter, fc->fe)) {
386 err("frontend registration failed!");
387 if (fc->fe->ops->release != NULL)
388 fc->fe->ops->release(fc->fe);
389 fc->fe = NULL;
390 return -EINVAL;
393 fc->init_state |= FC_STATE_FE_INIT;
394 return 0;
397 void flexcop_frontend_exit(struct flexcop_device *fc)
399 if (fc->init_state & FC_STATE_FE_INIT)
400 dvb_unregister_frontend(fc->fe);
402 fc->init_state &= ~FC_STATE_FE_INIT;