Skip to content

Commit

Permalink
[media] xc5000: reset device if encountering PLL lock failure
Browse files Browse the repository at this point in the history
It's possible for the xc5000 to enter an unknown state such that all
subsequent tuning requests fail.  The only way to recover is to reset the
tuner and reload the firmware.  This problem was detected after several days
straight of issuing tuning requests every five seconds.

Reset the firmware in the event that the PLL is in an unlocked state.  This
solution was provided by the engineer at CrestaTech (the company that acquired
Xceive).

Signed-off-by: Devin Heitmueller <dheitmueller@kernellabs.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
  • Loading branch information
Devin Heitmueller authored and Mauro Carvalho Chehab committed Aug 9, 2012
1 parent baede40 commit de49bc6
Showing 1 changed file with 52 additions and 6 deletions.
58 changes: 52 additions & 6 deletions drivers/media/common/tuners/xc5000.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ struct xc5000_priv {
u8 radio_input;

int chip_id;
u16 pll_register_no;
};

/* Misc Defines */
Expand Down Expand Up @@ -209,18 +210,21 @@ static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = {
struct xc5000_fw_cfg {
char *name;
u16 size;
u16 pll_reg;
};

#define XC5000A_FIRMWARE "dvb-fe-xc5000-1.6.114.fw"
static const struct xc5000_fw_cfg xc5000a_1_6_114 = {
.name = XC5000A_FIRMWARE,
.size = 12401,
.pll_reg = 0x806c,
};

#define XC5000C_FIRMWARE "dvb-fe-xc5000c-41.024.5.fw"
static const struct xc5000_fw_cfg xc5000c_41_024_5 = {
.name = XC5000C_FIRMWARE,
.size = 16497,
.pll_reg = 0x13,
};

static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id)
Expand All @@ -234,7 +238,7 @@ static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id)
}
}

static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe);
static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force);
static int xc5000_is_firmware_loaded(struct dvb_frontend *fe);
static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val);
static int xc5000_TunerReset(struct dvb_frontend *fe);
Expand Down Expand Up @@ -619,6 +623,7 @@ static int xc5000_fwupload(struct dvb_frontend *fe)
int ret;
const struct xc5000_fw_cfg *desired_fw =
xc5000_assign_firmware(priv->chip_id);
priv->pll_register_no = desired_fw->pll_reg;

/* request the firmware, this will block and timeout */
printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n",
Expand Down Expand Up @@ -668,6 +673,7 @@ static void xc_debug_dump(struct xc5000_priv *priv)
u8 hw_majorversion = 0, hw_minorversion = 0;
u8 fw_majorversion = 0, fw_minorversion = 0;
u16 fw_buildversion = 0;
u16 regval;

/* Wait for stats to stabilize.
* Frame Lines needs two frame times after initial lock
Expand Down Expand Up @@ -707,6 +713,11 @@ static void xc_debug_dump(struct xc5000_priv *priv)
xc_get_totalgain(priv, &totalgain);
dprintk(1, "*** Total gain = %d.%d dB\n", totalgain / 256,
(totalgain % 256) * 100 / 256);

if (priv->pll_register_no) {
xc5000_readreg(priv, priv->pll_register_no, &regval);
dprintk(1, "*** PLL lock status = 0x%04x\n", regval);
}
}

static int xc5000_set_params(struct dvb_frontend *fe)
Expand All @@ -717,7 +728,7 @@ static int xc5000_set_params(struct dvb_frontend *fe)
u32 freq = fe->dtv_property_cache.frequency;
u32 delsys = fe->dtv_property_cache.delivery_system;

if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) {
dprintk(1, "Unable to load firmware and init tuner\n");
return -EINVAL;
}
Expand Down Expand Up @@ -850,6 +861,7 @@ static int xc5000_set_tv_freq(struct dvb_frontend *fe,
struct analog_parameters *params)
{
struct xc5000_priv *priv = fe->tuner_priv;
u16 pll_lock_status;
int ret;

dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
Expand Down Expand Up @@ -930,6 +942,21 @@ static int xc5000_set_tv_freq(struct dvb_frontend *fe,
if (debug)
xc_debug_dump(priv);

if (priv->pll_register_no != 0) {
msleep(20);
xc5000_readreg(priv, priv->pll_register_no, &pll_lock_status);
if (pll_lock_status > 63) {
/* PLL is unlocked, force reload of the firmware */
dprintk(1, "xc5000: PLL not locked (0x%x). Reloading...\n",
pll_lock_status);
if (xc_load_fw_and_init_tuner(fe, 1) != XC_RESULT_SUCCESS) {
printk(KERN_ERR "xc5000: Unable to reload fw\n");
return -EREMOTEIO;
}
goto tune_channel;
}
}

return 0;
}

Expand Down Expand Up @@ -1000,7 +1027,7 @@ static int xc5000_set_analog_params(struct dvb_frontend *fe,
if (priv->i2c_props.adap == NULL)
return -EINVAL;

if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) {
dprintk(1, "Unable to load firmware and init tuner\n");
return -EINVAL;
}
Expand Down Expand Up @@ -1058,26 +1085,45 @@ static int xc5000_get_status(struct dvb_frontend *fe, u32 *status)
return 0;
}

static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe)
static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force)
{
struct xc5000_priv *priv = fe->tuner_priv;
int ret = XC_RESULT_SUCCESS;
u16 pll_lock_status;

if (force || xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {

fw_retry:

if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
ret = xc5000_fwupload(fe);
if (ret != XC_RESULT_SUCCESS)
return ret;

msleep(20);

/* Start the tuner self-calibration process */
ret |= xc_initialize(priv);

if (ret != XC_RESULT_SUCCESS)
goto fw_retry;

/* Wait for calibration to complete.
* We could continue but XC5000 will clock stretch subsequent
* I2C transactions until calibration is complete. This way we
* don't have to rely on clock stretching working.
*/
xc_wait(100);

if (priv->pll_register_no) {
xc5000_readreg(priv, priv->pll_register_no,
&pll_lock_status);
if (pll_lock_status > 63) {
/* PLL is unlocked, force reload of the firmware */
printk(KERN_ERR "xc5000: PLL not running after fwload.\n");
goto fw_retry;
}
}

/* Default to "CABLE" mode */
ret |= xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE);
}
Expand Down Expand Up @@ -1113,7 +1159,7 @@ static int xc5000_init(struct dvb_frontend *fe)
struct xc5000_priv *priv = fe->tuner_priv;
dprintk(1, "%s()\n", __func__);

if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) {
printk(KERN_ERR "xc5000: Unable to initialise tuner\n");
return -EREMOTEIO;
}
Expand Down

0 comments on commit de49bc6

Please sign in to comment.