Patrick Boettcher | 91bb9be | 2006-12-02 21:15:51 -0200 | [diff] [blame^] | 1 | /* |
| 2 | * Linux-DVB Driver for DiBcom's DiB7000M and |
| 3 | * first generation DiB7000P-demodulator-family. |
| 4 | * |
| 5 | * Copyright (C) 2005-6 DiBcom (http://www.dibcom.fr/) |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU General Public License as |
| 9 | * published by the Free Software Foundation, version 2. |
| 10 | */ |
| 11 | #include <linux/kernel.h> |
| 12 | #include <linux/i2c.h> |
| 13 | |
| 14 | #include "dvb_frontend.h" |
| 15 | |
| 16 | #include "dib7000m.h" |
| 17 | |
| 18 | static int debug; |
| 19 | module_param(debug, int, 0644); |
| 20 | MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); |
| 21 | |
| 22 | #define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB3000MC/P:"); printk(args); } } while (0) |
| 23 | |
| 24 | struct dib7000m_state { |
| 25 | struct dvb_frontend demod; |
| 26 | struct dib7000m_config cfg; |
| 27 | |
| 28 | u8 i2c_addr; |
| 29 | struct i2c_adapter *i2c_adap; |
| 30 | |
| 31 | struct dibx000_i2c_master i2c_master; |
| 32 | |
| 33 | /* offset is 1 in case of the 7000MC */ |
| 34 | u8 reg_offs; |
| 35 | |
| 36 | u16 wbd_ref; |
| 37 | |
| 38 | u8 current_band; |
| 39 | fe_bandwidth_t current_bandwidth; |
| 40 | struct dibx000_agc_config *current_agc; |
| 41 | u32 timf[9]; |
| 42 | |
| 43 | u16 revision; |
| 44 | }; |
| 45 | |
| 46 | static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg) |
| 47 | { |
| 48 | u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff }; |
| 49 | u8 rb[2]; |
| 50 | struct i2c_msg msg[2] = { |
| 51 | { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 }, |
| 52 | { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 }, |
| 53 | }; |
| 54 | |
| 55 | if (i2c_transfer(state->i2c_adap, msg, 2) != 2) |
| 56 | dprintk("i2c read error on %d\n",reg); |
| 57 | |
| 58 | return (rb[0] << 8) | rb[1]; |
| 59 | } |
| 60 | |
| 61 | /* |
| 62 | static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val) |
| 63 | { |
| 64 | u8 b[4] = { |
| 65 | (reg >> 8) & 0xff, reg & 0xff, |
| 66 | (val >> 8) & 0xff, val & 0xff, |
| 67 | }; |
| 68 | struct i2c_msg msg = { |
| 69 | .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4 |
| 70 | }; |
| 71 | return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; |
| 72 | } |
| 73 | */ |
| 74 | |
| 75 | static int dib7000m_get_frontend(struct dvb_frontend* fe, |
| 76 | struct dvb_frontend_parameters *fep) |
| 77 | { |
| 78 | struct dib7000m_state *state = fe->demodulator_priv; |
| 79 | u16 tps = dib7000m_read_word(state,480); |
| 80 | |
| 81 | fep->inversion = INVERSION_AUTO; |
| 82 | |
| 83 | fep->u.ofdm.bandwidth = state->current_bandwidth; |
| 84 | |
| 85 | switch ((tps >> 8) & 0x2) { |
| 86 | case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break; |
| 87 | case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break; |
| 88 | /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */ |
| 89 | } |
| 90 | |
| 91 | switch (tps & 0x3) { |
| 92 | case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break; |
| 93 | case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break; |
| 94 | case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break; |
| 95 | case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break; |
| 96 | } |
| 97 | |
| 98 | switch ((tps >> 14) & 0x3) { |
| 99 | case 0: fep->u.ofdm.constellation = QPSK; break; |
| 100 | case 1: fep->u.ofdm.constellation = QAM_16; break; |
| 101 | case 2: |
| 102 | default: fep->u.ofdm.constellation = QAM_64; break; |
| 103 | } |
| 104 | |
| 105 | /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ |
| 106 | /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */ |
| 107 | |
| 108 | fep->u.ofdm.hierarchy_information = HIERARCHY_NONE; |
| 109 | switch ((tps >> 5) & 0x7) { |
| 110 | case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break; |
| 111 | case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break; |
| 112 | case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break; |
| 113 | case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break; |
| 114 | case 7: |
| 115 | default: fep->u.ofdm.code_rate_HP = FEC_7_8; break; |
| 116 | |
| 117 | } |
| 118 | |
| 119 | switch ((tps >> 2) & 0x7) { |
| 120 | case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break; |
| 121 | case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break; |
| 122 | case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break; |
| 123 | case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break; |
| 124 | case 7: |
| 125 | default: fep->u.ofdm.code_rate_LP = FEC_7_8; break; |
| 126 | } |
| 127 | |
| 128 | /* native interleaver: (dib7000m_read_word(state, 481) >> 5) & 0x1 */ |
| 129 | |
| 130 | return 0; |
| 131 | } |
| 132 | |
| 133 | static int dib7000m_set_frontend(struct dvb_frontend* fe, |
| 134 | struct dvb_frontend_parameters *fep) |
| 135 | { |
| 136 | return 0; |
| 137 | } |
| 138 | |
| 139 | static int dib7000m_read_status(struct dvb_frontend *fe, fe_status_t *stat) |
| 140 | { |
| 141 | struct dib7000m_state *state = fe->demodulator_priv; |
| 142 | u16 lock = dib7000m_read_word(state, 509); |
| 143 | |
| 144 | *stat = 0; |
| 145 | |
| 146 | if (lock & 0x8000) |
| 147 | *stat |= FE_HAS_SIGNAL; |
| 148 | if (lock & 0x3000) |
| 149 | *stat |= FE_HAS_CARRIER; |
| 150 | if (lock & 0x0100) |
| 151 | *stat |= FE_HAS_VITERBI; |
| 152 | if (lock & 0x0010) |
| 153 | *stat |= FE_HAS_SYNC; |
| 154 | if (lock & 0x0008) |
| 155 | *stat |= FE_HAS_LOCK; |
| 156 | |
| 157 | return 0; |
| 158 | } |
| 159 | |
| 160 | static int dib7000m_read_ber(struct dvb_frontend *fe, u32 *ber) |
| 161 | { |
| 162 | struct dib7000m_state *state = fe->demodulator_priv; |
| 163 | *ber = (dib7000m_read_word(state, 526) << 16) | dib7000m_read_word(state, 527); |
| 164 | return 0; |
| 165 | } |
| 166 | |
| 167 | static int dib7000m_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) |
| 168 | { |
| 169 | struct dib7000m_state *state = fe->demodulator_priv; |
| 170 | *unc = dib7000m_read_word(state, 534); |
| 171 | return 0; |
| 172 | } |
| 173 | |
| 174 | static int dib7000m_read_signal_strength(struct dvb_frontend *fe, u16 *strength) |
| 175 | { |
| 176 | struct dib7000m_state *state = fe->demodulator_priv; |
| 177 | u16 val = dib7000m_read_word(state, 390); |
| 178 | *strength = 65535 - val; |
| 179 | return 0; |
| 180 | } |
| 181 | |
| 182 | static int dib7000m_read_snr(struct dvb_frontend* fe, u16 *snr) |
| 183 | { |
| 184 | *snr = 0x0000; |
| 185 | return 0; |
| 186 | } |
| 187 | |
| 188 | static int dib7000m_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) |
| 189 | { |
| 190 | tune->min_delay_ms = 1000; |
| 191 | return 0; |
| 192 | } |
| 193 | |
| 194 | static int dib7000m_init(struct dvb_frontend *fe) |
| 195 | { |
| 196 | return 0; |
| 197 | } |
| 198 | |
| 199 | static int dib7000m_sleep(struct dvb_frontend *fe) |
| 200 | { |
| 201 | return 0; |
| 202 | } |
| 203 | |
| 204 | static void dib7000m_release(struct dvb_frontend *fe) |
| 205 | { } |
| 206 | |
| 207 | static struct dvb_frontend_ops dib7000m_ops = { |
| 208 | .info = { |
| 209 | .name = "DiBcom 7000MA/MB/PA/PB/MC", |
| 210 | .type = FE_OFDM, |
| 211 | .frequency_min = 44250000, |
| 212 | .frequency_max = 867250000, |
| 213 | .frequency_stepsize = 62500, |
| 214 | .caps = FE_CAN_INVERSION_AUTO | |
| 215 | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | |
| 216 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | |
| 217 | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | |
| 218 | FE_CAN_TRANSMISSION_MODE_AUTO | |
| 219 | FE_CAN_GUARD_INTERVAL_AUTO | |
| 220 | FE_CAN_RECOVER | |
| 221 | FE_CAN_HIERARCHY_AUTO, |
| 222 | }, |
| 223 | |
| 224 | .release = dib7000m_release, |
| 225 | |
| 226 | .init = dib7000m_init, |
| 227 | .sleep = dib7000m_sleep, |
| 228 | |
| 229 | .set_frontend = dib7000m_set_frontend, |
| 230 | .get_tune_settings = dib7000m_fe_get_tune_settings, |
| 231 | .get_frontend = dib7000m_get_frontend, |
| 232 | |
| 233 | .read_status = dib7000m_read_status, |
| 234 | .read_ber = dib7000m_read_ber, |
| 235 | .read_signal_strength = dib7000m_read_signal_strength, |
| 236 | .read_snr = dib7000m_read_snr, |
| 237 | .read_ucblocks = dib7000m_read_unc_blocks, |
| 238 | }; |
| 239 | |
| 240 | MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>"); |
| 241 | MODULE_DESCRIPTION("Driver for the DiBcom 7000MA/MB/PA/PB/MC COFDM demodulator"); |
| 242 | MODULE_LICENSE("GPL"); |