cx24116.c

来自「trident tm5600的linux驱动」· C语言 代码 · 共 1,488 行 · 第 1/3 页

C
1,488
字号
				((d->msg[3] & 4) >> 2);		if (debug)			dprintk("%s burst=%d\n", __func__,				state->dsec_cmd.args[CX24116_DISEQC_BURST]);	}	/* Wait for LNB ready */	ret = cx24116_wait_for_lnb(fe);	if (ret != 0)		return ret;	/* Wait for voltage/min repeat delay */	msleep(100);	/* Command */	ret = cx24116_cmd_execute(fe, &state->dsec_cmd);	if (ret != 0)		return ret;	/*	 * Wait for send	 *	 * Eutelsat spec:	 * >15ms delay          + (XXX determine if FW does this, see set_tone)	 *  13.5ms per byte     +	 * >15ms delay          +	 *  12.5ms burst        +	 * >15ms delay            (XXX determine if FW does this, see set_tone)	 */	msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) +		((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60));	return 0;}/* Send DiSEqC burst */static int cx24116_diseqc_send_burst(struct dvb_frontend *fe,	fe_sec_mini_cmd_t burst){	struct cx24116_state *state = fe->demodulator_priv;	int ret;	dprintk("%s(%d) toneburst=%d\n", __func__, burst, toneburst);	/* DiSEqC burst */	if (burst == SEC_MINI_A)		state->dsec_cmd.args[CX24116_DISEQC_BURST] =			CX24116_DISEQC_MINI_A;	else if (burst == SEC_MINI_B)		state->dsec_cmd.args[CX24116_DISEQC_BURST] =			CX24116_DISEQC_MINI_B;	else		return -EINVAL;	/* DiSEqC toneburst */	if (toneburst != CX24116_DISEQC_MESGCACHE)		/* Burst is cached */		return 0;	/* Burst is to be sent with cached message */	/* Wait for LNB ready */	ret = cx24116_wait_for_lnb(fe);	if (ret != 0)		return ret;	/* Wait for voltage/min repeat delay */	msleep(100);	/* Command */	ret = cx24116_cmd_execute(fe, &state->dsec_cmd);	if (ret != 0)		return ret;	/*	 * Wait for send	 *	 * Eutelsat spec:	 * >15ms delay          + (XXX determine if FW does this, see set_tone)	 *  13.5ms per byte     +	 * >15ms delay          +	 *  12.5ms burst        +	 * >15ms delay            (XXX determine if FW does this, see set_tone)	 */	msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60);	return 0;}static void cx24116_release(struct dvb_frontend *fe){	struct cx24116_state *state = fe->demodulator_priv;	dprintk("%s\n", __func__);	kfree(state);}static struct dvb_frontend_ops cx24116_ops;struct dvb_frontend *cx24116_attach(const struct cx24116_config *config,	struct i2c_adapter *i2c){	struct cx24116_state *state = NULL;	int ret;	dprintk("%s\n", __func__);	/* allocate memory for the internal state */	state = kmalloc(sizeof(struct cx24116_state), GFP_KERNEL);	if (state == NULL)		goto error1;	/* setup the state */	memset(state, 0, sizeof(struct cx24116_state));	state->config = config;	state->i2c = i2c;	/* check if the demod is present */	ret = (cx24116_readreg(state, 0xFF) << 8) |		cx24116_readreg(state, 0xFE);	if (ret != 0x0501) {		printk(KERN_INFO "Invalid probe, probably not a CX24116 device\n");		goto error2;	}	/* create dvb_frontend */	memcpy(&state->frontend.ops, &cx24116_ops,		sizeof(struct dvb_frontend_ops));	state->frontend.demodulator_priv = state;	return &state->frontend;error2: kfree(state);error1: return NULL;}EXPORT_SYMBOL(cx24116_attach);#if 0static int cx24116_get_params(struct dvb_frontend *fe){	struct cx24116_state *state = fe->demodulator_priv;	struct dtv_frontend_properties *cache = &fe->dtv_property_cache;	dprintk("%s()\n", __func__);	cache->frequency = state->dcur.frequency;	cache->inversion = state->dcur.inversion;	cache->modulation = state->dcur.modulation;	cache->fec_inner = state->dcur.fec;	cache->symbol_rate = state->dcur.symbol_rate;	return 0;}#endif/* * Initialise or wake up device * * Power config will reset and load initial firmware if required */static int cx24116_initfe(struct dvb_frontend *fe){	struct cx24116_state *state = fe->demodulator_priv;	struct cx24116_cmd cmd;	int ret;	dprintk("%s()\n", __func__);	/* Power on */	cx24116_writereg(state, 0xe0, 0);	cx24116_writereg(state, 0xe1, 0);	cx24116_writereg(state, 0xea, 0);	/* Firmware CMD 36: Power config */	cmd.args[0x00] = CMD_TUNERSLEEP;	cmd.args[0x01] = 0;	cmd.len = 0x02;	ret = cx24116_cmd_execute(fe, &cmd);	if (ret != 0)		return ret;	return cx24116_diseqc_init(fe);}/* * Put device to sleep */static int cx24116_sleep(struct dvb_frontend *fe){	struct cx24116_state *state = fe->demodulator_priv;	struct cx24116_cmd cmd;	int ret;	dprintk("%s()\n", __func__);	/* Firmware CMD 36: Power config */	cmd.args[0x00] = CMD_TUNERSLEEP;	cmd.args[0x01] = 1;	cmd.len = 0x02;	ret = cx24116_cmd_execute(fe, &cmd);	if (ret != 0)		return ret;	/* Power off (Shutdown clocks) */	cx24116_writereg(state, 0xea, 0xff);	cx24116_writereg(state, 0xe1, 1);	cx24116_writereg(state, 0xe0, 1);	return 0;}static int cx24116_set_property(struct dvb_frontend *fe,	struct dtv_property *tvp){	dprintk("%s(..)\n", __func__);	return 0;}static int cx24116_get_property(struct dvb_frontend *fe,	struct dtv_property *tvp){	dprintk("%s(..)\n", __func__);	return 0;}/* dvb-core told us to tune, the tv property cache will be complete, * it's safe for is to pull values and use them for tuning purposes. */static int cx24116_set_frontend(struct dvb_frontend *fe,	struct dvb_frontend_parameters *p){	struct cx24116_state *state = fe->demodulator_priv;	struct dtv_frontend_properties *c = &fe->dtv_property_cache;	struct cx24116_cmd cmd;	fe_status_t tunerstat;	int i, status, ret, retune;	dprintk("%s()\n", __func__);	switch (c->delivery_system) {	case SYS_DVBS:		dprintk("%s: DVB-S delivery system selected\n", __func__);		/* Only QPSK is supported for DVB-S */		if (c->modulation != QPSK) {			dprintk("%s: unsupported modulation selected (%d)\n",				__func__, c->modulation);			return -EOPNOTSUPP;		}		/* Pilot doesn't exist in DVB-S, turn bit off */		state->dnxt.pilot_val = CX24116_PILOT_OFF;		retune = 1;		/* DVB-S only supports 0.35 */		if (c->rolloff != ROLLOFF_35) {			dprintk("%s: unsupported rolloff selected (%d)\n",				__func__, c->rolloff);			return -EOPNOTSUPP;		}		state->dnxt.rolloff_val = CX24116_ROLLOFF_035;		break;	case SYS_DVBS2:		dprintk("%s: DVB-S2 delivery system selected\n", __func__);		/*		 * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2,		 * but not hardware auto detection		 */		if (c->modulation != PSK_8 && c->modulation != QPSK) {			dprintk("%s: unsupported modulation selected (%d)\n",				__func__, c->modulation);			return -EOPNOTSUPP;		}		switch (c->pilot) {		case PILOT_AUTO:	/* Not supported but emulated */			state->dnxt.pilot_val = (c->modulation == QPSK)				? CX24116_PILOT_OFF : CX24116_PILOT_ON;			retune = 2;			break;		case PILOT_OFF:			state->dnxt.pilot_val = CX24116_PILOT_OFF;			break;		case PILOT_ON:			state->dnxt.pilot_val = CX24116_PILOT_ON;			break;		default:			dprintk("%s: unsupported pilot mode selected (%d)\n",				__func__, c->pilot);			return -EOPNOTSUPP;		}		switch (c->rolloff) {		case ROLLOFF_20:			state->dnxt.rolloff_val = CX24116_ROLLOFF_020;			break;		case ROLLOFF_25:			state->dnxt.rolloff_val = CX24116_ROLLOFF_025;			break;		case ROLLOFF_35:			state->dnxt.rolloff_val = CX24116_ROLLOFF_035;			break;		case ROLLOFF_AUTO:	/* Rolloff must be explicit */		default:			dprintk("%s: unsupported rolloff selected (%d)\n",				__func__, c->rolloff);			return -EOPNOTSUPP;		}		break;	default:		dprintk("%s: unsupported delivery system selected (%d)\n",			__func__, c->delivery_system);		return -EOPNOTSUPP;	}	state->dnxt.modulation = c->modulation;	state->dnxt.frequency = c->frequency;	state->dnxt.pilot = c->pilot;	state->dnxt.rolloff = c->rolloff;	ret = cx24116_set_inversion(state, c->inversion);	if (ret !=  0)		return ret;	/* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */	ret = cx24116_set_fec(state, c->modulation, c->fec_inner);	if (ret !=  0)		return ret;	ret = cx24116_set_symbolrate(state, c->symbol_rate);	if (ret !=  0)		return ret;	/* discard the 'current' tuning parameters and prepare to tune */	cx24116_clone_params(fe);	dprintk("%s:   modulation  = %d\n", __func__, state->dcur.modulation);	dprintk("%s:   frequency   = %d\n", __func__, state->dcur.frequency);	dprintk("%s:   pilot       = %d (val = 0x%02x)\n", __func__,		state->dcur.pilot, state->dcur.pilot_val);	dprintk("%s:   retune      = %d\n", __func__, retune);	dprintk("%s:   rolloff     = %d (val = 0x%02x)\n", __func__,		state->dcur.rolloff, state->dcur.rolloff_val);	dprintk("%s:   symbol_rate = %d\n", __func__, state->dcur.symbol_rate);	dprintk("%s:   FEC         = %d (mask/val = 0x%02x/0x%02x)\n", __func__,		state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val);	dprintk("%s:   Inversion   = %d (val = 0x%02x)\n", __func__,		state->dcur.inversion, state->dcur.inversion_val);	/* This is also done in advise/acquire on HVR4000 but not on LITE */	if (state->config->set_ts_params)		state->config->set_ts_params(fe, 0);	/* Set/Reset B/W */	cmd.args[0x00] = CMD_BANDWIDTH;	cmd.args[0x01] = 0x01;	cmd.len = 0x02;	ret = cx24116_cmd_execute(fe, &cmd);	if (ret != 0)		return ret;	/* Prepare a tune request */	cmd.args[0x00] = CMD_TUNEREQUEST;	/* Frequency */	cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16;	cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8;	cmd.args[0x03] = (state->dcur.frequency & 0x0000ff);	/* Symbol Rate */	cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8;	cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff);	/* Automatic Inversion */	cmd.args[0x06] = state->dcur.inversion_val;	/* Modulation / FEC / Pilot */	cmd.args[0x07] = state->dcur.fec_val | state->dcur.pilot_val;	cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8;	cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff;	cmd.args[0x0a] = 0x00;	cmd.args[0x0b] = 0x00;	cmd.args[0x0c] = state->dcur.rolloff_val;	cmd.args[0x0d] = state->dcur.fec_mask;	if (state->dcur.symbol_rate > 30000000) {		cmd.args[0x0e] = 0x04;		cmd.args[0x0f] = 0x00;		cmd.args[0x10] = 0x01;		cmd.args[0x11] = 0x77;		cmd.args[0x12] = 0x36;		cx24116_writereg(state, CX24116_REG_CLKDIV, 0x44);		cx24116_writereg(state, CX24116_REG_RATEDIV, 0x01);	} else {		cmd.args[0x0e] = 0x06;		cmd.args[0x0f] = 0x00;		cmd.args[0x10] = 0x00;		cmd.args[0x11] = 0xFA;		cmd.args[0x12] = 0x24;		cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);		cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);	}	cmd.len = 0x13;	/* We need to support pilot and non-pilot tuning in the	 * driver automatically. This is a workaround for because	 * the demod does not support autodetect.	 */	do {		/* Reset status register */		status = cx24116_readreg(state, CX24116_REG_SSTATUS)			& CX24116_SIGNAL_MASK;		cx24116_writereg(state, CX24116_REG_SSTATUS, status);		/* Tune */		ret = cx24116_cmd_execute(fe, &cmd);		if (ret != 0)			break;		/*		 * Wait for up to 500 ms before retrying		 *		 * If we are able to tune then generally it occurs within 100ms.		 * If it takes longer, try a different toneburst setting.		 */		for (i = 0; i < 50 ; i++) {			cx24116_read_status(fe, &tunerstat);			status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC);			if (status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) {				dprintk("%s: Tuned\n", __func__);				goto tuned;			}			msleep(10);		}		dprintk("%s: Not tuned\n", __func__);		/* Toggle pilot bit when in auto-pilot */		if (state->dcur.pilot == PILOT_AUTO)			cmd.args[0x07] ^= CX24116_PILOT_ON;	} while (--retune);tuned:  /* Set/Reset B/W */	cmd.args[0x00] = CMD_BANDWIDTH;	cmd.args[0x01] = 0x00;	cmd.len = 0x02;	ret = cx24116_cmd_execute(fe, &cmd);	if (ret != 0)		return ret;	return ret;}static struct dvb_frontend_ops cx24116_ops = {	.info = {		.name = "Conexant CX24116/CX24118",		.type = FE_QPSK,		.frequency_min = 950000,		.frequency_max = 2150000,		.frequency_stepsize = 1011, /* kHz for QPSK frontends */		.frequency_tolerance = 5000,		.symbol_rate_min = 1000000,		.symbol_rate_max = 45000000,		.caps = FE_CAN_INVERSION_AUTO |			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |			FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |			FE_CAN_QPSK | FE_CAN_RECOVER	},	.release = cx24116_release,	.init = cx24116_initfe,	.sleep = cx24116_sleep,	.read_status = cx24116_read_status,	.read_ber = cx24116_read_ber,	.read_signal_strength = cx24116_read_signal_strength,	.read_snr = cx24116_read_snr,	.read_ucblocks = cx24116_read_ucblocks,	.set_tone = cx24116_set_tone,	.set_voltage = cx24116_set_voltage,	.diseqc_send_master_cmd = cx24116_send_diseqc_msg,	.diseqc_send_burst = cx24116_diseqc_send_burst,	.set_property = cx24116_set_property,	.get_property = cx24116_get_property,	.set_frontend = cx24116_set_frontend,};MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware");MODULE_AUTHOR("Steven Toth");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?