blob: da0f1dc5aaf73ce15a2bb356ccc81760eb20dd1f [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/)
Patrick Boettcher99e44da2016-01-24 12:56:58 -02005 * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
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;
Mauro Carvalho Chehabc7a092e2015-04-28 18:29:05 -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
Mauro Carvalho Chehabc7a092e2015-04-28 18:29:05 -0300144 if (state->cfg->use_pwm3)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300145 dib3000mc_write_word(state, 245, (1 << 3) | (1 << 0));
146 else
147 dib3000mc_write_word(state, 245, 0);
148
Mauro Carvalho Chehabf9e2e0e2015-04-29 10:06:17 -0300149 dib3000mc_write_word(state, 1040, 0x3);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300150 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
Mauro Carvalho Chehabc7a092e2015-04-28 18:29:05 -0300420 return 0;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300421}
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
Mauro Carvalho Chehabc7a092e2015-04-28 18:29:05 -0300450#if 1
451 dib3000mc_write_word(state, 100, (16 << 6) + 9);
452#else
453 if (boost)
454 dib3000mc_write_word(state, 100, (11 << 6) + 6);
455 else
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300456 dib3000mc_write_word(state, 100, (16 << 6) + 9);
Mauro Carvalho Chehabc7a092e2015-04-28 18:29:05 -0300457#endif
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300458
459 dib3000mc_write_word(state, 1027, 0x0800);
460 dib3000mc_write_word(state, 1027, 0x0000);
461
462 //Default cfg isi offset adp
463 dib3000mc_write_word(state, 26, 0x6680);
464 dib3000mc_write_word(state, 29, 0x1273);
465 dib3000mc_write_word(state, 33, 5);
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300466 dib3000mc_set_adp_cfg(state, QAM_16);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300467 dib3000mc_write_word(state, 133, 15564);
468
469 dib3000mc_write_word(state, 12 , 0x0);
470 dib3000mc_write_word(state, 13 , 0x3e8);
471 dib3000mc_write_word(state, 14 , 0x0);
472 dib3000mc_write_word(state, 15 , 0x3f2);
473
474 dib3000mc_write_word(state, 93,0);
475 dib3000mc_write_word(state, 94,0);
476 dib3000mc_write_word(state, 95,0);
477 dib3000mc_write_word(state, 96,0);
478 dib3000mc_write_word(state, 97,0);
479 dib3000mc_write_word(state, 98,0);
480
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300481 dib3000mc_set_impulse_noise(state, 0, ch->transmission_mode);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300482
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300483 value = 0;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300484 switch (ch->transmission_mode) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300485 case TRANSMISSION_MODE_2K: value |= (0 << 7); break;
486 default:
487 case TRANSMISSION_MODE_8K: value |= (1 << 7); break;
488 }
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300489 switch (ch->guard_interval) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300490 case GUARD_INTERVAL_1_32: value |= (0 << 5); break;
491 case GUARD_INTERVAL_1_16: value |= (1 << 5); break;
492 case GUARD_INTERVAL_1_4: value |= (3 << 5); break;
493 default:
494 case GUARD_INTERVAL_1_8: value |= (2 << 5); break;
495 }
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300496 switch (ch->modulation) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300497 case QPSK: value |= (0 << 3); break;
498 case QAM_16: value |= (1 << 3); break;
499 default:
500 case QAM_64: value |= (2 << 3); break;
501 }
502 switch (HIERARCHY_1) {
503 case HIERARCHY_2: value |= 2; break;
504 case HIERARCHY_4: value |= 4; break;
505 default:
506 case HIERARCHY_1: value |= 1; break;
507 }
508 dib3000mc_write_word(state, 0, value);
Mario Rossie3ab2fd2006-12-20 10:54:30 -0300509 dib3000mc_write_word(state, 5, (1 << 8) | ((seq & 0xf) << 4));
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300510
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300511 value = 0;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300512 if (ch->hierarchy == 1)
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300513 value |= (1 << 4);
514 if (1 == 1)
515 value |= 1;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300516 switch ((ch->hierarchy == 0 || 1 == 1) ? ch->code_rate_HP : ch->code_rate_LP) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300517 case FEC_2_3: value |= (2 << 1); break;
518 case FEC_3_4: value |= (3 << 1); break;
519 case FEC_5_6: value |= (5 << 1); break;
520 case FEC_7_8: value |= (7 << 1); break;
521 default:
522 case FEC_1_2: value |= (1 << 1); break;
523 }
524 dib3000mc_write_word(state, 181, value);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300525
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300526 // diversity synchro delay add 50% SFN margin
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300527 switch (ch->transmission_mode) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300528 case TRANSMISSION_MODE_8K: value = 256; break;
529 case TRANSMISSION_MODE_2K:
530 default: value = 64; break;
531 }
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300532 switch (ch->guard_interval) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300533 case GUARD_INTERVAL_1_16: value *= 2; break;
534 case GUARD_INTERVAL_1_8: value *= 4; break;
535 case GUARD_INTERVAL_1_4: value *= 8; break;
536 default:
537 case GUARD_INTERVAL_1_32: value *= 1; break;
538 }
539 value <<= 4;
540 value |= dib3000mc_read_word(state, 180) & 0x000f;
541 dib3000mc_write_word(state, 180, value);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300542
543 // restart demod
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300544 value = dib3000mc_read_word(state, 0);
545 dib3000mc_write_word(state, 0, value | (1 << 9));
546 dib3000mc_write_word(state, 0, value);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300547
548 msleep(30);
549
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300550 dib3000mc_set_impulse_noise(state, state->cfg->impulse_noise_mode, ch->transmission_mode);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300551}
552
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300553static int dib3000mc_autosearch_start(struct dvb_frontend *demod)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300554{
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300555 struct dtv_frontend_properties *chan = &demod->dtv_property_cache;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300556 struct dib3000mc_state *state = demod->demodulator_priv;
557 u16 reg;
558// u32 val;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300559 struct dtv_frontend_properties schan;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300560
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300561 schan = *chan;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300562
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300563 /* TODO what is that ? */
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300564
565 /* a channel for autosearch */
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300566 schan.transmission_mode = TRANSMISSION_MODE_8K;
567 schan.guard_interval = GUARD_INTERVAL_1_32;
568 schan.modulation = QAM_64;
569 schan.code_rate_HP = FEC_2_3;
570 schan.code_rate_LP = FEC_2_3;
571 schan.hierarchy = 0;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300572
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300573 dib3000mc_set_channel_cfg(state, &schan, 11);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300574
575 reg = dib3000mc_read_word(state, 0);
576 dib3000mc_write_word(state, 0, reg | (1 << 8));
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300577 dib3000mc_read_word(state, 511);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300578 dib3000mc_write_word(state, 0, reg);
579
580 return 0;
581}
582
583static int dib3000mc_autosearch_is_irq(struct dvb_frontend *demod)
584{
585 struct dib3000mc_state *state = demod->demodulator_priv;
586 u16 irq_pending = dib3000mc_read_word(state, 511);
587
588 if (irq_pending & 0x1) // failed
589 return 1;
590
591 if (irq_pending & 0x2) // succeeded
592 return 2;
593
594 return 0; // still pending
595}
596
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300597static int dib3000mc_tune(struct dvb_frontend *demod)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300598{
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300599 struct dtv_frontend_properties *ch = &demod->dtv_property_cache;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300600 struct dib3000mc_state *state = demod->demodulator_priv;
601
602 // ** configure demod **
603 dib3000mc_set_channel_cfg(state, ch, 0);
604
605 // activates isi
Matt Doran8f6956c2007-07-31 07:09:30 -0300606 if (state->sfn_workaround_active) {
607 dprintk("SFN workaround is active\n");
608 dib3000mc_write_word(state, 29, 0x1273);
609 dib3000mc_write_word(state, 108, 0x4000); // P_pha3_force_pha_shift
610 } else {
611 dib3000mc_write_word(state, 29, 0x1073);
612 dib3000mc_write_word(state, 108, 0x0000); // P_pha3_force_pha_shift
613 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300614
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300615 dib3000mc_set_adp_cfg(state, (u8)ch->modulation);
616 if (ch->transmission_mode == TRANSMISSION_MODE_8K) {
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300617 dib3000mc_write_word(state, 26, 38528);
618 dib3000mc_write_word(state, 33, 8);
619 } else {
620 dib3000mc_write_word(state, 26, 30336);
621 dib3000mc_write_word(state, 33, 6);
622 }
623
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300624 if (dib3000mc_read_word(state, 509) & 0x80)
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300625 dib3000mc_set_timing(state, ch->transmission_mode,
626 BANDWIDTH_TO_KHZ(ch->bandwidth_hz), 1);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300627
628 return 0;
629}
630
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300631struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating)
632{
633 struct dib3000mc_state *st = demod->demodulator_priv;
634 return dibx000_get_i2c_adapter(&st->i2c_master, DIBX000_I2C_INTERFACE_TUNER, gating);
635}
636
637EXPORT_SYMBOL(dib3000mc_get_tuner_i2c_master);
638
Mauro Carvalho Chehab7e3e68b2016-02-04 12:58:30 -0200639static int dib3000mc_get_frontend(struct dvb_frontend* fe,
640 struct dtv_frontend_properties *fep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300642 struct dib3000mc_state *state = fe->demodulator_priv;
643 u16 tps = dib3000mc_read_word(state,458);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300645 fep->inversion = INVERSION_AUTO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300647 fep->bandwidth_hz = state->current_bandwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300649 switch ((tps >> 8) & 0x1) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300650 case 0: fep->transmission_mode = TRANSMISSION_MODE_2K; break;
651 case 1: fep->transmission_mode = TRANSMISSION_MODE_8K; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 }
653
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300654 switch (tps & 0x3) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300655 case 0: fep->guard_interval = GUARD_INTERVAL_1_32; break;
656 case 1: fep->guard_interval = GUARD_INTERVAL_1_16; break;
657 case 2: fep->guard_interval = GUARD_INTERVAL_1_8; break;
658 case 3: fep->guard_interval = GUARD_INTERVAL_1_4; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 }
660
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300661 switch ((tps >> 13) & 0x3) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300662 case 0: fep->modulation = QPSK; break;
663 case 1: fep->modulation = QAM_16; break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300664 case 2:
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300665 default: fep->modulation = QAM_64; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 }
667
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300668 /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */
669 /* (tps >> 12) & 0x1 == hrch is used, (tps >> 9) & 0x7 == alpha */
670
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300671 fep->hierarchy = HIERARCHY_NONE;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300672 switch ((tps >> 5) & 0x7) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300673 case 1: fep->code_rate_HP = FEC_1_2; break;
674 case 2: fep->code_rate_HP = FEC_2_3; break;
675 case 3: fep->code_rate_HP = FEC_3_4; break;
676 case 5: fep->code_rate_HP = FEC_5_6; break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300677 case 7:
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300678 default: fep->code_rate_HP = FEC_7_8; break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300679
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 }
681
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300682 switch ((tps >> 2) & 0x7) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300683 case 1: fep->code_rate_LP = FEC_1_2; break;
684 case 2: fep->code_rate_LP = FEC_2_3; break;
685 case 3: fep->code_rate_LP = FEC_3_4; break;
686 case 5: fep->code_rate_LP = FEC_5_6; break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300687 case 7:
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300688 default: fep->code_rate_LP = FEC_7_8; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
691 return 0;
692}
693
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300694static int dib3000mc_set_frontend(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695{
Mauro Carvalho Chehab7c61d802011-12-30 11:30:21 -0300696 struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300697 struct dib3000mc_state *state = fe->demodulator_priv;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300698 int ret;
Soeren Moch853ea132008-01-25 06:27:06 -0300699
700 dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300702 state->current_bandwidth = fep->bandwidth_hz;
703 dib3000mc_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->bandwidth_hz));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
Matt Doran8f6956c2007-07-31 07:09:30 -0300705 /* maybe the parameter has been changed */
706 state->sfn_workaround_active = buggy_sfn_workaround;
707
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300708 if (fe->ops.tuner_ops.set_params) {
Mauro Carvalho Chehab14d24d12011-12-24 12:24:33 -0300709 fe->ops.tuner_ops.set_params(fe);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300710 msleep(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300712
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300713 if (fep->transmission_mode == TRANSMISSION_MODE_AUTO ||
714 fep->guard_interval == GUARD_INTERVAL_AUTO ||
715 fep->modulation == QAM_AUTO ||
716 fep->code_rate_HP == FEC_AUTO) {
Jose Alberto Reguero3a0311c2008-01-25 06:05:16 -0300717 int i = 1000, found;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300718
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300719 dib3000mc_autosearch_start(fe);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300720 do {
721 msleep(1);
722 found = dib3000mc_autosearch_is_irq(fe);
723 } while (found == 0 && i--);
724
725 dprintk("autosearch returns: %d\n",found);
726 if (found == 0 || found == 1)
727 return 0; // no channel found
728
Mauro Carvalho Chehab7e3e68b2016-02-04 12:58:30 -0200729 dib3000mc_get_frontend(fe, fep);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300730 }
731
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300732 ret = dib3000mc_tune(fe);
Soeren Moch853ea132008-01-25 06:27:06 -0300733
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300734 /* make this a config parameter */
735 dib3000mc_set_output_mode(state, OUTMODE_MPEG2_FIFO);
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300736 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737}
738
Mauro Carvalho Chehab0df289a2015-06-07 14:53:52 -0300739static int dib3000mc_read_status(struct dvb_frontend *fe, enum fe_status *stat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300741 struct dib3000mc_state *state = fe->demodulator_priv;
742 u16 lock = dib3000mc_read_word(state, 509);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
744 *stat = 0;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300745
746 if (lock & 0x8000)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 *stat |= FE_HAS_SIGNAL;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300748 if (lock & 0x3000)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 *stat |= FE_HAS_CARRIER;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300750 if (lock & 0x0100)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 *stat |= FE_HAS_VITERBI;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300752 if (lock & 0x0010)
753 *stat |= FE_HAS_SYNC;
754 if (lock & 0x0008)
755 *stat |= FE_HAS_LOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
757 return 0;
758}
759
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300760static int dib3000mc_read_ber(struct dvb_frontend *fe, u32 *ber)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300762 struct dib3000mc_state *state = fe->demodulator_priv;
763 *ber = (dib3000mc_read_word(state, 500) << 16) | dib3000mc_read_word(state, 501);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 return 0;
765}
766
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300767static int dib3000mc_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300769 struct dib3000mc_state *state = fe->demodulator_priv;
770 *unc = dib3000mc_read_word(state, 508);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 return 0;
772}
773
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300774static int dib3000mc_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300776 struct dib3000mc_state *state = fe->demodulator_priv;
777 u16 val = dib3000mc_read_word(state, 392);
778 *strength = 65535 - val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 return 0;
780}
781
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr)
783{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300784 *snr = 0x0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 return 0;
786}
787
788static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
789{
Johannes Stezenbach776338e2005-06-23 22:02:35 -0700790 tune->min_delay_ms = 1000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 return 0;
792}
793
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300794static void dib3000mc_release(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300796 struct dib3000mc_state *state = fe->demodulator_priv;
797 dibx000_exit_i2c_master(&state->i2c_master);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 kfree(state);
799}
800
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300801int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300803 struct dib3000mc_state *state = fe->demodulator_priv;
804 dib3000mc_write_word(state, 212 + index, onoff ? (1 << 13) | pid : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 return 0;
806}
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300807EXPORT_SYMBOL(dib3000mc_pid_control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300809int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300811 struct dib3000mc_state *state = fe->demodulator_priv;
812 u16 tmp = dib3000mc_read_word(state, 206) & ~(1 << 4);
813 tmp |= (onoff << 4);
814 return dib3000mc_write_word(state, 206, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815}
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300816EXPORT_SYMBOL(dib3000mc_pid_parse);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300818void dib3000mc_set_config(struct dvb_frontend *fe, struct dib3000mc_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300820 struct dib3000mc_state *state = fe->demodulator_priv;
821 state->cfg = cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822}
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300823EXPORT_SYMBOL(dib3000mc_set_config);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300825int 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 -0700826{
Randy Dunlap0de8e352010-02-08 20:30:33 -0300827 struct dib3000mc_state *dmcst;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300828 int k;
829 u8 new_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300831 static u8 DIB3000MC_I2C_ADDRESS[] = {20,22,24,26};
832
Randy Dunlap0de8e352010-02-08 20:30:33 -0300833 dmcst = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL);
834 if (dmcst == NULL)
Andrew Mortonfebe2ea2010-07-20 19:22:42 -0300835 return -ENOMEM;
Randy Dunlap0de8e352010-02-08 20:30:33 -0300836
837 dmcst->i2c_adap = i2c;
838
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300839 for (k = no_of_demods-1; k >= 0; k--) {
Randy Dunlap0de8e352010-02-08 20:30:33 -0300840 dmcst->cfg = &cfg[k];
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300841
842 /* designated i2c address */
843 new_addr = DIB3000MC_I2C_ADDRESS[k];
Randy Dunlap0de8e352010-02-08 20:30:33 -0300844 dmcst->i2c_addr = new_addr;
845 if (dib3000mc_identify(dmcst) != 0) {
846 dmcst->i2c_addr = default_addr;
847 if (dib3000mc_identify(dmcst) != 0) {
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300848 dprintk("-E- DiB3000P/MC #%d: not identified\n", k);
Randy Dunlap0de8e352010-02-08 20:30:33 -0300849 kfree(dmcst);
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300850 return -ENODEV;
851 }
852 }
853
Randy Dunlap0de8e352010-02-08 20:30:33 -0300854 dib3000mc_set_output_mode(dmcst, OUTMODE_MPEG2_PAR_CONT_CLK);
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300855
856 // set new i2c address and force divstr (Bit 1) to value 0 (Bit 0)
Randy Dunlap0de8e352010-02-08 20:30:33 -0300857 dib3000mc_write_word(dmcst, 1024, (new_addr << 3) | 0x1);
858 dmcst->i2c_addr = new_addr;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300859 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300861 for (k = 0; k < no_of_demods; k++) {
Randy Dunlap0de8e352010-02-08 20:30:33 -0300862 dmcst->cfg = &cfg[k];
863 dmcst->i2c_addr = DIB3000MC_I2C_ADDRESS[k];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
Randy Dunlap0de8e352010-02-08 20:30:33 -0300865 dib3000mc_write_word(dmcst, 1024, dmcst->i2c_addr << 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300867 /* turn off data output */
Randy Dunlap0de8e352010-02-08 20:30:33 -0300868 dib3000mc_set_output_mode(dmcst, OUTMODE_HIGH_Z);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 }
Randy Dunlap0de8e352010-02-08 20:30:33 -0300870
871 kfree(dmcst);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300872 return 0;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300873}
874EXPORT_SYMBOL(dib3000mc_i2c_enumeration);
875
876static struct dvb_frontend_ops dib3000mc_ops;
877
878struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg)
879{
880 struct dvb_frontend *demod;
881 struct dib3000mc_state *st;
882 st = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL);
883 if (st == NULL)
884 return NULL;
885
886 st->cfg = cfg;
887 st->i2c_adap = i2c_adap;
Patrick Boettcher6958eff2006-09-19 12:51:40 -0300888 st->i2c_addr = i2c_addr;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300889
890 demod = &st->demod;
891 demod->demodulator_priv = st;
892 memcpy(&st->demod.ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops));
893
894 if (dib3000mc_identify(st) != 0)
895 goto error;
896
897 dibx000_init_i2c_master(&st->i2c_master, DIB3000MC, st->i2c_adap, st->i2c_addr);
898
Patrick Boettcher303cbea2006-09-19 12:51:56 -0300899 dib3000mc_write_word(st, 1037, 0x3130);
900
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300901 return demod;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
903error:
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300904 kfree(st);
905 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906}
Patrick Boettchere4d6c1f2006-08-08 15:48:09 -0300907EXPORT_SYMBOL(dib3000mc_attach);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908
909static struct dvb_frontend_ops dib3000mc_ops = {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300910 .delsys = { SYS_DVBT },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 .info = {
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300912 .name = "DiBcom 3000MC/P",
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300913 .frequency_min = 44250000,
914 .frequency_max = 867250000,
915 .frequency_stepsize = 62500,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 .caps = FE_CAN_INVERSION_AUTO |
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300917 FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
918 FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
919 FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
920 FE_CAN_TRANSMISSION_MODE_AUTO |
921 FE_CAN_GUARD_INTERVAL_AUTO |
922 FE_CAN_RECOVER |
923 FE_CAN_HIERARCHY_AUTO,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 },
925
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300926 .release = dib3000mc_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300928 .init = dib3000mc_init,
929 .sleep = dib3000mc_sleep,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300931 .set_frontend = dib3000mc_set_frontend,
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300932 .get_tune_settings = dib3000mc_fe_get_tune_settings,
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300933 .get_frontend = dib3000mc_get_frontend,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300935 .read_status = dib3000mc_read_status,
936 .read_ber = dib3000mc_read_ber,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 .read_signal_strength = dib3000mc_read_signal_strength,
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300938 .read_snr = dib3000mc_read_snr,
939 .read_ucblocks = dib3000mc_read_unc_blocks,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940};
941
Patrick Boettcher99e44da2016-01-24 12:56:58 -0200942MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300943MODULE_DESCRIPTION("Driver for the DiBcom 3000MC/P COFDM demodulator");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944MODULE_LICENSE("GPL");