2 Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver
4 Copyright (C) 2008 Sirius International (Hong Kong) Limited
5 Timothy Lee <timothy.lee@siriushk.com>
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>
28 #include "dvb_frontend.h"
32 #define REG_RESET 0x02
33 #define REG_RESET_OFF 0x01
42 #define REG_STRENGTH 0x4b
43 #define REG_STRENGTH_MASK 0x7f
44 #define REG_STRENGTH_CARRIER 0x80
45 #define REG_INVERSION 0x7c
46 #define REG_INVERSION_ON 0x80
50 #define REG_STATUS 0xa4
51 #define REG_STATUS_SYNC 0x04
52 #define REG_STATUS_LOCK 0x01
55 struct lgs8gl5_state
{
56 struct i2c_adapter
*i2c
;
57 const struct lgs8gl5_config
*config
;
58 struct dvb_frontend frontend
;
63 #define dprintk(args...) \
66 printk(KERN_DEBUG "lgs8gl5: " args); \
70 /* Writes into demod's register */
72 lgs8gl5_write_reg(struct lgs8gl5_state
*state
, u8 reg
, u8 data
)
75 u8 buf
[] = {reg
, data
};
76 struct i2c_msg msg
= {
77 .addr
= state
->config
->demod_address
,
83 ret
= i2c_transfer(state
->i2c
, &msg
, 1);
85 dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n",
86 __func__
, reg
, data
, ret
);
87 return (ret
!= 1) ? -1 : 0;
91 /* Reads from demod's register */
93 lgs8gl5_read_reg(struct lgs8gl5_state
*state
, u8 reg
)
98 struct i2c_msg msg
[2] = {
100 .addr
= state
->config
->demod_address
,
106 .addr
= state
->config
->demod_address
,
113 ret
= i2c_transfer(state
->i2c
, msg
, 2);
122 lgs8gl5_update_reg(struct lgs8gl5_state
*state
, u8 reg
, u8 data
)
124 lgs8gl5_read_reg(state
, reg
);
125 lgs8gl5_write_reg(state
, reg
, data
);
130 /* Writes into alternate device's register */
131 /* TODO: Find out what that device is for! */
133 lgs8gl5_update_alt_reg(struct lgs8gl5_state
*state
, u8 reg
, u8 data
)
138 u8 b2
[] = {reg
, data
};
139 struct i2c_msg msg
[3] = {
141 .addr
= state
->config
->demod_address
+ 2,
147 .addr
= state
->config
->demod_address
+ 2,
153 .addr
= state
->config
->demod_address
+ 2,
160 ret
= i2c_transfer(state
->i2c
, msg
, 3);
161 return (ret
!= 3) ? -1 : 0;
166 lgs8gl5_soft_reset(struct lgs8gl5_state
*state
)
170 dprintk("%s\n", __func__
);
172 val
= lgs8gl5_read_reg(state
, REG_RESET
);
173 lgs8gl5_write_reg(state
, REG_RESET
, val
& ~REG_RESET_OFF
);
174 lgs8gl5_write_reg(state
, REG_RESET
, val
| REG_RESET_OFF
);
179 /* Starts demodulation */
181 lgs8gl5_start_demod(struct lgs8gl5_state
*state
)
186 dprintk("%s\n", __func__
);
188 lgs8gl5_update_alt_reg(state
, 0xc2, 0x28);
189 lgs8gl5_soft_reset(state
);
190 lgs8gl5_update_reg(state
, REG_07
, 0x10);
191 lgs8gl5_update_reg(state
, REG_07
, 0x10);
192 lgs8gl5_write_reg(state
, REG_09
, 0x0e);
193 lgs8gl5_write_reg(state
, REG_0A
, 0xe5);
194 lgs8gl5_write_reg(state
, REG_0B
, 0x35);
195 lgs8gl5_write_reg(state
, REG_0C
, 0x30);
197 lgs8gl5_update_reg(state
, REG_03
, 0x00);
198 lgs8gl5_update_reg(state
, REG_7E
, 0x01);
199 lgs8gl5_update_alt_reg(state
, 0xc5, 0x00);
200 lgs8gl5_update_reg(state
, REG_04
, 0x02);
201 lgs8gl5_update_reg(state
, REG_37
, 0x01);
202 lgs8gl5_soft_reset(state
);
204 /* Wait for carrier */
205 for (n
= 0; n
< 10; n
++) {
206 val
= lgs8gl5_read_reg(state
, REG_STRENGTH
);
207 dprintk("Wait for carrier[%d] 0x%02X\n", n
, val
);
208 if (val
& REG_STRENGTH_CARRIER
)
212 if (!(val
& REG_STRENGTH_CARRIER
))
216 for (n
= 0; n
< 20; n
++) {
217 val
= lgs8gl5_read_reg(state
, REG_STATUS
);
218 dprintk("Wait for lock[%d] 0x%02X\n", n
, val
);
219 if (val
& REG_STATUS_LOCK
)
223 if (!(val
& REG_STATUS_LOCK
))
226 lgs8gl5_write_reg(state
, REG_7D
, lgs8gl5_read_reg(state
, REG_A2
));
227 lgs8gl5_soft_reset(state
);
232 lgs8gl5_init(struct dvb_frontend
*fe
)
234 struct lgs8gl5_state
*state
= fe
->demodulator_priv
;
236 dprintk("%s\n", __func__
);
238 lgs8gl5_update_alt_reg(state
, 0xc2, 0x28);
239 lgs8gl5_soft_reset(state
);
240 lgs8gl5_update_reg(state
, REG_07
, 0x10);
241 lgs8gl5_update_reg(state
, REG_07
, 0x10);
242 lgs8gl5_write_reg(state
, REG_09
, 0x0e);
243 lgs8gl5_write_reg(state
, REG_0A
, 0xe5);
244 lgs8gl5_write_reg(state
, REG_0B
, 0x35);
245 lgs8gl5_write_reg(state
, REG_0C
, 0x30);
252 lgs8gl5_read_status(struct dvb_frontend
*fe
, enum fe_status
*status
)
254 struct lgs8gl5_state
*state
= fe
->demodulator_priv
;
255 u8 level
= lgs8gl5_read_reg(state
, REG_STRENGTH
);
256 u8 flags
= lgs8gl5_read_reg(state
, REG_STATUS
);
260 if ((level
& REG_STRENGTH_MASK
) > 0)
261 *status
|= FE_HAS_SIGNAL
;
262 if (level
& REG_STRENGTH_CARRIER
)
263 *status
|= FE_HAS_CARRIER
;
264 if (flags
& REG_STATUS_SYNC
)
265 *status
|= FE_HAS_SYNC
;
266 if (flags
& REG_STATUS_LOCK
)
267 *status
|= FE_HAS_LOCK
;
274 lgs8gl5_read_ber(struct dvb_frontend
*fe
, u32
*ber
)
283 lgs8gl5_read_signal_strength(struct dvb_frontend
*fe
, u16
*signal_strength
)
285 struct lgs8gl5_state
*state
= fe
->demodulator_priv
;
286 u8 level
= lgs8gl5_read_reg(state
, REG_STRENGTH
);
287 *signal_strength
= (level
& REG_STRENGTH_MASK
) << 8;
294 lgs8gl5_read_snr(struct dvb_frontend
*fe
, u16
*snr
)
296 struct lgs8gl5_state
*state
= fe
->demodulator_priv
;
297 u8 level
= lgs8gl5_read_reg(state
, REG_STRENGTH
);
298 *snr
= (level
& REG_STRENGTH_MASK
) << 8;
305 lgs8gl5_read_ucblocks(struct dvb_frontend
*fe
, u32
*ucblocks
)
314 lgs8gl5_set_frontend(struct dvb_frontend
*fe
)
316 struct dtv_frontend_properties
*p
= &fe
->dtv_property_cache
;
317 struct lgs8gl5_state
*state
= fe
->demodulator_priv
;
319 dprintk("%s\n", __func__
);
321 if (p
->bandwidth_hz
!= 8000000)
324 if (fe
->ops
.tuner_ops
.set_params
) {
325 fe
->ops
.tuner_ops
.set_params(fe
);
326 if (fe
->ops
.i2c_gate_ctrl
)
327 fe
->ops
.i2c_gate_ctrl(fe
, 0);
330 /* lgs8gl5_set_inversion(state, p->inversion); */
332 lgs8gl5_start_demod(state
);
339 lgs8gl5_get_frontend(struct dvb_frontend
*fe
,
340 struct dtv_frontend_properties
*p
)
342 struct lgs8gl5_state
*state
= fe
->demodulator_priv
;
344 u8 inv
= lgs8gl5_read_reg(state
, REG_INVERSION
);
346 p
->inversion
= (inv
& REG_INVERSION_ON
) ? INVERSION_ON
: INVERSION_OFF
;
348 p
->code_rate_HP
= FEC_1_2
;
349 p
->code_rate_LP
= FEC_7_8
;
350 p
->guard_interval
= GUARD_INTERVAL_1_32
;
351 p
->transmission_mode
= TRANSMISSION_MODE_2K
;
352 p
->modulation
= QAM_64
;
353 p
->hierarchy
= HIERARCHY_NONE
;
354 p
->bandwidth_hz
= 8000000;
361 lgs8gl5_get_tune_settings(struct dvb_frontend
*fe
,
362 struct dvb_frontend_tune_settings
*fesettings
)
364 fesettings
->min_delay_ms
= 240;
365 fesettings
->step_size
= 0;
366 fesettings
->max_drift
= 0;
372 lgs8gl5_release(struct dvb_frontend
*fe
)
374 struct lgs8gl5_state
*state
= fe
->demodulator_priv
;
379 static const struct dvb_frontend_ops lgs8gl5_ops
;
383 lgs8gl5_attach(const struct lgs8gl5_config
*config
, struct i2c_adapter
*i2c
)
385 struct lgs8gl5_state
*state
= NULL
;
387 dprintk("%s\n", __func__
);
389 /* Allocate memory for the internal state */
390 state
= kzalloc(sizeof(struct lgs8gl5_state
), GFP_KERNEL
);
394 /* Setup the state */
395 state
->config
= config
;
398 /* Check if the demod is there */
399 if (lgs8gl5_read_reg(state
, REG_RESET
) < 0)
402 /* Create dvb_frontend */
403 memcpy(&state
->frontend
.ops
, &lgs8gl5_ops
,
404 sizeof(struct dvb_frontend_ops
));
405 state
->frontend
.demodulator_priv
= state
;
406 return &state
->frontend
;
412 EXPORT_SYMBOL(lgs8gl5_attach
);
415 static const struct dvb_frontend_ops lgs8gl5_ops
= {
416 .delsys
= { SYS_DTMB
},
418 .name
= "Legend Silicon LGS-8GL5 DMB-TH",
419 .frequency_min
= 474000000,
420 .frequency_max
= 858000000,
421 .frequency_stepsize
= 10000,
422 .frequency_tolerance
= 0,
423 .caps
= FE_CAN_FEC_AUTO
|
424 FE_CAN_QPSK
| FE_CAN_QAM_16
| FE_CAN_QAM_32
|
425 FE_CAN_QAM_64
| FE_CAN_QAM_AUTO
|
426 FE_CAN_TRANSMISSION_MODE_AUTO
|
427 FE_CAN_BANDWIDTH_AUTO
|
428 FE_CAN_GUARD_INTERVAL_AUTO
|
429 FE_CAN_HIERARCHY_AUTO
|
433 .release
= lgs8gl5_release
,
435 .init
= lgs8gl5_init
,
437 .set_frontend
= lgs8gl5_set_frontend
,
438 .get_frontend
= lgs8gl5_get_frontend
,
439 .get_tune_settings
= lgs8gl5_get_tune_settings
,
441 .read_status
= lgs8gl5_read_status
,
442 .read_ber
= lgs8gl5_read_ber
,
443 .read_signal_strength
= lgs8gl5_read_signal_strength
,
444 .read_snr
= lgs8gl5_read_snr
,
445 .read_ucblocks
= lgs8gl5_read_ucblocks
,
449 module_param(debug
, int, 0644);
450 MODULE_PARM_DESC(debug
, "Turn on/off frontend debugging (default:off).");
452 MODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver");
453 MODULE_AUTHOR("Timothy Lee");
454 MODULE_LICENSE("GPL");