2 Alps TDMB7 DVB OFDM frontend driver
4 Copyright (C) 2001-2002 Convergence Integrated Media GmbH
5 Holger Waechtler <holger@convergence.de>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <linux/kernel.h>
24 #include <linux/init.h>
25 #include <linux/module.h>
26 #include <linux/string.h>
27 #include <linux/slab.h>
29 #include "dvb_frontend.h"
30 #include "dvb_functions.h"
34 #define dprintk if (debug) printk
37 static struct dvb_frontend_info tdmb7_info
= {
40 .frequency_min
= 470000000,
41 .frequency_max
= 860000000,
42 .frequency_stepsize
= 166667,
44 .frequency_tolerance
= ???,
45 .symbol_rate_min
= ???,
46 .symbol_rate_max
= ???,
47 .symbol_rate_tolerance
= 500, /* ppm */
50 .caps
= FE_CAN_FEC_1_2
| FE_CAN_FEC_2_3
| FE_CAN_FEC_3_4
|
51 FE_CAN_FEC_5_6
| FE_CAN_FEC_7_8
| FE_CAN_FEC_AUTO
|
52 FE_CAN_QPSK
| FE_CAN_QAM_16
| FE_CAN_QAM_64
|
57 static u8 init_tab
[] = {
79 static int cx22700_writereg (struct dvb_i2c_bus
*i2c
, u8 reg
, u8 data
)
82 u8 buf
[] = { reg
, data
};
83 struct i2c_msg msg
= { .addr
= 0x43, .flags
= 0, .buf
= buf
, .len
= 2 };
85 dprintk ("%s\n", __FUNCTION__
);
87 ret
= i2c
->xfer (i2c
, &msg
, 1);
90 printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
91 __FUNCTION__
, reg
, data
, ret
);
93 return (ret
!= 1) ? -1 : 0;
97 static u8
cx22700_readreg (struct dvb_i2c_bus
*i2c
, u8 reg
)
102 struct i2c_msg msg
[] = { { .addr
= 0x43, .flags
= 0, .buf
= b0
, .len
= 1 },
103 { .addr
= 0x43, .flags
= I2C_M_RD
, .buf
= b1
, .len
= 1 } };
105 dprintk ("%s\n", __FUNCTION__
);
107 ret
= i2c
->xfer (i2c
, msg
, 2);
110 printk("%s: readreg error (ret == %i)\n", __FUNCTION__
, ret
);
116 static int pll_write (struct dvb_i2c_bus
*i2c
, u8 data
[4])
118 struct i2c_msg msg
= { .addr
= 0x61, .flags
= 0, .buf
= data
, .len
= 4 };
121 cx22700_writereg (i2c
, 0x0a, 0x00); /* open i2c bus switch */
122 ret
= i2c
->xfer (i2c
, &msg
, 1);
123 cx22700_writereg (i2c
, 0x0a, 0x01); /* close i2c bus switch */
126 printk("%s: i/o error (addr == 0x%02x, ret == %i)\n", __FUNCTION__
, msg
.addr
, ret
);
128 return (ret
!= 1) ? -1 : 0;
133 * set up the downconverter frequency divisor for a
134 * reference clock comparision frequency of 125 kHz.
136 static int pll_set_tv_freq (struct dvb_i2c_bus
*i2c
, u32 freq
)
138 u32 div
= (freq
+ 36166667) / 166667;
139 #if 1 //ALPS_SETTINGS
140 u8 buf
[4] = { (div
>> 8) & 0x7f, div
& 0xff, ((div
>> 10) & 0x60) | 0x85,
141 freq
< 592000000 ? 0x40 : 0x80 };
143 u8 buf
[4] = { (div
>> 8) & 0x7f, div
& 0xff, ((div
>> 10) & 0x60) | 0x85,
144 freq
< 470000000 ? 0x42 : freq
< 862000000 ? 0x41 : 0x81 };
147 dprintk ("%s: freq == %i, div == %i\n", __FUNCTION__
, (int) freq
, (int) div
);
149 return pll_write (i2c
, buf
);
153 static int cx22700_init (struct dvb_i2c_bus
*i2c
)
157 dprintk("cx22700_init: init chip\n");
159 cx22700_writereg (i2c
, 0x00, 0x02); /* soft reset */
160 cx22700_writereg (i2c
, 0x00, 0x00);
164 for (i
=0; i
<sizeof(init_tab
); i
+=2)
165 cx22700_writereg (i2c
, init_tab
[i
], init_tab
[i
+1]);
167 cx22700_writereg (i2c
, 0x00, 0x01);
173 static int cx22700_set_inversion (struct dvb_i2c_bus
*i2c
, int inversion
)
177 dprintk ("%s\n", __FUNCTION__
);
183 val
= cx22700_readreg (i2c
, 0x09);
184 return cx22700_writereg (i2c
, 0x09, val
| 0x01);
186 val
= cx22700_readreg (i2c
, 0x09);
187 return cx22700_writereg (i2c
, 0x09, val
& 0xfe);
194 static int cx22700_set_tps (struct dvb_i2c_bus
*i2c
, struct dvb_ofdm_parameters
*p
)
196 static const u8 qam_tab
[4] = { 0, 1, 0, 2 };
197 static const u8 fec_tab
[6] = { 0, 1, 2, 0, 3, 4 };
200 dprintk ("%s\n", __FUNCTION__
);
202 if (p
->code_rate_HP
< FEC_1_2
|| p
->code_rate_HP
> FEC_7_8
)
205 if (p
->code_rate_LP
< FEC_1_2
|| p
->code_rate_LP
> FEC_7_8
)
207 if (p
->code_rate_HP
== FEC_4_5
|| p
->code_rate_LP
== FEC_4_5
)
210 if (p
->guard_interval
< GUARD_INTERVAL_1_32
||
211 p
->guard_interval
> GUARD_INTERVAL_1_4
)
214 if (p
->transmission_mode
!= TRANSMISSION_MODE_2K
&&
215 p
->transmission_mode
!= TRANSMISSION_MODE_8K
)
218 if (p
->constellation
!= QPSK
&&
219 p
->constellation
!= QAM_16
&&
220 p
->constellation
!= QAM_64
)
223 if (p
->hierarchy_information
< HIERARCHY_NONE
||
224 p
->hierarchy_information
> HIERARCHY_4
)
227 if (p
->bandwidth
< BANDWIDTH_8_MHZ
&& p
->bandwidth
> BANDWIDTH_6_MHZ
)
230 if (p
->bandwidth
== BANDWIDTH_7_MHZ
)
231 cx22700_writereg (i2c
, 0x09, cx22700_readreg (i2c
, 0x09 | 0x10));
233 cx22700_writereg (i2c
, 0x09, cx22700_readreg (i2c
, 0x09 & ~0x10));
235 val
= qam_tab
[p
->constellation
- QPSK
];
236 val
|= p
->hierarchy_information
- HIERARCHY_NONE
;
238 cx22700_writereg (i2c
, 0x04, val
);
240 val
= fec_tab
[p
->code_rate_HP
- FEC_1_2
] << 3;
241 val
|= fec_tab
[p
->code_rate_LP
- FEC_1_2
];
243 cx22700_writereg (i2c
, 0x05, val
);
245 val
= (p
->guard_interval
- GUARD_INTERVAL_1_32
) << 2;
246 val
|= p
->transmission_mode
- TRANSMISSION_MODE_2K
;
248 cx22700_writereg (i2c
, 0x06, val
);
250 cx22700_writereg (i2c
, 0x08, 0x04 | 0x02); /* use user tps parameters */
251 cx22700_writereg (i2c
, 0x08, 0x04); /* restart aquisition */
257 static int cx22700_get_tps (struct dvb_i2c_bus
*i2c
, struct dvb_ofdm_parameters
*p
)
259 static const fe_modulation_t qam_tab
[3] = { QPSK
, QAM_16
, QAM_64
};
260 static const fe_code_rate_t fec_tab
[5] = { FEC_1_2
, FEC_2_3
, FEC_3_4
,
264 dprintk ("%s\n", __FUNCTION__
);
266 if (!(cx22700_readreg(i2c
, 0x07) & 0x20)) /* tps valid? */
269 val
= cx22700_readreg (i2c
, 0x01);
272 p
->hierarchy_information
= HIERARCHY_AUTO
;
274 p
->hierarchy_information
= HIERARCHY_NONE
+ (val
& 0x7);
276 if (((val
>> 3) & 0x3) > 2)
277 p
->constellation
= QAM_AUTO
;
279 p
->constellation
= qam_tab
[(val
>> 3) & 0x3];
282 val
= cx22700_readreg (i2c
, 0x02);
284 if (((val
>> 3) & 0x07) > 4)
285 p
->code_rate_HP
= FEC_AUTO
;
287 p
->code_rate_HP
= fec_tab
[(val
>> 3) & 0x07];
289 if ((val
& 0x07) > 4)
290 p
->code_rate_LP
= FEC_AUTO
;
292 p
->code_rate_LP
= fec_tab
[val
& 0x07];
295 val
= cx22700_readreg (i2c
, 0x03);
297 p
->guard_interval
= GUARD_INTERVAL_1_32
+ ((val
>> 6) & 0x3);
298 p
->transmission_mode
= TRANSMISSION_MODE_2K
+ ((val
>> 5) & 0x1);
304 static int tdmb7_ioctl (struct dvb_frontend
*fe
, unsigned int cmd
, void *arg
)
306 struct dvb_i2c_bus
*i2c
= fe
->i2c
;
308 dprintk ("%s\n", __FUNCTION__
);
312 memcpy (arg
, &tdmb7_info
, sizeof(struct dvb_frontend_info
));
317 fe_status_t
*status
= (fe_status_t
*) arg
;
318 u16 rs_ber
= (cx22700_readreg (i2c
, 0x0d) << 9)
319 | (cx22700_readreg (i2c
, 0x0e) << 1);
320 u8 sync
= cx22700_readreg (i2c
, 0x07);
325 *status
|= FE_HAS_SIGNAL
;
328 *status
|= FE_HAS_CARRIER
;
331 *status
|= FE_HAS_VITERBI
;
334 *status
|= FE_HAS_SYNC
;
337 *status
|= FE_HAS_LOCK
;
343 *((u32
*) arg
) = cx22700_readreg (i2c
, 0x0c) & 0x7f;
344 cx22700_writereg (i2c
, 0x0c, 0x00);
347 case FE_READ_SIGNAL_STRENGTH
:
349 u16 rs_ber
= (cx22700_readreg (i2c
, 0x0d) << 9)
350 | (cx22700_readreg (i2c
, 0x0e) << 1);
351 *((u16
*) arg
) = ~rs_ber
;
356 u16 rs_ber
= (cx22700_readreg (i2c
, 0x0d) << 9)
357 | (cx22700_readreg (i2c
, 0x0e) << 1);
358 *((u16
*) arg
) = ~rs_ber
;
361 case FE_READ_UNCORRECTED_BLOCKS
:
362 *((u32
*) arg
) = cx22700_readreg (i2c
, 0x0f);
363 cx22700_writereg (i2c
, 0x0f, 0x00);
366 case FE_SET_FRONTEND
:
368 struct dvb_frontend_parameters
*p
= arg
;
370 cx22700_writereg (i2c
, 0x00, 0x02); /* XXX CHECKME: soft reset*/
371 cx22700_writereg (i2c
, 0x00, 0x00);
373 pll_set_tv_freq (i2c
, p
->frequency
);
374 cx22700_set_inversion (i2c
, p
->inversion
);
375 cx22700_set_tps (i2c
, &p
->u
.ofdm
);
376 cx22700_writereg (i2c
, 0x37, 0x01); /* PAL loop filter off */
377 cx22700_writereg (i2c
, 0x00, 0x01); /* restart acquire */
381 case FE_GET_FRONTEND
:
383 struct dvb_frontend_parameters
*p
= arg
;
384 u8 reg09
= cx22700_readreg (i2c
, 0x09);
386 p
->inversion
= reg09
& 0x1 ? INVERSION_ON
: INVERSION_OFF
;
387 return cx22700_get_tps (i2c
, &p
->u
.ofdm
);
391 return cx22700_init (i2c
);
393 case FE_GET_TUNE_SETTINGS
:
395 struct dvb_frontend_tune_settings
* fesettings
= (struct dvb_frontend_tune_settings
*) arg
;
396 fesettings
->min_delay_ms
= 150;
397 fesettings
->step_size
= 166667;
398 fesettings
->max_drift
= 166667*2;
411 static int tdmb7_attach (struct dvb_i2c_bus
*i2c
, void **data
)
415 struct i2c_msg msg
[] = { { .addr
= 0x43, .flags
= 0, .buf
= b0
, .len
= 1 },
416 { .addr
= 0x43, .flags
= I2C_M_RD
, .buf
= b1
, .len
= 1 } };
418 dprintk ("%s\n", __FUNCTION__
);
420 if (i2c
->xfer (i2c
, msg
, 2) != 2)
423 return dvb_register_frontend (tdmb7_ioctl
, i2c
, NULL
, &tdmb7_info
);
427 static void tdmb7_detach (struct dvb_i2c_bus
*i2c
, void *data
)
429 dprintk ("%s\n", __FUNCTION__
);
431 dvb_unregister_frontend (tdmb7_ioctl
, i2c
);
435 static int __init
init_tdmb7 (void)
437 dprintk ("%s\n", __FUNCTION__
);
439 return dvb_register_i2c_device (THIS_MODULE
, tdmb7_attach
, tdmb7_detach
);
443 static void __exit
exit_tdmb7 (void)
445 dprintk ("%s\n", __FUNCTION__
);
447 dvb_unregister_i2c_device (tdmb7_attach
);
450 module_init (init_tdmb7
);
451 module_exit (exit_tdmb7
);
453 MODULE_PARM(debug
,"i");
454 MODULE_PARM_DESC(debug
, "enable verbose debug messages");
455 MODULE_DESCRIPTION("TDMB7 DVB Frontend driver");
456 MODULE_AUTHOR("Holger Waechtler");
457 MODULE_LICENSE("GPL");