diff options
Diffstat (limited to 'drivers/media/dvb/frontends/lgs8gxx.c')
-rw-r--r-- | drivers/media/dvb/frontends/lgs8gxx.c | 1073 |
1 files changed, 1073 insertions, 0 deletions
diff --git a/drivers/media/dvb/frontends/lgs8gxx.c b/drivers/media/dvb/frontends/lgs8gxx.c new file mode 100644 index 00000000000..1172b54689f --- /dev/null +++ b/drivers/media/dvb/frontends/lgs8gxx.c | |||
@@ -0,0 +1,1073 @@ | |||
1 | /* | ||
2 | * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator | ||
3 | * LGS8913, LGS8GL5, LGS8G75 | ||
4 | * experimental support LGS8G42, LGS8G52 | ||
5 | * | ||
6 | * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com> | ||
7 | * Copyright (C) 2008 Sirius International (Hong Kong) Limited | ||
8 | * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5) | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <asm/div64.h> | ||
27 | #include <linux/firmware.h> | ||
28 | |||
29 | #include "dvb_frontend.h" | ||
30 | |||
31 | #include "lgs8gxx.h" | ||
32 | #include "lgs8gxx_priv.h" | ||
33 | |||
34 | #define dprintk(args...) \ | ||
35 | do { \ | ||
36 | if (debug) \ | ||
37 | printk(KERN_DEBUG "lgs8gxx: " args); \ | ||
38 | } while (0) | ||
39 | |||
40 | static int debug; | ||
41 | static int fake_signal_str = 1; | ||
42 | |||
43 | module_param(debug, int, 0644); | ||
44 | MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); | ||
45 | |||
46 | module_param(fake_signal_str, int, 0644); | ||
47 | MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913." | ||
48 | "Signal strength calculation is slow.(default:on)."); | ||
49 | |||
50 | /* LGS8GXX internal helper functions */ | ||
51 | |||
52 | static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data) | ||
53 | { | ||
54 | int ret; | ||
55 | u8 buf[] = { reg, data }; | ||
56 | struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; | ||
57 | |||
58 | msg.addr = priv->config->demod_address; | ||
59 | if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) | ||
60 | msg.addr += 0x02; | ||
61 | |||
62 | if (debug >= 2) | ||
63 | dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, data); | ||
64 | |||
65 | ret = i2c_transfer(priv->i2c, &msg, 1); | ||
66 | |||
67 | if (ret != 1) | ||
68 | dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", | ||
69 | __func__, reg, data, ret); | ||
70 | |||
71 | return (ret != 1) ? -1 : 0; | ||
72 | } | ||
73 | |||
74 | static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data) | ||
75 | { | ||
76 | int ret; | ||
77 | u8 dev_addr; | ||
78 | |||
79 | u8 b0[] = { reg }; | ||
80 | u8 b1[] = { 0 }; | ||
81 | struct i2c_msg msg[] = { | ||
82 | { .flags = 0, .buf = b0, .len = 1 }, | ||
83 | { .flags = I2C_M_RD, .buf = b1, .len = 1 }, | ||
84 | }; | ||
85 | |||
86 | dev_addr = priv->config->demod_address; | ||
87 | if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) | ||
88 | dev_addr += 0x02; | ||
89 | msg[1].addr = msg[0].addr = dev_addr; | ||
90 | |||
91 | ret = i2c_transfer(priv->i2c, msg, 2); | ||
92 | if (ret != 2) { | ||
93 | dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ret); | ||
94 | return -1; | ||
95 | } | ||
96 | |||
97 | *p_data = b1[0]; | ||
98 | if (debug >= 2) | ||
99 | dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, b1[0]); | ||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv) | ||
104 | { | ||
105 | lgs8gxx_write_reg(priv, 0x02, 0x00); | ||
106 | msleep(1); | ||
107 | lgs8gxx_write_reg(priv, 0x02, 0x01); | ||
108 | msleep(100); | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static int wait_reg_mask(struct lgs8gxx_state *priv, u8 reg, u8 mask, | ||
114 | u8 val, u8 delay, u8 tries) | ||
115 | { | ||
116 | u8 t; | ||
117 | int i; | ||
118 | |||
119 | for (i = 0; i < tries; i++) { | ||
120 | lgs8gxx_read_reg(priv, reg, &t); | ||
121 | |||
122 | if ((t & mask) == val) | ||
123 | return 0; | ||
124 | msleep(delay); | ||
125 | } | ||
126 | |||
127 | return 1; | ||
128 | } | ||
129 | |||
130 | static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv) | ||
131 | { | ||
132 | const struct lgs8gxx_config *config = priv->config; | ||
133 | u8 if_conf; | ||
134 | |||
135 | if_conf = 0x10; /* AGC output on, RF_AGC output off; */ | ||
136 | |||
137 | if_conf |= | ||
138 | ((config->ext_adc) ? 0x80 : 0x00) | | ||
139 | ((config->if_neg_center) ? 0x04 : 0x00) | | ||
140 | ((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */ | ||
141 | ((config->adc_signed) ? 0x02 : 0x00) | | ||
142 | ((config->if_neg_edge) ? 0x01 : 0x00); | ||
143 | |||
144 | if (config->ext_adc && | ||
145 | (config->prod == LGS8GXX_PROD_LGS8G52)) { | ||
146 | lgs8gxx_write_reg(priv, 0xBA, 0x40); | ||
147 | } | ||
148 | |||
149 | lgs8gxx_write_reg(priv, 0x07, if_conf); | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/) | ||
155 | { | ||
156 | u64 val; | ||
157 | u32 v32; | ||
158 | u32 if_clk; | ||
159 | |||
160 | if_clk = priv->config->if_clk_freq; | ||
161 | |||
162 | val = freq; | ||
163 | if (freq != 0) { | ||
164 | val <<= 32; | ||
165 | if (if_clk != 0) | ||
166 | do_div(val, if_clk); | ||
167 | v32 = val & 0xFFFFFFFF; | ||
168 | dprintk("Set IF Freq to %dkHz\n", freq); | ||
169 | } else { | ||
170 | v32 = 0; | ||
171 | dprintk("Set IF Freq to baseband\n"); | ||
172 | } | ||
173 | dprintk("AFC_INIT_FREQ = 0x%08X\n", v32); | ||
174 | |||
175 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { | ||
176 | lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32)); | ||
177 | lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8)); | ||
178 | lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 16)); | ||
179 | lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 24)); | ||
180 | } else { | ||
181 | lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32)); | ||
182 | lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8)); | ||
183 | lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16)); | ||
184 | lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24)); | ||
185 | } | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv) | ||
191 | { | ||
192 | u64 val; | ||
193 | u32 v32 = 0; | ||
194 | u8 reg_addr, t; | ||
195 | int i; | ||
196 | |||
197 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) | ||
198 | reg_addr = 0x23; | ||
199 | else | ||
200 | reg_addr = 0x48; | ||
201 | |||
202 | for (i = 0; i < 4; i++) { | ||
203 | lgs8gxx_read_reg(priv, reg_addr, &t); | ||
204 | v32 <<= 8; | ||
205 | v32 |= t; | ||
206 | reg_addr--; | ||
207 | } | ||
208 | |||
209 | val = v32; | ||
210 | val *= priv->config->if_clk_freq; | ||
211 | val >>= 32; | ||
212 | dprintk("AFC = %u kHz\n", (u32)val); | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv) | ||
217 | { | ||
218 | u8 t; | ||
219 | u8 prod = priv->config->prod; | ||
220 | |||
221 | if (prod == LGS8GXX_PROD_LGS8913) | ||
222 | lgs8gxx_write_reg(priv, 0xC6, 0x01); | ||
223 | |||
224 | if (prod == LGS8GXX_PROD_LGS8G75) { | ||
225 | lgs8gxx_read_reg(priv, 0x0C, &t); | ||
226 | t &= (~0x04); | ||
227 | lgs8gxx_write_reg(priv, 0x0C, t | 0x80); | ||
228 | lgs8gxx_write_reg(priv, 0x39, 0x00); | ||
229 | lgs8gxx_write_reg(priv, 0x3D, 0x04); | ||
230 | } else if (prod == LGS8GXX_PROD_LGS8913 || | ||
231 | prod == LGS8GXX_PROD_LGS8GL5 || | ||
232 | prod == LGS8GXX_PROD_LGS8G42 || | ||
233 | prod == LGS8GXX_PROD_LGS8G52 || | ||
234 | prod == LGS8GXX_PROD_LGS8G54) { | ||
235 | lgs8gxx_read_reg(priv, 0x7E, &t); | ||
236 | lgs8gxx_write_reg(priv, 0x7E, t | 0x01); | ||
237 | |||
238 | /* clear FEC self reset */ | ||
239 | lgs8gxx_read_reg(priv, 0xC5, &t); | ||
240 | lgs8gxx_write_reg(priv, 0xC5, t & 0xE0); | ||
241 | } | ||
242 | |||
243 | if (prod == LGS8GXX_PROD_LGS8913) { | ||
244 | /* FEC auto detect */ | ||
245 | lgs8gxx_write_reg(priv, 0xC1, 0x03); | ||
246 | |||
247 | lgs8gxx_read_reg(priv, 0x7C, &t); | ||
248 | t = (t & 0x8C) | 0x03; | ||
249 | lgs8gxx_write_reg(priv, 0x7C, t); | ||
250 | |||
251 | /* BER test mode */ | ||
252 | lgs8gxx_read_reg(priv, 0xC3, &t); | ||
253 | t = (t & 0xEF) | 0x10; | ||
254 | lgs8gxx_write_reg(priv, 0xC3, t); | ||
255 | } | ||
256 | |||
257 | if (priv->config->prod == LGS8GXX_PROD_LGS8G52) | ||
258 | lgs8gxx_write_reg(priv, 0xD9, 0x40); | ||
259 | |||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv) | ||
264 | { | ||
265 | int ret = 0; | ||
266 | u8 t; | ||
267 | |||
268 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { | ||
269 | u8 t2; | ||
270 | lgs8gxx_read_reg(priv, 0x0C, &t); | ||
271 | t &= (~0x80); | ||
272 | lgs8gxx_write_reg(priv, 0x0C, t); | ||
273 | |||
274 | lgs8gxx_read_reg(priv, 0x0C, &t); | ||
275 | lgs8gxx_read_reg(priv, 0x19, &t2); | ||
276 | |||
277 | if (((t&0x03) == 0x01) && (t2&0x01)) { | ||
278 | lgs8gxx_write_reg(priv, 0x6E, 0x05); | ||
279 | lgs8gxx_write_reg(priv, 0x39, 0x02); | ||
280 | lgs8gxx_write_reg(priv, 0x39, 0x03); | ||
281 | lgs8gxx_write_reg(priv, 0x3D, 0x05); | ||
282 | lgs8gxx_write_reg(priv, 0x3E, 0x28); | ||
283 | lgs8gxx_write_reg(priv, 0x53, 0x80); | ||
284 | } else { | ||
285 | lgs8gxx_write_reg(priv, 0x6E, 0x3F); | ||
286 | lgs8gxx_write_reg(priv, 0x39, 0x00); | ||
287 | lgs8gxx_write_reg(priv, 0x3D, 0x04); | ||
288 | } | ||
289 | |||
290 | lgs8gxx_soft_reset(priv); | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | /* turn off auto-detect; manual settings */ | ||
295 | lgs8gxx_write_reg(priv, 0x7E, 0); | ||
296 | if (priv->config->prod == LGS8GXX_PROD_LGS8913) | ||
297 | lgs8gxx_write_reg(priv, 0xC1, 0); | ||
298 | |||
299 | ret = lgs8gxx_read_reg(priv, 0xC5, &t); | ||
300 | t = (t & 0xE0) | 0x06; | ||
301 | lgs8gxx_write_reg(priv, 0xC5, t); | ||
302 | |||
303 | lgs8gxx_soft_reset(priv); | ||
304 | |||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked) | ||
309 | { | ||
310 | int ret = 0; | ||
311 | u8 t; | ||
312 | |||
313 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) | ||
314 | ret = lgs8gxx_read_reg(priv, 0x13, &t); | ||
315 | else | ||
316 | ret = lgs8gxx_read_reg(priv, 0x4B, &t); | ||
317 | if (ret != 0) | ||
318 | return ret; | ||
319 | |||
320 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) | ||
321 | *locked = ((t & 0x80) == 0x80) ? 1 : 0; | ||
322 | else | ||
323 | *locked = ((t & 0xC0) == 0xC0) ? 1 : 0; | ||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | /* Wait for Code Acquisition Lock */ | ||
328 | static int lgs8gxx_wait_ca_lock(struct lgs8gxx_state *priv, u8 *locked) | ||
329 | { | ||
330 | int ret = 0; | ||
331 | u8 reg, mask, val; | ||
332 | |||
333 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { | ||
334 | reg = 0x13; | ||
335 | mask = 0x80; | ||
336 | val = 0x80; | ||
337 | } else { | ||
338 | reg = 0x4B; | ||
339 | mask = 0xC0; | ||
340 | val = 0xC0; | ||
341 | } | ||
342 | |||
343 | ret = wait_reg_mask(priv, reg, mask, val, 50, 40); | ||
344 | *locked = (ret == 0) ? 1 : 0; | ||
345 | |||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv, | ||
350 | u8 *finished) | ||
351 | { | ||
352 | int ret = 0; | ||
353 | u8 reg, mask, val; | ||
354 | |||
355 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { | ||
356 | reg = 0x1f; | ||
357 | mask = 0xC0; | ||
358 | val = 0x80; | ||
359 | } else { | ||
360 | reg = 0xA4; | ||
361 | mask = 0x03; | ||
362 | val = 0x01; | ||
363 | } | ||
364 | |||
365 | ret = wait_reg_mask(priv, reg, mask, val, 10, 20); | ||
366 | *finished = (ret == 0) ? 1 : 0; | ||
367 | |||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 cpn, | ||
372 | u8 *locked) | ||
373 | { | ||
374 | int err = 0; | ||
375 | u8 ad_fini = 0; | ||
376 | u8 t1, t2; | ||
377 | |||
378 | if (gi == GI_945) | ||
379 | dprintk("try GI 945\n"); | ||
380 | else if (gi == GI_595) | ||
381 | dprintk("try GI 595\n"); | ||
382 | else if (gi == GI_420) | ||
383 | dprintk("try GI 420\n"); | ||
384 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { | ||
385 | lgs8gxx_read_reg(priv, 0x0C, &t1); | ||
386 | lgs8gxx_read_reg(priv, 0x18, &t2); | ||
387 | t1 &= ~(GI_MASK); | ||
388 | t1 |= gi; | ||
389 | t2 &= 0xFE; | ||
390 | t2 |= cpn ? 0x01 : 0x00; | ||
391 | lgs8gxx_write_reg(priv, 0x0C, t1); | ||
392 | lgs8gxx_write_reg(priv, 0x18, t2); | ||
393 | } else { | ||
394 | lgs8gxx_write_reg(priv, 0x04, gi); | ||
395 | } | ||
396 | lgs8gxx_soft_reset(priv); | ||
397 | err = lgs8gxx_wait_ca_lock(priv, locked); | ||
398 | if (err || !(*locked)) | ||
399 | return err; | ||
400 | err = lgs8gxx_is_autodetect_finished(priv, &ad_fini); | ||
401 | if (err != 0) | ||
402 | return err; | ||
403 | if (ad_fini) { | ||
404 | dprintk("auto detect finished\n"); | ||
405 | } else | ||
406 | *locked = 0; | ||
407 | |||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv, | ||
412 | u8 *detected_param, u8 *gi) | ||
413 | { | ||
414 | int i, j; | ||
415 | int err = 0; | ||
416 | u8 locked = 0, tmp_gi; | ||
417 | |||
418 | dprintk("%s\n", __func__); | ||
419 | |||
420 | lgs8gxx_set_mode_auto(priv); | ||
421 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { | ||
422 | lgs8gxx_write_reg(priv, 0x67, 0xAA); | ||
423 | lgs8gxx_write_reg(priv, 0x6E, 0x3F); | ||
424 | } else { | ||
425 | /* Guard Interval */ | ||
426 | lgs8gxx_write_reg(priv, 0x03, 00); | ||
427 | } | ||
428 | |||
429 | for (i = 0; i < 2; i++) { | ||
430 | for (j = 0; j < 2; j++) { | ||
431 | tmp_gi = GI_945; | ||
432 | err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked); | ||
433 | if (err) | ||
434 | goto out; | ||
435 | if (locked) | ||
436 | goto locked; | ||
437 | } | ||
438 | for (j = 0; j < 2; j++) { | ||
439 | tmp_gi = GI_420; | ||
440 | err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked); | ||
441 | if (err) | ||
442 | goto out; | ||
443 | if (locked) | ||
444 | goto locked; | ||
445 | } | ||
446 | tmp_gi = GI_595; | ||
447 | err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked); | ||
448 | if (err) | ||
449 | goto out; | ||
450 | if (locked) | ||
451 | goto locked; | ||
452 | } | ||
453 | |||
454 | locked: | ||
455 | if ((err == 0) && (locked == 1)) { | ||
456 | u8 t; | ||
457 | |||
458 | if (priv->config->prod != LGS8GXX_PROD_LGS8G75) { | ||
459 | lgs8gxx_read_reg(priv, 0xA2, &t); | ||
460 | *detected_param = t; | ||
461 | } else { | ||
462 | lgs8gxx_read_reg(priv, 0x1F, &t); | ||
463 | *detected_param = t & 0x3F; | ||
464 | } | ||
465 | |||
466 | if (tmp_gi == GI_945) | ||
467 | dprintk("GI 945 locked\n"); | ||
468 | else if (tmp_gi == GI_595) | ||
469 | dprintk("GI 595 locked\n"); | ||
470 | else if (tmp_gi == GI_420) | ||
471 | dprintk("GI 420 locked\n"); | ||
472 | *gi = tmp_gi; | ||
473 | } | ||
474 | if (!locked) | ||
475 | err = -1; | ||
476 | |||
477 | out: | ||
478 | return err; | ||
479 | } | ||
480 | |||
481 | static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv) | ||
482 | { | ||
483 | s8 err; | ||
484 | u8 gi = 0x2; | ||
485 | u8 detected_param = 0; | ||
486 | |||
487 | err = lgs8gxx_auto_detect(priv, &detected_param, &gi); | ||
488 | |||
489 | if (err != 0) { | ||
490 | dprintk("lgs8gxx_auto_detect failed\n"); | ||
491 | } else | ||
492 | dprintk("detected param = 0x%02X\n", detected_param); | ||
493 | |||
494 | /* Apply detected parameters */ | ||
495 | if (priv->config->prod == LGS8GXX_PROD_LGS8913) { | ||
496 | u8 inter_leave_len = detected_param & TIM_MASK ; | ||
497 | /* Fix 8913 time interleaver detection bug */ | ||
498 | inter_leave_len = (inter_leave_len == TIM_MIDDLE) ? 0x60 : 0x40; | ||
499 | detected_param &= CF_MASK | SC_MASK | LGS_FEC_MASK; | ||
500 | detected_param |= inter_leave_len; | ||
501 | } | ||
502 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { | ||
503 | u8 t; | ||
504 | lgs8gxx_read_reg(priv, 0x19, &t); | ||
505 | t &= 0x81; | ||
506 | t |= detected_param << 1; | ||
507 | lgs8gxx_write_reg(priv, 0x19, t); | ||
508 | } else { | ||
509 | lgs8gxx_write_reg(priv, 0x7D, detected_param); | ||
510 | if (priv->config->prod == LGS8GXX_PROD_LGS8913) | ||
511 | lgs8gxx_write_reg(priv, 0xC0, detected_param); | ||
512 | } | ||
513 | /* lgs8gxx_soft_reset(priv); */ | ||
514 | |||
515 | /* Enter manual mode */ | ||
516 | lgs8gxx_set_mode_manual(priv); | ||
517 | |||
518 | switch (gi) { | ||
519 | case GI_945: | ||
520 | priv->curr_gi = 945; break; | ||
521 | case GI_595: | ||
522 | priv->curr_gi = 595; break; | ||
523 | case GI_420: | ||
524 | priv->curr_gi = 420; break; | ||
525 | default: | ||
526 | priv->curr_gi = 945; break; | ||
527 | } | ||
528 | } | ||
529 | |||
530 | static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv, | ||
531 | u8 serial, u8 clk_pol, u8 clk_gated) | ||
532 | { | ||
533 | int ret = 0; | ||
534 | u8 t, reg_addr; | ||
535 | |||
536 | reg_addr = (priv->config->prod == LGS8GXX_PROD_LGS8G75) ? 0x30 : 0xC2; | ||
537 | ret = lgs8gxx_read_reg(priv, reg_addr, &t); | ||
538 | if (ret != 0) | ||
539 | return ret; | ||
540 | |||
541 | t &= 0xF8; | ||
542 | t |= serial ? TS_SERIAL : TS_PARALLEL; | ||
543 | t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL; | ||
544 | t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN; | ||
545 | |||
546 | ret = lgs8gxx_write_reg(priv, reg_addr, t); | ||
547 | if (ret != 0) | ||
548 | return ret; | ||
549 | |||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | /* A/D input peak-to-peak voltage range */ | ||
554 | static int lgs8g75_set_adc_vpp(struct lgs8gxx_state *priv, | ||
555 | u8 sel) | ||
556 | { | ||
557 | u8 r26 = 0x73, r27 = 0x90; | ||
558 | |||
559 | if (priv->config->prod != LGS8GXX_PROD_LGS8G75) | ||
560 | return 0; | ||
561 | |||
562 | r26 |= (sel & 0x01) << 7; | ||
563 | r27 |= (sel & 0x02) >> 1; | ||
564 | lgs8gxx_write_reg(priv, 0x26, r26); | ||
565 | lgs8gxx_write_reg(priv, 0x27, r27); | ||
566 | |||
567 | return 0; | ||
568 | } | ||
569 | |||
570 | /* LGS8913 demod frontend functions */ | ||
571 | |||
572 | static int lgs8913_init(struct lgs8gxx_state *priv) | ||
573 | { | ||
574 | u8 t; | ||
575 | |||
576 | /* LGS8913 specific */ | ||
577 | lgs8gxx_write_reg(priv, 0xc1, 0x3); | ||
578 | |||
579 | lgs8gxx_read_reg(priv, 0x7c, &t); | ||
580 | lgs8gxx_write_reg(priv, 0x7c, (t&0x8c) | 0x3); | ||
581 | |||
582 | /* LGS8913 specific */ | ||
583 | lgs8gxx_read_reg(priv, 0xc3, &t); | ||
584 | lgs8gxx_write_reg(priv, 0xc3, t&0x10); | ||
585 | |||
586 | |||
587 | return 0; | ||
588 | } | ||
589 | |||
590 | static int lgs8g75_init_data(struct lgs8gxx_state *priv) | ||
591 | { | ||
592 | const struct firmware *fw; | ||
593 | int rc; | ||
594 | int i; | ||
595 | |||
596 | rc = request_firmware(&fw, "lgs8g75.fw", &priv->i2c->dev); | ||
597 | if (rc) | ||
598 | return rc; | ||
599 | |||
600 | lgs8gxx_write_reg(priv, 0xC6, 0x40); | ||
601 | |||
602 | lgs8gxx_write_reg(priv, 0x3D, 0x04); | ||
603 | lgs8gxx_write_reg(priv, 0x39, 0x00); | ||
604 | |||
605 | lgs8gxx_write_reg(priv, 0x3A, 0x00); | ||
606 | lgs8gxx_write_reg(priv, 0x38, 0x00); | ||
607 | lgs8gxx_write_reg(priv, 0x3B, 0x00); | ||
608 | lgs8gxx_write_reg(priv, 0x38, 0x00); | ||
609 | |||
610 | for (i = 0; i < fw->size; i++) { | ||
611 | lgs8gxx_write_reg(priv, 0x38, 0x00); | ||
612 | lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff)); | ||
613 | lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8)); | ||
614 | lgs8gxx_write_reg(priv, 0x3C, fw->data[i]); | ||
615 | } | ||
616 | |||
617 | lgs8gxx_write_reg(priv, 0x38, 0x00); | ||
618 | |||
619 | release_firmware(fw); | ||
620 | return 0; | ||
621 | } | ||
622 | |||
623 | static int lgs8gxx_init(struct dvb_frontend *fe) | ||
624 | { | ||
625 | struct lgs8gxx_state *priv = | ||
626 | (struct lgs8gxx_state *)fe->demodulator_priv; | ||
627 | const struct lgs8gxx_config *config = priv->config; | ||
628 | u8 data = 0; | ||
629 | s8 err; | ||
630 | dprintk("%s\n", __func__); | ||
631 | |||
632 | lgs8gxx_read_reg(priv, 0, &data); | ||
633 | dprintk("reg 0 = 0x%02X\n", data); | ||
634 | |||
635 | if (config->prod == LGS8GXX_PROD_LGS8G75) | ||
636 | lgs8g75_set_adc_vpp(priv, config->adc_vpp); | ||
637 | |||
638 | /* Setup MPEG output format */ | ||
639 | err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts, | ||
640 | config->ts_clk_pol, | ||
641 | config->ts_clk_gated); | ||
642 | if (err != 0) | ||
643 | return -EIO; | ||
644 | |||
645 | if (config->prod == LGS8GXX_PROD_LGS8913) | ||
646 | lgs8913_init(priv); | ||
647 | lgs8gxx_set_if_freq(priv, priv->config->if_freq); | ||
648 | lgs8gxx_set_ad_mode(priv); | ||
649 | |||
650 | return 0; | ||
651 | } | ||
652 | |||
653 | static void lgs8gxx_release(struct dvb_frontend *fe) | ||
654 | { | ||
655 | struct lgs8gxx_state *state = fe->demodulator_priv; | ||
656 | dprintk("%s\n", __func__); | ||
657 | |||
658 | kfree(state); | ||
659 | } | ||
660 | |||
661 | |||
662 | static int lgs8gxx_write(struct dvb_frontend *fe, const u8 buf[], int len) | ||
663 | { | ||
664 | struct lgs8gxx_state *priv = fe->demodulator_priv; | ||
665 | |||
666 | if (len != 2) | ||
667 | return -EINVAL; | ||
668 | |||
669 | return lgs8gxx_write_reg(priv, buf[0], buf[1]); | ||
670 | } | ||
671 | |||
672 | static int lgs8gxx_set_fe(struct dvb_frontend *fe, | ||
673 | struct dvb_frontend_parameters *fe_params) | ||
674 | { | ||
675 | struct lgs8gxx_state *priv = fe->demodulator_priv; | ||
676 | |||
677 | dprintk("%s\n", __func__); | ||
678 | |||
679 | /* set frequency */ | ||
680 | if (fe->ops.tuner_ops.set_params) { | ||
681 | fe->ops.tuner_ops.set_params(fe, fe_params); | ||
682 | if (fe->ops.i2c_gate_ctrl) | ||
683 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
684 | } | ||
685 | |||
686 | /* start auto lock */ | ||
687 | lgs8gxx_auto_lock(priv); | ||
688 | |||
689 | msleep(10); | ||
690 | |||
691 | return 0; | ||
692 | } | ||
693 | |||
694 | static int lgs8gxx_get_fe(struct dvb_frontend *fe, | ||
695 | struct dvb_frontend_parameters *fe_params) | ||
696 | { | ||
697 | dprintk("%s\n", __func__); | ||
698 | |||
699 | /* TODO: get real readings from device */ | ||
700 | /* inversion status */ | ||
701 | fe_params->inversion = INVERSION_OFF; | ||
702 | |||
703 | /* bandwidth */ | ||
704 | fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; | ||
705 | |||
706 | fe_params->u.ofdm.code_rate_HP = FEC_AUTO; | ||
707 | fe_params->u.ofdm.code_rate_LP = FEC_AUTO; | ||
708 | |||
709 | fe_params->u.ofdm.constellation = QAM_AUTO; | ||
710 | |||
711 | /* transmission mode */ | ||
712 | fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; | ||
713 | |||
714 | /* guard interval */ | ||
715 | fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; | ||
716 | |||
717 | /* hierarchy */ | ||
718 | fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE; | ||
719 | |||
720 | return 0; | ||
721 | } | ||
722 | |||
723 | static | ||
724 | int lgs8gxx_get_tune_settings(struct dvb_frontend *fe, | ||
725 | struct dvb_frontend_tune_settings *fesettings) | ||
726 | { | ||
727 | /* FIXME: copy from tda1004x.c */ | ||
728 | fesettings->min_delay_ms = 800; | ||
729 | fesettings->step_size = 0; | ||
730 | fesettings->max_drift = 0; | ||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | static int lgs8gxx_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) | ||
735 | { | ||
736 | struct lgs8gxx_state *priv = fe->demodulator_priv; | ||
737 | s8 ret; | ||
738 | u8 t, locked = 0; | ||
739 | |||
740 | dprintk("%s\n", __func__); | ||
741 | *fe_status = 0; | ||
742 | |||
743 | lgs8gxx_get_afc_phase(priv); | ||
744 | lgs8gxx_is_locked(priv, &locked); | ||
745 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { | ||
746 | if (locked) | ||
747 | *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | | ||
748 | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; | ||
749 | return 0; | ||
750 | } | ||
751 | |||
752 | ret = lgs8gxx_read_reg(priv, 0x4B, &t); | ||
753 | if (ret != 0) | ||
754 | return -EIO; | ||
755 | |||
756 | dprintk("Reg 0x4B: 0x%02X\n", t); | ||
757 | |||
758 | *fe_status = 0; | ||
759 | if (priv->config->prod == LGS8GXX_PROD_LGS8913) { | ||
760 | if ((t & 0x40) == 0x40) | ||
761 | *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; | ||
762 | if ((t & 0x80) == 0x80) | ||
763 | *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | | ||
764 | FE_HAS_LOCK; | ||
765 | } else { | ||
766 | if ((t & 0x80) == 0x80) | ||
767 | *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | | ||
768 | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; | ||
769 | } | ||
770 | |||
771 | /* success */ | ||
772 | dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); | ||
773 | return 0; | ||
774 | } | ||
775 | |||
776 | static int lgs8gxx_read_signal_agc(struct lgs8gxx_state *priv, u16 *signal) | ||
777 | { | ||
778 | u16 v; | ||
779 | u8 agc_lvl[2], cat; | ||
780 | |||
781 | dprintk("%s()\n", __func__); | ||
782 | lgs8gxx_read_reg(priv, 0x3F, &agc_lvl[0]); | ||
783 | lgs8gxx_read_reg(priv, 0x3E, &agc_lvl[1]); | ||
784 | |||
785 | v = agc_lvl[0]; | ||
786 | v <<= 8; | ||
787 | v |= agc_lvl[1]; | ||
788 | |||
789 | dprintk("agc_lvl: 0x%04X\n", v); | ||
790 | |||
791 | if (v < 0x100) | ||
792 | cat = 0; | ||
793 | else if (v < 0x190) | ||
794 | cat = 5; | ||
795 | else if (v < 0x2A8) | ||
796 | cat = 4; | ||
797 | else if (v < 0x381) | ||
798 | cat = 3; | ||
799 | else if (v < 0x400) | ||
800 | cat = 2; | ||
801 | else if (v == 0x400) | ||
802 | cat = 1; | ||
803 | else | ||
804 | cat = 0; | ||
805 | |||
806 | *signal = cat * 65535 / 5; | ||
807 | |||
808 | return 0; | ||
809 | } | ||
810 | |||
811 | static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) | ||
812 | { | ||
813 | u8 t; s8 ret; | ||
814 | s16 max_strength = 0; | ||
815 | u8 str; | ||
816 | u16 i, gi = priv->curr_gi; | ||
817 | |||
818 | dprintk("%s\n", __func__); | ||
819 | |||
820 | ret = lgs8gxx_read_reg(priv, 0x4B, &t); | ||
821 | if (ret != 0) | ||
822 | return -EIO; | ||
823 | |||
824 | if (fake_signal_str) { | ||
825 | if ((t & 0xC0) == 0xC0) { | ||
826 | dprintk("Fake signal strength\n"); | ||
827 | *signal = 0x7FFF; | ||
828 | } else | ||
829 | *signal = 0; | ||
830 | return 0; | ||
831 | } | ||
832 | |||
833 | dprintk("gi = %d\n", gi); | ||
834 | for (i = 0; i < gi; i++) { | ||
835 | |||
836 | if ((i & 0xFF) == 0) | ||
837 | lgs8gxx_write_reg(priv, 0x84, 0x03 & (i >> 8)); | ||
838 | lgs8gxx_write_reg(priv, 0x83, i & 0xFF); | ||
839 | |||
840 | lgs8gxx_read_reg(priv, 0x94, &str); | ||
841 | if (max_strength < str) | ||
842 | max_strength = str; | ||
843 | } | ||
844 | |||
845 | *signal = max_strength; | ||
846 | dprintk("%s: signal=0x%02X\n", __func__, *signal); | ||
847 | |||
848 | lgs8gxx_read_reg(priv, 0x95, &t); | ||
849 | dprintk("%s: AVG Noise=0x%02X\n", __func__, t); | ||
850 | |||
851 | return 0; | ||
852 | } | ||
853 | |||
854 | static int lgs8g75_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) | ||
855 | { | ||
856 | u8 t; | ||
857 | s16 v = 0; | ||
858 | |||
859 | dprintk("%s\n", __func__); | ||
860 | |||
861 | lgs8gxx_read_reg(priv, 0xB1, &t); | ||
862 | v |= t; | ||
863 | v <<= 8; | ||
864 | lgs8gxx_read_reg(priv, 0xB0, &t); | ||
865 | v |= t; | ||
866 | |||
867 | *signal = v; | ||
868 | dprintk("%s: signal=0x%02X\n", __func__, *signal); | ||
869 | |||
870 | return 0; | ||
871 | } | ||
872 | |||
873 | static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal) | ||
874 | { | ||
875 | struct lgs8gxx_state *priv = fe->demodulator_priv; | ||
876 | |||
877 | if (priv->config->prod == LGS8GXX_PROD_LGS8913) | ||
878 | return lgs8913_read_signal_strength(priv, signal); | ||
879 | else if (priv->config->prod == LGS8GXX_PROD_LGS8G75) | ||
880 | return lgs8g75_read_signal_strength(priv, signal); | ||
881 | else | ||
882 | return lgs8gxx_read_signal_agc(priv, signal); | ||
883 | } | ||
884 | |||
885 | static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr) | ||
886 | { | ||
887 | struct lgs8gxx_state *priv = fe->demodulator_priv; | ||
888 | u8 t; | ||
889 | *snr = 0; | ||
890 | |||
891 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) | ||
892 | lgs8gxx_read_reg(priv, 0x34, &t); | ||
893 | else | ||
894 | lgs8gxx_read_reg(priv, 0x95, &t); | ||
895 | dprintk("AVG Noise=0x%02X\n", t); | ||
896 | *snr = 256 - t; | ||
897 | *snr <<= 8; | ||
898 | dprintk("snr=0x%x\n", *snr); | ||
899 | |||
900 | return 0; | ||
901 | } | ||
902 | |||
903 | static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) | ||
904 | { | ||
905 | *ucblocks = 0; | ||
906 | dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks); | ||
907 | return 0; | ||
908 | } | ||
909 | |||
910 | static void packet_counter_start(struct lgs8gxx_state *priv) | ||
911 | { | ||
912 | u8 orig, t; | ||
913 | |||
914 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { | ||
915 | lgs8gxx_read_reg(priv, 0x30, &orig); | ||
916 | orig &= 0xE7; | ||
917 | t = orig | 0x10; | ||
918 | lgs8gxx_write_reg(priv, 0x30, t); | ||
919 | t = orig | 0x18; | ||
920 | lgs8gxx_write_reg(priv, 0x30, t); | ||
921 | t = orig | 0x10; | ||
922 | lgs8gxx_write_reg(priv, 0x30, t); | ||
923 | } else { | ||
924 | lgs8gxx_write_reg(priv, 0xC6, 0x01); | ||
925 | lgs8gxx_write_reg(priv, 0xC6, 0x41); | ||
926 | lgs8gxx_write_reg(priv, 0xC6, 0x01); | ||
927 | } | ||
928 | } | ||
929 | |||
930 | static void packet_counter_stop(struct lgs8gxx_state *priv) | ||
931 | { | ||
932 | u8 t; | ||
933 | |||
934 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { | ||
935 | lgs8gxx_read_reg(priv, 0x30, &t); | ||
936 | t &= 0xE7; | ||
937 | lgs8gxx_write_reg(priv, 0x30, t); | ||
938 | } else { | ||
939 | lgs8gxx_write_reg(priv, 0xC6, 0x81); | ||
940 | } | ||
941 | } | ||
942 | |||
943 | static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber) | ||
944 | { | ||
945 | struct lgs8gxx_state *priv = fe->demodulator_priv; | ||
946 | u8 reg_err, reg_total, t; | ||
947 | u32 total_cnt = 0, err_cnt = 0; | ||
948 | int i; | ||
949 | |||
950 | dprintk("%s\n", __func__); | ||
951 | |||
952 | packet_counter_start(priv); | ||
953 | msleep(200); | ||
954 | packet_counter_stop(priv); | ||
955 | |||
956 | if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { | ||
957 | reg_total = 0x28; reg_err = 0x2C; | ||
958 | } else { | ||
959 | reg_total = 0xD0; reg_err = 0xD4; | ||
960 | } | ||
961 | |||
962 | for (i = 0; i < 4; i++) { | ||
963 | total_cnt <<= 8; | ||
964 | lgs8gxx_read_reg(priv, reg_total+3-i, &t); | ||
965 | total_cnt |= t; | ||
966 | } | ||
967 | for (i = 0; i < 4; i++) { | ||
968 | err_cnt <<= 8; | ||
969 | lgs8gxx_read_reg(priv, reg_err+3-i, &t); | ||
970 | err_cnt |= t; | ||
971 | } | ||
972 | dprintk("error=%d total=%d\n", err_cnt, total_cnt); | ||
973 | |||
974 | if (total_cnt == 0) | ||
975 | *ber = 0; | ||
976 | else | ||
977 | *ber = err_cnt * 100 / total_cnt; | ||
978 | |||
979 | dprintk("%s: ber=0x%x\n", __func__, *ber); | ||
980 | return 0; | ||
981 | } | ||
982 | |||
983 | static int lgs8gxx_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) | ||
984 | { | ||
985 | struct lgs8gxx_state *priv = fe->demodulator_priv; | ||
986 | |||
987 | if (priv->config->tuner_address == 0) | ||
988 | return 0; | ||
989 | if (enable) { | ||
990 | u8 v = 0x80 | priv->config->tuner_address; | ||
991 | return lgs8gxx_write_reg(priv, 0x01, v); | ||
992 | } | ||
993 | return lgs8gxx_write_reg(priv, 0x01, 0); | ||
994 | } | ||
995 | |||
996 | static struct dvb_frontend_ops lgs8gxx_ops = { | ||
997 | .info = { | ||
998 | .name = "Legend Silicon LGS8913/LGS8GXX DMB-TH", | ||
999 | .type = FE_OFDM, | ||
1000 | .frequency_min = 474000000, | ||
1001 | .frequency_max = 858000000, | ||
1002 | .frequency_stepsize = 10000, | ||
1003 | .caps = | ||
1004 | FE_CAN_FEC_AUTO | | ||
1005 | FE_CAN_QAM_AUTO | | ||
1006 | FE_CAN_TRANSMISSION_MODE_AUTO | | ||
1007 | FE_CAN_GUARD_INTERVAL_AUTO | ||
1008 | }, | ||
1009 | |||
1010 | .release = lgs8gxx_release, | ||
1011 | |||
1012 | .init = lgs8gxx_init, | ||
1013 | .write = lgs8gxx_write, | ||
1014 | .i2c_gate_ctrl = lgs8gxx_i2c_gate_ctrl, | ||
1015 | |||
1016 | .set_frontend = lgs8gxx_set_fe, | ||
1017 | .get_frontend = lgs8gxx_get_fe, | ||
1018 | .get_tune_settings = lgs8gxx_get_tune_settings, | ||
1019 | |||
1020 | .read_status = lgs8gxx_read_status, | ||
1021 | .read_ber = lgs8gxx_read_ber, | ||
1022 | .read_signal_strength = lgs8gxx_read_signal_strength, | ||
1023 | .read_snr = lgs8gxx_read_snr, | ||
1024 | .read_ucblocks = lgs8gxx_read_ucblocks, | ||
1025 | }; | ||
1026 | |||
1027 | struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, | ||
1028 | struct i2c_adapter *i2c) | ||
1029 | { | ||
1030 | struct lgs8gxx_state *priv = NULL; | ||
1031 | u8 data = 0; | ||
1032 | |||
1033 | dprintk("%s()\n", __func__); | ||
1034 | |||
1035 | if (config == NULL || i2c == NULL) | ||
1036 | return NULL; | ||
1037 | |||
1038 | priv = kzalloc(sizeof(struct lgs8gxx_state), GFP_KERNEL); | ||
1039 | if (priv == NULL) | ||
1040 | goto error_out; | ||
1041 | |||
1042 | priv->config = config; | ||
1043 | priv->i2c = i2c; | ||
1044 | |||
1045 | /* check if the demod is there */ | ||
1046 | if (lgs8gxx_read_reg(priv, 0, &data) != 0) { | ||
1047 | dprintk("%s lgs8gxx not found at i2c addr 0x%02X\n", | ||
1048 | __func__, priv->config->demod_address); | ||
1049 | goto error_out; | ||
1050 | } | ||
1051 | |||
1052 | lgs8gxx_read_reg(priv, 1, &data); | ||
1053 | |||
1054 | memcpy(&priv->frontend.ops, &lgs8gxx_ops, | ||
1055 | sizeof(struct dvb_frontend_ops)); | ||
1056 | priv->frontend.demodulator_priv = priv; | ||
1057 | |||
1058 | if (config->prod == LGS8GXX_PROD_LGS8G75) | ||
1059 | lgs8g75_init_data(priv); | ||
1060 | |||
1061 | return &priv->frontend; | ||
1062 | |||
1063 | error_out: | ||
1064 | dprintk("%s() error_out\n", __func__); | ||
1065 | kfree(priv); | ||
1066 | return NULL; | ||
1067 | |||
1068 | } | ||
1069 | EXPORT_SYMBOL(lgs8gxx_attach); | ||
1070 | |||
1071 | MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver"); | ||
1072 | MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>"); | ||
1073 | MODULE_LICENSE("GPL"); | ||