📄 s5h1409.c
字号:
return -EINVAL; } state->current_modulation = m; s5h1409_softreset(fe); return 0;}static int s5h1409_i2c_gate_ctrl(struct dvb_frontend* fe, int enable){ struct s5h1409_state* state = fe->demodulator_priv; dprintk("%s(%d)\n", __FUNCTION__, enable); if (enable) return s5h1409_writereg(state, 0xf3, 1); else return s5h1409_writereg(state, 0xf3, 0);}static int s5h1409_set_gpio(struct dvb_frontend* fe, int enable){ struct s5h1409_state* state = fe->demodulator_priv; dprintk("%s(%d)\n", __FUNCTION__, enable); if (enable) return s5h1409_writereg(state, 0xe3, 0x1100); else return s5h1409_writereg(state, 0xe3, 0x1000);}static int s5h1409_sleep(struct dvb_frontend* fe, int enable){ struct s5h1409_state* state = fe->demodulator_priv; dprintk("%s(%d)\n", __FUNCTION__, enable); return s5h1409_writereg(state, 0xf2, enable);}static int s5h1409_register_reset(struct dvb_frontend* fe){ struct s5h1409_state* state = fe->demodulator_priv; dprintk("%s()\n", __FUNCTION__); return s5h1409_writereg(state, 0xfa, 0);}static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe){ struct s5h1409_state *state = fe->demodulator_priv; u16 reg; if (state->is_qam_locked) return; /* QAM EQ lock check */ reg = s5h1409_readreg(state, 0xf0); if ((reg >> 13) & 0x1) { state->is_qam_locked = 1; reg &= 0xff; s5h1409_writereg(state, 0x96, 0x00c); if ((reg < 0x38) || (reg > 0x68) ) { s5h1409_writereg(state, 0x93, 0x3332); s5h1409_writereg(state, 0x9e, 0x2c37); } else { s5h1409_writereg(state, 0x93, 0x3130); s5h1409_writereg(state, 0x9e, 0x2836); } } else { s5h1409_writereg(state, 0x96, 0x0008); s5h1409_writereg(state, 0x93, 0x3332); s5h1409_writereg(state, 0x9e, 0x2c37); }}static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe){ struct s5h1409_state *state = fe->demodulator_priv; u16 reg, reg1, reg2; reg = s5h1409_readreg(state, 0xf1); /* Master lock */ if ((reg >> 15) & 0x1) { if (state->qam_state != 2) { state->qam_state = 2; reg1 = s5h1409_readreg(state, 0xb2); reg2 = s5h1409_readreg(state, 0xad); s5h1409_writereg(state, 0x96, 0x20); s5h1409_writereg(state, 0xad, ( ((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff)) ); s5h1409_writereg(state, 0xab, 0x1100); } } else { if (state->qam_state != 1) { state->qam_state = 1; s5h1409_writereg(state, 0x96, 0x08); s5h1409_writereg(state, 0xab, 0x1101); } }}/* Talk to the demod, set the FEC, GUARD, QAM settings etc */static int s5h1409_set_frontend (struct dvb_frontend* fe, struct dvb_frontend_parameters *p){ struct s5h1409_state* state = fe->demodulator_priv; dprintk("%s(frequency=%d)\n", __FUNCTION__, p->frequency); s5h1409_softreset(fe); state->current_frequency = p->frequency; s5h1409_enable_modulation(fe, p->u.vsb.modulation); /* Allow the demod to settle */ msleep(100); if (fe->ops.tuner_ops.set_params) { if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); fe->ops.tuner_ops.set_params(fe, p); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); } /* Optimize the demod for QAM */ if (p->u.vsb.modulation != VSB_8) { s5h1409_set_qam_amhum_mode(fe); s5h1409_set_qam_interleave_mode(fe); } return 0;}/* Reset the demod hardware and reset all of the configuration registers to a default state. */static int s5h1409_init (struct dvb_frontend* fe){ int i; struct s5h1409_state* state = fe->demodulator_priv; dprintk("%s()\n", __FUNCTION__); s5h1409_sleep(fe, 0); s5h1409_register_reset(fe); for (i=0; i < ARRAY_SIZE(init_tab); i++) s5h1409_writereg(state, init_tab[i].reg, init_tab[i].data); /* The datasheet says that after initialisation, VSB is default */ state->current_modulation = VSB_8; if (state->config->output_mode == S5H1409_SERIAL_OUTPUT) s5h1409_writereg(state, 0xab, 0x100); /* Serial */ else s5h1409_writereg(state, 0xab, 0x0); /* Parallel */ s5h1409_set_spectralinversion(fe, state->config->inversion); s5h1409_set_if_freq(fe, state->config->if_freq); s5h1409_set_gpio(fe, state->config->gpio); s5h1409_softreset(fe); /* Note: Leaving the I2C gate closed. */ s5h1409_i2c_gate_ctrl(fe, 0); return 0;}static int s5h1409_read_status(struct dvb_frontend* fe, fe_status_t* status){ struct s5h1409_state* state = fe->demodulator_priv; u16 reg; u32 tuner_status = 0; *status = 0; /* Get the demodulator status */ reg = s5h1409_readreg(state, 0xf1); if(reg & 0x1000) *status |= FE_HAS_VITERBI; if(reg & 0x8000) *status |= FE_HAS_LOCK | FE_HAS_SYNC; switch(state->config->status_mode) { case S5H1409_DEMODLOCKING: if (*status & FE_HAS_VITERBI) *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; break; case S5H1409_TUNERLOCKING: /* Get the tuner status */ if (fe->ops.tuner_ops.get_status) { if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); fe->ops.tuner_ops.get_status(fe, &tuner_status); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); } if (tuner_status) *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; break; } dprintk("%s() status 0x%08x\n", __FUNCTION__, *status); return 0;}static int s5h1409_qam256_lookup_snr(struct dvb_frontend* fe, u16* snr, u16 v){ int i, ret = -EINVAL; dprintk("%s()\n", __FUNCTION__); for (i=0; i < ARRAY_SIZE(qam256_snr_tab); i++) { if (v < qam256_snr_tab[i].val) { *snr = qam256_snr_tab[i].data; ret = 0; break; } } return ret;}static int s5h1409_qam64_lookup_snr(struct dvb_frontend* fe, u16* snr, u16 v){ int i, ret = -EINVAL; dprintk("%s()\n", __FUNCTION__); for (i=0; i < ARRAY_SIZE(qam64_snr_tab); i++) { if (v < qam64_snr_tab[i].val) { *snr = qam64_snr_tab[i].data; ret = 0; break; } } return ret;}static int s5h1409_vsb_lookup_snr(struct dvb_frontend* fe, u16* snr, u16 v){ int i, ret = -EINVAL; dprintk("%s()\n", __FUNCTION__); for (i=0; i < ARRAY_SIZE(vsb_snr_tab); i++) { if (v > vsb_snr_tab[i].val) { *snr = vsb_snr_tab[i].data; ret = 0; break; } } dprintk("%s() snr=%d\n", __FUNCTION__, *snr); return ret;}static int s5h1409_read_snr(struct dvb_frontend* fe, u16* snr){ struct s5h1409_state* state = fe->demodulator_priv; u16 reg; dprintk("%s()\n", __FUNCTION__); switch(state->current_modulation) { case QAM_64: reg = s5h1409_readreg(state, 0xf0) & 0xff; return s5h1409_qam64_lookup_snr(fe, snr, reg); case QAM_256: reg = s5h1409_readreg(state, 0xf0) & 0xff; return s5h1409_qam256_lookup_snr(fe, snr, reg); case VSB_8: reg = s5h1409_readreg(state, 0xf1) & 0x3ff; return s5h1409_vsb_lookup_snr(fe, snr, reg); default: break; } return -EINVAL;}static int s5h1409_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength){ return s5h1409_read_snr(fe, signal_strength);}static int s5h1409_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks){ struct s5h1409_state* state = fe->demodulator_priv; *ucblocks = s5h1409_readreg(state, 0xb5); return 0;}static int s5h1409_read_ber(struct dvb_frontend* fe, u32* ber){ return s5h1409_read_ucblocks(fe, ber);}static int s5h1409_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p){ struct s5h1409_state* state = fe->demodulator_priv; p->frequency = state->current_frequency; p->u.vsb.modulation = state->current_modulation; return 0;}static int s5h1409_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune){ tune->min_delay_ms = 1000; return 0;}static void s5h1409_release(struct dvb_frontend* fe){ struct s5h1409_state* state = fe->demodulator_priv; kfree(state);}static struct dvb_frontend_ops s5h1409_ops;struct dvb_frontend* s5h1409_attach(const struct s5h1409_config* config, struct i2c_adapter* i2c){ struct s5h1409_state* state = NULL; /* allocate memory for the internal state */ state = kmalloc(sizeof(struct s5h1409_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ state->config = config; state->i2c = i2c; state->current_modulation = 0; /* check if the demod exists */ if (s5h1409_readreg(state, 0x04) != 0x0066) goto error; /* create dvb_frontend */ memcpy(&state->frontend.ops, &s5h1409_ops, sizeof(struct dvb_frontend_ops)); state->frontend.demodulator_priv = state; /* Note: Leaving the I2C gate open here. */ s5h1409_writereg(state, 0xf3, 1); return &state->frontend;error: kfree(state); return NULL;}static struct dvb_frontend_ops s5h1409_ops = { .info = { .name = "Samsung S5H1409 QAM/8VSB Frontend", .type = FE_ATSC, .frequency_min = 54000000, .frequency_max = 858000000, .frequency_stepsize = 62500, .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, .init = s5h1409_init, .i2c_gate_ctrl = s5h1409_i2c_gate_ctrl, .set_frontend = s5h1409_set_frontend, .get_frontend = s5h1409_get_frontend, .get_tune_settings = s5h1409_get_tune_settings, .read_status = s5h1409_read_status, .read_ber = s5h1409_read_ber, .read_signal_strength = s5h1409_read_signal_strength, .read_snr = s5h1409_read_snr, .read_ucblocks = s5h1409_read_ucblocks, .release = s5h1409_release,};module_param(debug, int, 0644);MODULE_PARM_DESC(debug, "Enable verbose debug messages");MODULE_DESCRIPTION("Samsung S5H1409 QAM-B/ATSC Demodulator driver");MODULE_AUTHOR("Steven Toth");MODULE_LICENSE("GPL");EXPORT_SYMBOL(s5h1409_attach);/* * Local variables: * c-basic-offset: 8 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -