blob: 0fcddc4569d2bc80252f02172b12cec2402a6e2a [file] [log] [blame]
David Wongf15da162009-04-01 04:35:10 -03001/*
David Wongb80dc1c2009-06-17 01:38:10 -03002 * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator
3 * LGS8913, LGS8GL5, LGS8G75
David Wongf15da162009-04-01 04:35:10 -03004 * experimental support LGS8G42, LGS8G52
5 *
David Wongb80dc1c2009-06-17 01:38:10 -03006 * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com>
David Wongf15da162009-04-01 04:35:10 -03007 * 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>
Ben Hutchings76386992009-10-31 23:14:35 -030027#include <linux/firmware.h>
David Wongf15da162009-04-01 04:35:10 -030028
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
40static int debug;
David Wongba7d4572009-06-11 09:38:54 -030041static int fake_signal_str = 1;
David Wongf15da162009-04-01 04:35:10 -030042
43module_param(debug, int, 0644);
44MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
45
46module_param(fake_signal_str, int, 0644);
47MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913."
David Wongba7d4572009-06-11 09:38:54 -030048"Signal strength calculation is slow.(default:on).");
David Wongf15da162009-04-01 04:35:10 -030049
50/* LGS8GXX internal helper functions */
51
52static 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;
David Wongb80dc1c2009-06-17 01:38:10 -030059 if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0)
David Wongf15da162009-04-01 04:35:10 -030060 msg.addr += 0x02;
61
62 if (debug >= 2)
63 printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n",
64 __func__, reg, data);
65
66 ret = i2c_transfer(priv->i2c, &msg, 1);
67
68 if (ret != 1)
69 dprintk(KERN_DEBUG "%s: error reg=0x%x, data=0x%x, ret=%i\n",
70 __func__, reg, data, ret);
71
72 return (ret != 1) ? -1 : 0;
73}
74
75static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data)
76{
77 int ret;
78 u8 dev_addr;
79
80 u8 b0[] = { reg };
81 u8 b1[] = { 0 };
82 struct i2c_msg msg[] = {
83 { .flags = 0, .buf = b0, .len = 1 },
84 { .flags = I2C_M_RD, .buf = b1, .len = 1 },
85 };
86
87 dev_addr = priv->config->demod_address;
David Wongb80dc1c2009-06-17 01:38:10 -030088 if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0)
David Wongf15da162009-04-01 04:35:10 -030089 dev_addr += 0x02;
90 msg[1].addr = msg[0].addr = dev_addr;
91
92 ret = i2c_transfer(priv->i2c, msg, 2);
93 if (ret != 2) {
94 dprintk(KERN_DEBUG "%s: error reg=0x%x, ret=%i\n",
95 __func__, reg, ret);
96 return -1;
97 }
98
99 *p_data = b1[0];
100 if (debug >= 2)
101 printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n",
102 __func__, reg, b1[0]);
103 return 0;
104}
105
106static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv)
107{
108 lgs8gxx_write_reg(priv, 0x02, 0x00);
109 msleep(1);
110 lgs8gxx_write_reg(priv, 0x02, 0x01);
111 msleep(100);
112
113 return 0;
114}
115
David Wongb80dc1c2009-06-17 01:38:10 -0300116static int wait_reg_mask(struct lgs8gxx_state *priv, u8 reg, u8 mask,
117 u8 val, u8 delay, u8 tries)
118{
119 u8 t;
120 int i;
121
122 for (i = 0; i < tries; i++) {
123 lgs8gxx_read_reg(priv, reg, &t);
124
125 if ((t & mask) == val)
126 return 0;
127 msleep(delay);
128 }
129
130 return 1;
131}
132
David Wongf15da162009-04-01 04:35:10 -0300133static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv)
134{
135 const struct lgs8gxx_config *config = priv->config;
136 u8 if_conf;
137
David Wongb80dc1c2009-06-17 01:38:10 -0300138 if_conf = 0x10; /* AGC output on, RF_AGC output off; */
David Wongf15da162009-04-01 04:35:10 -0300139
140 if_conf |=
141 ((config->ext_adc) ? 0x80 : 0x00) |
142 ((config->if_neg_center) ? 0x04 : 0x00) |
143 ((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */
David Wongb80dc1c2009-06-17 01:38:10 -0300144 ((config->adc_signed) ? 0x02 : 0x00) |
145 ((config->if_neg_edge) ? 0x01 : 0x00);
David Wongf15da162009-04-01 04:35:10 -0300146
147 if (config->ext_adc &&
148 (config->prod == LGS8GXX_PROD_LGS8G52)) {
149 lgs8gxx_write_reg(priv, 0xBA, 0x40);
150 }
151
152 lgs8gxx_write_reg(priv, 0x07, if_conf);
153
154 return 0;
155}
156
157static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/)
158{
159 u64 val;
160 u32 v32;
161 u32 if_clk;
162
163 if_clk = priv->config->if_clk_freq;
164
165 val = freq;
166 if (freq != 0) {
David Howells3ab1b9c2009-12-11 13:11:05 -0300167 val <<= 32;
David Wongf15da162009-04-01 04:35:10 -0300168 if (if_clk != 0)
169 do_div(val, if_clk);
170 v32 = val & 0xFFFFFFFF;
171 dprintk("Set IF Freq to %dkHz\n", freq);
172 } else {
173 v32 = 0;
174 dprintk("Set IF Freq to baseband\n");
175 }
176 dprintk("AFC_INIT_FREQ = 0x%08X\n", v32);
177
David Wongb80dc1c2009-06-17 01:38:10 -0300178 if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
179 lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32));
180 lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8));
181 lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 16));
182 lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 24));
183 } else {
184 lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32));
185 lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8));
186 lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16));
187 lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24));
188 }
David Wongf15da162009-04-01 04:35:10 -0300189
190 return 0;
191}
192
David Wongb80dc1c2009-06-17 01:38:10 -0300193static int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv)
194{
195 u64 val;
196 u32 v32 = 0;
197 u8 reg_addr, t;
198 int i;
199
200 if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
201 reg_addr = 0x23;
202 else
203 reg_addr = 0x48;
204
205 for (i = 0; i < 4; i++) {
206 lgs8gxx_read_reg(priv, reg_addr, &t);
207 v32 <<= 8;
208 v32 |= t;
209 reg_addr--;
210 }
211
212 val = v32;
213 val *= priv->config->if_clk_freq;
David Howells3ab1b9c2009-12-11 13:11:05 -0300214 val >>= 32;
David Wongb80dc1c2009-06-17 01:38:10 -0300215 dprintk("AFC = %u kHz\n", (u32)val);
216 return 0;
217}
218
David Wongf15da162009-04-01 04:35:10 -0300219static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv)
220{
221 u8 t;
David Wongb80dc1c2009-06-17 01:38:10 -0300222 u8 prod = priv->config->prod;
David Wongf15da162009-04-01 04:35:10 -0300223
David Wongb80dc1c2009-06-17 01:38:10 -0300224 if (prod == LGS8GXX_PROD_LGS8913)
David Wongf15da162009-04-01 04:35:10 -0300225 lgs8gxx_write_reg(priv, 0xC6, 0x01);
226
David Wongb80dc1c2009-06-17 01:38:10 -0300227 if (prod == LGS8GXX_PROD_LGS8G75) {
228 lgs8gxx_read_reg(priv, 0x0C, &t);
229 t &= (~0x04);
230 lgs8gxx_write_reg(priv, 0x0C, t | 0x80);
231 lgs8gxx_write_reg(priv, 0x39, 0x00);
232 lgs8gxx_write_reg(priv, 0x3D, 0x04);
233 } else if (prod == LGS8GXX_PROD_LGS8913 ||
234 prod == LGS8GXX_PROD_LGS8GL5 ||
235 prod == LGS8GXX_PROD_LGS8G42 ||
236 prod == LGS8GXX_PROD_LGS8G52 ||
237 prod == LGS8GXX_PROD_LGS8G54) {
238 lgs8gxx_read_reg(priv, 0x7E, &t);
239 lgs8gxx_write_reg(priv, 0x7E, t | 0x01);
David Wongf15da162009-04-01 04:35:10 -0300240
David Wongb80dc1c2009-06-17 01:38:10 -0300241 /* clear FEC self reset */
242 lgs8gxx_read_reg(priv, 0xC5, &t);
243 lgs8gxx_write_reg(priv, 0xC5, t & 0xE0);
244 }
David Wongf15da162009-04-01 04:35:10 -0300245
David Wongb80dc1c2009-06-17 01:38:10 -0300246 if (prod == LGS8GXX_PROD_LGS8913) {
David Wongf15da162009-04-01 04:35:10 -0300247 /* FEC auto detect */
248 lgs8gxx_write_reg(priv, 0xC1, 0x03);
249
250 lgs8gxx_read_reg(priv, 0x7C, &t);
251 t = (t & 0x8C) | 0x03;
252 lgs8gxx_write_reg(priv, 0x7C, t);
David Wongf15da162009-04-01 04:35:10 -0300253
David Wongf15da162009-04-01 04:35:10 -0300254 /* BER test mode */
255 lgs8gxx_read_reg(priv, 0xC3, &t);
256 t = (t & 0xEF) | 0x10;
257 lgs8gxx_write_reg(priv, 0xC3, t);
258 }
259
260 if (priv->config->prod == LGS8GXX_PROD_LGS8G52)
261 lgs8gxx_write_reg(priv, 0xD9, 0x40);
262
263 return 0;
264}
265
266static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv)
267{
268 int ret = 0;
269 u8 t;
270
David Wongb80dc1c2009-06-17 01:38:10 -0300271 if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
272 u8 t2;
273 lgs8gxx_read_reg(priv, 0x0C, &t);
274 t &= (~0x80);
275 lgs8gxx_write_reg(priv, 0x0C, t);
276
277 lgs8gxx_read_reg(priv, 0x0C, &t);
278 lgs8gxx_read_reg(priv, 0x19, &t2);
279
280 if (((t&0x03) == 0x01) && (t2&0x01)) {
281 lgs8gxx_write_reg(priv, 0x6E, 0x05);
282 lgs8gxx_write_reg(priv, 0x39, 0x02);
283 lgs8gxx_write_reg(priv, 0x39, 0x03);
284 lgs8gxx_write_reg(priv, 0x3D, 0x05);
285 lgs8gxx_write_reg(priv, 0x3E, 0x28);
286 lgs8gxx_write_reg(priv, 0x53, 0x80);
287 } else {
288 lgs8gxx_write_reg(priv, 0x6E, 0x3F);
289 lgs8gxx_write_reg(priv, 0x39, 0x00);
290 lgs8gxx_write_reg(priv, 0x3D, 0x04);
291 }
292
293 lgs8gxx_soft_reset(priv);
294 return 0;
295 }
296
David Wongf15da162009-04-01 04:35:10 -0300297 /* turn off auto-detect; manual settings */
298 lgs8gxx_write_reg(priv, 0x7E, 0);
299 if (priv->config->prod == LGS8GXX_PROD_LGS8913)
300 lgs8gxx_write_reg(priv, 0xC1, 0);
301
302 ret = lgs8gxx_read_reg(priv, 0xC5, &t);
303 t = (t & 0xE0) | 0x06;
304 lgs8gxx_write_reg(priv, 0xC5, t);
305
306 lgs8gxx_soft_reset(priv);
307
308 return 0;
309}
310
311static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked)
312{
313 int ret = 0;
314 u8 t;
315
David Wongb80dc1c2009-06-17 01:38:10 -0300316 if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
317 ret = lgs8gxx_read_reg(priv, 0x13, &t);
318 else
319 ret = lgs8gxx_read_reg(priv, 0x4B, &t);
David Wongf15da162009-04-01 04:35:10 -0300320 if (ret != 0)
321 return ret;
322
David Wongb80dc1c2009-06-17 01:38:10 -0300323 if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
324 *locked = ((t & 0x80) == 0x80) ? 1 : 0;
325 else
326 *locked = ((t & 0xC0) == 0xC0) ? 1 : 0;
327 return 0;
328}
329
330/* Wait for Code Acquisition Lock */
331static int lgs8gxx_wait_ca_lock(struct lgs8gxx_state *priv, u8 *locked)
332{
333 int ret = 0;
334 u8 reg, mask, val;
335
336 if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
337 reg = 0x13;
338 mask = 0x80;
339 val = 0x80;
340 } else {
341 reg = 0x4B;
342 mask = 0xC0;
343 val = 0xC0;
344 }
345
346 ret = wait_reg_mask(priv, reg, mask, val, 50, 40);
347 *locked = (ret == 0) ? 1 : 0;
348
David Wongf15da162009-04-01 04:35:10 -0300349 return 0;
350}
351
352static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv,
353 u8 *finished)
354{
355 int ret = 0;
David Wongb80dc1c2009-06-17 01:38:10 -0300356 u8 reg, mask, val;
David Wongf15da162009-04-01 04:35:10 -0300357
David Wongb80dc1c2009-06-17 01:38:10 -0300358 if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
359 reg = 0x1f;
360 mask = 0xC0;
361 val = 0x80;
362 } else {
363 reg = 0xA4;
364 mask = 0x03;
365 val = 0x01;
366 }
David Wongf15da162009-04-01 04:35:10 -0300367
David Wongb80dc1c2009-06-17 01:38:10 -0300368 ret = wait_reg_mask(priv, reg, mask, val, 10, 20);
369 *finished = (ret == 0) ? 1 : 0;
David Wongf15da162009-04-01 04:35:10 -0300370
371 return 0;
372}
373
David Wongb80dc1c2009-06-17 01:38:10 -0300374static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 cpn,
375 u8 *locked)
David Wongf15da162009-04-01 04:35:10 -0300376{
David Wongb80dc1c2009-06-17 01:38:10 -0300377 int err = 0;
David Wongf15da162009-04-01 04:35:10 -0300378 u8 ad_fini = 0;
David Wongb80dc1c2009-06-17 01:38:10 -0300379 u8 t1, t2;
David Wongf15da162009-04-01 04:35:10 -0300380
381 if (gi == GI_945)
382 dprintk("try GI 945\n");
383 else if (gi == GI_595)
384 dprintk("try GI 595\n");
385 else if (gi == GI_420)
386 dprintk("try GI 420\n");
David Wongb80dc1c2009-06-17 01:38:10 -0300387 if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
388 lgs8gxx_read_reg(priv, 0x0C, &t1);
389 lgs8gxx_read_reg(priv, 0x18, &t2);
390 t1 &= ~(GI_MASK);
391 t1 |= gi;
392 t2 &= 0xFE;
393 t2 |= cpn ? 0x01 : 0x00;
394 lgs8gxx_write_reg(priv, 0x0C, t1);
395 lgs8gxx_write_reg(priv, 0x18, t2);
396 } else {
397 lgs8gxx_write_reg(priv, 0x04, gi);
398 }
David Wongf15da162009-04-01 04:35:10 -0300399 lgs8gxx_soft_reset(priv);
David Wongb80dc1c2009-06-17 01:38:10 -0300400 err = lgs8gxx_wait_ca_lock(priv, locked);
401 if (err || !(*locked))
402 return err;
David Wongf15da162009-04-01 04:35:10 -0300403 err = lgs8gxx_is_autodetect_finished(priv, &ad_fini);
404 if (err != 0)
405 return err;
406 if (ad_fini) {
David Wongb80dc1c2009-06-17 01:38:10 -0300407 dprintk("auto detect finished\n");
408 } else
409 *locked = 0;
David Wongf15da162009-04-01 04:35:10 -0300410
411 return 0;
412}
413
414static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv,
415 u8 *detected_param, u8 *gi)
416{
417 int i, j;
418 int err = 0;
419 u8 locked = 0, tmp_gi;
420
421 dprintk("%s\n", __func__);
422
423 lgs8gxx_set_mode_auto(priv);
David Wongb80dc1c2009-06-17 01:38:10 -0300424 if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
425 lgs8gxx_write_reg(priv, 0x67, 0xAA);
426 lgs8gxx_write_reg(priv, 0x6E, 0x3F);
427 } else {
428 /* Guard Interval */
429 lgs8gxx_write_reg(priv, 0x03, 00);
430 }
David Wongf15da162009-04-01 04:35:10 -0300431
432 for (i = 0; i < 2; i++) {
433 for (j = 0; j < 2; j++) {
434 tmp_gi = GI_945;
David Wongb80dc1c2009-06-17 01:38:10 -0300435 err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked);
David Wongf15da162009-04-01 04:35:10 -0300436 if (err)
437 goto out;
438 if (locked)
439 goto locked;
440 }
441 for (j = 0; j < 2; j++) {
442 tmp_gi = GI_420;
David Wongb80dc1c2009-06-17 01:38:10 -0300443 err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked);
David Wongf15da162009-04-01 04:35:10 -0300444 if (err)
445 goto out;
446 if (locked)
447 goto locked;
448 }
449 tmp_gi = GI_595;
David Wongb80dc1c2009-06-17 01:38:10 -0300450 err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked);
David Wongf15da162009-04-01 04:35:10 -0300451 if (err)
452 goto out;
453 if (locked)
454 goto locked;
455 }
456
457locked:
458 if ((err == 0) && (locked == 1)) {
459 u8 t;
460
David Wongb80dc1c2009-06-17 01:38:10 -0300461 if (priv->config->prod != LGS8GXX_PROD_LGS8G75) {
462 lgs8gxx_read_reg(priv, 0xA2, &t);
463 *detected_param = t;
464 } else {
465 lgs8gxx_read_reg(priv, 0x1F, &t);
466 *detected_param = t & 0x3F;
467 }
David Wongf15da162009-04-01 04:35:10 -0300468
469 if (tmp_gi == GI_945)
470 dprintk("GI 945 locked\n");
471 else if (tmp_gi == GI_595)
472 dprintk("GI 595 locked\n");
473 else if (tmp_gi == GI_420)
474 dprintk("GI 420 locked\n");
475 *gi = tmp_gi;
476 }
477 if (!locked)
478 err = -1;
479
480out:
481 return err;
482}
483
484static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv)
485{
486 s8 err;
487 u8 gi = 0x2;
488 u8 detected_param = 0;
489
490 err = lgs8gxx_auto_detect(priv, &detected_param, &gi);
491
492 if (err != 0) {
493 dprintk("lgs8gxx_auto_detect failed\n");
David Wongb80dc1c2009-06-17 01:38:10 -0300494 } else
495 dprintk("detected param = 0x%02X\n", detected_param);
David Wongf15da162009-04-01 04:35:10 -0300496
497 /* Apply detected parameters */
498 if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
499 u8 inter_leave_len = detected_param & TIM_MASK ;
David Wongb80dc1c2009-06-17 01:38:10 -0300500 /* Fix 8913 time interleaver detection bug */
501 inter_leave_len = (inter_leave_len == TIM_MIDDLE) ? 0x60 : 0x40;
David Wongf15da162009-04-01 04:35:10 -0300502 detected_param &= CF_MASK | SC_MASK | LGS_FEC_MASK;
503 detected_param |= inter_leave_len;
504 }
David Wongb80dc1c2009-06-17 01:38:10 -0300505 if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
506 u8 t;
507 lgs8gxx_read_reg(priv, 0x19, &t);
508 t &= 0x81;
509 t |= detected_param << 1;
510 lgs8gxx_write_reg(priv, 0x19, t);
511 } else {
512 lgs8gxx_write_reg(priv, 0x7D, detected_param);
513 if (priv->config->prod == LGS8GXX_PROD_LGS8913)
514 lgs8gxx_write_reg(priv, 0xC0, detected_param);
515 }
David Wongf15da162009-04-01 04:35:10 -0300516 /* lgs8gxx_soft_reset(priv); */
517
518 /* Enter manual mode */
519 lgs8gxx_set_mode_manual(priv);
520
521 switch (gi) {
522 case GI_945:
523 priv->curr_gi = 945; break;
524 case GI_595:
525 priv->curr_gi = 595; break;
526 case GI_420:
527 priv->curr_gi = 420; break;
528 default:
529 priv->curr_gi = 945; break;
530 }
531}
532
533static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv,
534 u8 serial, u8 clk_pol, u8 clk_gated)
535{
536 int ret = 0;
David Wongb80dc1c2009-06-17 01:38:10 -0300537 u8 t, reg_addr;
David Wongf15da162009-04-01 04:35:10 -0300538
David Wongb80dc1c2009-06-17 01:38:10 -0300539 reg_addr = (priv->config->prod == LGS8GXX_PROD_LGS8G75) ? 0x30 : 0xC2;
540 ret = lgs8gxx_read_reg(priv, reg_addr, &t);
David Wongf15da162009-04-01 04:35:10 -0300541 if (ret != 0)
542 return ret;
543
544 t &= 0xF8;
545 t |= serial ? TS_SERIAL : TS_PARALLEL;
546 t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL;
547 t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN;
548
David Wongb80dc1c2009-06-17 01:38:10 -0300549 ret = lgs8gxx_write_reg(priv, reg_addr, t);
David Wongf15da162009-04-01 04:35:10 -0300550 if (ret != 0)
551 return ret;
552
553 return 0;
554}
555
David Wongb80dc1c2009-06-17 01:38:10 -0300556/* A/D input peak-to-peak voltage range */
557static int lgs8g75_set_adc_vpp(struct lgs8gxx_state *priv,
558 u8 sel)
559{
560 u8 r26 = 0x73, r27 = 0x90;
561
562 if (priv->config->prod != LGS8GXX_PROD_LGS8G75)
563 return 0;
564
565 r26 |= (sel & 0x01) << 7;
566 r27 |= (sel & 0x02) >> 1;
567 lgs8gxx_write_reg(priv, 0x26, r26);
568 lgs8gxx_write_reg(priv, 0x27, r27);
569
570 return 0;
571}
David Wongf15da162009-04-01 04:35:10 -0300572
573/* LGS8913 demod frontend functions */
574
575static int lgs8913_init(struct lgs8gxx_state *priv)
576{
577 u8 t;
578
579 /* LGS8913 specific */
580 lgs8gxx_write_reg(priv, 0xc1, 0x3);
581
582 lgs8gxx_read_reg(priv, 0x7c, &t);
583 lgs8gxx_write_reg(priv, 0x7c, (t&0x8c) | 0x3);
584
585 /* LGS8913 specific */
586 lgs8gxx_read_reg(priv, 0xc3, &t);
587 lgs8gxx_write_reg(priv, 0xc3, t&0x10);
588
589
590 return 0;
591}
592
David Wongb80dc1c2009-06-17 01:38:10 -0300593static int lgs8g75_init_data(struct lgs8gxx_state *priv)
594{
Ben Hutchings76386992009-10-31 23:14:35 -0300595 const struct firmware *fw;
596 int rc;
David Wongb80dc1c2009-06-17 01:38:10 -0300597 int i;
598
Ben Hutchings76386992009-10-31 23:14:35 -0300599 rc = request_firmware(&fw, "lgs8g75.fw", &priv->i2c->dev);
600 if (rc)
601 return rc;
602
David Wongb80dc1c2009-06-17 01:38:10 -0300603 lgs8gxx_write_reg(priv, 0xC6, 0x40);
604
605 lgs8gxx_write_reg(priv, 0x3D, 0x04);
606 lgs8gxx_write_reg(priv, 0x39, 0x00);
607
608 lgs8gxx_write_reg(priv, 0x3A, 0x00);
609 lgs8gxx_write_reg(priv, 0x38, 0x00);
610 lgs8gxx_write_reg(priv, 0x3B, 0x00);
611 lgs8gxx_write_reg(priv, 0x38, 0x00);
612
Ben Hutchings76386992009-10-31 23:14:35 -0300613 for (i = 0; i < fw->size; i++) {
David Wongb80dc1c2009-06-17 01:38:10 -0300614 lgs8gxx_write_reg(priv, 0x38, 0x00);
615 lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff));
616 lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8));
Ben Hutchings76386992009-10-31 23:14:35 -0300617 lgs8gxx_write_reg(priv, 0x3C, fw->data[i]);
David Wongb80dc1c2009-06-17 01:38:10 -0300618 }
619
620 lgs8gxx_write_reg(priv, 0x38, 0x00);
621
Ben Hutchings76386992009-10-31 23:14:35 -0300622 release_firmware(fw);
David Wongb80dc1c2009-06-17 01:38:10 -0300623 return 0;
624}
625
David Wongf15da162009-04-01 04:35:10 -0300626static int lgs8gxx_init(struct dvb_frontend *fe)
627{
628 struct lgs8gxx_state *priv =
629 (struct lgs8gxx_state *)fe->demodulator_priv;
630 const struct lgs8gxx_config *config = priv->config;
631 u8 data = 0;
632 s8 err;
633 dprintk("%s\n", __func__);
634
635 lgs8gxx_read_reg(priv, 0, &data);
636 dprintk("reg 0 = 0x%02X\n", data);
637
David Wongb80dc1c2009-06-17 01:38:10 -0300638 if (config->prod == LGS8GXX_PROD_LGS8G75)
639 lgs8g75_set_adc_vpp(priv, config->adc_vpp);
640
David Wongf15da162009-04-01 04:35:10 -0300641 /* Setup MPEG output format */
642 err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts,
643 config->ts_clk_pol,
644 config->ts_clk_gated);
645 if (err != 0)
646 return -EIO;
647
648 if (config->prod == LGS8GXX_PROD_LGS8913)
649 lgs8913_init(priv);
650 lgs8gxx_set_if_freq(priv, priv->config->if_freq);
David Wongb80dc1c2009-06-17 01:38:10 -0300651 lgs8gxx_set_ad_mode(priv);
David Wongf15da162009-04-01 04:35:10 -0300652
653 return 0;
654}
655
656static void lgs8gxx_release(struct dvb_frontend *fe)
657{
658 struct lgs8gxx_state *state = fe->demodulator_priv;
659 dprintk("%s\n", __func__);
660
661 kfree(state);
662}
663
664
lawrence rust2e4e98e2010-08-25 09:50:20 -0300665static int lgs8gxx_write(struct dvb_frontend *fe, const u8 buf[], int len)
David Wongf15da162009-04-01 04:35:10 -0300666{
667 struct lgs8gxx_state *priv = fe->demodulator_priv;
668
669 if (len != 2)
670 return -EINVAL;
671
672 return lgs8gxx_write_reg(priv, buf[0], buf[1]);
673}
674
675static int lgs8gxx_set_fe(struct dvb_frontend *fe,
676 struct dvb_frontend_parameters *fe_params)
677{
678 struct lgs8gxx_state *priv = fe->demodulator_priv;
679
680 dprintk("%s\n", __func__);
681
682 /* set frequency */
683 if (fe->ops.tuner_ops.set_params) {
684 fe->ops.tuner_ops.set_params(fe, fe_params);
685 if (fe->ops.i2c_gate_ctrl)
686 fe->ops.i2c_gate_ctrl(fe, 0);
687 }
688
689 /* start auto lock */
690 lgs8gxx_auto_lock(priv);
691
692 msleep(10);
693
694 return 0;
695}
696
697static int lgs8gxx_get_fe(struct dvb_frontend *fe,
698 struct dvb_frontend_parameters *fe_params)
699{
David Wongf15da162009-04-01 04:35:10 -0300700 dprintk("%s\n", __func__);
701
702 /* TODO: get real readings from device */
703 /* inversion status */
704 fe_params->inversion = INVERSION_OFF;
705
706 /* bandwidth */
707 fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
708
David Wongf15da162009-04-01 04:35:10 -0300709 fe_params->u.ofdm.code_rate_HP = FEC_AUTO;
710 fe_params->u.ofdm.code_rate_LP = FEC_AUTO;
711
David Wongb80dc1c2009-06-17 01:38:10 -0300712 fe_params->u.ofdm.constellation = QAM_AUTO;
David Wongf15da162009-04-01 04:35:10 -0300713
714 /* transmission mode */
715 fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO;
716
717 /* guard interval */
718 fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
719
720 /* hierarchy */
721 fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE;
722
723 return 0;
724}
725
726static
727int lgs8gxx_get_tune_settings(struct dvb_frontend *fe,
728 struct dvb_frontend_tune_settings *fesettings)
729{
730 /* FIXME: copy from tda1004x.c */
731 fesettings->min_delay_ms = 800;
732 fesettings->step_size = 0;
733 fesettings->max_drift = 0;
734 return 0;
735}
736
737static int lgs8gxx_read_status(struct dvb_frontend *fe, fe_status_t *fe_status)
738{
739 struct lgs8gxx_state *priv = fe->demodulator_priv;
740 s8 ret;
David Wongb80dc1c2009-06-17 01:38:10 -0300741 u8 t, locked = 0;
David Wongf15da162009-04-01 04:35:10 -0300742
743 dprintk("%s\n", __func__);
David Wongb80dc1c2009-06-17 01:38:10 -0300744 *fe_status = 0;
745
746 lgs8gxx_get_afc_phase(priv);
747 lgs8gxx_is_locked(priv, &locked);
748 if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
749 if (locked)
750 *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
751 FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
752 return 0;
753 }
David Wongf15da162009-04-01 04:35:10 -0300754
755 ret = lgs8gxx_read_reg(priv, 0x4B, &t);
756 if (ret != 0)
757 return -EIO;
758
759 dprintk("Reg 0x4B: 0x%02X\n", t);
760
761 *fe_status = 0;
762 if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
763 if ((t & 0x40) == 0x40)
764 *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
765 if ((t & 0x80) == 0x80)
766 *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC |
767 FE_HAS_LOCK;
768 } else {
769 if ((t & 0x80) == 0x80)
770 *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
771 FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
772 }
773
774 /* success */
775 dprintk("%s: fe_status=0x%x\n", __func__, *fe_status);
776 return 0;
777}
778
779static int lgs8gxx_read_signal_agc(struct lgs8gxx_state *priv, u16 *signal)
780{
781 u16 v;
782 u8 agc_lvl[2], cat;
783
784 dprintk("%s()\n", __func__);
785 lgs8gxx_read_reg(priv, 0x3F, &agc_lvl[0]);
786 lgs8gxx_read_reg(priv, 0x3E, &agc_lvl[1]);
787
788 v = agc_lvl[0];
789 v <<= 8;
790 v |= agc_lvl[1];
791
792 dprintk("agc_lvl: 0x%04X\n", v);
793
794 if (v < 0x100)
795 cat = 0;
796 else if (v < 0x190)
797 cat = 5;
798 else if (v < 0x2A8)
799 cat = 4;
800 else if (v < 0x381)
801 cat = 3;
802 else if (v < 0x400)
803 cat = 2;
804 else if (v == 0x400)
805 cat = 1;
806 else
807 cat = 0;
808
David Wong6762d952009-06-11 09:39:04 -0300809 *signal = cat * 65535 / 5;
David Wongf15da162009-04-01 04:35:10 -0300810
811 return 0;
812}
813
814static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal)
815{
816 u8 t; s8 ret;
817 s16 max_strength = 0;
818 u8 str;
819 u16 i, gi = priv->curr_gi;
820
821 dprintk("%s\n", __func__);
822
823 ret = lgs8gxx_read_reg(priv, 0x4B, &t);
824 if (ret != 0)
825 return -EIO;
826
827 if (fake_signal_str) {
828 if ((t & 0xC0) == 0xC0) {
David Wong6762d952009-06-11 09:39:04 -0300829 dprintk("Fake signal strength\n");
830 *signal = 0x7FFF;
David Wongf15da162009-04-01 04:35:10 -0300831 } else
832 *signal = 0;
833 return 0;
834 }
835
836 dprintk("gi = %d\n", gi);
837 for (i = 0; i < gi; i++) {
838
839 if ((i & 0xFF) == 0)
840 lgs8gxx_write_reg(priv, 0x84, 0x03 & (i >> 8));
841 lgs8gxx_write_reg(priv, 0x83, i & 0xFF);
842
843 lgs8gxx_read_reg(priv, 0x94, &str);
844 if (max_strength < str)
845 max_strength = str;
846 }
847
848 *signal = max_strength;
849 dprintk("%s: signal=0x%02X\n", __func__, *signal);
850
851 lgs8gxx_read_reg(priv, 0x95, &t);
852 dprintk("%s: AVG Noise=0x%02X\n", __func__, t);
853
854 return 0;
855}
856
David Wongb80dc1c2009-06-17 01:38:10 -0300857static int lgs8g75_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal)
858{
859 u8 t;
860 s16 v = 0;
861
862 dprintk("%s\n", __func__);
863
864 lgs8gxx_read_reg(priv, 0xB1, &t);
865 v |= t;
866 v <<= 8;
867 lgs8gxx_read_reg(priv, 0xB0, &t);
868 v |= t;
869
870 *signal = v;
871 dprintk("%s: signal=0x%02X\n", __func__, *signal);
872
873 return 0;
874}
875
David Wongf15da162009-04-01 04:35:10 -0300876static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal)
877{
878 struct lgs8gxx_state *priv = fe->demodulator_priv;
879
880 if (priv->config->prod == LGS8GXX_PROD_LGS8913)
881 return lgs8913_read_signal_strength(priv, signal);
David Wongb80dc1c2009-06-17 01:38:10 -0300882 else if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
883 return lgs8g75_read_signal_strength(priv, signal);
David Wongf15da162009-04-01 04:35:10 -0300884 else
885 return lgs8gxx_read_signal_agc(priv, signal);
886}
887
888static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr)
889{
890 struct lgs8gxx_state *priv = fe->demodulator_priv;
891 u8 t;
892 *snr = 0;
893
David Wongb80dc1c2009-06-17 01:38:10 -0300894 if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
895 lgs8gxx_read_reg(priv, 0x34, &t);
896 else
897 lgs8gxx_read_reg(priv, 0x95, &t);
David Wongf15da162009-04-01 04:35:10 -0300898 dprintk("AVG Noise=0x%02X\n", t);
899 *snr = 256 - t;
900 *snr <<= 8;
901 dprintk("snr=0x%x\n", *snr);
902
903 return 0;
904}
905
906static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
907{
908 *ucblocks = 0;
909 dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks);
910 return 0;
911}
912
David Wongb80dc1c2009-06-17 01:38:10 -0300913static void packet_counter_start(struct lgs8gxx_state *priv)
914{
915 u8 orig, t;
916
917 if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
918 lgs8gxx_read_reg(priv, 0x30, &orig);
919 orig &= 0xE7;
920 t = orig | 0x10;
921 lgs8gxx_write_reg(priv, 0x30, t);
922 t = orig | 0x18;
923 lgs8gxx_write_reg(priv, 0x30, t);
924 t = orig | 0x10;
925 lgs8gxx_write_reg(priv, 0x30, t);
926 } else {
927 lgs8gxx_write_reg(priv, 0xC6, 0x01);
928 lgs8gxx_write_reg(priv, 0xC6, 0x41);
929 lgs8gxx_write_reg(priv, 0xC6, 0x01);
930 }
931}
932
933static void packet_counter_stop(struct lgs8gxx_state *priv)
934{
935 u8 t;
936
937 if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
938 lgs8gxx_read_reg(priv, 0x30, &t);
939 t &= 0xE7;
940 lgs8gxx_write_reg(priv, 0x30, t);
941 } else {
942 lgs8gxx_write_reg(priv, 0xC6, 0x81);
943 }
944}
945
David Wongf15da162009-04-01 04:35:10 -0300946static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber)
947{
948 struct lgs8gxx_state *priv = fe->demodulator_priv;
David Wongb80dc1c2009-06-17 01:38:10 -0300949 u8 reg_err, reg_total, t;
950 u32 total_cnt = 0, err_cnt = 0;
951 int i;
David Wongf15da162009-04-01 04:35:10 -0300952
953 dprintk("%s\n", __func__);
954
David Wongb80dc1c2009-06-17 01:38:10 -0300955 packet_counter_start(priv);
David Wongf15da162009-04-01 04:35:10 -0300956 msleep(200);
David Wongb80dc1c2009-06-17 01:38:10 -0300957 packet_counter_stop(priv);
David Wongf15da162009-04-01 04:35:10 -0300958
David Wongb80dc1c2009-06-17 01:38:10 -0300959 if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
960 reg_total = 0x28; reg_err = 0x2C;
961 } else {
962 reg_total = 0xD0; reg_err = 0xD4;
963 }
964
965 for (i = 0; i < 4; i++) {
966 total_cnt <<= 8;
967 lgs8gxx_read_reg(priv, reg_total+3-i, &t);
968 total_cnt |= t;
969 }
970 for (i = 0; i < 4; i++) {
971 err_cnt <<= 8;
972 lgs8gxx_read_reg(priv, reg_err+3-i, &t);
973 err_cnt |= t;
974 }
David Wongf15da162009-04-01 04:35:10 -0300975 dprintk("error=%d total=%d\n", err_cnt, total_cnt);
976
977 if (total_cnt == 0)
978 *ber = 0;
979 else
980 *ber = err_cnt * 100 / total_cnt;
981
982 dprintk("%s: ber=0x%x\n", __func__, *ber);
983 return 0;
984}
985
986static int lgs8gxx_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
987{
988 struct lgs8gxx_state *priv = fe->demodulator_priv;
989
990 if (priv->config->tuner_address == 0)
991 return 0;
992 if (enable) {
993 u8 v = 0x80 | priv->config->tuner_address;
994 return lgs8gxx_write_reg(priv, 0x01, v);
995 }
996 return lgs8gxx_write_reg(priv, 0x01, 0);
997}
998
999static struct dvb_frontend_ops lgs8gxx_ops = {
1000 .info = {
1001 .name = "Legend Silicon LGS8913/LGS8GXX DMB-TH",
1002 .type = FE_OFDM,
1003 .frequency_min = 474000000,
1004 .frequency_max = 858000000,
1005 .frequency_stepsize = 10000,
1006 .caps =
1007 FE_CAN_FEC_AUTO |
1008 FE_CAN_QAM_AUTO |
1009 FE_CAN_TRANSMISSION_MODE_AUTO |
1010 FE_CAN_GUARD_INTERVAL_AUTO
1011 },
1012
1013 .release = lgs8gxx_release,
1014
1015 .init = lgs8gxx_init,
1016 .write = lgs8gxx_write,
1017 .i2c_gate_ctrl = lgs8gxx_i2c_gate_ctrl,
1018
1019 .set_frontend = lgs8gxx_set_fe,
1020 .get_frontend = lgs8gxx_get_fe,
1021 .get_tune_settings = lgs8gxx_get_tune_settings,
1022
1023 .read_status = lgs8gxx_read_status,
1024 .read_ber = lgs8gxx_read_ber,
1025 .read_signal_strength = lgs8gxx_read_signal_strength,
1026 .read_snr = lgs8gxx_read_snr,
1027 .read_ucblocks = lgs8gxx_read_ucblocks,
1028};
1029
1030struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config,
1031 struct i2c_adapter *i2c)
1032{
1033 struct lgs8gxx_state *priv = NULL;
1034 u8 data = 0;
1035
1036 dprintk("%s()\n", __func__);
1037
1038 if (config == NULL || i2c == NULL)
1039 return NULL;
1040
1041 priv = kzalloc(sizeof(struct lgs8gxx_state), GFP_KERNEL);
1042 if (priv == NULL)
1043 goto error_out;
1044
1045 priv->config = config;
1046 priv->i2c = i2c;
1047
1048 /* check if the demod is there */
1049 if (lgs8gxx_read_reg(priv, 0, &data) != 0) {
1050 dprintk("%s lgs8gxx not found at i2c addr 0x%02X\n",
1051 __func__, priv->config->demod_address);
1052 goto error_out;
1053 }
1054
1055 lgs8gxx_read_reg(priv, 1, &data);
1056
1057 memcpy(&priv->frontend.ops, &lgs8gxx_ops,
1058 sizeof(struct dvb_frontend_ops));
1059 priv->frontend.demodulator_priv = priv;
1060
David Wongb80dc1c2009-06-17 01:38:10 -03001061 if (config->prod == LGS8GXX_PROD_LGS8G75)
1062 lgs8g75_init_data(priv);
1063
David Wongf15da162009-04-01 04:35:10 -03001064 return &priv->frontend;
1065
1066error_out:
1067 dprintk("%s() error_out\n", __func__);
1068 kfree(priv);
1069 return NULL;
1070
1071}
1072EXPORT_SYMBOL(lgs8gxx_attach);
1073
1074MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver");
1075MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
1076MODULE_LICENSE("GPL");