diff options
author | David Wong | 2009-06-17 01:38:10 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab | 2009-09-12 12:17:38 -0300 |
commit | b80dc1c673f5ff79805215eb94ff008e634e4b2d (patch) | |
tree | 80a5849fadf39d81837a8e3ad5443b58032b8382 | |
parent | a0d64456f765297782d34750cf4dc862f2a8bce0 (diff) |
V4L/DVB (12271): lgs8gxx: add lgs8g75 support
lgs8gxx: add lgs8g75 demodulator support
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/lgs8gxx.c | 484 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/lgs8gxx.h | 11 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/lgs8gxx_priv.h | 12 |
3 files changed, 401 insertions, 106 deletions
diff --git a/drivers/media/dvb/frontends/lgs8gxx.c b/drivers/media/dvb/frontends/lgs8gxx.c index fde27645bbed..eabcadc425d5 100644 --- a/drivers/media/dvb/frontends/lgs8gxx.c +++ b/drivers/media/dvb/frontends/lgs8gxx.c @@ -1,9 +1,9 @@ /* - * Support for Legend Silicon DMB-TH demodulator - * LGS8913, LGS8GL5 + * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator + * LGS8913, LGS8GL5, LGS8G75 * experimental support LGS8G42, LGS8G52 * - * Copyright (C) 2007,2008 David T.L. Wong <davidtlwong@gmail.com> + * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com> * Copyright (C) 2008 Sirius International (Hong Kong) Limited * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5) * @@ -46,6 +46,42 @@ module_param(fake_signal_str, int, 0644); MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913." "Signal strength calculation is slow.(default:on)."); +static const u8 lgs8g75_initdat[] = { + 0x01, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0xF5, 0xA8, 0xF5, 0xB8, 0xF5, 0x88, 0xF5, + 0x89, 0xF5, 0x87, 0x75, 0xD0, 0x00, 0x11, 0x50, + 0x11, 0x50, 0xF4, 0xF5, 0x80, 0xF5, 0x90, 0xF5, + 0xA0, 0xF5, 0xB0, 0x75, 0x81, 0x30, 0x80, 0x01, + 0x32, 0x90, 0x80, 0x12, 0x74, 0xFF, 0xF0, 0x90, + 0x80, 0x13, 0x74, 0x1F, 0xF0, 0x90, 0x80, 0x23, + 0x74, 0x01, 0xF0, 0x90, 0x80, 0x22, 0xF0, 0x90, + 0x00, 0x48, 0x74, 0x00, 0xF0, 0x90, 0x80, 0x4D, + 0x74, 0x05, 0xF0, 0x90, 0x80, 0x09, 0xE0, 0x60, + 0x21, 0x12, 0x00, 0xDD, 0x14, 0x60, 0x1B, 0x12, + 0x00, 0xDD, 0x14, 0x60, 0x15, 0x12, 0x00, 0xDD, + 0x14, 0x60, 0x0F, 0x12, 0x00, 0xDD, 0x14, 0x60, + 0x09, 0x12, 0x00, 0xDD, 0x14, 0x60, 0x03, 0x12, + 0x00, 0xDD, 0x90, 0x80, 0x42, 0xE0, 0x60, 0x0B, + 0x14, 0x60, 0x0C, 0x14, 0x60, 0x0D, 0x14, 0x60, + 0x0E, 0x01, 0xB3, 0x74, 0x04, 0x01, 0xB9, 0x74, + 0x05, 0x01, 0xB9, 0x74, 0x07, 0x01, 0xB9, 0x74, + 0x0A, 0xC0, 0xE0, 0x74, 0xC8, 0x12, 0x00, 0xE2, + 0xD0, 0xE0, 0x14, 0x70, 0xF4, 0x90, 0x80, 0x09, + 0xE0, 0x70, 0xAE, 0x12, 0x00, 0xF6, 0x12, 0x00, + 0xFE, 0x90, 0x00, 0x48, 0xE0, 0x04, 0xF0, 0x90, + 0x80, 0x4E, 0xF0, 0x01, 0x73, 0x90, 0x80, 0x08, + 0xF0, 0x22, 0xF8, 0x7A, 0x0C, 0x79, 0xFD, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, + 0xF6, 0xDA, 0xF2, 0xD8, 0xEE, 0x22, 0x90, 0x80, + 0x65, 0xE0, 0x54, 0xFD, 0xF0, 0x22, 0x90, 0x80, + 0x65, 0xE0, 0x44, 0xC2, 0xF0, 0x22 +}; + /* LGS8GXX internal helper functions */ static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data) @@ -55,7 +91,7 @@ static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data) struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; msg.addr = priv->config->demod_address; - if (reg >= 0xC0) + if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) msg.addr += 0x02; if (debug >= 2) @@ -84,7 +120,7 @@ static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data) }; dev_addr = priv->config->demod_address; - if (reg >= 0xC0) + if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) dev_addr += 0x02; msg[1].addr = msg[0].addr = dev_addr; @@ -112,19 +148,36 @@ static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv) return 0; } +static int wait_reg_mask(struct lgs8gxx_state *priv, u8 reg, u8 mask, + u8 val, u8 delay, u8 tries) +{ + u8 t; + int i; + + for (i = 0; i < tries; i++) { + lgs8gxx_read_reg(priv, reg, &t); + + if ((t & mask) == val) + return 0; + msleep(delay); + } + + return 1; +} + static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv) { const struct lgs8gxx_config *config = priv->config; u8 if_conf; - if_conf = 0x10; /* AGC output on; */ + if_conf = 0x10; /* AGC output on, RF_AGC output off; */ if_conf |= ((config->ext_adc) ? 0x80 : 0x00) | ((config->if_neg_center) ? 0x04 : 0x00) | ((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */ - ((config->ext_adc && config->adc_signed) ? 0x02 : 0x00) | - ((config->ext_adc && config->if_neg_edge) ? 0x01 : 0x00); + ((config->adc_signed) ? 0x02 : 0x00) | + ((config->if_neg_edge) ? 0x01 : 0x00); if (config->ext_adc && (config->prod == LGS8GXX_PROD_LGS8G52)) { @@ -157,39 +210,82 @@ static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/) } dprintk("AFC_INIT_FREQ = 0x%08X\n", v32); - lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32)); - lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8)); - lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16)); - lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24)); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32)); + lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8)); + lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 16)); + lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 24)); + } else { + lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32)); + lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8)); + lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16)); + lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24)); + } + + return 0; +} + +static int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv) +{ + u64 val; + u32 v32 = 0; + u8 reg_addr, t; + int i; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + reg_addr = 0x23; + else + reg_addr = 0x48; + + for (i = 0; i < 4; i++) { + lgs8gxx_read_reg(priv, reg_addr, &t); + v32 <<= 8; + v32 |= t; + reg_addr--; + } + val = v32; + val *= priv->config->if_clk_freq; + val /= (u64)1 << 32; + dprintk("AFC = %u kHz\n", (u32)val); return 0; } static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv) { u8 t; + u8 prod = priv->config->prod; - if (priv->config->prod == LGS8GXX_PROD_LGS8913) + if (prod == LGS8GXX_PROD_LGS8913) lgs8gxx_write_reg(priv, 0xC6, 0x01); - lgs8gxx_read_reg(priv, 0x7E, &t); - lgs8gxx_write_reg(priv, 0x7E, t | 0x01); - - /* clear FEC self reset */ - lgs8gxx_read_reg(priv, 0xC5, &t); - lgs8gxx_write_reg(priv, 0xC5, t & 0xE0); + if (prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x0C, &t); + t &= (~0x04); + lgs8gxx_write_reg(priv, 0x0C, t | 0x80); + lgs8gxx_write_reg(priv, 0x39, 0x00); + lgs8gxx_write_reg(priv, 0x3D, 0x04); + } else if (prod == LGS8GXX_PROD_LGS8913 || + prod == LGS8GXX_PROD_LGS8GL5 || + prod == LGS8GXX_PROD_LGS8G42 || + prod == LGS8GXX_PROD_LGS8G52 || + prod == LGS8GXX_PROD_LGS8G54) { + lgs8gxx_read_reg(priv, 0x7E, &t); + lgs8gxx_write_reg(priv, 0x7E, t | 0x01); + + /* clear FEC self reset */ + lgs8gxx_read_reg(priv, 0xC5, &t); + lgs8gxx_write_reg(priv, 0xC5, t & 0xE0); + } - if (priv->config->prod == LGS8GXX_PROD_LGS8913) { + if (prod == LGS8GXX_PROD_LGS8913) { /* FEC auto detect */ lgs8gxx_write_reg(priv, 0xC1, 0x03); lgs8gxx_read_reg(priv, 0x7C, &t); t = (t & 0x8C) | 0x03; lgs8gxx_write_reg(priv, 0x7C, t); - } - - if (priv->config->prod == LGS8GXX_PROD_LGS8913) { /* BER test mode */ lgs8gxx_read_reg(priv, 0xC3, &t); t = (t & 0xEF) | 0x10; @@ -207,6 +303,32 @@ static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv) int ret = 0; u8 t; + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + u8 t2; + lgs8gxx_read_reg(priv, 0x0C, &t); + t &= (~0x80); + lgs8gxx_write_reg(priv, 0x0C, t); + + lgs8gxx_read_reg(priv, 0x0C, &t); + lgs8gxx_read_reg(priv, 0x19, &t2); + + if (((t&0x03) == 0x01) && (t2&0x01)) { + lgs8gxx_write_reg(priv, 0x6E, 0x05); + lgs8gxx_write_reg(priv, 0x39, 0x02); + lgs8gxx_write_reg(priv, 0x39, 0x03); + lgs8gxx_write_reg(priv, 0x3D, 0x05); + lgs8gxx_write_reg(priv, 0x3E, 0x28); + lgs8gxx_write_reg(priv, 0x53, 0x80); + } else { + lgs8gxx_write_reg(priv, 0x6E, 0x3F); + lgs8gxx_write_reg(priv, 0x39, 0x00); + lgs8gxx_write_reg(priv, 0x3D, 0x04); + } + + lgs8gxx_soft_reset(priv); + return 0; + } + /* turn off auto-detect; manual settings */ lgs8gxx_write_reg(priv, 0x7E, 0); if (priv->config->prod == LGS8GXX_PROD_LGS8913) @@ -226,11 +348,39 @@ static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked) int ret = 0; u8 t; - ret = lgs8gxx_read_reg(priv, 0x4B, &t); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + ret = lgs8gxx_read_reg(priv, 0x13, &t); + else + ret = lgs8gxx_read_reg(priv, 0x4B, &t); if (ret != 0) return ret; - *locked = ((t & 0xC0) == 0xC0) ? 1 : 0; + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + *locked = ((t & 0x80) == 0x80) ? 1 : 0; + else + *locked = ((t & 0xC0) == 0xC0) ? 1 : 0; + return 0; +} + +/* Wait for Code Acquisition Lock */ +static int lgs8gxx_wait_ca_lock(struct lgs8gxx_state *priv, u8 *locked) +{ + int ret = 0; + u8 reg, mask, val; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + reg = 0x13; + mask = 0x80; + val = 0x80; + } else { + reg = 0x4B; + mask = 0xC0; + val = 0xC0; + } + + ret = wait_reg_mask(priv, reg, mask, val, 50, 40); + *locked = (ret == 0) ? 1 : 0; + return 0; } @@ -238,21 +388,30 @@ static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv, u8 *finished) { int ret = 0; - u8 t; + u8 reg, mask, val; - ret = lgs8gxx_read_reg(priv, 0xA4, &t); - if (ret != 0) - return ret; + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + reg = 0x1f; + mask = 0xC0; + val = 0x80; + } else { + reg = 0xA4; + mask = 0x03; + val = 0x01; + } - *finished = ((t & 0x3) == 0x1) ? 1 : 0; + ret = wait_reg_mask(priv, reg, mask, val, 10, 20); + *finished = (ret == 0) ? 1 : 0; return 0; } -static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 *locked) +static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 cpn, + u8 *locked) { - int err; + int err = 0; u8 ad_fini = 0; + u8 t1, t2; if (gi == GI_945) dprintk("try GI 945\n"); @@ -260,17 +419,29 @@ static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 *locked) dprintk("try GI 595\n"); else if (gi == GI_420) dprintk("try GI 420\n"); - lgs8gxx_write_reg(priv, 0x04, gi); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x0C, &t1); + lgs8gxx_read_reg(priv, 0x18, &t2); + t1 &= ~(GI_MASK); + t1 |= gi; + t2 &= 0xFE; + t2 |= cpn ? 0x01 : 0x00; + lgs8gxx_write_reg(priv, 0x0C, t1); + lgs8gxx_write_reg(priv, 0x18, t2); + } else { + lgs8gxx_write_reg(priv, 0x04, gi); + } lgs8gxx_soft_reset(priv); - msleep(50); + err = lgs8gxx_wait_ca_lock(priv, locked); + if (err || !(*locked)) + return err; err = lgs8gxx_is_autodetect_finished(priv, &ad_fini); if (err != 0) return err; if (ad_fini) { - err = lgs8gxx_is_locked(priv, locked); - if (err != 0) - return err; - } + dprintk("auto detect finished\n"); + } else + *locked = 0; return 0; } @@ -285,13 +456,18 @@ static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv, dprintk("%s\n", __func__); lgs8gxx_set_mode_auto(priv); - /* Guard Interval */ - lgs8gxx_write_reg(priv, 0x03, 00); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_write_reg(priv, 0x67, 0xAA); + lgs8gxx_write_reg(priv, 0x6E, 0x3F); + } else { + /* Guard Interval */ + lgs8gxx_write_reg(priv, 0x03, 00); + } for (i = 0; i < 2; i++) { for (j = 0; j < 2; j++) { tmp_gi = GI_945; - err = lgs8gxx_autolock_gi(priv, GI_945, &locked); + err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked); if (err) goto out; if (locked) @@ -299,14 +475,14 @@ static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv, } for (j = 0; j < 2; j++) { tmp_gi = GI_420; - err = lgs8gxx_autolock_gi(priv, GI_420, &locked); + err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked); if (err) goto out; if (locked) goto locked; } tmp_gi = GI_595; - err = lgs8gxx_autolock_gi(priv, GI_595, &locked); + err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked); if (err) goto out; if (locked) @@ -317,8 +493,13 @@ locked: if ((err == 0) && (locked == 1)) { u8 t; - lgs8gxx_read_reg(priv, 0xA2, &t); - *detected_param = t; + if (priv->config->prod != LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0xA2, &t); + *detected_param = t; + } else { + lgs8gxx_read_reg(priv, 0x1F, &t); + *detected_param = t & 0x3F; + } if (tmp_gi == GI_945) dprintk("GI 945 locked\n"); @@ -345,18 +526,28 @@ static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv) if (err != 0) { dprintk("lgs8gxx_auto_detect failed\n"); - } + } else + dprintk("detected param = 0x%02X\n", detected_param); /* Apply detected parameters */ if (priv->config->prod == LGS8GXX_PROD_LGS8913) { u8 inter_leave_len = detected_param & TIM_MASK ; - inter_leave_len = (inter_leave_len == TIM_LONG) ? 0x60 : 0x40; + /* Fix 8913 time interleaver detection bug */ + inter_leave_len = (inter_leave_len == TIM_MIDDLE) ? 0x60 : 0x40; detected_param &= CF_MASK | SC_MASK | LGS_FEC_MASK; detected_param |= inter_leave_len; } - lgs8gxx_write_reg(priv, 0x7D, detected_param); - if (priv->config->prod == LGS8GXX_PROD_LGS8913) - lgs8gxx_write_reg(priv, 0xC0, detected_param); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + u8 t; + lgs8gxx_read_reg(priv, 0x19, &t); + t &= 0x81; + t |= detected_param << 1; + lgs8gxx_write_reg(priv, 0x19, t); + } else { + lgs8gxx_write_reg(priv, 0x7D, detected_param); + if (priv->config->prod == LGS8GXX_PROD_LGS8913) + lgs8gxx_write_reg(priv, 0xC0, detected_param); + } /* lgs8gxx_soft_reset(priv); */ /* Enter manual mode */ @@ -378,9 +569,10 @@ static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv, u8 serial, u8 clk_pol, u8 clk_gated) { int ret = 0; - u8 t; + u8 t, reg_addr; - ret = lgs8gxx_read_reg(priv, 0xC2, &t); + reg_addr = (priv->config->prod == LGS8GXX_PROD_LGS8G75) ? 0x30 : 0xC2; + ret = lgs8gxx_read_reg(priv, reg_addr, &t); if (ret != 0) return ret; @@ -389,13 +581,29 @@ static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv, t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL; t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN; - ret = lgs8gxx_write_reg(priv, 0xC2, t); + ret = lgs8gxx_write_reg(priv, reg_addr, t); if (ret != 0) return ret; return 0; } +/* A/D input peak-to-peak voltage range */ +static int lgs8g75_set_adc_vpp(struct lgs8gxx_state *priv, + u8 sel) +{ + u8 r26 = 0x73, r27 = 0x90; + + if (priv->config->prod != LGS8GXX_PROD_LGS8G75) + return 0; + + r26 |= (sel & 0x01) << 7; + r27 |= (sel & 0x02) >> 1; + lgs8gxx_write_reg(priv, 0x26, r26); + lgs8gxx_write_reg(priv, 0x27, r27); + + return 0; +} /* LGS8913 demod frontend functions */ @@ -417,6 +625,34 @@ static int lgs8913_init(struct lgs8gxx_state *priv) return 0; } +static int lgs8g75_init_data(struct lgs8gxx_state *priv) +{ + const u8 *p = lgs8g75_initdat; + int i; + + lgs8gxx_write_reg(priv, 0xC6, 0x40); + + lgs8gxx_write_reg(priv, 0x3D, 0x04); + lgs8gxx_write_reg(priv, 0x39, 0x00); + + lgs8gxx_write_reg(priv, 0x3A, 0x00); + lgs8gxx_write_reg(priv, 0x38, 0x00); + lgs8gxx_write_reg(priv, 0x3B, 0x00); + lgs8gxx_write_reg(priv, 0x38, 0x00); + + for (i = 0; i < sizeof(lgs8g75_initdat); i++) { + lgs8gxx_write_reg(priv, 0x38, 0x00); + lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff)); + lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8)); + lgs8gxx_write_reg(priv, 0x3C, *p); + p++; + } + + lgs8gxx_write_reg(priv, 0x38, 0x00); + + return 0; +} + static int lgs8gxx_init(struct dvb_frontend *fe) { struct lgs8gxx_state *priv = @@ -429,6 +665,9 @@ static int lgs8gxx_init(struct dvb_frontend *fe) lgs8gxx_read_reg(priv, 0, &data); dprintk("reg 0 = 0x%02X\n", data); + if (config->prod == LGS8GXX_PROD_LGS8G75) + lgs8g75_set_adc_vpp(priv, config->adc_vpp); + /* Setup MPEG output format */ err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts, config->ts_clk_pol, @@ -439,8 +678,7 @@ static int lgs8gxx_init(struct dvb_frontend *fe) if (config->prod == LGS8GXX_PROD_LGS8913) lgs8913_init(priv); lgs8gxx_set_if_freq(priv, priv->config->if_freq); - if (config->prod != LGS8GXX_PROD_LGS8913) - lgs8gxx_set_ad_mode(priv); + lgs8gxx_set_ad_mode(priv); return 0; } @@ -489,9 +727,6 @@ static int lgs8gxx_set_fe(struct dvb_frontend *fe, static int lgs8gxx_get_fe(struct dvb_frontend *fe, struct dvb_frontend_parameters *fe_params) { - struct lgs8gxx_state *priv = fe->demodulator_priv; - u8 t; - dprintk("%s\n", __func__); /* TODO: get real readings from device */ @@ -501,29 +736,10 @@ static int lgs8gxx_get_fe(struct dvb_frontend *fe, /* bandwidth */ fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; - - lgs8gxx_read_reg(priv, 0x7D, &t); fe_params->u.ofdm.code_rate_HP = FEC_AUTO; fe_params->u.ofdm.code_rate_LP = FEC_AUTO; - /* constellation */ - switch (t & SC_MASK) { - case SC_QAM64: - fe_params->u.ofdm.constellation = QAM_64; - break; - case SC_QAM32: - fe_params->u.ofdm.constellation = QAM_32; - break; - case SC_QAM16: - fe_params->u.ofdm.constellation = QAM_16; - break; - case SC_QAM4: - case SC_QAM4NR: - fe_params->u.ofdm.constellation = QPSK; - break; - default: - fe_params->u.ofdm.constellation = QAM_64; - } + fe_params->u.ofdm.constellation = QAM_AUTO; /* transmission mode */ fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; @@ -552,9 +768,19 @@ static int lgs8gxx_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) { struct lgs8gxx_state *priv = fe->demodulator_priv; s8 ret; - u8 t; + u8 t, locked = 0; dprintk("%s\n", __func__); + *fe_status = 0; + + lgs8gxx_get_afc_phase(priv); + lgs8gxx_is_locked(priv, &locked); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + if (locked) + *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + return 0; + } ret = lgs8gxx_read_reg(priv, 0x4B, &t); if (ret != 0) @@ -658,12 +884,33 @@ static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) return 0; } +static int lgs8g75_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) +{ + u8 t; + s16 v = 0; + + dprintk("%s\n", __func__); + + lgs8gxx_read_reg(priv, 0xB1, &t); + v |= t; + v <<= 8; + lgs8gxx_read_reg(priv, 0xB0, &t); + v |= t; + + *signal = v; + dprintk("%s: signal=0x%02X\n", __func__, *signal); + + return 0; +} + static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal) { struct lgs8gxx_state *priv = fe->demodulator_priv; if (priv->config->prod == LGS8GXX_PROD_LGS8913) return lgs8913_read_signal_strength(priv, signal); + else if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + return lgs8g75_read_signal_strength(priv, signal); else return lgs8gxx_read_signal_agc(priv, signal); } @@ -674,7 +921,10 @@ static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr) u8 t; *snr = 0; - lgs8gxx_read_reg(priv, 0x95, &t); + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) + lgs8gxx_read_reg(priv, 0x34, &t); + else + lgs8gxx_read_reg(priv, 0x95, &t); dprintk("AVG Noise=0x%02X\n", t); *snr = 256 - t; *snr <<= 8; @@ -690,31 +940,68 @@ static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) return 0; } +static void packet_counter_start(struct lgs8gxx_state *priv) +{ + u8 orig, t; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x30, &orig); + orig &= 0xE7; + t = orig | 0x10; + lgs8gxx_write_reg(priv, 0x30, t); + t = orig | 0x18; + lgs8gxx_write_reg(priv, 0x30, t); + t = orig | 0x10; + lgs8gxx_write_reg(priv, 0x30, t); + } else { + lgs8gxx_write_reg(priv, 0xC6, 0x01); + lgs8gxx_write_reg(priv, 0xC6, 0x41); + lgs8gxx_write_reg(priv, 0xC6, 0x01); + } +} + +static void packet_counter_stop(struct lgs8gxx_state *priv) +{ + u8 t; + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + lgs8gxx_read_reg(priv, 0x30, &t); + t &= 0xE7; + lgs8gxx_write_reg(priv, 0x30, t); + } else { + lgs8gxx_write_reg(priv, 0xC6, 0x81); + } +} + static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber) { struct lgs8gxx_state *priv = fe->demodulator_priv; - u8 r0, r1, r2, r3; - u32 total_cnt, err_cnt; + u8 reg_err, reg_total, t; + u32 total_cnt = 0, err_cnt = 0; + int i; dprintk("%s\n", __func__); - lgs8gxx_write_reg(priv, 0xc6, 0x01); - lgs8gxx_write_reg(priv, 0xc6, 0x41); - lgs8gxx_write_reg(priv, 0xc6, 0x01); - + packet_counter_start(priv); msleep(200); + packet_counter_stop(priv); + + if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { + reg_total = 0x28; reg_err = 0x2C; + } else { + reg_total = 0xD0; reg_err = 0xD4; + } - lgs8gxx_write_reg(priv, 0xc6, 0x81); - lgs8gxx_read_reg(priv, 0xd0, &r0); - lgs8gxx_read_reg(priv, 0xd1, &r1); - lgs8gxx_read_reg(priv, 0xd2, &r2); - lgs8gxx_read_reg(priv, 0xd3, &r3); - total_cnt = (r3 << 24) | (r2 << 16) | (r1 << 8) | (r0); - lgs8gxx_read_reg(priv, 0xd4, &r0); - lgs8gxx_read_reg(priv, 0xd5, &r1); - lgs8gxx_read_reg(priv, 0xd6, &r2); - lgs8gxx_read_reg(priv, 0xd7, &r3); - err_cnt = (r3 << 24) | (r2 << 16) | (r1 << 8) | (r0); + for (i = 0; i < 4; i++) { + total_cnt <<= 8; + lgs8gxx_read_reg(priv, reg_total+3-i, &t); + total_cnt |= t; + } + for (i = 0; i < 4; i++) { + err_cnt <<= 8; + lgs8gxx_read_reg(priv, reg_err+3-i, &t); + err_cnt |= t; + } dprintk("error=%d total=%d\n", err_cnt, total_cnt); if (total_cnt == 0) @@ -801,6 +1088,9 @@ struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, sizeof(struct dvb_frontend_ops)); priv->frontend.demodulator_priv = priv; + if (config->prod == LGS8GXX_PROD_LGS8G75) + lgs8g75_init_data(priv); + return &priv->frontend; error_out: diff --git a/drivers/media/dvb/frontends/lgs8gxx.h b/drivers/media/dvb/frontends/lgs8gxx.h index 321d366a8307..33c3c5e162fa 100644 --- a/drivers/media/dvb/frontends/lgs8gxx.h +++ b/drivers/media/dvb/frontends/lgs8gxx.h @@ -1,9 +1,9 @@ /* - * Support for Legend Silicon DMB-TH demodulator - * LGS8913, LGS8GL5 + * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator + * LGS8913, LGS8GL5, LGS8G75 * experimental support LGS8G42, LGS8G52 * - * Copyright (C) 2007,2008 David T.L. Wong <davidtlwong@gmail.com> + * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com> * Copyright (C) 2008 Sirius International (Hong Kong) Limited * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5) * @@ -34,6 +34,7 @@ #define LGS8GXX_PROD_LGS8G42 3 #define LGS8GXX_PROD_LGS8G52 4 #define LGS8GXX_PROD_LGS8G54 5 +#define LGS8GXX_PROD_LGS8G75 6 struct lgs8gxx_config { @@ -70,6 +71,10 @@ struct lgs8gxx_config { /*IF use Negative center frequency*/ u8 if_neg_center; + /*8G75 internal ADC input range selection*/ + /*0: 0.8Vpp, 1: 1.0Vpp, 2: 1.6Vpp, 3: 2.0Vpp*/ + u8 adc_vpp; + /* slave address and configuration of the tuner */ u8 tuner_address; }; diff --git a/drivers/media/dvb/frontends/lgs8gxx_priv.h b/drivers/media/dvb/frontends/lgs8gxx_priv.h index 9776d30686dc..8ef376f1414d 100644 --- a/drivers/media/dvb/frontends/lgs8gxx_priv.h +++ b/drivers/media/dvb/frontends/lgs8gxx_priv.h @@ -1,9 +1,9 @@ /* - * Support for Legend Silicon DMB-TH demodulator - * LGS8913, LGS8GL5 + * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator + * LGS8913, LGS8GL5, LGS8G75 * experimental support LGS8G42, LGS8G52 * - * Copyright (C) 2007,2008 David T.L. Wong <davidtlwong@gmail.com> + * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com> * Copyright (C) 2008 Sirius International (Hong Kong) Limited * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5) * @@ -38,7 +38,7 @@ struct lgs8gxx_state { #define SC_QAM64 0x10 /* 64QAM modulation */ #define SC_QAM32 0x0C /* 32QAM modulation */ #define SC_QAM16 0x08 /* 16QAM modulation */ -#define SC_QAM4NR 0x04 /* 4QAM modulation */ +#define SC_QAM4NR 0x04 /* 4QAM-NR modulation */ #define SC_QAM4 0x00 /* 4QAM modulation */ #define LGS_FEC_MASK 0x03 /* FEC Rate Mask */ @@ -47,8 +47,8 @@ struct lgs8gxx_state { #define LGS_FEC_0_8 0x02 /* FEC Rate 0.8 */ #define TIM_MASK 0x20 /* Time Interleave Length Mask */ -#define TIM_LONG 0x00 /* Time Interleave Length = 720 */ -#define TIM_MIDDLE 0x20 /* Time Interleave Length = 240 */ +#define TIM_LONG 0x20 /* Time Interleave Length = 720 */ +#define TIM_MIDDLE 0x00 /* Time Interleave Length = 240 */ #define CF_MASK 0x80 /* Control Frame Mask */ #define CF_EN 0x80 /* Control Frame On */ |