blob: c9e1db2517232e31d487162326331029de478794 [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
Mauro Carvalho Chehab441d54e2016-10-14 08:55:41 -030014#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/kernel.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090017#include <linux/slab.h>
Patrick Boettcherb7571f82006-08-08 15:48:10 -030018#include <linux/i2c.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019
Mauro Carvalho Chehabfada1932017-12-28 13:03:51 -050020#include <media/dvb_frontend.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021
Patrick Boettcherb7571f82006-08-08 15:48:10 -030022#include "dib3000mc.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070023
Linus Torvalds1da177e2005-04-16 15:20:36 -070024static int debug;
25module_param(debug, int, 0644);
Patrick Boettcherb7571f82006-08-08 15:48:10 -030026MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
Linus Torvalds1da177e2005-04-16 15:20:36 -070027
Matt Doran8f6956c2007-07-31 07:09:30 -030028static int buggy_sfn_workaround;
29module_param(buggy_sfn_workaround, int, 0644);
Patrick Boettcher8d999962007-07-31 10:36:06 -030030MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (default: 0)");
Matt Doran8f6956c2007-07-31 07:09:30 -030031
Mauro Carvalho Chehab441d54e2016-10-14 08:55:41 -030032#define dprintk(fmt, arg...) do { \
33 if (debug) \
34 printk(KERN_DEBUG pr_fmt("%s: " fmt), \
35 __func__, ##arg); \
36} while (0)
Patrick Boettcherb7571f82006-08-08 15:48:10 -030037
38struct dib3000mc_state {
39 struct dvb_frontend demod;
40 struct dib3000mc_config *cfg;
41
42 u8 i2c_addr;
43 struct i2c_adapter *i2c_adap;
44
45 struct dibx000_i2c_master i2c_master;
46
Patrick Boettcher01b4bf32006-09-19 12:51:53 -030047 u32 timf;
48
Mauro Carvalho Chehab88ab8982011-12-26 20:01:24 -030049 u32 current_bandwidth;
Patrick Boettcherb7571f82006-08-08 15:48:10 -030050
51 u16 dev_id;
Matt Doran8f6956c2007-07-31 07:09:30 -030052
53 u8 sfn_workaround_active :1;
Patrick Boettcherb7571f82006-08-08 15:48:10 -030054};
55
56static u16 dib3000mc_read_word(struct dib3000mc_state *state, u16 reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -070057{
Patrick Boettcherb7571f82006-08-08 15:48:10 -030058 struct i2c_msg msg[2] = {
Sean Youngb4756702017-09-02 07:42:42 -040059 { .addr = state->i2c_addr >> 1, .flags = 0, .len = 2 },
60 { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .len = 2 },
Patrick Boettcherb7571f82006-08-08 15:48:10 -030061 };
Sean Youngb4756702017-09-02 07:42:42 -040062 u16 word;
63 u8 *b;
64
65 b = kmalloc(4, GFP_KERNEL);
66 if (!b)
67 return 0;
68
69 b[0] = (reg >> 8) | 0x80;
70 b[1] = reg;
71 b[2] = 0;
72 b[3] = 0;
73
74 msg[0].buf = b;
75 msg[1].buf = b + 2;
Patrick Boettcherb7571f82006-08-08 15:48:10 -030076
77 if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
78 dprintk("i2c read error on %d\n",reg);
79
Sean Youngb4756702017-09-02 07:42:42 -040080 word = (b[2] << 8) | b[3];
81 kfree(b);
82
83 return word;
Patrick Boettcherb7571f82006-08-08 15:48:10 -030084}
85
86static int dib3000mc_write_word(struct dib3000mc_state *state, u16 reg, u16 val)
87{
Patrick Boettcherb7571f82006-08-08 15:48:10 -030088 struct i2c_msg msg = {
Sean Youngb4756702017-09-02 07:42:42 -040089 .addr = state->i2c_addr >> 1, .flags = 0, .len = 4
Patrick Boettcherb7571f82006-08-08 15:48:10 -030090 };
Sean Youngb4756702017-09-02 07:42:42 -040091 int rc;
92 u8 *b;
93
94 b = kmalloc(4, GFP_KERNEL);
95 if (!b)
96 return -ENOMEM;
97
98 b[0] = reg >> 8;
99 b[1] = reg;
100 b[2] = val >> 8;
101 b[3] = val;
102
103 msg.buf = b;
104
105 rc = i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
106 kfree(b);
107
108 return rc;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300109}
110
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300111static int dib3000mc_identify(struct dib3000mc_state *state)
112{
113 u16 value;
114 if ((value = dib3000mc_read_word(state, 1025)) != 0x01b3) {
115 dprintk("-E- DiB3000MC/P: wrong Vendor ID (read=0x%x)\n",value);
116 return -EREMOTEIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 }
118
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300119 value = dib3000mc_read_word(state, 1026);
120 if (value != 0x3001 && value != 0x3002) {
121 dprintk("-E- DiB3000MC/P: wrong Device ID (%x)\n",value);
122 return -EREMOTEIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300124 state->dev_id = value;
125
126 dprintk("-I- found DiB3000MC/P: %x\n",state->dev_id);
127
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 return 0;
129}
130
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300131static int dib3000mc_set_timing(struct dib3000mc_state *state, s16 nfft, u32 bw, u8 update_offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132{
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300133 u32 timf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300135 if (state->timf == 0) {
136 timf = 1384402; // default value for 8MHz
137 if (update_offset)
138 msleep(200); // first time we do an update
139 } else
140 timf = state->timf;
141
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300142 timf *= (bw / 1000);
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300143
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300144 if (update_offset) {
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300145 s16 tim_offs = dib3000mc_read_word(state, 416);
146
147 if (tim_offs & 0x2000)
148 tim_offs -= 0x4000;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300149
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300150 if (nfft == TRANSMISSION_MODE_2K)
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300151 tim_offs *= 4;
152
153 timf += tim_offs;
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300154 state->timf = timf / (bw / 1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300156
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300157 dprintk("timf: %d\n", timf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300159 dib3000mc_write_word(state, 23, (u16) (timf >> 16));
160 dib3000mc_write_word(state, 24, (u16) (timf ) & 0xffff);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300161
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 return 0;
163}
164
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300165static int dib3000mc_setup_pwm_state(struct dib3000mc_state *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166{
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300167 u16 reg_51, reg_52 = state->cfg->agc->setup & 0xfefb;
Mauro Carvalho Chehabc7a092e2015-04-28 18:29:05 -0300168 if (state->cfg->pwm3_inversion) {
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300169 reg_51 = (2 << 14) | (0 << 10) | (7 << 6) | (2 << 2) | (2 << 0);
170 reg_52 |= (1 << 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 } else {
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300172 reg_51 = (2 << 14) | (4 << 10) | (7 << 6) | (2 << 2) | (2 << 0);
173 reg_52 |= (1 << 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 }
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300175 dib3000mc_write_word(state, 51, reg_51);
176 dib3000mc_write_word(state, 52, reg_52);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300177
Mauro Carvalho Chehabc7a092e2015-04-28 18:29:05 -0300178 if (state->cfg->use_pwm3)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300179 dib3000mc_write_word(state, 245, (1 << 3) | (1 << 0));
180 else
181 dib3000mc_write_word(state, 245, 0);
182
Mauro Carvalho Chehabf9e2e0e2015-04-29 10:06:17 -0300183 dib3000mc_write_word(state, 1040, 0x3);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300184 return 0;
185}
186
187static int dib3000mc_set_output_mode(struct dib3000mc_state *state, int mode)
188{
189 int ret = 0;
190 u16 fifo_threshold = 1792;
191 u16 outreg = 0;
192 u16 outmode = 0;
193 u16 elecout = 1;
Patrick Boettcherfb6065b2006-08-21 08:21:52 -0300194 u16 smo_reg = dib3000mc_read_word(state, 206) & 0x0010; /* keep the pid_parse bit */
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300195
196 dprintk("-I- Setting output mode for demod %p to %d\n",
197 &state->demod, mode);
198
199 switch (mode) {
200 case OUTMODE_HIGH_Z: // disable
201 elecout = 0;
202 break;
203 case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock
204 outmode = 0;
205 break;
206 case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock
207 outmode = 1;
208 break;
209 case OUTMODE_MPEG2_SERIAL: // STBs with serial input
210 outmode = 2;
211 break;
212 case OUTMODE_MPEG2_FIFO: // e.g. USB feeding
213 elecout = 3;
214 /*ADDR @ 206 :
215 P_smo_error_discard [1;6:6] = 0
216 P_smo_rs_discard [1;5:5] = 0
217 P_smo_pid_parse [1;4:4] = 0
218 P_smo_fifo_flush [1;3:3] = 0
219 P_smo_mode [2;2:1] = 11
220 P_smo_ovf_prot [1;0:0] = 0
221 */
Patrick Boettcherfb6065b2006-08-21 08:21:52 -0300222 smo_reg |= 3 << 1;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300223 fifo_threshold = 512;
224 outmode = 5;
225 break;
226 case OUTMODE_DIVERSITY:
227 outmode = 4;
228 elecout = 1;
229 break;
230 default:
231 dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod);
232 outmode = 0;
233 break;
234 }
235
236 if ((state->cfg->output_mpeg2_in_188_bytes))
Patrick Boettcher559463b2006-08-19 16:13:53 -0300237 smo_reg |= (1 << 5); // P_smo_rs_discard [1;5:5] = 1
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300238
239 outreg = dib3000mc_read_word(state, 244) & 0x07FF;
240 outreg |= (outmode << 11);
241 ret |= dib3000mc_write_word(state, 244, outreg);
242 ret |= dib3000mc_write_word(state, 206, smo_reg); /*smo_ mode*/
243 ret |= dib3000mc_write_word(state, 207, fifo_threshold); /* synchronous fread */
244 ret |= dib3000mc_write_word(state, 1040, elecout); /* P_out_cfg */
245 return ret;
246}
247
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300248static int dib3000mc_set_bandwidth(struct dib3000mc_state *state, u32 bw)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300249{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300250 u16 bw_cfg[6] = { 0 };
251 u16 imp_bw_cfg[3] = { 0 };
252 u16 reg;
253
254/* settings here are for 27.7MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 switch (bw) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300256 case 8000:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300257 bw_cfg[0] = 0x0019; bw_cfg[1] = 0x5c30; bw_cfg[2] = 0x0054; bw_cfg[3] = 0x88a0; bw_cfg[4] = 0x01a6; bw_cfg[5] = 0xab20;
258 imp_bw_cfg[0] = 0x04db; imp_bw_cfg[1] = 0x00db; imp_bw_cfg[2] = 0x00b7;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300260
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300261 case 7000:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300262 bw_cfg[0] = 0x001c; bw_cfg[1] = 0xfba5; bw_cfg[2] = 0x0060; bw_cfg[3] = 0x9c25; bw_cfg[4] = 0x01e3; bw_cfg[5] = 0x0cb7;
263 imp_bw_cfg[0] = 0x04c0; imp_bw_cfg[1] = 0x00c0; imp_bw_cfg[2] = 0x00a0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300265
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300266 case 6000:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300267 bw_cfg[0] = 0x0021; bw_cfg[1] = 0xd040; bw_cfg[2] = 0x0070; bw_cfg[3] = 0xb62b; bw_cfg[4] = 0x0233; bw_cfg[5] = 0x8ed5;
268 imp_bw_cfg[0] = 0x04a5; imp_bw_cfg[1] = 0x00a5; imp_bw_cfg[2] = 0x0089;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300270
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300271 case 5000:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300272 bw_cfg[0] = 0x0028; bw_cfg[1] = 0x9380; bw_cfg[2] = 0x0087; bw_cfg[3] = 0x4100; bw_cfg[4] = 0x02a4; bw_cfg[5] = 0x4500;
273 imp_bw_cfg[0] = 0x0489; imp_bw_cfg[1] = 0x0089; imp_bw_cfg[2] = 0x0072;
274 break;
275
276 default: return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300278
279 for (reg = 6; reg < 12; reg++)
280 dib3000mc_write_word(state, reg, bw_cfg[reg - 6]);
281 dib3000mc_write_word(state, 12, 0x0000);
282 dib3000mc_write_word(state, 13, 0x03e8);
283 dib3000mc_write_word(state, 14, 0x0000);
284 dib3000mc_write_word(state, 15, 0x03f2);
285 dib3000mc_write_word(state, 16, 0x0001);
286 dib3000mc_write_word(state, 17, 0xb0d0);
287 // P_sec_len
288 dib3000mc_write_word(state, 18, 0x0393);
289 dib3000mc_write_word(state, 19, 0x8700);
290
291 for (reg = 55; reg < 58; reg++)
292 dib3000mc_write_word(state, reg, imp_bw_cfg[reg - 55]);
293
294 // Timing configuration
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300295 dib3000mc_set_timing(state, TRANSMISSION_MODE_2K, bw, 0);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300296
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 return 0;
298}
299
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300300static u16 impulse_noise_val[29] =
301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300303 0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c, 0x3ffe, 0x7f3,
304 0x2d94, 0x76, 0x53d, 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3, 0x3feb, 0x7d2,
305 0x365e, 0x76, 0x48c, 0x3ffe, 0x5b3, 0x3feb, 0x76, 0x0000, 0xd
306};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300308static void dib3000mc_set_impulse_noise(struct dib3000mc_state *state, u8 mode, s16 nfft)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300310 u16 i;
311 for (i = 58; i < 87; i++)
312 dib3000mc_write_word(state, i, impulse_noise_val[i-58]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300314 if (nfft == TRANSMISSION_MODE_8K) {
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300315 dib3000mc_write_word(state, 58, 0x3b);
316 dib3000mc_write_word(state, 84, 0x00);
317 dib3000mc_write_word(state, 85, 0x8200);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 }
319
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300320 dib3000mc_write_word(state, 34, 0x1294);
321 dib3000mc_write_word(state, 35, 0x1ff8);
322 if (mode == 1)
323 dib3000mc_write_word(state, 55, dib3000mc_read_word(state, 55) | (1 << 10));
324}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300326static int dib3000mc_init(struct dvb_frontend *demod)
327{
328 struct dib3000mc_state *state = demod->demodulator_priv;
329 struct dibx000_agc_config *agc = state->cfg->agc;
330
331 // Restart Configuration
332 dib3000mc_write_word(state, 1027, 0x8000);
333 dib3000mc_write_word(state, 1027, 0x0000);
334
335 // power up the demod + mobility configuration
336 dib3000mc_write_word(state, 140, 0x0000);
337 dib3000mc_write_word(state, 1031, 0);
338
339 if (state->cfg->mobile_mode) {
340 dib3000mc_write_word(state, 139, 0x0000);
341 dib3000mc_write_word(state, 141, 0x0000);
342 dib3000mc_write_word(state, 175, 0x0002);
343 dib3000mc_write_word(state, 1032, 0x0000);
344 } else {
345 dib3000mc_write_word(state, 139, 0x0001);
346 dib3000mc_write_word(state, 141, 0x0000);
347 dib3000mc_write_word(state, 175, 0x0000);
348 dib3000mc_write_word(state, 1032, 0x012C);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 }
Patrick Boettcher303cbea2006-09-19 12:51:56 -0300350 dib3000mc_write_word(state, 1033, 0x0000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300352 // P_clk_cfg
Patrick Boettcher303cbea2006-09-19 12:51:56 -0300353 dib3000mc_write_word(state, 1037, 0x3130);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300355 // other configurations
356
357 // P_ctrl_sfreq
358 dib3000mc_write_word(state, 33, (5 << 0));
359 dib3000mc_write_word(state, 88, (1 << 10) | (0x10 << 0));
360
361 // Phase noise control
362 // P_fft_phacor_inh, P_fft_phacor_cpe, P_fft_powrange
363 dib3000mc_write_word(state, 99, (1 << 9) | (0x20 << 0));
364
365 if (state->cfg->phase_noise_mode == 0)
366 dib3000mc_write_word(state, 111, 0x00);
367 else
368 dib3000mc_write_word(state, 111, 0x02);
369
370 // P_agc_global
371 dib3000mc_write_word(state, 50, 0x8000);
372
373 // agc setup misc
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300374 dib3000mc_setup_pwm_state(state);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300375
376 // P_agc_counter_lock
377 dib3000mc_write_word(state, 53, 0x87);
378 // P_agc_counter_unlock
379 dib3000mc_write_word(state, 54, 0x87);
380
381 /* agc */
382 dib3000mc_write_word(state, 36, state->cfg->max_time);
Patrick Boettcher5570dd02006-10-13 11:35:12 -0300383 dib3000mc_write_word(state, 37, (state->cfg->agc_command1 << 13) | (state->cfg->agc_command2 << 12) | (0x1d << 0));
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300384 dib3000mc_write_word(state, 38, state->cfg->pwm3_value);
385 dib3000mc_write_word(state, 39, state->cfg->ln_adc_level);
386
387 // set_agc_loop_Bw
388 dib3000mc_write_word(state, 40, 0x0179);
389 dib3000mc_write_word(state, 41, 0x03f0);
390
391 dib3000mc_write_word(state, 42, agc->agc1_max);
392 dib3000mc_write_word(state, 43, agc->agc1_min);
393 dib3000mc_write_word(state, 44, agc->agc2_max);
394 dib3000mc_write_word(state, 45, agc->agc2_min);
395 dib3000mc_write_word(state, 46, (agc->agc1_pt1 << 8) | agc->agc1_pt2);
396 dib3000mc_write_word(state, 47, (agc->agc1_slope1 << 8) | agc->agc1_slope2);
397 dib3000mc_write_word(state, 48, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
398 dib3000mc_write_word(state, 49, (agc->agc2_slope1 << 8) | agc->agc2_slope2);
399
400// Begin: TimeOut registers
401 // P_pha3_thres
402 dib3000mc_write_word(state, 110, 3277);
403 // P_timf_alpha = 6, P_corm_alpha = 6, P_corm_thres = 0x80
404 dib3000mc_write_word(state, 26, 0x6680);
405 // lock_mask0
406 dib3000mc_write_word(state, 1, 4);
407 // lock_mask1
408 dib3000mc_write_word(state, 2, 4);
409 // lock_mask2
410 dib3000mc_write_word(state, 3, 0x1000);
411 // P_search_maxtrial=1
412 dib3000mc_write_word(state, 5, 1);
413
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300414 dib3000mc_set_bandwidth(state, 8000);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300415
416 // div_lock_mask
417 dib3000mc_write_word(state, 4, 0x814);
418
419 dib3000mc_write_word(state, 21, (1 << 9) | 0x164);
420 dib3000mc_write_word(state, 22, 0x463d);
421
422 // Spurious rm cfg
423 // P_cspu_regul, P_cspu_win_cut
424 dib3000mc_write_word(state, 120, 0x200f);
425 // P_adp_selec_monit
426 dib3000mc_write_word(state, 134, 0);
427
428 // Fec cfg
429 dib3000mc_write_word(state, 195, 0x10);
430
431 // diversity register: P_dvsy_sync_wait..
432 dib3000mc_write_word(state, 180, 0x2FF0);
433
434 // Impulse noise configuration
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300435 dib3000mc_set_impulse_noise(state, 0, TRANSMISSION_MODE_8K);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300436
437 // output mode set-up
438 dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z);
439
440 /* close the i2c-gate */
441 dib3000mc_write_word(state, 769, (1 << 7) );
442
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 return 0;
444}
445
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300446static int dib3000mc_sleep(struct dvb_frontend *demod)
447{
448 struct dib3000mc_state *state = demod->demodulator_priv;
449
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300450 dib3000mc_write_word(state, 1031, 0xFFFF);
451 dib3000mc_write_word(state, 1032, 0xFFFF);
Patrick Boettcher303cbea2006-09-19 12:51:56 -0300452 dib3000mc_write_word(state, 1033, 0xFFF0);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300453
Mauro Carvalho Chehabc7a092e2015-04-28 18:29:05 -0300454 return 0;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300455}
456
457static void dib3000mc_set_adp_cfg(struct dib3000mc_state *state, s16 qam)
458{
459 u16 cfg[4] = { 0 },reg;
460 switch (qam) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300461 case QPSK:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300462 cfg[0] = 0x099a; cfg[1] = 0x7fae; cfg[2] = 0x0333; cfg[3] = 0x7ff0;
463 break;
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300464 case QAM_16:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300465 cfg[0] = 0x023d; cfg[1] = 0x7fdf; cfg[2] = 0x00a4; cfg[3] = 0x7ff0;
466 break;
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300467 case QAM_64:
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300468 cfg[0] = 0x0148; cfg[1] = 0x7ff0; cfg[2] = 0x00a4; cfg[3] = 0x7ff8;
469 break;
470 }
471 for (reg = 129; reg < 133; reg++)
472 dib3000mc_write_word(state, reg, cfg[reg - 129]);
473}
474
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300475static void dib3000mc_set_channel_cfg(struct dib3000mc_state *state,
476 struct dtv_frontend_properties *ch, u16 seq)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300477{
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300478 u16 value;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300479 u32 bw = BANDWIDTH_TO_KHZ(ch->bandwidth_hz);
480
481 dib3000mc_set_bandwidth(state, bw);
482 dib3000mc_set_timing(state, ch->transmission_mode, bw, 0);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300483
Mauro Carvalho Chehabc7a092e2015-04-28 18:29:05 -0300484#if 1
485 dib3000mc_write_word(state, 100, (16 << 6) + 9);
486#else
487 if (boost)
488 dib3000mc_write_word(state, 100, (11 << 6) + 6);
489 else
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300490 dib3000mc_write_word(state, 100, (16 << 6) + 9);
Mauro Carvalho Chehabc7a092e2015-04-28 18:29:05 -0300491#endif
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300492
493 dib3000mc_write_word(state, 1027, 0x0800);
494 dib3000mc_write_word(state, 1027, 0x0000);
495
496 //Default cfg isi offset adp
497 dib3000mc_write_word(state, 26, 0x6680);
498 dib3000mc_write_word(state, 29, 0x1273);
499 dib3000mc_write_word(state, 33, 5);
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300500 dib3000mc_set_adp_cfg(state, QAM_16);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300501 dib3000mc_write_word(state, 133, 15564);
502
503 dib3000mc_write_word(state, 12 , 0x0);
504 dib3000mc_write_word(state, 13 , 0x3e8);
505 dib3000mc_write_word(state, 14 , 0x0);
506 dib3000mc_write_word(state, 15 , 0x3f2);
507
508 dib3000mc_write_word(state, 93,0);
509 dib3000mc_write_word(state, 94,0);
510 dib3000mc_write_word(state, 95,0);
511 dib3000mc_write_word(state, 96,0);
512 dib3000mc_write_word(state, 97,0);
513 dib3000mc_write_word(state, 98,0);
514
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300515 dib3000mc_set_impulse_noise(state, 0, ch->transmission_mode);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300516
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300517 value = 0;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300518 switch (ch->transmission_mode) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300519 case TRANSMISSION_MODE_2K: value |= (0 << 7); break;
520 default:
521 case TRANSMISSION_MODE_8K: value |= (1 << 7); break;
522 }
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300523 switch (ch->guard_interval) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300524 case GUARD_INTERVAL_1_32: value |= (0 << 5); break;
525 case GUARD_INTERVAL_1_16: value |= (1 << 5); break;
526 case GUARD_INTERVAL_1_4: value |= (3 << 5); break;
527 default:
528 case GUARD_INTERVAL_1_8: value |= (2 << 5); break;
529 }
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300530 switch (ch->modulation) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300531 case QPSK: value |= (0 << 3); break;
532 case QAM_16: value |= (1 << 3); break;
533 default:
534 case QAM_64: value |= (2 << 3); break;
535 }
536 switch (HIERARCHY_1) {
537 case HIERARCHY_2: value |= 2; break;
538 case HIERARCHY_4: value |= 4; break;
539 default:
540 case HIERARCHY_1: value |= 1; break;
541 }
542 dib3000mc_write_word(state, 0, value);
Mario Rossie3ab2fd2006-12-20 10:54:30 -0300543 dib3000mc_write_word(state, 5, (1 << 8) | ((seq & 0xf) << 4));
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300544
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300545 value = 0;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300546 if (ch->hierarchy == 1)
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300547 value |= (1 << 4);
548 if (1 == 1)
549 value |= 1;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300550 switch ((ch->hierarchy == 0 || 1 == 1) ? ch->code_rate_HP : ch->code_rate_LP) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300551 case FEC_2_3: value |= (2 << 1); break;
552 case FEC_3_4: value |= (3 << 1); break;
553 case FEC_5_6: value |= (5 << 1); break;
554 case FEC_7_8: value |= (7 << 1); break;
555 default:
556 case FEC_1_2: value |= (1 << 1); break;
557 }
558 dib3000mc_write_word(state, 181, value);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300559
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300560 // diversity synchro delay add 50% SFN margin
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300561 switch (ch->transmission_mode) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300562 case TRANSMISSION_MODE_8K: value = 256; break;
563 case TRANSMISSION_MODE_2K:
564 default: value = 64; break;
565 }
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300566 switch (ch->guard_interval) {
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300567 case GUARD_INTERVAL_1_16: value *= 2; break;
568 case GUARD_INTERVAL_1_8: value *= 4; break;
569 case GUARD_INTERVAL_1_4: value *= 8; break;
570 default:
571 case GUARD_INTERVAL_1_32: value *= 1; break;
572 }
573 value <<= 4;
574 value |= dib3000mc_read_word(state, 180) & 0x000f;
575 dib3000mc_write_word(state, 180, value);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300576
577 // restart demod
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300578 value = dib3000mc_read_word(state, 0);
579 dib3000mc_write_word(state, 0, value | (1 << 9));
580 dib3000mc_write_word(state, 0, value);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300581
582 msleep(30);
583
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300584 dib3000mc_set_impulse_noise(state, state->cfg->impulse_noise_mode, ch->transmission_mode);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300585}
586
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300587static int dib3000mc_autosearch_start(struct dvb_frontend *demod)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300588{
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300589 struct dtv_frontend_properties *chan = &demod->dtv_property_cache;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300590 struct dib3000mc_state *state = demod->demodulator_priv;
591 u16 reg;
592// u32 val;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300593 struct dtv_frontend_properties schan;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300594
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300595 schan = *chan;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300596
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300597 /* TODO what is that ? */
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300598
599 /* a channel for autosearch */
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300600 schan.transmission_mode = TRANSMISSION_MODE_8K;
601 schan.guard_interval = GUARD_INTERVAL_1_32;
602 schan.modulation = QAM_64;
603 schan.code_rate_HP = FEC_2_3;
604 schan.code_rate_LP = FEC_2_3;
605 schan.hierarchy = 0;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300606
Patrick Boettcherb6884a12007-07-27 10:08:51 -0300607 dib3000mc_set_channel_cfg(state, &schan, 11);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300608
609 reg = dib3000mc_read_word(state, 0);
610 dib3000mc_write_word(state, 0, reg | (1 << 8));
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300611 dib3000mc_read_word(state, 511);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300612 dib3000mc_write_word(state, 0, reg);
613
614 return 0;
615}
616
617static int dib3000mc_autosearch_is_irq(struct dvb_frontend *demod)
618{
619 struct dib3000mc_state *state = demod->demodulator_priv;
620 u16 irq_pending = dib3000mc_read_word(state, 511);
621
622 if (irq_pending & 0x1) // failed
623 return 1;
624
625 if (irq_pending & 0x2) // succeeded
626 return 2;
627
628 return 0; // still pending
629}
630
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300631static int dib3000mc_tune(struct dvb_frontend *demod)
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300632{
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300633 struct dtv_frontend_properties *ch = &demod->dtv_property_cache;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300634 struct dib3000mc_state *state = demod->demodulator_priv;
635
636 // ** configure demod **
637 dib3000mc_set_channel_cfg(state, ch, 0);
638
639 // activates isi
Matt Doran8f6956c2007-07-31 07:09:30 -0300640 if (state->sfn_workaround_active) {
641 dprintk("SFN workaround is active\n");
642 dib3000mc_write_word(state, 29, 0x1273);
643 dib3000mc_write_word(state, 108, 0x4000); // P_pha3_force_pha_shift
644 } else {
645 dib3000mc_write_word(state, 29, 0x1073);
646 dib3000mc_write_word(state, 108, 0x0000); // P_pha3_force_pha_shift
647 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300648
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300649 dib3000mc_set_adp_cfg(state, (u8)ch->modulation);
650 if (ch->transmission_mode == TRANSMISSION_MODE_8K) {
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300651 dib3000mc_write_word(state, 26, 38528);
652 dib3000mc_write_word(state, 33, 8);
653 } else {
654 dib3000mc_write_word(state, 26, 30336);
655 dib3000mc_write_word(state, 33, 6);
656 }
657
Patrick Boettcher01b4bf32006-09-19 12:51:53 -0300658 if (dib3000mc_read_word(state, 509) & 0x80)
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300659 dib3000mc_set_timing(state, ch->transmission_mode,
660 BANDWIDTH_TO_KHZ(ch->bandwidth_hz), 1);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300661
662 return 0;
663}
664
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300665struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating)
666{
667 struct dib3000mc_state *st = demod->demodulator_priv;
668 return dibx000_get_i2c_adapter(&st->i2c_master, DIBX000_I2C_INTERFACE_TUNER, gating);
669}
670
671EXPORT_SYMBOL(dib3000mc_get_tuner_i2c_master);
672
Mauro Carvalho Chehab7e3e68b2016-02-04 12:58:30 -0200673static int dib3000mc_get_frontend(struct dvb_frontend* fe,
674 struct dtv_frontend_properties *fep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300676 struct dib3000mc_state *state = fe->demodulator_priv;
677 u16 tps = dib3000mc_read_word(state,458);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300679 fep->inversion = INVERSION_AUTO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300681 fep->bandwidth_hz = state->current_bandwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300683 switch ((tps >> 8) & 0x1) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300684 case 0: fep->transmission_mode = TRANSMISSION_MODE_2K; break;
685 case 1: fep->transmission_mode = TRANSMISSION_MODE_8K; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 }
687
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300688 switch (tps & 0x3) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300689 case 0: fep->guard_interval = GUARD_INTERVAL_1_32; break;
690 case 1: fep->guard_interval = GUARD_INTERVAL_1_16; break;
691 case 2: fep->guard_interval = GUARD_INTERVAL_1_8; break;
692 case 3: fep->guard_interval = GUARD_INTERVAL_1_4; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 }
694
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300695 switch ((tps >> 13) & 0x3) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300696 case 0: fep->modulation = QPSK; break;
697 case 1: fep->modulation = QAM_16; break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300698 case 2:
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300699 default: fep->modulation = QAM_64; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 }
701
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300702 /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */
703 /* (tps >> 12) & 0x1 == hrch is used, (tps >> 9) & 0x7 == alpha */
704
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300705 fep->hierarchy = HIERARCHY_NONE;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300706 switch ((tps >> 5) & 0x7) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300707 case 1: fep->code_rate_HP = FEC_1_2; break;
708 case 2: fep->code_rate_HP = FEC_2_3; break;
709 case 3: fep->code_rate_HP = FEC_3_4; break;
710 case 5: fep->code_rate_HP = FEC_5_6; break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300711 case 7:
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300712 default: fep->code_rate_HP = FEC_7_8; break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300713
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 }
715
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300716 switch ((tps >> 2) & 0x7) {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300717 case 1: fep->code_rate_LP = FEC_1_2; break;
718 case 2: fep->code_rate_LP = FEC_2_3; break;
719 case 3: fep->code_rate_LP = FEC_3_4; break;
720 case 5: fep->code_rate_LP = FEC_5_6; break;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300721 case 7:
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300722 default: fep->code_rate_LP = FEC_7_8; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
725 return 0;
726}
727
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300728static int dib3000mc_set_frontend(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729{
Mauro Carvalho Chehab7c61d802011-12-30 11:30:21 -0300730 struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300731 struct dib3000mc_state *state = fe->demodulator_priv;
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300732 int ret;
Soeren Moch853ea132008-01-25 06:27:06 -0300733
734 dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300736 state->current_bandwidth = fep->bandwidth_hz;
737 dib3000mc_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->bandwidth_hz));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
Matt Doran8f6956c2007-07-31 07:09:30 -0300739 /* maybe the parameter has been changed */
740 state->sfn_workaround_active = buggy_sfn_workaround;
741
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300742 if (fe->ops.tuner_ops.set_params) {
Mauro Carvalho Chehab14d24d12011-12-24 12:24:33 -0300743 fe->ops.tuner_ops.set_params(fe);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300744 msleep(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 }
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300746
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300747 if (fep->transmission_mode == TRANSMISSION_MODE_AUTO ||
748 fep->guard_interval == GUARD_INTERVAL_AUTO ||
749 fep->modulation == QAM_AUTO ||
750 fep->code_rate_HP == FEC_AUTO) {
Jose Alberto Reguero3a0311c2008-01-25 06:05:16 -0300751 int i = 1000, found;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300752
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300753 dib3000mc_autosearch_start(fe);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300754 do {
755 msleep(1);
756 found = dib3000mc_autosearch_is_irq(fe);
757 } while (found == 0 && i--);
758
759 dprintk("autosearch returns: %d\n",found);
760 if (found == 0 || found == 1)
761 return 0; // no channel found
762
Mauro Carvalho Chehab7e3e68b2016-02-04 12:58:30 -0200763 dib3000mc_get_frontend(fe, fep);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300764 }
765
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300766 ret = dib3000mc_tune(fe);
Soeren Moch853ea132008-01-25 06:27:06 -0300767
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300768 /* make this a config parameter */
769 dib3000mc_set_output_mode(state, OUTMODE_MPEG2_FIFO);
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300770 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771}
772
Mauro Carvalho Chehab0df289a2015-06-07 14:53:52 -0300773static int dib3000mc_read_status(struct dvb_frontend *fe, enum fe_status *stat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300775 struct dib3000mc_state *state = fe->demodulator_priv;
776 u16 lock = dib3000mc_read_word(state, 509);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777
778 *stat = 0;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300779
780 if (lock & 0x8000)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 *stat |= FE_HAS_SIGNAL;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300782 if (lock & 0x3000)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 *stat |= FE_HAS_CARRIER;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300784 if (lock & 0x0100)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 *stat |= FE_HAS_VITERBI;
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300786 if (lock & 0x0010)
787 *stat |= FE_HAS_SYNC;
788 if (lock & 0x0008)
789 *stat |= FE_HAS_LOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
791 return 0;
792}
793
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300794static int dib3000mc_read_ber(struct dvb_frontend *fe, u32 *ber)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300796 struct dib3000mc_state *state = fe->demodulator_priv;
797 *ber = (dib3000mc_read_word(state, 500) << 16) | dib3000mc_read_word(state, 501);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 return 0;
799}
800
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300801static int dib3000mc_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300803 struct dib3000mc_state *state = fe->demodulator_priv;
804 *unc = dib3000mc_read_word(state, 508);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 return 0;
806}
807
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300808static int dib3000mc_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300810 struct dib3000mc_state *state = fe->demodulator_priv;
811 u16 val = dib3000mc_read_word(state, 392);
812 *strength = 65535 - val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 return 0;
814}
815
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr)
817{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300818 *snr = 0x0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 return 0;
820}
821
822static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
823{
Johannes Stezenbach776338e2005-06-23 22:02:35 -0700824 tune->min_delay_ms = 1000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 return 0;
826}
827
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300828static void dib3000mc_release(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300830 struct dib3000mc_state *state = fe->demodulator_priv;
831 dibx000_exit_i2c_master(&state->i2c_master);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 kfree(state);
833}
834
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300835int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300837 struct dib3000mc_state *state = fe->demodulator_priv;
838 dib3000mc_write_word(state, 212 + index, onoff ? (1 << 13) | pid : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 return 0;
840}
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300841EXPORT_SYMBOL(dib3000mc_pid_control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300843int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300845 struct dib3000mc_state *state = fe->demodulator_priv;
846 u16 tmp = dib3000mc_read_word(state, 206) & ~(1 << 4);
847 tmp |= (onoff << 4);
848 return dib3000mc_write_word(state, 206, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849}
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300850EXPORT_SYMBOL(dib3000mc_pid_parse);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300852void dib3000mc_set_config(struct dvb_frontend *fe, struct dib3000mc_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853{
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300854 struct dib3000mc_state *state = fe->demodulator_priv;
855 state->cfg = cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856}
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300857EXPORT_SYMBOL(dib3000mc_set_config);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300859int 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 -0700860{
Randy Dunlap0de8e352010-02-08 20:30:33 -0300861 struct dib3000mc_state *dmcst;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300862 int k;
863 u8 new_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300865 static u8 DIB3000MC_I2C_ADDRESS[] = {20,22,24,26};
866
Randy Dunlap0de8e352010-02-08 20:30:33 -0300867 dmcst = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL);
868 if (dmcst == NULL)
Andrew Mortonfebe2ea2010-07-20 19:22:42 -0300869 return -ENOMEM;
Randy Dunlap0de8e352010-02-08 20:30:33 -0300870
871 dmcst->i2c_adap = i2c;
872
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300873 for (k = no_of_demods-1; k >= 0; k--) {
Randy Dunlap0de8e352010-02-08 20:30:33 -0300874 dmcst->cfg = &cfg[k];
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300875
876 /* designated i2c address */
877 new_addr = DIB3000MC_I2C_ADDRESS[k];
Randy Dunlap0de8e352010-02-08 20:30:33 -0300878 dmcst->i2c_addr = new_addr;
879 if (dib3000mc_identify(dmcst) != 0) {
880 dmcst->i2c_addr = default_addr;
881 if (dib3000mc_identify(dmcst) != 0) {
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300882 dprintk("-E- DiB3000P/MC #%d: not identified\n", k);
Randy Dunlap0de8e352010-02-08 20:30:33 -0300883 kfree(dmcst);
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300884 return -ENODEV;
885 }
886 }
887
Randy Dunlap0de8e352010-02-08 20:30:33 -0300888 dib3000mc_set_output_mode(dmcst, OUTMODE_MPEG2_PAR_CONT_CLK);
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300889
890 // set new i2c address and force divstr (Bit 1) to value 0 (Bit 0)
Randy Dunlap0de8e352010-02-08 20:30:33 -0300891 dib3000mc_write_word(dmcst, 1024, (new_addr << 3) | 0x1);
892 dmcst->i2c_addr = new_addr;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300893 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300895 for (k = 0; k < no_of_demods; k++) {
Randy Dunlap0de8e352010-02-08 20:30:33 -0300896 dmcst->cfg = &cfg[k];
897 dmcst->i2c_addr = DIB3000MC_I2C_ADDRESS[k];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
Randy Dunlap0de8e352010-02-08 20:30:33 -0300899 dib3000mc_write_word(dmcst, 1024, dmcst->i2c_addr << 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300901 /* turn off data output */
Randy Dunlap0de8e352010-02-08 20:30:33 -0300902 dib3000mc_set_output_mode(dmcst, OUTMODE_HIGH_Z);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 }
Randy Dunlap0de8e352010-02-08 20:30:33 -0300904
905 kfree(dmcst);
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300906 return 0;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300907}
908EXPORT_SYMBOL(dib3000mc_i2c_enumeration);
909
Max Kellermannbd336e62016-08-09 18:32:21 -0300910static const struct dvb_frontend_ops dib3000mc_ops;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300911
912struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg)
913{
914 struct dvb_frontend *demod;
915 struct dib3000mc_state *st;
916 st = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL);
917 if (st == NULL)
918 return NULL;
919
920 st->cfg = cfg;
921 st->i2c_adap = i2c_adap;
Patrick Boettcher6958eff2006-09-19 12:51:40 -0300922 st->i2c_addr = i2c_addr;
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300923
924 demod = &st->demod;
925 demod->demodulator_priv = st;
926 memcpy(&st->demod.ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops));
927
928 if (dib3000mc_identify(st) != 0)
929 goto error;
930
931 dibx000_init_i2c_master(&st->i2c_master, DIB3000MC, st->i2c_adap, st->i2c_addr);
932
Patrick Boettcher303cbea2006-09-19 12:51:56 -0300933 dib3000mc_write_word(st, 1037, 0x3130);
934
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300935 return demod;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
937error:
Patrick Boettcher136cafb2006-09-19 12:51:33 -0300938 kfree(st);
939 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940}
Patrick Boettchere4d6c1f2006-08-08 15:48:09 -0300941EXPORT_SYMBOL(dib3000mc_attach);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942
Max Kellermannbd336e62016-08-09 18:32:21 -0300943static const struct dvb_frontend_ops dib3000mc_ops = {
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300944 .delsys = { SYS_DVBT },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 .info = {
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300946 .name = "DiBcom 3000MC/P",
Mauro Carvalho Chehabf1b1eab2018-07-05 18:59:36 -0400947 .frequency_min_hz = 44250 * kHz,
948 .frequency_max_hz = 867250 * kHz,
949 .frequency_stepsize_hz = 62500,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 .caps = FE_CAN_INVERSION_AUTO |
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300951 FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
952 FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
953 FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
954 FE_CAN_TRANSMISSION_MODE_AUTO |
955 FE_CAN_GUARD_INTERVAL_AUTO |
956 FE_CAN_RECOVER |
957 FE_CAN_HIERARCHY_AUTO,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 },
959
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300960 .release = dib3000mc_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300962 .init = dib3000mc_init,
963 .sleep = dib3000mc_sleep,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300965 .set_frontend = dib3000mc_set_frontend,
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300966 .get_tune_settings = dib3000mc_fe_get_tune_settings,
Mauro Carvalho Chehabc1f814f2011-12-22 19:06:20 -0300967 .get_frontend = dib3000mc_get_frontend,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300969 .read_status = dib3000mc_read_status,
970 .read_ber = dib3000mc_read_ber,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 .read_signal_strength = dib3000mc_read_signal_strength,
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300972 .read_snr = dib3000mc_read_snr,
973 .read_ucblocks = dib3000mc_read_unc_blocks,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974};
975
Patrick Boettcher99e44da2016-01-24 12:56:58 -0200976MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
Patrick Boettcherb7571f82006-08-08 15:48:10 -0300977MODULE_DESCRIPTION("Driver for the DiBcom 3000MC/P COFDM demodulator");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978MODULE_LICENSE("GPL");