diff options
Diffstat (limited to 'drivers/media/dvb/frontends/drxk_hard.c')
-rw-r--r-- | drivers/media/dvb/frontends/drxk_hard.c | 350 |
1 files changed, 265 insertions, 85 deletions
diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c index 60b868faeacf..1ab8154542da 100644 --- a/drivers/media/dvb/frontends/drxk_hard.c +++ b/drivers/media/dvb/frontends/drxk_hard.c @@ -28,6 +28,7 @@ #include <linux/delay.h> #include <linux/firmware.h> #include <linux/i2c.h> +#include <linux/hardirq.h> #include <asm/div64.h> #include "dvb_frontend.h" @@ -308,16 +309,42 @@ static u32 Log10Times100(u32 x) /* I2C **********************************************************************/ /****************************************************************************/ -static int i2c_read1(struct i2c_adapter *adapter, u8 adr, u8 *val) +static int drxk_i2c_lock(struct drxk_state *state) +{ + i2c_lock_adapter(state->i2c); + state->drxk_i2c_exclusive_lock = true; + + return 0; +} + +static void drxk_i2c_unlock(struct drxk_state *state) +{ + if (!state->drxk_i2c_exclusive_lock) + return; + + i2c_unlock_adapter(state->i2c); + state->drxk_i2c_exclusive_lock = false; +} + +static int drxk_i2c_transfer(struct drxk_state *state, struct i2c_msg *msgs, + unsigned len) +{ + if (state->drxk_i2c_exclusive_lock) + return __i2c_transfer(state->i2c, msgs, len); + else + return i2c_transfer(state->i2c, msgs, len); +} + +static int i2c_read1(struct drxk_state *state, u8 adr, u8 *val) { struct i2c_msg msgs[1] = { {.addr = adr, .flags = I2C_M_RD, .buf = val, .len = 1} }; - return i2c_transfer(adapter, msgs, 1); + return drxk_i2c_transfer(state, msgs, 1); } -static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len) +static int i2c_write(struct drxk_state *state, u8 adr, u8 *data, int len) { int status; struct i2c_msg msg = { @@ -330,7 +357,7 @@ static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len) printk(KERN_CONT " %02x", data[i]); printk(KERN_CONT "\n"); } - status = i2c_transfer(adap, &msg, 1); + status = drxk_i2c_transfer(state, &msg, 1); if (status >= 0 && status != 1) status = -EIO; @@ -340,7 +367,7 @@ static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len) return status; } -static int i2c_read(struct i2c_adapter *adap, +static int i2c_read(struct drxk_state *state, u8 adr, u8 *msg, int len, u8 *answ, int alen) { int status; @@ -351,7 +378,7 @@ static int i2c_read(struct i2c_adapter *adap, .buf = answ, .len = alen} }; - status = i2c_transfer(adap, msgs, 2); + status = drxk_i2c_transfer(state, msgs, 2); if (status != 2) { if (debug > 2) printk(KERN_CONT ": ERROR!\n"); @@ -394,7 +421,7 @@ static int read16_flags(struct drxk_state *state, u32 reg, u16 *data, u8 flags) len = 2; } dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags); - status = i2c_read(state->i2c, adr, mm1, len, mm2, 2); + status = i2c_read(state, adr, mm1, len, mm2, 2); if (status < 0) return status; if (data) @@ -428,7 +455,7 @@ static int read32_flags(struct drxk_state *state, u32 reg, u32 *data, u8 flags) len = 2; } dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags); - status = i2c_read(state->i2c, adr, mm1, len, mm2, 4); + status = i2c_read(state, adr, mm1, len, mm2, 4); if (status < 0) return status; if (data) @@ -464,7 +491,7 @@ static int write16_flags(struct drxk_state *state, u32 reg, u16 data, u8 flags) mm[len + 1] = (data >> 8) & 0xff; dprintk(2, "(0x%08x, 0x%04x, 0x%02x)\n", reg, data, flags); - return i2c_write(state->i2c, adr, mm, len + 2); + return i2c_write(state, adr, mm, len + 2); } static int write16(struct drxk_state *state, u32 reg, u16 data) @@ -495,7 +522,7 @@ static int write32_flags(struct drxk_state *state, u32 reg, u32 data, u8 flags) mm[len + 3] = (data >> 24) & 0xff; dprintk(2, "(0x%08x, 0x%08x, 0x%02x)\n", reg, data, flags); - return i2c_write(state->i2c, adr, mm, len + 4); + return i2c_write(state, adr, mm, len + 4); } static int write32(struct drxk_state *state, u32 reg, u32 data) @@ -542,7 +569,7 @@ static int write_block(struct drxk_state *state, u32 Address, printk(KERN_CONT " %02x", pBlock[i]); printk(KERN_CONT "\n"); } - status = i2c_write(state->i2c, state->demod_address, + status = i2c_write(state, state->demod_address, &state->Chunk[0], Chunk + AdrLength); if (status < 0) { printk(KERN_ERR "drxk: %s: i2c write error at addr 0x%02x\n", @@ -568,17 +595,17 @@ int PowerUpDevice(struct drxk_state *state) dprintk(1, "\n"); - status = i2c_read1(state->i2c, state->demod_address, &data); + status = i2c_read1(state, state->demod_address, &data); if (status < 0) { do { data = 0; - status = i2c_write(state->i2c, state->demod_address, + status = i2c_write(state, state->demod_address, &data, 1); msleep(10); retryCount++; if (status < 0) continue; - status = i2c_read1(state->i2c, state->demod_address, + status = i2c_read1(state, state->demod_address, &data); } while (status < 0 && (retryCount < DRXK_MAX_RETRIES_POWERUP)); @@ -932,7 +959,7 @@ static int GetDeviceCapabilities(struct drxk_state *state) if (status < 0) goto error; -printk(KERN_ERR "drxk: status = 0x%08x\n", sioTopJtagidLo); + printk(KERN_INFO "drxk: status = 0x%08x\n", sioTopJtagidLo); /* driver 0.9.0 */ switch ((sioTopJtagidLo >> 29) & 0xF) { @@ -2824,7 +2851,7 @@ static int ConfigureI2CBridge(struct drxk_state *state, bool bEnableBridge) dprintk(1, "\n"); if (state->m_DrxkState == DRXK_UNINITIALIZED) - goto error; + return 0; if (state->m_DrxkState == DRXK_POWERED_DOWN) goto error; @@ -2977,7 +3004,7 @@ static int ADCSynchronization(struct drxk_state *state) status = read16(state, IQM_AF_CLKNEG__A, &clkNeg); if (status < 0) goto error; - if ((clkNeg | IQM_AF_CLKNEG_CLKNEGDATA__M) == + if ((clkNeg & IQM_AF_CLKNEG_CLKNEGDATA__M) == IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS) { clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M)); clkNeg |= @@ -5361,7 +5388,7 @@ static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus) SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK, 0, NULL, 2, Result); if (status < 0) - printk(KERN_ERR "drxk: %s status = %08x\n", __func__, status); + printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED) { /* 0x0000 NOT LOCKED */ @@ -5388,12 +5415,67 @@ static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus) #define QAM_LOCKRANGE__M 0x10 #define QAM_LOCKRANGE_NORMAL 0x10 +static int QAMDemodulatorCommand(struct drxk_state *state, + int numberOfParameters) +{ + int status; + u16 cmdResult; + u16 setParamParameters[4] = { 0, 0, 0, 0 }; + + setParamParameters[0] = state->m_Constellation; /* modulation */ + setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */ + + if (numberOfParameters == 2) { + u16 setEnvParameters[1] = { 0 }; + + if (state->m_OperationMode == OM_QAM_ITU_C) + setEnvParameters[0] = QAM_TOP_ANNEX_C; + else + setEnvParameters[0] = QAM_TOP_ANNEX_A; + + status = scu_command(state, + SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, + 1, setEnvParameters, 1, &cmdResult); + if (status < 0) + goto error; + + status = scu_command(state, + SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, + numberOfParameters, setParamParameters, + 1, &cmdResult); + } else if (numberOfParameters == 4) { + if (state->m_OperationMode == OM_QAM_ITU_C) + setParamParameters[2] = QAM_TOP_ANNEX_C; + else + setParamParameters[2] = QAM_TOP_ANNEX_A; + + setParamParameters[3] |= (QAM_MIRROR_AUTO_ON); + /* Env parameters */ + /* check for LOCKRANGE Extented */ + /* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */ + + status = scu_command(state, + SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, + numberOfParameters, setParamParameters, + 1, &cmdResult); + } else { + printk(KERN_WARNING "drxk: Unknown QAM demodulator parameter " + "count %d\n", numberOfParameters); + } + +error: + if (status < 0) + printk(KERN_WARNING "drxk: Warning %d on %s\n", + status, __func__); + return status; +} + static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz, s32 tunerFreqOffset) { int status; - u16 setParamParameters[4] = { 0, 0, 0, 0 }; u16 cmdResult; + int qamDemodParamCount = state->qam_demod_parameter_count; dprintk(1, "\n"); /* @@ -5445,34 +5527,42 @@ static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz, } if (status < 0) goto error; - setParamParameters[0] = state->m_Constellation; /* modulation */ - setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */ - if (state->m_OperationMode == OM_QAM_ITU_C) - setParamParameters[2] = QAM_TOP_ANNEX_C; - else - setParamParameters[2] = QAM_TOP_ANNEX_A; - setParamParameters[3] |= (QAM_MIRROR_AUTO_ON); - /* Env parameters */ - /* check for LOCKRANGE Extented */ - /* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */ - status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, 4, setParamParameters, 1, &cmdResult); - if (status < 0) { - /* Fall-back to the simpler call */ - if (state->m_OperationMode == OM_QAM_ITU_C) - setParamParameters[0] = QAM_TOP_ANNEX_C; - else - setParamParameters[0] = QAM_TOP_ANNEX_A; - status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, 1, setParamParameters, 1, &cmdResult); - if (status < 0) - goto error; + /* Use the 4-parameter if it's requested or we're probing for + * the correct command. */ + if (state->qam_demod_parameter_count == 4 + || !state->qam_demod_parameter_count) { + qamDemodParamCount = 4; + status = QAMDemodulatorCommand(state, qamDemodParamCount); + } - setParamParameters[0] = state->m_Constellation; /* modulation */ - setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */ - status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, 2, setParamParameters, 1, &cmdResult); + /* Use the 2-parameter command if it was requested or if we're + * probing for the correct command and the 4-parameter command + * failed. */ + if (state->qam_demod_parameter_count == 2 + || (!state->qam_demod_parameter_count && status < 0)) { + qamDemodParamCount = 2; + status = QAMDemodulatorCommand(state, qamDemodParamCount); } - if (status < 0) + + if (status < 0) { + dprintk(1, "Could not set demodulator parameters. Make " + "sure qam_demod_parameter_count (%d) is correct for " + "your firmware (%s).\n", + state->qam_demod_parameter_count, + state->microcode_name); goto error; + } else if (!state->qam_demod_parameter_count) { + dprintk(1, "Auto-probing the correct QAM demodulator command " + "parameters was successful - using %d parameters.\n", + qamDemodParamCount); + + /* + * One of our commands was successful. We don't need to + * auto-probe anymore, now that we got the correct command. + */ + state->qam_demod_parameter_count = qamDemodParamCount; + } /* * STEP 3: enable the system in a mode where the ADC provides valid @@ -5968,34 +6058,15 @@ error: return status; } -static int load_microcode(struct drxk_state *state, const char *mc_name) -{ - const struct firmware *fw = NULL; - int err = 0; - - dprintk(1, "\n"); - - err = request_firmware(&fw, mc_name, state->i2c->dev.parent); - if (err < 0) { - printk(KERN_ERR - "drxk: Could not load firmware file %s.\n", mc_name); - printk(KERN_INFO - "drxk: Copy %s to your hotplug directory!\n", mc_name); - return err; - } - err = DownloadMicrocode(state, fw->data, fw->size); - release_firmware(fw); - return err; -} - static int init_drxk(struct drxk_state *state) { - int status = 0; + int status = 0, n = 0; enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM; u16 driverVersion; dprintk(1, "\n"); if ((state->m_DrxkState == DRXK_UNINITIALIZED)) { + drxk_i2c_lock(state); status = PowerUpDevice(state); if (status < 0) goto error; @@ -6073,8 +6144,12 @@ static int init_drxk(struct drxk_state *state) if (status < 0) goto error; - if (state->microcode_name) - load_microcode(state, state->microcode_name); + if (state->fw) { + status = DownloadMicrocode(state, state->fw->data, + state->fw->size); + if (status < 0) + goto error; + } /* disable token-ring bus through OFDM block for possible ucode upload */ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_OFF); @@ -6167,19 +6242,71 @@ static int init_drxk(struct drxk_state *state) state->m_DrxkState = DRXK_POWERED_DOWN; } else state->m_DrxkState = DRXK_STOPPED; + + /* Initialize the supported delivery systems */ + n = 0; + if (state->m_hasDVBC) { + state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A; + state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C; + strlcat(state->frontend.ops.info.name, " DVB-C", + sizeof(state->frontend.ops.info.name)); + } + if (state->m_hasDVBT) { + state->frontend.ops.delsys[n++] = SYS_DVBT; + strlcat(state->frontend.ops.info.name, " DVB-T", + sizeof(state->frontend.ops.info.name)); + } + drxk_i2c_unlock(state); } error: - if (status < 0) + if (status < 0) { + state->m_DrxkState = DRXK_NO_DEV; + drxk_i2c_unlock(state); printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); + } return status; } +static void load_firmware_cb(const struct firmware *fw, + void *context) +{ + struct drxk_state *state = context; + + dprintk(1, ": %s\n", fw ? "firmware loaded" : "firmware not loaded"); + if (!fw) { + printk(KERN_ERR + "drxk: Could not load firmware file %s.\n", + state->microcode_name); + printk(KERN_INFO + "drxk: Copy %s to your hotplug directory!\n", + state->microcode_name); + state->microcode_name = NULL; + + /* + * As firmware is now load asynchronous, it is not possible + * anymore to fail at frontend attach. We might silently + * return here, and hope that the driver won't crash. + * We might also change all DVB callbacks to return -ENODEV + * if the device is not initialized. + * As the DRX-K devices have their own internal firmware, + * let's just hope that it will match a firmware revision + * compatible with this driver and proceed. + */ + } + state->fw = fw; + + init_drxk(state); +} + static void drxk_release(struct dvb_frontend *fe) { struct drxk_state *state = fe->demodulator_priv; dprintk(1, "\n"); + if (state->fw) + release_firmware(state->fw); + kfree(state); } @@ -6188,6 +6315,12 @@ static int drxk_sleep(struct dvb_frontend *fe) struct drxk_state *state = fe->demodulator_priv; dprintk(1, "\n"); + + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return 0; + ShutDown(state); return 0; } @@ -6196,7 +6329,11 @@ static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) { struct drxk_state *state = fe->demodulator_priv; - dprintk(1, "%s\n", enable ? "enable" : "disable"); + dprintk(1, ": %s\n", enable ? "enable" : "disable"); + + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + return ConfigureI2CBridge(state, enable ? true : false); } @@ -6209,6 +6346,12 @@ static int drxk_set_parameters(struct dvb_frontend *fe) dprintk(1, "\n"); + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return -EAGAIN; + if (!fe->ops.tuner_ops.get_if_frequency) { printk(KERN_ERR "drxk: Error: get_if_frequency() not defined at tuner. Can't work without it!\n"); @@ -6262,6 +6405,12 @@ static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status) u32 stat; dprintk(1, "\n"); + + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return -EAGAIN; + *status = 0; GetLockStatus(state, &stat, 0); if (stat == MPEG_LOCK) @@ -6275,8 +6424,15 @@ static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status) static int drxk_read_ber(struct dvb_frontend *fe, u32 *ber) { + struct drxk_state *state = fe->demodulator_priv; + dprintk(1, "\n"); + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return -EAGAIN; + *ber = 0; return 0; } @@ -6288,6 +6444,12 @@ static int drxk_read_signal_strength(struct dvb_frontend *fe, u32 val = 0; dprintk(1, "\n"); + + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return -EAGAIN; + ReadIFAgc(state, &val); *strength = val & 0xffff; return 0; @@ -6299,6 +6461,12 @@ static int drxk_read_snr(struct dvb_frontend *fe, u16 *snr) s32 snr2; dprintk(1, "\n"); + + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return -EAGAIN; + GetSignalToNoise(state, &snr2); *snr = snr2 & 0xffff; return 0; @@ -6310,6 +6478,12 @@ static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) u16 err; dprintk(1, "\n"); + + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return -EAGAIN; + DVBTQAMGetAccPktErr(state, &err); *ucblocks = (u32) err; return 0; @@ -6318,9 +6492,16 @@ static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) static int drxk_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *sets) { + struct drxk_state *state = fe->demodulator_priv; struct dtv_frontend_properties *p = &fe->dtv_property_cache; dprintk(1, "\n"); + + if (state->m_DrxkState == DRXK_NO_DEV) + return -ENODEV; + if (state->m_DrxkState == DRXK_UNINITIALIZED) + return -EAGAIN; + switch (p->delivery_system) { case SYS_DVBC_ANNEX_A: case SYS_DVBC_ANNEX_C: @@ -6371,10 +6552,9 @@ static struct dvb_frontend_ops drxk_ops = { struct dvb_frontend *drxk_attach(const struct drxk_config *config, struct i2c_adapter *i2c) { - int n; - struct drxk_state *state = NULL; u8 adr = config->adr; + int status; dprintk(1, "\n"); state = kzalloc(sizeof(struct drxk_state), GFP_KERNEL); @@ -6385,6 +6565,7 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config, state->demod_address = adr; state->single_master = config->single_master; state->microcode_name = config->microcode_name; + state->qam_demod_parameter_count = config->qam_demod_parameter_count; state->no_i2c_bridge = config->no_i2c_bridge; state->antenna_gpio = config->antenna_gpio; state->antenna_dvbt = config->antenna_dvbt; @@ -6425,22 +6606,21 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config, state->frontend.demodulator_priv = state; init_state(state); - if (init_drxk(state) < 0) - goto error; - /* Initialize the supported delivery systems */ - n = 0; - if (state->m_hasDVBC) { - state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A; - state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C; - strlcat(state->frontend.ops.info.name, " DVB-C", - sizeof(state->frontend.ops.info.name)); - } - if (state->m_hasDVBT) { - state->frontend.ops.delsys[n++] = SYS_DVBT; - strlcat(state->frontend.ops.info.name, " DVB-T", - sizeof(state->frontend.ops.info.name)); - } + /* Load firmware and initialize DRX-K */ + if (state->microcode_name) { + status = request_firmware_nowait(THIS_MODULE, 1, + state->microcode_name, + state->i2c->dev.parent, + GFP_KERNEL, + state, load_firmware_cb); + if (status < 0) { + printk(KERN_ERR + "drxk: failed to request a firmware\n"); + return NULL; + } + } else if (init_drxk(state) < 0) + goto error; printk(KERN_INFO "drxk: frontend initialized.\n"); return &state->frontend; |