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 + -
显示快捷键?