diff options
author | Michael Krufky <mkrufky@linuxtv.org> | 2009-01-13 02:40:36 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-03-30 11:43:16 -0400 |
commit | cae78ed599c348999a318ace0fcc3ff0277c8fa4 (patch) | |
tree | 6cf7b7f1c4f1966d550d6004670b7c1c8cc0a12c /drivers/media | |
parent | 90a4cc70fa853d83f0a7cc34b960744d8d0280e9 (diff) |
V4L/DVB (10925): add support for LG Electronics LGDT3305 ATSC/QAM-B Demodulator
Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/dvb/frontends/Kconfig | 8 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/lgdt3305.c | 1087 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/lgdt3305.h | 85 |
4 files changed, 1181 insertions, 0 deletions
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 2887d3398fb8..5c78f6329d68 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig | |||
@@ -419,6 +419,14 @@ config DVB_LGDT3304 | |||
419 | An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want | 419 | An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want |
420 | to support this frontend. | 420 | to support this frontend. |
421 | 421 | ||
422 | config DVB_LGDT3305 | ||
423 | tristate "LG Electronics LGDT3305 based" | ||
424 | depends on DVB_CORE && I2C | ||
425 | default m if DVB_FE_CUSTOMISE | ||
426 | help | ||
427 | An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want | ||
428 | to support this frontend. | ||
429 | |||
422 | config DVB_S5H1409 | 430 | config DVB_S5H1409 |
423 | tristate "Samsung S5H1409 based" | 431 | tristate "Samsung S5H1409 based" |
424 | depends on DVB_CORE && I2C | 432 | depends on DVB_CORE && I2C |
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 1e3866b2fd2e..2a250399b555 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile | |||
@@ -43,6 +43,7 @@ obj-$(CONFIG_DVB_BCM3510) += bcm3510.o | |||
43 | obj-$(CONFIG_DVB_S5H1420) += s5h1420.o | 43 | obj-$(CONFIG_DVB_S5H1420) += s5h1420.o |
44 | obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o | 44 | obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o |
45 | obj-$(CONFIG_DVB_LGDT3304) += lgdt3304.o | 45 | obj-$(CONFIG_DVB_LGDT3304) += lgdt3304.o |
46 | obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o | ||
46 | obj-$(CONFIG_DVB_CX24123) += cx24123.o | 47 | obj-$(CONFIG_DVB_CX24123) += cx24123.o |
47 | obj-$(CONFIG_DVB_LNBP21) += lnbp21.o | 48 | obj-$(CONFIG_DVB_LNBP21) += lnbp21.o |
48 | obj-$(CONFIG_DVB_ISL6405) += isl6405.o | 49 | obj-$(CONFIG_DVB_ISL6405) += isl6405.o |
diff --git a/drivers/media/dvb/frontends/lgdt3305.c b/drivers/media/dvb/frontends/lgdt3305.c new file mode 100644 index 000000000000..698c88b0749c --- /dev/null +++ b/drivers/media/dvb/frontends/lgdt3305.c | |||
@@ -0,0 +1,1087 @@ | |||
1 | /* | ||
2 | * Support for LGDT3305 - VSB/QAM | ||
3 | * | ||
4 | * Copyright (C) 2008, 2009 Michael Krufky <mkrufky@linuxtv.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/dvb/frontend.h> | ||
23 | #include "dvb_math.h" | ||
24 | #include "lgdt3305.h" | ||
25 | |||
26 | static int debug; | ||
27 | module_param(debug, int, 0644); | ||
28 | MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))"); | ||
29 | |||
30 | #define DBG_INFO 1 | ||
31 | #define DBG_REG 2 | ||
32 | |||
33 | #define lg_printk(kern, fmt, arg...) \ | ||
34 | printk(kern "%s: " fmt, __func__, ##arg) | ||
35 | |||
36 | #define lg_info(fmt, arg...) printk(KERN_INFO "lgdt3305: " fmt, ##arg) | ||
37 | #define lg_warn(fmt, arg...) lg_printk(KERN_WARNING, fmt, ##arg) | ||
38 | #define lg_err(fmt, arg...) lg_printk(KERN_ERR, fmt, ##arg) | ||
39 | #define lg_dbg(fmt, arg...) if (debug & DBG_INFO) \ | ||
40 | lg_printk(KERN_DEBUG, fmt, ##arg) | ||
41 | #define lg_reg(fmt, arg...) if (debug & DBG_REG) \ | ||
42 | lg_printk(KERN_DEBUG, fmt, ##arg) | ||
43 | |||
44 | #define lg_fail(ret) \ | ||
45 | ({ \ | ||
46 | int __ret; \ | ||
47 | __ret = (ret < 0); \ | ||
48 | if (__ret) \ | ||
49 | lg_err("error %d on line %d\n", ret, __LINE__); \ | ||
50 | __ret; \ | ||
51 | }) | ||
52 | |||
53 | struct lgdt3305_state { | ||
54 | struct i2c_adapter *i2c_adap; | ||
55 | const struct lgdt3305_config *cfg; | ||
56 | |||
57 | struct dvb_frontend frontend; | ||
58 | |||
59 | fe_modulation_t current_modulation; | ||
60 | u32 current_frequency; | ||
61 | u32 snr; | ||
62 | }; | ||
63 | |||
64 | /* ------------------------------------------------------------------------ */ | ||
65 | |||
66 | #define LGDT3305_GEN_CTRL_1 0x0000 | ||
67 | #define LGDT3305_GEN_CTRL_2 0x0001 | ||
68 | #define LGDT3305_GEN_CTRL_3 0x0002 | ||
69 | #define LGDT3305_GEN_STATUS 0x0003 | ||
70 | #define LGDT3305_GEN_CONTROL 0x0007 | ||
71 | #define LGDT3305_GEN_CTRL_4 0x000a | ||
72 | #define LGDT3305_DGTL_AGC_REF_1 0x0012 | ||
73 | #define LGDT3305_DGTL_AGC_REF_2 0x0013 | ||
74 | #define LGDT3305_CR_CTR_FREQ_1 0x0106 | ||
75 | #define LGDT3305_CR_CTR_FREQ_2 0x0107 | ||
76 | #define LGDT3305_CR_CTR_FREQ_3 0x0108 | ||
77 | #define LGDT3305_CR_CTR_FREQ_4 0x0109 | ||
78 | #define LGDT3305_CR_MSE_1 0x011b | ||
79 | #define LGDT3305_CR_MSE_2 0x011c | ||
80 | #define LGDT3305_CR_LOCK_STATUS 0x011d | ||
81 | #define LGDT3305_CR_CTRL_7 0x0126 | ||
82 | #define LGDT3305_AGC_POWER_REF_1 0x0300 | ||
83 | #define LGDT3305_AGC_POWER_REF_2 0x0301 | ||
84 | #define LGDT3305_AGC_DELAY_PT_1 0x0302 | ||
85 | #define LGDT3305_AGC_DELAY_PT_2 0x0303 | ||
86 | #define LGDT3305_RFAGC_LOOP_FLTR_BW_1 0x0306 | ||
87 | #define LGDT3305_RFAGC_LOOP_FLTR_BW_2 0x0307 | ||
88 | #define LGDT3305_IFBW_1 0x0308 | ||
89 | #define LGDT3305_IFBW_2 0x0309 | ||
90 | #define LGDT3305_AGC_CTRL_1 0x030c | ||
91 | #define LGDT3305_AGC_CTRL_4 0x0314 | ||
92 | #define LGDT3305_EQ_MSE_1 0x0413 | ||
93 | #define LGDT3305_EQ_MSE_2 0x0414 | ||
94 | #define LGDT3305_EQ_MSE_3 0x0415 | ||
95 | #define LGDT3305_PT_MSE_1 0x0417 | ||
96 | #define LGDT3305_PT_MSE_2 0x0418 | ||
97 | #define LGDT3305_PT_MSE_3 0x0419 | ||
98 | #define LGDT3305_FEC_BLOCK_CTRL 0x0504 | ||
99 | #define LGDT3305_FEC_LOCK_STATUS 0x050a | ||
100 | #define LGDT3305_FEC_PKT_ERR_1 0x050c | ||
101 | #define LGDT3305_FEC_PKT_ERR_2 0x050d | ||
102 | #define LGDT3305_TP_CTRL_1 0x050e | ||
103 | #define LGDT3305_BERT_PERIOD 0x0801 | ||
104 | #define LGDT3305_BERT_ERROR_COUNT_1 0x080a | ||
105 | #define LGDT3305_BERT_ERROR_COUNT_2 0x080b | ||
106 | #define LGDT3305_BERT_ERROR_COUNT_3 0x080c | ||
107 | #define LGDT3305_BERT_ERROR_COUNT_4 0x080d | ||
108 | |||
109 | static int lgdt3305_write_reg(struct lgdt3305_state *state, u16 reg, u8 val) | ||
110 | { | ||
111 | int ret; | ||
112 | u8 buf[] = { reg >> 8, reg & 0xff, val }; | ||
113 | struct i2c_msg msg = { | ||
114 | .addr = state->cfg->i2c_addr, .flags = 0, | ||
115 | .buf = buf, .len = 3, | ||
116 | }; | ||
117 | |||
118 | lg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val); | ||
119 | |||
120 | ret = i2c_transfer(state->i2c_adap, &msg, 1); | ||
121 | |||
122 | if (ret != 1) { | ||
123 | lg_err("error (addr %02x %02x <- %02x, err = %i)\n", | ||
124 | msg.buf[0], msg.buf[1], msg.buf[2], ret); | ||
125 | if (ret < 0) | ||
126 | return ret; | ||
127 | else | ||
128 | return -EREMOTEIO; | ||
129 | } | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static int lgdt3305_read_reg(struct lgdt3305_state *state, u16 reg, u8 *val) | ||
134 | { | ||
135 | int ret; | ||
136 | u8 reg_buf[] = { reg >> 8, reg & 0xff }; | ||
137 | struct i2c_msg msg[] = { | ||
138 | { .addr = state->cfg->i2c_addr, | ||
139 | .flags = 0, .buf = reg_buf, .len = 2 }, | ||
140 | { .addr = state->cfg->i2c_addr, | ||
141 | .flags = I2C_M_RD, .buf = val, .len = 1 }, | ||
142 | }; | ||
143 | |||
144 | lg_reg("reg: 0x%04x\n", reg); | ||
145 | |||
146 | ret = i2c_transfer(state->i2c_adap, msg, 2); | ||
147 | |||
148 | if (ret != 2) { | ||
149 | lg_err("error (addr %02x reg %04x error (ret == %i)\n", | ||
150 | state->cfg->i2c_addr, reg, ret); | ||
151 | if (ret < 0) | ||
152 | return ret; | ||
153 | else | ||
154 | return -EREMOTEIO; | ||
155 | } | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | #define read_reg(state, reg) \ | ||
160 | ({ \ | ||
161 | u8 __val; \ | ||
162 | int ret = lgdt3305_read_reg(state, reg, &__val); \ | ||
163 | if (lg_fail(ret)) \ | ||
164 | __val = 0; \ | ||
165 | __val; \ | ||
166 | }) | ||
167 | |||
168 | static int lgdt3305_set_reg_bit(struct lgdt3305_state *state, | ||
169 | u16 reg, int bit, int onoff) | ||
170 | { | ||
171 | u8 val; | ||
172 | int ret; | ||
173 | |||
174 | lg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff); | ||
175 | |||
176 | ret = lgdt3305_read_reg(state, reg, &val); | ||
177 | if (lg_fail(ret)) | ||
178 | goto fail; | ||
179 | |||
180 | val &= ~(1 << bit); | ||
181 | val |= (onoff & 1) << bit; | ||
182 | |||
183 | ret = lgdt3305_write_reg(state, reg, val); | ||
184 | fail: | ||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | struct lgdt3305_reg { | ||
189 | u16 reg; | ||
190 | u8 val; | ||
191 | }; | ||
192 | |||
193 | static int lgdt3305_write_regs(struct lgdt3305_state *state, | ||
194 | struct lgdt3305_reg *regs, int len) | ||
195 | { | ||
196 | int i, ret; | ||
197 | |||
198 | lg_reg("writing %d registers...\n", len); | ||
199 | |||
200 | for (i = 0; i < len - 1; i++) { | ||
201 | ret = lgdt3305_write_reg(state, regs[i].reg, regs[i].val); | ||
202 | if (lg_fail(ret)) | ||
203 | return ret; | ||
204 | } | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | /* ------------------------------------------------------------------------ */ | ||
209 | |||
210 | static int lgdt3305_soft_reset(struct lgdt3305_state *state) | ||
211 | { | ||
212 | int ret; | ||
213 | |||
214 | lg_dbg("\n"); | ||
215 | |||
216 | ret = lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_3, 0, 0); | ||
217 | if (lg_fail(ret)) | ||
218 | goto fail; | ||
219 | |||
220 | msleep(20); | ||
221 | ret = lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_3, 0, 1); | ||
222 | fail: | ||
223 | return ret; | ||
224 | } | ||
225 | |||
226 | static inline int lgdt3305_mpeg_mode(struct lgdt3305_state *state, | ||
227 | enum lgdt3305_mpeg_mode mode) | ||
228 | { | ||
229 | lg_dbg("(%d)\n", mode); | ||
230 | return lgdt3305_set_reg_bit(state, LGDT3305_TP_CTRL_1, 5, mode); | ||
231 | } | ||
232 | |||
233 | static int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state, | ||
234 | enum lgdt3305_tp_clock_edge edge, | ||
235 | enum lgdt3305_tp_valid_polarity valid) | ||
236 | { | ||
237 | u8 val; | ||
238 | int ret; | ||
239 | |||
240 | lg_dbg("edge = %d, valid = %d\n", edge, valid); | ||
241 | |||
242 | ret = lgdt3305_read_reg(state, LGDT3305_TP_CTRL_1, &val); | ||
243 | if (lg_fail(ret)) | ||
244 | goto fail; | ||
245 | |||
246 | val &= ~0x09; | ||
247 | |||
248 | if (edge) | ||
249 | val |= 0x08; | ||
250 | if (valid) | ||
251 | val |= 0x01; | ||
252 | |||
253 | ret = lgdt3305_write_reg(state, LGDT3305_TP_CTRL_1, val); | ||
254 | if (lg_fail(ret)) | ||
255 | goto fail; | ||
256 | |||
257 | ret = lgdt3305_soft_reset(state); | ||
258 | fail: | ||
259 | return ret; | ||
260 | } | ||
261 | |||
262 | static int lgdt3305_set_modulation(struct lgdt3305_state *state, | ||
263 | struct dvb_frontend_parameters *param) | ||
264 | { | ||
265 | u8 opermode; | ||
266 | int ret; | ||
267 | |||
268 | lg_dbg("\n"); | ||
269 | |||
270 | ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_1, &opermode); | ||
271 | if (lg_fail(ret)) | ||
272 | goto fail; | ||
273 | |||
274 | opermode &= ~0x03; | ||
275 | |||
276 | switch (param->u.vsb.modulation) { | ||
277 | case VSB_8: | ||
278 | opermode |= 0x03; | ||
279 | break; | ||
280 | case QAM_64: | ||
281 | opermode |= 0x00; | ||
282 | break; | ||
283 | case QAM_256: | ||
284 | opermode |= 0x01; | ||
285 | break; | ||
286 | default: | ||
287 | return -EINVAL; | ||
288 | } | ||
289 | ret = lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_1, opermode); | ||
290 | fail: | ||
291 | return ret; | ||
292 | } | ||
293 | |||
294 | static int lgdt3305_set_filter_extension(struct lgdt3305_state *state, | ||
295 | struct dvb_frontend_parameters *param) | ||
296 | { | ||
297 | int val; | ||
298 | |||
299 | switch (param->u.vsb.modulation) { | ||
300 | case VSB_8: | ||
301 | val = 0; | ||
302 | break; | ||
303 | case QAM_64: | ||
304 | case QAM_256: | ||
305 | val = 1; | ||
306 | break; | ||
307 | default: | ||
308 | return -EINVAL; | ||
309 | } | ||
310 | lg_dbg("val = %d\n", val); | ||
311 | |||
312 | return lgdt3305_set_reg_bit(state, 0x043f, 2, val); | ||
313 | } | ||
314 | |||
315 | /* ------------------------------------------------------------------------ */ | ||
316 | |||
317 | static int lgdt3305_passband_digital_agc(struct lgdt3305_state *state, | ||
318 | struct dvb_frontend_parameters *param) | ||
319 | { | ||
320 | u16 agc_ref; | ||
321 | |||
322 | switch (param->u.vsb.modulation) { | ||
323 | case VSB_8: | ||
324 | agc_ref = 0x32c4; | ||
325 | break; | ||
326 | case QAM_64: | ||
327 | agc_ref = 0x2a00; | ||
328 | break; | ||
329 | case QAM_256: | ||
330 | agc_ref = 0x2a80; | ||
331 | break; | ||
332 | default: | ||
333 | return -EINVAL; | ||
334 | } | ||
335 | |||
336 | lg_dbg("agc ref: 0x%04x\n", agc_ref); | ||
337 | |||
338 | lgdt3305_write_reg(state, LGDT3305_DGTL_AGC_REF_1, agc_ref >> 8); | ||
339 | lgdt3305_write_reg(state, LGDT3305_DGTL_AGC_REF_2, agc_ref & 0xff); | ||
340 | |||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | static int lgdt3305_rfagc_loop(struct lgdt3305_state *state, | ||
345 | struct dvb_frontend_parameters *param) | ||
346 | { | ||
347 | u16 ifbw, rfbw, agcdelay; | ||
348 | |||
349 | switch (param->u.vsb.modulation) { | ||
350 | case VSB_8: | ||
351 | agcdelay = 0x04c0; | ||
352 | rfbw = 0x8000; | ||
353 | ifbw = 0x8000; | ||
354 | break; | ||
355 | case QAM_64: | ||
356 | case QAM_256: | ||
357 | agcdelay = 0x046b; | ||
358 | rfbw = 0x8889; | ||
359 | ifbw = 0x8888; | ||
360 | break; | ||
361 | default: | ||
362 | return -EINVAL; | ||
363 | } | ||
364 | |||
365 | if (state->cfg->rf_agc_loop) { | ||
366 | lg_dbg("agcdelay: 0x%04x, rfbw: 0x%04x\n", agcdelay, rfbw); | ||
367 | |||
368 | /* rf agc loop filter bandwidth */ | ||
369 | lgdt3305_write_reg(state, LGDT3305_AGC_DELAY_PT_1, | ||
370 | agcdelay >> 8); | ||
371 | lgdt3305_write_reg(state, LGDT3305_AGC_DELAY_PT_2, | ||
372 | agcdelay & 0xff); | ||
373 | |||
374 | lgdt3305_write_reg(state, LGDT3305_RFAGC_LOOP_FLTR_BW_1, | ||
375 | rfbw >> 8); | ||
376 | lgdt3305_write_reg(state, LGDT3305_RFAGC_LOOP_FLTR_BW_2, | ||
377 | rfbw & 0xff); | ||
378 | } else { | ||
379 | lg_dbg("ifbw: 0x%04x\n", ifbw); | ||
380 | |||
381 | /* if agc loop filter bandwidth */ | ||
382 | lgdt3305_write_reg(state, LGDT3305_IFBW_1, ifbw >> 8); | ||
383 | lgdt3305_write_reg(state, LGDT3305_IFBW_2, ifbw & 0xff); | ||
384 | } | ||
385 | |||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | static int lgdt3305_agc_setup(struct lgdt3305_state *state, | ||
390 | struct dvb_frontend_parameters *param) | ||
391 | { | ||
392 | int lockdten, acqen; | ||
393 | |||
394 | switch (param->u.vsb.modulation) { | ||
395 | case VSB_8: | ||
396 | lockdten = 0; | ||
397 | acqen = 0; | ||
398 | break; | ||
399 | case QAM_64: | ||
400 | case QAM_256: | ||
401 | lockdten = 1; | ||
402 | acqen = 1; | ||
403 | break; | ||
404 | default: | ||
405 | return -EINVAL; | ||
406 | } | ||
407 | |||
408 | lg_dbg("lockdten = %d, acqen = %d\n", lockdten, acqen); | ||
409 | |||
410 | /* control agc function */ | ||
411 | lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1); | ||
412 | lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen); | ||
413 | |||
414 | return lgdt3305_rfagc_loop(state, param); | ||
415 | } | ||
416 | |||
417 | static int lgdt3305_set_agc_power_ref(struct lgdt3305_state *state, | ||
418 | struct dvb_frontend_parameters *param) | ||
419 | { | ||
420 | u16 usref = 0; | ||
421 | |||
422 | switch (param->u.vsb.modulation) { | ||
423 | case VSB_8: | ||
424 | if (state->cfg->usref_8vsb) | ||
425 | usref = state->cfg->usref_8vsb; | ||
426 | break; | ||
427 | case QAM_64: | ||
428 | if (state->cfg->usref_qam64) | ||
429 | usref = state->cfg->usref_qam64; | ||
430 | break; | ||
431 | case QAM_256: | ||
432 | if (state->cfg->usref_qam256) | ||
433 | usref = state->cfg->usref_qam256; | ||
434 | break; | ||
435 | default: | ||
436 | return -EINVAL; | ||
437 | } | ||
438 | |||
439 | if (usref) { | ||
440 | lg_dbg("set manual mode: 0x%04x\n", usref); | ||
441 | |||
442 | lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 3, 1); | ||
443 | |||
444 | lgdt3305_write_reg(state, LGDT3305_AGC_POWER_REF_1, | ||
445 | 0xff & (usref >> 8)); | ||
446 | lgdt3305_write_reg(state, LGDT3305_AGC_POWER_REF_2, | ||
447 | 0xff & (usref >> 0)); | ||
448 | } | ||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | /* ------------------------------------------------------------------------ */ | ||
453 | |||
454 | static int lgdt3305_spectral_inversion(struct lgdt3305_state *state, | ||
455 | struct dvb_frontend_parameters *param, | ||
456 | int inversion) | ||
457 | { | ||
458 | int ret; | ||
459 | |||
460 | lg_dbg("(%d)\n", inversion); | ||
461 | |||
462 | switch (param->u.vsb.modulation) { | ||
463 | case VSB_8: | ||
464 | ret = lgdt3305_write_reg(state, LGDT3305_CR_CTRL_7, | ||
465 | inversion ? 0xf9 : 0x79); | ||
466 | break; | ||
467 | case QAM_64: | ||
468 | case QAM_256: | ||
469 | ret = lgdt3305_write_reg(state, LGDT3305_FEC_BLOCK_CTRL, | ||
470 | inversion ? 0xfd : 0xff); | ||
471 | break; | ||
472 | default: | ||
473 | ret = -EINVAL; | ||
474 | } | ||
475 | return ret; | ||
476 | } | ||
477 | |||
478 | static int lgdt3305_set_if(struct lgdt3305_state *state, | ||
479 | struct dvb_frontend_parameters *param) | ||
480 | { | ||
481 | u16 if_freq_khz; | ||
482 | u8 nco1, nco2, nco3, nco4; | ||
483 | u64 nco; | ||
484 | |||
485 | switch (param->u.vsb.modulation) { | ||
486 | case VSB_8: | ||
487 | if_freq_khz = state->cfg->vsb_if_khz; | ||
488 | break; | ||
489 | case QAM_64: | ||
490 | case QAM_256: | ||
491 | if_freq_khz = state->cfg->qam_if_khz; | ||
492 | break; | ||
493 | default: | ||
494 | return -EINVAL; | ||
495 | } | ||
496 | |||
497 | nco = if_freq_khz / 10; | ||
498 | |||
499 | #define LGDT3305_64BIT_DIVISION_ENABLED 0 | ||
500 | /* FIXME: 64bit division disabled to avoid linking error: | ||
501 | * WARNING: "__udivdi3" [lgdt3305.ko] undefined! | ||
502 | */ | ||
503 | switch (param->u.vsb.modulation) { | ||
504 | case VSB_8: | ||
505 | #if LGDT3305_64BIT_DIVISION_ENABLED | ||
506 | nco <<= 24; | ||
507 | nco /= 625; | ||
508 | #else | ||
509 | nco *= ((1 << 24) / 625); | ||
510 | #endif | ||
511 | break; | ||
512 | case QAM_64: | ||
513 | case QAM_256: | ||
514 | #if LGDT3305_64BIT_DIVISION_ENABLED | ||
515 | nco <<= 28; | ||
516 | nco /= 625; | ||
517 | #else | ||
518 | nco *= ((1 << 28) / 625); | ||
519 | #endif | ||
520 | break; | ||
521 | default: | ||
522 | return -EINVAL; | ||
523 | } | ||
524 | |||
525 | nco1 = (nco >> 24) & 0x3f; | ||
526 | nco1 |= 0x40; | ||
527 | nco2 = (nco >> 16) & 0xff; | ||
528 | nco3 = (nco >> 8) & 0xff; | ||
529 | nco4 = nco & 0xff; | ||
530 | |||
531 | lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, nco1); | ||
532 | lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, nco2); | ||
533 | lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, nco3); | ||
534 | lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, nco4); | ||
535 | |||
536 | lg_dbg("%d KHz -> [%02x%02x%02x%02x]\n", | ||
537 | if_freq_khz, nco1, nco2, nco3, nco4); | ||
538 | |||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | /* ------------------------------------------------------------------------ */ | ||
543 | |||
544 | static int lgdt3305_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) | ||
545 | { | ||
546 | struct lgdt3305_state *state = fe->demodulator_priv; | ||
547 | |||
548 | if (state->cfg->deny_i2c_rptr) | ||
549 | return 0; | ||
550 | |||
551 | lg_dbg("(%d)\n", enable); | ||
552 | |||
553 | return lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_2, 5, | ||
554 | enable ? 0 : 1); | ||
555 | } | ||
556 | |||
557 | static int lgdt3305_sleep(struct dvb_frontend *fe) | ||
558 | { | ||
559 | struct lgdt3305_state *state = fe->demodulator_priv; | ||
560 | u8 gen_ctrl_3, gen_ctrl_4; | ||
561 | |||
562 | lg_dbg("\n"); | ||
563 | |||
564 | gen_ctrl_3 = read_reg(state, LGDT3305_GEN_CTRL_3); | ||
565 | gen_ctrl_4 = read_reg(state, LGDT3305_GEN_CTRL_4); | ||
566 | |||
567 | /* hold in software reset while sleeping */ | ||
568 | gen_ctrl_3 &= ~0x01; | ||
569 | /* tristate the IF-AGC pin */ | ||
570 | gen_ctrl_3 |= 0x02; | ||
571 | /* tristate the RF-AGC pin */ | ||
572 | gen_ctrl_3 |= 0x04; | ||
573 | |||
574 | /* disable vsb/qam module */ | ||
575 | gen_ctrl_4 &= ~0x01; | ||
576 | /* disable adc module */ | ||
577 | gen_ctrl_4 &= ~0x02; | ||
578 | |||
579 | lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_3, gen_ctrl_3); | ||
580 | lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_4, gen_ctrl_4); | ||
581 | |||
582 | return 0; | ||
583 | } | ||
584 | |||
585 | static int lgdt3305_init(struct dvb_frontend *fe) | ||
586 | { | ||
587 | struct lgdt3305_state *state = fe->demodulator_priv; | ||
588 | int ret; | ||
589 | |||
590 | static struct lgdt3305_reg lgdt3305_init_data[] = { | ||
591 | { .reg = LGDT3305_GEN_CTRL_1, | ||
592 | .val = 0x03, }, | ||
593 | { .reg = LGDT3305_GEN_CTRL_2, | ||
594 | .val = 0xb0, }, | ||
595 | { .reg = LGDT3305_GEN_CTRL_3, | ||
596 | .val = 0x01, }, | ||
597 | { .reg = LGDT3305_GEN_CONTROL, | ||
598 | .val = 0x6f, }, | ||
599 | { .reg = LGDT3305_GEN_CTRL_4, | ||
600 | .val = 0x03, }, | ||
601 | { .reg = LGDT3305_DGTL_AGC_REF_1, | ||
602 | .val = 0x32, }, | ||
603 | { .reg = LGDT3305_DGTL_AGC_REF_2, | ||
604 | .val = 0xc4, }, | ||
605 | { .reg = LGDT3305_CR_CTR_FREQ_1, | ||
606 | .val = 0x00, }, | ||
607 | { .reg = LGDT3305_CR_CTR_FREQ_2, | ||
608 | .val = 0x00, }, | ||
609 | { .reg = LGDT3305_CR_CTR_FREQ_3, | ||
610 | .val = 0x00, }, | ||
611 | { .reg = LGDT3305_CR_CTR_FREQ_4, | ||
612 | .val = 0x00, }, | ||
613 | { .reg = LGDT3305_CR_CTRL_7, | ||
614 | .val = 0x79, }, | ||
615 | { .reg = LGDT3305_AGC_POWER_REF_1, | ||
616 | .val = 0x32, }, | ||
617 | { .reg = LGDT3305_AGC_POWER_REF_2, | ||
618 | .val = 0xc4, }, | ||
619 | { .reg = LGDT3305_AGC_DELAY_PT_1, | ||
620 | .val = 0x0d, }, | ||
621 | { .reg = LGDT3305_AGC_DELAY_PT_2, | ||
622 | .val = 0x30, }, | ||
623 | { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_1, | ||
624 | .val = 0x80, }, | ||
625 | { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_2, | ||
626 | .val = 0x00, }, | ||
627 | { .reg = LGDT3305_IFBW_1, | ||
628 | .val = 0x80, }, | ||
629 | { .reg = LGDT3305_IFBW_2, | ||
630 | .val = 0x00, }, | ||
631 | { .reg = LGDT3305_AGC_CTRL_1, | ||
632 | .val = 0x30, }, | ||
633 | { .reg = LGDT3305_AGC_CTRL_4, | ||
634 | .val = 0x61, }, | ||
635 | { .reg = LGDT3305_FEC_BLOCK_CTRL, | ||
636 | .val = 0xff, }, | ||
637 | { .reg = LGDT3305_TP_CTRL_1, | ||
638 | .val = 0x1b, }, | ||
639 | }; | ||
640 | |||
641 | lg_dbg("\n"); | ||
642 | |||
643 | ret = lgdt3305_write_regs(state, lgdt3305_init_data, | ||
644 | ARRAY_SIZE(lgdt3305_init_data)); | ||
645 | if (lg_fail(ret)) | ||
646 | goto fail; | ||
647 | |||
648 | ret = lgdt3305_soft_reset(state); | ||
649 | fail: | ||
650 | return ret; | ||
651 | } | ||
652 | |||
653 | static int lgdt3305_set_parameters(struct dvb_frontend *fe, | ||
654 | struct dvb_frontend_parameters *param) | ||
655 | { | ||
656 | struct lgdt3305_state *state = fe->demodulator_priv; | ||
657 | int ret; | ||
658 | |||
659 | lg_dbg("(%d, %d)\n", param->frequency, param->u.vsb.modulation); | ||
660 | |||
661 | if (fe->ops.tuner_ops.set_params) { | ||
662 | ret = fe->ops.tuner_ops.set_params(fe, param); | ||
663 | if (fe->ops.i2c_gate_ctrl) | ||
664 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
665 | if (lg_fail(ret)) | ||
666 | goto fail; | ||
667 | state->current_frequency = param->frequency; | ||
668 | } | ||
669 | |||
670 | ret = lgdt3305_set_modulation(state, param); | ||
671 | if (lg_fail(ret)) | ||
672 | goto fail; | ||
673 | |||
674 | ret = lgdt3305_passband_digital_agc(state, param); | ||
675 | if (lg_fail(ret)) | ||
676 | goto fail; | ||
677 | ret = lgdt3305_set_agc_power_ref(state, param); | ||
678 | if (lg_fail(ret)) | ||
679 | goto fail; | ||
680 | ret = lgdt3305_agc_setup(state, param); | ||
681 | if (lg_fail(ret)) | ||
682 | goto fail; | ||
683 | |||
684 | /* low if */ | ||
685 | ret = lgdt3305_write_reg(state, LGDT3305_GEN_CONTROL, 0x2f); | ||
686 | if (lg_fail(ret)) | ||
687 | goto fail; | ||
688 | ret = lgdt3305_set_reg_bit(state, LGDT3305_CR_CTR_FREQ_1, 6, 1); | ||
689 | if (lg_fail(ret)) | ||
690 | goto fail; | ||
691 | |||
692 | ret = lgdt3305_set_if(state, param); | ||
693 | if (lg_fail(ret)) | ||
694 | goto fail; | ||
695 | ret = lgdt3305_spectral_inversion(state, param, | ||
696 | state->cfg->spectral_inversion | ||
697 | ? 1 : 0); | ||
698 | if (lg_fail(ret)) | ||
699 | goto fail; | ||
700 | |||
701 | ret = lgdt3305_set_filter_extension(state, param); | ||
702 | if (lg_fail(ret)) | ||
703 | goto fail; | ||
704 | |||
705 | state->current_modulation = param->u.vsb.modulation; | ||
706 | |||
707 | ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode); | ||
708 | if (lg_fail(ret)) | ||
709 | goto fail; | ||
710 | |||
711 | /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ | ||
712 | ret = lgdt3305_mpeg_mode_polarity(state, | ||
713 | state->cfg->tpclk_edge, | ||
714 | state->cfg->tpvalid_polarity); | ||
715 | fail: | ||
716 | return ret; | ||
717 | } | ||
718 | |||
719 | static int lgdt3305_get_frontend(struct dvb_frontend *fe, | ||
720 | struct dvb_frontend_parameters *param) | ||
721 | { | ||
722 | struct lgdt3305_state *state = fe->demodulator_priv; | ||
723 | |||
724 | lg_dbg("\n"); | ||
725 | |||
726 | param->u.vsb.modulation = state->current_modulation; | ||
727 | param->frequency = state->current_frequency; | ||
728 | return 0; | ||
729 | } | ||
730 | |||
731 | /* ------------------------------------------------------------------------ */ | ||
732 | |||
733 | static int lgdt3305_read_cr_lock_status(struct lgdt3305_state *state, | ||
734 | int *locked) | ||
735 | { | ||
736 | u8 val; | ||
737 | int ret; | ||
738 | char *cr_lock_state = ""; | ||
739 | |||
740 | *locked = 0; | ||
741 | |||
742 | ret = lgdt3305_read_reg(state, LGDT3305_CR_LOCK_STATUS, &val); | ||
743 | if (lg_fail(ret)) | ||
744 | goto fail; | ||
745 | |||
746 | switch (state->current_modulation) { | ||
747 | case QAM_256: | ||
748 | case QAM_64: | ||
749 | if (val & (1 << 1)) | ||
750 | *locked = 1; | ||
751 | |||
752 | switch (val & 0x07) { | ||
753 | case 0: | ||
754 | cr_lock_state = "QAM UNLOCK"; | ||
755 | break; | ||
756 | case 4: | ||
757 | cr_lock_state = "QAM 1stLock"; | ||
758 | break; | ||
759 | case 6: | ||
760 | cr_lock_state = "QAM 2ndLock"; | ||
761 | break; | ||
762 | case 7: | ||
763 | cr_lock_state = "QAM FinalLock"; | ||
764 | break; | ||
765 | default: | ||
766 | cr_lock_state = "CLOCKQAM-INVALID!"; | ||
767 | break; | ||
768 | } | ||
769 | break; | ||
770 | case VSB_8: | ||
771 | if (val & (1 << 7)) { | ||
772 | *locked = 1; | ||
773 | cr_lock_state = "CLOCKVSB"; | ||
774 | } | ||
775 | break; | ||
776 | default: | ||
777 | ret = -EINVAL; | ||
778 | } | ||
779 | lg_dbg("(%d) %s\n", *locked, cr_lock_state); | ||
780 | fail: | ||
781 | return ret; | ||
782 | } | ||
783 | |||
784 | static int lgdt3305_read_fec_lock_status(struct lgdt3305_state *state, | ||
785 | int *locked) | ||
786 | { | ||
787 | u8 val; | ||
788 | int ret, mpeg_lock, fec_lock, viterbi_lock; | ||
789 | |||
790 | *locked = 0; | ||
791 | |||
792 | switch (state->current_modulation) { | ||
793 | case QAM_256: | ||
794 | case QAM_64: | ||
795 | ret = lgdt3305_read_reg(state, | ||
796 | LGDT3305_FEC_LOCK_STATUS, &val); | ||
797 | if (lg_fail(ret)) | ||
798 | goto fail; | ||
799 | |||
800 | mpeg_lock = (val & (1 << 0)) ? 1 : 0; | ||
801 | fec_lock = (val & (1 << 2)) ? 1 : 0; | ||
802 | viterbi_lock = (val & (1 << 3)) ? 1 : 0; | ||
803 | |||
804 | *locked = mpeg_lock && fec_lock && viterbi_lock; | ||
805 | |||
806 | lg_dbg("(%d) %s%s%s\n", *locked, | ||
807 | mpeg_lock ? "mpeg lock " : "", | ||
808 | fec_lock ? "fec lock " : "", | ||
809 | viterbi_lock ? "viterbi lock" : ""); | ||
810 | break; | ||
811 | case VSB_8: | ||
812 | default: | ||
813 | ret = -EINVAL; | ||
814 | } | ||
815 | fail: | ||
816 | return ret; | ||
817 | } | ||
818 | |||
819 | static int lgdt3305_read_status(struct dvb_frontend *fe, fe_status_t *status) | ||
820 | { | ||
821 | struct lgdt3305_state *state = fe->demodulator_priv; | ||
822 | u8 val; | ||
823 | int ret, signal, inlock, nofecerr, snrgood, | ||
824 | cr_lock, fec_lock, sync_lock; | ||
825 | |||
826 | *status = 0; | ||
827 | |||
828 | ret = lgdt3305_read_reg(state, LGDT3305_GEN_STATUS, &val); | ||
829 | if (lg_fail(ret)) | ||
830 | goto fail; | ||
831 | |||
832 | signal = (val & (1 << 4)) ? 1 : 0; | ||
833 | inlock = (val & (1 << 3)) ? 0 : 1; | ||
834 | sync_lock = (val & (1 << 2)) ? 1 : 0; | ||
835 | nofecerr = (val & (1 << 1)) ? 1 : 0; | ||
836 | snrgood = (val & (1 << 0)) ? 1 : 0; | ||
837 | |||
838 | lg_dbg("%s%s%s%s%s\n", | ||
839 | signal ? "SIGNALEXIST " : "", | ||
840 | inlock ? "INLOCK " : "", | ||
841 | sync_lock ? "SYNCLOCK " : "", | ||
842 | nofecerr ? "NOFECERR " : "", | ||
843 | snrgood ? "SNRGOOD " : ""); | ||
844 | |||
845 | ret = lgdt3305_read_cr_lock_status(state, &cr_lock); | ||
846 | if (lg_fail(ret)) | ||
847 | goto fail; | ||
848 | |||
849 | if (signal) | ||
850 | *status |= FE_HAS_SIGNAL; | ||
851 | if (cr_lock) | ||
852 | *status |= FE_HAS_CARRIER; | ||
853 | if (nofecerr) | ||
854 | *status |= FE_HAS_VITERBI; | ||
855 | if (sync_lock) | ||
856 | *status |= FE_HAS_SYNC; | ||
857 | |||
858 | switch (state->current_modulation) { | ||
859 | case QAM_256: | ||
860 | case QAM_64: | ||
861 | ret = lgdt3305_read_fec_lock_status(state, &fec_lock); | ||
862 | if (lg_fail(ret)) | ||
863 | goto fail; | ||
864 | |||
865 | if (fec_lock) | ||
866 | *status |= FE_HAS_LOCK; | ||
867 | break; | ||
868 | case VSB_8: | ||
869 | if (inlock) | ||
870 | *status |= FE_HAS_LOCK; | ||
871 | break; | ||
872 | default: | ||
873 | ret = -EINVAL; | ||
874 | } | ||
875 | fail: | ||
876 | return ret; | ||
877 | } | ||
878 | |||
879 | /* ------------------------------------------------------------------------ */ | ||
880 | |||
881 | /* borrowed from lgdt330x.c */ | ||
882 | static u32 calculate_snr(u32 mse, u32 c) | ||
883 | { | ||
884 | if (mse == 0) /* no signal */ | ||
885 | return 0; | ||
886 | |||
887 | mse = intlog10(mse); | ||
888 | if (mse > c) { | ||
889 | /* Negative SNR, which is possible, but realisticly the | ||
890 | demod will lose lock before the signal gets this bad. The | ||
891 | API only allows for unsigned values, so just return 0 */ | ||
892 | return 0; | ||
893 | } | ||
894 | return 10*(c - mse); | ||
895 | } | ||
896 | |||
897 | static int lgdt3305_read_snr(struct dvb_frontend *fe, u16 *snr) | ||
898 | { | ||
899 | struct lgdt3305_state *state = fe->demodulator_priv; | ||
900 | u32 noise; /* noise value */ | ||
901 | u32 c; /* per-modulation SNR calculation constant */ | ||
902 | |||
903 | switch (state->current_modulation) { | ||
904 | case VSB_8: | ||
905 | #ifdef USE_PTMSE | ||
906 | /* Use Phase Tracker Mean-Square Error Register */ | ||
907 | /* SNR for ranges from -13.11 to +44.08 */ | ||
908 | noise = ((read_reg(state, LGDT3305_PT_MSE_1) & 0x07) << 16) | | ||
909 | (read_reg(state, LGDT3305_PT_MSE_2) << 8) | | ||
910 | (read_reg(state, LGDT3305_PT_MSE_3) & 0xff); | ||
911 | c = 73957994; /* log10(25*32^2)*2^24 */ | ||
912 | #else | ||
913 | /* Use Equalizer Mean-Square Error Register */ | ||
914 | /* SNR for ranges from -16.12 to +44.08 */ | ||
915 | noise = ((read_reg(state, LGDT3305_EQ_MSE_1) & 0x0f) << 16) | | ||
916 | (read_reg(state, LGDT3305_EQ_MSE_2) << 8) | | ||
917 | (read_reg(state, LGDT3305_EQ_MSE_3) & 0xff); | ||
918 | c = 73957994; /* log10(25*32^2)*2^24 */ | ||
919 | #endif | ||
920 | break; | ||
921 | case QAM_64: | ||
922 | case QAM_256: | ||
923 | noise = (read_reg(state, LGDT3305_CR_MSE_1) << 8) | | ||
924 | (read_reg(state, LGDT3305_CR_MSE_2) & 0xff); | ||
925 | |||
926 | c = (state->current_modulation == QAM_64) ? | ||
927 | 97939837 : 98026066; | ||
928 | /* log10(688128)*2^24 and log10(696320)*2^24 */ | ||
929 | break; | ||
930 | default: | ||
931 | return -EINVAL; | ||
932 | } | ||
933 | state->snr = calculate_snr(noise, c); | ||
934 | /*report SNR in dB * 10 */ | ||
935 | *snr = (state->snr / ((1 << 24) / 10)); | ||
936 | lg_dbg("noise = 0x%08x, snr = %d.%02d dB\n", noise, | ||
937 | state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); | ||
938 | |||
939 | return 0; | ||
940 | } | ||
941 | |||
942 | static int lgdt3305_read_signal_strength(struct dvb_frontend *fe, | ||
943 | u16 *strength) | ||
944 | { | ||
945 | /* borrowed from lgdt330x.c | ||
946 | * | ||
947 | * Calculate strength from SNR up to 35dB | ||
948 | * Even though the SNR can go higher than 35dB, | ||
949 | * there is some comfort factor in having a range of | ||
950 | * strong signals that can show at 100% | ||
951 | */ | ||
952 | struct lgdt3305_state *state = fe->demodulator_priv; | ||
953 | u16 snr; | ||
954 | int ret; | ||
955 | |||
956 | *strength = 0; | ||
957 | |||
958 | ret = fe->ops.read_snr(fe, &snr); | ||
959 | if (lg_fail(ret)) | ||
960 | goto fail; | ||
961 | /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ | ||
962 | /* scale the range 0 - 35*2^24 into 0 - 65535 */ | ||
963 | if (state->snr >= 8960 * 0x10000) | ||
964 | *strength = 0xffff; | ||
965 | else | ||
966 | *strength = state->snr / 8960; | ||
967 | fail: | ||
968 | return ret; | ||
969 | } | ||
970 | |||
971 | /* ------------------------------------------------------------------------ */ | ||
972 | |||
973 | static int lgdt3305_read_ber(struct dvb_frontend *fe, u32 *ber) | ||
974 | { | ||
975 | *ber = 0; | ||
976 | return 0; | ||
977 | } | ||
978 | |||
979 | static int lgdt3305_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) | ||
980 | { | ||
981 | struct lgdt3305_state *state = fe->demodulator_priv; | ||
982 | |||
983 | *ucblocks = | ||
984 | (read_reg(state, LGDT3305_FEC_PKT_ERR_1) << 8) | | ||
985 | (read_reg(state, LGDT3305_FEC_PKT_ERR_2) & 0xff); | ||
986 | |||
987 | return 0; | ||
988 | } | ||
989 | |||
990 | static int lgdt3305_get_tune_settings(struct dvb_frontend *fe, | ||
991 | struct dvb_frontend_tune_settings | ||
992 | *fe_tune_settings) | ||
993 | { | ||
994 | fe_tune_settings->min_delay_ms = 500; | ||
995 | lg_dbg("\n"); | ||
996 | return 0; | ||
997 | } | ||
998 | |||
999 | static void lgdt3305_release(struct dvb_frontend *fe) | ||
1000 | { | ||
1001 | struct lgdt3305_state *state = fe->demodulator_priv; | ||
1002 | lg_dbg("\n"); | ||
1003 | kfree(state); | ||
1004 | } | ||
1005 | |||
1006 | static struct dvb_frontend_ops lgdt3305_ops; | ||
1007 | |||
1008 | struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, | ||
1009 | struct i2c_adapter *i2c_adap) | ||
1010 | { | ||
1011 | struct lgdt3305_state *state = NULL; | ||
1012 | int ret; | ||
1013 | u8 val; | ||
1014 | |||
1015 | lg_dbg("(%d-%04x)\n", | ||
1016 | i2c_adap ? i2c_adapter_id(i2c_adap) : 0, | ||
1017 | config ? config->i2c_addr : 0); | ||
1018 | |||
1019 | state = kzalloc(sizeof(struct lgdt3305_state), GFP_KERNEL); | ||
1020 | if (state == NULL) | ||
1021 | goto fail; | ||
1022 | |||
1023 | state->cfg = config; | ||
1024 | state->i2c_adap = i2c_adap; | ||
1025 | |||
1026 | memcpy(&state->frontend.ops, &lgdt3305_ops, | ||
1027 | sizeof(struct dvb_frontend_ops)); | ||
1028 | state->frontend.demodulator_priv = state; | ||
1029 | |||
1030 | /* verify that we're talking to a lg dt3305 */ | ||
1031 | ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_2, &val); | ||
1032 | if ((lg_fail(ret)) | (val == 0)) | ||
1033 | goto fail; | ||
1034 | ret = lgdt3305_write_reg(state, 0x0808, 0x80); | ||
1035 | if (lg_fail(ret)) | ||
1036 | goto fail; | ||
1037 | ret = lgdt3305_read_reg(state, 0x0808, &val); | ||
1038 | if ((lg_fail(ret)) | (val != 0x80)) | ||
1039 | goto fail; | ||
1040 | ret = lgdt3305_write_reg(state, 0x0808, 0x00); | ||
1041 | if (lg_fail(ret)) | ||
1042 | goto fail; | ||
1043 | |||
1044 | state->current_frequency = -1; | ||
1045 | state->current_modulation = -1; | ||
1046 | |||
1047 | return &state->frontend; | ||
1048 | fail: | ||
1049 | lg_warn("unable to detect LGDT3305 hardware\n"); | ||
1050 | state->frontend.demodulator_priv = NULL; | ||
1051 | kfree(state); | ||
1052 | return NULL; | ||
1053 | } | ||
1054 | EXPORT_SYMBOL(lgdt3305_attach); | ||
1055 | |||
1056 | static struct dvb_frontend_ops lgdt3305_ops = { | ||
1057 | .info = { | ||
1058 | .name = "LG Electronics LGDT3305 VSB/QAM Frontend", | ||
1059 | .type = FE_ATSC, | ||
1060 | .frequency_min = 54000000, | ||
1061 | .frequency_max = 858000000, | ||
1062 | .frequency_stepsize = 62500, | ||
1063 | .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB | ||
1064 | }, | ||
1065 | .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, | ||
1066 | .init = lgdt3305_init, | ||
1067 | .sleep = lgdt3305_sleep, | ||
1068 | .set_frontend = lgdt3305_set_parameters, | ||
1069 | .get_frontend = lgdt3305_get_frontend, | ||
1070 | .get_tune_settings = lgdt3305_get_tune_settings, | ||
1071 | .read_status = lgdt3305_read_status, | ||
1072 | .read_ber = lgdt3305_read_ber, | ||
1073 | .read_signal_strength = lgdt3305_read_signal_strength, | ||
1074 | .read_snr = lgdt3305_read_snr, | ||
1075 | .read_ucblocks = lgdt3305_read_ucblocks, | ||
1076 | .release = lgdt3305_release, | ||
1077 | }; | ||
1078 | |||
1079 | MODULE_DESCRIPTION("LG Electronics LGDT3305 ATSC/QAM-B Demodulator Driver"); | ||
1080 | MODULE_AUTHOR("Michael Krufky"); | ||
1081 | MODULE_LICENSE("GPL"); | ||
1082 | |||
1083 | /* | ||
1084 | * Local variables: | ||
1085 | * c-basic-offset: 8 | ||
1086 | * End: | ||
1087 | */ | ||
diff --git a/drivers/media/dvb/frontends/lgdt3305.h b/drivers/media/dvb/frontends/lgdt3305.h new file mode 100644 index 000000000000..4fa6e52d1fe8 --- /dev/null +++ b/drivers/media/dvb/frontends/lgdt3305.h | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
2 | * Support for LGDT3305 - VSB/QAM | ||
3 | * | ||
4 | * Copyright (C) 2008, 2009 Michael Krufky <mkrufky@linuxtv.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #ifndef _LGDT3305_H_ | ||
23 | #define _LGDT3305_H_ | ||
24 | |||
25 | #include <linux/i2c.h> | ||
26 | #include "dvb_frontend.h" | ||
27 | |||
28 | |||
29 | enum lgdt3305_mpeg_mode { | ||
30 | LGDT3305_MPEG_PARALLEL = 0, | ||
31 | LGDT3305_MPEG_SERIAL = 1, | ||
32 | }; | ||
33 | |||
34 | enum lgdt3305_tp_clock_edge { | ||
35 | LGDT3305_TPCLK_RISING_EDGE = 0, | ||
36 | LGDT3305_TPCLK_FALLING_EDGE = 1, | ||
37 | }; | ||
38 | |||
39 | enum lgdt3305_tp_valid_polarity { | ||
40 | LGDT3305_TP_VALID_LOW = 0, | ||
41 | LGDT3305_TP_VALID_HIGH = 1, | ||
42 | }; | ||
43 | |||
44 | struct lgdt3305_config { | ||
45 | u8 i2c_addr; | ||
46 | |||
47 | /* user defined IF frequency in KHz */ | ||
48 | u16 qam_if_khz; | ||
49 | u16 vsb_if_khz; | ||
50 | |||
51 | /* AGC Power reference - defaults are used if left unset */ | ||
52 | u16 usref_8vsb; /* default: 0x32c4 */ | ||
53 | u16 usref_qam64; /* default: 0x5400 */ | ||
54 | u16 usref_qam256; /* default: 0x2a80 */ | ||
55 | |||
56 | /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */ | ||
57 | int deny_i2c_rptr:1; | ||
58 | |||
59 | /* spectral inversion - 0:disabled 1:enabled */ | ||
60 | int spectral_inversion:1; | ||
61 | |||
62 | /* use RF AGC loop - 0:disabled 1:enabled */ | ||
63 | int rf_agc_loop:1; | ||
64 | |||
65 | enum lgdt3305_mpeg_mode mpeg_mode; | ||
66 | enum lgdt3305_tp_clock_edge tpclk_edge; | ||
67 | enum lgdt3305_tp_valid_polarity tpvalid_polarity; | ||
68 | }; | ||
69 | |||
70 | #if defined(CONFIG_DVB_LGDT3305) || (defined(CONFIG_DVB_LGDT3305_MODULE) && \ | ||
71 | defined(MODULE)) | ||
72 | extern | ||
73 | struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, | ||
74 | struct i2c_adapter *i2c_adap); | ||
75 | #else | ||
76 | static inline | ||
77 | struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, | ||
78 | struct i2c_adapter *i2c_adap) | ||
79 | { | ||
80 | printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); | ||
81 | return NULL; | ||
82 | } | ||
83 | #endif /* CONFIG_DVB_LGDT3305 */ | ||
84 | |||
85 | #endif /* _LGDT3305_H_ */ | ||