dib7000m.c

来自「linux 内核源代码」· C语言 代码 · 共 1,403 行 · 第 1/3 页

C
1,403
字号
		factor = 1;	else		factor = 6;	// always use the setting for 8MHz here lock_time for 7,6 MHz are longer	value = 30 * state->internal_clk * factor;	ret |= dib7000m_write_word(state, 6,  (u16) ((value >> 16) & 0xffff)); // lock0 wait time	ret |= dib7000m_write_word(state, 7,  (u16)  (value        & 0xffff)); // lock0 wait time	value = 100 * state->internal_clk * factor;	ret |= dib7000m_write_word(state, 8,  (u16) ((value >> 16) & 0xffff)); // lock1 wait time	ret |= dib7000m_write_word(state, 9,  (u16)  (value        & 0xffff)); // lock1 wait time	value = 500 * state->internal_clk * factor;	ret |= dib7000m_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time	ret |= dib7000m_write_word(state, 11, (u16)  (value        & 0xffff)); // lock2 wait time	// start search	value = dib7000m_read_word(state, 0);	ret |= dib7000m_write_word(state, 0, (u16) (value | (1 << 9)));	/* clear n_irq_pending */	if (state->revision == 0x4000)		dib7000m_write_word(state, 1793, 0);	else		dib7000m_read_word(state, 537);	ret |= dib7000m_write_word(state, 0, (u16) value);	return ret;}static int dib7000m_autosearch_irq(struct dib7000m_state *state, u16 reg){	u16 irq_pending = dib7000m_read_word(state, reg);	if (irq_pending & 0x1) { // failed		dprintk( "autosearch failed");		return 1;	}	if (irq_pending & 0x2) { // succeeded		dprintk( "autosearch succeeded");		return 2;	}	return 0; // still pending}static int dib7000m_autosearch_is_irq(struct dvb_frontend *demod){	struct dib7000m_state *state = demod->demodulator_priv;	if (state->revision == 0x4000)		return dib7000m_autosearch_irq(state, 1793);	else		return dib7000m_autosearch_irq(state, 537);}static int dib7000m_tune(struct dvb_frontend *demod, struct dvb_frontend_parameters *ch){	struct dib7000m_state *state = demod->demodulator_priv;	int ret = 0;	u16 value;	// we are already tuned - just resuming from suspend	if (ch != NULL)		dib7000m_set_channel(state, ch, 0);	else		return -EINVAL;	// restart demod	ret |= dib7000m_write_word(state, 898, 0x4000);	ret |= dib7000m_write_word(state, 898, 0x0000);	msleep(45);	dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD);	/* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */	ret |= dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3));	// never achieved a lock before - wait for timfreq to update	if (state->timf == 0)		msleep(200);	//dump_reg(state);	/* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */	value = (6 << 8) | 0x80;	switch (ch->u.ofdm.transmission_mode) {		case TRANSMISSION_MODE_2K: value |= (7 << 12); break;		case /* 4K MODE */ 255: value |= (8 << 12); break;		default:		case TRANSMISSION_MODE_8K: value |= (9 << 12); break;	}	ret |= dib7000m_write_word(state, 26, value);	/* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */	value = (0 << 4);	switch (ch->u.ofdm.transmission_mode) {		case TRANSMISSION_MODE_2K: value |= 0x6; break;		case /* 4K MODE */ 255: value |= 0x7; break;		default:		case TRANSMISSION_MODE_8K: value |= 0x8; break;	}	ret |= dib7000m_write_word(state, 32, value);	/* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */	value = (0 << 4);	switch (ch->u.ofdm.transmission_mode) {		case TRANSMISSION_MODE_2K: value |= 0x6; break;		case /* 4K MODE */ 255: value |= 0x7; break;		default:		case TRANSMISSION_MODE_8K: value |= 0x8; break;	}	ret |= dib7000m_write_word(state, 33,  value);	// we achieved a lock - it's time to update the timf freq	if ((dib7000m_read_word(state, 535) >> 6)  & 0x1)		dib7000m_update_timf(state);    dib7000m_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth));	return ret;}static int dib7000m_wakeup(struct dvb_frontend *demod){	struct dib7000m_state *state = demod->demodulator_priv;	dib7000m_set_power_mode(state, DIB7000M_POWER_ALL);	if (dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0)		dprintk( "could not start Slow ADC");	return 0;}static int dib7000m_sleep(struct dvb_frontend *demod){	struct dib7000m_state *st = demod->demodulator_priv;	dib7000m_set_output_mode(st, OUTMODE_HIGH_Z);	dib7000m_set_power_mode(st, DIB7000M_POWER_INTERFACE_ONLY);	return dib7000m_set_adc_state(st, DIBX000_SLOW_ADC_OFF) |		dib7000m_set_adc_state(st, DIBX000_ADC_OFF);}static int dib7000m_identify(struct dib7000m_state *state){	u16 value;	if ((value = dib7000m_read_word(state, 896)) != 0x01b3) {		dprintk( "wrong Vendor ID (0x%x)",value);		return -EREMOTEIO;	}	state->revision = dib7000m_read_word(state, 897);	if (state->revision != 0x4000 &&		state->revision != 0x4001 &&		state->revision != 0x4002 &&		state->revision != 0x4003) {		dprintk( "wrong Device ID (0x%x)",value);		return -EREMOTEIO;	}	/* protect this driver to be used with 7000PC */	if (state->revision == 0x4000 && dib7000m_read_word(state, 769) == 0x4000) {		dprintk( "this driver does not work with DiB7000PC");		return -EREMOTEIO;	}	switch (state->revision) {		case 0x4000: dprintk( "found DiB7000MA/PA/MB/PB"); break;		case 0x4001: state->reg_offs = 1; dprintk( "found DiB7000HC"); break;		case 0x4002: state->reg_offs = 1; dprintk( "found DiB7000MC"); break;		case 0x4003: state->reg_offs = 1; dprintk( "found DiB9000"); break;	}	return 0;}static int dib7000m_get_frontend(struct dvb_frontend* fe,				struct dvb_frontend_parameters *fep){	struct dib7000m_state *state = fe->demodulator_priv;	u16 tps = dib7000m_read_word(state,480);	fep->inversion = INVERSION_AUTO;	fep->u.ofdm.bandwidth = state->current_bandwidth;	switch ((tps >> 8) & 0x3) {		case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break;		case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break;		/* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */	}	switch (tps & 0x3) {		case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break;		case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break;		case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break;		case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break;	}	switch ((tps >> 14) & 0x3) {		case 0: fep->u.ofdm.constellation = QPSK; break;		case 1: fep->u.ofdm.constellation = QAM_16; break;		case 2:		default: fep->u.ofdm.constellation = QAM_64; break;	}	/* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */	/* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */	fep->u.ofdm.hierarchy_information = HIERARCHY_NONE;	switch ((tps >> 5) & 0x7) {		case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break;		case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break;		case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break;		case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break;		case 7:		default: fep->u.ofdm.code_rate_HP = FEC_7_8; break;	}	switch ((tps >> 2) & 0x7) {		case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break;		case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break;		case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break;		case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break;		case 7:		default: fep->u.ofdm.code_rate_LP = FEC_7_8; break;	}	/* native interleaver: (dib7000m_read_word(state, 481) >>  5) & 0x1 */	return 0;}static int dib7000m_set_frontend(struct dvb_frontend* fe,				struct dvb_frontend_parameters *fep){	struct dib7000m_state *state = fe->demodulator_priv;	int time;	state->current_bandwidth = fep->u.ofdm.bandwidth;	dib7000m_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->u.ofdm.bandwidth));	if (fe->ops.tuner_ops.set_params)		fe->ops.tuner_ops.set_params(fe, fep);	/* start up the AGC */	state->agc_state = 0;	do {		time = dib7000m_agc_startup(fe, fep);		if (time != -1)			msleep(time);	} while (time != -1);	if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ||		fep->u.ofdm.guard_interval    == GUARD_INTERVAL_AUTO ||		fep->u.ofdm.constellation     == QAM_AUTO ||		fep->u.ofdm.code_rate_HP      == FEC_AUTO) {		int i = 800, found;		dib7000m_autosearch_start(fe, fep);		do {			msleep(1);			found = dib7000m_autosearch_is_irq(fe);		} while (found == 0 && i--);		dprintk("autosearch returns: %d",found);		if (found == 0 || found == 1)			return 0; // no channel found		dib7000m_get_frontend(fe, fep);	}	/* make this a config parameter */	dib7000m_set_output_mode(state, OUTMODE_MPEG2_FIFO);	return dib7000m_tune(fe, fep);}static int dib7000m_read_status(struct dvb_frontend *fe, fe_status_t *stat){	struct dib7000m_state *state = fe->demodulator_priv;	u16 lock = dib7000m_read_word(state, 535);	*stat = 0;	if (lock & 0x8000)		*stat |= FE_HAS_SIGNAL;	if (lock & 0x3000)		*stat |= FE_HAS_CARRIER;	if (lock & 0x0100)		*stat |= FE_HAS_VITERBI;	if (lock & 0x0010)		*stat |= FE_HAS_SYNC;	if (lock & 0x0008)		*stat |= FE_HAS_LOCK;	return 0;}static int dib7000m_read_ber(struct dvb_frontend *fe, u32 *ber){	struct dib7000m_state *state = fe->demodulator_priv;	*ber = (dib7000m_read_word(state, 526) << 16) | dib7000m_read_word(state, 527);	return 0;}static int dib7000m_read_unc_blocks(struct dvb_frontend *fe, u32 *unc){	struct dib7000m_state *state = fe->demodulator_priv;	*unc = dib7000m_read_word(state, 534);	return 0;}static int dib7000m_read_signal_strength(struct dvb_frontend *fe, u16 *strength){	struct dib7000m_state *state = fe->demodulator_priv;	u16 val = dib7000m_read_word(state, 390);	*strength = 65535 - val;	return 0;}static int dib7000m_read_snr(struct dvb_frontend* fe, u16 *snr){	*snr = 0x0000;	return 0;}static int dib7000m_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune){	tune->min_delay_ms = 1000;	return 0;}static void dib7000m_release(struct dvb_frontend *demod){	struct dib7000m_state *st = demod->demodulator_priv;	dibx000_exit_i2c_master(&st->i2c_master);	kfree(st);}struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating){	struct dib7000m_state *st = demod->demodulator_priv;	return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating);}EXPORT_SYMBOL(dib7000m_get_i2c_master);int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000m_config cfg[]){	struct dib7000m_state st = { .i2c_adap = i2c };	int k = 0;	u8 new_addr = 0;	for (k = no_of_demods-1; k >= 0; k--) {		st.cfg = cfg[k];		/* designated i2c address */		new_addr          = (0x40 + k) << 1;		st.i2c_addr = new_addr;		if (dib7000m_identify(&st) != 0) {			st.i2c_addr = default_addr;			if (dib7000m_identify(&st) != 0) {				dprintk("DiB7000M #%d: not identified", k);				return -EIO;			}		}		/* start diversity to pull_down div_str - just for i2c-enumeration */		dib7000m_set_output_mode(&st, OUTMODE_DIVERSITY);		dib7000m_write_word(&st, 1796, 0x0); // select DVB-T output		/* set new i2c address and force divstart */		dib7000m_write_word(&st, 1794, (new_addr << 2) | 0x2);		dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr);	}	for (k = 0; k < no_of_demods; k++) {		st.cfg = cfg[k];		st.i2c_addr = (0x40 + k) << 1;		// unforce divstr		dib7000m_write_word(&st,1794, st.i2c_addr << 2);		/* deactivate div - it was just for i2c-enumeration */		dib7000m_set_output_mode(&st, OUTMODE_HIGH_Z);	}	return 0;}EXPORT_SYMBOL(dib7000m_i2c_enumeration);static struct dvb_frontend_ops dib7000m_ops;struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg){	struct dvb_frontend *demod;	struct dib7000m_state *st;	st = kzalloc(sizeof(struct dib7000m_state), GFP_KERNEL);	if (st == NULL)		return NULL;	memcpy(&st->cfg, cfg, sizeof(struct dib7000m_config));	st->i2c_adap = i2c_adap;	st->i2c_addr = i2c_addr;	demod                   = &st->demod;	demod->demodulator_priv = st;	memcpy(&st->demod.ops, &dib7000m_ops, sizeof(struct dvb_frontend_ops));	st->timf_default = cfg->bw->timf;	if (dib7000m_identify(st) != 0)		goto error;	if (st->revision == 0x4000)		dibx000_init_i2c_master(&st->i2c_master, DIB7000, st->i2c_adap, st->i2c_addr);	else		dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c_adap, st->i2c_addr);	dib7000m_demod_reset(st);	return demod;error:	kfree(st);	return NULL;}EXPORT_SYMBOL(dib7000m_attach);static struct dvb_frontend_ops dib7000m_ops = {	.info = {		.name = "DiBcom 7000MA/MB/PA/PB/MC",		.type = FE_OFDM,		.frequency_min      = 44250000,		.frequency_max      = 867250000,		.frequency_stepsize = 62500,		.caps = FE_CAN_INVERSION_AUTO |			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |			FE_CAN_TRANSMISSION_MODE_AUTO |			FE_CAN_GUARD_INTERVAL_AUTO |			FE_CAN_RECOVER |			FE_CAN_HIERARCHY_AUTO,	},	.release              = dib7000m_release,	.init                 = dib7000m_wakeup,	.sleep                = dib7000m_sleep,	.set_frontend         = dib7000m_set_frontend,	.get_tune_settings    = dib7000m_fe_get_tune_settings,	.get_frontend         = dib7000m_get_frontend,	.read_status          = dib7000m_read_status,	.read_ber             = dib7000m_read_ber,	.read_signal_strength = dib7000m_read_signal_strength,	.read_snr             = dib7000m_read_snr,	.read_ucblocks        = dib7000m_read_unc_blocks,};MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");MODULE_DESCRIPTION("Driver for the DiBcom 7000MA/MB/PA/PB/MC COFDM demodulator");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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