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