Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dd7d5013 authored by Steven Toth's avatar Steven Toth Committed by Mauro Carvalho Chehab
Browse files

V4L/DVB (6402): s5h1409: Fix broken QAM support



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: default avatarSteven Toth <stoth@hauppauge.com>
Signed-off-by: default avatarMichael Krufky <mkrufky@linuxtv.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent bb8d56a4
Loading
Loading
Loading
Loading
+86 −10
Original line number Diff line number Diff line
@@ -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;
@@ -94,6 +97,7 @@ static struct init_tab {
	{ 0xac, 0x1003, },
	{ 0xad, 0x103f, },
	{ 0xe2, 0x0100, },
	{ 0xe3, 0x0000, },
	{ 0x28, 0x1010, },
	{ 0xb1, 0x000e, },
};
@@ -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;
}

@@ -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;
@@ -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 */
@@ -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__);
@@ -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)
@@ -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)
@@ -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;
}

@@ -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;
}