Commit b80dc1c6 authored by David Wong's avatar David Wong Committed by Mauro Carvalho Chehab

V4L/DVB (12271): lgs8gxx: add lgs8g75 support

lgs8gxx: add lgs8g75 demodulator support
Signed-off-by: default avatarDavid T.L. Wong <davidtlwong@gmail.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent a0d64456
/* /*
* Support for Legend Silicon DMB-TH demodulator * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator
* LGS8913, LGS8GL5 * LGS8913, LGS8GL5, LGS8G75
* experimental support LGS8G42, LGS8G52 * 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 * Copyright (C) 2008 Sirius International (Hong Kong) Limited
* Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5) * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
* *
...@@ -46,6 +46,42 @@ module_param(fake_signal_str, int, 0644); ...@@ -46,6 +46,42 @@ module_param(fake_signal_str, int, 0644);
MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913." MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913."
"Signal strength calculation is slow.(default:on)."); "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 */ /* LGS8GXX internal helper functions */
static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data) 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) ...@@ -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 }; struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 };
msg.addr = priv->config->demod_address; msg.addr = priv->config->demod_address;
if (reg >= 0xC0) if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0)
msg.addr += 0x02; msg.addr += 0x02;
if (debug >= 2) if (debug >= 2)
...@@ -84,7 +120,7 @@ static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data) ...@@ -84,7 +120,7 @@ static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data)
}; };
dev_addr = priv->config->demod_address; dev_addr = priv->config->demod_address;
if (reg >= 0xC0) if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0)
dev_addr += 0x02; dev_addr += 0x02;
msg[1].addr = msg[0].addr = dev_addr; msg[1].addr = msg[0].addr = dev_addr;
...@@ -112,19 +148,36 @@ static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv) ...@@ -112,19 +148,36 @@ static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv)
return 0; 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) static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv)
{ {
const struct lgs8gxx_config *config = priv->config; const struct lgs8gxx_config *config = priv->config;
u8 if_conf; u8 if_conf;
if_conf = 0x10; /* AGC output on; */ if_conf = 0x10; /* AGC output on, RF_AGC output off; */
if_conf |= if_conf |=
((config->ext_adc) ? 0x80 : 0x00) | ((config->ext_adc) ? 0x80 : 0x00) |
((config->if_neg_center) ? 0x04 : 0x00) | ((config->if_neg_center) ? 0x04 : 0x00) |
((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */ ((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */
((config->ext_adc && config->adc_signed) ? 0x02 : 0x00) | ((config->adc_signed) ? 0x02 : 0x00) |
((config->ext_adc && config->if_neg_edge) ? 0x01 : 0x00); ((config->if_neg_edge) ? 0x01 : 0x00);
if (config->ext_adc && if (config->ext_adc &&
(config->prod == LGS8GXX_PROD_LGS8G52)) { (config->prod == LGS8GXX_PROD_LGS8G52)) {
...@@ -157,39 +210,82 @@ static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/) ...@@ -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); dprintk("AFC_INIT_FREQ = 0x%08X\n", v32);
lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32)); if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8)); lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32));
lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16)); lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8));
lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24)); 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; return 0;
} }
static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv) static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv)
{ {
u8 t; 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_write_reg(priv, 0xC6, 0x01);
lgs8gxx_read_reg(priv, 0x7E, &t); if (prod == LGS8GXX_PROD_LGS8G75) {
lgs8gxx_write_reg(priv, 0x7E, t | 0x01); lgs8gxx_read_reg(priv, 0x0C, &t);
t &= (~0x04);
/* clear FEC self reset */ lgs8gxx_write_reg(priv, 0x0C, t | 0x80);
lgs8gxx_read_reg(priv, 0xC5, &t); lgs8gxx_write_reg(priv, 0x39, 0x00);
lgs8gxx_write_reg(priv, 0xC5, t & 0xE0); 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 */ /* FEC auto detect */
lgs8gxx_write_reg(priv, 0xC1, 0x03); lgs8gxx_write_reg(priv, 0xC1, 0x03);
lgs8gxx_read_reg(priv, 0x7C, &t); lgs8gxx_read_reg(priv, 0x7C, &t);
t = (t & 0x8C) | 0x03; t = (t & 0x8C) | 0x03;
lgs8gxx_write_reg(priv, 0x7C, t); lgs8gxx_write_reg(priv, 0x7C, t);
}
if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
/* BER test mode */ /* BER test mode */
lgs8gxx_read_reg(priv, 0xC3, &t); lgs8gxx_read_reg(priv, 0xC3, &t);
t = (t & 0xEF) | 0x10; t = (t & 0xEF) | 0x10;
...@@ -207,6 +303,32 @@ static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv) ...@@ -207,6 +303,32 @@ static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv)
int ret = 0; int ret = 0;
u8 t; 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 */ /* turn off auto-detect; manual settings */
lgs8gxx_write_reg(priv, 0x7E, 0); lgs8gxx_write_reg(priv, 0x7E, 0);
if (priv->config->prod == LGS8GXX_PROD_LGS8913) if (priv->config->prod == LGS8GXX_PROD_LGS8913)
...@@ -226,11 +348,39 @@ static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked) ...@@ -226,11 +348,39 @@ static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked)
int ret = 0; int ret = 0;
u8 t; 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) if (ret != 0)
return ret; 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; return 0;
} }
...@@ -238,21 +388,30 @@ static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv, ...@@ -238,21 +388,30 @@ static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv,
u8 *finished) u8 *finished)
{ {
int ret = 0; int ret = 0;
u8 t; u8 reg, mask, val;
ret = lgs8gxx_read_reg(priv, 0xA4, &t); if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
if (ret != 0) reg = 0x1f;
return ret; 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; 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 ad_fini = 0;
u8 t1, t2;
if (gi == GI_945) if (gi == GI_945)
dprintk("try GI 945\n"); dprintk("try GI 945\n");
...@@ -260,17 +419,29 @@ static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 *locked) ...@@ -260,17 +419,29 @@ static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 *locked)
dprintk("try GI 595\n"); dprintk("try GI 595\n");
else if (gi == GI_420) else if (gi == GI_420)
dprintk("try GI 420\n"); 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); 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); err = lgs8gxx_is_autodetect_finished(priv, &ad_fini);
if (err != 0) if (err != 0)
return err; return err;
if (ad_fini) { if (ad_fini) {
err = lgs8gxx_is_locked(priv, locked); dprintk("auto detect finished\n");
if (err != 0) } else
return err; *locked = 0;
}
return 0; return 0;
} }
...@@ -285,13 +456,18 @@ static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv, ...@@ -285,13 +456,18 @@ static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv,
dprintk("%s\n", __func__); dprintk("%s\n", __func__);
lgs8gxx_set_mode_auto(priv); lgs8gxx_set_mode_auto(priv);
/* Guard Interval */ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
lgs8gxx_write_reg(priv, 0x03, 00); 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 (i = 0; i < 2; i++) {
for (j = 0; j < 2; j++) { for (j = 0; j < 2; j++) {
tmp_gi = GI_945; tmp_gi = GI_945;
err = lgs8gxx_autolock_gi(priv, GI_945, &locked); err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked);
if (err) if (err)
goto out; goto out;
if (locked) if (locked)
...@@ -299,14 +475,14 @@ static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv, ...@@ -299,14 +475,14 @@ static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv,
} }
for (j = 0; j < 2; j++) { for (j = 0; j < 2; j++) {
tmp_gi = GI_420; tmp_gi = GI_420;
err = lgs8gxx_autolock_gi(priv, GI_420, &locked); err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked);
if (err) if (err)
goto out; goto out;
if (locked) if (locked)
goto locked; goto locked;
} }
tmp_gi = GI_595; tmp_gi = GI_595;
err = lgs8gxx_autolock_gi(priv, GI_595, &locked); err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked);
if (err) if (err)
goto out; goto out;
if (locked) if (locked)
...@@ -317,8 +493,13 @@ locked: ...@@ -317,8 +493,13 @@ locked:
if ((err == 0) && (locked == 1)) { if ((err == 0) && (locked == 1)) {
u8 t; u8 t;
lgs8gxx_read_reg(priv, 0xA2, &t); if (priv->config->prod != LGS8GXX_PROD_LGS8G75) {
*detected_param = t; 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) if (tmp_gi == GI_945)
dprintk("GI 945 locked\n"); dprintk("GI 945 locked\n");
...@@ -345,18 +526,28 @@ static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv) ...@@ -345,18 +526,28 @@ static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv)
if (err != 0) { if (err != 0) {
dprintk("lgs8gxx_auto_detect failed\n"); dprintk("lgs8gxx_auto_detect failed\n");
} } else
dprintk("detected param = 0x%02X\n", detected_param);
/* Apply detected parameters */ /* Apply detected parameters */
if (priv->config->prod == LGS8GXX_PROD_LGS8913) { if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
u8 inter_leave_len = detected_param & TIM_MASK ; 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 &= CF_MASK | SC_MASK | LGS_FEC_MASK;
detected_param |= inter_leave_len; detected_param |= inter_leave_len;
} }
lgs8gxx_write_reg(priv, 0x7D, detected_param); if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
if (priv->config->prod == LGS8GXX_PROD_LGS8913) u8 t;
lgs8gxx_write_reg(priv, 0xC0, detected_param); 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); */ /* lgs8gxx_soft_reset(priv); */
/* Enter manual mode */ /* Enter manual mode */
...@@ -378,9 +569,10 @@ static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv, ...@@ -378,9 +569,10 @@ static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv,
u8 serial, u8 clk_pol, u8 clk_gated) u8 serial, u8 clk_pol, u8 clk_gated)
{ {
int ret = 0; 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) if (ret != 0)
return ret; return ret;
...@@ -389,13 +581,29 @@ static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv, ...@@ -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_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL;
t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN; 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) if (ret != 0)
return ret; return ret;
return 0; 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 */ /* LGS8913 demod frontend functions */
...@@ -417,6 +625,34 @@ static int lgs8913_init(struct lgs8gxx_state *priv) ...@@ -417,6 +625,34 @@ static int lgs8913_init(struct lgs8gxx_state *priv)
return 0; 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) static int lgs8gxx_init(struct dvb_frontend *fe)
{ {
struct lgs8gxx_state *priv = struct lgs8gxx_state *priv =
...@@ -429,6 +665,9 @@ static int lgs8gxx_init(struct dvb_frontend *fe) ...@@ -429,6 +665,9 @@ static int lgs8gxx_init(struct dvb_frontend *fe)
lgs8gxx_read_reg(priv, 0, &data); lgs8gxx_read_reg(priv, 0, &data);
dprintk("reg 0 = 0x%02X\n", 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 */ /* Setup MPEG output format */
err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts, err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts,
config->ts_clk_pol, config->ts_clk_pol,
...@@ -439,8 +678,7 @@ static int lgs8gxx_init(struct dvb_frontend *fe) ...@@ -439,8 +678,7 @@ static int lgs8gxx_init(struct dvb_frontend *fe)
if (config->prod == LGS8GXX_PROD_LGS8913) if (config->prod == LGS8GXX_PROD_LGS8913)
lgs8913_init(priv); lgs8913_init(priv);
lgs8gxx_set_if_freq(priv, priv->config->if_freq); 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; return 0;
} }
...@@ -489,9 +727,6 @@ static int lgs8gxx_set_fe(struct dvb_frontend *fe, ...@@ -489,9 +727,6 @@ static int lgs8gxx_set_fe(struct dvb_frontend *fe,
static int lgs8gxx_get_fe(struct dvb_frontend *fe, static int lgs8gxx_get_fe(struct dvb_frontend *fe,
struct dvb_frontend_parameters *fe_params) struct dvb_frontend_parameters *fe_params)
{ {
struct lgs8gxx_state *priv = fe->demodulator_priv;
u8 t;
dprintk("%s\n", __func__); dprintk("%s\n", __func__);
/* TODO: get real readings from device */ /* TODO: get real readings from device */
...@@ -501,29 +736,10 @@ static int lgs8gxx_get_fe(struct dvb_frontend *fe, ...@@ -501,29 +736,10 @@ static int lgs8gxx_get_fe(struct dvb_frontend *fe,
/* bandwidth */ /* bandwidth */
fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; 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_HP = FEC_AUTO;
fe_params->u.ofdm.code_rate_LP = FEC_AUTO; fe_params->u.ofdm.code_rate_LP = FEC_AUTO;
/* constellation */ fe_params->u.ofdm.constellation = QAM_AUTO;
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;
}
/* transmission mode */ /* transmission mode */
fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; 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) ...@@ -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; struct lgs8gxx_state *priv = fe->demodulator_priv;
s8 ret; s8 ret;
u8 t; u8 t, locked = 0;
dprintk("%s\n", __func__); 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); ret = lgs8gxx_read_reg(priv, 0x4B, &t);
if (ret != 0) if (ret != 0)
...@@ -658,12 +884,33 @@ static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) ...@@ -658,12 +884,33 @@ static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal)
return 0; 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) static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal)
{ {
struct lgs8gxx_state *priv = fe->demodulator_priv; struct lgs8gxx_state *priv = fe->demodulator_priv;
if (priv->config->prod == LGS8GXX_PROD_LGS8913) if (priv->config->prod == LGS8GXX_PROD_LGS8913)
return lgs8913_read_signal_strength(priv, signal); return lgs8913_read_signal_strength(priv, signal);
else if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
return lgs8g75_read_signal_strength(priv, signal);
else else
return lgs8gxx_read_signal_agc(priv, signal); return lgs8gxx_read_signal_agc(priv, signal);
} }
...@@ -674,7 +921,10 @@ static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr) ...@@ -674,7 +921,10 @@ static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr)
u8 t; u8 t;
*snr = 0; *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); dprintk("AVG Noise=0x%02X\n", t);
*snr = 256 - t; *snr = 256 - t;
*snr <<= 8; *snr <<= 8;
...@@ -690,31 +940,68 @@ static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ...@@ -690,31 +940,68 @@ static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
return 0; 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) static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber)
{ {
struct lgs8gxx_state *priv = fe->demodulator_priv; struct lgs8gxx_state *priv = fe->demodulator_priv;
u8 r0, r1, r2, r3; u8 reg_err, reg_total, t;
u32 total_cnt, err_cnt; u32 total_cnt = 0, err_cnt = 0;
int i;
dprintk("%s\n", __func__); dprintk("%s\n", __func__);
lgs8gxx_write_reg(priv, 0xc6, 0x01); packet_counter_start(priv);
lgs8gxx_write_reg(priv, 0xc6, 0x41);
lgs8gxx_write_reg(priv, 0xc6, 0x01);
msleep(200); 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); for (i = 0; i < 4; i++) {
lgs8gxx_read_reg(priv, 0xd0, &r0); total_cnt <<= 8;
lgs8gxx_read_reg(priv, 0xd1, &r1); lgs8gxx_read_reg(priv, reg_total+3-i, &t);
lgs8gxx_read_reg(priv, 0xd2, &r2); total_cnt |= t;
lgs8gxx_read_reg(priv, 0xd3, &r3); }
total_cnt = (r3 << 24) | (r2 << 16) | (r1 << 8) | (r0); for (i = 0; i < 4; i++) {
lgs8gxx_read_reg(priv, 0xd4, &r0); err_cnt <<= 8;
lgs8gxx_read_reg(priv, 0xd5, &r1); lgs8gxx_read_reg(priv, reg_err+3-i, &t);
lgs8gxx_read_reg(priv, 0xd6, &r2); err_cnt |= t;
lgs8gxx_read_reg(priv, 0xd7, &r3); }
err_cnt = (r3 << 24) | (r2 << 16) | (r1 << 8) | (r0);
dprintk("error=%d total=%d\n", err_cnt, total_cnt); dprintk("error=%d total=%d\n", err_cnt, total_cnt);
if (total_cnt == 0) if (total_cnt == 0)
...@@ -801,6 +1088,9 @@ struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, ...@@ -801,6 +1088,9 @@ struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config,
sizeof(struct dvb_frontend_ops)); sizeof(struct dvb_frontend_ops));
priv->frontend.demodulator_priv = priv; priv->frontend.demodulator_priv = priv;
if (config->prod == LGS8GXX_PROD_LGS8G75)
lgs8g75_init_data(priv);
return &priv->frontend; return &priv->frontend;
error_out: error_out:
......
/* /*
* Support for Legend Silicon DMB-TH demodulator * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator
* LGS8913, LGS8GL5 * LGS8913, LGS8GL5, LGS8G75
* experimental support LGS8G42, LGS8G52 * 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 * Copyright (C) 2008 Sirius International (Hong Kong) Limited
* Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5) * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
* *
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#define LGS8GXX_PROD_LGS8G42 3 #define LGS8GXX_PROD_LGS8G42 3
#define LGS8GXX_PROD_LGS8G52 4 #define LGS8GXX_PROD_LGS8G52 4
#define LGS8GXX_PROD_LGS8G54 5 #define LGS8GXX_PROD_LGS8G54 5
#define LGS8GXX_PROD_LGS8G75 6
struct lgs8gxx_config { struct lgs8gxx_config {
...@@ -70,6 +71,10 @@ struct lgs8gxx_config { ...@@ -70,6 +71,10 @@ struct lgs8gxx_config {
/*IF use Negative center frequency*/ /*IF use Negative center frequency*/
u8 if_neg_center; 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 */ /* slave address and configuration of the tuner */
u8 tuner_address; u8 tuner_address;
}; };
......
/* /*
* Support for Legend Silicon DMB-TH demodulator * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator
* LGS8913, LGS8GL5 * LGS8913, LGS8GL5, LGS8G75
* experimental support LGS8G42, LGS8G52 * 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 * Copyright (C) 2008 Sirius International (Hong Kong) Limited
* Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5) * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
* *
...@@ -38,7 +38,7 @@ struct lgs8gxx_state { ...@@ -38,7 +38,7 @@ struct lgs8gxx_state {
#define SC_QAM64 0x10 /* 64QAM modulation */ #define SC_QAM64 0x10 /* 64QAM modulation */
#define SC_QAM32 0x0C /* 32QAM modulation */ #define SC_QAM32 0x0C /* 32QAM modulation */
#define SC_QAM16 0x08 /* 16QAM 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 SC_QAM4 0x00 /* 4QAM modulation */
#define LGS_FEC_MASK 0x03 /* FEC Rate Mask */ #define LGS_FEC_MASK 0x03 /* FEC Rate Mask */
...@@ -47,8 +47,8 @@ struct lgs8gxx_state { ...@@ -47,8 +47,8 @@ struct lgs8gxx_state {
#define LGS_FEC_0_8 0x02 /* FEC Rate 0.8 */ #define LGS_FEC_0_8 0x02 /* FEC Rate 0.8 */
#define TIM_MASK 0x20 /* Time Interleave Length Mask */ #define TIM_MASK 0x20 /* Time Interleave Length Mask */
#define TIM_LONG 0x00 /* Time Interleave Length = 720 */ #define TIM_LONG 0x20 /* Time Interleave Length = 720 */
#define TIM_MIDDLE 0x20 /* Time Interleave Length = 240 */ #define TIM_MIDDLE 0x00 /* Time Interleave Length = 240 */
#define CF_MASK 0x80 /* Control Frame Mask */ #define CF_MASK 0x80 /* Control Frame Mask */
#define CF_EN 0x80 /* Control Frame On */ #define CF_EN 0x80 /* Control Frame On */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment