diff options
author | David T.L. Wong <davidtlwong@gmail.com> | 2009-10-26 06:08:17 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-12-05 15:41:34 -0500 |
commit | ec27b6aafb1796a09aad401143f70ad079421ca4 (patch) | |
tree | 3393ec54fb8bed465e3fa550cf603aa7e15dda01 | |
parent | 95963cbcd0ee00026cc34ba84906ee4aa2029f4d (diff) |
V4L/DVB (13374): AltoBeam ATBM8830 GB20600-2006(DMB-TH) demodulator
Adds support for Maxim MAX2165 silicon tuner.
It was tested on Mygica X8558Pro, which has MAX2165, ATBM8830 and CX23885.
Signed-off-by: David T. L. Wong <davidtlwong@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/dvb/frontends/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/atbm8830.c | 495 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/atbm8830.h | 76 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/atbm8830_priv.h | 75 |
5 files changed, 654 insertions, 0 deletions
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 4f785f549d02..fdc7926cb2db 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig | |||
@@ -564,6 +564,13 @@ config DVB_LGS8GXX | |||
564 | help | 564 | help |
565 | A DMB-TH tuner module. Say Y when you want to support this frontend. | 565 | A DMB-TH tuner module. Say Y when you want to support this frontend. |
566 | 566 | ||
567 | config DVB_ATBM8830 | ||
568 | tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator" | ||
569 | depends on DVB_CORE && I2C | ||
570 | default m if DVB_FE_CUSTOMISE | ||
571 | help | ||
572 | A DMB-TH tuner module. Say Y when you want to support this frontend. | ||
573 | |||
567 | comment "Tools to develop new frontends" | 574 | comment "Tools to develop new frontends" |
568 | 575 | ||
569 | config DVB_DUMMY_FE | 576 | config DVB_DUMMY_FE |
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 1ca758eeff59..390eec6d5d84 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile | |||
@@ -64,6 +64,7 @@ obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o | |||
64 | obj-$(CONFIG_DVB_S5H1411) += s5h1411.o | 64 | obj-$(CONFIG_DVB_S5H1411) += s5h1411.o |
65 | obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o | 65 | obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o |
66 | obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o | 66 | obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o |
67 | obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o | ||
67 | obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o | 68 | obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o |
68 | obj-$(CONFIG_DVB_AF9013) += af9013.o | 69 | obj-$(CONFIG_DVB_AF9013) += af9013.o |
69 | obj-$(CONFIG_DVB_CX24116) += cx24116.o | 70 | obj-$(CONFIG_DVB_CX24116) += cx24116.o |
diff --git a/drivers/media/dvb/frontends/atbm8830.c b/drivers/media/dvb/frontends/atbm8830.c new file mode 100644 index 000000000000..87fb3c23b80f --- /dev/null +++ b/drivers/media/dvb/frontends/atbm8830.c | |||
@@ -0,0 +1,495 @@ | |||
1 | /* | ||
2 | * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator | ||
3 | * ATBM8830, ATBM8831 | ||
4 | * | ||
5 | * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com> | ||
6 | * | ||
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. | ||
11 | * | ||
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. | ||
16 | * | ||
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. | ||
20 | */ | ||
21 | |||
22 | #include "dvb_frontend.h" | ||
23 | |||
24 | #include "atbm8830.h" | ||
25 | #include "atbm8830_priv.h" | ||
26 | |||
27 | #define dprintk(args...) \ | ||
28 | do { \ | ||
29 | if (debug) \ | ||
30 | printk(KERN_DEBUG "atbm8830: " args); \ | ||
31 | } while (0) | ||
32 | |||
33 | static int debug; | ||
34 | |||
35 | module_param(debug, int, 0644); | ||
36 | MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); | ||
37 | |||
38 | static int atbm8830_write_reg(struct atbm_state *priv, u16 reg, u8 data) | ||
39 | { | ||
40 | int ret = 0; | ||
41 | u8 dev_addr; | ||
42 | u8 buf1[] = { reg >> 8, reg & 0xFF }; | ||
43 | u8 buf2[] = { data }; | ||
44 | struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; | ||
45 | struct i2c_msg msg2 = { .flags = 0, .buf = buf2, .len = 1 }; | ||
46 | |||
47 | dev_addr = priv->config->demod_address; | ||
48 | msg1.addr = dev_addr; | ||
49 | msg2.addr = dev_addr; | ||
50 | |||
51 | if (debug >= 2) | ||
52 | printk(KERN_DEBUG "%s: reg=0x%04X, data=0x%02X\n", | ||
53 | __func__, reg, data); | ||
54 | |||
55 | ret = i2c_transfer(priv->i2c, &msg1, 1); | ||
56 | if (ret != 1) | ||
57 | return -EIO; | ||
58 | |||
59 | ret = i2c_transfer(priv->i2c, &msg2, 1); | ||
60 | return (ret != 1) ? -EIO : 0; | ||
61 | } | ||
62 | |||
63 | static int atbm8830_read_reg(struct atbm_state *priv, u16 reg, u8 *p_data) | ||
64 | { | ||
65 | int ret; | ||
66 | u8 dev_addr; | ||
67 | |||
68 | u8 buf1[] = { reg >> 8, reg & 0xFF }; | ||
69 | u8 buf2[] = { 0 }; | ||
70 | struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; | ||
71 | struct i2c_msg msg2 = { .flags = I2C_M_RD, .buf = buf2, .len = 1 }; | ||
72 | |||
73 | dev_addr = priv->config->demod_address; | ||
74 | msg1.addr = dev_addr; | ||
75 | msg2.addr = dev_addr; | ||
76 | |||
77 | ret = i2c_transfer(priv->i2c, &msg1, 1); | ||
78 | if (ret != 1) { | ||
79 | dprintk(KERN_DEBUG "%s: error reg=0x%04x, ret=%i\n", | ||
80 | __func__, reg, ret); | ||
81 | return -EIO; | ||
82 | } | ||
83 | |||
84 | ret = i2c_transfer(priv->i2c, &msg2, 1); | ||
85 | if (ret != 1) | ||
86 | return -EIO; | ||
87 | |||
88 | *p_data = buf2[0]; | ||
89 | if (debug >= 2) | ||
90 | printk(KERN_DEBUG "%s: reg=0x%04X, data=0x%02X\n", | ||
91 | __func__, reg, buf2[0]); | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | /* Lock register latch so that multi-register read is atomic */ | ||
97 | static inline int atbm8830_reglatch_lock(struct atbm_state *priv, int lock) | ||
98 | { | ||
99 | return atbm8830_write_reg(priv, REG_READ_LATCH, lock ? 1 : 0); | ||
100 | } | ||
101 | |||
102 | static int set_osc_freq(struct atbm_state *priv, u32 freq /*in kHz*/) | ||
103 | { | ||
104 | u32 val; | ||
105 | |||
106 | val = (u64)0x100000 * freq / 30400; | ||
107 | |||
108 | atbm8830_write_reg(priv, REG_OSC_CLK, val); | ||
109 | atbm8830_write_reg(priv, REG_OSC_CLK + 1, val >> 8); | ||
110 | atbm8830_write_reg(priv, REG_OSC_CLK + 2, val >> 16); | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static int set_if_freq(struct atbm_state *priv, u32 freq /*in kHz*/) | ||
116 | { | ||
117 | |||
118 | u32 fs = priv->config->osc_clk_freq; | ||
119 | double t; | ||
120 | u32 val; | ||
121 | u8 dat; | ||
122 | |||
123 | t = 2 * 3.141593 * (freq - fs) / fs * (1 << 22); | ||
124 | val = t; | ||
125 | |||
126 | if (freq != 0) { | ||
127 | atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 1); | ||
128 | atbm8830_write_reg(priv, REG_IF_FREQ, val); | ||
129 | atbm8830_write_reg(priv, REG_IF_FREQ+1, val >> 8); | ||
130 | atbm8830_write_reg(priv, REG_IF_FREQ+2, val >> 16); | ||
131 | |||
132 | atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); | ||
133 | dat &= 0xFC; | ||
134 | atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); | ||
135 | } else { | ||
136 | /* Zero IF */ | ||
137 | atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 0); | ||
138 | |||
139 | atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); | ||
140 | dat &= 0xFC; | ||
141 | dat |= 0x02; | ||
142 | atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); | ||
143 | |||
144 | if (priv->config->zif_swap_iq) | ||
145 | atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x03); | ||
146 | else | ||
147 | atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x01); | ||
148 | } | ||
149 | |||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static int is_locked(struct atbm_state *priv, u8 *locked) | ||
154 | { | ||
155 | u8 status; | ||
156 | |||
157 | atbm8830_read_reg(priv, REG_LOCK_STATUS, &status); | ||
158 | |||
159 | if (locked != NULL) | ||
160 | *locked = (status == 1); | ||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static int set_agc_config(struct atbm_state *priv, | ||
165 | u8 min, u8 max, u8 hold_loop) | ||
166 | { | ||
167 | atbm8830_write_reg(priv, REG_AGC_MIN, min); | ||
168 | atbm8830_write_reg(priv, REG_AGC_MAX, max); | ||
169 | atbm8830_write_reg(priv, REG_AGC_HOLD_LOOP, hold_loop); | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static int set_static_channel_mode(struct atbm_state *priv) | ||
175 | { | ||
176 | int i; | ||
177 | |||
178 | for (i = 0; i < 5; i++) | ||
179 | atbm8830_write_reg(priv, 0x099B + i, 0x08); | ||
180 | |||
181 | atbm8830_write_reg(priv, 0x095B, 0x7F); | ||
182 | atbm8830_write_reg(priv, 0x09CB, 0x01); | ||
183 | atbm8830_write_reg(priv, 0x09CC, 0x7F); | ||
184 | atbm8830_write_reg(priv, 0x09CD, 0x7F); | ||
185 | atbm8830_write_reg(priv, 0x0E01, 0x20); | ||
186 | |||
187 | /* For single carrier */ | ||
188 | atbm8830_write_reg(priv, 0x0B03, 0x0A); | ||
189 | atbm8830_write_reg(priv, 0x0935, 0x10); | ||
190 | atbm8830_write_reg(priv, 0x0936, 0x08); | ||
191 | atbm8830_write_reg(priv, 0x093E, 0x08); | ||
192 | atbm8830_write_reg(priv, 0x096E, 0x06); | ||
193 | |||
194 | /* frame_count_max0 */ | ||
195 | atbm8830_write_reg(priv, 0x0B09, 0x00); | ||
196 | /* frame_count_max1 */ | ||
197 | atbm8830_write_reg(priv, 0x0B0A, 0x08); | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int set_ts_config(struct atbm_state *priv) | ||
203 | { | ||
204 | const struct atbm8830_config *cfg = priv->config; | ||
205 | |||
206 | /*Set parallel/serial ts mode*/ | ||
207 | atbm8830_write_reg(priv, REG_TS_SERIAL, cfg->serial_ts ? 1 : 0); | ||
208 | atbm8830_write_reg(priv, REG_TS_CLK_MODE, cfg->serial_ts ? 1 : 0); | ||
209 | /*Set ts sampling edge*/ | ||
210 | atbm8830_write_reg(priv, REG_TS_SAMPLE_EDGE, | ||
211 | cfg->ts_sampling_edge ? 1 : 0); | ||
212 | /*Set ts clock freerun*/ | ||
213 | atbm8830_write_reg(priv, REG_TS_CLK_FREERUN, | ||
214 | cfg->ts_clk_gated ? 0 : 1); | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static int atbm8830_init(struct dvb_frontend *fe) | ||
220 | { | ||
221 | struct atbm_state *priv = fe->demodulator_priv; | ||
222 | const struct atbm8830_config *cfg = priv->config; | ||
223 | |||
224 | /*Set oscillator frequency*/ | ||
225 | set_osc_freq(priv, cfg->osc_clk_freq); | ||
226 | |||
227 | /*Set IF frequency*/ | ||
228 | set_if_freq(priv, cfg->if_freq); | ||
229 | |||
230 | |||
231 | /*Set static channel mode*/ | ||
232 | set_static_channel_mode(priv); | ||
233 | |||
234 | set_ts_config(priv); | ||
235 | /*Turn off DSP reset*/ | ||
236 | atbm8830_write_reg(priv, 0x000A, 0); | ||
237 | |||
238 | /*SW version test*/ | ||
239 | atbm8830_write_reg(priv, 0x020C, 11); | ||
240 | |||
241 | /* Run */ | ||
242 | atbm8830_write_reg(priv, REG_DEMOD_RUN, 1); | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | |||
248 | static void atbm8830_release(struct dvb_frontend *fe) | ||
249 | { | ||
250 | struct atbm_state *state = fe->demodulator_priv; | ||
251 | dprintk("%s\n", __func__); | ||
252 | |||
253 | kfree(state); | ||
254 | } | ||
255 | |||
256 | static int atbm8830_set_fe(struct dvb_frontend *fe, | ||
257 | struct dvb_frontend_parameters *fe_params) | ||
258 | { | ||
259 | struct atbm_state *priv = fe->demodulator_priv; | ||
260 | int i; | ||
261 | u8 locked = 0; | ||
262 | dprintk("%s\n", __func__); | ||
263 | |||
264 | /* set frequency */ | ||
265 | if (fe->ops.tuner_ops.set_params) { | ||
266 | if (fe->ops.i2c_gate_ctrl) | ||
267 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
268 | fe->ops.tuner_ops.set_params(fe, fe_params); | ||
269 | if (fe->ops.i2c_gate_ctrl) | ||
270 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
271 | } | ||
272 | |||
273 | /* start auto lock */ | ||
274 | for (i = 0; i < 10; i++) { | ||
275 | mdelay(100); | ||
276 | dprintk("Try %d\n", i); | ||
277 | is_locked(priv, &locked); | ||
278 | if (locked != 0) { | ||
279 | dprintk("ATBM8830 locked!\n"); | ||
280 | break; | ||
281 | } | ||
282 | } | ||
283 | |||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | static int atbm8830_get_fe(struct dvb_frontend *fe, | ||
288 | struct dvb_frontend_parameters *fe_params) | ||
289 | { | ||
290 | dprintk("%s\n", __func__); | ||
291 | |||
292 | /* TODO: get real readings from device */ | ||
293 | /* inversion status */ | ||
294 | fe_params->inversion = INVERSION_OFF; | ||
295 | |||
296 | /* bandwidth */ | ||
297 | fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; | ||
298 | |||
299 | fe_params->u.ofdm.code_rate_HP = FEC_AUTO; | ||
300 | fe_params->u.ofdm.code_rate_LP = FEC_AUTO; | ||
301 | |||
302 | fe_params->u.ofdm.constellation = QAM_AUTO; | ||
303 | |||
304 | /* transmission mode */ | ||
305 | fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; | ||
306 | |||
307 | /* guard interval */ | ||
308 | fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; | ||
309 | |||
310 | /* hierarchy */ | ||
311 | fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE; | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | static int atbm8830_get_tune_settings(struct dvb_frontend *fe, | ||
317 | struct dvb_frontend_tune_settings *fesettings) | ||
318 | { | ||
319 | fesettings->min_delay_ms = 0; | ||
320 | fesettings->step_size = 0; | ||
321 | fesettings->max_drift = 0; | ||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | static int atbm8830_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) | ||
326 | { | ||
327 | struct atbm_state *priv = fe->demodulator_priv; | ||
328 | u8 locked = 0; | ||
329 | u8 agc_locked = 0; | ||
330 | |||
331 | dprintk("%s\n", __func__); | ||
332 | *fe_status = 0; | ||
333 | |||
334 | is_locked(priv, &locked); | ||
335 | if (locked) { | ||
336 | *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | | ||
337 | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; | ||
338 | } | ||
339 | dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); | ||
340 | |||
341 | atbm8830_read_reg(priv, REG_AGC_LOCK, &agc_locked); | ||
342 | dprintk("AGC Lock: %d\n", agc_locked); | ||
343 | |||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | static int atbm8830_read_ber(struct dvb_frontend *fe, u32 *ber) | ||
348 | { | ||
349 | struct atbm_state *priv = fe->demodulator_priv; | ||
350 | u32 frame_err; | ||
351 | u8 t; | ||
352 | |||
353 | dprintk("%s\n", __func__); | ||
354 | |||
355 | atbm8830_reglatch_lock(priv, 1); | ||
356 | |||
357 | atbm8830_read_reg(priv, REG_FRAME_ERR_CNT + 1, &t); | ||
358 | frame_err = t & 0x7F; | ||
359 | frame_err <<= 8; | ||
360 | atbm8830_read_reg(priv, REG_FRAME_ERR_CNT, &t); | ||
361 | frame_err |= t; | ||
362 | |||
363 | atbm8830_reglatch_lock(priv, 0); | ||
364 | |||
365 | *ber = frame_err * 100 / 32767; | ||
366 | |||
367 | dprintk("%s: ber=0x%x\n", __func__, *ber); | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static int atbm8830_read_signal_strength(struct dvb_frontend *fe, u16 *signal) | ||
372 | { | ||
373 | struct atbm_state *priv = fe->demodulator_priv; | ||
374 | u32 pwm; | ||
375 | u8 t; | ||
376 | |||
377 | dprintk("%s\n", __func__); | ||
378 | atbm8830_reglatch_lock(priv, 1); | ||
379 | |||
380 | atbm8830_read_reg(priv, REG_AGC_PWM_VAL + 1, &t); | ||
381 | pwm = t & 0x03; | ||
382 | pwm <<= 8; | ||
383 | atbm8830_read_reg(priv, REG_AGC_PWM_VAL, &t); | ||
384 | pwm |= t; | ||
385 | |||
386 | atbm8830_reglatch_lock(priv, 0); | ||
387 | |||
388 | dprintk("AGC PWM = 0x%02X\n", pwm); | ||
389 | pwm = 0x400 - pwm; | ||
390 | |||
391 | *signal = pwm * 0x10000 / 0x400; | ||
392 | |||
393 | return 0; | ||
394 | } | ||
395 | |||
396 | static int atbm8830_read_snr(struct dvb_frontend *fe, u16 *snr) | ||
397 | { | ||
398 | dprintk("%s\n", __func__); | ||
399 | *snr = 0; | ||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | static int atbm8830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) | ||
404 | { | ||
405 | dprintk("%s\n", __func__); | ||
406 | *ucblocks = 0; | ||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | static int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) | ||
411 | { | ||
412 | struct atbm_state *priv = fe->demodulator_priv; | ||
413 | |||
414 | return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0); | ||
415 | } | ||
416 | |||
417 | static struct dvb_frontend_ops atbm8830_ops = { | ||
418 | .info = { | ||
419 | .name = "AltoBeam ATBM8830/8831 DMB-TH", | ||
420 | .type = FE_OFDM, | ||
421 | .frequency_min = 474000000, | ||
422 | .frequency_max = 858000000, | ||
423 | .frequency_stepsize = 10000, | ||
424 | .caps = | ||
425 | FE_CAN_FEC_AUTO | | ||
426 | FE_CAN_QAM_AUTO | | ||
427 | FE_CAN_TRANSMISSION_MODE_AUTO | | ||
428 | FE_CAN_GUARD_INTERVAL_AUTO | ||
429 | }, | ||
430 | |||
431 | .release = atbm8830_release, | ||
432 | |||
433 | .init = atbm8830_init, | ||
434 | .sleep = NULL, | ||
435 | .write = NULL, | ||
436 | .i2c_gate_ctrl = atbm8830_i2c_gate_ctrl, | ||
437 | |||
438 | .set_frontend = atbm8830_set_fe, | ||
439 | .get_frontend = atbm8830_get_fe, | ||
440 | .get_tune_settings = atbm8830_get_tune_settings, | ||
441 | |||
442 | .read_status = atbm8830_read_status, | ||
443 | .read_ber = atbm8830_read_ber, | ||
444 | .read_signal_strength = atbm8830_read_signal_strength, | ||
445 | .read_snr = atbm8830_read_snr, | ||
446 | .read_ucblocks = atbm8830_read_ucblocks, | ||
447 | }; | ||
448 | |||
449 | struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, | ||
450 | struct i2c_adapter *i2c) | ||
451 | { | ||
452 | struct atbm_state *priv = NULL; | ||
453 | u8 data = 0; | ||
454 | |||
455 | dprintk("%s()\n", __func__); | ||
456 | |||
457 | if (config == NULL || i2c == NULL) | ||
458 | return NULL; | ||
459 | |||
460 | priv = kzalloc(sizeof(struct atbm_state), GFP_KERNEL); | ||
461 | if (priv == NULL) | ||
462 | goto error_out; | ||
463 | |||
464 | priv->config = config; | ||
465 | priv->i2c = i2c; | ||
466 | |||
467 | /* check if the demod is there */ | ||
468 | if (atbm8830_read_reg(priv, REG_CHIP_ID, &data) != 0) { | ||
469 | dprintk("%s atbm8830/8831 not found at i2c addr 0x%02X\n", | ||
470 | __func__, priv->config->demod_address); | ||
471 | goto error_out; | ||
472 | } | ||
473 | dprintk("atbm8830 chip id: 0x%02X\n", data); | ||
474 | |||
475 | memcpy(&priv->frontend.ops, &atbm8830_ops, | ||
476 | sizeof(struct dvb_frontend_ops)); | ||
477 | priv->frontend.demodulator_priv = priv; | ||
478 | |||
479 | atbm8830_init(&priv->frontend); | ||
480 | |||
481 | atbm8830_i2c_gate_ctrl(&priv->frontend, 1); | ||
482 | |||
483 | return &priv->frontend; | ||
484 | |||
485 | error_out: | ||
486 | dprintk("%s() error_out\n", __func__); | ||
487 | kfree(priv); | ||
488 | return NULL; | ||
489 | |||
490 | } | ||
491 | EXPORT_SYMBOL(atbm8830_attach); | ||
492 | |||
493 | MODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver"); | ||
494 | MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>"); | ||
495 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/dvb/frontends/atbm8830.h b/drivers/media/dvb/frontends/atbm8830.h new file mode 100644 index 000000000000..e8149f393300 --- /dev/null +++ b/drivers/media/dvb/frontends/atbm8830.h | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator | ||
3 | * ATBM8830, ATBM8831 | ||
4 | * | ||
5 | * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com> | ||
6 | * | ||
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. | ||
11 | * | ||
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. | ||
16 | * | ||
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. | ||
20 | */ | ||
21 | |||
22 | #ifndef __ATBM8830_H__ | ||
23 | #define __ATBM8830_H__ | ||
24 | |||
25 | #include <linux/dvb/frontend.h> | ||
26 | #include <linux/i2c.h> | ||
27 | |||
28 | #define ATBM8830_PROD_8830 0 | ||
29 | #define ATBM8830_PROD_8831 1 | ||
30 | |||
31 | struct atbm8830_config { | ||
32 | |||
33 | /* product type */ | ||
34 | u8 prod; | ||
35 | |||
36 | /* the demodulator's i2c address */ | ||
37 | u8 demod_address; | ||
38 | |||
39 | /* parallel or serial transport stream */ | ||
40 | u8 serial_ts; | ||
41 | |||
42 | /* transport stream clock output only when receving valid stream */ | ||
43 | u8 ts_clk_gated; | ||
44 | |||
45 | /* Decoder sample TS data at rising edge of clock */ | ||
46 | u8 ts_sampling_edge; | ||
47 | |||
48 | /* Oscillator clock frequency */ | ||
49 | u32 osc_clk_freq; /* in kHz */ | ||
50 | |||
51 | /* IF frequency */ | ||
52 | u32 if_freq; /* in kHz */ | ||
53 | |||
54 | /* Swap I/Q for zero IF */ | ||
55 | u8 zif_swap_iq; | ||
56 | |||
57 | /* Tuner AGC settings */ | ||
58 | u8 agc_min; | ||
59 | u8 agc_max; | ||
60 | u8 agc_hold_loop; | ||
61 | }; | ||
62 | |||
63 | #if defined(CONFIG_DVB_ATBM8830) || \ | ||
64 | (defined(CONFIG_DVB_ATBM8830_MODULE) && defined(MODULE)) | ||
65 | extern struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, | ||
66 | struct i2c_adapter *i2c); | ||
67 | #else | ||
68 | static inline | ||
69 | struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, | ||
70 | struct i2c_adapter *i2c) { | ||
71 | printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); | ||
72 | return NULL; | ||
73 | } | ||
74 | #endif /* CONFIG_DVB_ATBM8830 */ | ||
75 | |||
76 | #endif /* __ATBM8830_H__ */ | ||
diff --git a/drivers/media/dvb/frontends/atbm8830_priv.h b/drivers/media/dvb/frontends/atbm8830_priv.h new file mode 100644 index 000000000000..ce960f76092a --- /dev/null +++ b/drivers/media/dvb/frontends/atbm8830_priv.h | |||
@@ -0,0 +1,75 @@ | |||
1 | /* | ||
2 | * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator | ||
3 | * ATBM8830, ATBM8831 | ||
4 | * | ||
5 | * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com> | ||
6 | * | ||
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. | ||
11 | * | ||
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. | ||
16 | * | ||
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. | ||
20 | */ | ||
21 | |||
22 | #ifndef __ATBM8830_PRIV_H | ||
23 | #define __ATBM8830_PRIV_H | ||
24 | |||
25 | struct atbm_state { | ||
26 | struct i2c_adapter *i2c; | ||
27 | /* configuration settings */ | ||
28 | const struct atbm8830_config *config; | ||
29 | struct dvb_frontend frontend; | ||
30 | }; | ||
31 | |||
32 | #define REG_CHIP_ID 0x0000 | ||
33 | #define REG_TUNER_BASEBAND 0x0001 | ||
34 | #define REG_DEMOD_RUN 0x0004 | ||
35 | #define REG_DSP_RESET 0x0005 | ||
36 | #define REG_RAM_RESET 0x0006 | ||
37 | #define REG_ADC_RESET 0x0007 | ||
38 | #define REG_TSPORT_RESET 0x0008 | ||
39 | #define REG_BLKERR_POL 0x000C | ||
40 | #define REG_I2C_GATE 0x0103 | ||
41 | #define REG_TS_SAMPLE_EDGE 0x0301 | ||
42 | #define REG_TS_PKT_LEN_204 0x0302 | ||
43 | #define REG_TS_PKT_LEN_AUTO 0x0303 | ||
44 | #define REG_TS_SERIAL 0x0305 | ||
45 | #define REG_TS_CLK_FREERUN 0x0306 | ||
46 | #define REG_TS_VALID_MODE 0x0307 | ||
47 | #define REG_TS_CLK_MODE 0x030B /* 1 for serial, 0 for parallel */ | ||
48 | |||
49 | #define REG_TS_ERRBIT_USE 0x030C | ||
50 | #define REG_LOCK_STATUS 0x030D | ||
51 | #define REG_ADC_CONFIG 0x0602 | ||
52 | #define REG_CARRIER_OFFSET 0x0827 /* 0x0827-0x0829 little endian */ | ||
53 | #define REG_DETECTED_PN_MODE 0x082D | ||
54 | #define REG_READ_LATCH 0x084D | ||
55 | #define REG_IF_FREQ 0x0A00 /* 0x0A00-0x0A02 little endian */ | ||
56 | #define REG_OSC_CLK 0x0A03 /* 0x0A03-0x0A05 little endian */ | ||
57 | #define REG_BYPASS_CCI 0x0A06 | ||
58 | #define REG_ANALOG_LUMA_DETECTED 0x0A25 | ||
59 | #define REG_ANALOG_AUDIO_DETECTED 0x0A26 | ||
60 | #define REG_ANALOG_CHROMA_DETECTED 0x0A39 | ||
61 | #define REG_FRAME_ERR_CNT 0x0B04 | ||
62 | #define REG_USE_EXT_ADC 0x0C00 | ||
63 | #define REG_SWAP_I_Q 0x0C01 | ||
64 | #define REG_TPS_MANUAL 0x0D01 | ||
65 | #define REG_TPS_CONFIG 0x0D02 | ||
66 | #define REG_BYPASS_DEINTERLEAVER 0x0E00 | ||
67 | #define REG_AGC_TARGET 0x1003 /* 0x1003-0x1005 little endian */ | ||
68 | #define REG_AGC_MIN 0x1020 | ||
69 | #define REG_AGC_MAX 0x1023 | ||
70 | #define REG_AGC_LOCK 0x1027 | ||
71 | #define REG_AGC_PWM_VAL 0x1028 /* 0x1028-0x1029 little endian */ | ||
72 | #define REG_AGC_HOLD_LOOP 0x1031 | ||
73 | |||
74 | #endif | ||
75 | |||