]>
Commit | Line | Data |
---|---|---|
67ea14f2 MR |
1 | /* |
2 | * Driver for LG ATSC lgdt3304 driver | |
3 | * | |
4 | * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de> | |
5 | * | |
6 | */ | |
7 | ||
8 | #include <linux/kernel.h> | |
9 | #include <linux/module.h> | |
5a0e3ad6 | 10 | #include <linux/slab.h> |
67ea14f2 MR |
11 | #include <linux/delay.h> |
12 | #include "dvb_frontend.h" | |
13 | #include "lgdt3304.h" | |
14 | ||
15 | static unsigned int debug = 0; | |
16 | module_param(debug, int, 0644); | |
17 | MODULE_PARM_DESC(debug,"lgdt3304 debugging (default off)"); | |
18 | ||
19 | #define dprintk(fmt, args...) if (debug) do {\ | |
20 | printk("lgdt3304 debug: " fmt, ##args); } while (0) | |
21 | ||
22 | struct lgdt3304_state | |
23 | { | |
f71f11ed MCC |
24 | struct dvb_frontend frontend; |
25 | fe_modulation_t current_modulation; | |
26 | __u32 snr; | |
27 | __u32 current_frequency; | |
67ea14f2 MR |
28 | __u8 addr; |
29 | struct i2c_adapter *i2c; | |
30 | }; | |
31 | ||
32 | static int i2c_write_demod_bytes (struct dvb_frontend *fe, __u8 *buf, int len) | |
33 | { | |
34 | struct lgdt3304_state *state = fe->demodulator_priv; | |
35 | struct i2c_msg i2cmsgs = { | |
36 | .addr = state->addr, | |
37 | .flags = 0, | |
38 | .len = 3, | |
39 | .buf = buf | |
40 | }; | |
f71f11ed MCC |
41 | int i; |
42 | int err; | |
67ea14f2 | 43 | |
f71f11ed | 44 | for (i=0; i<len-1; i+=3){ |
67ea14f2 | 45 | if((err = i2c_transfer(state->i2c, &i2cmsgs, 1))<0) { |
9b4778f6 | 46 | printk("%s i2c_transfer error %d\n", __func__, err); |
f71f11ed MCC |
47 | if (err < 0) |
48 | return err; | |
49 | else | |
50 | return -EREMOTEIO; | |
51 | } | |
52 | i2cmsgs.buf += 3; | |
53 | } | |
54 | return 0; | |
67ea14f2 MR |
55 | } |
56 | ||
57 | static int lgdt3304_i2c_read_reg(struct dvb_frontend *fe, unsigned int reg) | |
58 | { | |
59 | struct lgdt3304_state *state = fe->demodulator_priv; | |
60 | struct i2c_msg i2cmsgs[2]; | |
61 | int ret; | |
62 | __u8 buf; | |
63 | ||
64 | __u8 regbuf[2] = { reg>>8, reg&0xff }; | |
65 | ||
66 | i2cmsgs[0].addr = state->addr; | |
67 | i2cmsgs[0].flags = 0; | |
68 | i2cmsgs[0].len = 2; | |
69 | i2cmsgs[0].buf = regbuf; | |
70 | ||
71 | i2cmsgs[1].addr = state->addr; | |
72 | i2cmsgs[1].flags = I2C_M_RD; | |
73 | i2cmsgs[1].len = 1; | |
74 | i2cmsgs[1].buf = &buf; | |
75 | ||
76 | if((ret = i2c_transfer(state->i2c, i2cmsgs, 2))<0) { | |
9b4778f6 | 77 | printk("%s i2c_transfer error %d\n", __func__, ret); |
67ea14f2 MR |
78 | return ret; |
79 | } | |
80 | ||
81 | return buf; | |
82 | } | |
83 | ||
84 | static int lgdt3304_i2c_write_reg(struct dvb_frontend *fe, int reg, int val) | |
85 | { | |
86 | struct lgdt3304_state *state = fe->demodulator_priv; | |
87 | char buffer[3] = { reg>>8, reg&0xff, val }; | |
88 | int ret; | |
89 | ||
90 | struct i2c_msg i2cmsgs = { | |
91 | .addr = state->addr, | |
92 | .flags = 0, | |
93 | .len = 3, | |
94 | .buf=buffer | |
95 | }; | |
96 | ret = i2c_transfer(state->i2c, &i2cmsgs, 1); | |
97 | if (ret != 1) { | |
9b4778f6 | 98 | printk("%s i2c_transfer error %d\n", __func__, ret); |
67ea14f2 MR |
99 | return ret; |
100 | } | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
105 | ||
106 | static int lgdt3304_soft_Reset(struct dvb_frontend *fe) | |
107 | { | |
108 | lgdt3304_i2c_write_reg(fe, 0x0002, 0x9a); | |
109 | lgdt3304_i2c_write_reg(fe, 0x0002, 0x9b); | |
110 | mdelay(200); | |
111 | return 0; | |
112 | } | |
113 | ||
114 | static int lgdt3304_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) { | |
115 | int err = 0; | |
116 | ||
117 | static __u8 lgdt3304_vsb8_data[] = { | |
118 | /* 16bit , 8bit */ | |
119 | /* regs , val */ | |
120 | 0x00, 0x00, 0x02, | |
121 | 0x00, 0x00, 0x13, | |
122 | 0x00, 0x0d, 0x02, | |
123 | 0x00, 0x0e, 0x02, | |
124 | 0x00, 0x12, 0x32, | |
125 | 0x00, 0x13, 0xc4, | |
126 | 0x01, 0x12, 0x17, | |
127 | 0x01, 0x13, 0x15, | |
128 | 0x01, 0x14, 0x18, | |
129 | 0x01, 0x15, 0xff, | |
130 | 0x01, 0x16, 0x2c, | |
131 | 0x02, 0x14, 0x67, | |
132 | 0x02, 0x24, 0x8d, | |
133 | 0x04, 0x27, 0x12, | |
134 | 0x04, 0x28, 0x4f, | |
135 | 0x03, 0x08, 0x80, | |
136 | 0x03, 0x09, 0x00, | |
137 | 0x03, 0x0d, 0x00, | |
138 | 0x03, 0x0e, 0x1c, | |
139 | 0x03, 0x14, 0xe1, | |
140 | 0x05, 0x0e, 0x5b, | |
141 | }; | |
142 | ||
143 | /* not yet tested .. */ | |
144 | static __u8 lgdt3304_qam64_data[] = { | |
145 | /* 16bit , 8bit */ | |
146 | /* regs , val */ | |
147 | 0x00, 0x00, 0x18, | |
148 | 0x00, 0x0d, 0x02, | |
149 | //0x00, 0x0e, 0x02, | |
150 | 0x00, 0x12, 0x2a, | |
151 | 0x00, 0x13, 0x00, | |
152 | 0x03, 0x14, 0xe3, | |
153 | 0x03, 0x0e, 0x1c, | |
154 | 0x03, 0x08, 0x66, | |
155 | 0x03, 0x09, 0x66, | |
156 | 0x03, 0x0a, 0x08, | |
157 | 0x03, 0x0b, 0x9b, | |
158 | 0x05, 0x0e, 0x5b, | |
159 | }; | |
160 | ||
161 | ||
162 | /* tested with KWorld a340 */ | |
163 | static __u8 lgdt3304_qam256_data[] = { | |
164 | /* 16bit , 8bit */ | |
165 | /* regs , val */ | |
166 | 0x00, 0x00, 0x01, //0x19, | |
167 | 0x00, 0x12, 0x2a, | |
168 | 0x00, 0x13, 0x80, | |
169 | 0x00, 0x0d, 0x02, | |
170 | 0x03, 0x14, 0xe3, | |
171 | ||
172 | 0x03, 0x0e, 0x1c, | |
173 | 0x03, 0x08, 0x66, | |
174 | 0x03, 0x09, 0x66, | |
175 | 0x03, 0x0a, 0x08, | |
176 | 0x03, 0x0b, 0x9b, | |
177 | ||
178 | 0x03, 0x0d, 0x14, | |
179 | //0x05, 0x0e, 0x5b, | |
180 | 0x01, 0x06, 0x4a, | |
181 | 0x01, 0x07, 0x3d, | |
182 | 0x01, 0x08, 0x70, | |
183 | 0x01, 0x09, 0xa3, | |
184 | ||
185 | 0x05, 0x04, 0xfd, | |
186 | ||
187 | 0x00, 0x0d, 0x82, | |
188 | ||
189 | 0x05, 0x0e, 0x5b, | |
190 | ||
191 | 0x05, 0x0e, 0x5b, | |
192 | ||
193 | 0x00, 0x02, 0x9a, | |
194 | ||
195 | 0x00, 0x02, 0x9b, | |
196 | ||
197 | 0x00, 0x00, 0x01, | |
198 | 0x00, 0x12, 0x2a, | |
199 | 0x00, 0x13, 0x80, | |
200 | 0x00, 0x0d, 0x02, | |
201 | 0x03, 0x14, 0xe3, | |
202 | ||
203 | 0x03, 0x0e, 0x1c, | |
204 | 0x03, 0x08, 0x66, | |
205 | 0x03, 0x09, 0x66, | |
206 | 0x03, 0x0a, 0x08, | |
207 | 0x03, 0x0b, 0x9b, | |
208 | ||
209 | 0x03, 0x0d, 0x14, | |
210 | 0x01, 0x06, 0x4a, | |
211 | 0x01, 0x07, 0x3d, | |
212 | 0x01, 0x08, 0x70, | |
213 | 0x01, 0x09, 0xa3, | |
214 | ||
215 | 0x05, 0x04, 0xfd, | |
216 | ||
217 | 0x00, 0x0d, 0x82, | |
218 | ||
219 | 0x05, 0x0e, 0x5b, | |
220 | }; | |
221 | ||
222 | struct lgdt3304_state *state = fe->demodulator_priv; | |
223 | if (state->current_modulation != param->u.vsb.modulation) { | |
224 | switch(param->u.vsb.modulation) { | |
225 | case VSB_8: | |
226 | err = i2c_write_demod_bytes(fe, lgdt3304_vsb8_data, | |
227 | sizeof(lgdt3304_vsb8_data)); | |
228 | break; | |
229 | case QAM_64: | |
230 | err = i2c_write_demod_bytes(fe, lgdt3304_qam64_data, | |
231 | sizeof(lgdt3304_qam64_data)); | |
232 | break; | |
233 | case QAM_256: | |
234 | err = i2c_write_demod_bytes(fe, lgdt3304_qam256_data, | |
235 | sizeof(lgdt3304_qam256_data)); | |
236 | break; | |
237 | default: | |
238 | break; | |
239 | } | |
240 | ||
241 | if (err) { | |
9b4778f6 | 242 | printk("%s error setting modulation\n", __func__); |
67ea14f2 MR |
243 | } else { |
244 | state->current_modulation = param->u.vsb.modulation; | |
245 | } | |
246 | } | |
247 | state->current_frequency = param->frequency; | |
248 | ||
249 | lgdt3304_soft_Reset(fe); | |
250 | ||
251 | ||
f71f11ed MCC |
252 | if (fe->ops.tuner_ops.set_params) |
253 | fe->ops.tuner_ops.set_params(fe, param); | |
67ea14f2 MR |
254 | |
255 | return 0; | |
256 | } | |
257 | ||
258 | static int lgdt3304_init(struct dvb_frontend *fe) { | |
f71f11ed | 259 | return 0; |
67ea14f2 MR |
260 | } |
261 | ||
262 | static int lgdt3304_sleep(struct dvb_frontend *fe) { | |
263 | return 0; | |
264 | } | |
265 | ||
266 | ||
267 | static int lgdt3304_read_status(struct dvb_frontend *fe, fe_status_t *status) | |
268 | { | |
269 | struct lgdt3304_state *state = fe->demodulator_priv; | |
270 | int r011d; | |
271 | int qam_lck; | |
272 | ||
273 | *status = 0; | |
274 | dprintk("lgdt read status\n"); | |
275 | ||
276 | r011d = lgdt3304_i2c_read_reg(fe, 0x011d); | |
277 | ||
278 | dprintk("%02x\n", r011d); | |
279 | ||
280 | switch(state->current_modulation) { | |
281 | case VSB_8: | |
282 | if (r011d & 0x80) { | |
283 | dprintk("VSB Locked\n"); | |
284 | *status |= FE_HAS_CARRIER; | |
285 | *status |= FE_HAS_LOCK; | |
286 | *status |= FE_HAS_SYNC; | |
287 | *status |= FE_HAS_SIGNAL; | |
288 | } | |
289 | break; | |
290 | case QAM_64: | |
291 | case QAM_256: | |
292 | qam_lck = r011d & 0x7; | |
293 | switch(qam_lck) { | |
294 | case 0x0: dprintk("Unlock\n"); | |
295 | break; | |
296 | case 0x4: dprintk("1st Lock in acquisition state\n"); | |
297 | break; | |
298 | case 0x6: dprintk("2nd Lock in acquisition state\n"); | |
299 | break; | |
300 | case 0x7: dprintk("Final Lock in good reception state\n"); | |
301 | *status |= FE_HAS_CARRIER; | |
302 | *status |= FE_HAS_LOCK; | |
303 | *status |= FE_HAS_SYNC; | |
304 | *status |= FE_HAS_SIGNAL; | |
305 | break; | |
306 | } | |
307 | break; | |
308 | default: | |
9b4778f6 | 309 | printk("%s unhandled modulation\n", __func__); |
67ea14f2 MR |
310 | } |
311 | ||
312 | ||
313 | return 0; | |
314 | } | |
315 | ||
316 | static int lgdt3304_read_ber(struct dvb_frontend *fe, __u32 *ber) | |
317 | { | |
318 | dprintk("read ber\n"); | |
319 | return 0; | |
320 | } | |
321 | ||
322 | static int lgdt3304_read_snr(struct dvb_frontend *fe, __u16 *snr) | |
323 | { | |
324 | dprintk("read snr\n"); | |
325 | return 0; | |
326 | } | |
327 | ||
328 | static int lgdt3304_read_ucblocks(struct dvb_frontend *fe, __u32 *ucblocks) | |
329 | { | |
330 | dprintk("read ucblocks\n"); | |
331 | return 0; | |
332 | } | |
333 | ||
334 | static void lgdt3304_release(struct dvb_frontend *fe) | |
335 | { | |
336 | struct lgdt3304_state *state = (struct lgdt3304_state *)fe->demodulator_priv; | |
337 | kfree(state); | |
338 | } | |
339 | ||
340 | static struct dvb_frontend_ops demod_lgdt3304={ | |
341 | .info = { | |
342 | .name = "LG 3304", | |
343 | .type = FE_ATSC, | |
344 | .frequency_min = 54000000, | |
345 | .frequency_max = 858000000, | |
346 | .frequency_stepsize = 62500, | |
347 | .symbol_rate_min = 5056941, | |
348 | .symbol_rate_max = 10762000, | |
349 | .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB | |
350 | }, | |
351 | .init = lgdt3304_init, | |
352 | .sleep = lgdt3304_sleep, | |
353 | .set_frontend = lgdt3304_set_parameters, | |
354 | .read_snr = lgdt3304_read_snr, | |
355 | .read_ber = lgdt3304_read_ber, | |
356 | .read_status = lgdt3304_read_status, | |
357 | .read_ucblocks = lgdt3304_read_ucblocks, | |
358 | .release = lgdt3304_release, | |
359 | }; | |
360 | ||
361 | struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config, | |
362 | struct i2c_adapter *i2c) | |
363 | { | |
364 | ||
365 | struct lgdt3304_state *state; | |
366 | state = kzalloc(sizeof(struct lgdt3304_state), GFP_KERNEL); | |
8d04df49 RK |
367 | if (state == NULL) |
368 | return NULL; | |
67ea14f2 MR |
369 | state->addr = config->i2c_address; |
370 | state->i2c = i2c; | |
371 | ||
372 | memcpy(&state->frontend.ops, &demod_lgdt3304, sizeof(struct dvb_frontend_ops)); | |
373 | state->frontend.demodulator_priv = state; | |
374 | return &state->frontend; | |
375 | } | |
376 | ||
377 | EXPORT_SYMBOL_GPL(lgdt3304_attach); | |
378 | MODULE_AUTHOR("Markus Rechberger <mrechberger@empiatech.com>"); | |
379 | MODULE_DESCRIPTION("LGE LGDT3304 DVB-T demodulator driver"); | |
380 | MODULE_LICENSE("GPL"); |