1 // SPDX-License-Identifier: GPL-2.0-or-later
3 Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver
5 Copyright (C) 2008 Sirius International (Hong Kong) Limited
6 Timothy Lee <timothy.lee@siriushk.com>
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/string.h>
15 #include <linux/slab.h>
16 #include <media/dvb_frontend.h>
20 #define REG_RESET 0x02
21 #define REG_RESET_OFF 0x01
30 #define REG_STRENGTH 0x4b
31 #define REG_STRENGTH_MASK 0x7f
32 #define REG_STRENGTH_CARRIER 0x80
33 #define REG_INVERSION 0x7c
34 #define REG_INVERSION_ON 0x80
38 #define REG_STATUS 0xa4
39 #define REG_STATUS_SYNC 0x04
40 #define REG_STATUS_LOCK 0x01
43 struct lgs8gl5_state
{
44 struct i2c_adapter
*i2c
;
45 const struct lgs8gl5_config
*config
;
46 struct dvb_frontend frontend
;
51 #define dprintk(args...) \
54 printk(KERN_DEBUG "lgs8gl5: " args); \
58 /* Writes into demod's register */
60 lgs8gl5_write_reg(struct lgs8gl5_state
*state
, u8 reg
, u8 data
)
63 u8 buf
[] = {reg
, data
};
64 struct i2c_msg msg
= {
65 .addr
= state
->config
->demod_address
,
71 ret
= i2c_transfer(state
->i2c
, &msg
, 1);
73 dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n",
74 __func__
, reg
, data
, ret
);
75 return (ret
!= 1) ? -1 : 0;
79 /* Reads from demod's register */
81 lgs8gl5_read_reg(struct lgs8gl5_state
*state
, u8 reg
)
86 struct i2c_msg msg
[2] = {
88 .addr
= state
->config
->demod_address
,
94 .addr
= state
->config
->demod_address
,
101 ret
= i2c_transfer(state
->i2c
, msg
, 2);
110 lgs8gl5_update_reg(struct lgs8gl5_state
*state
, u8 reg
, u8 data
)
112 lgs8gl5_read_reg(state
, reg
);
113 lgs8gl5_write_reg(state
, reg
, data
);
118 /* Writes into alternate device's register */
119 /* TODO: Find out what that device is for! */
121 lgs8gl5_update_alt_reg(struct lgs8gl5_state
*state
, u8 reg
, u8 data
)
126 u8 b2
[] = {reg
, data
};
127 struct i2c_msg msg
[3] = {
129 .addr
= state
->config
->demod_address
+ 2,
135 .addr
= state
->config
->demod_address
+ 2,
141 .addr
= state
->config
->demod_address
+ 2,
148 ret
= i2c_transfer(state
->i2c
, msg
, 3);
149 return (ret
!= 3) ? -1 : 0;
154 lgs8gl5_soft_reset(struct lgs8gl5_state
*state
)
158 dprintk("%s\n", __func__
);
160 val
= lgs8gl5_read_reg(state
, REG_RESET
);
161 lgs8gl5_write_reg(state
, REG_RESET
, val
& ~REG_RESET_OFF
);
162 lgs8gl5_write_reg(state
, REG_RESET
, val
| REG_RESET_OFF
);
167 /* Starts demodulation */
169 lgs8gl5_start_demod(struct lgs8gl5_state
*state
)
174 dprintk("%s\n", __func__
);
176 lgs8gl5_update_alt_reg(state
, 0xc2, 0x28);
177 lgs8gl5_soft_reset(state
);
178 lgs8gl5_update_reg(state
, REG_07
, 0x10);
179 lgs8gl5_update_reg(state
, REG_07
, 0x10);
180 lgs8gl5_write_reg(state
, REG_09
, 0x0e);
181 lgs8gl5_write_reg(state
, REG_0A
, 0xe5);
182 lgs8gl5_write_reg(state
, REG_0B
, 0x35);
183 lgs8gl5_write_reg(state
, REG_0C
, 0x30);
185 lgs8gl5_update_reg(state
, REG_03
, 0x00);
186 lgs8gl5_update_reg(state
, REG_7E
, 0x01);
187 lgs8gl5_update_alt_reg(state
, 0xc5, 0x00);
188 lgs8gl5_update_reg(state
, REG_04
, 0x02);
189 lgs8gl5_update_reg(state
, REG_37
, 0x01);
190 lgs8gl5_soft_reset(state
);
192 /* Wait for carrier */
193 for (n
= 0; n
< 10; n
++) {
194 val
= lgs8gl5_read_reg(state
, REG_STRENGTH
);
195 dprintk("Wait for carrier[%d] 0x%02X\n", n
, val
);
196 if (val
& REG_STRENGTH_CARRIER
)
200 if (!(val
& REG_STRENGTH_CARRIER
))
204 for (n
= 0; n
< 20; n
++) {
205 val
= lgs8gl5_read_reg(state
, REG_STATUS
);
206 dprintk("Wait for lock[%d] 0x%02X\n", n
, val
);
207 if (val
& REG_STATUS_LOCK
)
211 if (!(val
& REG_STATUS_LOCK
))
214 lgs8gl5_write_reg(state
, REG_7D
, lgs8gl5_read_reg(state
, REG_A2
));
215 lgs8gl5_soft_reset(state
);
220 lgs8gl5_init(struct dvb_frontend
*fe
)
222 struct lgs8gl5_state
*state
= fe
->demodulator_priv
;
224 dprintk("%s\n", __func__
);
226 lgs8gl5_update_alt_reg(state
, 0xc2, 0x28);
227 lgs8gl5_soft_reset(state
);
228 lgs8gl5_update_reg(state
, REG_07
, 0x10);
229 lgs8gl5_update_reg(state
, REG_07
, 0x10);
230 lgs8gl5_write_reg(state
, REG_09
, 0x0e);
231 lgs8gl5_write_reg(state
, REG_0A
, 0xe5);
232 lgs8gl5_write_reg(state
, REG_0B
, 0x35);
233 lgs8gl5_write_reg(state
, REG_0C
, 0x30);
240 lgs8gl5_read_status(struct dvb_frontend
*fe
, enum fe_status
*status
)
242 struct lgs8gl5_state
*state
= fe
->demodulator_priv
;
243 u8 level
= lgs8gl5_read_reg(state
, REG_STRENGTH
);
244 u8 flags
= lgs8gl5_read_reg(state
, REG_STATUS
);
248 if ((level
& REG_STRENGTH_MASK
) > 0)
249 *status
|= FE_HAS_SIGNAL
;
250 if (level
& REG_STRENGTH_CARRIER
)
251 *status
|= FE_HAS_CARRIER
;
252 if (flags
& REG_STATUS_SYNC
)
253 *status
|= FE_HAS_SYNC
;
254 if (flags
& REG_STATUS_LOCK
)
255 *status
|= FE_HAS_LOCK
;
262 lgs8gl5_read_ber(struct dvb_frontend
*fe
, u32
*ber
)
271 lgs8gl5_read_signal_strength(struct dvb_frontend
*fe
, u16
*signal_strength
)
273 struct lgs8gl5_state
*state
= fe
->demodulator_priv
;
274 u8 level
= lgs8gl5_read_reg(state
, REG_STRENGTH
);
275 *signal_strength
= (level
& REG_STRENGTH_MASK
) << 8;
282 lgs8gl5_read_snr(struct dvb_frontend
*fe
, u16
*snr
)
284 struct lgs8gl5_state
*state
= fe
->demodulator_priv
;
285 u8 level
= lgs8gl5_read_reg(state
, REG_STRENGTH
);
286 *snr
= (level
& REG_STRENGTH_MASK
) << 8;
293 lgs8gl5_read_ucblocks(struct dvb_frontend
*fe
, u32
*ucblocks
)
302 lgs8gl5_set_frontend(struct dvb_frontend
*fe
)
304 struct dtv_frontend_properties
*p
= &fe
->dtv_property_cache
;
305 struct lgs8gl5_state
*state
= fe
->demodulator_priv
;
307 dprintk("%s\n", __func__
);
309 if (p
->bandwidth_hz
!= 8000000)
312 if (fe
->ops
.tuner_ops
.set_params
) {
313 fe
->ops
.tuner_ops
.set_params(fe
);
314 if (fe
->ops
.i2c_gate_ctrl
)
315 fe
->ops
.i2c_gate_ctrl(fe
, 0);
318 /* lgs8gl5_set_inversion(state, p->inversion); */
320 lgs8gl5_start_demod(state
);
327 lgs8gl5_get_frontend(struct dvb_frontend
*fe
,
328 struct dtv_frontend_properties
*p
)
330 struct lgs8gl5_state
*state
= fe
->demodulator_priv
;
332 u8 inv
= lgs8gl5_read_reg(state
, REG_INVERSION
);
334 p
->inversion
= (inv
& REG_INVERSION_ON
) ? INVERSION_ON
: INVERSION_OFF
;
336 p
->code_rate_HP
= FEC_1_2
;
337 p
->code_rate_LP
= FEC_7_8
;
338 p
->guard_interval
= GUARD_INTERVAL_1_32
;
339 p
->transmission_mode
= TRANSMISSION_MODE_2K
;
340 p
->modulation
= QAM_64
;
341 p
->hierarchy
= HIERARCHY_NONE
;
342 p
->bandwidth_hz
= 8000000;
349 lgs8gl5_get_tune_settings(struct dvb_frontend
*fe
,
350 struct dvb_frontend_tune_settings
*fesettings
)
352 fesettings
->min_delay_ms
= 240;
353 fesettings
->step_size
= 0;
354 fesettings
->max_drift
= 0;
360 lgs8gl5_release(struct dvb_frontend
*fe
)
362 struct lgs8gl5_state
*state
= fe
->demodulator_priv
;
367 static const struct dvb_frontend_ops lgs8gl5_ops
;
371 lgs8gl5_attach(const struct lgs8gl5_config
*config
, struct i2c_adapter
*i2c
)
373 struct lgs8gl5_state
*state
= NULL
;
375 dprintk("%s\n", __func__
);
377 /* Allocate memory for the internal state */
378 state
= kzalloc(sizeof(struct lgs8gl5_state
), GFP_KERNEL
);
382 /* Setup the state */
383 state
->config
= config
;
386 /* Check if the demod is there */
387 if (lgs8gl5_read_reg(state
, REG_RESET
) < 0)
390 /* Create dvb_frontend */
391 memcpy(&state
->frontend
.ops
, &lgs8gl5_ops
,
392 sizeof(struct dvb_frontend_ops
));
393 state
->frontend
.demodulator_priv
= state
;
394 return &state
->frontend
;
400 EXPORT_SYMBOL(lgs8gl5_attach
);
403 static const struct dvb_frontend_ops lgs8gl5_ops
= {
404 .delsys
= { SYS_DTMB
},
406 .name
= "Legend Silicon LGS-8GL5 DMB-TH",
407 .frequency_min_hz
= 474 * MHz
,
408 .frequency_max_hz
= 858 * MHz
,
409 .frequency_stepsize_hz
= 10 * kHz
,
410 .caps
= FE_CAN_FEC_AUTO
|
411 FE_CAN_QPSK
| FE_CAN_QAM_16
| FE_CAN_QAM_32
|
412 FE_CAN_QAM_64
| FE_CAN_QAM_AUTO
|
413 FE_CAN_TRANSMISSION_MODE_AUTO
|
414 FE_CAN_BANDWIDTH_AUTO
|
415 FE_CAN_GUARD_INTERVAL_AUTO
|
416 FE_CAN_HIERARCHY_AUTO
|
420 .release
= lgs8gl5_release
,
422 .init
= lgs8gl5_init
,
424 .set_frontend
= lgs8gl5_set_frontend
,
425 .get_frontend
= lgs8gl5_get_frontend
,
426 .get_tune_settings
= lgs8gl5_get_tune_settings
,
428 .read_status
= lgs8gl5_read_status
,
429 .read_ber
= lgs8gl5_read_ber
,
430 .read_signal_strength
= lgs8gl5_read_signal_strength
,
431 .read_snr
= lgs8gl5_read_snr
,
432 .read_ucblocks
= lgs8gl5_read_ucblocks
,
436 module_param(debug
, int, 0644);
437 MODULE_PARM_DESC(debug
, "Turn on/off frontend debugging (default:off).");
439 MODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver");
440 MODULE_AUTHOR("Timothy Lee");
441 MODULE_LICENSE("GPL");