blob: fa851601e7d4af1baae5312cce3819380df2a523 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Patrick Boettcherb7571f82006-08-08 15:48:10 -03002 * Driver for DiBcom DiB3000MC/P-demodulator.
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 *
Patrick Boettcherb6884a12007-07-27 10:08:51 -03004 * Copyright (C) 2004-7 DiBcom (http://www.dibcom.fr/)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
6 *
Patrick Boettcherb7571f82006-08-08 15:48:10 -03007 * This code is partially based on the previous dib3000mc.c .
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 *
Patrick Boettcherb7571f82006-08-08 15:48:10 -03009 * This program is free software; you can redistribute it and/or
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation, version 2.
Linus Torvalds1da177e2005-04-16 15:20:36 -070012 */
Patrick Boettcherb7571f82006-08-08 15:48:10 -030013
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/kernel.h>
Patrick Boettcherb7571f82006-08-08 15:48:10 -030015#include <linux/i2c.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016
Patrick Boettcherb7571f82006-08-08 15:48:10 -030017#include "dvb_frontend.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070018
Patrick Boettcherb7571f82006-08-08 15:48:10 -030019#include "dib3000mc.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070020
Linus Torvalds1da177e2005-04-16 15:20:36 -070021static int debug;
22module_param(debug, int, 0644);
Patrick Boettcherb7571f82006-08-08 15:48:10 -030023MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
Matt Doran8f6956c2007-07-31 07:09:30 -030025static int buggy_sfn_workaround;
26module_param(buggy_sfn_workaround, int, 0644);
Patrick Boettcher8d999962007-07-31 10:36:06 -030027MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (default: 0)");
Matt Doran8f6956c2007-07-31 07:09:30 -030028
Patrick Boettcherb6884a12007-07-27 10:08:51 -030029#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB3000MC/P:"); printk(args); printk("\n"); } } while (0)
Patrick Boettcherb7571f82006-08-08 15:48:10 -030030
31struct dib3000mc_state {
32 struct dvb_frontend demod;
33 struct dib3000mc_config *cfg;
34
35 u8 i2c_addr;
36 struct i2c_adapter *i2c_adap;
37
38 struct dibx000_i2c_master i2c_master;
39
Patrick Boettcher01b4bf32006-09-19 12:51:53 -030040 u32 timf;
41
Patrick Boettcherb7571f82006-08-08 15:48:10 -030042 fe_bandwidth_t current_bandwidth;
43
44 u16 dev_id;
Matt Doran8f6956c2007-07-31 07:09:30 -030045
46 u8 sfn_workaround_active :1;
Patrick Boettcherb7571f82006-08-08 15:48:10 -030047};
48
49static u16 dib3000mc_read_word(struct dib3000mc_state *state, u16 reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -070050{
Patrick Boettcherb7571f82006-08-08 15:48:10 -030051 u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff };
52 u8 rb[2];
53 struct i2c_msg msg[2] = {
54 { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 },
55 { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 },
56 };
57
58 if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
59 dprintk("i2c read error on %d\n",reg);
60
61 return (rb[0] << 8) | rb[1];
62}
63
64static int dib3000mc_write_word(struct dib3000mc_state *state, u16 reg, u16 val)
65{
66 u8 b[4] = {
67 (reg >> 8) & 0xff, reg & 0xff,
68 (val >> 8) & 0xff, val & 0xff,
69 };
70 struct i2c_msg msg = {
71 .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4
72 };
73 return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
74}
75
Patrick Boettcherb7571f82006-08-08 15:48:10 -030076static int dib3000mc_identify(struct dib3000mc_state *state)
77{
78 u16 value;
79 if ((value = dib3000mc_read_word(state, 1025)) != 0x01b3) {
80 dprintk("-E- DiB3000MC/P: wrong Vendor ID (read=0x%x)\n",value);
81 return -EREMOTEIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 }
83
Patrick Boettcherb7571f82006-08-08 15:48:10 -030084 value = dib3000mc_read_word(state, 1026);
85 if (value != 0x3001 && value != 0x3002) {
86 dprintk("-E- DiB3000MC/P: wrong Device ID (%x)\n",value);
87 return -EREMOTEIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -030089 state->dev_id = value;
90
91 dprintk("-I- found DiB3000MC/P: %x\n",state->dev_id);
92
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 return 0;
94}
95
Patrick Boettcherb6884a12007-07-27 10:08:51 -030096static int dib3000mc_set_timing(struct dib3000mc_state *state, s16 nfft, u32 bw, u8 update_offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -070097{
Patrick Boettcher01b4bf32006-09-19 12:51:53 -030098 u32 timf;
Linus Torvalds1da177e2005-04-16 15:20:36 -070099
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300100 if (state->timf == 0) {
101 timf = 1384402; // default value for 8MHz
102 if (update_offset)
103 msleep(200); // first time we do an update
104 } else
105 timf = state->timf;
106
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300107 timf *= (bw / 1000);
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300108
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300109 if (update_offset) {
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300110 s16 tim_offs = dib3000mc_read_word(state, 416);
111
112 if (tim_offs & 0x2000)
113 tim_offs -= 0x4000;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300114
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300115 if (nfft == TRANSMISSION_MODE_2K)
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300116 tim_offs *= 4;
117
118 timf += tim_offs;
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300119 state->timf = timf / (bw / 1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300121
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300122 dprintk("timf: %d\n", timf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300124 dib3000mc_write_word(state, 23, (u16) (timf >> 16));
125 dib3000mc_write_word(state, 24, (u16) (timf ) & 0xffff);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300126
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 return 0;
128}
129
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300130static int dib3000mc_setup_pwm_state(struct dib3000mc_state *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131{
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300132 u16 reg_51, reg_52 = state->cfg->agc->setup & 0xfefb;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300133 if (state->cfg->pwm3_inversion) {
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300134 reg_51 = (2 << 14) | (0 << 10) | (7 << 6) | (2 << 2) | (2 << 0);
135 reg_52 |= (1 << 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 } else {
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300137 reg_51 = (2 << 14) | (4 << 10) | (7 << 6) | (2 << 2) | (2 << 0);
138 reg_52 |= (1 << 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 }
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300140 dib3000mc_write_word(state, 51, reg_51);
141 dib3000mc_write_word(state, 52, reg_52);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300142
143 if (state->cfg->use_pwm3)
144 dib3000mc_write_word(state, 245, (1 << 3) | (1 << 0));
145 else
146 dib3000mc_write_word(state, 245, 0);
147
148 dib3000mc_write_word(state, 1040, 0x3);
149 return 0;
150}
151
152static int dib3000mc_set_output_mode(struct dib3000mc_state *state, int mode)
153{
154 int ret = 0;
155 u16 fifo_threshold = 1792;
156 u16 outreg = 0;
157 u16 outmode = 0;
158 u16 elecout = 1;
Patrick Boettcherfb6065b2006-08-21 08:21:52 -0300159 u16 smo_reg = dib3000mc_read_word(state, 206) & 0x0010; /* keep the pid_parse bit */
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300160
161 dprintk("-I- Setting output mode for demod %p to %d\n",
162 &state->demod, mode);
163
164 switch (mode) {
165 case OUTMODE_HIGH_Z: // disable
166 elecout = 0;
167 break;
168 case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock
169 outmode = 0;
170 break;
171 case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock
172 outmode = 1;
173 break;
174 case OUTMODE_MPEG2_SERIAL: // STBs with serial input
175 outmode = 2;
176 break;
177 case OUTMODE_MPEG2_FIFO: // e.g. USB feeding
178 elecout = 3;
179 /*ADDR @ 206 :
180 P_smo_error_discard [1;6:6] = 0
181 P_smo_rs_discard [1;5:5] = 0
182 P_smo_pid_parse [1;4:4] = 0
183 P_smo_fifo_flush [1;3:3] = 0
184 P_smo_mode [2;2:1] = 11
185 P_smo_ovf_prot [1;0:0] = 0
186 */
Patrick Boettcherfb6065b2006-08-21 08:21:52 -0300187 smo_reg |= 3 << 1;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300188 fifo_threshold = 512;
189 outmode = 5;
190 break;
191 case OUTMODE_DIVERSITY:
192 outmode = 4;
193 elecout = 1;
194 break;
195 default:
196 dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod);
197 outmode = 0;
198 break;
199 }
200
201 if ((state->cfg->output_mpeg2_in_188_bytes))
Patrick Boettcher559463b2006-08-19 16:13:53 -0300202 smo_reg |= (1 << 5); // P_smo_rs_discard [1;5:5] = 1
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300203
204 outreg = dib3000mc_read_word(state, 244) & 0x07FF;
205 outreg |= (outmode << 11);
206 ret |= dib3000mc_write_word(state, 244, outreg);
207 ret |= dib3000mc_write_word(state, 206, smo_reg); /*smo_ mode*/
208 ret |= dib3000mc_write_word(state, 207, fifo_threshold); /* synchronous fread */
209 ret |= dib3000mc_write_word(state, 1040, elecout); /* P_out_cfg */
210 return ret;
211}
212
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300213static int dib3000mc_set_bandwidth(struct dib3000mc_state *state, u32 bw)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300214{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300215 u16 bw_cfg[6] = { 0 };
216 u16 imp_bw_cfg[3] = { 0 };
217 u16 reg;
218
219/* settings here are for 27.7MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 switch (bw) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300221 case 8000:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300222 bw_cfg[0] = 0x0019; bw_cfg[1] = 0x5c30; bw_cfg[2] = 0x0054; bw_cfg[3] = 0x88a0; bw_cfg[4] = 0x01a6; bw_cfg[5] = 0xab20;
223 imp_bw_cfg[0] = 0x04db; imp_bw_cfg[1] = 0x00db; imp_bw_cfg[2] = 0x00b7;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300225
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300226 case 7000:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300227 bw_cfg[0] = 0x001c; bw_cfg[1] = 0xfba5; bw_cfg[2] = 0x0060; bw_cfg[3] = 0x9c25; bw_cfg[4] = 0x01e3; bw_cfg[5] = 0x0cb7;
228 imp_bw_cfg[0] = 0x04c0; imp_bw_cfg[1] = 0x00c0; imp_bw_cfg[2] = 0x00a0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300230
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300231 case 6000:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300232 bw_cfg[0] = 0x0021; bw_cfg[1] = 0xd040; bw_cfg[2] = 0x0070; bw_cfg[3] = 0xb62b; bw_cfg[4] = 0x0233; bw_cfg[5] = 0x8ed5;
233 imp_bw_cfg[0] = 0x04a5; imp_bw_cfg[1] = 0x00a5; imp_bw_cfg[2] = 0x0089;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300235
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300236 case 5000:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300237 bw_cfg[0] = 0x0028; bw_cfg[1] = 0x9380; bw_cfg[2] = 0x0087; bw_cfg[3] = 0x4100; bw_cfg[4] = 0x02a4; bw_cfg[5] = 0x4500;
238 imp_bw_cfg[0] = 0x0489; imp_bw_cfg[1] = 0x0089; imp_bw_cfg[2] = 0x0072;
239 break;
240
241 default: return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300243
244 for (reg = 6; reg < 12; reg++)
245 dib3000mc_write_word(state, reg, bw_cfg[reg - 6]);
246 dib3000mc_write_word(state, 12, 0x0000);
247 dib3000mc_write_word(state, 13, 0x03e8);
248 dib3000mc_write_word(state, 14, 0x0000);
249 dib3000mc_write_word(state, 15, 0x03f2);
250 dib3000mc_write_word(state, 16, 0x0001);
251 dib3000mc_write_word(state, 17, 0xb0d0);
252 // P_sec_len
253 dib3000mc_write_word(state, 18, 0x0393);
254 dib3000mc_write_word(state, 19, 0x8700);
255
256 for (reg = 55; reg < 58; reg++)
257 dib3000mc_write_word(state, reg, imp_bw_cfg[reg - 55]);
258
259 // Timing configuration
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300260 dib3000mc_set_timing(state, TRANSMISSION_MODE_2K, bw, 0);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300261
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 return 0;
263}
264
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300265static u16 impulse_noise_val[29] =
266
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300268 0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c, 0x3ffe, 0x7f3,
269 0x2d94, 0x76, 0x53d, 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3, 0x3feb, 0x7d2,
270 0x365e, 0x76, 0x48c, 0x3ffe, 0x5b3, 0x3feb, 0x76, 0x0000, 0xd
271};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300273static void dib3000mc_set_impulse_noise(struct dib3000mc_state *state, u8 mode, s16 nfft)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300275 u16 i;
276 for (i = 58; i < 87; i++)
277 dib3000mc_write_word(state, i, impulse_noise_val[i-58]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300279 if (nfft == TRANSMISSION_MODE_8K) {
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300280 dib3000mc_write_word(state, 58, 0x3b);
281 dib3000mc_write_word(state, 84, 0x00);
282 dib3000mc_write_word(state, 85, 0x8200);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 }
284
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300285 dib3000mc_write_word(state, 34, 0x1294);
286 dib3000mc_write_word(state, 35, 0x1ff8);
287 if (mode == 1)
288 dib3000mc_write_word(state, 55, dib3000mc_read_word(state, 55) | (1 << 10));
289}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300291static int dib3000mc_init(struct dvb_frontend *demod)
292{
293 struct dib3000mc_state *state = demod->demodulator_priv;
294 struct dibx000_agc_config *agc = state->cfg->agc;
295
296 // Restart Configuration
297 dib3000mc_write_word(state, 1027, 0x8000);
298 dib3000mc_write_word(state, 1027, 0x0000);
299
300 // power up the demod + mobility configuration
301 dib3000mc_write_word(state, 140, 0x0000);
302 dib3000mc_write_word(state, 1031, 0);
303
304 if (state->cfg->mobile_mode) {
305 dib3000mc_write_word(state, 139, 0x0000);
306 dib3000mc_write_word(state, 141, 0x0000);
307 dib3000mc_write_word(state, 175, 0x0002);
308 dib3000mc_write_word(state, 1032, 0x0000);
309 } else {
310 dib3000mc_write_word(state, 139, 0x0001);
311 dib3000mc_write_word(state, 141, 0x0000);
312 dib3000mc_write_word(state, 175, 0x0000);
313 dib3000mc_write_word(state, 1032, 0x012C);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 }
Patrick Boettcher303cbea2006-09-19 12:51:56 -0300315 dib3000mc_write_word(state, 1033, 0x0000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300317 // P_clk_cfg
Patrick Boettcher303cbea2006-09-19 12:51:56 -0300318 dib3000mc_write_word(state, 1037, 0x3130);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300320 // other configurations
321
322 // P_ctrl_sfreq
323 dib3000mc_write_word(state, 33, (5 << 0));
324 dib3000mc_write_word(state, 88, (1 << 10) | (0x10 << 0));
325
326 // Phase noise control
327 // P_fft_phacor_inh, P_fft_phacor_cpe, P_fft_powrange
328 dib3000mc_write_word(state, 99, (1 << 9) | (0x20 << 0));
329
330 if (state->cfg->phase_noise_mode == 0)
331 dib3000mc_write_word(state, 111, 0x00);
332 else
333 dib3000mc_write_word(state, 111, 0x02);
334
335 // P_agc_global
336 dib3000mc_write_word(state, 50, 0x8000);
337
338 // agc setup misc
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300339 dib3000mc_setup_pwm_state(state);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300340
341 // P_agc_counter_lock
342 dib3000mc_write_word(state, 53, 0x87);
343 // P_agc_counter_unlock
344 dib3000mc_write_word(state, 54, 0x87);
345
346 /* agc */
347 dib3000mc_write_word(state, 36, state->cfg->max_time);
Patrick Boettcher5570dd02006-10-13 11:35:12 -0300348 dib3000mc_write_word(state, 37, (state->cfg->agc_command1 << 13) | (state->cfg->agc_command2 << 12) | (0x1d << 0));
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300349 dib3000mc_write_word(state, 38, state->cfg->pwm3_value);
350 dib3000mc_write_word(state, 39, state->cfg->ln_adc_level);
351
352 // set_agc_loop_Bw
353 dib3000mc_write_word(state, 40, 0x0179);
354 dib3000mc_write_word(state, 41, 0x03f0);
355
356 dib3000mc_write_word(state, 42, agc->agc1_max);
357 dib3000mc_write_word(state, 43, agc->agc1_min);
358 dib3000mc_write_word(state, 44, agc->agc2_max);
359 dib3000mc_write_word(state, 45, agc->agc2_min);
360 dib3000mc_write_word(state, 46, (agc->agc1_pt1 << 8) | agc->agc1_pt2);
361 dib3000mc_write_word(state, 47, (agc->agc1_slope1 << 8) | agc->agc1_slope2);
362 dib3000mc_write_word(state, 48, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
363 dib3000mc_write_word(state, 49, (agc->agc2_slope1 << 8) | agc->agc2_slope2);
364
365// Begin: TimeOut registers
366 // P_pha3_thres
367 dib3000mc_write_word(state, 110, 3277);
368 // P_timf_alpha = 6, P_corm_alpha = 6, P_corm_thres = 0x80
369 dib3000mc_write_word(state, 26, 0x6680);
370 // lock_mask0
371 dib3000mc_write_word(state, 1, 4);
372 // lock_mask1
373 dib3000mc_write_word(state, 2, 4);
374 // lock_mask2
375 dib3000mc_write_word(state, 3, 0x1000);
376 // P_search_maxtrial=1
377 dib3000mc_write_word(state, 5, 1);
378
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300379 dib3000mc_set_bandwidth(state, 8000);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300380
381 // div_lock_mask
382 dib3000mc_write_word(state, 4, 0x814);
383
384 dib3000mc_write_word(state, 21, (1 << 9) | 0x164);
385 dib3000mc_write_word(state, 22, 0x463d);
386
387 // Spurious rm cfg
388 // P_cspu_regul, P_cspu_win_cut
389 dib3000mc_write_word(state, 120, 0x200f);
390 // P_adp_selec_monit
391 dib3000mc_write_word(state, 134, 0);
392
393 // Fec cfg
394 dib3000mc_write_word(state, 195, 0x10);
395
396 // diversity register: P_dvsy_sync_wait..
397 dib3000mc_write_word(state, 180, 0x2FF0);
398
399 // Impulse noise configuration
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300400 dib3000mc_set_impulse_noise(state, 0, TRANSMISSION_MODE_8K);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300401
402 // output mode set-up
403 dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z);
404
405 /* close the i2c-gate */
406 dib3000mc_write_word(state, 769, (1 << 7) );
407
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 return 0;
409}
410
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300411static int dib3000mc_sleep(struct dvb_frontend *demod)
412{
413 struct dib3000mc_state *state = demod->demodulator_priv;
414
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300415 dib3000mc_write_word(state, 1031, 0xFFFF);
416 dib3000mc_write_word(state, 1032, 0xFFFF);
Patrick Boettcher303cbea2006-09-19 12:51:56 -0300417 dib3000mc_write_word(state, 1033, 0xFFF0);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300418
419 return 0;
420}
421
422static void dib3000mc_set_adp_cfg(struct dib3000mc_state *state, s16 qam)
423{
424 u16 cfg[4] = { 0 },reg;
425 switch (qam) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300426 case QPSK:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300427 cfg[0] = 0x099a; cfg[1] = 0x7fae; cfg[2] = 0x0333; cfg[3] = 0x7ff0;
428 break;
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300429 case QAM_16:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300430 cfg[0] = 0x023d; cfg[1] = 0x7fdf; cfg[2] = 0x00a4; cfg[3] = 0x7ff0;
431 break;
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300432 case QAM_64:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300433 cfg[0] = 0x0148; cfg[1] = 0x7ff0; cfg[2] = 0x00a4; cfg[3] = 0x7ff8;
434 break;
435 }
436 for (reg = 129; reg < 133; reg++)
437 dib3000mc_write_word(state, reg, cfg[reg - 129]);
438}
439
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300440static void dib3000mc_set_channel_cfg(struct dib3000mc_state *state, struct dvb_frontend_parameters *ch, u16 seq)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300441{
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300442 u16 value;
443 dib3000mc_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth));
444 dib3000mc_set_timing(state, ch->u.ofdm.transmission_mode, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth), 0);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300445
446// if (boost)
447// dib3000mc_write_word(state, 100, (11 << 6) + 6);
448// else
449 dib3000mc_write_word(state, 100, (16 << 6) + 9);
450
451 dib3000mc_write_word(state, 1027, 0x0800);
452 dib3000mc_write_word(state, 1027, 0x0000);
453
454 //Default cfg isi offset adp
455 dib3000mc_write_word(state, 26, 0x6680);
456 dib3000mc_write_word(state, 29, 0x1273);
457 dib3000mc_write_word(state, 33, 5);
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300458 dib3000mc_set_adp_cfg(state, QAM_16);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300459 dib3000mc_write_word(state, 133, 15564);
460
461 dib3000mc_write_word(state, 12 , 0x0);
462 dib3000mc_write_word(state, 13 , 0x3e8);
463 dib3000mc_write_word(state, 14 , 0x0);
464 dib3000mc_write_word(state, 15 , 0x3f2);
465
466 dib3000mc_write_word(state, 93,0);
467 dib3000mc_write_word(state, 94,0);
468 dib3000mc_write_word(state, 95,0);
469 dib3000mc_write_word(state, 96,0);
470 dib3000mc_write_word(state, 97,0);
471 dib3000mc_write_word(state, 98,0);
472
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300473 dib3000mc_set_impulse_noise(state, 0, ch->u.ofdm.transmission_mode);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300474
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300475 value = 0;
476 switch (ch->u.ofdm.transmission_mode) {
477 case TRANSMISSION_MODE_2K: value |= (0 << 7); break;
478 default:
479 case TRANSMISSION_MODE_8K: value |= (1 << 7); break;
480 }
481 switch (ch->u.ofdm.guard_interval) {
482 case GUARD_INTERVAL_1_32: value |= (0 << 5); break;
483 case GUARD_INTERVAL_1_16: value |= (1 << 5); break;
484 case GUARD_INTERVAL_1_4: value |= (3 << 5); break;
485 default:
486 case GUARD_INTERVAL_1_8: value |= (2 << 5); break;
487 }
488 switch (ch->u.ofdm.constellation) {
489 case QPSK: value |= (0 << 3); break;
490 case QAM_16: value |= (1 << 3); break;
491 default:
492 case QAM_64: value |= (2 << 3); break;
493 }
494 switch (HIERARCHY_1) {
495 case HIERARCHY_2: value |= 2; break;
496 case HIERARCHY_4: value |= 4; break;
497 default:
498 case HIERARCHY_1: value |= 1; break;
499 }
500 dib3000mc_write_word(state, 0, value);
Mario Rossie3ab2fd2006-12-20 10:54:30 -0300501 dib3000mc_write_word(state, 5, (1 << 8) | ((seq & 0xf) << 4));
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300502
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300503 value = 0;
504 if (ch->u.ofdm.hierarchy_information == 1)
505 value |= (1 << 4);
506 if (1 == 1)
507 value |= 1;
508 switch ((ch->u.ofdm.hierarchy_information == 0 || 1 == 1) ? ch->u.ofdm.code_rate_HP : ch->u.ofdm.code_rate_LP) {
509 case FEC_2_3: value |= (2 << 1); break;
510 case FEC_3_4: value |= (3 << 1); break;
511 case FEC_5_6: value |= (5 << 1); break;
512 case FEC_7_8: value |= (7 << 1); break;
513 default:
514 case FEC_1_2: value |= (1 << 1); break;
515 }
516 dib3000mc_write_word(state, 181, value);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300517
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300518 // diversity synchro delay add 50% SFN margin
519 switch (ch->u.ofdm.transmission_mode) {
520 case TRANSMISSION_MODE_8K: value = 256; break;
521 case TRANSMISSION_MODE_2K:
522 default: value = 64; break;
523 }
524 switch (ch->u.ofdm.guard_interval) {
525 case GUARD_INTERVAL_1_16: value *= 2; break;
526 case GUARD_INTERVAL_1_8: value *= 4; break;
527 case GUARD_INTERVAL_1_4: value *= 8; break;
528 default:
529 case GUARD_INTERVAL_1_32: value *= 1; break;
530 }
531 value <<= 4;
532 value |= dib3000mc_read_word(state, 180) & 0x000f;
533 dib3000mc_write_word(state, 180, value);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300534
535 // restart demod
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300536 value = dib3000mc_read_word(state, 0);
537 dib3000mc_write_word(state, 0, value | (1 << 9));
538 dib3000mc_write_word(state, 0, value);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300539
540 msleep(30);
541
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300542 dib3000mc_set_impulse_noise(state, state->cfg->impulse_noise_mode, ch->u.ofdm.transmission_mode);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300543}
544
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300545static int dib3000mc_autosearch_start(struct dvb_frontend *demod, struct dvb_frontend_parameters *chan)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300546{
547 struct dib3000mc_state *state = demod->demodulator_priv;
548 u16 reg;
549// u32 val;
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300550 struct dvb_frontend_parameters schan;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300551
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300552 schan = *chan;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300553
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300554 /* TODO what is that ? */
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300555
556 /* a channel for autosearch */
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300557 schan.u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
558 schan.u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
559 schan.u.ofdm.constellation = QAM_64;
560 schan.u.ofdm.code_rate_HP = FEC_2_3;
561 schan.u.ofdm.code_rate_LP = FEC_2_3;
562 schan.u.ofdm.hierarchy_information = 0;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300563
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300564 dib3000mc_set_channel_cfg(state, &schan, 11);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300565
566 reg = dib3000mc_read_word(state, 0);
567 dib3000mc_write_word(state, 0, reg | (1 << 8));
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300568 dib3000mc_read_word(state, 511);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300569 dib3000mc_write_word(state, 0, reg);
570
571 return 0;
572}
573
574static int dib3000mc_autosearch_is_irq(struct dvb_frontend *demod)
575{
576 struct dib3000mc_state *state = demod->demodulator_priv;
577 u16 irq_pending = dib3000mc_read_word(state, 511);
578
579 if (irq_pending & 0x1) // failed
580 return 1;
581
582 if (irq_pending & 0x2) // succeeded
583 return 2;
584
585 return 0; // still pending
586}
587
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300588static int dib3000mc_tune(struct dvb_frontend *demod, struct dvb_frontend_parameters *ch)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300589{
590 struct dib3000mc_state *state = demod->demodulator_priv;
591
592 // ** configure demod **
593 dib3000mc_set_channel_cfg(state, ch, 0);
594
595 // activates isi
Matt Doran8f6956c2007-07-31 07:09:30 -0300596 if (state->sfn_workaround_active) {
597 dprintk("SFN workaround is active\n");
598 dib3000mc_write_word(state, 29, 0x1273);
599 dib3000mc_write_word(state, 108, 0x4000); // P_pha3_force_pha_shift
600 } else {
601 dib3000mc_write_word(state, 29, 0x1073);
602 dib3000mc_write_word(state, 108, 0x0000); // P_pha3_force_pha_shift
603 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300604
Patrick Boettcher01373a52007-07-30 12:49:04 -0300605 dib3000mc_set_adp_cfg(state, (u8)ch->u.ofdm.constellation);
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300606 if (ch->u.ofdm.transmission_mode == TRANSMISSION_MODE_8K) {
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300607 dib3000mc_write_word(state, 26, 38528);
608 dib3000mc_write_word(state, 33, 8);
609 } else {
610 dib3000mc_write_word(state, 26, 30336);
611 dib3000mc_write_word(state, 33, 6);
612 }
613
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300614 if (dib3000mc_read_word(state, 509) & 0x80)
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300615 dib3000mc_set_timing(state, ch->u.ofdm.transmission_mode, BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth), 1);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300616
617 return 0;
618}
619
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300620struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating)
621{
622 struct dib3000mc_state *st = demod->demodulator_priv;
623 return dibx000_get_i2c_adapter(&st->i2c_master, DIBX000_I2C_INTERFACE_TUNER, gating);
624}
625
626EXPORT_SYMBOL(dib3000mc_get_tuner_i2c_master);
627
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628static int dib3000mc_get_frontend(struct dvb_frontend* fe,
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300629 struct dvb_frontend_parameters *fep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300631 struct dib3000mc_state *state = fe->demodulator_priv;
632 u16 tps = dib3000mc_read_word(state,458);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300634 fep->inversion = INVERSION_AUTO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300636 fep->u.ofdm.bandwidth = state->current_bandwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300638 switch ((tps >> 8) & 0x1) {
639 case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break;
640 case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 }
642
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300643 switch (tps & 0x3) {
644 case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break;
645 case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break;
646 case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break;
647 case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 }
649
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300650 switch ((tps >> 13) & 0x3) {
651 case 0: fep->u.ofdm.constellation = QPSK; break;
652 case 1: fep->u.ofdm.constellation = QAM_16; break;
653 case 2:
654 default: fep->u.ofdm.constellation = QAM_64; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 }
656
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300657 /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */
658 /* (tps >> 12) & 0x1 == hrch is used, (tps >> 9) & 0x7 == alpha */
659
660 fep->u.ofdm.hierarchy_information = HIERARCHY_NONE;
661 switch ((tps >> 5) & 0x7) {
662 case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break;
663 case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break;
664 case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break;
665 case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break;
666 case 7:
667 default: fep->u.ofdm.code_rate_HP = FEC_7_8; break;
668
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 }
670
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300671 switch ((tps >> 2) & 0x7) {
672 case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break;
673 case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break;
674 case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break;
675 case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break;
676 case 7:
677 default: fep->u.ofdm.code_rate_LP = FEC_7_8; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679
680 return 0;
681}
682
683static int dib3000mc_set_frontend(struct dvb_frontend* fe,
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300684 struct dvb_frontend_parameters *fep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300686 struct dib3000mc_state *state = fe->demodulator_priv;
Soeren Moch853ea132008-01-25 06:27:06 -0300687 int ret;
688
689 dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300691 state->current_bandwidth = fep->u.ofdm.bandwidth;
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300692 dib3000mc_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->u.ofdm.bandwidth));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
Matt Doran8f6956c2007-07-31 07:09:30 -0300694 /* maybe the parameter has been changed */
695 state->sfn_workaround_active = buggy_sfn_workaround;
696
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300697 if (fe->ops.tuner_ops.set_params) {
698 fe->ops.tuner_ops.set_params(fe, fep);
699 msleep(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300701
702 if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ||
703 fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ||
704 fep->u.ofdm.constellation == QAM_AUTO ||
705 fep->u.ofdm.code_rate_HP == FEC_AUTO) {
Jose Alberto Reguero3a0311c2008-01-25 06:05:16 -0300706 int i = 1000, found;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300707
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300708 dib3000mc_autosearch_start(fe, fep);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300709 do {
710 msleep(1);
711 found = dib3000mc_autosearch_is_irq(fe);
712 } while (found == 0 && i--);
713
714 dprintk("autosearch returns: %d\n",found);
715 if (found == 0 || found == 1)
716 return 0; // no channel found
717
718 dib3000mc_get_frontend(fe, fep);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300719 }
720
Soeren Moch853ea132008-01-25 06:27:06 -0300721 ret = dib3000mc_tune(fe, fep);
722
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300723 /* make this a config parameter */
724 dib3000mc_set_output_mode(state, OUTMODE_MPEG2_FIFO);
Soeren Moch853ea132008-01-25 06:27:06 -0300725 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726}
727
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300728static int dib3000mc_read_status(struct dvb_frontend *fe, fe_status_t *stat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300730 struct dib3000mc_state *state = fe->demodulator_priv;
731 u16 lock = dib3000mc_read_word(state, 509);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732
733 *stat = 0;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300734
735 if (lock & 0x8000)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 *stat |= FE_HAS_SIGNAL;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300737 if (lock & 0x3000)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 *stat |= FE_HAS_CARRIER;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300739 if (lock & 0x0100)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 *stat |= FE_HAS_VITERBI;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300741 if (lock & 0x0010)
742 *stat |= FE_HAS_SYNC;
743 if (lock & 0x0008)
744 *stat |= FE_HAS_LOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745
746 return 0;
747}
748
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300749static int dib3000mc_read_ber(struct dvb_frontend *fe, u32 *ber)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300751 struct dib3000mc_state *state = fe->demodulator_priv;
752 *ber = (dib3000mc_read_word(state, 500) << 16) | dib3000mc_read_word(state, 501);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 return 0;
754}
755
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300756static int dib3000mc_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300758 struct dib3000mc_state *state = fe->demodulator_priv;
759 *unc = dib3000mc_read_word(state, 508);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 return 0;
761}
762
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300763static int dib3000mc_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300765 struct dib3000mc_state *state = fe->demodulator_priv;
766 u16 val = dib3000mc_read_word(state, 392);
767 *strength = 65535 - val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 return 0;
769}
770
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr)
772{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300773 *snr = 0x0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 return 0;
775}
776
777static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
778{
Johannes Stezenbach776338e2005-06-23 22:02:35 -0700779 tune->min_delay_ms = 1000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 return 0;
781}
782
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300783static void dib3000mc_release(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300785 struct dib3000mc_state *state = fe->demodulator_priv;
786 dibx000_exit_i2c_master(&state->i2c_master);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 kfree(state);
788}
789
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300790int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300792 struct dib3000mc_state *state = fe->demodulator_priv;
793 dib3000mc_write_word(state, 212 + index, onoff ? (1 << 13) | pid : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 return 0;
795}
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300796EXPORT_SYMBOL(dib3000mc_pid_control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300798int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300800 struct dib3000mc_state *state = fe->demodulator_priv;
801 u16 tmp = dib3000mc_read_word(state, 206) & ~(1 << 4);
802 tmp |= (onoff << 4);
803 return dib3000mc_write_word(state, 206, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804}
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300805EXPORT_SYMBOL(dib3000mc_pid_parse);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300807void dib3000mc_set_config(struct dvb_frontend *fe, struct dib3000mc_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300809 struct dib3000mc_state *state = fe->demodulator_priv;
810 state->cfg = cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811}
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300812EXPORT_SYMBOL(dib3000mc_set_config);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300814int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib3000mc_config cfg[])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815{
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300816 struct dib3000mc_state st = { .i2c_adap = i2c };
817 int k;
818 u8 new_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300820 static u8 DIB3000MC_I2C_ADDRESS[] = {20,22,24,26};
821
822 for (k = no_of_demods-1; k >= 0; k--) {
823 st.cfg = &cfg[k];
824
825 /* designated i2c address */
826 new_addr = DIB3000MC_I2C_ADDRESS[k];
827 st.i2c_addr = new_addr;
828 if (dib3000mc_identify(&st) != 0) {
829 st.i2c_addr = default_addr;
830 if (dib3000mc_identify(&st) != 0) {
831 dprintk("-E- DiB3000P/MC #%d: not identified\n", k);
832 return -ENODEV;
833 }
834 }
835
836 dib3000mc_set_output_mode(&st, OUTMODE_MPEG2_PAR_CONT_CLK);
837
838 // set new i2c address and force divstr (Bit 1) to value 0 (Bit 0)
839 dib3000mc_write_word(&st, 1024, (new_addr << 3) | 0x1);
840 st.i2c_addr = new_addr;
841 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300843 for (k = 0; k < no_of_demods; k++) {
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300844 st.cfg = &cfg[k];
845 st.i2c_addr = DIB3000MC_I2C_ADDRESS[k];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300847 dib3000mc_write_word(&st, 1024, st.i2c_addr << 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300849 /* turn off data output */
850 dib3000mc_set_output_mode(&st, OUTMODE_HIGH_Z);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300852 return 0;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300853}
854EXPORT_SYMBOL(dib3000mc_i2c_enumeration);
855
856static struct dvb_frontend_ops dib3000mc_ops;
857
858struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg)
859{
860 struct dvb_frontend *demod;
861 struct dib3000mc_state *st;
862 st = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL);
863 if (st == NULL)
864 return NULL;
865
866 st->cfg = cfg;
867 st->i2c_adap = i2c_adap;
Patrick Boettcher6958eff2006-09-19 12:51:40 -0300868 st->i2c_addr = i2c_addr;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300869
870 demod = &st->demod;
871 demod->demodulator_priv = st;
872 memcpy(&st->demod.ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops));
873
874 if (dib3000mc_identify(st) != 0)
875 goto error;
876
877 dibx000_init_i2c_master(&st->i2c_master, DIB3000MC, st->i2c_adap, st->i2c_addr);
878
Patrick Boettcher303cbea2006-09-19 12:51:56 -0300879 dib3000mc_write_word(st, 1037, 0x3130);
880
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300881 return demod;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882
883error:
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300884 kfree(st);
885 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886}
Patrick Boettchere4d6c1f2006-08-08 15:48:09 -0300887EXPORT_SYMBOL(dib3000mc_attach);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888
889static struct dvb_frontend_ops dib3000mc_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 .info = {
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300891 .name = "DiBcom 3000MC/P",
892 .type = FE_OFDM,
893 .frequency_min = 44250000,
894 .frequency_max = 867250000,
895 .frequency_stepsize = 62500,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 .caps = FE_CAN_INVERSION_AUTO |
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300897 FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
898 FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
899 FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
900 FE_CAN_TRANSMISSION_MODE_AUTO |
901 FE_CAN_GUARD_INTERVAL_AUTO |
902 FE_CAN_RECOVER |
903 FE_CAN_HIERARCHY_AUTO,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 },
905
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300906 .release = dib3000mc_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300908 .init = dib3000mc_init,
909 .sleep = dib3000mc_sleep,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300911 .set_frontend = dib3000mc_set_frontend,
912 .get_tune_settings = dib3000mc_fe_get_tune_settings,
913 .get_frontend = dib3000mc_get_frontend,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300915 .read_status = dib3000mc_read_status,
916 .read_ber = dib3000mc_read_ber,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 .read_signal_strength = dib3000mc_read_signal_strength,
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300918 .read_snr = dib3000mc_read_snr,
919 .read_ucblocks = dib3000mc_read_unc_blocks,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920};
921
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300922MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
923MODULE_DESCRIPTION("Driver for the DiBcom 3000MC/P COFDM demodulator");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924MODULE_LICENSE("GPL");