blob: ffad181a96920bf90250d3aba9757247f1b56cec [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090015#include <linux/slab.h>
Patrick Boettcherb7571f82006-08-08 15:48:10 -030016#include <linux/i2c.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017
Patrick Boettcherb7571f82006-08-08 15:48:10 -030018#include "dvb_frontend.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070019
Patrick Boettcherb7571f82006-08-08 15:48:10 -030020#include "dib3000mc.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070021
Linus Torvalds1da177e2005-04-16 15:20:36 -070022static int debug;
23module_param(debug, int, 0644);
Patrick Boettcherb7571f82006-08-08 15:48:10 -030024MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
Matt Doran8f6956c2007-07-31 07:09:30 -030026static int buggy_sfn_workaround;
27module_param(buggy_sfn_workaround, int, 0644);
Patrick Boettcher8d999962007-07-31 10:36:06 -030028MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (default: 0)");
Matt Doran8f6956c2007-07-31 07:09:30 -030029
Patrick Boettcherb6884a12007-07-27 10:08:51 -030030#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB3000MC/P:"); printk(args); printk("\n"); } } while (0)
Patrick Boettcherb7571f82006-08-08 15:48:10 -030031
32struct dib3000mc_state {
33 struct dvb_frontend demod;
34 struct dib3000mc_config *cfg;
35
36 u8 i2c_addr;
37 struct i2c_adapter *i2c_adap;
38
39 struct dibx000_i2c_master i2c_master;
40
Patrick Boettcher01b4bf32006-09-19 12:51:53 -030041 u32 timf;
42
Mauro Carvalho Chehab88ab8982011-12-26 20:01:24 -030043 u32 current_bandwidth;
Patrick Boettcherb7571f82006-08-08 15:48:10 -030044
45 u16 dev_id;
Matt Doran8f6956c2007-07-31 07:09:30 -030046
47 u8 sfn_workaround_active :1;
Patrick Boettcherb7571f82006-08-08 15:48:10 -030048};
49
50static u16 dib3000mc_read_word(struct dib3000mc_state *state, u16 reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -070051{
Patrick Boettcherb7571f82006-08-08 15:48:10 -030052 u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff };
53 u8 rb[2];
54 struct i2c_msg msg[2] = {
55 { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 },
56 { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 },
57 };
58
59 if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
60 dprintk("i2c read error on %d\n",reg);
61
62 return (rb[0] << 8) | rb[1];
63}
64
65static int dib3000mc_write_word(struct dib3000mc_state *state, u16 reg, u16 val)
66{
67 u8 b[4] = {
68 (reg >> 8) & 0xff, reg & 0xff,
69 (val >> 8) & 0xff, val & 0xff,
70 };
71 struct i2c_msg msg = {
72 .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4
73 };
74 return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
75}
76
Patrick Boettcherb7571f82006-08-08 15:48:10 -030077static int dib3000mc_identify(struct dib3000mc_state *state)
78{
79 u16 value;
80 if ((value = dib3000mc_read_word(state, 1025)) != 0x01b3) {
81 dprintk("-E- DiB3000MC/P: wrong Vendor ID (read=0x%x)\n",value);
82 return -EREMOTEIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 }
84
Patrick Boettcherb7571f82006-08-08 15:48:10 -030085 value = dib3000mc_read_word(state, 1026);
86 if (value != 0x3001 && value != 0x3002) {
87 dprintk("-E- DiB3000MC/P: wrong Device ID (%x)\n",value);
88 return -EREMOTEIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -030090 state->dev_id = value;
91
92 dprintk("-I- found DiB3000MC/P: %x\n",state->dev_id);
93
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 return 0;
95}
96
Patrick Boettcherb6884a12007-07-27 10:08:51 -030097static int dib3000mc_set_timing(struct dib3000mc_state *state, s16 nfft, u32 bw, u8 update_offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -070098{
Patrick Boettcher01b4bf32006-09-19 12:51:53 -030099 u32 timf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300101 if (state->timf == 0) {
102 timf = 1384402; // default value for 8MHz
103 if (update_offset)
104 msleep(200); // first time we do an update
105 } else
106 timf = state->timf;
107
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300108 timf *= (bw / 1000);
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300109
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300110 if (update_offset) {
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300111 s16 tim_offs = dib3000mc_read_word(state, 416);
112
113 if (tim_offs & 0x2000)
114 tim_offs -= 0x4000;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300115
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300116 if (nfft == TRANSMISSION_MODE_2K)
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300117 tim_offs *= 4;
118
119 timf += tim_offs;
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300120 state->timf = timf / (bw / 1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300122
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300123 dprintk("timf: %d\n", timf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300125 dib3000mc_write_word(state, 23, (u16) (timf >> 16));
126 dib3000mc_write_word(state, 24, (u16) (timf ) & 0xffff);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300127
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 return 0;
129}
130
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300131static int dib3000mc_setup_pwm_state(struct dib3000mc_state *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132{
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300133 u16 reg_51, reg_52 = state->cfg->agc->setup & 0xfefb;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300134 if (state->cfg->pwm3_inversion) {
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300135 reg_51 = (2 << 14) | (0 << 10) | (7 << 6) | (2 << 2) | (2 << 0);
136 reg_52 |= (1 << 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 } else {
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300138 reg_51 = (2 << 14) | (4 << 10) | (7 << 6) | (2 << 2) | (2 << 0);
139 reg_52 |= (1 << 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 }
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300141 dib3000mc_write_word(state, 51, reg_51);
142 dib3000mc_write_word(state, 52, reg_52);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300143
144 if (state->cfg->use_pwm3)
145 dib3000mc_write_word(state, 245, (1 << 3) | (1 << 0));
146 else
147 dib3000mc_write_word(state, 245, 0);
148
149 dib3000mc_write_word(state, 1040, 0x3);
150 return 0;
151}
152
153static int dib3000mc_set_output_mode(struct dib3000mc_state *state, int mode)
154{
155 int ret = 0;
156 u16 fifo_threshold = 1792;
157 u16 outreg = 0;
158 u16 outmode = 0;
159 u16 elecout = 1;
Patrick Boettcherfb6065b2006-08-21 08:21:52 -0300160 u16 smo_reg = dib3000mc_read_word(state, 206) & 0x0010; /* keep the pid_parse bit */
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300161
162 dprintk("-I- Setting output mode for demod %p to %d\n",
163 &state->demod, mode);
164
165 switch (mode) {
166 case OUTMODE_HIGH_Z: // disable
167 elecout = 0;
168 break;
169 case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock
170 outmode = 0;
171 break;
172 case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock
173 outmode = 1;
174 break;
175 case OUTMODE_MPEG2_SERIAL: // STBs with serial input
176 outmode = 2;
177 break;
178 case OUTMODE_MPEG2_FIFO: // e.g. USB feeding
179 elecout = 3;
180 /*ADDR @ 206 :
181 P_smo_error_discard [1;6:6] = 0
182 P_smo_rs_discard [1;5:5] = 0
183 P_smo_pid_parse [1;4:4] = 0
184 P_smo_fifo_flush [1;3:3] = 0
185 P_smo_mode [2;2:1] = 11
186 P_smo_ovf_prot [1;0:0] = 0
187 */
Patrick Boettcherfb6065b2006-08-21 08:21:52 -0300188 smo_reg |= 3 << 1;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300189 fifo_threshold = 512;
190 outmode = 5;
191 break;
192 case OUTMODE_DIVERSITY:
193 outmode = 4;
194 elecout = 1;
195 break;
196 default:
197 dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod);
198 outmode = 0;
199 break;
200 }
201
202 if ((state->cfg->output_mpeg2_in_188_bytes))
Patrick Boettcher559463b2006-08-19 16:13:53 -0300203 smo_reg |= (1 << 5); // P_smo_rs_discard [1;5:5] = 1
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300204
205 outreg = dib3000mc_read_word(state, 244) & 0x07FF;
206 outreg |= (outmode << 11);
207 ret |= dib3000mc_write_word(state, 244, outreg);
208 ret |= dib3000mc_write_word(state, 206, smo_reg); /*smo_ mode*/
209 ret |= dib3000mc_write_word(state, 207, fifo_threshold); /* synchronous fread */
210 ret |= dib3000mc_write_word(state, 1040, elecout); /* P_out_cfg */
211 return ret;
212}
213
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300214static int dib3000mc_set_bandwidth(struct dib3000mc_state *state, u32 bw)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300215{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300216 u16 bw_cfg[6] = { 0 };
217 u16 imp_bw_cfg[3] = { 0 };
218 u16 reg;
219
220/* settings here are for 27.7MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 switch (bw) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300222 case 8000:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300223 bw_cfg[0] = 0x0019; bw_cfg[1] = 0x5c30; bw_cfg[2] = 0x0054; bw_cfg[3] = 0x88a0; bw_cfg[4] = 0x01a6; bw_cfg[5] = 0xab20;
224 imp_bw_cfg[0] = 0x04db; imp_bw_cfg[1] = 0x00db; imp_bw_cfg[2] = 0x00b7;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300226
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300227 case 7000:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300228 bw_cfg[0] = 0x001c; bw_cfg[1] = 0xfba5; bw_cfg[2] = 0x0060; bw_cfg[3] = 0x9c25; bw_cfg[4] = 0x01e3; bw_cfg[5] = 0x0cb7;
229 imp_bw_cfg[0] = 0x04c0; imp_bw_cfg[1] = 0x00c0; imp_bw_cfg[2] = 0x00a0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300231
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300232 case 6000:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300233 bw_cfg[0] = 0x0021; bw_cfg[1] = 0xd040; bw_cfg[2] = 0x0070; bw_cfg[3] = 0xb62b; bw_cfg[4] = 0x0233; bw_cfg[5] = 0x8ed5;
234 imp_bw_cfg[0] = 0x04a5; imp_bw_cfg[1] = 0x00a5; imp_bw_cfg[2] = 0x0089;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300236
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300237 case 5000:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300238 bw_cfg[0] = 0x0028; bw_cfg[1] = 0x9380; bw_cfg[2] = 0x0087; bw_cfg[3] = 0x4100; bw_cfg[4] = 0x02a4; bw_cfg[5] = 0x4500;
239 imp_bw_cfg[0] = 0x0489; imp_bw_cfg[1] = 0x0089; imp_bw_cfg[2] = 0x0072;
240 break;
241
242 default: return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300244
245 for (reg = 6; reg < 12; reg++)
246 dib3000mc_write_word(state, reg, bw_cfg[reg - 6]);
247 dib3000mc_write_word(state, 12, 0x0000);
248 dib3000mc_write_word(state, 13, 0x03e8);
249 dib3000mc_write_word(state, 14, 0x0000);
250 dib3000mc_write_word(state, 15, 0x03f2);
251 dib3000mc_write_word(state, 16, 0x0001);
252 dib3000mc_write_word(state, 17, 0xb0d0);
253 // P_sec_len
254 dib3000mc_write_word(state, 18, 0x0393);
255 dib3000mc_write_word(state, 19, 0x8700);
256
257 for (reg = 55; reg < 58; reg++)
258 dib3000mc_write_word(state, reg, imp_bw_cfg[reg - 55]);
259
260 // Timing configuration
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300261 dib3000mc_set_timing(state, TRANSMISSION_MODE_2K, bw, 0);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300262
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 return 0;
264}
265
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300266static u16 impulse_noise_val[29] =
267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300269 0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c, 0x3ffe, 0x7f3,
270 0x2d94, 0x76, 0x53d, 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3, 0x3feb, 0x7d2,
271 0x365e, 0x76, 0x48c, 0x3ffe, 0x5b3, 0x3feb, 0x76, 0x0000, 0xd
272};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300274static void dib3000mc_set_impulse_noise(struct dib3000mc_state *state, u8 mode, s16 nfft)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300276 u16 i;
277 for (i = 58; i < 87; i++)
278 dib3000mc_write_word(state, i, impulse_noise_val[i-58]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300280 if (nfft == TRANSMISSION_MODE_8K) {
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300281 dib3000mc_write_word(state, 58, 0x3b);
282 dib3000mc_write_word(state, 84, 0x00);
283 dib3000mc_write_word(state, 85, 0x8200);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 }
285
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300286 dib3000mc_write_word(state, 34, 0x1294);
287 dib3000mc_write_word(state, 35, 0x1ff8);
288 if (mode == 1)
289 dib3000mc_write_word(state, 55, dib3000mc_read_word(state, 55) | (1 << 10));
290}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300292static int dib3000mc_init(struct dvb_frontend *demod)
293{
294 struct dib3000mc_state *state = demod->demodulator_priv;
295 struct dibx000_agc_config *agc = state->cfg->agc;
296
297 // Restart Configuration
298 dib3000mc_write_word(state, 1027, 0x8000);
299 dib3000mc_write_word(state, 1027, 0x0000);
300
301 // power up the demod + mobility configuration
302 dib3000mc_write_word(state, 140, 0x0000);
303 dib3000mc_write_word(state, 1031, 0);
304
305 if (state->cfg->mobile_mode) {
306 dib3000mc_write_word(state, 139, 0x0000);
307 dib3000mc_write_word(state, 141, 0x0000);
308 dib3000mc_write_word(state, 175, 0x0002);
309 dib3000mc_write_word(state, 1032, 0x0000);
310 } else {
311 dib3000mc_write_word(state, 139, 0x0001);
312 dib3000mc_write_word(state, 141, 0x0000);
313 dib3000mc_write_word(state, 175, 0x0000);
314 dib3000mc_write_word(state, 1032, 0x012C);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 }
Patrick Boettcher303cbea2006-09-19 12:51:56 -0300316 dib3000mc_write_word(state, 1033, 0x0000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300318 // P_clk_cfg
Patrick Boettcher303cbea2006-09-19 12:51:56 -0300319 dib3000mc_write_word(state, 1037, 0x3130);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300321 // other configurations
322
323 // P_ctrl_sfreq
324 dib3000mc_write_word(state, 33, (5 << 0));
325 dib3000mc_write_word(state, 88, (1 << 10) | (0x10 << 0));
326
327 // Phase noise control
328 // P_fft_phacor_inh, P_fft_phacor_cpe, P_fft_powrange
329 dib3000mc_write_word(state, 99, (1 << 9) | (0x20 << 0));
330
331 if (state->cfg->phase_noise_mode == 0)
332 dib3000mc_write_word(state, 111, 0x00);
333 else
334 dib3000mc_write_word(state, 111, 0x02);
335
336 // P_agc_global
337 dib3000mc_write_word(state, 50, 0x8000);
338
339 // agc setup misc
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300340 dib3000mc_setup_pwm_state(state);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300341
342 // P_agc_counter_lock
343 dib3000mc_write_word(state, 53, 0x87);
344 // P_agc_counter_unlock
345 dib3000mc_write_word(state, 54, 0x87);
346
347 /* agc */
348 dib3000mc_write_word(state, 36, state->cfg->max_time);
Patrick Boettcher5570dd02006-10-13 11:35:12 -0300349 dib3000mc_write_word(state, 37, (state->cfg->agc_command1 << 13) | (state->cfg->agc_command2 << 12) | (0x1d << 0));
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300350 dib3000mc_write_word(state, 38, state->cfg->pwm3_value);
351 dib3000mc_write_word(state, 39, state->cfg->ln_adc_level);
352
353 // set_agc_loop_Bw
354 dib3000mc_write_word(state, 40, 0x0179);
355 dib3000mc_write_word(state, 41, 0x03f0);
356
357 dib3000mc_write_word(state, 42, agc->agc1_max);
358 dib3000mc_write_word(state, 43, agc->agc1_min);
359 dib3000mc_write_word(state, 44, agc->agc2_max);
360 dib3000mc_write_word(state, 45, agc->agc2_min);
361 dib3000mc_write_word(state, 46, (agc->agc1_pt1 << 8) | agc->agc1_pt2);
362 dib3000mc_write_word(state, 47, (agc->agc1_slope1 << 8) | agc->agc1_slope2);
363 dib3000mc_write_word(state, 48, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
364 dib3000mc_write_word(state, 49, (agc->agc2_slope1 << 8) | agc->agc2_slope2);
365
366// Begin: TimeOut registers
367 // P_pha3_thres
368 dib3000mc_write_word(state, 110, 3277);
369 // P_timf_alpha = 6, P_corm_alpha = 6, P_corm_thres = 0x80
370 dib3000mc_write_word(state, 26, 0x6680);
371 // lock_mask0
372 dib3000mc_write_word(state, 1, 4);
373 // lock_mask1
374 dib3000mc_write_word(state, 2, 4);
375 // lock_mask2
376 dib3000mc_write_word(state, 3, 0x1000);
377 // P_search_maxtrial=1
378 dib3000mc_write_word(state, 5, 1);
379
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300380 dib3000mc_set_bandwidth(state, 8000);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300381
382 // div_lock_mask
383 dib3000mc_write_word(state, 4, 0x814);
384
385 dib3000mc_write_word(state, 21, (1 << 9) | 0x164);
386 dib3000mc_write_word(state, 22, 0x463d);
387
388 // Spurious rm cfg
389 // P_cspu_regul, P_cspu_win_cut
390 dib3000mc_write_word(state, 120, 0x200f);
391 // P_adp_selec_monit
392 dib3000mc_write_word(state, 134, 0);
393
394 // Fec cfg
395 dib3000mc_write_word(state, 195, 0x10);
396
397 // diversity register: P_dvsy_sync_wait..
398 dib3000mc_write_word(state, 180, 0x2FF0);
399
400 // Impulse noise configuration
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300401 dib3000mc_set_impulse_noise(state, 0, TRANSMISSION_MODE_8K);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300402
403 // output mode set-up
404 dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z);
405
406 /* close the i2c-gate */
407 dib3000mc_write_word(state, 769, (1 << 7) );
408
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 return 0;
410}
411
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300412static int dib3000mc_sleep(struct dvb_frontend *demod)
413{
414 struct dib3000mc_state *state = demod->demodulator_priv;
415
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300416 dib3000mc_write_word(state, 1031, 0xFFFF);
417 dib3000mc_write_word(state, 1032, 0xFFFF);
Patrick Boettcher303cbea2006-09-19 12:51:56 -0300418 dib3000mc_write_word(state, 1033, 0xFFF0);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300419
420 return 0;
421}
422
423static void dib3000mc_set_adp_cfg(struct dib3000mc_state *state, s16 qam)
424{
425 u16 cfg[4] = { 0 },reg;
426 switch (qam) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300427 case QPSK:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300428 cfg[0] = 0x099a; cfg[1] = 0x7fae; cfg[2] = 0x0333; cfg[3] = 0x7ff0;
429 break;
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300430 case QAM_16:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300431 cfg[0] = 0x023d; cfg[1] = 0x7fdf; cfg[2] = 0x00a4; cfg[3] = 0x7ff0;
432 break;
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300433 case QAM_64:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300434 cfg[0] = 0x0148; cfg[1] = 0x7ff0; cfg[2] = 0x00a4; cfg[3] = 0x7ff8;
435 break;
436 }
437 for (reg = 129; reg < 133; reg++)
438 dib3000mc_write_word(state, reg, cfg[reg - 129]);
439}
440
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300441static void dib3000mc_set_channel_cfg(struct dib3000mc_state *state,
442 struct dtv_frontend_properties *ch, u16 seq)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300443{
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300444 u16 value;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300445 u32 bw = BANDWIDTH_TO_KHZ(ch->bandwidth_hz);
446
447 dib3000mc_set_bandwidth(state, bw);
448 dib3000mc_set_timing(state, ch->transmission_mode, bw, 0);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300449
450// if (boost)
451// dib3000mc_write_word(state, 100, (11 << 6) + 6);
452// else
453 dib3000mc_write_word(state, 100, (16 << 6) + 9);
454
455 dib3000mc_write_word(state, 1027, 0x0800);
456 dib3000mc_write_word(state, 1027, 0x0000);
457
458 //Default cfg isi offset adp
459 dib3000mc_write_word(state, 26, 0x6680);
460 dib3000mc_write_word(state, 29, 0x1273);
461 dib3000mc_write_word(state, 33, 5);
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300462 dib3000mc_set_adp_cfg(state, QAM_16);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300463 dib3000mc_write_word(state, 133, 15564);
464
465 dib3000mc_write_word(state, 12 , 0x0);
466 dib3000mc_write_word(state, 13 , 0x3e8);
467 dib3000mc_write_word(state, 14 , 0x0);
468 dib3000mc_write_word(state, 15 , 0x3f2);
469
470 dib3000mc_write_word(state, 93,0);
471 dib3000mc_write_word(state, 94,0);
472 dib3000mc_write_word(state, 95,0);
473 dib3000mc_write_word(state, 96,0);
474 dib3000mc_write_word(state, 97,0);
475 dib3000mc_write_word(state, 98,0);
476
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300477 dib3000mc_set_impulse_noise(state, 0, ch->transmission_mode);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300478
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300479 value = 0;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300480 switch (ch->transmission_mode) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300481 case TRANSMISSION_MODE_2K: value |= (0 << 7); break;
482 default:
483 case TRANSMISSION_MODE_8K: value |= (1 << 7); break;
484 }
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300485 switch (ch->guard_interval) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300486 case GUARD_INTERVAL_1_32: value |= (0 << 5); break;
487 case GUARD_INTERVAL_1_16: value |= (1 << 5); break;
488 case GUARD_INTERVAL_1_4: value |= (3 << 5); break;
489 default:
490 case GUARD_INTERVAL_1_8: value |= (2 << 5); break;
491 }
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300492 switch (ch->modulation) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300493 case QPSK: value |= (0 << 3); break;
494 case QAM_16: value |= (1 << 3); break;
495 default:
496 case QAM_64: value |= (2 << 3); break;
497 }
498 switch (HIERARCHY_1) {
499 case HIERARCHY_2: value |= 2; break;
500 case HIERARCHY_4: value |= 4; break;
501 default:
502 case HIERARCHY_1: value |= 1; break;
503 }
504 dib3000mc_write_word(state, 0, value);
Mario Rossie3ab2fd2006-12-20 10:54:30 -0300505 dib3000mc_write_word(state, 5, (1 << 8) | ((seq & 0xf) << 4));
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300506
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300507 value = 0;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300508 if (ch->hierarchy == 1)
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300509 value |= (1 << 4);
510 if (1 == 1)
511 value |= 1;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300512 switch ((ch->hierarchy == 0 || 1 == 1) ? ch->code_rate_HP : ch->code_rate_LP) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300513 case FEC_2_3: value |= (2 << 1); break;
514 case FEC_3_4: value |= (3 << 1); break;
515 case FEC_5_6: value |= (5 << 1); break;
516 case FEC_7_8: value |= (7 << 1); break;
517 default:
518 case FEC_1_2: value |= (1 << 1); break;
519 }
520 dib3000mc_write_word(state, 181, value);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300521
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300522 // diversity synchro delay add 50% SFN margin
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300523 switch (ch->transmission_mode) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300524 case TRANSMISSION_MODE_8K: value = 256; break;
525 case TRANSMISSION_MODE_2K:
526 default: value = 64; break;
527 }
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300528 switch (ch->guard_interval) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300529 case GUARD_INTERVAL_1_16: value *= 2; break;
530 case GUARD_INTERVAL_1_8: value *= 4; break;
531 case GUARD_INTERVAL_1_4: value *= 8; break;
532 default:
533 case GUARD_INTERVAL_1_32: value *= 1; break;
534 }
535 value <<= 4;
536 value |= dib3000mc_read_word(state, 180) & 0x000f;
537 dib3000mc_write_word(state, 180, value);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300538
539 // restart demod
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300540 value = dib3000mc_read_word(state, 0);
541 dib3000mc_write_word(state, 0, value | (1 << 9));
542 dib3000mc_write_word(state, 0, value);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300543
544 msleep(30);
545
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300546 dib3000mc_set_impulse_noise(state, state->cfg->impulse_noise_mode, ch->transmission_mode);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300547}
548
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300549static int dib3000mc_autosearch_start(struct dvb_frontend *demod)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300550{
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300551 struct dtv_frontend_properties *chan = &demod->dtv_property_cache;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300552 struct dib3000mc_state *state = demod->demodulator_priv;
553 u16 reg;
554// u32 val;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300555 struct dtv_frontend_properties schan;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300556
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300557 schan = *chan;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300558
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300559 /* TODO what is that ? */
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300560
561 /* a channel for autosearch */
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300562 schan.transmission_mode = TRANSMISSION_MODE_8K;
563 schan.guard_interval = GUARD_INTERVAL_1_32;
564 schan.modulation = QAM_64;
565 schan.code_rate_HP = FEC_2_3;
566 schan.code_rate_LP = FEC_2_3;
567 schan.hierarchy = 0;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300568
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300569 dib3000mc_set_channel_cfg(state, &schan, 11);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300570
571 reg = dib3000mc_read_word(state, 0);
572 dib3000mc_write_word(state, 0, reg | (1 << 8));
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300573 dib3000mc_read_word(state, 511);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300574 dib3000mc_write_word(state, 0, reg);
575
576 return 0;
577}
578
579static int dib3000mc_autosearch_is_irq(struct dvb_frontend *demod)
580{
581 struct dib3000mc_state *state = demod->demodulator_priv;
582 u16 irq_pending = dib3000mc_read_word(state, 511);
583
584 if (irq_pending & 0x1) // failed
585 return 1;
586
587 if (irq_pending & 0x2) // succeeded
588 return 2;
589
590 return 0; // still pending
591}
592
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300593static int dib3000mc_tune(struct dvb_frontend *demod)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300594{
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300595 struct dtv_frontend_properties *ch = &demod->dtv_property_cache;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300596 struct dib3000mc_state *state = demod->demodulator_priv;
597
598 // ** configure demod **
599 dib3000mc_set_channel_cfg(state, ch, 0);
600
601 // activates isi
Matt Doran8f6956c2007-07-31 07:09:30 -0300602 if (state->sfn_workaround_active) {
603 dprintk("SFN workaround is active\n");
604 dib3000mc_write_word(state, 29, 0x1273);
605 dib3000mc_write_word(state, 108, 0x4000); // P_pha3_force_pha_shift
606 } else {
607 dib3000mc_write_word(state, 29, 0x1073);
608 dib3000mc_write_word(state, 108, 0x0000); // P_pha3_force_pha_shift
609 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300610
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300611 dib3000mc_set_adp_cfg(state, (u8)ch->modulation);
612 if (ch->transmission_mode == TRANSMISSION_MODE_8K) {
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300613 dib3000mc_write_word(state, 26, 38528);
614 dib3000mc_write_word(state, 33, 8);
615 } else {
616 dib3000mc_write_word(state, 26, 30336);
617 dib3000mc_write_word(state, 33, 6);
618 }
619
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300620 if (dib3000mc_read_word(state, 509) & 0x80)
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300621 dib3000mc_set_timing(state, ch->transmission_mode,
622 BANDWIDTH_TO_KHZ(ch->bandwidth_hz), 1);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300623
624 return 0;
625}
626
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300627struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating)
628{
629 struct dib3000mc_state *st = demod->demodulator_priv;
630 return dibx000_get_i2c_adapter(&st->i2c_master, DIBX000_I2C_INTERFACE_TUNER, gating);
631}
632
633EXPORT_SYMBOL(dib3000mc_get_tuner_i2c_master);
634
Mauro Carvalho Chehab7c61d802011-12-30 11:30:21 -0300635static int dib3000mc_get_frontend(struct dvb_frontend* fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636{
Mauro Carvalho Chehab7c61d802011-12-30 11:30:21 -0300637 struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300638 struct dib3000mc_state *state = fe->demodulator_priv;
639 u16 tps = dib3000mc_read_word(state,458);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300641 fep->inversion = INVERSION_AUTO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300643 fep->bandwidth_hz = state->current_bandwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300645 switch ((tps >> 8) & 0x1) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300646 case 0: fep->transmission_mode = TRANSMISSION_MODE_2K; break;
647 case 1: fep->transmission_mode = TRANSMISSION_MODE_8K; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 }
649
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300650 switch (tps & 0x3) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300651 case 0: fep->guard_interval = GUARD_INTERVAL_1_32; break;
652 case 1: fep->guard_interval = GUARD_INTERVAL_1_16; break;
653 case 2: fep->guard_interval = GUARD_INTERVAL_1_8; break;
654 case 3: fep->guard_interval = GUARD_INTERVAL_1_4; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 }
656
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300657 switch ((tps >> 13) & 0x3) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300658 case 0: fep->modulation = QPSK; break;
659 case 1: fep->modulation = QAM_16; break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300660 case 2:
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300661 default: fep->modulation = QAM_64; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 }
663
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300664 /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */
665 /* (tps >> 12) & 0x1 == hrch is used, (tps >> 9) & 0x7 == alpha */
666
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300667 fep->hierarchy = HIERARCHY_NONE;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300668 switch ((tps >> 5) & 0x7) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300669 case 1: fep->code_rate_HP = FEC_1_2; break;
670 case 2: fep->code_rate_HP = FEC_2_3; break;
671 case 3: fep->code_rate_HP = FEC_3_4; break;
672 case 5: fep->code_rate_HP = FEC_5_6; break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300673 case 7:
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300674 default: fep->code_rate_HP = FEC_7_8; break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300675
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 }
677
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300678 switch ((tps >> 2) & 0x7) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300679 case 1: fep->code_rate_LP = FEC_1_2; break;
680 case 2: fep->code_rate_LP = FEC_2_3; break;
681 case 3: fep->code_rate_LP = FEC_3_4; break;
682 case 5: fep->code_rate_LP = FEC_5_6; break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300683 case 7:
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300684 default: fep->code_rate_LP = FEC_7_8; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
687 return 0;
688}
689
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300690static int dib3000mc_set_frontend(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691{
Mauro Carvalho Chehab7c61d802011-12-30 11:30:21 -0300692 struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300693 struct dib3000mc_state *state = fe->demodulator_priv;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300694 int ret;
Soeren Moch853ea132008-01-25 06:27:06 -0300695
696 dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300698 state->current_bandwidth = fep->bandwidth_hz;
699 dib3000mc_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->bandwidth_hz));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700
Matt Doran8f6956c2007-07-31 07:09:30 -0300701 /* maybe the parameter has been changed */
702 state->sfn_workaround_active = buggy_sfn_workaround;
703
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300704 if (fe->ops.tuner_ops.set_params) {
Mauro Carvalho Chehab14d24d12011-12-24 12:24:33 -0300705 fe->ops.tuner_ops.set_params(fe);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300706 msleep(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300708
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300709 if (fep->transmission_mode == TRANSMISSION_MODE_AUTO ||
710 fep->guard_interval == GUARD_INTERVAL_AUTO ||
711 fep->modulation == QAM_AUTO ||
712 fep->code_rate_HP == FEC_AUTO) {
Jose Alberto Reguero3a0311c2008-01-25 06:05:16 -0300713 int i = 1000, found;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300714
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300715 dib3000mc_autosearch_start(fe);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300716 do {
717 msleep(1);
718 found = dib3000mc_autosearch_is_irq(fe);
719 } while (found == 0 && i--);
720
721 dprintk("autosearch returns: %d\n",found);
722 if (found == 0 || found == 1)
723 return 0; // no channel found
724
Mauro Carvalho Chehab7c61d802011-12-30 11:30:21 -0300725 dib3000mc_get_frontend(fe);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300726 }
727
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300728 ret = dib3000mc_tune(fe);
Soeren Moch853ea132008-01-25 06:27:06 -0300729
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300730 /* make this a config parameter */
731 dib3000mc_set_output_mode(state, OUTMODE_MPEG2_FIFO);
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300732 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733}
734
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300735static int dib3000mc_read_status(struct dvb_frontend *fe, fe_status_t *stat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300737 struct dib3000mc_state *state = fe->demodulator_priv;
738 u16 lock = dib3000mc_read_word(state, 509);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739
740 *stat = 0;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300741
742 if (lock & 0x8000)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 *stat |= FE_HAS_SIGNAL;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300744 if (lock & 0x3000)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 *stat |= FE_HAS_CARRIER;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300746 if (lock & 0x0100)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 *stat |= FE_HAS_VITERBI;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300748 if (lock & 0x0010)
749 *stat |= FE_HAS_SYNC;
750 if (lock & 0x0008)
751 *stat |= FE_HAS_LOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
753 return 0;
754}
755
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300756static int dib3000mc_read_ber(struct dvb_frontend *fe, u32 *ber)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300758 struct dib3000mc_state *state = fe->demodulator_priv;
759 *ber = (dib3000mc_read_word(state, 500) << 16) | dib3000mc_read_word(state, 501);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 return 0;
761}
762
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300763static int dib3000mc_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300765 struct dib3000mc_state *state = fe->demodulator_priv;
766 *unc = dib3000mc_read_word(state, 508);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 return 0;
768}
769
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300770static int dib3000mc_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300772 struct dib3000mc_state *state = fe->demodulator_priv;
773 u16 val = dib3000mc_read_word(state, 392);
774 *strength = 65535 - val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 return 0;
776}
777
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr)
779{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300780 *snr = 0x0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 return 0;
782}
783
784static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
785{
Johannes Stezenbach776338e2005-06-23 22:02:35 -0700786 tune->min_delay_ms = 1000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 return 0;
788}
789
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300790static void dib3000mc_release(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300792 struct dib3000mc_state *state = fe->demodulator_priv;
793 dibx000_exit_i2c_master(&state->i2c_master);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 kfree(state);
795}
796
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300797int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300799 struct dib3000mc_state *state = fe->demodulator_priv;
800 dib3000mc_write_word(state, 212 + index, onoff ? (1 << 13) | pid : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 return 0;
802}
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300803EXPORT_SYMBOL(dib3000mc_pid_control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300805int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300807 struct dib3000mc_state *state = fe->demodulator_priv;
808 u16 tmp = dib3000mc_read_word(state, 206) & ~(1 << 4);
809 tmp |= (onoff << 4);
810 return dib3000mc_write_word(state, 206, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811}
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300812EXPORT_SYMBOL(dib3000mc_pid_parse);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300814void dib3000mc_set_config(struct dvb_frontend *fe, struct dib3000mc_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300816 struct dib3000mc_state *state = fe->demodulator_priv;
817 state->cfg = cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818}
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300819EXPORT_SYMBOL(dib3000mc_set_config);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300821int 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 -0700822{
Randy Dunlap0de8e352010-02-08 20:30:33 -0300823 struct dib3000mc_state *dmcst;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300824 int k;
825 u8 new_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300827 static u8 DIB3000MC_I2C_ADDRESS[] = {20,22,24,26};
828
Randy Dunlap0de8e352010-02-08 20:30:33 -0300829 dmcst = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL);
830 if (dmcst == NULL)
Andrew Mortonfebe2ea2010-07-20 19:22:42 -0300831 return -ENOMEM;
Randy Dunlap0de8e352010-02-08 20:30:33 -0300832
833 dmcst->i2c_adap = i2c;
834
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300835 for (k = no_of_demods-1; k >= 0; k--) {
Randy Dunlap0de8e352010-02-08 20:30:33 -0300836 dmcst->cfg = &cfg[k];
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300837
838 /* designated i2c address */
839 new_addr = DIB3000MC_I2C_ADDRESS[k];
Randy Dunlap0de8e352010-02-08 20:30:33 -0300840 dmcst->i2c_addr = new_addr;
841 if (dib3000mc_identify(dmcst) != 0) {
842 dmcst->i2c_addr = default_addr;
843 if (dib3000mc_identify(dmcst) != 0) {
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300844 dprintk("-E- DiB3000P/MC #%d: not identified\n", k);
Randy Dunlap0de8e352010-02-08 20:30:33 -0300845 kfree(dmcst);
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300846 return -ENODEV;
847 }
848 }
849
Randy Dunlap0de8e352010-02-08 20:30:33 -0300850 dib3000mc_set_output_mode(dmcst, OUTMODE_MPEG2_PAR_CONT_CLK);
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300851
852 // set new i2c address and force divstr (Bit 1) to value 0 (Bit 0)
Randy Dunlap0de8e352010-02-08 20:30:33 -0300853 dib3000mc_write_word(dmcst, 1024, (new_addr << 3) | 0x1);
854 dmcst->i2c_addr = new_addr;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300855 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300857 for (k = 0; k < no_of_demods; k++) {
Randy Dunlap0de8e352010-02-08 20:30:33 -0300858 dmcst->cfg = &cfg[k];
859 dmcst->i2c_addr = DIB3000MC_I2C_ADDRESS[k];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
Randy Dunlap0de8e352010-02-08 20:30:33 -0300861 dib3000mc_write_word(dmcst, 1024, dmcst->i2c_addr << 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300863 /* turn off data output */
Randy Dunlap0de8e352010-02-08 20:30:33 -0300864 dib3000mc_set_output_mode(dmcst, OUTMODE_HIGH_Z);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 }
Randy Dunlap0de8e352010-02-08 20:30:33 -0300866
867 kfree(dmcst);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300868 return 0;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300869}
870EXPORT_SYMBOL(dib3000mc_i2c_enumeration);
871
872static struct dvb_frontend_ops dib3000mc_ops;
873
874struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg)
875{
876 struct dvb_frontend *demod;
877 struct dib3000mc_state *st;
878 st = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL);
879 if (st == NULL)
880 return NULL;
881
882 st->cfg = cfg;
883 st->i2c_adap = i2c_adap;
Patrick Boettcher6958eff2006-09-19 12:51:40 -0300884 st->i2c_addr = i2c_addr;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300885
886 demod = &st->demod;
887 demod->demodulator_priv = st;
888 memcpy(&st->demod.ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops));
889
890 if (dib3000mc_identify(st) != 0)
891 goto error;
892
893 dibx000_init_i2c_master(&st->i2c_master, DIB3000MC, st->i2c_adap, st->i2c_addr);
894
Patrick Boettcher303cbea2006-09-19 12:51:56 -0300895 dib3000mc_write_word(st, 1037, 0x3130);
896
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300897 return demod;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
899error:
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300900 kfree(st);
901 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902}
Patrick Boettchere4d6c1f2006-08-08 15:48:09 -0300903EXPORT_SYMBOL(dib3000mc_attach);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904
905static struct dvb_frontend_ops dib3000mc_ops = {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300906 .delsys = { SYS_DVBT },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 .info = {
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300908 .name = "DiBcom 3000MC/P",
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300909 .frequency_min = 44250000,
910 .frequency_max = 867250000,
911 .frequency_stepsize = 62500,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 .caps = FE_CAN_INVERSION_AUTO |
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300913 FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
914 FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
915 FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
916 FE_CAN_TRANSMISSION_MODE_AUTO |
917 FE_CAN_GUARD_INTERVAL_AUTO |
918 FE_CAN_RECOVER |
919 FE_CAN_HIERARCHY_AUTO,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 },
921
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300922 .release = dib3000mc_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300924 .init = dib3000mc_init,
925 .sleep = dib3000mc_sleep,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300927 .set_frontend = dib3000mc_set_frontend,
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300928 .get_tune_settings = dib3000mc_fe_get_tune_settings,
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300929 .get_frontend = dib3000mc_get_frontend,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300931 .read_status = dib3000mc_read_status,
932 .read_ber = dib3000mc_read_ber,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 .read_signal_strength = dib3000mc_read_signal_strength,
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300934 .read_snr = dib3000mc_read_snr,
935 .read_ucblocks = dib3000mc_read_unc_blocks,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936};
937
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300938MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
939MODULE_DESCRIPTION("Driver for the DiBcom 3000MC/P COFDM demodulator");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940MODULE_LICENSE("GPL");