1a10e763bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29a0bf528SMauro Carvalho Chehab /*
39a0bf528SMauro Carvalho Chehab  * Driver for DiBcom DiB3000MC/P-demodulator.
49a0bf528SMauro Carvalho Chehab  *
59a0bf528SMauro Carvalho Chehab  * Copyright (C) 2004-7 DiBcom (http://www.dibcom.fr/)
699e44da7SPatrick Boettcher  * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
79a0bf528SMauro Carvalho Chehab  *
89a0bf528SMauro Carvalho Chehab  * This code is partially based on the previous dib3000mc.c .
99a0bf528SMauro Carvalho Chehab  */
109a0bf528SMauro Carvalho Chehab 
11441d54e3SMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12441d54e3SMauro Carvalho Chehab 
139a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
149a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
159a0bf528SMauro Carvalho Chehab #include <linux/i2c.h>
169a0bf528SMauro Carvalho Chehab 
17fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
189a0bf528SMauro Carvalho Chehab 
199a0bf528SMauro Carvalho Chehab #include "dib3000mc.h"
209a0bf528SMauro Carvalho Chehab 
219a0bf528SMauro Carvalho Chehab static int debug;
229a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
239a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
249a0bf528SMauro Carvalho Chehab 
259a0bf528SMauro Carvalho Chehab static int buggy_sfn_workaround;
269a0bf528SMauro Carvalho Chehab module_param(buggy_sfn_workaround, int, 0644);
279a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (default: 0)");
289a0bf528SMauro Carvalho Chehab 
29441d54e3SMauro Carvalho Chehab #define dprintk(fmt, arg...) do {					\
30441d54e3SMauro Carvalho Chehab 	if (debug)							\
31441d54e3SMauro Carvalho Chehab 		printk(KERN_DEBUG pr_fmt("%s: " fmt),			\
32441d54e3SMauro Carvalho Chehab 		       __func__, ##arg);				\
33441d54e3SMauro Carvalho Chehab } while (0)
349a0bf528SMauro Carvalho Chehab 
359a0bf528SMauro Carvalho Chehab struct dib3000mc_state {
369a0bf528SMauro Carvalho Chehab 	struct dvb_frontend demod;
379a0bf528SMauro Carvalho Chehab 	struct dib3000mc_config *cfg;
389a0bf528SMauro Carvalho Chehab 
399a0bf528SMauro Carvalho Chehab 	u8 i2c_addr;
409a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c_adap;
419a0bf528SMauro Carvalho Chehab 
429a0bf528SMauro Carvalho Chehab 	struct dibx000_i2c_master i2c_master;
439a0bf528SMauro Carvalho Chehab 
449a0bf528SMauro Carvalho Chehab 	u32 timf;
459a0bf528SMauro Carvalho Chehab 
469a0bf528SMauro Carvalho Chehab 	u32 current_bandwidth;
479a0bf528SMauro Carvalho Chehab 
489a0bf528SMauro Carvalho Chehab 	u16 dev_id;
499a0bf528SMauro Carvalho Chehab 
509a0bf528SMauro Carvalho Chehab 	u8 sfn_workaround_active :1;
519a0bf528SMauro Carvalho Chehab };
529a0bf528SMauro Carvalho Chehab 
dib3000mc_read_word(struct dib3000mc_state * state,u16 reg)539a0bf528SMauro Carvalho Chehab static u16 dib3000mc_read_word(struct dib3000mc_state *state, u16 reg)
549a0bf528SMauro Carvalho Chehab {
559a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[2] = {
56b4756707SSean Young 		{ .addr = state->i2c_addr >> 1, .flags = 0,        .len = 2 },
57b4756707SSean Young 		{ .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .len = 2 },
589a0bf528SMauro Carvalho Chehab 	};
59b4756707SSean Young 	u16 word;
60b4756707SSean Young 	u8 *b;
61b4756707SSean Young 
62b4756707SSean Young 	b = kmalloc(4, GFP_KERNEL);
63b4756707SSean Young 	if (!b)
64b4756707SSean Young 		return 0;
65b4756707SSean Young 
66b4756707SSean Young 	b[0] = (reg >> 8) | 0x80;
67b4756707SSean Young 	b[1] = reg;
68b4756707SSean Young 	b[2] = 0;
69b4756707SSean Young 	b[3] = 0;
70b4756707SSean Young 
71b4756707SSean Young 	msg[0].buf = b;
72b4756707SSean Young 	msg[1].buf = b + 2;
739a0bf528SMauro Carvalho Chehab 
749a0bf528SMauro Carvalho Chehab 	if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
759a0bf528SMauro Carvalho Chehab 		dprintk("i2c read error on %d\n",reg);
769a0bf528SMauro Carvalho Chehab 
77b4756707SSean Young 	word = (b[2] << 8) | b[3];
78b4756707SSean Young 	kfree(b);
79b4756707SSean Young 
80b4756707SSean Young 	return word;
819a0bf528SMauro Carvalho Chehab }
829a0bf528SMauro Carvalho Chehab 
dib3000mc_write_word(struct dib3000mc_state * state,u16 reg,u16 val)839a0bf528SMauro Carvalho Chehab static int dib3000mc_write_word(struct dib3000mc_state *state, u16 reg, u16 val)
849a0bf528SMauro Carvalho Chehab {
859a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {
86b4756707SSean Young 		.addr = state->i2c_addr >> 1, .flags = 0, .len = 4
879a0bf528SMauro Carvalho Chehab 	};
88b4756707SSean Young 	int rc;
89b4756707SSean Young 	u8 *b;
90b4756707SSean Young 
91b4756707SSean Young 	b = kmalloc(4, GFP_KERNEL);
92b4756707SSean Young 	if (!b)
93b4756707SSean Young 		return -ENOMEM;
94b4756707SSean Young 
95b4756707SSean Young 	b[0] = reg >> 8;
96b4756707SSean Young 	b[1] = reg;
97b4756707SSean Young 	b[2] = val >> 8;
98b4756707SSean Young 	b[3] = val;
99b4756707SSean Young 
100b4756707SSean Young 	msg.buf = b;
101b4756707SSean Young 
102b4756707SSean Young 	rc = i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
103b4756707SSean Young 	kfree(b);
104b4756707SSean Young 
105b4756707SSean Young 	return rc;
1069a0bf528SMauro Carvalho Chehab }
1079a0bf528SMauro Carvalho Chehab 
dib3000mc_identify(struct dib3000mc_state * state)1089a0bf528SMauro Carvalho Chehab static int dib3000mc_identify(struct dib3000mc_state *state)
1099a0bf528SMauro Carvalho Chehab {
1109a0bf528SMauro Carvalho Chehab 	u16 value;
1119a0bf528SMauro Carvalho Chehab 	if ((value = dib3000mc_read_word(state, 1025)) != 0x01b3) {
1129a0bf528SMauro Carvalho Chehab 		dprintk("-E-  DiB3000MC/P: wrong Vendor ID (read=0x%x)\n",value);
1139a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
1149a0bf528SMauro Carvalho Chehab 	}
1159a0bf528SMauro Carvalho Chehab 
1169a0bf528SMauro Carvalho Chehab 	value = dib3000mc_read_word(state, 1026);
1179a0bf528SMauro Carvalho Chehab 	if (value != 0x3001 && value != 0x3002) {
1189a0bf528SMauro Carvalho Chehab 		dprintk("-E-  DiB3000MC/P: wrong Device ID (%x)\n",value);
1199a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
1209a0bf528SMauro Carvalho Chehab 	}
1219a0bf528SMauro Carvalho Chehab 	state->dev_id = value;
1229a0bf528SMauro Carvalho Chehab 
1239a0bf528SMauro Carvalho Chehab 	dprintk("-I-  found DiB3000MC/P: %x\n",state->dev_id);
1249a0bf528SMauro Carvalho Chehab 
1259a0bf528SMauro Carvalho Chehab 	return 0;
1269a0bf528SMauro Carvalho Chehab }
1279a0bf528SMauro Carvalho Chehab 
dib3000mc_set_timing(struct dib3000mc_state * state,s16 nfft,u32 bw,u8 update_offset)1289a0bf528SMauro Carvalho Chehab static int dib3000mc_set_timing(struct dib3000mc_state *state, s16 nfft, u32 bw, u8 update_offset)
1299a0bf528SMauro Carvalho Chehab {
1309a0bf528SMauro Carvalho Chehab 	u32 timf;
1319a0bf528SMauro Carvalho Chehab 
1329a0bf528SMauro Carvalho Chehab 	if (state->timf == 0) {
1339a0bf528SMauro Carvalho Chehab 		timf = 1384402; // default value for 8MHz
1349a0bf528SMauro Carvalho Chehab 		if (update_offset)
1359a0bf528SMauro Carvalho Chehab 			msleep(200); // first time we do an update
1369a0bf528SMauro Carvalho Chehab 	} else
1379a0bf528SMauro Carvalho Chehab 		timf = state->timf;
1389a0bf528SMauro Carvalho Chehab 
1399a0bf528SMauro Carvalho Chehab 	timf *= (bw / 1000);
1409a0bf528SMauro Carvalho Chehab 
1419a0bf528SMauro Carvalho Chehab 	if (update_offset) {
1429a0bf528SMauro Carvalho Chehab 		s16 tim_offs = dib3000mc_read_word(state, 416);
1439a0bf528SMauro Carvalho Chehab 
1449a0bf528SMauro Carvalho Chehab 		if (tim_offs &  0x2000)
1459a0bf528SMauro Carvalho Chehab 			tim_offs -= 0x4000;
1469a0bf528SMauro Carvalho Chehab 
1479a0bf528SMauro Carvalho Chehab 		if (nfft == TRANSMISSION_MODE_2K)
1489a0bf528SMauro Carvalho Chehab 			tim_offs *= 4;
1499a0bf528SMauro Carvalho Chehab 
1509a0bf528SMauro Carvalho Chehab 		timf += tim_offs;
1519a0bf528SMauro Carvalho Chehab 		state->timf = timf / (bw / 1000);
1529a0bf528SMauro Carvalho Chehab 	}
1539a0bf528SMauro Carvalho Chehab 
1549a0bf528SMauro Carvalho Chehab 	dprintk("timf: %d\n", timf);
1559a0bf528SMauro Carvalho Chehab 
1569a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 23, (u16) (timf >> 16));
1579a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 24, (u16) (timf      ) & 0xffff);
1589a0bf528SMauro Carvalho Chehab 
1599a0bf528SMauro Carvalho Chehab 	return 0;
1609a0bf528SMauro Carvalho Chehab }
1619a0bf528SMauro Carvalho Chehab 
dib3000mc_setup_pwm_state(struct dib3000mc_state * state)1629a0bf528SMauro Carvalho Chehab static int dib3000mc_setup_pwm_state(struct dib3000mc_state *state)
1639a0bf528SMauro Carvalho Chehab {
1649a0bf528SMauro Carvalho Chehab 	u16 reg_51, reg_52 = state->cfg->agc->setup & 0xfefb;
1659a0bf528SMauro Carvalho Chehab 	if (state->cfg->pwm3_inversion) {
1669a0bf528SMauro Carvalho Chehab 		reg_51 =  (2 << 14) | (0 << 10) | (7 << 6) | (2 << 2) | (2 << 0);
1679a0bf528SMauro Carvalho Chehab 		reg_52 |= (1 << 2);
1689a0bf528SMauro Carvalho Chehab 	} else {
1699a0bf528SMauro Carvalho Chehab 		reg_51 = (2 << 14) | (4 << 10) | (7 << 6) | (2 << 2) | (2 << 0);
1709a0bf528SMauro Carvalho Chehab 		reg_52 |= (1 << 8);
1719a0bf528SMauro Carvalho Chehab 	}
1729a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 51, reg_51);
1739a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 52, reg_52);
1749a0bf528SMauro Carvalho Chehab 
1759a0bf528SMauro Carvalho Chehab 	if (state->cfg->use_pwm3)
1769a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 245, (1 << 3) | (1 << 0));
1779a0bf528SMauro Carvalho Chehab 	else
1789a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 245, 0);
1799a0bf528SMauro Carvalho Chehab 
1809a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 1040, 0x3);
1819a0bf528SMauro Carvalho Chehab 	return 0;
1829a0bf528SMauro Carvalho Chehab }
1839a0bf528SMauro Carvalho Chehab 
dib3000mc_set_output_mode(struct dib3000mc_state * state,int mode)1849a0bf528SMauro Carvalho Chehab static int dib3000mc_set_output_mode(struct dib3000mc_state *state, int mode)
1859a0bf528SMauro Carvalho Chehab {
1869a0bf528SMauro Carvalho Chehab 	int    ret = 0;
1879a0bf528SMauro Carvalho Chehab 	u16 fifo_threshold = 1792;
1889a0bf528SMauro Carvalho Chehab 	u16 outreg = 0;
1899a0bf528SMauro Carvalho Chehab 	u16 outmode = 0;
1909a0bf528SMauro Carvalho Chehab 	u16 elecout = 1;
1919a0bf528SMauro Carvalho Chehab 	u16 smo_reg = dib3000mc_read_word(state, 206) & 0x0010; /* keep the pid_parse bit */
1929a0bf528SMauro Carvalho Chehab 
1939a0bf528SMauro Carvalho Chehab 	dprintk("-I-  Setting output mode for demod %p to %d\n",
1949a0bf528SMauro Carvalho Chehab 			&state->demod, mode);
1959a0bf528SMauro Carvalho Chehab 
1969a0bf528SMauro Carvalho Chehab 	switch (mode) {
1979a0bf528SMauro Carvalho Chehab 		case OUTMODE_HIGH_Z:  // disable
1989a0bf528SMauro Carvalho Chehab 			elecout = 0;
1999a0bf528SMauro Carvalho Chehab 			break;
2009a0bf528SMauro Carvalho Chehab 		case OUTMODE_MPEG2_PAR_GATED_CLK:   // STBs with parallel gated clock
2019a0bf528SMauro Carvalho Chehab 			outmode = 0;
2029a0bf528SMauro Carvalho Chehab 			break;
2039a0bf528SMauro Carvalho Chehab 		case OUTMODE_MPEG2_PAR_CONT_CLK:    // STBs with parallel continues clock
2049a0bf528SMauro Carvalho Chehab 			outmode = 1;
2059a0bf528SMauro Carvalho Chehab 			break;
2069a0bf528SMauro Carvalho Chehab 		case OUTMODE_MPEG2_SERIAL:          // STBs with serial input
2079a0bf528SMauro Carvalho Chehab 			outmode = 2;
2089a0bf528SMauro Carvalho Chehab 			break;
2099a0bf528SMauro Carvalho Chehab 		case OUTMODE_MPEG2_FIFO:            // e.g. USB feeding
2109a0bf528SMauro Carvalho Chehab 			elecout = 3;
2119a0bf528SMauro Carvalho Chehab 			/*ADDR @ 206 :
2129a0bf528SMauro Carvalho Chehab 			P_smo_error_discard  [1;6:6] = 0
2139a0bf528SMauro Carvalho Chehab 			P_smo_rs_discard     [1;5:5] = 0
2149a0bf528SMauro Carvalho Chehab 			P_smo_pid_parse      [1;4:4] = 0
2159a0bf528SMauro Carvalho Chehab 			P_smo_fifo_flush     [1;3:3] = 0
2169a0bf528SMauro Carvalho Chehab 			P_smo_mode           [2;2:1] = 11
2179a0bf528SMauro Carvalho Chehab 			P_smo_ovf_prot       [1;0:0] = 0
2189a0bf528SMauro Carvalho Chehab 			*/
2199a0bf528SMauro Carvalho Chehab 			smo_reg |= 3 << 1;
2209a0bf528SMauro Carvalho Chehab 			fifo_threshold = 512;
2219a0bf528SMauro Carvalho Chehab 			outmode = 5;
2229a0bf528SMauro Carvalho Chehab 			break;
2239a0bf528SMauro Carvalho Chehab 		case OUTMODE_DIVERSITY:
2249a0bf528SMauro Carvalho Chehab 			outmode = 4;
2259a0bf528SMauro Carvalho Chehab 			elecout = 1;
2269a0bf528SMauro Carvalho Chehab 			break;
2279a0bf528SMauro Carvalho Chehab 		default:
2289a0bf528SMauro Carvalho Chehab 			dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod);
2299a0bf528SMauro Carvalho Chehab 			outmode = 0;
2309a0bf528SMauro Carvalho Chehab 			break;
2319a0bf528SMauro Carvalho Chehab 	}
2329a0bf528SMauro Carvalho Chehab 
2339a0bf528SMauro Carvalho Chehab 	if ((state->cfg->output_mpeg2_in_188_bytes))
2349a0bf528SMauro Carvalho Chehab 		smo_reg |= (1 << 5); // P_smo_rs_discard     [1;5:5] = 1
2359a0bf528SMauro Carvalho Chehab 
2369a0bf528SMauro Carvalho Chehab 	outreg = dib3000mc_read_word(state, 244) & 0x07FF;
2379a0bf528SMauro Carvalho Chehab 	outreg |= (outmode << 11);
2389a0bf528SMauro Carvalho Chehab 	ret |= dib3000mc_write_word(state,  244, outreg);
2399a0bf528SMauro Carvalho Chehab 	ret |= dib3000mc_write_word(state,  206, smo_reg);   /*smo_ mode*/
2409a0bf528SMauro Carvalho Chehab 	ret |= dib3000mc_write_word(state,  207, fifo_threshold); /* synchronous fread */
2419a0bf528SMauro Carvalho Chehab 	ret |= dib3000mc_write_word(state, 1040, elecout);         /* P_out_cfg */
2429a0bf528SMauro Carvalho Chehab 	return ret;
2439a0bf528SMauro Carvalho Chehab }
2449a0bf528SMauro Carvalho Chehab 
dib3000mc_set_bandwidth(struct dib3000mc_state * state,u32 bw)2459a0bf528SMauro Carvalho Chehab static int dib3000mc_set_bandwidth(struct dib3000mc_state *state, u32 bw)
2469a0bf528SMauro Carvalho Chehab {
2479a0bf528SMauro Carvalho Chehab 	u16 bw_cfg[6] = { 0 };
2489a0bf528SMauro Carvalho Chehab 	u16 imp_bw_cfg[3] = { 0 };
2499a0bf528SMauro Carvalho Chehab 	u16 reg;
2509a0bf528SMauro Carvalho Chehab 
2519a0bf528SMauro Carvalho Chehab /* settings here are for 27.7MHz */
2529a0bf528SMauro Carvalho Chehab 	switch (bw) {
2539a0bf528SMauro Carvalho Chehab 		case 8000:
2549a0bf528SMauro Carvalho Chehab 			bw_cfg[0] = 0x0019; bw_cfg[1] = 0x5c30; bw_cfg[2] = 0x0054; bw_cfg[3] = 0x88a0; bw_cfg[4] = 0x01a6; bw_cfg[5] = 0xab20;
2559a0bf528SMauro Carvalho Chehab 			imp_bw_cfg[0] = 0x04db; imp_bw_cfg[1] = 0x00db; imp_bw_cfg[2] = 0x00b7;
2569a0bf528SMauro Carvalho Chehab 			break;
2579a0bf528SMauro Carvalho Chehab 
2589a0bf528SMauro Carvalho Chehab 		case 7000:
2599a0bf528SMauro Carvalho Chehab 			bw_cfg[0] = 0x001c; bw_cfg[1] = 0xfba5; bw_cfg[2] = 0x0060; bw_cfg[3] = 0x9c25; bw_cfg[4] = 0x01e3; bw_cfg[5] = 0x0cb7;
2609a0bf528SMauro Carvalho Chehab 			imp_bw_cfg[0] = 0x04c0; imp_bw_cfg[1] = 0x00c0; imp_bw_cfg[2] = 0x00a0;
2619a0bf528SMauro Carvalho Chehab 			break;
2629a0bf528SMauro Carvalho Chehab 
2639a0bf528SMauro Carvalho Chehab 		case 6000:
2649a0bf528SMauro Carvalho Chehab 			bw_cfg[0] = 0x0021; bw_cfg[1] = 0xd040; bw_cfg[2] = 0x0070; bw_cfg[3] = 0xb62b; bw_cfg[4] = 0x0233; bw_cfg[5] = 0x8ed5;
2659a0bf528SMauro Carvalho Chehab 			imp_bw_cfg[0] = 0x04a5; imp_bw_cfg[1] = 0x00a5; imp_bw_cfg[2] = 0x0089;
2669a0bf528SMauro Carvalho Chehab 			break;
2679a0bf528SMauro Carvalho Chehab 
2689a0bf528SMauro Carvalho Chehab 		case 5000:
2699a0bf528SMauro Carvalho Chehab 			bw_cfg[0] = 0x0028; bw_cfg[1] = 0x9380; bw_cfg[2] = 0x0087; bw_cfg[3] = 0x4100; bw_cfg[4] = 0x02a4; bw_cfg[5] = 0x4500;
2709a0bf528SMauro Carvalho Chehab 			imp_bw_cfg[0] = 0x0489; imp_bw_cfg[1] = 0x0089; imp_bw_cfg[2] = 0x0072;
2719a0bf528SMauro Carvalho Chehab 			break;
2729a0bf528SMauro Carvalho Chehab 
2739a0bf528SMauro Carvalho Chehab 		default: return -EINVAL;
2749a0bf528SMauro Carvalho Chehab 	}
2759a0bf528SMauro Carvalho Chehab 
2769a0bf528SMauro Carvalho Chehab 	for (reg = 6; reg < 12; reg++)
2779a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, reg, bw_cfg[reg - 6]);
2789a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 12, 0x0000);
2799a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 13, 0x03e8);
2809a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 14, 0x0000);
2819a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 15, 0x03f2);
2829a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 16, 0x0001);
2839a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 17, 0xb0d0);
2849a0bf528SMauro Carvalho Chehab 	// P_sec_len
2859a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 18, 0x0393);
2869a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 19, 0x8700);
2879a0bf528SMauro Carvalho Chehab 
2889a0bf528SMauro Carvalho Chehab 	for (reg = 55; reg < 58; reg++)
2899a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, reg, imp_bw_cfg[reg - 55]);
2909a0bf528SMauro Carvalho Chehab 
2919a0bf528SMauro Carvalho Chehab 	// Timing configuration
2929a0bf528SMauro Carvalho Chehab 	dib3000mc_set_timing(state, TRANSMISSION_MODE_2K, bw, 0);
2939a0bf528SMauro Carvalho Chehab 
2949a0bf528SMauro Carvalho Chehab 	return 0;
2959a0bf528SMauro Carvalho Chehab }
2969a0bf528SMauro Carvalho Chehab 
2979a0bf528SMauro Carvalho Chehab static u16 impulse_noise_val[29] =
2989a0bf528SMauro Carvalho Chehab 
2999a0bf528SMauro Carvalho Chehab {
3009a0bf528SMauro Carvalho Chehab 	0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c, 0x3ffe, 0x7f3,
3019a0bf528SMauro Carvalho Chehab 	0x2d94, 0x76, 0x53d, 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3, 0x3feb, 0x7d2,
3029a0bf528SMauro Carvalho Chehab 	0x365e, 0x76, 0x48c, 0x3ffe, 0x5b3, 0x3feb, 0x76, 0x0000, 0xd
3039a0bf528SMauro Carvalho Chehab };
3049a0bf528SMauro Carvalho Chehab 
dib3000mc_set_impulse_noise(struct dib3000mc_state * state,u8 mode,s16 nfft)3059a0bf528SMauro Carvalho Chehab static void dib3000mc_set_impulse_noise(struct dib3000mc_state *state, u8 mode, s16 nfft)
3069a0bf528SMauro Carvalho Chehab {
3079a0bf528SMauro Carvalho Chehab 	u16 i;
3089a0bf528SMauro Carvalho Chehab 	for (i = 58; i < 87; i++)
3099a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, i, impulse_noise_val[i-58]);
3109a0bf528SMauro Carvalho Chehab 
3119a0bf528SMauro Carvalho Chehab 	if (nfft == TRANSMISSION_MODE_8K) {
3129a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 58, 0x3b);
3139a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 84, 0x00);
3149a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 85, 0x8200);
3159a0bf528SMauro Carvalho Chehab 	}
3169a0bf528SMauro Carvalho Chehab 
3179a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 34, 0x1294);
3189a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 35, 0x1ff8);
3199a0bf528SMauro Carvalho Chehab 	if (mode == 1)
3209a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 55, dib3000mc_read_word(state, 55) | (1 << 10));
3219a0bf528SMauro Carvalho Chehab }
3229a0bf528SMauro Carvalho Chehab 
dib3000mc_init(struct dvb_frontend * demod)3239a0bf528SMauro Carvalho Chehab static int dib3000mc_init(struct dvb_frontend *demod)
3249a0bf528SMauro Carvalho Chehab {
3259a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = demod->demodulator_priv;
3269a0bf528SMauro Carvalho Chehab 	struct dibx000_agc_config *agc = state->cfg->agc;
3279a0bf528SMauro Carvalho Chehab 
3289a0bf528SMauro Carvalho Chehab 	// Restart Configuration
3299a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 1027, 0x8000);
3309a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 1027, 0x0000);
3319a0bf528SMauro Carvalho Chehab 
3329a0bf528SMauro Carvalho Chehab 	// power up the demod + mobility configuration
3339a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 140, 0x0000);
3349a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 1031, 0);
3359a0bf528SMauro Carvalho Chehab 
3369a0bf528SMauro Carvalho Chehab 	if (state->cfg->mobile_mode) {
3379a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 139,  0x0000);
3389a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 141,  0x0000);
3399a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 175,  0x0002);
3409a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 1032, 0x0000);
3419a0bf528SMauro Carvalho Chehab 	} else {
3429a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 139,  0x0001);
3439a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 141,  0x0000);
3449a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 175,  0x0000);
3459a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 1032, 0x012C);
3469a0bf528SMauro Carvalho Chehab 	}
3479a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 1033, 0x0000);
3489a0bf528SMauro Carvalho Chehab 
3499a0bf528SMauro Carvalho Chehab 	// P_clk_cfg
3509a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 1037, 0x3130);
3519a0bf528SMauro Carvalho Chehab 
3529a0bf528SMauro Carvalho Chehab 	// other configurations
3539a0bf528SMauro Carvalho Chehab 
3549a0bf528SMauro Carvalho Chehab 	// P_ctrl_sfreq
3559a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 33, (5 << 0));
3569a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 88, (1 << 10) | (0x10 << 0));
3579a0bf528SMauro Carvalho Chehab 
3589a0bf528SMauro Carvalho Chehab 	// Phase noise control
3599a0bf528SMauro Carvalho Chehab 	// P_fft_phacor_inh, P_fft_phacor_cpe, P_fft_powrange
3609a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 99, (1 << 9) | (0x20 << 0));
3619a0bf528SMauro Carvalho Chehab 
3629a0bf528SMauro Carvalho Chehab 	if (state->cfg->phase_noise_mode == 0)
3639a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 111, 0x00);
3649a0bf528SMauro Carvalho Chehab 	else
3659a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 111, 0x02);
3669a0bf528SMauro Carvalho Chehab 
3679a0bf528SMauro Carvalho Chehab 	// P_agc_global
3689a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 50, 0x8000);
3699a0bf528SMauro Carvalho Chehab 
3709a0bf528SMauro Carvalho Chehab 	// agc setup misc
3719a0bf528SMauro Carvalho Chehab 	dib3000mc_setup_pwm_state(state);
3729a0bf528SMauro Carvalho Chehab 
3739a0bf528SMauro Carvalho Chehab 	// P_agc_counter_lock
3749a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 53, 0x87);
3759a0bf528SMauro Carvalho Chehab 	// P_agc_counter_unlock
3769a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 54, 0x87);
3779a0bf528SMauro Carvalho Chehab 
3789a0bf528SMauro Carvalho Chehab 	/* agc */
3799a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 36, state->cfg->max_time);
3809a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 37, (state->cfg->agc_command1 << 13) | (state->cfg->agc_command2 << 12) | (0x1d << 0));
3819a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 38, state->cfg->pwm3_value);
3829a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 39, state->cfg->ln_adc_level);
3839a0bf528SMauro Carvalho Chehab 
3849a0bf528SMauro Carvalho Chehab 	// set_agc_loop_Bw
3859a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 40, 0x0179);
3869a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 41, 0x03f0);
3879a0bf528SMauro Carvalho Chehab 
3889a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 42, agc->agc1_max);
3899a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 43, agc->agc1_min);
3909a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 44, agc->agc2_max);
3919a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 45, agc->agc2_min);
3929a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 46, (agc->agc1_pt1 << 8) | agc->agc1_pt2);
3939a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 47, (agc->agc1_slope1 << 8) | agc->agc1_slope2);
3949a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 48, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
3959a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 49, (agc->agc2_slope1 << 8) | agc->agc2_slope2);
3969a0bf528SMauro Carvalho Chehab 
3979a0bf528SMauro Carvalho Chehab // Begin: TimeOut registers
3989a0bf528SMauro Carvalho Chehab 	// P_pha3_thres
3999a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 110, 3277);
4009a0bf528SMauro Carvalho Chehab 	// P_timf_alpha = 6, P_corm_alpha = 6, P_corm_thres = 0x80
4019a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state,  26, 0x6680);
4029a0bf528SMauro Carvalho Chehab 	// lock_mask0
4039a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 1, 4);
4049a0bf528SMauro Carvalho Chehab 	// lock_mask1
4059a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 2, 4);
4069a0bf528SMauro Carvalho Chehab 	// lock_mask2
4079a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 3, 0x1000);
4089a0bf528SMauro Carvalho Chehab 	// P_search_maxtrial=1
4099a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 5, 1);
4109a0bf528SMauro Carvalho Chehab 
4119a0bf528SMauro Carvalho Chehab 	dib3000mc_set_bandwidth(state, 8000);
4129a0bf528SMauro Carvalho Chehab 
4139a0bf528SMauro Carvalho Chehab 	// div_lock_mask
4149a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state,  4, 0x814);
4159a0bf528SMauro Carvalho Chehab 
4169a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 21, (1 << 9) | 0x164);
4179a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 22, 0x463d);
4189a0bf528SMauro Carvalho Chehab 
4199a0bf528SMauro Carvalho Chehab 	// Spurious rm cfg
4209a0bf528SMauro Carvalho Chehab 	// P_cspu_regul, P_cspu_win_cut
4219a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 120, 0x200f);
4229a0bf528SMauro Carvalho Chehab 	// P_adp_selec_monit
4239a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 134, 0);
4249a0bf528SMauro Carvalho Chehab 
4259a0bf528SMauro Carvalho Chehab 	// Fec cfg
4269a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 195, 0x10);
4279a0bf528SMauro Carvalho Chehab 
4289a0bf528SMauro Carvalho Chehab 	// diversity register: P_dvsy_sync_wait..
4299a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 180, 0x2FF0);
4309a0bf528SMauro Carvalho Chehab 
4319a0bf528SMauro Carvalho Chehab 	// Impulse noise configuration
4329a0bf528SMauro Carvalho Chehab 	dib3000mc_set_impulse_noise(state, 0, TRANSMISSION_MODE_8K);
4339a0bf528SMauro Carvalho Chehab 
4349a0bf528SMauro Carvalho Chehab 	// output mode set-up
4359a0bf528SMauro Carvalho Chehab 	dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z);
4369a0bf528SMauro Carvalho Chehab 
4379a0bf528SMauro Carvalho Chehab 	/* close the i2c-gate */
4389a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 769, (1 << 7) );
4399a0bf528SMauro Carvalho Chehab 
4409a0bf528SMauro Carvalho Chehab 	return 0;
4419a0bf528SMauro Carvalho Chehab }
4429a0bf528SMauro Carvalho Chehab 
dib3000mc_sleep(struct dvb_frontend * demod)4439a0bf528SMauro Carvalho Chehab static int dib3000mc_sleep(struct dvb_frontend *demod)
4449a0bf528SMauro Carvalho Chehab {
4459a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = demod->demodulator_priv;
4469a0bf528SMauro Carvalho Chehab 
4479a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 1031, 0xFFFF);
4489a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 1032, 0xFFFF);
4499a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 1033, 0xFFF0);
4509a0bf528SMauro Carvalho Chehab 
4519a0bf528SMauro Carvalho Chehab 	return 0;
4529a0bf528SMauro Carvalho Chehab }
4539a0bf528SMauro Carvalho Chehab 
dib3000mc_set_adp_cfg(struct dib3000mc_state * state,s16 qam)4549a0bf528SMauro Carvalho Chehab static void dib3000mc_set_adp_cfg(struct dib3000mc_state *state, s16 qam)
4559a0bf528SMauro Carvalho Chehab {
4569a0bf528SMauro Carvalho Chehab 	u16 cfg[4] = { 0 },reg;
4579a0bf528SMauro Carvalho Chehab 	switch (qam) {
4589a0bf528SMauro Carvalho Chehab 		case QPSK:
4599a0bf528SMauro Carvalho Chehab 			cfg[0] = 0x099a; cfg[1] = 0x7fae; cfg[2] = 0x0333; cfg[3] = 0x7ff0;
4609a0bf528SMauro Carvalho Chehab 			break;
4619a0bf528SMauro Carvalho Chehab 		case QAM_16:
4629a0bf528SMauro Carvalho Chehab 			cfg[0] = 0x023d; cfg[1] = 0x7fdf; cfg[2] = 0x00a4; cfg[3] = 0x7ff0;
4639a0bf528SMauro Carvalho Chehab 			break;
4649a0bf528SMauro Carvalho Chehab 		case QAM_64:
4659a0bf528SMauro Carvalho Chehab 			cfg[0] = 0x0148; cfg[1] = 0x7ff0; cfg[2] = 0x00a4; cfg[3] = 0x7ff8;
4669a0bf528SMauro Carvalho Chehab 			break;
4679a0bf528SMauro Carvalho Chehab 	}
4689a0bf528SMauro Carvalho Chehab 	for (reg = 129; reg < 133; reg++)
4699a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, reg, cfg[reg - 129]);
4709a0bf528SMauro Carvalho Chehab }
4719a0bf528SMauro Carvalho Chehab 
dib3000mc_set_channel_cfg(struct dib3000mc_state * state,struct dtv_frontend_properties * ch,u16 seq)4729a0bf528SMauro Carvalho Chehab static void dib3000mc_set_channel_cfg(struct dib3000mc_state *state,
4739a0bf528SMauro Carvalho Chehab 				      struct dtv_frontend_properties *ch, u16 seq)
4749a0bf528SMauro Carvalho Chehab {
4759a0bf528SMauro Carvalho Chehab 	u16 value;
4769a0bf528SMauro Carvalho Chehab 	u32 bw = BANDWIDTH_TO_KHZ(ch->bandwidth_hz);
4779a0bf528SMauro Carvalho Chehab 
4789a0bf528SMauro Carvalho Chehab 	dib3000mc_set_bandwidth(state, bw);
4799a0bf528SMauro Carvalho Chehab 	dib3000mc_set_timing(state, ch->transmission_mode, bw, 0);
4809a0bf528SMauro Carvalho Chehab 
481c7a092e3SMauro Carvalho Chehab #if 1
4829a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 100, (16 << 6) + 9);
483c7a092e3SMauro Carvalho Chehab #else
484c7a092e3SMauro Carvalho Chehab 	if (boost)
485c7a092e3SMauro Carvalho Chehab 		dib3000mc_write_word(state, 100, (11 << 6) + 6);
486c7a092e3SMauro Carvalho Chehab 	else
487c7a092e3SMauro Carvalho Chehab 		dib3000mc_write_word(state, 100, (16 << 6) + 9);
488c7a092e3SMauro Carvalho Chehab #endif
4899a0bf528SMauro Carvalho Chehab 
4909a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 1027, 0x0800);
4919a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 1027, 0x0000);
4929a0bf528SMauro Carvalho Chehab 
4939a0bf528SMauro Carvalho Chehab 	//Default cfg isi offset adp
4949a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 26,  0x6680);
4959a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 29,  0x1273);
4969a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 33,       5);
4979a0bf528SMauro Carvalho Chehab 	dib3000mc_set_adp_cfg(state, QAM_16);
4989a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 133,  15564);
4999a0bf528SMauro Carvalho Chehab 
5009a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 12 , 0x0);
5019a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 13 , 0x3e8);
5029a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 14 , 0x0);
5039a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 15 , 0x3f2);
5049a0bf528SMauro Carvalho Chehab 
5059a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 93,0);
5069a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 94,0);
5079a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 95,0);
5089a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 96,0);
5099a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 97,0);
5109a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 98,0);
5119a0bf528SMauro Carvalho Chehab 
5129a0bf528SMauro Carvalho Chehab 	dib3000mc_set_impulse_noise(state, 0, ch->transmission_mode);
5139a0bf528SMauro Carvalho Chehab 
5149a0bf528SMauro Carvalho Chehab 	value = 0;
5159a0bf528SMauro Carvalho Chehab 	switch (ch->transmission_mode) {
5169a0bf528SMauro Carvalho Chehab 		case TRANSMISSION_MODE_2K: value |= (0 << 7); break;
5179a0bf528SMauro Carvalho Chehab 		default:
5189a0bf528SMauro Carvalho Chehab 		case TRANSMISSION_MODE_8K: value |= (1 << 7); break;
5199a0bf528SMauro Carvalho Chehab 	}
5209a0bf528SMauro Carvalho Chehab 	switch (ch->guard_interval) {
5219a0bf528SMauro Carvalho Chehab 		case GUARD_INTERVAL_1_32: value |= (0 << 5); break;
5229a0bf528SMauro Carvalho Chehab 		case GUARD_INTERVAL_1_16: value |= (1 << 5); break;
5239a0bf528SMauro Carvalho Chehab 		case GUARD_INTERVAL_1_4:  value |= (3 << 5); break;
5249a0bf528SMauro Carvalho Chehab 		default:
5259a0bf528SMauro Carvalho Chehab 		case GUARD_INTERVAL_1_8:  value |= (2 << 5); break;
5269a0bf528SMauro Carvalho Chehab 	}
5279a0bf528SMauro Carvalho Chehab 	switch (ch->modulation) {
5289a0bf528SMauro Carvalho Chehab 		case QPSK:  value |= (0 << 3); break;
5299a0bf528SMauro Carvalho Chehab 		case QAM_16: value |= (1 << 3); break;
5309a0bf528SMauro Carvalho Chehab 		default:
5319a0bf528SMauro Carvalho Chehab 		case QAM_64: value |= (2 << 3); break;
5329a0bf528SMauro Carvalho Chehab 	}
5339a0bf528SMauro Carvalho Chehab 	switch (HIERARCHY_1) {
5349a0bf528SMauro Carvalho Chehab 		case HIERARCHY_2: value |= 2; break;
5359a0bf528SMauro Carvalho Chehab 		case HIERARCHY_4: value |= 4; break;
5369a0bf528SMauro Carvalho Chehab 		default:
5379a0bf528SMauro Carvalho Chehab 		case HIERARCHY_1: value |= 1; break;
5389a0bf528SMauro Carvalho Chehab 	}
5399a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 0, value);
5409a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 5, (1 << 8) | ((seq & 0xf) << 4));
5419a0bf528SMauro Carvalho Chehab 
5429a0bf528SMauro Carvalho Chehab 	value = 0;
5439a0bf528SMauro Carvalho Chehab 	if (ch->hierarchy == 1)
5449a0bf528SMauro Carvalho Chehab 		value |= (1 << 4);
5459a0bf528SMauro Carvalho Chehab 	if (1 == 1)
5469a0bf528SMauro Carvalho Chehab 		value |= 1;
5479a0bf528SMauro Carvalho Chehab 	switch ((ch->hierarchy == 0 || 1 == 1) ? ch->code_rate_HP : ch->code_rate_LP) {
5489a0bf528SMauro Carvalho Chehab 		case FEC_2_3: value |= (2 << 1); break;
5499a0bf528SMauro Carvalho Chehab 		case FEC_3_4: value |= (3 << 1); break;
5509a0bf528SMauro Carvalho Chehab 		case FEC_5_6: value |= (5 << 1); break;
5519a0bf528SMauro Carvalho Chehab 		case FEC_7_8: value |= (7 << 1); break;
5529a0bf528SMauro Carvalho Chehab 		default:
5539a0bf528SMauro Carvalho Chehab 		case FEC_1_2: value |= (1 << 1); break;
5549a0bf528SMauro Carvalho Chehab 	}
5559a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 181, value);
5569a0bf528SMauro Carvalho Chehab 
5579a0bf528SMauro Carvalho Chehab 	// diversity synchro delay add 50% SFN margin
5589a0bf528SMauro Carvalho Chehab 	switch (ch->transmission_mode) {
5599a0bf528SMauro Carvalho Chehab 		case TRANSMISSION_MODE_8K: value = 256; break;
5609a0bf528SMauro Carvalho Chehab 		case TRANSMISSION_MODE_2K:
5619a0bf528SMauro Carvalho Chehab 		default: value = 64; break;
5629a0bf528SMauro Carvalho Chehab 	}
5639a0bf528SMauro Carvalho Chehab 	switch (ch->guard_interval) {
5649a0bf528SMauro Carvalho Chehab 		case GUARD_INTERVAL_1_16: value *= 2; break;
5659a0bf528SMauro Carvalho Chehab 		case GUARD_INTERVAL_1_8:  value *= 4; break;
5669a0bf528SMauro Carvalho Chehab 		case GUARD_INTERVAL_1_4:  value *= 8; break;
5679a0bf528SMauro Carvalho Chehab 		default:
5689a0bf528SMauro Carvalho Chehab 		case GUARD_INTERVAL_1_32: value *= 1; break;
5699a0bf528SMauro Carvalho Chehab 	}
5709a0bf528SMauro Carvalho Chehab 	value <<= 4;
5719a0bf528SMauro Carvalho Chehab 	value |= dib3000mc_read_word(state, 180) & 0x000f;
5729a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 180, value);
5739a0bf528SMauro Carvalho Chehab 
5749a0bf528SMauro Carvalho Chehab 	// restart demod
5759a0bf528SMauro Carvalho Chehab 	value = dib3000mc_read_word(state, 0);
5769a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 0, value | (1 << 9));
5779a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 0, value);
5789a0bf528SMauro Carvalho Chehab 
5799a0bf528SMauro Carvalho Chehab 	msleep(30);
5809a0bf528SMauro Carvalho Chehab 
5819a0bf528SMauro Carvalho Chehab 	dib3000mc_set_impulse_noise(state, state->cfg->impulse_noise_mode, ch->transmission_mode);
5829a0bf528SMauro Carvalho Chehab }
5839a0bf528SMauro Carvalho Chehab 
dib3000mc_autosearch_start(struct dvb_frontend * demod)5849a0bf528SMauro Carvalho Chehab static int dib3000mc_autosearch_start(struct dvb_frontend *demod)
5859a0bf528SMauro Carvalho Chehab {
5869a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *chan = &demod->dtv_property_cache;
5879a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = demod->demodulator_priv;
5889a0bf528SMauro Carvalho Chehab 	u16 reg;
5899a0bf528SMauro Carvalho Chehab //	u32 val;
5909a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties schan;
5919a0bf528SMauro Carvalho Chehab 
5929a0bf528SMauro Carvalho Chehab 	schan = *chan;
5939a0bf528SMauro Carvalho Chehab 
5949a0bf528SMauro Carvalho Chehab 	/* TODO what is that ? */
5959a0bf528SMauro Carvalho Chehab 
5969a0bf528SMauro Carvalho Chehab 	/* a channel for autosearch */
5979a0bf528SMauro Carvalho Chehab 	schan.transmission_mode = TRANSMISSION_MODE_8K;
5989a0bf528SMauro Carvalho Chehab 	schan.guard_interval = GUARD_INTERVAL_1_32;
5999a0bf528SMauro Carvalho Chehab 	schan.modulation = QAM_64;
6009a0bf528SMauro Carvalho Chehab 	schan.code_rate_HP = FEC_2_3;
6019a0bf528SMauro Carvalho Chehab 	schan.code_rate_LP = FEC_2_3;
6029a0bf528SMauro Carvalho Chehab 	schan.hierarchy = 0;
6039a0bf528SMauro Carvalho Chehab 
6049a0bf528SMauro Carvalho Chehab 	dib3000mc_set_channel_cfg(state, &schan, 11);
6059a0bf528SMauro Carvalho Chehab 
6069a0bf528SMauro Carvalho Chehab 	reg = dib3000mc_read_word(state, 0);
6079a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 0, reg | (1 << 8));
6089a0bf528SMauro Carvalho Chehab 	dib3000mc_read_word(state, 511);
6099a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 0, reg);
6109a0bf528SMauro Carvalho Chehab 
6119a0bf528SMauro Carvalho Chehab 	return 0;
6129a0bf528SMauro Carvalho Chehab }
6139a0bf528SMauro Carvalho Chehab 
dib3000mc_autosearch_is_irq(struct dvb_frontend * demod)6149a0bf528SMauro Carvalho Chehab static int dib3000mc_autosearch_is_irq(struct dvb_frontend *demod)
6159a0bf528SMauro Carvalho Chehab {
6169a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = demod->demodulator_priv;
6179a0bf528SMauro Carvalho Chehab 	u16 irq_pending = dib3000mc_read_word(state, 511);
6189a0bf528SMauro Carvalho Chehab 
6199a0bf528SMauro Carvalho Chehab 	if (irq_pending & 0x1) // failed
6209a0bf528SMauro Carvalho Chehab 		return 1;
6219a0bf528SMauro Carvalho Chehab 
6229a0bf528SMauro Carvalho Chehab 	if (irq_pending & 0x2) // succeeded
6239a0bf528SMauro Carvalho Chehab 		return 2;
6249a0bf528SMauro Carvalho Chehab 
6259a0bf528SMauro Carvalho Chehab 	return 0; // still pending
6269a0bf528SMauro Carvalho Chehab }
6279a0bf528SMauro Carvalho Chehab 
dib3000mc_tune(struct dvb_frontend * demod)6289a0bf528SMauro Carvalho Chehab static int dib3000mc_tune(struct dvb_frontend *demod)
6299a0bf528SMauro Carvalho Chehab {
6309a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *ch = &demod->dtv_property_cache;
6319a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = demod->demodulator_priv;
6329a0bf528SMauro Carvalho Chehab 
6339a0bf528SMauro Carvalho Chehab 	// ** configure demod **
6349a0bf528SMauro Carvalho Chehab 	dib3000mc_set_channel_cfg(state, ch, 0);
6359a0bf528SMauro Carvalho Chehab 
6369a0bf528SMauro Carvalho Chehab 	// activates isi
6379a0bf528SMauro Carvalho Chehab 	if (state->sfn_workaround_active) {
6389a0bf528SMauro Carvalho Chehab 		dprintk("SFN workaround is active\n");
6399a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 29, 0x1273);
6409a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 108, 0x4000); // P_pha3_force_pha_shift
6419a0bf528SMauro Carvalho Chehab 	} else {
6429a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 29, 0x1073);
6439a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 108, 0x0000); // P_pha3_force_pha_shift
6449a0bf528SMauro Carvalho Chehab 	}
6459a0bf528SMauro Carvalho Chehab 
6469a0bf528SMauro Carvalho Chehab 	dib3000mc_set_adp_cfg(state, (u8)ch->modulation);
6479a0bf528SMauro Carvalho Chehab 	if (ch->transmission_mode == TRANSMISSION_MODE_8K) {
6489a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 26, 38528);
6499a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 33, 8);
6509a0bf528SMauro Carvalho Chehab 	} else {
6519a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 26, 30336);
6529a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(state, 33, 6);
6539a0bf528SMauro Carvalho Chehab 	}
6549a0bf528SMauro Carvalho Chehab 
6559a0bf528SMauro Carvalho Chehab 	if (dib3000mc_read_word(state, 509) & 0x80)
6569a0bf528SMauro Carvalho Chehab 		dib3000mc_set_timing(state, ch->transmission_mode,
6579a0bf528SMauro Carvalho Chehab 				     BANDWIDTH_TO_KHZ(ch->bandwidth_hz), 1);
6589a0bf528SMauro Carvalho Chehab 
6599a0bf528SMauro Carvalho Chehab 	return 0;
6609a0bf528SMauro Carvalho Chehab }
6619a0bf528SMauro Carvalho Chehab 
dib3000mc_get_tuner_i2c_master(struct dvb_frontend * demod,int gating)6629a0bf528SMauro Carvalho Chehab struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating)
6639a0bf528SMauro Carvalho Chehab {
6649a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *st = demod->demodulator_priv;
6659a0bf528SMauro Carvalho Chehab 	return dibx000_get_i2c_adapter(&st->i2c_master, DIBX000_I2C_INTERFACE_TUNER, gating);
6669a0bf528SMauro Carvalho Chehab }
6679a0bf528SMauro Carvalho Chehab 
6689a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib3000mc_get_tuner_i2c_master);
6699a0bf528SMauro Carvalho Chehab 
dib3000mc_get_frontend(struct dvb_frontend * fe,struct dtv_frontend_properties * fep)6707e3e68bcSMauro Carvalho Chehab static int dib3000mc_get_frontend(struct dvb_frontend* fe,
6717e3e68bcSMauro Carvalho Chehab 				  struct dtv_frontend_properties *fep)
6729a0bf528SMauro Carvalho Chehab {
6739a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = fe->demodulator_priv;
6749a0bf528SMauro Carvalho Chehab 	u16 tps = dib3000mc_read_word(state,458);
6759a0bf528SMauro Carvalho Chehab 
6769a0bf528SMauro Carvalho Chehab 	fep->inversion = INVERSION_AUTO;
6779a0bf528SMauro Carvalho Chehab 
6789a0bf528SMauro Carvalho Chehab 	fep->bandwidth_hz = state->current_bandwidth;
6799a0bf528SMauro Carvalho Chehab 
6809a0bf528SMauro Carvalho Chehab 	switch ((tps >> 8) & 0x1) {
6819a0bf528SMauro Carvalho Chehab 		case 0: fep->transmission_mode = TRANSMISSION_MODE_2K; break;
6829a0bf528SMauro Carvalho Chehab 		case 1: fep->transmission_mode = TRANSMISSION_MODE_8K; break;
6839a0bf528SMauro Carvalho Chehab 	}
6849a0bf528SMauro Carvalho Chehab 
6859a0bf528SMauro Carvalho Chehab 	switch (tps & 0x3) {
6869a0bf528SMauro Carvalho Chehab 		case 0: fep->guard_interval = GUARD_INTERVAL_1_32; break;
6879a0bf528SMauro Carvalho Chehab 		case 1: fep->guard_interval = GUARD_INTERVAL_1_16; break;
6889a0bf528SMauro Carvalho Chehab 		case 2: fep->guard_interval = GUARD_INTERVAL_1_8; break;
6899a0bf528SMauro Carvalho Chehab 		case 3: fep->guard_interval = GUARD_INTERVAL_1_4; break;
6909a0bf528SMauro Carvalho Chehab 	}
6919a0bf528SMauro Carvalho Chehab 
6929a0bf528SMauro Carvalho Chehab 	switch ((tps >> 13) & 0x3) {
6939a0bf528SMauro Carvalho Chehab 		case 0: fep->modulation = QPSK; break;
6949a0bf528SMauro Carvalho Chehab 		case 1: fep->modulation = QAM_16; break;
6959a0bf528SMauro Carvalho Chehab 		case 2:
6969a0bf528SMauro Carvalho Chehab 		default: fep->modulation = QAM_64; break;
6979a0bf528SMauro Carvalho Chehab 	}
6989a0bf528SMauro Carvalho Chehab 
6999a0bf528SMauro Carvalho Chehab 	/* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */
7009a0bf528SMauro Carvalho Chehab 	/* (tps >> 12) & 0x1 == hrch is used, (tps >> 9) & 0x7 == alpha */
7019a0bf528SMauro Carvalho Chehab 
7029a0bf528SMauro Carvalho Chehab 	fep->hierarchy = HIERARCHY_NONE;
7039a0bf528SMauro Carvalho Chehab 	switch ((tps >> 5) & 0x7) {
7049a0bf528SMauro Carvalho Chehab 		case 1: fep->code_rate_HP = FEC_1_2; break;
7059a0bf528SMauro Carvalho Chehab 		case 2: fep->code_rate_HP = FEC_2_3; break;
7069a0bf528SMauro Carvalho Chehab 		case 3: fep->code_rate_HP = FEC_3_4; break;
7079a0bf528SMauro Carvalho Chehab 		case 5: fep->code_rate_HP = FEC_5_6; break;
7089a0bf528SMauro Carvalho Chehab 		case 7:
7099a0bf528SMauro Carvalho Chehab 		default: fep->code_rate_HP = FEC_7_8; break;
7109a0bf528SMauro Carvalho Chehab 
7119a0bf528SMauro Carvalho Chehab 	}
7129a0bf528SMauro Carvalho Chehab 
7139a0bf528SMauro Carvalho Chehab 	switch ((tps >> 2) & 0x7) {
7149a0bf528SMauro Carvalho Chehab 		case 1: fep->code_rate_LP = FEC_1_2; break;
7159a0bf528SMauro Carvalho Chehab 		case 2: fep->code_rate_LP = FEC_2_3; break;
7169a0bf528SMauro Carvalho Chehab 		case 3: fep->code_rate_LP = FEC_3_4; break;
7179a0bf528SMauro Carvalho Chehab 		case 5: fep->code_rate_LP = FEC_5_6; break;
7189a0bf528SMauro Carvalho Chehab 		case 7:
7199a0bf528SMauro Carvalho Chehab 		default: fep->code_rate_LP = FEC_7_8; break;
7209a0bf528SMauro Carvalho Chehab 	}
7219a0bf528SMauro Carvalho Chehab 
7229a0bf528SMauro Carvalho Chehab 	return 0;
7239a0bf528SMauro Carvalho Chehab }
7249a0bf528SMauro Carvalho Chehab 
dib3000mc_set_frontend(struct dvb_frontend * fe)7259a0bf528SMauro Carvalho Chehab static int dib3000mc_set_frontend(struct dvb_frontend *fe)
7269a0bf528SMauro Carvalho Chehab {
7279a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
7289a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = fe->demodulator_priv;
7299a0bf528SMauro Carvalho Chehab 	int ret;
7309a0bf528SMauro Carvalho Chehab 
7319a0bf528SMauro Carvalho Chehab 	dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z);
7329a0bf528SMauro Carvalho Chehab 
7339a0bf528SMauro Carvalho Chehab 	state->current_bandwidth = fep->bandwidth_hz;
7349a0bf528SMauro Carvalho Chehab 	dib3000mc_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->bandwidth_hz));
7359a0bf528SMauro Carvalho Chehab 
7369a0bf528SMauro Carvalho Chehab 	/* maybe the parameter has been changed */
7379a0bf528SMauro Carvalho Chehab 	state->sfn_workaround_active = buggy_sfn_workaround;
7389a0bf528SMauro Carvalho Chehab 
7399a0bf528SMauro Carvalho Chehab 	if (fe->ops.tuner_ops.set_params) {
7409a0bf528SMauro Carvalho Chehab 		fe->ops.tuner_ops.set_params(fe);
7419a0bf528SMauro Carvalho Chehab 		msleep(100);
7429a0bf528SMauro Carvalho Chehab 	}
7439a0bf528SMauro Carvalho Chehab 
7449a0bf528SMauro Carvalho Chehab 	if (fep->transmission_mode  == TRANSMISSION_MODE_AUTO ||
7459a0bf528SMauro Carvalho Chehab 	    fep->guard_interval == GUARD_INTERVAL_AUTO ||
7469a0bf528SMauro Carvalho Chehab 	    fep->modulation     == QAM_AUTO ||
7479a0bf528SMauro Carvalho Chehab 	    fep->code_rate_HP   == FEC_AUTO) {
7489a0bf528SMauro Carvalho Chehab 		int i = 1000, found;
7499a0bf528SMauro Carvalho Chehab 
7509a0bf528SMauro Carvalho Chehab 		dib3000mc_autosearch_start(fe);
7519a0bf528SMauro Carvalho Chehab 		do {
7529a0bf528SMauro Carvalho Chehab 			msleep(1);
7539a0bf528SMauro Carvalho Chehab 			found = dib3000mc_autosearch_is_irq(fe);
7549a0bf528SMauro Carvalho Chehab 		} while (found == 0 && i--);
7559a0bf528SMauro Carvalho Chehab 
7569a0bf528SMauro Carvalho Chehab 		dprintk("autosearch returns: %d\n",found);
7579a0bf528SMauro Carvalho Chehab 		if (found == 0 || found == 1)
7589a0bf528SMauro Carvalho Chehab 			return 0; // no channel found
7599a0bf528SMauro Carvalho Chehab 
7607e3e68bcSMauro Carvalho Chehab 		dib3000mc_get_frontend(fe, fep);
7619a0bf528SMauro Carvalho Chehab 	}
7629a0bf528SMauro Carvalho Chehab 
7639a0bf528SMauro Carvalho Chehab 	ret = dib3000mc_tune(fe);
7649a0bf528SMauro Carvalho Chehab 
7659a0bf528SMauro Carvalho Chehab 	/* make this a config parameter */
7669a0bf528SMauro Carvalho Chehab 	dib3000mc_set_output_mode(state, OUTMODE_MPEG2_FIFO);
7679a0bf528SMauro Carvalho Chehab 	return ret;
7689a0bf528SMauro Carvalho Chehab }
7699a0bf528SMauro Carvalho Chehab 
dib3000mc_read_status(struct dvb_frontend * fe,enum fe_status * stat)7700df289a2SMauro Carvalho Chehab static int dib3000mc_read_status(struct dvb_frontend *fe, enum fe_status *stat)
7719a0bf528SMauro Carvalho Chehab {
7729a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = fe->demodulator_priv;
7739a0bf528SMauro Carvalho Chehab 	u16 lock = dib3000mc_read_word(state, 509);
7749a0bf528SMauro Carvalho Chehab 
7759a0bf528SMauro Carvalho Chehab 	*stat = 0;
7769a0bf528SMauro Carvalho Chehab 
7779a0bf528SMauro Carvalho Chehab 	if (lock & 0x8000)
7789a0bf528SMauro Carvalho Chehab 		*stat |= FE_HAS_SIGNAL;
7799a0bf528SMauro Carvalho Chehab 	if (lock & 0x3000)
7809a0bf528SMauro Carvalho Chehab 		*stat |= FE_HAS_CARRIER;
7819a0bf528SMauro Carvalho Chehab 	if (lock & 0x0100)
7829a0bf528SMauro Carvalho Chehab 		*stat |= FE_HAS_VITERBI;
7839a0bf528SMauro Carvalho Chehab 	if (lock & 0x0010)
7849a0bf528SMauro Carvalho Chehab 		*stat |= FE_HAS_SYNC;
7859a0bf528SMauro Carvalho Chehab 	if (lock & 0x0008)
7869a0bf528SMauro Carvalho Chehab 		*stat |= FE_HAS_LOCK;
7879a0bf528SMauro Carvalho Chehab 
7889a0bf528SMauro Carvalho Chehab 	return 0;
7899a0bf528SMauro Carvalho Chehab }
7909a0bf528SMauro Carvalho Chehab 
dib3000mc_read_ber(struct dvb_frontend * fe,u32 * ber)7919a0bf528SMauro Carvalho Chehab static int dib3000mc_read_ber(struct dvb_frontend *fe, u32 *ber)
7929a0bf528SMauro Carvalho Chehab {
7939a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = fe->demodulator_priv;
7949a0bf528SMauro Carvalho Chehab 	*ber = (dib3000mc_read_word(state, 500) << 16) | dib3000mc_read_word(state, 501);
7959a0bf528SMauro Carvalho Chehab 	return 0;
7969a0bf528SMauro Carvalho Chehab }
7979a0bf528SMauro Carvalho Chehab 
dib3000mc_read_unc_blocks(struct dvb_frontend * fe,u32 * unc)7989a0bf528SMauro Carvalho Chehab static int dib3000mc_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
7999a0bf528SMauro Carvalho Chehab {
8009a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = fe->demodulator_priv;
8019a0bf528SMauro Carvalho Chehab 	*unc = dib3000mc_read_word(state, 508);
8029a0bf528SMauro Carvalho Chehab 	return 0;
8039a0bf528SMauro Carvalho Chehab }
8049a0bf528SMauro Carvalho Chehab 
dib3000mc_read_signal_strength(struct dvb_frontend * fe,u16 * strength)8059a0bf528SMauro Carvalho Chehab static int dib3000mc_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
8069a0bf528SMauro Carvalho Chehab {
8079a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = fe->demodulator_priv;
8089a0bf528SMauro Carvalho Chehab 	u16 val = dib3000mc_read_word(state, 392);
8099a0bf528SMauro Carvalho Chehab 	*strength = 65535 - val;
8109a0bf528SMauro Carvalho Chehab 	return 0;
8119a0bf528SMauro Carvalho Chehab }
8129a0bf528SMauro Carvalho Chehab 
dib3000mc_read_snr(struct dvb_frontend * fe,u16 * snr)8139a0bf528SMauro Carvalho Chehab static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr)
8149a0bf528SMauro Carvalho Chehab {
8159a0bf528SMauro Carvalho Chehab 	*snr = 0x0000;
8169a0bf528SMauro Carvalho Chehab 	return 0;
8179a0bf528SMauro Carvalho Chehab }
8189a0bf528SMauro Carvalho Chehab 
dib3000mc_fe_get_tune_settings(struct dvb_frontend * fe,struct dvb_frontend_tune_settings * tune)8199a0bf528SMauro Carvalho Chehab static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
8209a0bf528SMauro Carvalho Chehab {
8219a0bf528SMauro Carvalho Chehab 	tune->min_delay_ms = 1000;
8229a0bf528SMauro Carvalho Chehab 	return 0;
8239a0bf528SMauro Carvalho Chehab }
8249a0bf528SMauro Carvalho Chehab 
dib3000mc_release(struct dvb_frontend * fe)8259a0bf528SMauro Carvalho Chehab static void dib3000mc_release(struct dvb_frontend *fe)
8269a0bf528SMauro Carvalho Chehab {
8279a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = fe->demodulator_priv;
8289a0bf528SMauro Carvalho Chehab 	dibx000_exit_i2c_master(&state->i2c_master);
8299a0bf528SMauro Carvalho Chehab 	kfree(state);
8309a0bf528SMauro Carvalho Chehab }
8319a0bf528SMauro Carvalho Chehab 
dib3000mc_pid_control(struct dvb_frontend * fe,int index,int pid,int onoff)8329a0bf528SMauro Carvalho Chehab int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff)
8339a0bf528SMauro Carvalho Chehab {
8349a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = fe->demodulator_priv;
8359a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(state, 212 + index,  onoff ? (1 << 13) | pid : 0);
8369a0bf528SMauro Carvalho Chehab 	return 0;
8379a0bf528SMauro Carvalho Chehab }
8389a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib3000mc_pid_control);
8399a0bf528SMauro Carvalho Chehab 
dib3000mc_pid_parse(struct dvb_frontend * fe,int onoff)8409a0bf528SMauro Carvalho Chehab int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff)
8419a0bf528SMauro Carvalho Chehab {
8429a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = fe->demodulator_priv;
8439a0bf528SMauro Carvalho Chehab 	u16 tmp = dib3000mc_read_word(state, 206) & ~(1 << 4);
8449a0bf528SMauro Carvalho Chehab 	tmp |= (onoff << 4);
8459a0bf528SMauro Carvalho Chehab 	return dib3000mc_write_word(state, 206, tmp);
8469a0bf528SMauro Carvalho Chehab }
8479a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib3000mc_pid_parse);
8489a0bf528SMauro Carvalho Chehab 
dib3000mc_set_config(struct dvb_frontend * fe,struct dib3000mc_config * cfg)8499a0bf528SMauro Carvalho Chehab void dib3000mc_set_config(struct dvb_frontend *fe, struct dib3000mc_config *cfg)
8509a0bf528SMauro Carvalho Chehab {
8519a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *state = fe->demodulator_priv;
8529a0bf528SMauro Carvalho Chehab 	state->cfg = cfg;
8539a0bf528SMauro Carvalho Chehab }
8549a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib3000mc_set_config);
8559a0bf528SMauro Carvalho Chehab 
dib3000mc_i2c_enumeration(struct i2c_adapter * i2c,int no_of_demods,u8 default_addr,struct dib3000mc_config cfg[])8569a0bf528SMauro Carvalho Chehab int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib3000mc_config cfg[])
8579a0bf528SMauro Carvalho Chehab {
8589a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *dmcst;
8599a0bf528SMauro Carvalho Chehab 	int k;
8609a0bf528SMauro Carvalho Chehab 	u8 new_addr;
8619a0bf528SMauro Carvalho Chehab 
862211e6e86SColin Ian King 	static const u8 DIB3000MC_I2C_ADDRESS[] = { 20, 22, 24, 26 };
8639a0bf528SMauro Carvalho Chehab 
8649a0bf528SMauro Carvalho Chehab 	dmcst = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL);
8659a0bf528SMauro Carvalho Chehab 	if (dmcst == NULL)
8669a0bf528SMauro Carvalho Chehab 		return -ENOMEM;
8679a0bf528SMauro Carvalho Chehab 
8689a0bf528SMauro Carvalho Chehab 	dmcst->i2c_adap = i2c;
8699a0bf528SMauro Carvalho Chehab 
8709a0bf528SMauro Carvalho Chehab 	for (k = no_of_demods-1; k >= 0; k--) {
8719a0bf528SMauro Carvalho Chehab 		dmcst->cfg = &cfg[k];
8729a0bf528SMauro Carvalho Chehab 
8739a0bf528SMauro Carvalho Chehab 		/* designated i2c address */
8749a0bf528SMauro Carvalho Chehab 		new_addr          = DIB3000MC_I2C_ADDRESS[k];
8759a0bf528SMauro Carvalho Chehab 		dmcst->i2c_addr = new_addr;
8769a0bf528SMauro Carvalho Chehab 		if (dib3000mc_identify(dmcst) != 0) {
8779a0bf528SMauro Carvalho Chehab 			dmcst->i2c_addr = default_addr;
8789a0bf528SMauro Carvalho Chehab 			if (dib3000mc_identify(dmcst) != 0) {
8799a0bf528SMauro Carvalho Chehab 				dprintk("-E-  DiB3000P/MC #%d: not identified\n", k);
8809a0bf528SMauro Carvalho Chehab 				kfree(dmcst);
8819a0bf528SMauro Carvalho Chehab 				return -ENODEV;
8829a0bf528SMauro Carvalho Chehab 			}
8839a0bf528SMauro Carvalho Chehab 		}
8849a0bf528SMauro Carvalho Chehab 
8859a0bf528SMauro Carvalho Chehab 		dib3000mc_set_output_mode(dmcst, OUTMODE_MPEG2_PAR_CONT_CLK);
8869a0bf528SMauro Carvalho Chehab 
8879a0bf528SMauro Carvalho Chehab 		// set new i2c address and force divstr (Bit 1) to value 0 (Bit 0)
8889a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(dmcst, 1024, (new_addr << 3) | 0x1);
8899a0bf528SMauro Carvalho Chehab 		dmcst->i2c_addr = new_addr;
8909a0bf528SMauro Carvalho Chehab 	}
8919a0bf528SMauro Carvalho Chehab 
8929a0bf528SMauro Carvalho Chehab 	for (k = 0; k < no_of_demods; k++) {
8939a0bf528SMauro Carvalho Chehab 		dmcst->cfg = &cfg[k];
8949a0bf528SMauro Carvalho Chehab 		dmcst->i2c_addr = DIB3000MC_I2C_ADDRESS[k];
8959a0bf528SMauro Carvalho Chehab 
8969a0bf528SMauro Carvalho Chehab 		dib3000mc_write_word(dmcst, 1024, dmcst->i2c_addr << 3);
8979a0bf528SMauro Carvalho Chehab 
8989a0bf528SMauro Carvalho Chehab 		/* turn off data output */
8999a0bf528SMauro Carvalho Chehab 		dib3000mc_set_output_mode(dmcst, OUTMODE_HIGH_Z);
9009a0bf528SMauro Carvalho Chehab 	}
9019a0bf528SMauro Carvalho Chehab 
9029a0bf528SMauro Carvalho Chehab 	kfree(dmcst);
9039a0bf528SMauro Carvalho Chehab 	return 0;
9049a0bf528SMauro Carvalho Chehab }
9059a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib3000mc_i2c_enumeration);
9069a0bf528SMauro Carvalho Chehab 
907bd336e63SMax Kellermann static const struct dvb_frontend_ops dib3000mc_ops;
9089a0bf528SMauro Carvalho Chehab 
dib3000mc_attach(struct i2c_adapter * i2c_adap,u8 i2c_addr,struct dib3000mc_config * cfg)9099a0bf528SMauro Carvalho Chehab struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg)
9109a0bf528SMauro Carvalho Chehab {
9119a0bf528SMauro Carvalho Chehab 	struct dvb_frontend *demod;
9129a0bf528SMauro Carvalho Chehab 	struct dib3000mc_state *st;
9139a0bf528SMauro Carvalho Chehab 	st = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL);
9149a0bf528SMauro Carvalho Chehab 	if (st == NULL)
9159a0bf528SMauro Carvalho Chehab 		return NULL;
9169a0bf528SMauro Carvalho Chehab 
9179a0bf528SMauro Carvalho Chehab 	st->cfg = cfg;
9189a0bf528SMauro Carvalho Chehab 	st->i2c_adap = i2c_adap;
9199a0bf528SMauro Carvalho Chehab 	st->i2c_addr = i2c_addr;
9209a0bf528SMauro Carvalho Chehab 
9219a0bf528SMauro Carvalho Chehab 	demod                   = &st->demod;
9229a0bf528SMauro Carvalho Chehab 	demod->demodulator_priv = st;
9239a0bf528SMauro Carvalho Chehab 	memcpy(&st->demod.ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops));
9249a0bf528SMauro Carvalho Chehab 
9259a0bf528SMauro Carvalho Chehab 	if (dib3000mc_identify(st) != 0)
9269a0bf528SMauro Carvalho Chehab 		goto error;
9279a0bf528SMauro Carvalho Chehab 
9289a0bf528SMauro Carvalho Chehab 	dibx000_init_i2c_master(&st->i2c_master, DIB3000MC, st->i2c_adap, st->i2c_addr);
9299a0bf528SMauro Carvalho Chehab 
9309a0bf528SMauro Carvalho Chehab 	dib3000mc_write_word(st, 1037, 0x3130);
9319a0bf528SMauro Carvalho Chehab 
9329a0bf528SMauro Carvalho Chehab 	return demod;
9339a0bf528SMauro Carvalho Chehab 
9349a0bf528SMauro Carvalho Chehab error:
9359a0bf528SMauro Carvalho Chehab 	kfree(st);
9369a0bf528SMauro Carvalho Chehab 	return NULL;
9379a0bf528SMauro Carvalho Chehab }
938*86495af1SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(dib3000mc_attach);
9399a0bf528SMauro Carvalho Chehab 
940bd336e63SMax Kellermann static const struct dvb_frontend_ops dib3000mc_ops = {
9419a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBT },
9429a0bf528SMauro Carvalho Chehab 	.info = {
9439a0bf528SMauro Carvalho Chehab 		.name = "DiBcom 3000MC/P",
944f1b1eabfSMauro Carvalho Chehab 		.frequency_min_hz      =  44250 * kHz,
945f1b1eabfSMauro Carvalho Chehab 		.frequency_max_hz      = 867250 * kHz,
946f1b1eabfSMauro Carvalho Chehab 		.frequency_stepsize_hz = 62500,
9479a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_INVERSION_AUTO |
9489a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
9499a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
9509a0bf528SMauro Carvalho Chehab 			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
9519a0bf528SMauro Carvalho Chehab 			FE_CAN_TRANSMISSION_MODE_AUTO |
9529a0bf528SMauro Carvalho Chehab 			FE_CAN_GUARD_INTERVAL_AUTO |
9539a0bf528SMauro Carvalho Chehab 			FE_CAN_RECOVER |
9549a0bf528SMauro Carvalho Chehab 			FE_CAN_HIERARCHY_AUTO,
9559a0bf528SMauro Carvalho Chehab 	},
9569a0bf528SMauro Carvalho Chehab 
9579a0bf528SMauro Carvalho Chehab 	.release              = dib3000mc_release,
9589a0bf528SMauro Carvalho Chehab 
9599a0bf528SMauro Carvalho Chehab 	.init                 = dib3000mc_init,
9609a0bf528SMauro Carvalho Chehab 	.sleep                = dib3000mc_sleep,
9619a0bf528SMauro Carvalho Chehab 
9629a0bf528SMauro Carvalho Chehab 	.set_frontend         = dib3000mc_set_frontend,
9639a0bf528SMauro Carvalho Chehab 	.get_tune_settings    = dib3000mc_fe_get_tune_settings,
9649a0bf528SMauro Carvalho Chehab 	.get_frontend         = dib3000mc_get_frontend,
9659a0bf528SMauro Carvalho Chehab 
9669a0bf528SMauro Carvalho Chehab 	.read_status          = dib3000mc_read_status,
9679a0bf528SMauro Carvalho Chehab 	.read_ber             = dib3000mc_read_ber,
9689a0bf528SMauro Carvalho Chehab 	.read_signal_strength = dib3000mc_read_signal_strength,
9699a0bf528SMauro Carvalho Chehab 	.read_snr             = dib3000mc_read_snr,
9709a0bf528SMauro Carvalho Chehab 	.read_ucblocks        = dib3000mc_read_unc_blocks,
9719a0bf528SMauro Carvalho Chehab };
9729a0bf528SMauro Carvalho Chehab 
97399e44da7SPatrick Boettcher MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
9749a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("Driver for the DiBcom 3000MC/P COFDM demodulator");
9759a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
976