|
@@ -0,0 +1,242 @@
|
|
|
+/*
|
|
|
+ * Linux-DVB Driver for DiBcom's DiB7000M and
|
|
|
+ * first generation DiB7000P-demodulator-family.
|
|
|
+ *
|
|
|
+ * Copyright (C) 2005-6 DiBcom (http://www.dibcom.fr/)
|
|
|
+ *
|
|
|
+ * This program is free software; you can redistribute it and/or
|
|
|
+ * modify it under the terms of the GNU General Public License as
|
|
|
+ * published by the Free Software Foundation, version 2.
|
|
|
+ */
|
|
|
+#include <linux/kernel.h>
|
|
|
+#include <linux/i2c.h>
|
|
|
+
|
|
|
+#include "dvb_frontend.h"
|
|
|
+
|
|
|
+#include "dib7000m.h"
|
|
|
+
|
|
|
+static int debug;
|
|
|
+module_param(debug, int, 0644);
|
|
|
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
|
|
|
+
|
|
|
+#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB3000MC/P:"); printk(args); } } while (0)
|
|
|
+
|
|
|
+struct dib7000m_state {
|
|
|
+ struct dvb_frontend demod;
|
|
|
+ struct dib7000m_config cfg;
|
|
|
+
|
|
|
+ u8 i2c_addr;
|
|
|
+ struct i2c_adapter *i2c_adap;
|
|
|
+
|
|
|
+ struct dibx000_i2c_master i2c_master;
|
|
|
+
|
|
|
+/* offset is 1 in case of the 7000MC */
|
|
|
+ u8 reg_offs;
|
|
|
+
|
|
|
+ u16 wbd_ref;
|
|
|
+
|
|
|
+ u8 current_band;
|
|
|
+ fe_bandwidth_t current_bandwidth;
|
|
|
+ struct dibx000_agc_config *current_agc;
|
|
|
+ u32 timf[9];
|
|
|
+
|
|
|
+ u16 revision;
|
|
|
+};
|
|
|
+
|
|
|
+static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg)
|
|
|
+{
|
|
|
+ u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff };
|
|
|
+ u8 rb[2];
|
|
|
+ struct i2c_msg msg[2] = {
|
|
|
+ { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 },
|
|
|
+ { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 },
|
|
|
+ };
|
|
|
+
|
|
|
+ if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
|
|
|
+ dprintk("i2c read error on %d\n",reg);
|
|
|
+
|
|
|
+ return (rb[0] << 8) | rb[1];
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val)
|
|
|
+{
|
|
|
+ u8 b[4] = {
|
|
|
+ (reg >> 8) & 0xff, reg & 0xff,
|
|
|
+ (val >> 8) & 0xff, val & 0xff,
|
|
|
+ };
|
|
|
+ struct i2c_msg msg = {
|
|
|
+ .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4
|
|
|
+ };
|
|
|
+ return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 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) & 0x2) {
|
|
|
+ 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)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+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, 509);
|
|
|
+
|
|
|
+ *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 int dib7000m_init(struct dvb_frontend *fe)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int dib7000m_sleep(struct dvb_frontend *fe)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void dib7000m_release(struct dvb_frontend *fe)
|
|
|
+{ }
|
|
|
+
|
|
|
+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_init,
|
|
|
+ .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");
|