Skip to content

Commit

Permalink
V4L/DVB (6402): s5h1409: Fix broken QAM support
Browse files Browse the repository at this point in the history
This patch enables QAM Annex-B support (US digital cable)
for the s5h1409 VSB/QAM demodulator.

Tested successfully with the mt2131 tuner,
present on the following supported boards:

Hauppauge WinTV-HVR-1250
Hauppauge WinTV-HVR-1800
Hauppauge WinTV-HVR-1800lp

This patch is also known to work with an upcoming XC5000 tuner driver.

Signed-off-by: Steven Toth <stoth@hauppauge.com>
Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
  • Loading branch information
Steven Toth authored and Mauro Carvalho Chehab committed Nov 4, 2007
1 parent bb8d56a commit dd7d501
Showing 1 changed file with 86 additions and 10 deletions.
96 changes: 86 additions & 10 deletions drivers/media/dvb/frontends/s5h1409.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ struct s5h1409_state {
fe_modulation_t current_modulation;

u32 current_frequency;

u32 is_qam_locked;
u32 qam_state;
};

static int debug = 0;
Expand Down Expand Up @@ -94,6 +97,7 @@ static struct init_tab {
{ 0xac, 0x1003, },
{ 0xad, 0x103f, },
{ 0xe2, 0x0100, },
{ 0xe3, 0x0000, },
{ 0x28, 0x1010, },
{ 0xb1, 0x000e, },
};
Expand Down Expand Up @@ -335,6 +339,8 @@ static int s5h1409_softreset(struct dvb_frontend* fe)

s5h1409_writereg(state, 0xf5, 0);
s5h1409_writereg(state, 0xf5, 1);
state->is_qam_locked = 0;
state->qam_state = 0;
return 0;
}

Expand All @@ -349,6 +355,11 @@ static int s5h1409_set_if_freq(struct dvb_frontend* fe, int KHz)
s5h1409_writereg(state, 0x87, 0x01be);
s5h1409_writereg(state, 0x88, 0x0436);
s5h1409_writereg(state, 0x89, 0x054d);
} else
if (KHz == 4000) {
s5h1409_writereg(state, 0x87, 0x014b);
s5h1409_writereg(state, 0x88, 0x0cb5);
s5h1409_writereg(state, 0x89, 0x03e2);
} else {
printk("%s() Invalid arg = %d KHz\n", __FUNCTION__, KHz);
ret = -1;
Expand All @@ -361,7 +372,7 @@ static int s5h1409_set_spectralinversion(struct dvb_frontend* fe, int inverted)
{
struct s5h1409_state* state = fe->demodulator_priv;

dprintk("%s()\n", __FUNCTION__);
dprintk("%s(%d)\n", __FUNCTION__, inverted);

if(inverted == 1)
return s5h1409_writereg(state, 0x1b, 0x1101); /* Inverted */
Expand All @@ -382,14 +393,10 @@ static int s5h1409_enable_modulation(struct dvb_frontend* fe,
s5h1409_writereg(state, 0xf4, 0);
break;
case QAM_64:
dprintk("%s() QAM_64\n", __FUNCTION__);
s5h1409_writereg(state, 0xf4, 1);
s5h1409_writereg(state, 0x85, 0x100);
break;
case QAM_256:
dprintk("%s() QAM_256\n", __FUNCTION__);
dprintk("%s() QAM_AUTO (64/256)\n", __FUNCTION__);
s5h1409_writereg(state, 0xf4, 1);
s5h1409_writereg(state, 0x85, 0x101);
s5h1409_writereg(state, 0x85, 0x110);
break;
default:
dprintk("%s() Invalid modulation\n", __FUNCTION__);
Expand Down Expand Up @@ -423,7 +430,7 @@ static int s5h1409_set_gpio(struct dvb_frontend* fe, int enable)
if (enable)
return s5h1409_writereg(state, 0xe3, 0x1100);
else
return s5h1409_writereg(state, 0xe3, 0);
return s5h1409_writereg(state, 0xe3, 0x1000);
}

static int s5h1409_sleep(struct dvb_frontend* fe, int enable)
Expand All @@ -444,6 +451,66 @@ static int s5h1409_register_reset(struct dvb_frontend* fe)
return s5h1409_writereg(state, 0xfa, 0);
}

static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe)
{
struct s5h1409_state *state = fe->demodulator_priv;
u16 reg;

if (state->is_qam_locked)
return;

/* QAM EQ lock check */
reg = s5h1409_readreg(state, 0xf0);

if ((reg >> 13) & 0x1) {

state->is_qam_locked = 1;
reg &= 0xff;

s5h1409_writereg(state, 0x96, 0x00c);
if ((reg < 0x38) || (reg > 0x68) ) {
s5h1409_writereg(state, 0x93, 0x3332);
s5h1409_writereg(state, 0x9e, 0x2c37);
} else {
s5h1409_writereg(state, 0x93, 0x3130);
s5h1409_writereg(state, 0x9e, 0x2836);
}

} else {
s5h1409_writereg(state, 0x96, 0x0008);
s5h1409_writereg(state, 0x93, 0x3332);
s5h1409_writereg(state, 0x9e, 0x2c37);
}
}

static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe)
{
struct s5h1409_state *state = fe->demodulator_priv;
u16 reg, reg1, reg2;

reg = s5h1409_readreg(state, 0xf1);

/* Master lock */
if ((reg >> 15) & 0x1) {
if (state->qam_state != 2) {
state->qam_state = 2;
reg1 = s5h1409_readreg(state, 0xb2);
reg2 = s5h1409_readreg(state, 0xad);

s5h1409_writereg(state, 0x96, 0x20);
s5h1409_writereg(state, 0xad,
( ((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff)) );
s5h1409_writereg(state, 0xab, 0x1100);
}
} else {
if (state->qam_state != 1) {
state->qam_state = 1;
s5h1409_writereg(state, 0x96, 0x08);
s5h1409_writereg(state, 0xab, 0x1101);
}
}
}

/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
static int s5h1409_set_frontend (struct dvb_frontend* fe,
struct dvb_frontend_parameters *p)
Expand All @@ -458,12 +525,21 @@ static int s5h1409_set_frontend (struct dvb_frontend* fe,

s5h1409_enable_modulation(fe, p->u.vsb.modulation);

/* Allow the demod to settle */
msleep(100);

if (fe->ops.tuner_ops.set_params) {
if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1);
fe->ops.tuner_ops.set_params(fe, p);
if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
}

/* Optimize the demod for QAM */
if (p->u.vsb.modulation != VSB_8) {
s5h1409_set_qam_amhum_mode(fe);
s5h1409_set_qam_interleave_mode(fe);
}

return 0;
}

Expand Down Expand Up @@ -495,8 +571,8 @@ static int s5h1409_init (struct dvb_frontend* fe)
s5h1409_set_gpio(fe, state->config->gpio);
s5h1409_softreset(fe);

/* Note: Leaving the I2C gate open here. */
s5h1409_i2c_gate_ctrl(fe, 1);
/* Note: Leaving the I2C gate closed. */
s5h1409_i2c_gate_ctrl(fe, 0);

return 0;
}
Expand Down

0 comments on commit dd7d501

Please sign in to comment.