cx24116.c
来自「trident tm5600的linux驱动」· C语言 代码 · 共 1,488 行 · 第 1/3 页
C
1,488 行
} /* Make sure we don't recurse back through here * during loading */ state->skip_fw_load = 1; ret = cx24116_load_firmware(fe, fw); if (ret) printk(KERN_ERR "%s: Writing firmware to device failed\n", __func__); release_firmware(fw); printk(KERN_INFO "%s: Firmware upload %s\n", __func__, ret == 0 ? "complete" : "failed"); /* Ensure firmware is always loaded if required */ state->skip_fw_load = 0; } return ret;}/* Take a basic firmware command structure, format it * and forward it for processing */static int cx24116_cmd_execute(struct dvb_frontend *fe, struct cx24116_cmd *cmd){ struct cx24116_state *state = fe->demodulator_priv; int i, ret; dprintk("%s()\n", __func__); /* Load the firmware if required */ ret = cx24116_firmware_ondemand(fe); if (ret != 0) { printk(KERN_ERR "%s(): Unable initialise the firmware\n", __func__); return ret; } /* Write the command */ for (i = 0; i < cmd->len ; i++) { dprintk("%s: 0x%02x == 0x%02x\n", __func__, i, cmd->args[i]); cx24116_writereg(state, i, cmd->args[i]); } /* Start execution and wait for cmd to terminate */ cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01); while (cx24116_readreg(state, CX24116_REG_EXECUTE)) { msleep(10); if (i++ > 64) { /* Avoid looping forever if the firmware does not respond */ printk(KERN_WARNING "%s() Firmware not responding\n", __func__); return -EREMOTEIO; } } return 0;}static int cx24116_load_firmware(struct dvb_frontend *fe, const struct firmware *fw){ struct cx24116_state *state = fe->demodulator_priv; struct cx24116_cmd cmd; int i, ret; unsigned char vers[4]; dprintk("%s\n", __func__); dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", fw->size, fw->data[0], fw->data[1], fw->data[fw->size-2], fw->data[fw->size-1]); /* Toggle 88x SRST pin to reset demod */ if (state->config->reset_device) state->config->reset_device(fe); /* Begin the firmware load process */ /* Prepare the demod, load the firmware, cleanup after load */ /* Init PLL */ cx24116_writereg(state, 0xE5, 0x00); cx24116_writereg(state, 0xF1, 0x08); cx24116_writereg(state, 0xF2, 0x13); /* Start PLL */ cx24116_writereg(state, 0xe0, 0x03); cx24116_writereg(state, 0xe0, 0x00); /* Unknown */ cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46); cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00); /* Unknown */ cx24116_writereg(state, 0xF0, 0x03); cx24116_writereg(state, 0xF4, 0x81); cx24116_writereg(state, 0xF5, 0x00); cx24116_writereg(state, 0xF6, 0x00); /* write the entire firmware as one transaction */ cx24116_writeregN(state, 0xF7, fw->data, fw->size); cx24116_writereg(state, 0xF4, 0x10); cx24116_writereg(state, 0xF0, 0x00); cx24116_writereg(state, 0xF8, 0x06); /* Firmware CMD 10: VCO config */ cmd.args[0x00] = CMD_SET_VCO; cmd.args[0x01] = 0x05; cmd.args[0x02] = 0xdc; cmd.args[0x03] = 0xda; cmd.args[0x04] = 0xae; cmd.args[0x05] = 0xaa; cmd.args[0x06] = 0x04; cmd.args[0x07] = 0x9d; cmd.args[0x08] = 0xfc; cmd.args[0x09] = 0x06; cmd.len = 0x0a; ret = cx24116_cmd_execute(fe, &cmd); if (ret != 0) return ret; cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00); /* Firmware CMD 14: Tuner config */ cmd.args[0x00] = CMD_TUNERINIT; cmd.args[0x01] = 0x00; cmd.args[0x02] = 0x00; cmd.len = 0x03; ret = cx24116_cmd_execute(fe, &cmd); if (ret != 0) return ret; cx24116_writereg(state, 0xe5, 0x00); /* Firmware CMD 13: MPEG config */ cmd.args[0x00] = CMD_MPEGCONFIG; cmd.args[0x01] = 0x01; cmd.args[0x02] = 0x75; cmd.args[0x03] = 0x00; if (state->config->mpg_clk_pos_pol) cmd.args[0x04] = state->config->mpg_clk_pos_pol; else cmd.args[0x04] = 0x02; cmd.args[0x05] = 0x00; cmd.len = 0x06; ret = cx24116_cmd_execute(fe, &cmd); if (ret != 0) return ret; /* Firmware CMD 35: Get firmware version */ cmd.args[0x00] = CMD_UPDFWVERS; cmd.len = 0x02; for (i = 0; i < 4; i++) { cmd.args[0x01] = i; ret = cx24116_cmd_execute(fe, &cmd); if (ret != 0) return ret; vers[i] = cx24116_readreg(state, CX24116_REG_MAILBOX); } printk(KERN_INFO "%s: FW version %i.%i.%i.%i\n", __func__, vers[0], vers[1], vers[2], vers[3]); return 0;}static int cx24116_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage){ /* The isl6421 module will override this function in the fops. */ dprintk("%s() This should never appear if the isl6421 module " "is loaded correctly\n", __func__); return -EOPNOTSUPP;}static int cx24116_read_status(struct dvb_frontend *fe, fe_status_t *status){ struct cx24116_state *state = fe->demodulator_priv; int lock = cx24116_readreg(state, CX24116_REG_SSTATUS); dprintk("%s: status = 0x%02x\n", __func__, lock); *status = 0; if (lock & CX24116_HAS_SIGNAL) *status |= FE_HAS_SIGNAL; if (lock & CX24116_HAS_CARRIER) *status |= FE_HAS_CARRIER; if (lock & CX24116_HAS_VITERBI) *status |= FE_HAS_VITERBI; if (lock & CX24116_HAS_SYNCLOCK) *status |= FE_HAS_SYNC | FE_HAS_LOCK; return 0;}static int cx24116_read_ber(struct dvb_frontend *fe, u32 *ber){ struct cx24116_state *state = fe->demodulator_priv; dprintk("%s()\n", __func__); *ber = (cx24116_readreg(state, CX24116_REG_BER24) << 24) | (cx24116_readreg(state, CX24116_REG_BER16) << 16) | (cx24116_readreg(state, CX24116_REG_BER8) << 8) | cx24116_readreg(state, CX24116_REG_BER0); return 0;}/* TODO Determine function and scale appropriately */static int cx24116_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength){ struct cx24116_state *state = fe->demodulator_priv; struct cx24116_cmd cmd; int ret; u16 sig_reading; dprintk("%s()\n", __func__); /* Firmware CMD 19: Get AGC */ cmd.args[0x00] = CMD_GETAGC; cmd.len = 0x01; ret = cx24116_cmd_execute(fe, &cmd); if (ret != 0) return ret; sig_reading = (cx24116_readreg(state, CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK) | (cx24116_readreg(state, CX24116_REG_SIGNAL) << 6); *signal_strength = 0 - sig_reading; dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __func__, sig_reading, *signal_strength); return 0;}/* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */static int cx24116_read_snr_pct(struct dvb_frontend *fe, u16 *snr){ struct cx24116_state *state = fe->demodulator_priv; u8 snr_reading; static const u32 snr_tab[] = { /* 10 x Table (rounded up) */ 0x00000, 0x0199A, 0x03333, 0x04ccD, 0x06667, 0x08000, 0x0999A, 0x0b333, 0x0cccD, 0x0e667, 0x10000, 0x1199A, 0x13333, 0x14ccD, 0x16667, 0x18000 }; dprintk("%s()\n", __func__); snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY0); if (snr_reading >= 0xa0 /* 100% */) *snr = 0xffff; else *snr = snr_tab[(snr_reading & 0xf0) >> 4] + (snr_tab[(snr_reading & 0x0f)] >> 4); dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, snr_reading, *snr); return 0;}/* The reelbox patches show the value in the registers represents * ESNO, from 0->30db (values 0->300). We provide this value by * default. */static int cx24116_read_snr_esno(struct dvb_frontend *fe, u16 *snr){ struct cx24116_state *state = fe->demodulator_priv; dprintk("%s()\n", __func__); *snr = cx24116_readreg(state, CX24116_REG_QUALITY8) << 8 | cx24116_readreg(state, CX24116_REG_QUALITY0); dprintk("%s: raw 0x%04x\n", __func__, *snr); return 0;}static int cx24116_read_snr(struct dvb_frontend *fe, u16 *snr){ if (esno_snr == 1) return cx24116_read_snr_esno(fe, snr); else return cx24116_read_snr_pct(fe, snr);}static int cx24116_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks){ struct cx24116_state *state = fe->demodulator_priv; dprintk("%s()\n", __func__); *ucblocks = (cx24116_readreg(state, CX24116_REG_UCB8) << 8) | cx24116_readreg(state, CX24116_REG_UCB0); return 0;}/* Overwrite the current tuning params, we are about to tune */static void cx24116_clone_params(struct dvb_frontend *fe){ struct cx24116_state *state = fe->demodulator_priv; memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur));}/* Wait for LNB */static int cx24116_wait_for_lnb(struct dvb_frontend *fe){ struct cx24116_state *state = fe->demodulator_priv; int i; dprintk("%s() qstatus = 0x%02x\n", __func__, cx24116_readreg(state, CX24116_REG_QSTATUS)); /* Wait for up to 300 ms */ for (i = 0; i < 30 ; i++) { if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20) return 0; msleep(10); } dprintk("%s(): LNB not ready\n", __func__); return -ETIMEDOUT; /* -EBUSY ? */}static int cx24116_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone){ struct cx24116_cmd cmd; int ret; dprintk("%s(%d)\n", __func__, tone); if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone); return -EINVAL; } /* Wait for LNB ready */ ret = cx24116_wait_for_lnb(fe); if (ret != 0) return ret; /* Min delay time after DiSEqC send */ msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ /* This is always done before the tone is set */ cmd.args[0x00] = CMD_SET_TONEPRE; cmd.args[0x01] = 0x00; cmd.len = 0x02; ret = cx24116_cmd_execute(fe, &cmd); if (ret != 0) return ret; /* Now we set the tone */ cmd.args[0x00] = CMD_SET_TONE; cmd.args[0x01] = 0x00; cmd.args[0x02] = 0x00; switch (tone) { case SEC_TONE_ON: dprintk("%s: setting tone on\n", __func__); cmd.args[0x03] = 0x01; break; case SEC_TONE_OFF: dprintk("%s: setting tone off\n", __func__); cmd.args[0x03] = 0x00; break; } cmd.len = 0x04; /* Min delay time before DiSEqC send */ msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ return cx24116_cmd_execute(fe, &cmd);}/* Initialise DiSEqC */static int cx24116_diseqc_init(struct dvb_frontend *fe){ struct cx24116_state *state = fe->demodulator_priv; struct cx24116_cmd cmd; int ret; /* Firmware CMD 20: LNB/DiSEqC config */ cmd.args[0x00] = CMD_LNBCONFIG; cmd.args[0x01] = 0x00; cmd.args[0x02] = 0x10; cmd.args[0x03] = 0x00; cmd.args[0x04] = 0x8f; cmd.args[0x05] = 0x28; cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01; cmd.args[0x07] = 0x01; cmd.len = 0x08; ret = cx24116_cmd_execute(fe, &cmd); if (ret != 0) return ret; /* Prepare a DiSEqC command */ state->dsec_cmd.args[0x00] = CMD_LNBSEND; /* DiSEqC burst */ state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A; /* Unknown */ state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02; state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00; /* Continuation flag? */ state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; /* DiSEqC message length */ state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00; /* Command length */ state->dsec_cmd.len = CX24116_DISEQC_MSGOFS; return 0;}/* Send DiSEqC message with derived burst (hack) || previous burst */static int cx24116_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *d){ struct cx24116_state *state = fe->demodulator_priv; int i, ret; /* Dump DiSEqC message */ if (debug) { printk(KERN_INFO "cx24116: %s(", __func__); for (i = 0 ; i < d->msg_len ;) { printk(KERN_INFO "0x%02x", d->msg[i]); if (++i < d->msg_len) printk(KERN_INFO ", "); } printk(") toneburst=%d\n", toneburst); } /* Validate length */ if (d->msg_len > (CX24116_ARGLEN - CX24116_DISEQC_MSGOFS)) return -EINVAL; /* DiSEqC message */ for (i = 0; i < d->msg_len; i++) state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i]; /* DiSEqC message length */ state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len; /* Command length */ state->dsec_cmd.len = CX24116_DISEQC_MSGOFS + state->dsec_cmd.args[CX24116_DISEQC_MSGLEN]; /* DiSEqC toneburst */ if (toneburst == CX24116_DISEQC_MESGCACHE) /* Message is cached */ return 0; else if (toneburst == CX24116_DISEQC_TONEOFF) /* Message is sent without burst */ state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0; else if (toneburst == CX24116_DISEQC_TONECACHE) { /* * Message is sent with derived else cached burst * * WRITE PORT GROUP COMMAND 38 * * 0/A/A: E0 10 38 F0..F3 * 1/B/B: E0 10 38 F4..F7 * 2/C/A: E0 10 38 F8..FB * 3/D/B: E0 10 38 FC..FF * * databyte[3]= 8421:8421 * ABCD:WXYZ * CLR :SET * * WX= PORT SELECT 0..3 (X=TONEBURST) * Y = VOLTAGE (0=13V, 1=18V) * Z = BAND (0=LOW, 1=HIGH(22K)) */ if (d->msg_len >= 4 && d->msg[2] == 0x38) state->dsec_cmd.args[CX24116_DISEQC_BURST] =
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?