V4L/DVB (13330): s5h1409: properly handle QAM optimization after lock achieved
authorDevin Heitmueller <dheitmueller@kernellabs.com>
Wed, 28 Oct 2009 04:26:05 +0000 (01:26 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sat, 5 Dec 2009 20:41:22 +0000 (18:41 -0200)
The sh51409 driver was only doing the QAM optimization a single time, and it
would only occur if you received a lock instantaneously after the tuning
request.  Restructure the code so that the optimization occurs once you reach
a signal lock.

Note that this depends on the caller polling for status, but we don't have
much choice at this point without an independent thread monitoring the lock
status.  Also, at this point pretty much every application polls for status
lock after doing the tune, so the likelihood of the optimization not occurring
in the real world is pretty low.

The state machine has also been reworked such that setting the interleave mode
is now a dependency of doing the QAM optimization.  Before both were mutually
exclusive, which was not consistent with the Windows driver.  We now have a
single state machine that controls both.

The changes as-is are only enabled for the HVR-1600.  Once the changes are
tested with some of the other boards, this change should be made generic and
the "_legacy" functions should be removed.

This work was sponsored by ONELAN Limited.

Signed-off-by: Devin Heitmueller <dheitmueller@kernellabs.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/dvb/frontends/s5h1409.c

index 85fba58..5507159 100644 (file)
@@ -44,7 +44,15 @@ struct s5h1409_state {
        int if_freq;
 
        u32 is_qam_locked;
-       u32 qam_state;
+
+       /* QAM tuning state goes through the following state transitions */
+#define QAM_STATE_UNTUNED 0
+#define QAM_STATE_TUNING_STARTED 1
+#define QAM_STATE_INTERLEAVE_SET 2
+#define QAM_STATE_QAM_OPTIMIZED_L1 3
+#define QAM_STATE_QAM_OPTIMIZED_L2 4
+#define QAM_STATE_QAM_OPTIMIZED_L3 5
+       u8  qam_state;
 };
 
 static int debug;
@@ -347,7 +355,7 @@ 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;
+       state->qam_state = QAM_STATE_UNTUNED;
        return 0;
 }
 
@@ -474,6 +482,59 @@ static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe)
        struct s5h1409_state *state = fe->demodulator_priv;
        u16 reg;
 
+       if (state->qam_state < QAM_STATE_INTERLEAVE_SET) {
+               /* We should not perform amhum optimization until
+                  the interleave mode has been configured */
+               return;
+       }
+
+       if (state->qam_state == QAM_STATE_QAM_OPTIMIZED_L3) {
+               /* We've already reached the maximum optimization level, so
+                  dont bother banging on the status registers */
+               return;
+       }
+
+       /* QAM EQ lock check */
+       reg = s5h1409_readreg(state, 0xf0);
+
+       if ((reg >> 13) & 0x1) {
+               reg &= 0xff;
+
+               s5h1409_writereg(state, 0x96, 0x000c);
+               if (reg < 0x68) {
+                       if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L3) {
+                               dprintk("%s() setting QAM state to OPT_L3\n",
+                                       __func__);
+                               s5h1409_writereg(state, 0x93, 0x3130);
+                               s5h1409_writereg(state, 0x9e, 0x2836);
+                               state->qam_state = QAM_STATE_QAM_OPTIMIZED_L3;
+                       }
+               } else {
+                       if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L2) {
+                               dprintk("%s() setting QAM state to OPT_L2\n",
+                                       __func__);
+                               s5h1409_writereg(state, 0x93, 0x3332);
+                               s5h1409_writereg(state, 0x9e, 0x2c37);
+                               state->qam_state = QAM_STATE_QAM_OPTIMIZED_L2;
+                       }
+               }
+
+       } else {
+               if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L1) {
+                       dprintk("%s() setting QAM state to OPT_L1\n", __func__);
+                       s5h1409_writereg(state, 0x96, 0x0008);
+                       s5h1409_writereg(state, 0x93, 0x3332);
+                       s5h1409_writereg(state, 0x9e, 0x2c37);
+                       state->qam_state = QAM_STATE_QAM_OPTIMIZED_L1;
+               }
+       }
+}
+
+static void s5h1409_set_qam_amhum_mode_legacy(struct dvb_frontend *fe)
+{
+       struct s5h1409_state *state = fe->demodulator_priv;
+       u16 reg;
+
        if (state->is_qam_locked)
                return;
 
@@ -506,6 +567,46 @@ static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe)
        struct s5h1409_state *state = fe->demodulator_priv;
        u16 reg, reg1, reg2;
 
+       if (state->qam_state >= QAM_STATE_INTERLEAVE_SET) {
+               /* We've done the optimization already */
+               return;
+       }
+
+       reg = s5h1409_readreg(state, 0xf1);
+
+       /* Master lock */
+       if ((reg >> 15) & 0x1) {
+               if (state->qam_state == QAM_STATE_UNTUNED ||
+                   state->qam_state == QAM_STATE_TUNING_STARTED) {
+                       dprintk("%s() setting QAM state to INTERLEAVE_SET\n",
+                               __func__);
+                       reg1 = s5h1409_readreg(state, 0xb2);
+                       reg2 = s5h1409_readreg(state, 0xad);
+
+                       s5h1409_writereg(state, 0x96, 0x0020);
+                       s5h1409_writereg(state, 0xad,
+                               (((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff)));
+                       s5h1409_writereg(state, 0xab,
+                               s5h1409_readreg(state, 0xab) & 0xeffe);
+                       state->qam_state = QAM_STATE_INTERLEAVE_SET;
+               }
+       } else {
+               if (state->qam_state == QAM_STATE_UNTUNED) {
+                       dprintk("%s() setting QAM state to TUNING_STARTED\n",
+                               __func__);
+                       s5h1409_writereg(state, 0x96, 0x08);
+                       s5h1409_writereg(state, 0xab,
+                               s5h1409_readreg(state, 0xab) | 0x1001);
+                       state->qam_state = QAM_STATE_TUNING_STARTED;
+               }
+       }
+}
+
+static void s5h1409_set_qam_interleave_mode_legacy(struct dvb_frontend *fe)
+{
+       struct s5h1409_state *state = fe->demodulator_priv;
+       u16 reg, reg1, reg2;
+
        reg = s5h1409_readreg(state, 0xf1);
 
        /* Master lock */
@@ -553,16 +654,24 @@ static int s5h1409_set_frontend(struct dvb_frontend *fe,
                        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);
-       }
-
        /* Issue a reset to the demod so it knows to resync against the
           newly tuned frequency */
        s5h1409_softreset(fe);
 
+       /* Optimize the demod for QAM */
+       if (state->current_modulation != VSB_8) {
+               /* This almost certainly applies to all boards, but for now
+                  only do it for the HVR-1600.  Once the other boards are
+                  tested, the "legacy" versions can just go away */
+               if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) {
+                       s5h1409_set_qam_amhum_mode(fe);
+                       s5h1409_set_qam_interleave_mode(fe);
+               } else {
+                       s5h1409_set_qam_amhum_mode_legacy(fe);
+                       s5h1409_set_qam_interleave_mode_legacy(fe);
+               }
+       }
+
        return 0;
 }
 
@@ -656,6 +765,17 @@ static int s5h1409_read_status(struct dvb_frontend *fe, fe_status_t *status)
 
        *status = 0;
 
+       /* Optimize the demod for QAM */
+       if (state->current_modulation != VSB_8) {
+               /* This almost certainly applies to all boards, but for now
+                  only do it for the HVR-1600.  Once the other boards are
+                  tested, the "legacy" versions can just go away */
+               if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) {
+                       s5h1409_set_qam_amhum_mode(fe);
+                       s5h1409_set_qam_interleave_mode(fe);
+               }
+       }
+
        /* Get the demodulator status */
        reg = s5h1409_readreg(state, 0xf1);
        if (reg & 0x1000)