1a10e763bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29a0bf528SMauro Carvalho Chehab /*
39a0bf528SMauro Carvalho Chehab  * Linux-DVB Driver for DiBcom's DiB9000 and demodulator-family.
49a0bf528SMauro Carvalho Chehab  *
59a0bf528SMauro Carvalho Chehab  * Copyright (C) 2005-10 DiBcom (http://www.dibcom.fr/)
69a0bf528SMauro Carvalho Chehab  */
73dd72262SMauro Carvalho Chehab 
83dd72262SMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
93dd72262SMauro Carvalho Chehab 
109a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
119a0bf528SMauro Carvalho Chehab #include <linux/i2c.h>
129a0bf528SMauro Carvalho Chehab #include <linux/mutex.h>
139a0bf528SMauro Carvalho Chehab 
14f97fa3dcSAndy Shevchenko #include <linux/int_log.h>
15fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
169a0bf528SMauro Carvalho Chehab 
179a0bf528SMauro Carvalho Chehab #include "dib9000.h"
189a0bf528SMauro Carvalho Chehab #include "dibx000_common.h"
199a0bf528SMauro Carvalho Chehab 
209a0bf528SMauro Carvalho Chehab static int debug;
219a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
229a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
239a0bf528SMauro Carvalho Chehab 
243dd72262SMauro Carvalho Chehab #define dprintk(fmt, arg...) do {					\
253dd72262SMauro Carvalho Chehab 	if (debug)							\
263dd72262SMauro Carvalho Chehab 		printk(KERN_DEBUG pr_fmt("%s: " fmt),			\
273dd72262SMauro Carvalho Chehab 		       __func__, ##arg);				\
283dd72262SMauro Carvalho Chehab } while (0)
293dd72262SMauro Carvalho Chehab 
309a0bf528SMauro Carvalho Chehab #define MAX_NUMBER_OF_FRONTENDS 6
319a0bf528SMauro Carvalho Chehab 
329a0bf528SMauro Carvalho Chehab struct i2c_device {
339a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c_adap;
349a0bf528SMauro Carvalho Chehab 	u8 i2c_addr;
359a0bf528SMauro Carvalho Chehab 	u8 *i2c_read_buffer;
369a0bf528SMauro Carvalho Chehab 	u8 *i2c_write_buffer;
379a0bf528SMauro Carvalho Chehab };
389a0bf528SMauro Carvalho Chehab 
399a0bf528SMauro Carvalho Chehab struct dib9000_pid_ctrl {
409a0bf528SMauro Carvalho Chehab #define DIB9000_PID_FILTER_CTRL 0
419a0bf528SMauro Carvalho Chehab #define DIB9000_PID_FILTER      1
429a0bf528SMauro Carvalho Chehab 	u8 cmd;
439a0bf528SMauro Carvalho Chehab 	u8 id;
449a0bf528SMauro Carvalho Chehab 	u16 pid;
459a0bf528SMauro Carvalho Chehab 	u8 onoff;
469a0bf528SMauro Carvalho Chehab };
479a0bf528SMauro Carvalho Chehab 
489a0bf528SMauro Carvalho Chehab struct dib9000_state {
499a0bf528SMauro Carvalho Chehab 	struct i2c_device i2c;
509a0bf528SMauro Carvalho Chehab 
519a0bf528SMauro Carvalho Chehab 	struct dibx000_i2c_master i2c_master;
529a0bf528SMauro Carvalho Chehab 	struct i2c_adapter tuner_adap;
539a0bf528SMauro Carvalho Chehab 	struct i2c_adapter component_bus;
549a0bf528SMauro Carvalho Chehab 
559a0bf528SMauro Carvalho Chehab 	u16 revision;
569a0bf528SMauro Carvalho Chehab 	u8 reg_offs;
579a0bf528SMauro Carvalho Chehab 
589a0bf528SMauro Carvalho Chehab 	enum frontend_tune_state tune_state;
599a0bf528SMauro Carvalho Chehab 	u32 status;
609a0bf528SMauro Carvalho Chehab 	struct dvb_frontend_parametersContext channel_status;
619a0bf528SMauro Carvalho Chehab 
629a0bf528SMauro Carvalho Chehab 	u8 fe_id;
639a0bf528SMauro Carvalho Chehab 
649a0bf528SMauro Carvalho Chehab #define DIB9000_GPIO_DEFAULT_DIRECTIONS 0xffff
659a0bf528SMauro Carvalho Chehab 	u16 gpio_dir;
669a0bf528SMauro Carvalho Chehab #define DIB9000_GPIO_DEFAULT_VALUES     0x0000
679a0bf528SMauro Carvalho Chehab 	u16 gpio_val;
689a0bf528SMauro Carvalho Chehab #define DIB9000_GPIO_DEFAULT_PWM_POS    0xffff
699a0bf528SMauro Carvalho Chehab 	u16 gpio_pwm_pos;
709a0bf528SMauro Carvalho Chehab 
719a0bf528SMauro Carvalho Chehab 	union {			/* common for all chips */
729a0bf528SMauro Carvalho Chehab 		struct {
739a0bf528SMauro Carvalho Chehab 			u8 mobile_mode:1;
749a0bf528SMauro Carvalho Chehab 		} host;
759a0bf528SMauro Carvalho Chehab 
769a0bf528SMauro Carvalho Chehab 		struct {
779a0bf528SMauro Carvalho Chehab 			struct dib9000_fe_memory_map {
789a0bf528SMauro Carvalho Chehab 				u16 addr;
799a0bf528SMauro Carvalho Chehab 				u16 size;
809a0bf528SMauro Carvalho Chehab 			} fe_mm[18];
819a0bf528SMauro Carvalho Chehab 			u8 memcmd;
829a0bf528SMauro Carvalho Chehab 
839a0bf528SMauro Carvalho Chehab 			struct mutex mbx_if_lock;	/* to protect read/write operations */
849a0bf528SMauro Carvalho Chehab 			struct mutex mbx_lock;	/* to protect the whole mailbox handling */
859a0bf528SMauro Carvalho Chehab 
869a0bf528SMauro Carvalho Chehab 			struct mutex mem_lock;	/* to protect the memory accesses */
879a0bf528SMauro Carvalho Chehab 			struct mutex mem_mbx_lock;	/* to protect the memory-based mailbox */
889a0bf528SMauro Carvalho Chehab 
899a0bf528SMauro Carvalho Chehab #define MBX_MAX_WORDS (256 - 200 - 2)
909a0bf528SMauro Carvalho Chehab #define DIB9000_MSG_CACHE_SIZE 2
919a0bf528SMauro Carvalho Chehab 			u16 message_cache[DIB9000_MSG_CACHE_SIZE][MBX_MAX_WORDS];
929a0bf528SMauro Carvalho Chehab 			u8 fw_is_running;
939a0bf528SMauro Carvalho Chehab 		} risc;
949a0bf528SMauro Carvalho Chehab 	} platform;
959a0bf528SMauro Carvalho Chehab 
969a0bf528SMauro Carvalho Chehab 	union {			/* common for all platforms */
979a0bf528SMauro Carvalho Chehab 		struct {
989a0bf528SMauro Carvalho Chehab 			struct dib9000_config cfg;
999a0bf528SMauro Carvalho Chehab 		} d9;
1009a0bf528SMauro Carvalho Chehab 	} chip;
1019a0bf528SMauro Carvalho Chehab 
1029a0bf528SMauro Carvalho Chehab 	struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS];
1039a0bf528SMauro Carvalho Chehab 	u16 component_bus_speed;
1049a0bf528SMauro Carvalho Chehab 
1059a0bf528SMauro Carvalho Chehab 	/* for the I2C transfer */
1069a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[2];
1079a0bf528SMauro Carvalho Chehab 	u8 i2c_write_buffer[255];
1089a0bf528SMauro Carvalho Chehab 	u8 i2c_read_buffer[255];
1099a0bf528SMauro Carvalho Chehab 	struct mutex demod_lock;
1109a0bf528SMauro Carvalho Chehab 	u8 get_frontend_internal;
1119a0bf528SMauro Carvalho Chehab 	struct dib9000_pid_ctrl pid_ctrl[10];
1129a0bf528SMauro Carvalho Chehab 	s8 pid_ctrl_index; /* -1: empty list; -2: do not use the list */
1139a0bf528SMauro Carvalho Chehab };
1149a0bf528SMauro Carvalho Chehab 
1159a0bf528SMauro Carvalho Chehab static const u32 fe_info[44] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1169a0bf528SMauro Carvalho Chehab 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1179a0bf528SMauro Carvalho Chehab 	0, 0, 0, 0, 0, 0, 0, 0
1189a0bf528SMauro Carvalho Chehab };
1199a0bf528SMauro Carvalho Chehab 
1209a0bf528SMauro Carvalho Chehab enum dib9000_power_mode {
1219a0bf528SMauro Carvalho Chehab 	DIB9000_POWER_ALL = 0,
1229a0bf528SMauro Carvalho Chehab 
1239a0bf528SMauro Carvalho Chehab 	DIB9000_POWER_NO,
1249a0bf528SMauro Carvalho Chehab 	DIB9000_POWER_INTERF_ANALOG_AGC,
1259a0bf528SMauro Carvalho Chehab 	DIB9000_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD,
1269a0bf528SMauro Carvalho Chehab 	DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD,
1279a0bf528SMauro Carvalho Chehab 	DIB9000_POWER_INTERFACE_ONLY,
1289a0bf528SMauro Carvalho Chehab };
1299a0bf528SMauro Carvalho Chehab 
1309a0bf528SMauro Carvalho Chehab enum dib9000_out_messages {
1319a0bf528SMauro Carvalho Chehab 	OUT_MSG_HBM_ACK,
1329a0bf528SMauro Carvalho Chehab 	OUT_MSG_HOST_BUF_FAIL,
1339a0bf528SMauro Carvalho Chehab 	OUT_MSG_REQ_VERSION,
1349a0bf528SMauro Carvalho Chehab 	OUT_MSG_BRIDGE_I2C_W,
1359a0bf528SMauro Carvalho Chehab 	OUT_MSG_BRIDGE_I2C_R,
1369a0bf528SMauro Carvalho Chehab 	OUT_MSG_BRIDGE_APB_W,
1379a0bf528SMauro Carvalho Chehab 	OUT_MSG_BRIDGE_APB_R,
1389a0bf528SMauro Carvalho Chehab 	OUT_MSG_SCAN_CHANNEL,
1399a0bf528SMauro Carvalho Chehab 	OUT_MSG_MONIT_DEMOD,
1409a0bf528SMauro Carvalho Chehab 	OUT_MSG_CONF_GPIO,
1419a0bf528SMauro Carvalho Chehab 	OUT_MSG_DEBUG_HELP,
1429a0bf528SMauro Carvalho Chehab 	OUT_MSG_SUBBAND_SEL,
1439a0bf528SMauro Carvalho Chehab 	OUT_MSG_ENABLE_TIME_SLICE,
1449a0bf528SMauro Carvalho Chehab 	OUT_MSG_FE_FW_DL,
1459a0bf528SMauro Carvalho Chehab 	OUT_MSG_FE_CHANNEL_SEARCH,
1469a0bf528SMauro Carvalho Chehab 	OUT_MSG_FE_CHANNEL_TUNE,
1479a0bf528SMauro Carvalho Chehab 	OUT_MSG_FE_SLEEP,
1489a0bf528SMauro Carvalho Chehab 	OUT_MSG_FE_SYNC,
1499a0bf528SMauro Carvalho Chehab 	OUT_MSG_CTL_MONIT,
1509a0bf528SMauro Carvalho Chehab 
1519a0bf528SMauro Carvalho Chehab 	OUT_MSG_CONF_SVC,
1529a0bf528SMauro Carvalho Chehab 	OUT_MSG_SET_HBM,
1539a0bf528SMauro Carvalho Chehab 	OUT_MSG_INIT_DEMOD,
1549a0bf528SMauro Carvalho Chehab 	OUT_MSG_ENABLE_DIVERSITY,
1559a0bf528SMauro Carvalho Chehab 	OUT_MSG_SET_OUTPUT_MODE,
1569a0bf528SMauro Carvalho Chehab 	OUT_MSG_SET_PRIORITARY_CHANNEL,
1579a0bf528SMauro Carvalho Chehab 	OUT_MSG_ACK_FRG,
1589a0bf528SMauro Carvalho Chehab 	OUT_MSG_INIT_PMU,
1599a0bf528SMauro Carvalho Chehab };
1609a0bf528SMauro Carvalho Chehab 
1619a0bf528SMauro Carvalho Chehab enum dib9000_in_messages {
1629a0bf528SMauro Carvalho Chehab 	IN_MSG_DATA,
1639a0bf528SMauro Carvalho Chehab 	IN_MSG_FRAME_INFO,
1649a0bf528SMauro Carvalho Chehab 	IN_MSG_CTL_MONIT,
1659a0bf528SMauro Carvalho Chehab 	IN_MSG_ACK_FREE_ITEM,
1669a0bf528SMauro Carvalho Chehab 	IN_MSG_DEBUG_BUF,
1679a0bf528SMauro Carvalho Chehab 	IN_MSG_MPE_MONITOR,
1689a0bf528SMauro Carvalho Chehab 	IN_MSG_RAWTS_MONITOR,
1699a0bf528SMauro Carvalho Chehab 	IN_MSG_END_BRIDGE_I2C_RW,
1709a0bf528SMauro Carvalho Chehab 	IN_MSG_END_BRIDGE_APB_RW,
1719a0bf528SMauro Carvalho Chehab 	IN_MSG_VERSION,
1729a0bf528SMauro Carvalho Chehab 	IN_MSG_END_OF_SCAN,
1739a0bf528SMauro Carvalho Chehab 	IN_MSG_MONIT_DEMOD,
1749a0bf528SMauro Carvalho Chehab 	IN_MSG_ERROR,
1759a0bf528SMauro Carvalho Chehab 	IN_MSG_FE_FW_DL_DONE,
1769a0bf528SMauro Carvalho Chehab 	IN_MSG_EVENT,
1779a0bf528SMauro Carvalho Chehab 	IN_MSG_ACK_CHANGE_SVC,
1789a0bf528SMauro Carvalho Chehab 	IN_MSG_HBM_PROF,
1799a0bf528SMauro Carvalho Chehab };
1809a0bf528SMauro Carvalho Chehab 
1819a0bf528SMauro Carvalho Chehab /* memory_access requests */
1829a0bf528SMauro Carvalho Chehab #define FE_MM_W_CHANNEL                   0
1839a0bf528SMauro Carvalho Chehab #define FE_MM_W_FE_INFO                   1
1849a0bf528SMauro Carvalho Chehab #define FE_MM_RW_SYNC                     2
1859a0bf528SMauro Carvalho Chehab 
1869a0bf528SMauro Carvalho Chehab #define FE_SYNC_CHANNEL          1
1879a0bf528SMauro Carvalho Chehab #define FE_SYNC_W_GENERIC_MONIT	 2
1889a0bf528SMauro Carvalho Chehab #define FE_SYNC_COMPONENT_ACCESS 3
1899a0bf528SMauro Carvalho Chehab 
1909a0bf528SMauro Carvalho Chehab #define FE_MM_R_CHANNEL_SEARCH_STATE      3
1919a0bf528SMauro Carvalho Chehab #define FE_MM_R_CHANNEL_UNION_CONTEXT     4
1929a0bf528SMauro Carvalho Chehab #define FE_MM_R_FE_INFO                   5
1939a0bf528SMauro Carvalho Chehab #define FE_MM_R_FE_MONITOR                6
1949a0bf528SMauro Carvalho Chehab 
1959a0bf528SMauro Carvalho Chehab #define FE_MM_W_CHANNEL_HEAD              7
1969a0bf528SMauro Carvalho Chehab #define FE_MM_W_CHANNEL_UNION             8
1979a0bf528SMauro Carvalho Chehab #define FE_MM_W_CHANNEL_CONTEXT           9
1989a0bf528SMauro Carvalho Chehab #define FE_MM_R_CHANNEL_UNION            10
1999a0bf528SMauro Carvalho Chehab #define FE_MM_R_CHANNEL_CONTEXT          11
2009a0bf528SMauro Carvalho Chehab #define FE_MM_R_CHANNEL_TUNE_STATE       12
2019a0bf528SMauro Carvalho Chehab 
2029a0bf528SMauro Carvalho Chehab #define FE_MM_R_GENERIC_MONITORING_SIZE	 13
2039a0bf528SMauro Carvalho Chehab #define FE_MM_W_GENERIC_MONITORING	     14
2049a0bf528SMauro Carvalho Chehab #define FE_MM_R_GENERIC_MONITORING	     15
2059a0bf528SMauro Carvalho Chehab 
2069a0bf528SMauro Carvalho Chehab #define FE_MM_W_COMPONENT_ACCESS         16
2079a0bf528SMauro Carvalho Chehab #define FE_MM_RW_COMPONENT_ACCESS_BUFFER 17
2089a0bf528SMauro Carvalho Chehab static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, u8 * b, u32 len);
2099a0bf528SMauro Carvalho Chehab static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len);
2109a0bf528SMauro Carvalho Chehab 
to_fw_output_mode(u16 mode)2119a0bf528SMauro Carvalho Chehab static u16 to_fw_output_mode(u16 mode)
2129a0bf528SMauro Carvalho Chehab {
2139a0bf528SMauro Carvalho Chehab 	switch (mode) {
2149a0bf528SMauro Carvalho Chehab 	case OUTMODE_HIGH_Z:
2159a0bf528SMauro Carvalho Chehab 		return 0;
2169a0bf528SMauro Carvalho Chehab 	case OUTMODE_MPEG2_PAR_GATED_CLK:
2179a0bf528SMauro Carvalho Chehab 		return 4;
2189a0bf528SMauro Carvalho Chehab 	case OUTMODE_MPEG2_PAR_CONT_CLK:
2199a0bf528SMauro Carvalho Chehab 		return 8;
2209a0bf528SMauro Carvalho Chehab 	case OUTMODE_MPEG2_SERIAL:
2219a0bf528SMauro Carvalho Chehab 		return 16;
2229a0bf528SMauro Carvalho Chehab 	case OUTMODE_DIVERSITY:
2239a0bf528SMauro Carvalho Chehab 		return 128;
2249a0bf528SMauro Carvalho Chehab 	case OUTMODE_MPEG2_FIFO:
2259a0bf528SMauro Carvalho Chehab 		return 2;
2269a0bf528SMauro Carvalho Chehab 	case OUTMODE_ANALOG_ADC:
2279a0bf528SMauro Carvalho Chehab 		return 1;
2289a0bf528SMauro Carvalho Chehab 	default:
2299a0bf528SMauro Carvalho Chehab 		return 0;
2309a0bf528SMauro Carvalho Chehab 	}
2319a0bf528SMauro Carvalho Chehab }
2329a0bf528SMauro Carvalho Chehab 
dib9000_read16_attr(struct dib9000_state * state,u16 reg,u8 * b,u32 len,u16 attribute)23388d0518cSMauro Carvalho Chehab static int dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 *b, u32 len, u16 attribute)
2349a0bf528SMauro Carvalho Chehab {
2359a0bf528SMauro Carvalho Chehab 	u32 chunk_size = 126;
2369a0bf528SMauro Carvalho Chehab 	u32 l;
2379a0bf528SMauro Carvalho Chehab 	int ret;
2389a0bf528SMauro Carvalho Chehab 
2399a0bf528SMauro Carvalho Chehab 	if (state->platform.risc.fw_is_running && (reg < 1024))
2409a0bf528SMauro Carvalho Chehab 		return dib9000_risc_apb_access_read(state, reg, attribute, NULL, 0, b, len);
2419a0bf528SMauro Carvalho Chehab 
2429a0bf528SMauro Carvalho Chehab 	memset(state->msg, 0, 2 * sizeof(struct i2c_msg));
2439a0bf528SMauro Carvalho Chehab 	state->msg[0].addr = state->i2c.i2c_addr >> 1;
2449a0bf528SMauro Carvalho Chehab 	state->msg[0].flags = 0;
2459a0bf528SMauro Carvalho Chehab 	state->msg[0].buf = state->i2c_write_buffer;
2469a0bf528SMauro Carvalho Chehab 	state->msg[0].len = 2;
2479a0bf528SMauro Carvalho Chehab 	state->msg[1].addr = state->i2c.i2c_addr >> 1;
2489a0bf528SMauro Carvalho Chehab 	state->msg[1].flags = I2C_M_RD;
2499a0bf528SMauro Carvalho Chehab 	state->msg[1].buf = b;
2509a0bf528SMauro Carvalho Chehab 	state->msg[1].len = len;
2519a0bf528SMauro Carvalho Chehab 
2529a0bf528SMauro Carvalho Chehab 	state->i2c_write_buffer[0] = reg >> 8;
2539a0bf528SMauro Carvalho Chehab 	state->i2c_write_buffer[1] = reg & 0xff;
2549a0bf528SMauro Carvalho Chehab 
2559a0bf528SMauro Carvalho Chehab 	if (attribute & DATA_BUS_ACCESS_MODE_8BIT)
2569a0bf528SMauro Carvalho Chehab 		state->i2c_write_buffer[0] |= (1 << 5);
2579a0bf528SMauro Carvalho Chehab 	if (attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
2589a0bf528SMauro Carvalho Chehab 		state->i2c_write_buffer[0] |= (1 << 4);
2599a0bf528SMauro Carvalho Chehab 
2609a0bf528SMauro Carvalho Chehab 	do {
261e59a9e50Szhaoxiao 		l = min(len, chunk_size);
2629a0bf528SMauro Carvalho Chehab 		state->msg[1].len = l;
2639a0bf528SMauro Carvalho Chehab 		state->msg[1].buf = b;
2649a0bf528SMauro Carvalho Chehab 		ret = i2c_transfer(state->i2c.i2c_adap, state->msg, 2) != 2 ? -EREMOTEIO : 0;
2659a0bf528SMauro Carvalho Chehab 		if (ret != 0) {
2663dd72262SMauro Carvalho Chehab 			dprintk("i2c read error on %d\n", reg);
2679a0bf528SMauro Carvalho Chehab 			return -EREMOTEIO;
2689a0bf528SMauro Carvalho Chehab 		}
2699a0bf528SMauro Carvalho Chehab 
2709a0bf528SMauro Carvalho Chehab 		b += l;
2719a0bf528SMauro Carvalho Chehab 		len -= l;
2729a0bf528SMauro Carvalho Chehab 
2739a0bf528SMauro Carvalho Chehab 		if (!(attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT))
2749a0bf528SMauro Carvalho Chehab 			reg += l / 2;
2759a0bf528SMauro Carvalho Chehab 	} while ((ret == 0) && len);
2769a0bf528SMauro Carvalho Chehab 
2779a0bf528SMauro Carvalho Chehab 	return 0;
2789a0bf528SMauro Carvalho Chehab }
2799a0bf528SMauro Carvalho Chehab 
dib9000_i2c_read16(struct i2c_device * i2c,u16 reg)2809a0bf528SMauro Carvalho Chehab static u16 dib9000_i2c_read16(struct i2c_device *i2c, u16 reg)
2819a0bf528SMauro Carvalho Chehab {
2829a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[2] = {
2839a0bf528SMauro Carvalho Chehab 		{.addr = i2c->i2c_addr >> 1, .flags = 0,
2849a0bf528SMauro Carvalho Chehab 			.buf = i2c->i2c_write_buffer, .len = 2},
2859a0bf528SMauro Carvalho Chehab 		{.addr = i2c->i2c_addr >> 1, .flags = I2C_M_RD,
2869a0bf528SMauro Carvalho Chehab 			.buf = i2c->i2c_read_buffer, .len = 2},
2879a0bf528SMauro Carvalho Chehab 	};
2889a0bf528SMauro Carvalho Chehab 
2899a0bf528SMauro Carvalho Chehab 	i2c->i2c_write_buffer[0] = reg >> 8;
2909a0bf528SMauro Carvalho Chehab 	i2c->i2c_write_buffer[1] = reg & 0xff;
2919a0bf528SMauro Carvalho Chehab 
2929a0bf528SMauro Carvalho Chehab 	if (i2c_transfer(i2c->i2c_adap, msg, 2) != 2) {
2933dd72262SMauro Carvalho Chehab 		dprintk("read register %x error\n", reg);
2949a0bf528SMauro Carvalho Chehab 		return 0;
2959a0bf528SMauro Carvalho Chehab 	}
2969a0bf528SMauro Carvalho Chehab 
2979a0bf528SMauro Carvalho Chehab 	return (i2c->i2c_read_buffer[0] << 8) | i2c->i2c_read_buffer[1];
2989a0bf528SMauro Carvalho Chehab }
2999a0bf528SMauro Carvalho Chehab 
dib9000_read_word(struct dib9000_state * state,u16 reg)3009a0bf528SMauro Carvalho Chehab static inline u16 dib9000_read_word(struct dib9000_state *state, u16 reg)
3019a0bf528SMauro Carvalho Chehab {
3029a0bf528SMauro Carvalho Chehab 	if (dib9000_read16_attr(state, reg, state->i2c_read_buffer, 2, 0) != 0)
3039a0bf528SMauro Carvalho Chehab 		return 0;
3049a0bf528SMauro Carvalho Chehab 	return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
3059a0bf528SMauro Carvalho Chehab }
3069a0bf528SMauro Carvalho Chehab 
dib9000_read_word_attr(struct dib9000_state * state,u16 reg,u16 attribute)3079a0bf528SMauro Carvalho Chehab static inline u16 dib9000_read_word_attr(struct dib9000_state *state, u16 reg, u16 attribute)
3089a0bf528SMauro Carvalho Chehab {
3099a0bf528SMauro Carvalho Chehab 	if (dib9000_read16_attr(state, reg, state->i2c_read_buffer, 2,
3109a0bf528SMauro Carvalho Chehab 				attribute) != 0)
3119a0bf528SMauro Carvalho Chehab 		return 0;
3129a0bf528SMauro Carvalho Chehab 	return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
3139a0bf528SMauro Carvalho Chehab }
3149a0bf528SMauro Carvalho Chehab 
3159a0bf528SMauro Carvalho Chehab #define dib9000_read16_noinc_attr(state, reg, b, len, attribute) dib9000_read16_attr(state, reg, b, len, (attribute) | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
3169a0bf528SMauro Carvalho Chehab 
dib9000_write16_attr(struct dib9000_state * state,u16 reg,const u8 * buf,u32 len,u16 attribute)31788d0518cSMauro Carvalho Chehab static int dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 *buf, u32 len, u16 attribute)
3189a0bf528SMauro Carvalho Chehab {
3199a0bf528SMauro Carvalho Chehab 	u32 chunk_size = 126;
3209a0bf528SMauro Carvalho Chehab 	u32 l;
3219a0bf528SMauro Carvalho Chehab 	int ret;
3229a0bf528SMauro Carvalho Chehab 
3239a0bf528SMauro Carvalho Chehab 	if (state->platform.risc.fw_is_running && (reg < 1024)) {
3249a0bf528SMauro Carvalho Chehab 		if (dib9000_risc_apb_access_write
3259a0bf528SMauro Carvalho Chehab 		    (state, reg, DATA_BUS_ACCESS_MODE_16BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | attribute, buf, len) != 0)
3269a0bf528SMauro Carvalho Chehab 			return -EINVAL;
3279a0bf528SMauro Carvalho Chehab 		return 0;
3289a0bf528SMauro Carvalho Chehab 	}
3299a0bf528SMauro Carvalho Chehab 
3309a0bf528SMauro Carvalho Chehab 	memset(&state->msg[0], 0, sizeof(struct i2c_msg));
3319a0bf528SMauro Carvalho Chehab 	state->msg[0].addr = state->i2c.i2c_addr >> 1;
3329a0bf528SMauro Carvalho Chehab 	state->msg[0].flags = 0;
3339a0bf528SMauro Carvalho Chehab 	state->msg[0].buf = state->i2c_write_buffer;
3349a0bf528SMauro Carvalho Chehab 	state->msg[0].len = len + 2;
3359a0bf528SMauro Carvalho Chehab 
3369a0bf528SMauro Carvalho Chehab 	state->i2c_write_buffer[0] = (reg >> 8) & 0xff;
3379a0bf528SMauro Carvalho Chehab 	state->i2c_write_buffer[1] = (reg) & 0xff;
3389a0bf528SMauro Carvalho Chehab 
3399a0bf528SMauro Carvalho Chehab 	if (attribute & DATA_BUS_ACCESS_MODE_8BIT)
3409a0bf528SMauro Carvalho Chehab 		state->i2c_write_buffer[0] |= (1 << 5);
3419a0bf528SMauro Carvalho Chehab 	if (attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
3429a0bf528SMauro Carvalho Chehab 		state->i2c_write_buffer[0] |= (1 << 4);
3439a0bf528SMauro Carvalho Chehab 
3449a0bf528SMauro Carvalho Chehab 	do {
345e59a9e50Szhaoxiao 		l = min(len, chunk_size);
3469a0bf528SMauro Carvalho Chehab 		state->msg[0].len = l + 2;
3479a0bf528SMauro Carvalho Chehab 		memcpy(&state->i2c_write_buffer[2], buf, l);
3489a0bf528SMauro Carvalho Chehab 
3499a0bf528SMauro Carvalho Chehab 		ret = i2c_transfer(state->i2c.i2c_adap, state->msg, 1) != 1 ? -EREMOTEIO : 0;
3509a0bf528SMauro Carvalho Chehab 
3519a0bf528SMauro Carvalho Chehab 		buf += l;
3529a0bf528SMauro Carvalho Chehab 		len -= l;
3539a0bf528SMauro Carvalho Chehab 
3549a0bf528SMauro Carvalho Chehab 		if (!(attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT))
3559a0bf528SMauro Carvalho Chehab 			reg += l / 2;
3569a0bf528SMauro Carvalho Chehab 	} while ((ret == 0) && len);
3579a0bf528SMauro Carvalho Chehab 
3589a0bf528SMauro Carvalho Chehab 	return ret;
3599a0bf528SMauro Carvalho Chehab }
3609a0bf528SMauro Carvalho Chehab 
dib9000_i2c_write16(struct i2c_device * i2c,u16 reg,u16 val)3619a0bf528SMauro Carvalho Chehab static int dib9000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val)
3629a0bf528SMauro Carvalho Chehab {
3639a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {
3649a0bf528SMauro Carvalho Chehab 		.addr = i2c->i2c_addr >> 1, .flags = 0,
3659a0bf528SMauro Carvalho Chehab 		.buf = i2c->i2c_write_buffer, .len = 4
3669a0bf528SMauro Carvalho Chehab 	};
3679a0bf528SMauro Carvalho Chehab 
3689a0bf528SMauro Carvalho Chehab 	i2c->i2c_write_buffer[0] = (reg >> 8) & 0xff;
3699a0bf528SMauro Carvalho Chehab 	i2c->i2c_write_buffer[1] = reg & 0xff;
3709a0bf528SMauro Carvalho Chehab 	i2c->i2c_write_buffer[2] = (val >> 8) & 0xff;
3719a0bf528SMauro Carvalho Chehab 	i2c->i2c_write_buffer[3] = val & 0xff;
3729a0bf528SMauro Carvalho Chehab 
3739a0bf528SMauro Carvalho Chehab 	return i2c_transfer(i2c->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
3749a0bf528SMauro Carvalho Chehab }
3759a0bf528SMauro Carvalho Chehab 
dib9000_write_word(struct dib9000_state * state,u16 reg,u16 val)3769a0bf528SMauro Carvalho Chehab static inline int dib9000_write_word(struct dib9000_state *state, u16 reg, u16 val)
3779a0bf528SMauro Carvalho Chehab {
3789a0bf528SMauro Carvalho Chehab 	u8 b[2] = { val >> 8, val & 0xff };
3799a0bf528SMauro Carvalho Chehab 	return dib9000_write16_attr(state, reg, b, 2, 0);
3809a0bf528SMauro Carvalho Chehab }
3819a0bf528SMauro Carvalho Chehab 
dib9000_write_word_attr(struct dib9000_state * state,u16 reg,u16 val,u16 attribute)3829a0bf528SMauro Carvalho Chehab static inline int dib9000_write_word_attr(struct dib9000_state *state, u16 reg, u16 val, u16 attribute)
3839a0bf528SMauro Carvalho Chehab {
3849a0bf528SMauro Carvalho Chehab 	u8 b[2] = { val >> 8, val & 0xff };
3859a0bf528SMauro Carvalho Chehab 	return dib9000_write16_attr(state, reg, b, 2, attribute);
3869a0bf528SMauro Carvalho Chehab }
3879a0bf528SMauro Carvalho Chehab 
3889a0bf528SMauro Carvalho Chehab #define dib9000_write(state, reg, buf, len) dib9000_write16_attr(state, reg, buf, len, 0)
3899a0bf528SMauro Carvalho Chehab #define dib9000_write16_noinc(state, reg, buf, len) dib9000_write16_attr(state, reg, buf, len, DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
3909a0bf528SMauro Carvalho Chehab #define dib9000_write16_noinc_attr(state, reg, buf, len, attribute) dib9000_write16_attr(state, reg, buf, len, DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | (attribute))
3919a0bf528SMauro Carvalho Chehab 
3929a0bf528SMauro Carvalho Chehab #define dib9000_mbx_send(state, id, data, len) dib9000_mbx_send_attr(state, id, data, len, 0)
3939a0bf528SMauro Carvalho Chehab #define dib9000_mbx_get_message(state, id, msg, len) dib9000_mbx_get_message_attr(state, id, msg, len, 0)
3949a0bf528SMauro Carvalho Chehab 
3959a0bf528SMauro Carvalho Chehab #define MAC_IRQ      (1 << 1)
3969a0bf528SMauro Carvalho Chehab #define IRQ_POL_MSK  (1 << 4)
3979a0bf528SMauro Carvalho Chehab 
3989a0bf528SMauro Carvalho Chehab #define dib9000_risc_mem_read_chunks(state, b, len) dib9000_read16_attr(state, 1063, b, len, DATA_BUS_ACCESS_MODE_8BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
3999a0bf528SMauro Carvalho Chehab #define dib9000_risc_mem_write_chunks(state, buf, len) dib9000_write16_attr(state, 1063, buf, len, DATA_BUS_ACCESS_MODE_8BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
4009a0bf528SMauro Carvalho Chehab 
dib9000_risc_mem_setup_cmd(struct dib9000_state * state,u32 addr,u32 len,u8 reading)4019a0bf528SMauro Carvalho Chehab static void dib9000_risc_mem_setup_cmd(struct dib9000_state *state, u32 addr, u32 len, u8 reading)
4029a0bf528SMauro Carvalho Chehab {
4039a0bf528SMauro Carvalho Chehab 	u8 b[14] = { 0 };
4049a0bf528SMauro Carvalho Chehab 
4059a0bf528SMauro Carvalho Chehab /*      dprintk("%d memcmd: %d %d %d\n", state->fe_id, addr, addr+len, len); */
4069a0bf528SMauro Carvalho Chehab /*      b[0] = 0 << 7; */
4079a0bf528SMauro Carvalho Chehab 	b[1] = 1;
4089a0bf528SMauro Carvalho Chehab 
4099a0bf528SMauro Carvalho Chehab /*      b[2] = 0; */
4109a0bf528SMauro Carvalho Chehab /*      b[3] = 0; */
4119a0bf528SMauro Carvalho Chehab 	b[4] = (u8) (addr >> 8);
4129a0bf528SMauro Carvalho Chehab 	b[5] = (u8) (addr & 0xff);
4139a0bf528SMauro Carvalho Chehab 
4149a0bf528SMauro Carvalho Chehab /*      b[10] = 0; */
4159a0bf528SMauro Carvalho Chehab /*      b[11] = 0; */
4169a0bf528SMauro Carvalho Chehab 	b[12] = (u8) (addr >> 8);
4179a0bf528SMauro Carvalho Chehab 	b[13] = (u8) (addr & 0xff);
4189a0bf528SMauro Carvalho Chehab 
4199a0bf528SMauro Carvalho Chehab 	addr += len;
4209a0bf528SMauro Carvalho Chehab /*      b[6] = 0; */
4219a0bf528SMauro Carvalho Chehab /*      b[7] = 0; */
4229a0bf528SMauro Carvalho Chehab 	b[8] = (u8) (addr >> 8);
4239a0bf528SMauro Carvalho Chehab 	b[9] = (u8) (addr & 0xff);
4249a0bf528SMauro Carvalho Chehab 
4259a0bf528SMauro Carvalho Chehab 	dib9000_write(state, 1056, b, 14);
4269a0bf528SMauro Carvalho Chehab 	if (reading)
4279a0bf528SMauro Carvalho Chehab 		dib9000_write_word(state, 1056, (1 << 15) | 1);
4289a0bf528SMauro Carvalho Chehab 	state->platform.risc.memcmd = -1;	/* if it was called directly reset it - to force a future setup-call to set it */
4299a0bf528SMauro Carvalho Chehab }
4309a0bf528SMauro Carvalho Chehab 
dib9000_risc_mem_setup(struct dib9000_state * state,u8 cmd)4319a0bf528SMauro Carvalho Chehab static void dib9000_risc_mem_setup(struct dib9000_state *state, u8 cmd)
4329a0bf528SMauro Carvalho Chehab {
4339a0bf528SMauro Carvalho Chehab 	struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd & 0x7f];
4349a0bf528SMauro Carvalho Chehab 	/* decide whether we need to "refresh" the memory controller */
4359a0bf528SMauro Carvalho Chehab 	if (state->platform.risc.memcmd == cmd &&	/* same command */
4369a0bf528SMauro Carvalho Chehab 	    !(cmd & 0x80 && m->size < 67))	/* and we do not want to read something with less than 67 bytes looping - working around a bug in the memory controller */
4379a0bf528SMauro Carvalho Chehab 		return;
4389a0bf528SMauro Carvalho Chehab 	dib9000_risc_mem_setup_cmd(state, m->addr, m->size, cmd & 0x80);
4399a0bf528SMauro Carvalho Chehab 	state->platform.risc.memcmd = cmd;
4409a0bf528SMauro Carvalho Chehab }
4419a0bf528SMauro Carvalho Chehab 
dib9000_risc_mem_read(struct dib9000_state * state,u8 cmd,u8 * b,u16 len)4429a0bf528SMauro Carvalho Chehab static int dib9000_risc_mem_read(struct dib9000_state *state, u8 cmd, u8 * b, u16 len)
4439a0bf528SMauro Carvalho Chehab {
4449a0bf528SMauro Carvalho Chehab 	if (!state->platform.risc.fw_is_running)
4459a0bf528SMauro Carvalho Chehab 		return -EIO;
4469a0bf528SMauro Carvalho Chehab 
4479a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) {
4483dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
4499a0bf528SMauro Carvalho Chehab 		return -EINTR;
4509a0bf528SMauro Carvalho Chehab 	}
4519a0bf528SMauro Carvalho Chehab 	dib9000_risc_mem_setup(state, cmd | 0x80);
4529a0bf528SMauro Carvalho Chehab 	dib9000_risc_mem_read_chunks(state, b, len);
4539a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->platform.risc.mem_lock);
4549a0bf528SMauro Carvalho Chehab 	return 0;
4559a0bf528SMauro Carvalho Chehab }
4569a0bf528SMauro Carvalho Chehab 
dib9000_risc_mem_write(struct dib9000_state * state,u8 cmd,const u8 * b)4579a0bf528SMauro Carvalho Chehab static int dib9000_risc_mem_write(struct dib9000_state *state, u8 cmd, const u8 * b)
4589a0bf528SMauro Carvalho Chehab {
4599a0bf528SMauro Carvalho Chehab 	struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd];
4609a0bf528SMauro Carvalho Chehab 	if (!state->platform.risc.fw_is_running)
4619a0bf528SMauro Carvalho Chehab 		return -EIO;
4629a0bf528SMauro Carvalho Chehab 
4639a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) {
4643dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
4659a0bf528SMauro Carvalho Chehab 		return -EINTR;
4669a0bf528SMauro Carvalho Chehab 	}
4679a0bf528SMauro Carvalho Chehab 	dib9000_risc_mem_setup(state, cmd);
4689a0bf528SMauro Carvalho Chehab 	dib9000_risc_mem_write_chunks(state, b, m->size);
4699a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->platform.risc.mem_lock);
4709a0bf528SMauro Carvalho Chehab 	return 0;
4719a0bf528SMauro Carvalho Chehab }
4729a0bf528SMauro Carvalho Chehab 
dib9000_firmware_download(struct dib9000_state * state,u8 risc_id,u16 key,const u8 * code,u32 len)4739a0bf528SMauro Carvalho Chehab static int dib9000_firmware_download(struct dib9000_state *state, u8 risc_id, u16 key, const u8 * code, u32 len)
4749a0bf528SMauro Carvalho Chehab {
4759a0bf528SMauro Carvalho Chehab 	u16 offs;
4769a0bf528SMauro Carvalho Chehab 
4779a0bf528SMauro Carvalho Chehab 	if (risc_id == 1)
4789a0bf528SMauro Carvalho Chehab 		offs = 16;
4799a0bf528SMauro Carvalho Chehab 	else
4809a0bf528SMauro Carvalho Chehab 		offs = 0;
4819a0bf528SMauro Carvalho Chehab 
4829a0bf528SMauro Carvalho Chehab 	/* config crtl reg */
4839a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1024 + offs, 0x000f);
4849a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1025 + offs, 0);
4859a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1031 + offs, key);
4869a0bf528SMauro Carvalho Chehab 
4873dd72262SMauro Carvalho Chehab 	dprintk("going to download %dB of microcode\n", len);
4889a0bf528SMauro Carvalho Chehab 	if (dib9000_write16_noinc(state, 1026 + offs, (u8 *) code, (u16) len) != 0) {
4893dd72262SMauro Carvalho Chehab 		dprintk("error while downloading microcode for RISC %c\n", 'A' + risc_id);
4909a0bf528SMauro Carvalho Chehab 		return -EIO;
4919a0bf528SMauro Carvalho Chehab 	}
4929a0bf528SMauro Carvalho Chehab 
4933dd72262SMauro Carvalho Chehab 	dprintk("Microcode for RISC %c loaded\n", 'A' + risc_id);
4949a0bf528SMauro Carvalho Chehab 
4959a0bf528SMauro Carvalho Chehab 	return 0;
4969a0bf528SMauro Carvalho Chehab }
4979a0bf528SMauro Carvalho Chehab 
dib9000_mbx_host_init(struct dib9000_state * state,u8 risc_id)4989a0bf528SMauro Carvalho Chehab static int dib9000_mbx_host_init(struct dib9000_state *state, u8 risc_id)
4999a0bf528SMauro Carvalho Chehab {
5009a0bf528SMauro Carvalho Chehab 	u16 mbox_offs;
5019a0bf528SMauro Carvalho Chehab 	u16 reset_reg;
5029a0bf528SMauro Carvalho Chehab 	u16 tries = 1000;
5039a0bf528SMauro Carvalho Chehab 
5049a0bf528SMauro Carvalho Chehab 	if (risc_id == 1)
5059a0bf528SMauro Carvalho Chehab 		mbox_offs = 16;
5069a0bf528SMauro Carvalho Chehab 	else
5079a0bf528SMauro Carvalho Chehab 		mbox_offs = 0;
5089a0bf528SMauro Carvalho Chehab 
5099a0bf528SMauro Carvalho Chehab 	/* Reset mailbox  */
5109a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1027 + mbox_offs, 0x8000);
5119a0bf528SMauro Carvalho Chehab 
5129a0bf528SMauro Carvalho Chehab 	/* Read reset status */
5139a0bf528SMauro Carvalho Chehab 	do {
5149a0bf528SMauro Carvalho Chehab 		reset_reg = dib9000_read_word(state, 1027 + mbox_offs);
5159a0bf528SMauro Carvalho Chehab 		msleep(100);
5169a0bf528SMauro Carvalho Chehab 	} while ((reset_reg & 0x8000) && --tries);
5179a0bf528SMauro Carvalho Chehab 
5189a0bf528SMauro Carvalho Chehab 	if (reset_reg & 0x8000) {
5193dd72262SMauro Carvalho Chehab 		dprintk("MBX: init ERROR, no response from RISC %c\n", 'A' + risc_id);
5209a0bf528SMauro Carvalho Chehab 		return -EIO;
5219a0bf528SMauro Carvalho Chehab 	}
5223dd72262SMauro Carvalho Chehab 	dprintk("MBX: initialized\n");
5239a0bf528SMauro Carvalho Chehab 	return 0;
5249a0bf528SMauro Carvalho Chehab }
5259a0bf528SMauro Carvalho Chehab 
5269a0bf528SMauro Carvalho Chehab #define MAX_MAILBOX_TRY 100
dib9000_mbx_send_attr(struct dib9000_state * state,u8 id,u16 * data,u8 len,u16 attr)5279a0bf528SMauro Carvalho Chehab static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, u8 len, u16 attr)
5289a0bf528SMauro Carvalho Chehab {
5299a0bf528SMauro Carvalho Chehab 	u8 *d, b[2];
5309a0bf528SMauro Carvalho Chehab 	u16 tmp;
5319a0bf528SMauro Carvalho Chehab 	u16 size;
5329a0bf528SMauro Carvalho Chehab 	u32 i;
5339a0bf528SMauro Carvalho Chehab 	int ret = 0;
5349a0bf528SMauro Carvalho Chehab 
5359a0bf528SMauro Carvalho Chehab 	if (!state->platform.risc.fw_is_running)
5369a0bf528SMauro Carvalho Chehab 		return -EINVAL;
5379a0bf528SMauro Carvalho Chehab 
5389a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) {
5393dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
5409a0bf528SMauro Carvalho Chehab 		return -EINTR;
5419a0bf528SMauro Carvalho Chehab 	}
5429a0bf528SMauro Carvalho Chehab 	tmp = MAX_MAILBOX_TRY;
5439a0bf528SMauro Carvalho Chehab 	do {
5449a0bf528SMauro Carvalho Chehab 		size = dib9000_read_word_attr(state, 1043, attr) & 0xff;
5459a0bf528SMauro Carvalho Chehab 		if ((size + len + 1) > MBX_MAX_WORDS && --tmp) {
5463dd72262SMauro Carvalho Chehab 			dprintk("MBX: RISC mbx full, retrying\n");
5479a0bf528SMauro Carvalho Chehab 			msleep(100);
5489a0bf528SMauro Carvalho Chehab 		} else
5499a0bf528SMauro Carvalho Chehab 			break;
5509a0bf528SMauro Carvalho Chehab 	} while (1);
5519a0bf528SMauro Carvalho Chehab 
5523dd72262SMauro Carvalho Chehab 	/*dprintk( "MBX: size: %d\n", size); */
5539a0bf528SMauro Carvalho Chehab 
5549a0bf528SMauro Carvalho Chehab 	if (tmp == 0) {
5559a0bf528SMauro Carvalho Chehab 		ret = -EINVAL;
5569a0bf528SMauro Carvalho Chehab 		goto out;
5579a0bf528SMauro Carvalho Chehab 	}
5589a0bf528SMauro Carvalho Chehab #ifdef DUMP_MSG
5593dd72262SMauro Carvalho Chehab 	dprintk("--> %02x %d %*ph\n", id, len + 1, len, data);
5609a0bf528SMauro Carvalho Chehab #endif
5619a0bf528SMauro Carvalho Chehab 
5629a0bf528SMauro Carvalho Chehab 	/* byte-order conversion - works on big (where it is not necessary) or little endian */
5639a0bf528SMauro Carvalho Chehab 	d = (u8 *) data;
5649a0bf528SMauro Carvalho Chehab 	for (i = 0; i < len; i++) {
5659a0bf528SMauro Carvalho Chehab 		tmp = data[i];
5669a0bf528SMauro Carvalho Chehab 		*d++ = tmp >> 8;
5679a0bf528SMauro Carvalho Chehab 		*d++ = tmp & 0xff;
5689a0bf528SMauro Carvalho Chehab 	}
5699a0bf528SMauro Carvalho Chehab 
5709a0bf528SMauro Carvalho Chehab 	/* write msg */
5719a0bf528SMauro Carvalho Chehab 	b[0] = id;
5729a0bf528SMauro Carvalho Chehab 	b[1] = len + 1;
5739a0bf528SMauro Carvalho Chehab 	if (dib9000_write16_noinc_attr(state, 1045, b, 2, attr) != 0 || dib9000_write16_noinc_attr(state, 1045, (u8 *) data, len * 2, attr) != 0) {
5749a0bf528SMauro Carvalho Chehab 		ret = -EIO;
5759a0bf528SMauro Carvalho Chehab 		goto out;
5769a0bf528SMauro Carvalho Chehab 	}
5779a0bf528SMauro Carvalho Chehab 
5789a0bf528SMauro Carvalho Chehab 	/* update register nb_mes_in_RX */
5799a0bf528SMauro Carvalho Chehab 	ret = (u8) dib9000_write_word_attr(state, 1043, 1 << 14, attr);
5809a0bf528SMauro Carvalho Chehab 
5819a0bf528SMauro Carvalho Chehab out:
5829a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->platform.risc.mbx_if_lock);
5839a0bf528SMauro Carvalho Chehab 
5849a0bf528SMauro Carvalho Chehab 	return ret;
5859a0bf528SMauro Carvalho Chehab }
5869a0bf528SMauro Carvalho Chehab 
dib9000_mbx_read(struct dib9000_state * state,u16 * data,u8 risc_id,u16 attr)5879a0bf528SMauro Carvalho Chehab static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id, u16 attr)
5889a0bf528SMauro Carvalho Chehab {
5899a0bf528SMauro Carvalho Chehab #ifdef DUMP_MSG
5909a0bf528SMauro Carvalho Chehab 	u16 *d = data;
5919a0bf528SMauro Carvalho Chehab #endif
5929a0bf528SMauro Carvalho Chehab 
5939a0bf528SMauro Carvalho Chehab 	u16 tmp, i;
5949a0bf528SMauro Carvalho Chehab 	u8 size;
5959a0bf528SMauro Carvalho Chehab 	u8 mc_base;
5969a0bf528SMauro Carvalho Chehab 
5979a0bf528SMauro Carvalho Chehab 	if (!state->platform.risc.fw_is_running)
5989a0bf528SMauro Carvalho Chehab 		return 0;
5999a0bf528SMauro Carvalho Chehab 
6009a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) {
6013dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
6029a0bf528SMauro Carvalho Chehab 		return 0;
6039a0bf528SMauro Carvalho Chehab 	}
6049a0bf528SMauro Carvalho Chehab 	if (risc_id == 1)
6059a0bf528SMauro Carvalho Chehab 		mc_base = 16;
6069a0bf528SMauro Carvalho Chehab 	else
6079a0bf528SMauro Carvalho Chehab 		mc_base = 0;
6089a0bf528SMauro Carvalho Chehab 
6099a0bf528SMauro Carvalho Chehab 	/* Length and type in the first word */
6109a0bf528SMauro Carvalho Chehab 	*data = dib9000_read_word_attr(state, 1029 + mc_base, attr);
6119a0bf528SMauro Carvalho Chehab 
6129a0bf528SMauro Carvalho Chehab 	size = *data & 0xff;
6139a0bf528SMauro Carvalho Chehab 	if (size <= MBX_MAX_WORDS) {
6149a0bf528SMauro Carvalho Chehab 		data++;
6159a0bf528SMauro Carvalho Chehab 		size--;		/* Initial word already read */
6169a0bf528SMauro Carvalho Chehab 
6179a0bf528SMauro Carvalho Chehab 		dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, size * 2, attr);
6189a0bf528SMauro Carvalho Chehab 
6199a0bf528SMauro Carvalho Chehab 		/* to word conversion */
6209a0bf528SMauro Carvalho Chehab 		for (i = 0; i < size; i++) {
6219a0bf528SMauro Carvalho Chehab 			tmp = *data;
6229a0bf528SMauro Carvalho Chehab 			*data = (tmp >> 8) | (tmp << 8);
6239a0bf528SMauro Carvalho Chehab 			data++;
6249a0bf528SMauro Carvalho Chehab 		}
6259a0bf528SMauro Carvalho Chehab 
6269a0bf528SMauro Carvalho Chehab #ifdef DUMP_MSG
6273dd72262SMauro Carvalho Chehab 		dprintk("<--\n");
6289a0bf528SMauro Carvalho Chehab 		for (i = 0; i < size + 1; i++)
6293dd72262SMauro Carvalho Chehab 			dprintk("%04x\n", d[i]);
6309a0bf528SMauro Carvalho Chehab 		dprintk("\n");
6319a0bf528SMauro Carvalho Chehab #endif
6329a0bf528SMauro Carvalho Chehab 	} else {
6333dd72262SMauro Carvalho Chehab 		dprintk("MBX: message is too big for message cache (%d), flushing message\n", size);
6349a0bf528SMauro Carvalho Chehab 		size--;		/* Initial word already read */
6359a0bf528SMauro Carvalho Chehab 		while (size--)
6369a0bf528SMauro Carvalho Chehab 			dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, 2, attr);
6379a0bf528SMauro Carvalho Chehab 	}
6389a0bf528SMauro Carvalho Chehab 	/* Update register nb_mes_in_TX */
6399a0bf528SMauro Carvalho Chehab 	dib9000_write_word_attr(state, 1028 + mc_base, 1 << 14, attr);
6409a0bf528SMauro Carvalho Chehab 
6419a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->platform.risc.mbx_if_lock);
6429a0bf528SMauro Carvalho Chehab 
6439a0bf528SMauro Carvalho Chehab 	return size + 1;
6449a0bf528SMauro Carvalho Chehab }
6459a0bf528SMauro Carvalho Chehab 
dib9000_risc_debug_buf(struct dib9000_state * state,u16 * data,u8 size)6469a0bf528SMauro Carvalho Chehab static int dib9000_risc_debug_buf(struct dib9000_state *state, u16 * data, u8 size)
6479a0bf528SMauro Carvalho Chehab {
6489a0bf528SMauro Carvalho Chehab 	u32 ts = data[1] << 16 | data[0];
6499a0bf528SMauro Carvalho Chehab 	char *b = (char *)&data[2];
6509a0bf528SMauro Carvalho Chehab 
6519a0bf528SMauro Carvalho Chehab 	b[2 * (size - 2) - 1] = '\0';	/* Bullet proof the buffer */
6529a0bf528SMauro Carvalho Chehab 	if (*b == '~') {
6539a0bf528SMauro Carvalho Chehab 		b++;
6543dd72262SMauro Carvalho Chehab 		dprintk("%s\n", b);
6559a0bf528SMauro Carvalho Chehab 	} else
6563dd72262SMauro Carvalho Chehab 		dprintk("RISC%d: %d.%04d %s\n",
6573dd72262SMauro Carvalho Chehab 			state->fe_id,
6583dd72262SMauro Carvalho Chehab 			ts / 10000, ts % 10000, *b ? b : "<empty>");
6599a0bf528SMauro Carvalho Chehab 	return 1;
6609a0bf528SMauro Carvalho Chehab }
6619a0bf528SMauro Carvalho Chehab 
dib9000_mbx_fetch_to_cache(struct dib9000_state * state,u16 attr)6629a0bf528SMauro Carvalho Chehab static int dib9000_mbx_fetch_to_cache(struct dib9000_state *state, u16 attr)
6639a0bf528SMauro Carvalho Chehab {
6649a0bf528SMauro Carvalho Chehab 	int i;
6659a0bf528SMauro Carvalho Chehab 	u8 size;
6669a0bf528SMauro Carvalho Chehab 	u16 *block;
6679a0bf528SMauro Carvalho Chehab 	/* find a free slot */
6689a0bf528SMauro Carvalho Chehab 	for (i = 0; i < DIB9000_MSG_CACHE_SIZE; i++) {
6699a0bf528SMauro Carvalho Chehab 		block = state->platform.risc.message_cache[i];
6709a0bf528SMauro Carvalho Chehab 		if (*block == 0) {
6719a0bf528SMauro Carvalho Chehab 			size = dib9000_mbx_read(state, block, 1, attr);
6729a0bf528SMauro Carvalho Chehab 
6733dd72262SMauro Carvalho Chehab /*                      dprintk( "MBX: fetched %04x message to cache\n", *block); */
6749a0bf528SMauro Carvalho Chehab 
6759a0bf528SMauro Carvalho Chehab 			switch (*block >> 8) {
6769a0bf528SMauro Carvalho Chehab 			case IN_MSG_DEBUG_BUF:
6779a0bf528SMauro Carvalho Chehab 				dib9000_risc_debug_buf(state, block + 1, size);	/* debug-messages are going to be printed right away */
6789a0bf528SMauro Carvalho Chehab 				*block = 0;	/* free the block */
6799a0bf528SMauro Carvalho Chehab 				break;
6809a0bf528SMauro Carvalho Chehab #if 0
6819a0bf528SMauro Carvalho Chehab 			case IN_MSG_DATA:	/* FE-TRACE */
6829a0bf528SMauro Carvalho Chehab 				dib9000_risc_data_process(state, block + 1, size);
6839a0bf528SMauro Carvalho Chehab 				*block = 0;
6849a0bf528SMauro Carvalho Chehab 				break;
6859a0bf528SMauro Carvalho Chehab #endif
6869a0bf528SMauro Carvalho Chehab 			default:
6879a0bf528SMauro Carvalho Chehab 				break;
6889a0bf528SMauro Carvalho Chehab 			}
6899a0bf528SMauro Carvalho Chehab 
6909a0bf528SMauro Carvalho Chehab 			return 1;
6919a0bf528SMauro Carvalho Chehab 		}
6929a0bf528SMauro Carvalho Chehab 	}
6933dd72262SMauro Carvalho Chehab 	dprintk("MBX: no free cache-slot found for new message...\n");
6949a0bf528SMauro Carvalho Chehab 	return -1;
6959a0bf528SMauro Carvalho Chehab }
6969a0bf528SMauro Carvalho Chehab 
dib9000_mbx_count(struct dib9000_state * state,u8 risc_id,u16 attr)6979a0bf528SMauro Carvalho Chehab static u8 dib9000_mbx_count(struct dib9000_state *state, u8 risc_id, u16 attr)
6989a0bf528SMauro Carvalho Chehab {
6999a0bf528SMauro Carvalho Chehab 	if (risc_id == 0)
7009a0bf528SMauro Carvalho Chehab 		return (u8) (dib9000_read_word_attr(state, 1028, attr) >> 10) & 0x1f;	/* 5 bit field */
7019a0bf528SMauro Carvalho Chehab 	else
7029a0bf528SMauro Carvalho Chehab 		return (u8) (dib9000_read_word_attr(state, 1044, attr) >> 8) & 0x7f;	/* 7 bit field */
7039a0bf528SMauro Carvalho Chehab }
7049a0bf528SMauro Carvalho Chehab 
dib9000_mbx_process(struct dib9000_state * state,u16 attr)7059a0bf528SMauro Carvalho Chehab static int dib9000_mbx_process(struct dib9000_state *state, u16 attr)
7069a0bf528SMauro Carvalho Chehab {
7079a0bf528SMauro Carvalho Chehab 	int ret = 0;
7089a0bf528SMauro Carvalho Chehab 
7099a0bf528SMauro Carvalho Chehab 	if (!state->platform.risc.fw_is_running)
7109a0bf528SMauro Carvalho Chehab 		return -1;
7119a0bf528SMauro Carvalho Chehab 
7129a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->platform.risc.mbx_lock) < 0) {
7133dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
7149a0bf528SMauro Carvalho Chehab 		return -1;
7159a0bf528SMauro Carvalho Chehab 	}
7169a0bf528SMauro Carvalho Chehab 
7179a0bf528SMauro Carvalho Chehab 	if (dib9000_mbx_count(state, 1, attr))	/* 1=RiscB */
7189a0bf528SMauro Carvalho Chehab 		ret = dib9000_mbx_fetch_to_cache(state, attr);
7199a0bf528SMauro Carvalho Chehab 
7209a0bf528SMauro Carvalho Chehab 	dib9000_read_word_attr(state, 1229, attr);	/* Clear the IRQ */
7219a0bf528SMauro Carvalho Chehab /*      if (tmp) */
7223dd72262SMauro Carvalho Chehab /*              dprintk( "cleared IRQ: %x\n", tmp); */
7239a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->platform.risc.mbx_lock);
7249a0bf528SMauro Carvalho Chehab 
7259a0bf528SMauro Carvalho Chehab 	return ret;
7269a0bf528SMauro Carvalho Chehab }
7279a0bf528SMauro Carvalho Chehab 
dib9000_mbx_get_message_attr(struct dib9000_state * state,u16 id,u16 * msg,u8 * size,u16 attr)7289a0bf528SMauro Carvalho Chehab static int dib9000_mbx_get_message_attr(struct dib9000_state *state, u16 id, u16 * msg, u8 * size, u16 attr)
7299a0bf528SMauro Carvalho Chehab {
7309a0bf528SMauro Carvalho Chehab 	u8 i;
7319a0bf528SMauro Carvalho Chehab 	u16 *block;
7329a0bf528SMauro Carvalho Chehab 	u16 timeout = 30;
7339a0bf528SMauro Carvalho Chehab 
7349a0bf528SMauro Carvalho Chehab 	*msg = 0;
7359a0bf528SMauro Carvalho Chehab 	do {
7369a0bf528SMauro Carvalho Chehab 		/* dib9000_mbx_get_from_cache(); */
7379a0bf528SMauro Carvalho Chehab 		for (i = 0; i < DIB9000_MSG_CACHE_SIZE; i++) {
7389a0bf528SMauro Carvalho Chehab 			block = state->platform.risc.message_cache[i];
7399a0bf528SMauro Carvalho Chehab 			if ((*block >> 8) == id) {
7409a0bf528SMauro Carvalho Chehab 				*size = (*block & 0xff) - 1;
7419a0bf528SMauro Carvalho Chehab 				memcpy(msg, block + 1, (*size) * 2);
7429a0bf528SMauro Carvalho Chehab 				*block = 0;	/* free the block */
7439a0bf528SMauro Carvalho Chehab 				i = 0;	/* signal that we found a message */
7449a0bf528SMauro Carvalho Chehab 				break;
7459a0bf528SMauro Carvalho Chehab 			}
7469a0bf528SMauro Carvalho Chehab 		}
7479a0bf528SMauro Carvalho Chehab 
7489a0bf528SMauro Carvalho Chehab 		if (i == 0)
7499a0bf528SMauro Carvalho Chehab 			break;
7509a0bf528SMauro Carvalho Chehab 
7519a0bf528SMauro Carvalho Chehab 		if (dib9000_mbx_process(state, attr) == -1)	/* try to fetch one message - if any */
7529a0bf528SMauro Carvalho Chehab 			return -1;
7539a0bf528SMauro Carvalho Chehab 
7549a0bf528SMauro Carvalho Chehab 	} while (--timeout);
7559a0bf528SMauro Carvalho Chehab 
7569a0bf528SMauro Carvalho Chehab 	if (timeout == 0) {
7573dd72262SMauro Carvalho Chehab 		dprintk("waiting for message %d timed out\n", id);
7589a0bf528SMauro Carvalho Chehab 		return -1;
7599a0bf528SMauro Carvalho Chehab 	}
7609a0bf528SMauro Carvalho Chehab 
7619a0bf528SMauro Carvalho Chehab 	return i == 0;
7629a0bf528SMauro Carvalho Chehab }
7639a0bf528SMauro Carvalho Chehab 
dib9000_risc_check_version(struct dib9000_state * state)7649a0bf528SMauro Carvalho Chehab static int dib9000_risc_check_version(struct dib9000_state *state)
7659a0bf528SMauro Carvalho Chehab {
7669a0bf528SMauro Carvalho Chehab 	u8 r[4];
7679a0bf528SMauro Carvalho Chehab 	u8 size;
7689a0bf528SMauro Carvalho Chehab 	u16 fw_version = 0;
7699a0bf528SMauro Carvalho Chehab 
7709a0bf528SMauro Carvalho Chehab 	if (dib9000_mbx_send(state, OUT_MSG_REQ_VERSION, &fw_version, 1) != 0)
7719a0bf528SMauro Carvalho Chehab 		return -EIO;
7729a0bf528SMauro Carvalho Chehab 
7739a0bf528SMauro Carvalho Chehab 	if (dib9000_mbx_get_message(state, IN_MSG_VERSION, (u16 *) r, &size) < 0)
7749a0bf528SMauro Carvalho Chehab 		return -EIO;
7759a0bf528SMauro Carvalho Chehab 
7769a0bf528SMauro Carvalho Chehab 	fw_version = (r[0] << 8) | r[1];
7773dd72262SMauro Carvalho Chehab 	dprintk("RISC: ver: %d.%02d (IC: %d)\n", fw_version >> 10, fw_version & 0x3ff, (r[2] << 8) | r[3]);
7789a0bf528SMauro Carvalho Chehab 
7799a0bf528SMauro Carvalho Chehab 	if ((fw_version >> 10) != 7)
7809a0bf528SMauro Carvalho Chehab 		return -EINVAL;
7819a0bf528SMauro Carvalho Chehab 
7829a0bf528SMauro Carvalho Chehab 	switch (fw_version & 0x3ff) {
7839a0bf528SMauro Carvalho Chehab 	case 11:
7849a0bf528SMauro Carvalho Chehab 	case 12:
7859a0bf528SMauro Carvalho Chehab 	case 14:
7869a0bf528SMauro Carvalho Chehab 	case 15:
7879a0bf528SMauro Carvalho Chehab 	case 16:
7889a0bf528SMauro Carvalho Chehab 	case 17:
7899a0bf528SMauro Carvalho Chehab 		break;
7909a0bf528SMauro Carvalho Chehab 	default:
7919a0bf528SMauro Carvalho Chehab 		dprintk("RISC: invalid firmware version");
7929a0bf528SMauro Carvalho Chehab 		return -EINVAL;
7939a0bf528SMauro Carvalho Chehab 	}
7949a0bf528SMauro Carvalho Chehab 
7959a0bf528SMauro Carvalho Chehab 	dprintk("RISC: valid firmware version");
7969a0bf528SMauro Carvalho Chehab 	return 0;
7979a0bf528SMauro Carvalho Chehab }
7989a0bf528SMauro Carvalho Chehab 
dib9000_fw_boot(struct dib9000_state * state,const u8 * codeA,u32 lenA,const u8 * codeB,u32 lenB)7999a0bf528SMauro Carvalho Chehab static int dib9000_fw_boot(struct dib9000_state *state, const u8 * codeA, u32 lenA, const u8 * codeB, u32 lenB)
8009a0bf528SMauro Carvalho Chehab {
8019a0bf528SMauro Carvalho Chehab 	/* Reconfig pool mac ram */
8029a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1225, 0x02);	/* A: 8k C, 4 k D - B: 32k C 6 k D - IRAM 96k */
8039a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1226, 0x05);
8049a0bf528SMauro Carvalho Chehab 
8059a0bf528SMauro Carvalho Chehab 	/* Toggles IP crypto to Host APB interface. */
8069a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1542, 1);
8079a0bf528SMauro Carvalho Chehab 
8089a0bf528SMauro Carvalho Chehab 	/* Set jump and no jump in the dma box */
8099a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1074, 0);
8109a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1075, 0);
8119a0bf528SMauro Carvalho Chehab 
8129a0bf528SMauro Carvalho Chehab 	/* Set MAC as APB Master. */
8139a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1237, 0);
8149a0bf528SMauro Carvalho Chehab 
8159a0bf528SMauro Carvalho Chehab 	/* Reset the RISCs */
8169a0bf528SMauro Carvalho Chehab 	if (codeA != NULL)
8179a0bf528SMauro Carvalho Chehab 		dib9000_write_word(state, 1024, 2);
8189a0bf528SMauro Carvalho Chehab 	else
8199a0bf528SMauro Carvalho Chehab 		dib9000_write_word(state, 1024, 15);
8209a0bf528SMauro Carvalho Chehab 	if (codeB != NULL)
8219a0bf528SMauro Carvalho Chehab 		dib9000_write_word(state, 1040, 2);
8229a0bf528SMauro Carvalho Chehab 
8239a0bf528SMauro Carvalho Chehab 	if (codeA != NULL)
8249a0bf528SMauro Carvalho Chehab 		dib9000_firmware_download(state, 0, 0x1234, codeA, lenA);
8259a0bf528SMauro Carvalho Chehab 	if (codeB != NULL)
8269a0bf528SMauro Carvalho Chehab 		dib9000_firmware_download(state, 1, 0x1234, codeB, lenB);
8279a0bf528SMauro Carvalho Chehab 
8289a0bf528SMauro Carvalho Chehab 	/* Run the RISCs */
8299a0bf528SMauro Carvalho Chehab 	if (codeA != NULL)
8309a0bf528SMauro Carvalho Chehab 		dib9000_write_word(state, 1024, 0);
8319a0bf528SMauro Carvalho Chehab 	if (codeB != NULL)
8329a0bf528SMauro Carvalho Chehab 		dib9000_write_word(state, 1040, 0);
8339a0bf528SMauro Carvalho Chehab 
8349a0bf528SMauro Carvalho Chehab 	if (codeA != NULL)
8359a0bf528SMauro Carvalho Chehab 		if (dib9000_mbx_host_init(state, 0) != 0)
8369a0bf528SMauro Carvalho Chehab 			return -EIO;
8379a0bf528SMauro Carvalho Chehab 	if (codeB != NULL)
8389a0bf528SMauro Carvalho Chehab 		if (dib9000_mbx_host_init(state, 1) != 0)
8399a0bf528SMauro Carvalho Chehab 			return -EIO;
8409a0bf528SMauro Carvalho Chehab 
8419a0bf528SMauro Carvalho Chehab 	msleep(100);
8429a0bf528SMauro Carvalho Chehab 	state->platform.risc.fw_is_running = 1;
8439a0bf528SMauro Carvalho Chehab 
8449a0bf528SMauro Carvalho Chehab 	if (dib9000_risc_check_version(state) != 0)
8459a0bf528SMauro Carvalho Chehab 		return -EINVAL;
8469a0bf528SMauro Carvalho Chehab 
8479a0bf528SMauro Carvalho Chehab 	state->platform.risc.memcmd = 0xff;
8489a0bf528SMauro Carvalho Chehab 	return 0;
8499a0bf528SMauro Carvalho Chehab }
8509a0bf528SMauro Carvalho Chehab 
dib9000_identify(struct i2c_device * client)8519a0bf528SMauro Carvalho Chehab static u16 dib9000_identify(struct i2c_device *client)
8529a0bf528SMauro Carvalho Chehab {
8539a0bf528SMauro Carvalho Chehab 	u16 value;
8549a0bf528SMauro Carvalho Chehab 
8559a0bf528SMauro Carvalho Chehab 	value = dib9000_i2c_read16(client, 896);
8569a0bf528SMauro Carvalho Chehab 	if (value != 0x01b3) {
8573dd72262SMauro Carvalho Chehab 		dprintk("wrong Vendor ID (0x%x)\n", value);
8589a0bf528SMauro Carvalho Chehab 		return 0;
8599a0bf528SMauro Carvalho Chehab 	}
8609a0bf528SMauro Carvalho Chehab 
8619a0bf528SMauro Carvalho Chehab 	value = dib9000_i2c_read16(client, 897);
8629a0bf528SMauro Carvalho Chehab 	if (value != 0x4000 && value != 0x4001 && value != 0x4002 && value != 0x4003 && value != 0x4004 && value != 0x4005) {
8633dd72262SMauro Carvalho Chehab 		dprintk("wrong Device ID (0x%x)\n", value);
8649a0bf528SMauro Carvalho Chehab 		return 0;
8659a0bf528SMauro Carvalho Chehab 	}
8669a0bf528SMauro Carvalho Chehab 
8679a0bf528SMauro Carvalho Chehab 	/* protect this driver to be used with 7000PC */
8689a0bf528SMauro Carvalho Chehab 	if (value == 0x4000 && dib9000_i2c_read16(client, 769) == 0x4000) {
8693dd72262SMauro Carvalho Chehab 		dprintk("this driver does not work with DiB7000PC\n");
8709a0bf528SMauro Carvalho Chehab 		return 0;
8719a0bf528SMauro Carvalho Chehab 	}
8729a0bf528SMauro Carvalho Chehab 
8739a0bf528SMauro Carvalho Chehab 	switch (value) {
8749a0bf528SMauro Carvalho Chehab 	case 0x4000:
8753dd72262SMauro Carvalho Chehab 		dprintk("found DiB7000MA/PA/MB/PB\n");
8769a0bf528SMauro Carvalho Chehab 		break;
8779a0bf528SMauro Carvalho Chehab 	case 0x4001:
8783dd72262SMauro Carvalho Chehab 		dprintk("found DiB7000HC\n");
8799a0bf528SMauro Carvalho Chehab 		break;
8809a0bf528SMauro Carvalho Chehab 	case 0x4002:
8813dd72262SMauro Carvalho Chehab 		dprintk("found DiB7000MC\n");
8829a0bf528SMauro Carvalho Chehab 		break;
8839a0bf528SMauro Carvalho Chehab 	case 0x4003:
8843dd72262SMauro Carvalho Chehab 		dprintk("found DiB9000A\n");
8859a0bf528SMauro Carvalho Chehab 		break;
8869a0bf528SMauro Carvalho Chehab 	case 0x4004:
8873dd72262SMauro Carvalho Chehab 		dprintk("found DiB9000H\n");
8889a0bf528SMauro Carvalho Chehab 		break;
8899a0bf528SMauro Carvalho Chehab 	case 0x4005:
8903dd72262SMauro Carvalho Chehab 		dprintk("found DiB9000M\n");
8919a0bf528SMauro Carvalho Chehab 		break;
8929a0bf528SMauro Carvalho Chehab 	}
8939a0bf528SMauro Carvalho Chehab 
8949a0bf528SMauro Carvalho Chehab 	return value;
8959a0bf528SMauro Carvalho Chehab }
8969a0bf528SMauro Carvalho Chehab 
dib9000_set_power_mode(struct dib9000_state * state,enum dib9000_power_mode mode)8979a0bf528SMauro Carvalho Chehab static void dib9000_set_power_mode(struct dib9000_state *state, enum dib9000_power_mode mode)
8989a0bf528SMauro Carvalho Chehab {
8999a0bf528SMauro Carvalho Chehab 	/* by default everything is going to be powered off */
9009a0bf528SMauro Carvalho Chehab 	u16 reg_903 = 0x3fff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906;
9019a0bf528SMauro Carvalho Chehab 	u8 offset;
9029a0bf528SMauro Carvalho Chehab 
9039a0bf528SMauro Carvalho Chehab 	if (state->revision == 0x4003 || state->revision == 0x4004 || state->revision == 0x4005)
9049a0bf528SMauro Carvalho Chehab 		offset = 1;
9059a0bf528SMauro Carvalho Chehab 	else
9069a0bf528SMauro Carvalho Chehab 		offset = 0;
9079a0bf528SMauro Carvalho Chehab 
9089a0bf528SMauro Carvalho Chehab 	reg_906 = dib9000_read_word(state, 906 + offset) | 0x3;	/* keep settings for RISC */
9099a0bf528SMauro Carvalho Chehab 
9109a0bf528SMauro Carvalho Chehab 	/* now, depending on the requested mode, we power on */
9119a0bf528SMauro Carvalho Chehab 	switch (mode) {
9129a0bf528SMauro Carvalho Chehab 		/* power up everything in the demod */
9139a0bf528SMauro Carvalho Chehab 	case DIB9000_POWER_ALL:
9149a0bf528SMauro Carvalho Chehab 		reg_903 = 0x0000;
9159a0bf528SMauro Carvalho Chehab 		reg_904 = 0x0000;
9169a0bf528SMauro Carvalho Chehab 		reg_905 = 0x0000;
9179a0bf528SMauro Carvalho Chehab 		reg_906 = 0x0000;
9189a0bf528SMauro Carvalho Chehab 		break;
9199a0bf528SMauro Carvalho Chehab 
9209a0bf528SMauro Carvalho Chehab 		/* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */
9219a0bf528SMauro Carvalho Chehab 	case DIB9000_POWER_INTERFACE_ONLY:	/* TODO power up either SDIO or I2C or SRAM */
9229a0bf528SMauro Carvalho Chehab 		reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2));
9239a0bf528SMauro Carvalho Chehab 		break;
9249a0bf528SMauro Carvalho Chehab 
9259a0bf528SMauro Carvalho Chehab 	case DIB9000_POWER_INTERF_ANALOG_AGC:
9269a0bf528SMauro Carvalho Chehab 		reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10));
9279a0bf528SMauro Carvalho Chehab 		reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2));
9289a0bf528SMauro Carvalho Chehab 		reg_906 &= ~((1 << 0));
9299a0bf528SMauro Carvalho Chehab 		break;
9309a0bf528SMauro Carvalho Chehab 
9319a0bf528SMauro Carvalho Chehab 	case DIB9000_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD:
9329a0bf528SMauro Carvalho Chehab 		reg_903 = 0x0000;
9339a0bf528SMauro Carvalho Chehab 		reg_904 = 0x801f;
9349a0bf528SMauro Carvalho Chehab 		reg_905 = 0x0000;
9359a0bf528SMauro Carvalho Chehab 		reg_906 &= ~((1 << 0));
9369a0bf528SMauro Carvalho Chehab 		break;
9379a0bf528SMauro Carvalho Chehab 
9389a0bf528SMauro Carvalho Chehab 	case DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD:
9399a0bf528SMauro Carvalho Chehab 		reg_903 = 0x0000;
9409a0bf528SMauro Carvalho Chehab 		reg_904 = 0x8000;
9419a0bf528SMauro Carvalho Chehab 		reg_905 = 0x010b;
9429a0bf528SMauro Carvalho Chehab 		reg_906 &= ~((1 << 0));
9439a0bf528SMauro Carvalho Chehab 		break;
9449a0bf528SMauro Carvalho Chehab 	default:
9459a0bf528SMauro Carvalho Chehab 	case DIB9000_POWER_NO:
9469a0bf528SMauro Carvalho Chehab 		break;
9479a0bf528SMauro Carvalho Chehab 	}
9489a0bf528SMauro Carvalho Chehab 
9499a0bf528SMauro Carvalho Chehab 	/* always power down unused parts */
9509a0bf528SMauro Carvalho Chehab 	if (!state->platform.host.mobile_mode)
9519a0bf528SMauro Carvalho Chehab 		reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1);
9529a0bf528SMauro Carvalho Chehab 
9539a0bf528SMauro Carvalho Chehab 	/* P_sdio_select_clk = 0 on MC and after */
9549a0bf528SMauro Carvalho Chehab 	if (state->revision != 0x4000)
9559a0bf528SMauro Carvalho Chehab 		reg_906 <<= 1;
9569a0bf528SMauro Carvalho Chehab 
9579a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 903 + offset, reg_903);
9589a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 904 + offset, reg_904);
9599a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 905 + offset, reg_905);
9609a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 906 + offset, reg_906);
9619a0bf528SMauro Carvalho Chehab }
9629a0bf528SMauro Carvalho Chehab 
dib9000_fw_reset(struct dvb_frontend * fe)9639a0bf528SMauro Carvalho Chehab static int dib9000_fw_reset(struct dvb_frontend *fe)
9649a0bf528SMauro Carvalho Chehab {
9659a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
9669a0bf528SMauro Carvalho Chehab 
9679a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1817, 0x0003);
9689a0bf528SMauro Carvalho Chehab 
9699a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1227, 1);
9709a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1227, 0);
9719a0bf528SMauro Carvalho Chehab 
9729a0bf528SMauro Carvalho Chehab 	switch ((state->revision = dib9000_identify(&state->i2c))) {
9739a0bf528SMauro Carvalho Chehab 	case 0x4003:
9749a0bf528SMauro Carvalho Chehab 	case 0x4004:
9759a0bf528SMauro Carvalho Chehab 	case 0x4005:
9769a0bf528SMauro Carvalho Chehab 		state->reg_offs = 1;
9779a0bf528SMauro Carvalho Chehab 		break;
9789a0bf528SMauro Carvalho Chehab 	default:
9799a0bf528SMauro Carvalho Chehab 		return -EINVAL;
9809a0bf528SMauro Carvalho Chehab 	}
9819a0bf528SMauro Carvalho Chehab 
9829a0bf528SMauro Carvalho Chehab 	/* reset the i2c-master to use the host interface */
9839a0bf528SMauro Carvalho Chehab 	dibx000_reset_i2c_master(&state->i2c_master);
9849a0bf528SMauro Carvalho Chehab 
9859a0bf528SMauro Carvalho Chehab 	dib9000_set_power_mode(state, DIB9000_POWER_ALL);
9869a0bf528SMauro Carvalho Chehab 
9879a0bf528SMauro Carvalho Chehab 	/* unforce divstr regardless whether i2c enumeration was done or not */
9889a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1794, dib9000_read_word(state, 1794) & ~(1 << 1));
9899a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1796, 0);
9909a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1805, 0x805);
9919a0bf528SMauro Carvalho Chehab 
9929a0bf528SMauro Carvalho Chehab 	/* restart all parts */
9939a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 898, 0xffff);
9949a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 899, 0xffff);
9959a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 900, 0x0001);
9969a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 901, 0xff19);
9979a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 902, 0x003c);
9989a0bf528SMauro Carvalho Chehab 
9999a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 898, 0);
10009a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 899, 0);
10019a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 900, 0);
10029a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 901, 0);
10039a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 902, 0);
10049a0bf528SMauro Carvalho Chehab 
10059a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 911, state->chip.d9.cfg.if_drives);
10069a0bf528SMauro Carvalho Chehab 
10079a0bf528SMauro Carvalho Chehab 	dib9000_set_power_mode(state, DIB9000_POWER_INTERFACE_ONLY);
10089a0bf528SMauro Carvalho Chehab 
10099a0bf528SMauro Carvalho Chehab 	return 0;
10109a0bf528SMauro Carvalho Chehab }
10119a0bf528SMauro Carvalho Chehab 
dib9000_risc_apb_access_read(struct dib9000_state * state,u32 address,u16 attribute,const u8 * tx,u32 txlen,u8 * b,u32 len)10129a0bf528SMauro Carvalho Chehab static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, u8 * b, u32 len)
10139a0bf528SMauro Carvalho Chehab {
10149a0bf528SMauro Carvalho Chehab 	u16 mb[10];
10159a0bf528SMauro Carvalho Chehab 	u8 i, s;
10169a0bf528SMauro Carvalho Chehab 
10179a0bf528SMauro Carvalho Chehab 	if (address >= 1024 || !state->platform.risc.fw_is_running)
10189a0bf528SMauro Carvalho Chehab 		return -EINVAL;
10199a0bf528SMauro Carvalho Chehab 
1020868c9a17SMauro Carvalho Chehab 	/* dprintk( "APB access through rd fw %d %x\n", address, attribute); */
10219a0bf528SMauro Carvalho Chehab 
10229a0bf528SMauro Carvalho Chehab 	mb[0] = (u16) address;
10239a0bf528SMauro Carvalho Chehab 	mb[1] = len / 2;
10249a0bf528SMauro Carvalho Chehab 	dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_R, mb, 2, attribute);
10259a0bf528SMauro Carvalho Chehab 	switch (dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute)) {
10269a0bf528SMauro Carvalho Chehab 	case 1:
10279a0bf528SMauro Carvalho Chehab 		s--;
10289a0bf528SMauro Carvalho Chehab 		for (i = 0; i < s; i++) {
10299a0bf528SMauro Carvalho Chehab 			b[i * 2] = (mb[i + 1] >> 8) & 0xff;
10309a0bf528SMauro Carvalho Chehab 			b[i * 2 + 1] = (mb[i + 1]) & 0xff;
10319a0bf528SMauro Carvalho Chehab 		}
10329a0bf528SMauro Carvalho Chehab 		return 0;
10339a0bf528SMauro Carvalho Chehab 	default:
10349a0bf528SMauro Carvalho Chehab 		return -EIO;
10359a0bf528SMauro Carvalho Chehab 	}
10369a0bf528SMauro Carvalho Chehab 	return -EIO;
10379a0bf528SMauro Carvalho Chehab }
10389a0bf528SMauro Carvalho Chehab 
dib9000_risc_apb_access_write(struct dib9000_state * state,u32 address,u16 attribute,const u8 * b,u32 len)10399a0bf528SMauro Carvalho Chehab static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len)
10409a0bf528SMauro Carvalho Chehab {
10419a0bf528SMauro Carvalho Chehab 	u16 mb[10];
10429a0bf528SMauro Carvalho Chehab 	u8 s, i;
10439a0bf528SMauro Carvalho Chehab 
10449a0bf528SMauro Carvalho Chehab 	if (address >= 1024 || !state->platform.risc.fw_is_running)
10459a0bf528SMauro Carvalho Chehab 		return -EINVAL;
10469a0bf528SMauro Carvalho Chehab 
104718d75a09SHeinrich Schuchardt 	if (len > 18)
104818d75a09SHeinrich Schuchardt 		return -EINVAL;
104918d75a09SHeinrich Schuchardt 
1050868c9a17SMauro Carvalho Chehab 	/* dprintk( "APB access through wr fw %d %x\n", address, attribute); */
10519a0bf528SMauro Carvalho Chehab 
105218d75a09SHeinrich Schuchardt 	mb[0] = (u16)address;
105318d75a09SHeinrich Schuchardt 	for (i = 0; i + 1 < len; i += 2)
105418d75a09SHeinrich Schuchardt 		mb[1 + i / 2] = b[i] << 8 | b[i + 1];
105518d75a09SHeinrich Schuchardt 	if (len & 1)
105618d75a09SHeinrich Schuchardt 		mb[1 + len / 2] = b[len - 1] << 8;
10579a0bf528SMauro Carvalho Chehab 
105818d75a09SHeinrich Schuchardt 	dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_W, mb, (3 + len) / 2, attribute);
10599a0bf528SMauro Carvalho Chehab 	return dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute) == 1 ? 0 : -EINVAL;
10609a0bf528SMauro Carvalho Chehab }
10619a0bf528SMauro Carvalho Chehab 
dib9000_fw_memmbx_sync(struct dib9000_state * state,u8 i)10629a0bf528SMauro Carvalho Chehab static int dib9000_fw_memmbx_sync(struct dib9000_state *state, u8 i)
10639a0bf528SMauro Carvalho Chehab {
10649a0bf528SMauro Carvalho Chehab 	u8 index_loop = 10;
10659a0bf528SMauro Carvalho Chehab 
10669a0bf528SMauro Carvalho Chehab 	if (!state->platform.risc.fw_is_running)
10679a0bf528SMauro Carvalho Chehab 		return 0;
10689a0bf528SMauro Carvalho Chehab 	dib9000_risc_mem_write(state, FE_MM_RW_SYNC, &i);
10699a0bf528SMauro Carvalho Chehab 	do {
10709a0bf528SMauro Carvalho Chehab 		dib9000_risc_mem_read(state, FE_MM_RW_SYNC, state->i2c_read_buffer, 1);
10719a0bf528SMauro Carvalho Chehab 	} while (state->i2c_read_buffer[0] && index_loop--);
10729a0bf528SMauro Carvalho Chehab 
10739a0bf528SMauro Carvalho Chehab 	if (index_loop > 0)
10749a0bf528SMauro Carvalho Chehab 		return 0;
10759a0bf528SMauro Carvalho Chehab 	return -EIO;
10769a0bf528SMauro Carvalho Chehab }
10779a0bf528SMauro Carvalho Chehab 
dib9000_fw_init(struct dib9000_state * state)10789a0bf528SMauro Carvalho Chehab static int dib9000_fw_init(struct dib9000_state *state)
10799a0bf528SMauro Carvalho Chehab {
10809a0bf528SMauro Carvalho Chehab 	struct dibGPIOFunction *f;
10819a0bf528SMauro Carvalho Chehab 	u16 b[40] = { 0 };
10829a0bf528SMauro Carvalho Chehab 	u8 i;
10839a0bf528SMauro Carvalho Chehab 	u8 size;
10849a0bf528SMauro Carvalho Chehab 
10859a0bf528SMauro Carvalho Chehab 	if (dib9000_fw_boot(state, NULL, 0, state->chip.d9.cfg.microcode_B_fe_buffer, state->chip.d9.cfg.microcode_B_fe_size) != 0)
10869a0bf528SMauro Carvalho Chehab 		return -EIO;
10879a0bf528SMauro Carvalho Chehab 
10889a0bf528SMauro Carvalho Chehab 	/* initialize the firmware */
10899a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(state->chip.d9.cfg.gpio_function); i++) {
10909a0bf528SMauro Carvalho Chehab 		f = &state->chip.d9.cfg.gpio_function[i];
10919a0bf528SMauro Carvalho Chehab 		if (f->mask) {
10929a0bf528SMauro Carvalho Chehab 			switch (f->function) {
10939a0bf528SMauro Carvalho Chehab 			case BOARD_GPIO_FUNCTION_COMPONENT_ON:
10949a0bf528SMauro Carvalho Chehab 				b[0] = (u16) f->mask;
10959a0bf528SMauro Carvalho Chehab 				b[1] = (u16) f->direction;
10969a0bf528SMauro Carvalho Chehab 				b[2] = (u16) f->value;
10979a0bf528SMauro Carvalho Chehab 				break;
10989a0bf528SMauro Carvalho Chehab 			case BOARD_GPIO_FUNCTION_COMPONENT_OFF:
10999a0bf528SMauro Carvalho Chehab 				b[3] = (u16) f->mask;
11009a0bf528SMauro Carvalho Chehab 				b[4] = (u16) f->direction;
11019a0bf528SMauro Carvalho Chehab 				b[5] = (u16) f->value;
11029a0bf528SMauro Carvalho Chehab 				break;
11039a0bf528SMauro Carvalho Chehab 			}
11049a0bf528SMauro Carvalho Chehab 		}
11059a0bf528SMauro Carvalho Chehab 	}
11069a0bf528SMauro Carvalho Chehab 	if (dib9000_mbx_send(state, OUT_MSG_CONF_GPIO, b, 15) != 0)
11079a0bf528SMauro Carvalho Chehab 		return -EIO;
11089a0bf528SMauro Carvalho Chehab 
11099a0bf528SMauro Carvalho Chehab 	/* subband */
11109a0bf528SMauro Carvalho Chehab 	b[0] = state->chip.d9.cfg.subband.size;	/* type == 0 -> GPIO - PWM not yet supported */
11119a0bf528SMauro Carvalho Chehab 	for (i = 0; i < state->chip.d9.cfg.subband.size; i++) {
11129a0bf528SMauro Carvalho Chehab 		b[1 + i * 4] = state->chip.d9.cfg.subband.subband[i].f_mhz;
11139a0bf528SMauro Carvalho Chehab 		b[2 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.mask;
11149a0bf528SMauro Carvalho Chehab 		b[3 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.direction;
11159a0bf528SMauro Carvalho Chehab 		b[4 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.value;
11169a0bf528SMauro Carvalho Chehab 	}
11179a0bf528SMauro Carvalho Chehab 	b[1 + i * 4] = 0;	/* fe_id */
11189a0bf528SMauro Carvalho Chehab 	if (dib9000_mbx_send(state, OUT_MSG_SUBBAND_SEL, b, 2 + 4 * i) != 0)
11199a0bf528SMauro Carvalho Chehab 		return -EIO;
11209a0bf528SMauro Carvalho Chehab 
11219a0bf528SMauro Carvalho Chehab 	/* 0 - id, 1 - no_of_frontends */
11229a0bf528SMauro Carvalho Chehab 	b[0] = (0 << 8) | 1;
11239a0bf528SMauro Carvalho Chehab 	/* 0 = i2c-address demod, 0 = tuner */
11249a0bf528SMauro Carvalho Chehab 	b[1] = (0 << 8) | (0);
11259a0bf528SMauro Carvalho Chehab 	b[2] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000) >> 16) & 0xffff);
11269a0bf528SMauro Carvalho Chehab 	b[3] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000)) & 0xffff);
11279a0bf528SMauro Carvalho Chehab 	b[4] = (u16) ((state->chip.d9.cfg.vcxo_timer >> 16) & 0xffff);
11289a0bf528SMauro Carvalho Chehab 	b[5] = (u16) ((state->chip.d9.cfg.vcxo_timer) & 0xffff);
11299a0bf528SMauro Carvalho Chehab 	b[6] = (u16) ((state->chip.d9.cfg.timing_frequency >> 16) & 0xffff);
11309a0bf528SMauro Carvalho Chehab 	b[7] = (u16) ((state->chip.d9.cfg.timing_frequency) & 0xffff);
11319a0bf528SMauro Carvalho Chehab 	b[29] = state->chip.d9.cfg.if_drives;
11329a0bf528SMauro Carvalho Chehab 	if (dib9000_mbx_send(state, OUT_MSG_INIT_DEMOD, b, ARRAY_SIZE(b)) != 0)
11339a0bf528SMauro Carvalho Chehab 		return -EIO;
11349a0bf528SMauro Carvalho Chehab 
11359a0bf528SMauro Carvalho Chehab 	if (dib9000_mbx_send(state, OUT_MSG_FE_FW_DL, NULL, 0) != 0)
11369a0bf528SMauro Carvalho Chehab 		return -EIO;
11379a0bf528SMauro Carvalho Chehab 
11389a0bf528SMauro Carvalho Chehab 	if (dib9000_mbx_get_message(state, IN_MSG_FE_FW_DL_DONE, b, &size) < 0)
11399a0bf528SMauro Carvalho Chehab 		return -EIO;
11409a0bf528SMauro Carvalho Chehab 
11419a0bf528SMauro Carvalho Chehab 	if (size > ARRAY_SIZE(b)) {
11429a0bf528SMauro Carvalho Chehab 		dprintk("error : firmware returned %dbytes needed but the used buffer has only %dbytes\n Firmware init ABORTED", size,
11439a0bf528SMauro Carvalho Chehab 			(int)ARRAY_SIZE(b));
11449a0bf528SMauro Carvalho Chehab 		return -EINVAL;
11459a0bf528SMauro Carvalho Chehab 	}
11469a0bf528SMauro Carvalho Chehab 
11479a0bf528SMauro Carvalho Chehab 	for (i = 0; i < size; i += 2) {
11489a0bf528SMauro Carvalho Chehab 		state->platform.risc.fe_mm[i / 2].addr = b[i + 0];
11499a0bf528SMauro Carvalho Chehab 		state->platform.risc.fe_mm[i / 2].size = b[i + 1];
11509a0bf528SMauro Carvalho Chehab 	}
11519a0bf528SMauro Carvalho Chehab 
11529a0bf528SMauro Carvalho Chehab 	return 0;
11539a0bf528SMauro Carvalho Chehab }
11549a0bf528SMauro Carvalho Chehab 
dib9000_fw_set_channel_head(struct dib9000_state * state)11559a0bf528SMauro Carvalho Chehab static void dib9000_fw_set_channel_head(struct dib9000_state *state)
11569a0bf528SMauro Carvalho Chehab {
11579a0bf528SMauro Carvalho Chehab 	u8 b[9];
11589a0bf528SMauro Carvalho Chehab 	u32 freq = state->fe[0]->dtv_property_cache.frequency / 1000;
11599a0bf528SMauro Carvalho Chehab 	if (state->fe_id % 2)
11609a0bf528SMauro Carvalho Chehab 		freq += 101;
11619a0bf528SMauro Carvalho Chehab 
11629a0bf528SMauro Carvalho Chehab 	b[0] = (u8) ((freq >> 0) & 0xff);
11639a0bf528SMauro Carvalho Chehab 	b[1] = (u8) ((freq >> 8) & 0xff);
11649a0bf528SMauro Carvalho Chehab 	b[2] = (u8) ((freq >> 16) & 0xff);
11659a0bf528SMauro Carvalho Chehab 	b[3] = (u8) ((freq >> 24) & 0xff);
11669a0bf528SMauro Carvalho Chehab 	b[4] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 0) & 0xff);
11679a0bf528SMauro Carvalho Chehab 	b[5] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 8) & 0xff);
11689a0bf528SMauro Carvalho Chehab 	b[6] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 16) & 0xff);
11699a0bf528SMauro Carvalho Chehab 	b[7] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 24) & 0xff);
11709a0bf528SMauro Carvalho Chehab 	b[8] = 0x80;		/* do not wait for CELL ID when doing autosearch */
11719a0bf528SMauro Carvalho Chehab 	if (state->fe[0]->dtv_property_cache.delivery_system == SYS_DVBT)
11729a0bf528SMauro Carvalho Chehab 		b[8] |= 1;
11739a0bf528SMauro Carvalho Chehab 	dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_HEAD, b);
11749a0bf528SMauro Carvalho Chehab }
11759a0bf528SMauro Carvalho Chehab 
dib9000_fw_get_channel(struct dvb_frontend * fe)11769a0bf528SMauro Carvalho Chehab static int dib9000_fw_get_channel(struct dvb_frontend *fe)
11779a0bf528SMauro Carvalho Chehab {
11789a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
11799a0bf528SMauro Carvalho Chehab 	struct dibDVBTChannel {
11809a0bf528SMauro Carvalho Chehab 		s8 spectrum_inversion;
11819a0bf528SMauro Carvalho Chehab 
11829a0bf528SMauro Carvalho Chehab 		s8 nfft;
11839a0bf528SMauro Carvalho Chehab 		s8 guard;
11849a0bf528SMauro Carvalho Chehab 		s8 constellation;
11859a0bf528SMauro Carvalho Chehab 
11869a0bf528SMauro Carvalho Chehab 		s8 hrch;
11879a0bf528SMauro Carvalho Chehab 		s8 alpha;
11889a0bf528SMauro Carvalho Chehab 		s8 code_rate_hp;
11899a0bf528SMauro Carvalho Chehab 		s8 code_rate_lp;
11909a0bf528SMauro Carvalho Chehab 		s8 select_hp;
11919a0bf528SMauro Carvalho Chehab 
11929a0bf528SMauro Carvalho Chehab 		s8 intlv_native;
11939a0bf528SMauro Carvalho Chehab 	};
11949a0bf528SMauro Carvalho Chehab 	struct dibDVBTChannel *ch;
11959a0bf528SMauro Carvalho Chehab 	int ret = 0;
11969a0bf528SMauro Carvalho Chehab 
11979a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
11983dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
11999a0bf528SMauro Carvalho Chehab 		return -EINTR;
12009a0bf528SMauro Carvalho Chehab 	}
12019a0bf528SMauro Carvalho Chehab 	if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
12029a0bf528SMauro Carvalho Chehab 		ret = -EIO;
12039a0bf528SMauro Carvalho Chehab 		goto error;
12049a0bf528SMauro Carvalho Chehab 	}
12059a0bf528SMauro Carvalho Chehab 
12069a0bf528SMauro Carvalho Chehab 	dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_UNION,
12079a0bf528SMauro Carvalho Chehab 			state->i2c_read_buffer, sizeof(struct dibDVBTChannel));
12089a0bf528SMauro Carvalho Chehab 	ch = (struct dibDVBTChannel *)state->i2c_read_buffer;
12099a0bf528SMauro Carvalho Chehab 
12109a0bf528SMauro Carvalho Chehab 
12119a0bf528SMauro Carvalho Chehab 	switch (ch->spectrum_inversion & 0x7) {
12129a0bf528SMauro Carvalho Chehab 	case 1:
12139a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.inversion = INVERSION_ON;
12149a0bf528SMauro Carvalho Chehab 		break;
12159a0bf528SMauro Carvalho Chehab 	case 0:
12169a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.inversion = INVERSION_OFF;
12179a0bf528SMauro Carvalho Chehab 		break;
12189a0bf528SMauro Carvalho Chehab 	default:
12199a0bf528SMauro Carvalho Chehab 	case -1:
12209a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.inversion = INVERSION_AUTO;
12219a0bf528SMauro Carvalho Chehab 		break;
12229a0bf528SMauro Carvalho Chehab 	}
12239a0bf528SMauro Carvalho Chehab 	switch (ch->nfft) {
12249a0bf528SMauro Carvalho Chehab 	case 0:
12259a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_2K;
12269a0bf528SMauro Carvalho Chehab 		break;
12279a0bf528SMauro Carvalho Chehab 	case 2:
12289a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_4K;
12299a0bf528SMauro Carvalho Chehab 		break;
12309a0bf528SMauro Carvalho Chehab 	case 1:
12319a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K;
12329a0bf528SMauro Carvalho Chehab 		break;
12339a0bf528SMauro Carvalho Chehab 	default:
12349a0bf528SMauro Carvalho Chehab 	case -1:
12359a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_AUTO;
12369a0bf528SMauro Carvalho Chehab 		break;
12379a0bf528SMauro Carvalho Chehab 	}
12389a0bf528SMauro Carvalho Chehab 	switch (ch->guard) {
12399a0bf528SMauro Carvalho Chehab 	case 0:
12409a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_32;
12419a0bf528SMauro Carvalho Chehab 		break;
12429a0bf528SMauro Carvalho Chehab 	case 1:
12439a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_16;
12449a0bf528SMauro Carvalho Chehab 		break;
12459a0bf528SMauro Carvalho Chehab 	case 2:
12469a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8;
12479a0bf528SMauro Carvalho Chehab 		break;
12489a0bf528SMauro Carvalho Chehab 	case 3:
12499a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_4;
12509a0bf528SMauro Carvalho Chehab 		break;
12519a0bf528SMauro Carvalho Chehab 	default:
12529a0bf528SMauro Carvalho Chehab 	case -1:
12539a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_AUTO;
12549a0bf528SMauro Carvalho Chehab 		break;
12559a0bf528SMauro Carvalho Chehab 	}
12569a0bf528SMauro Carvalho Chehab 	switch (ch->constellation) {
12579a0bf528SMauro Carvalho Chehab 	case 2:
12589a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.modulation = QAM_64;
12599a0bf528SMauro Carvalho Chehab 		break;
12609a0bf528SMauro Carvalho Chehab 	case 1:
12619a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.modulation = QAM_16;
12629a0bf528SMauro Carvalho Chehab 		break;
12639a0bf528SMauro Carvalho Chehab 	case 0:
12649a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.modulation = QPSK;
12659a0bf528SMauro Carvalho Chehab 		break;
12669a0bf528SMauro Carvalho Chehab 	default:
12679a0bf528SMauro Carvalho Chehab 	case -1:
12689a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.modulation = QAM_AUTO;
12699a0bf528SMauro Carvalho Chehab 		break;
12709a0bf528SMauro Carvalho Chehab 	}
12719a0bf528SMauro Carvalho Chehab 	switch (ch->hrch) {
12729a0bf528SMauro Carvalho Chehab 	case 0:
12739a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_NONE;
12749a0bf528SMauro Carvalho Chehab 		break;
12759a0bf528SMauro Carvalho Chehab 	case 1:
12769a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_1;
12779a0bf528SMauro Carvalho Chehab 		break;
12789a0bf528SMauro Carvalho Chehab 	default:
12799a0bf528SMauro Carvalho Chehab 	case -1:
12809a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_AUTO;
12819a0bf528SMauro Carvalho Chehab 		break;
12829a0bf528SMauro Carvalho Chehab 	}
12839a0bf528SMauro Carvalho Chehab 	switch (ch->code_rate_hp) {
12849a0bf528SMauro Carvalho Chehab 	case 1:
12859a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.code_rate_HP = FEC_1_2;
12869a0bf528SMauro Carvalho Chehab 		break;
12879a0bf528SMauro Carvalho Chehab 	case 2:
12889a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.code_rate_HP = FEC_2_3;
12899a0bf528SMauro Carvalho Chehab 		break;
12909a0bf528SMauro Carvalho Chehab 	case 3:
12919a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.code_rate_HP = FEC_3_4;
12929a0bf528SMauro Carvalho Chehab 		break;
12939a0bf528SMauro Carvalho Chehab 	case 5:
12949a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.code_rate_HP = FEC_5_6;
12959a0bf528SMauro Carvalho Chehab 		break;
12969a0bf528SMauro Carvalho Chehab 	case 7:
12979a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.code_rate_HP = FEC_7_8;
12989a0bf528SMauro Carvalho Chehab 		break;
12999a0bf528SMauro Carvalho Chehab 	default:
13009a0bf528SMauro Carvalho Chehab 	case -1:
13019a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.code_rate_HP = FEC_AUTO;
13029a0bf528SMauro Carvalho Chehab 		break;
13039a0bf528SMauro Carvalho Chehab 	}
13049a0bf528SMauro Carvalho Chehab 	switch (ch->code_rate_lp) {
13059a0bf528SMauro Carvalho Chehab 	case 1:
13069a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.code_rate_LP = FEC_1_2;
13079a0bf528SMauro Carvalho Chehab 		break;
13089a0bf528SMauro Carvalho Chehab 	case 2:
13099a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.code_rate_LP = FEC_2_3;
13109a0bf528SMauro Carvalho Chehab 		break;
13119a0bf528SMauro Carvalho Chehab 	case 3:
13129a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.code_rate_LP = FEC_3_4;
13139a0bf528SMauro Carvalho Chehab 		break;
13149a0bf528SMauro Carvalho Chehab 	case 5:
13159a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.code_rate_LP = FEC_5_6;
13169a0bf528SMauro Carvalho Chehab 		break;
13179a0bf528SMauro Carvalho Chehab 	case 7:
13189a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.code_rate_LP = FEC_7_8;
13199a0bf528SMauro Carvalho Chehab 		break;
13209a0bf528SMauro Carvalho Chehab 	default:
13219a0bf528SMauro Carvalho Chehab 	case -1:
13229a0bf528SMauro Carvalho Chehab 		state->fe[0]->dtv_property_cache.code_rate_LP = FEC_AUTO;
13239a0bf528SMauro Carvalho Chehab 		break;
13249a0bf528SMauro Carvalho Chehab 	}
13259a0bf528SMauro Carvalho Chehab 
13269a0bf528SMauro Carvalho Chehab error:
13279a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->platform.risc.mem_mbx_lock);
13289a0bf528SMauro Carvalho Chehab 	return ret;
13299a0bf528SMauro Carvalho Chehab }
13309a0bf528SMauro Carvalho Chehab 
dib9000_fw_set_channel_union(struct dvb_frontend * fe)13319a0bf528SMauro Carvalho Chehab static int dib9000_fw_set_channel_union(struct dvb_frontend *fe)
13329a0bf528SMauro Carvalho Chehab {
13339a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
13349a0bf528SMauro Carvalho Chehab 	struct dibDVBTChannel {
13359a0bf528SMauro Carvalho Chehab 		s8 spectrum_inversion;
13369a0bf528SMauro Carvalho Chehab 
13379a0bf528SMauro Carvalho Chehab 		s8 nfft;
13389a0bf528SMauro Carvalho Chehab 		s8 guard;
13399a0bf528SMauro Carvalho Chehab 		s8 constellation;
13409a0bf528SMauro Carvalho Chehab 
13419a0bf528SMauro Carvalho Chehab 		s8 hrch;
13429a0bf528SMauro Carvalho Chehab 		s8 alpha;
13439a0bf528SMauro Carvalho Chehab 		s8 code_rate_hp;
13449a0bf528SMauro Carvalho Chehab 		s8 code_rate_lp;
13459a0bf528SMauro Carvalho Chehab 		s8 select_hp;
13469a0bf528SMauro Carvalho Chehab 
13479a0bf528SMauro Carvalho Chehab 		s8 intlv_native;
13489a0bf528SMauro Carvalho Chehab 	};
13499a0bf528SMauro Carvalho Chehab 	struct dibDVBTChannel ch;
13509a0bf528SMauro Carvalho Chehab 
13519a0bf528SMauro Carvalho Chehab 	switch (state->fe[0]->dtv_property_cache.inversion) {
13529a0bf528SMauro Carvalho Chehab 	case INVERSION_ON:
13539a0bf528SMauro Carvalho Chehab 		ch.spectrum_inversion = 1;
13549a0bf528SMauro Carvalho Chehab 		break;
13559a0bf528SMauro Carvalho Chehab 	case INVERSION_OFF:
13569a0bf528SMauro Carvalho Chehab 		ch.spectrum_inversion = 0;
13579a0bf528SMauro Carvalho Chehab 		break;
13589a0bf528SMauro Carvalho Chehab 	default:
13599a0bf528SMauro Carvalho Chehab 	case INVERSION_AUTO:
13609a0bf528SMauro Carvalho Chehab 		ch.spectrum_inversion = -1;
13619a0bf528SMauro Carvalho Chehab 		break;
13629a0bf528SMauro Carvalho Chehab 	}
13639a0bf528SMauro Carvalho Chehab 	switch (state->fe[0]->dtv_property_cache.transmission_mode) {
13649a0bf528SMauro Carvalho Chehab 	case TRANSMISSION_MODE_2K:
13659a0bf528SMauro Carvalho Chehab 		ch.nfft = 0;
13669a0bf528SMauro Carvalho Chehab 		break;
13679a0bf528SMauro Carvalho Chehab 	case TRANSMISSION_MODE_4K:
13689a0bf528SMauro Carvalho Chehab 		ch.nfft = 2;
13699a0bf528SMauro Carvalho Chehab 		break;
13709a0bf528SMauro Carvalho Chehab 	case TRANSMISSION_MODE_8K:
13719a0bf528SMauro Carvalho Chehab 		ch.nfft = 1;
13729a0bf528SMauro Carvalho Chehab 		break;
13739a0bf528SMauro Carvalho Chehab 	default:
13749a0bf528SMauro Carvalho Chehab 	case TRANSMISSION_MODE_AUTO:
13759a0bf528SMauro Carvalho Chehab 		ch.nfft = 1;
13769a0bf528SMauro Carvalho Chehab 		break;
13779a0bf528SMauro Carvalho Chehab 	}
13789a0bf528SMauro Carvalho Chehab 	switch (state->fe[0]->dtv_property_cache.guard_interval) {
13799a0bf528SMauro Carvalho Chehab 	case GUARD_INTERVAL_1_32:
13809a0bf528SMauro Carvalho Chehab 		ch.guard = 0;
13819a0bf528SMauro Carvalho Chehab 		break;
13829a0bf528SMauro Carvalho Chehab 	case GUARD_INTERVAL_1_16:
13839a0bf528SMauro Carvalho Chehab 		ch.guard = 1;
13849a0bf528SMauro Carvalho Chehab 		break;
13859a0bf528SMauro Carvalho Chehab 	case GUARD_INTERVAL_1_8:
13869a0bf528SMauro Carvalho Chehab 		ch.guard = 2;
13879a0bf528SMauro Carvalho Chehab 		break;
13889a0bf528SMauro Carvalho Chehab 	case GUARD_INTERVAL_1_4:
13899a0bf528SMauro Carvalho Chehab 		ch.guard = 3;
13909a0bf528SMauro Carvalho Chehab 		break;
13919a0bf528SMauro Carvalho Chehab 	default:
13929a0bf528SMauro Carvalho Chehab 	case GUARD_INTERVAL_AUTO:
13939a0bf528SMauro Carvalho Chehab 		ch.guard = -1;
13949a0bf528SMauro Carvalho Chehab 		break;
13959a0bf528SMauro Carvalho Chehab 	}
13969a0bf528SMauro Carvalho Chehab 	switch (state->fe[0]->dtv_property_cache.modulation) {
13979a0bf528SMauro Carvalho Chehab 	case QAM_64:
13989a0bf528SMauro Carvalho Chehab 		ch.constellation = 2;
13999a0bf528SMauro Carvalho Chehab 		break;
14009a0bf528SMauro Carvalho Chehab 	case QAM_16:
14019a0bf528SMauro Carvalho Chehab 		ch.constellation = 1;
14029a0bf528SMauro Carvalho Chehab 		break;
14039a0bf528SMauro Carvalho Chehab 	case QPSK:
14049a0bf528SMauro Carvalho Chehab 		ch.constellation = 0;
14059a0bf528SMauro Carvalho Chehab 		break;
14069a0bf528SMauro Carvalho Chehab 	default:
14079a0bf528SMauro Carvalho Chehab 	case QAM_AUTO:
14089a0bf528SMauro Carvalho Chehab 		ch.constellation = -1;
14099a0bf528SMauro Carvalho Chehab 		break;
14109a0bf528SMauro Carvalho Chehab 	}
14119a0bf528SMauro Carvalho Chehab 	switch (state->fe[0]->dtv_property_cache.hierarchy) {
14129a0bf528SMauro Carvalho Chehab 	case HIERARCHY_NONE:
14139a0bf528SMauro Carvalho Chehab 		ch.hrch = 0;
14149a0bf528SMauro Carvalho Chehab 		break;
14159a0bf528SMauro Carvalho Chehab 	case HIERARCHY_1:
14169a0bf528SMauro Carvalho Chehab 	case HIERARCHY_2:
14179a0bf528SMauro Carvalho Chehab 	case HIERARCHY_4:
14189a0bf528SMauro Carvalho Chehab 		ch.hrch = 1;
14199a0bf528SMauro Carvalho Chehab 		break;
14209a0bf528SMauro Carvalho Chehab 	default:
14219a0bf528SMauro Carvalho Chehab 	case HIERARCHY_AUTO:
14229a0bf528SMauro Carvalho Chehab 		ch.hrch = -1;
14239a0bf528SMauro Carvalho Chehab 		break;
14249a0bf528SMauro Carvalho Chehab 	}
14259a0bf528SMauro Carvalho Chehab 	ch.alpha = 1;
14269a0bf528SMauro Carvalho Chehab 	switch (state->fe[0]->dtv_property_cache.code_rate_HP) {
14279a0bf528SMauro Carvalho Chehab 	case FEC_1_2:
14289a0bf528SMauro Carvalho Chehab 		ch.code_rate_hp = 1;
14299a0bf528SMauro Carvalho Chehab 		break;
14309a0bf528SMauro Carvalho Chehab 	case FEC_2_3:
14319a0bf528SMauro Carvalho Chehab 		ch.code_rate_hp = 2;
14329a0bf528SMauro Carvalho Chehab 		break;
14339a0bf528SMauro Carvalho Chehab 	case FEC_3_4:
14349a0bf528SMauro Carvalho Chehab 		ch.code_rate_hp = 3;
14359a0bf528SMauro Carvalho Chehab 		break;
14369a0bf528SMauro Carvalho Chehab 	case FEC_5_6:
14379a0bf528SMauro Carvalho Chehab 		ch.code_rate_hp = 5;
14389a0bf528SMauro Carvalho Chehab 		break;
14399a0bf528SMauro Carvalho Chehab 	case FEC_7_8:
14409a0bf528SMauro Carvalho Chehab 		ch.code_rate_hp = 7;
14419a0bf528SMauro Carvalho Chehab 		break;
14429a0bf528SMauro Carvalho Chehab 	default:
14439a0bf528SMauro Carvalho Chehab 	case FEC_AUTO:
14449a0bf528SMauro Carvalho Chehab 		ch.code_rate_hp = -1;
14459a0bf528SMauro Carvalho Chehab 		break;
14469a0bf528SMauro Carvalho Chehab 	}
14479a0bf528SMauro Carvalho Chehab 	switch (state->fe[0]->dtv_property_cache.code_rate_LP) {
14489a0bf528SMauro Carvalho Chehab 	case FEC_1_2:
14499a0bf528SMauro Carvalho Chehab 		ch.code_rate_lp = 1;
14509a0bf528SMauro Carvalho Chehab 		break;
14519a0bf528SMauro Carvalho Chehab 	case FEC_2_3:
14529a0bf528SMauro Carvalho Chehab 		ch.code_rate_lp = 2;
14539a0bf528SMauro Carvalho Chehab 		break;
14549a0bf528SMauro Carvalho Chehab 	case FEC_3_4:
14559a0bf528SMauro Carvalho Chehab 		ch.code_rate_lp = 3;
14569a0bf528SMauro Carvalho Chehab 		break;
14579a0bf528SMauro Carvalho Chehab 	case FEC_5_6:
14589a0bf528SMauro Carvalho Chehab 		ch.code_rate_lp = 5;
14599a0bf528SMauro Carvalho Chehab 		break;
14609a0bf528SMauro Carvalho Chehab 	case FEC_7_8:
14619a0bf528SMauro Carvalho Chehab 		ch.code_rate_lp = 7;
14629a0bf528SMauro Carvalho Chehab 		break;
14639a0bf528SMauro Carvalho Chehab 	default:
14649a0bf528SMauro Carvalho Chehab 	case FEC_AUTO:
14659a0bf528SMauro Carvalho Chehab 		ch.code_rate_lp = -1;
14669a0bf528SMauro Carvalho Chehab 		break;
14679a0bf528SMauro Carvalho Chehab 	}
14689a0bf528SMauro Carvalho Chehab 	ch.select_hp = 1;
14699a0bf528SMauro Carvalho Chehab 	ch.intlv_native = 1;
14709a0bf528SMauro Carvalho Chehab 
14719a0bf528SMauro Carvalho Chehab 	dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_UNION, (u8 *) &ch);
14729a0bf528SMauro Carvalho Chehab 
14739a0bf528SMauro Carvalho Chehab 	return 0;
14749a0bf528SMauro Carvalho Chehab }
14759a0bf528SMauro Carvalho Chehab 
dib9000_fw_tune(struct dvb_frontend * fe)14769a0bf528SMauro Carvalho Chehab static int dib9000_fw_tune(struct dvb_frontend *fe)
14779a0bf528SMauro Carvalho Chehab {
14789a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
14799a0bf528SMauro Carvalho Chehab 	int ret = 10, search = state->channel_status.status == CHANNEL_STATUS_PARAMETERS_UNKNOWN;
14809a0bf528SMauro Carvalho Chehab 	s8 i;
14819a0bf528SMauro Carvalho Chehab 
14829a0bf528SMauro Carvalho Chehab 	switch (state->tune_state) {
14839a0bf528SMauro Carvalho Chehab 	case CT_DEMOD_START:
14849a0bf528SMauro Carvalho Chehab 		dib9000_fw_set_channel_head(state);
14859a0bf528SMauro Carvalho Chehab 
14869a0bf528SMauro Carvalho Chehab 		/* write the channel context - a channel is initialized to 0, so it is OK */
14879a0bf528SMauro Carvalho Chehab 		dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_CONTEXT, (u8 *) fe_info);
14889a0bf528SMauro Carvalho Chehab 		dib9000_risc_mem_write(state, FE_MM_W_FE_INFO, (u8 *) fe_info);
14899a0bf528SMauro Carvalho Chehab 
14909a0bf528SMauro Carvalho Chehab 		if (search)
14919a0bf528SMauro Carvalho Chehab 			dib9000_mbx_send(state, OUT_MSG_FE_CHANNEL_SEARCH, NULL, 0);
14929a0bf528SMauro Carvalho Chehab 		else {
14939a0bf528SMauro Carvalho Chehab 			dib9000_fw_set_channel_union(fe);
14949a0bf528SMauro Carvalho Chehab 			dib9000_mbx_send(state, OUT_MSG_FE_CHANNEL_TUNE, NULL, 0);
14959a0bf528SMauro Carvalho Chehab 		}
14969a0bf528SMauro Carvalho Chehab 		state->tune_state = CT_DEMOD_STEP_1;
14979a0bf528SMauro Carvalho Chehab 		break;
14989a0bf528SMauro Carvalho Chehab 	case CT_DEMOD_STEP_1:
14999a0bf528SMauro Carvalho Chehab 		if (search)
15009a0bf528SMauro Carvalho Chehab 			dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_SEARCH_STATE, state->i2c_read_buffer, 1);
15019a0bf528SMauro Carvalho Chehab 		else
15029a0bf528SMauro Carvalho Chehab 			dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_TUNE_STATE, state->i2c_read_buffer, 1);
15039a0bf528SMauro Carvalho Chehab 		i = (s8)state->i2c_read_buffer[0];
15049a0bf528SMauro Carvalho Chehab 		switch (i) {	/* something happened */
15059a0bf528SMauro Carvalho Chehab 		case 0:
15069a0bf528SMauro Carvalho Chehab 			break;
15079a0bf528SMauro Carvalho Chehab 		case -2:	/* tps locks are "slower" than MPEG locks -> even in autosearch data is OK here */
15089a0bf528SMauro Carvalho Chehab 			if (search)
15099a0bf528SMauro Carvalho Chehab 				state->status = FE_STATUS_DEMOD_SUCCESS;
15109a0bf528SMauro Carvalho Chehab 			else {
15119a0bf528SMauro Carvalho Chehab 				state->tune_state = CT_DEMOD_STOP;
15129a0bf528SMauro Carvalho Chehab 				state->status = FE_STATUS_LOCKED;
15139a0bf528SMauro Carvalho Chehab 			}
15149a0bf528SMauro Carvalho Chehab 			break;
15159a0bf528SMauro Carvalho Chehab 		default:
15169a0bf528SMauro Carvalho Chehab 			state->status = FE_STATUS_TUNE_FAILED;
15179a0bf528SMauro Carvalho Chehab 			state->tune_state = CT_DEMOD_STOP;
15189a0bf528SMauro Carvalho Chehab 			break;
15199a0bf528SMauro Carvalho Chehab 		}
15209a0bf528SMauro Carvalho Chehab 		break;
15219a0bf528SMauro Carvalho Chehab 	default:
15229a0bf528SMauro Carvalho Chehab 		ret = FE_CALLBACK_TIME_NEVER;
15239a0bf528SMauro Carvalho Chehab 		break;
15249a0bf528SMauro Carvalho Chehab 	}
15259a0bf528SMauro Carvalho Chehab 
15269a0bf528SMauro Carvalho Chehab 	return ret;
15279a0bf528SMauro Carvalho Chehab }
15289a0bf528SMauro Carvalho Chehab 
dib9000_fw_set_diversity_in(struct dvb_frontend * fe,int onoff)15299a0bf528SMauro Carvalho Chehab static int dib9000_fw_set_diversity_in(struct dvb_frontend *fe, int onoff)
15309a0bf528SMauro Carvalho Chehab {
15319a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
15329a0bf528SMauro Carvalho Chehab 	u16 mode = (u16) onoff;
15339a0bf528SMauro Carvalho Chehab 	return dib9000_mbx_send(state, OUT_MSG_ENABLE_DIVERSITY, &mode, 1);
15349a0bf528SMauro Carvalho Chehab }
15359a0bf528SMauro Carvalho Chehab 
dib9000_fw_set_output_mode(struct dvb_frontend * fe,int mode)15369a0bf528SMauro Carvalho Chehab static int dib9000_fw_set_output_mode(struct dvb_frontend *fe, int mode)
15379a0bf528SMauro Carvalho Chehab {
15389a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
15399a0bf528SMauro Carvalho Chehab 	u16 outreg, smo_mode;
15409a0bf528SMauro Carvalho Chehab 
15413dd72262SMauro Carvalho Chehab 	dprintk("setting output mode for demod %p to %d\n", fe, mode);
15429a0bf528SMauro Carvalho Chehab 
15439a0bf528SMauro Carvalho Chehab 	switch (mode) {
15449a0bf528SMauro Carvalho Chehab 	case OUTMODE_MPEG2_PAR_GATED_CLK:
15459a0bf528SMauro Carvalho Chehab 		outreg = (1 << 10);	/* 0x0400 */
15469a0bf528SMauro Carvalho Chehab 		break;
15479a0bf528SMauro Carvalho Chehab 	case OUTMODE_MPEG2_PAR_CONT_CLK:
15489a0bf528SMauro Carvalho Chehab 		outreg = (1 << 10) | (1 << 6);	/* 0x0440 */
15499a0bf528SMauro Carvalho Chehab 		break;
15509a0bf528SMauro Carvalho Chehab 	case OUTMODE_MPEG2_SERIAL:
15519a0bf528SMauro Carvalho Chehab 		outreg = (1 << 10) | (2 << 6) | (0 << 1);	/* 0x0482 */
15529a0bf528SMauro Carvalho Chehab 		break;
15539a0bf528SMauro Carvalho Chehab 	case OUTMODE_DIVERSITY:
15549a0bf528SMauro Carvalho Chehab 		outreg = (1 << 10) | (4 << 6);	/* 0x0500 */
15559a0bf528SMauro Carvalho Chehab 		break;
15569a0bf528SMauro Carvalho Chehab 	case OUTMODE_MPEG2_FIFO:
15579a0bf528SMauro Carvalho Chehab 		outreg = (1 << 10) | (5 << 6);
15589a0bf528SMauro Carvalho Chehab 		break;
15599a0bf528SMauro Carvalho Chehab 	case OUTMODE_HIGH_Z:
15609a0bf528SMauro Carvalho Chehab 		outreg = 0;
15619a0bf528SMauro Carvalho Chehab 		break;
15629a0bf528SMauro Carvalho Chehab 	default:
15633dd72262SMauro Carvalho Chehab 		dprintk("Unhandled output_mode passed to be set for demod %p\n", &state->fe[0]);
15649a0bf528SMauro Carvalho Chehab 		return -EINVAL;
15659a0bf528SMauro Carvalho Chehab 	}
15669a0bf528SMauro Carvalho Chehab 
15679a0bf528SMauro Carvalho Chehab 	dib9000_write_word(state, 1795, outreg);
15689a0bf528SMauro Carvalho Chehab 
15699a0bf528SMauro Carvalho Chehab 	switch (mode) {
15709a0bf528SMauro Carvalho Chehab 	case OUTMODE_MPEG2_PAR_GATED_CLK:
15719a0bf528SMauro Carvalho Chehab 	case OUTMODE_MPEG2_PAR_CONT_CLK:
15729a0bf528SMauro Carvalho Chehab 	case OUTMODE_MPEG2_SERIAL:
15739a0bf528SMauro Carvalho Chehab 	case OUTMODE_MPEG2_FIFO:
15749a0bf528SMauro Carvalho Chehab 		smo_mode = (dib9000_read_word(state, 295) & 0x0010) | (1 << 1);
15759a0bf528SMauro Carvalho Chehab 		if (state->chip.d9.cfg.output_mpeg2_in_188_bytes)
15769a0bf528SMauro Carvalho Chehab 			smo_mode |= (1 << 5);
15779a0bf528SMauro Carvalho Chehab 		dib9000_write_word(state, 295, smo_mode);
15789a0bf528SMauro Carvalho Chehab 		break;
15799a0bf528SMauro Carvalho Chehab 	}
15809a0bf528SMauro Carvalho Chehab 
15819a0bf528SMauro Carvalho Chehab 	outreg = to_fw_output_mode(mode);
15829a0bf528SMauro Carvalho Chehab 	return dib9000_mbx_send(state, OUT_MSG_SET_OUTPUT_MODE, &outreg, 1);
15839a0bf528SMauro Carvalho Chehab }
15849a0bf528SMauro Carvalho Chehab 
dib9000_tuner_xfer(struct i2c_adapter * i2c_adap,struct i2c_msg msg[],int num)15859a0bf528SMauro Carvalho Chehab static int dib9000_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
15869a0bf528SMauro Carvalho Chehab {
15879a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = i2c_get_adapdata(i2c_adap);
15889a0bf528SMauro Carvalho Chehab 	u16 i, len, t, index_msg;
15899a0bf528SMauro Carvalho Chehab 
15909a0bf528SMauro Carvalho Chehab 	for (index_msg = 0; index_msg < num; index_msg++) {
15919a0bf528SMauro Carvalho Chehab 		if (msg[index_msg].flags & I2C_M_RD) {	/* read */
15929a0bf528SMauro Carvalho Chehab 			len = msg[index_msg].len;
15939a0bf528SMauro Carvalho Chehab 			if (len > 16)
15949a0bf528SMauro Carvalho Chehab 				len = 16;
15959a0bf528SMauro Carvalho Chehab 
15969a0bf528SMauro Carvalho Chehab 			if (dib9000_read_word(state, 790) != 0)
15973dd72262SMauro Carvalho Chehab 				dprintk("TunerITF: read busy\n");
15989a0bf528SMauro Carvalho Chehab 
15999a0bf528SMauro Carvalho Chehab 			dib9000_write_word(state, 784, (u16) (msg[index_msg].addr));
16009a0bf528SMauro Carvalho Chehab 			dib9000_write_word(state, 787, (len / 2) - 1);
16019a0bf528SMauro Carvalho Chehab 			dib9000_write_word(state, 786, 1);	/* start read */
16029a0bf528SMauro Carvalho Chehab 
16039a0bf528SMauro Carvalho Chehab 			i = 1000;
16049a0bf528SMauro Carvalho Chehab 			while (dib9000_read_word(state, 790) != (len / 2) && i)
16059a0bf528SMauro Carvalho Chehab 				i--;
16069a0bf528SMauro Carvalho Chehab 
16079a0bf528SMauro Carvalho Chehab 			if (i == 0)
16083dd72262SMauro Carvalho Chehab 				dprintk("TunerITF: read failed\n");
16099a0bf528SMauro Carvalho Chehab 
16109a0bf528SMauro Carvalho Chehab 			for (i = 0; i < len; i += 2) {
16119a0bf528SMauro Carvalho Chehab 				t = dib9000_read_word(state, 785);
16129a0bf528SMauro Carvalho Chehab 				msg[index_msg].buf[i] = (t >> 8) & 0xff;
16139a0bf528SMauro Carvalho Chehab 				msg[index_msg].buf[i + 1] = (t) & 0xff;
16149a0bf528SMauro Carvalho Chehab 			}
16159a0bf528SMauro Carvalho Chehab 			if (dib9000_read_word(state, 790) != 0)
16163dd72262SMauro Carvalho Chehab 				dprintk("TunerITF: read more data than expected\n");
16179a0bf528SMauro Carvalho Chehab 		} else {
16189a0bf528SMauro Carvalho Chehab 			i = 1000;
16199a0bf528SMauro Carvalho Chehab 			while (dib9000_read_word(state, 789) && i)
16209a0bf528SMauro Carvalho Chehab 				i--;
16219a0bf528SMauro Carvalho Chehab 			if (i == 0)
16223dd72262SMauro Carvalho Chehab 				dprintk("TunerITF: write busy\n");
16239a0bf528SMauro Carvalho Chehab 
16249a0bf528SMauro Carvalho Chehab 			len = msg[index_msg].len;
16259a0bf528SMauro Carvalho Chehab 			if (len > 16)
16269a0bf528SMauro Carvalho Chehab 				len = 16;
16279a0bf528SMauro Carvalho Chehab 
16289a0bf528SMauro Carvalho Chehab 			for (i = 0; i < len; i += 2)
16299a0bf528SMauro Carvalho Chehab 				dib9000_write_word(state, 785, (msg[index_msg].buf[i] << 8) | msg[index_msg].buf[i + 1]);
16309a0bf528SMauro Carvalho Chehab 			dib9000_write_word(state, 784, (u16) msg[index_msg].addr);
16319a0bf528SMauro Carvalho Chehab 			dib9000_write_word(state, 787, (len / 2) - 1);
16329a0bf528SMauro Carvalho Chehab 			dib9000_write_word(state, 786, 0);	/* start write */
16339a0bf528SMauro Carvalho Chehab 
16349a0bf528SMauro Carvalho Chehab 			i = 1000;
16359a0bf528SMauro Carvalho Chehab 			while (dib9000_read_word(state, 791) > 0 && i)
16369a0bf528SMauro Carvalho Chehab 				i--;
16379a0bf528SMauro Carvalho Chehab 			if (i == 0)
16383dd72262SMauro Carvalho Chehab 				dprintk("TunerITF: write failed\n");
16399a0bf528SMauro Carvalho Chehab 		}
16409a0bf528SMauro Carvalho Chehab 	}
16419a0bf528SMauro Carvalho Chehab 	return num;
16429a0bf528SMauro Carvalho Chehab }
16439a0bf528SMauro Carvalho Chehab 
dib9000_fw_set_component_bus_speed(struct dvb_frontend * fe,u16 speed)16449a0bf528SMauro Carvalho Chehab int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed)
16459a0bf528SMauro Carvalho Chehab {
16469a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
16479a0bf528SMauro Carvalho Chehab 
16489a0bf528SMauro Carvalho Chehab 	state->component_bus_speed = speed;
16499a0bf528SMauro Carvalho Chehab 	return 0;
16509a0bf528SMauro Carvalho Chehab }
16519a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib9000_fw_set_component_bus_speed);
16529a0bf528SMauro Carvalho Chehab 
dib9000_fw_component_bus_xfer(struct i2c_adapter * i2c_adap,struct i2c_msg msg[],int num)16539a0bf528SMauro Carvalho Chehab static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
16549a0bf528SMauro Carvalho Chehab {
16559a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = i2c_get_adapdata(i2c_adap);
16569a0bf528SMauro Carvalho Chehab 	u8 type = 0;		/* I2C */
16579a0bf528SMauro Carvalho Chehab 	u8 port = DIBX000_I2C_INTERFACE_GPIO_3_4;
16589a0bf528SMauro Carvalho Chehab 	u16 scl = state->component_bus_speed;	/* SCL frequency */
16599a0bf528SMauro Carvalho Chehab 	struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[FE_MM_RW_COMPONENT_ACCESS_BUFFER];
16609a0bf528SMauro Carvalho Chehab 	u8 p[13] = { 0 };
16619a0bf528SMauro Carvalho Chehab 
16629a0bf528SMauro Carvalho Chehab 	p[0] = type;
16639a0bf528SMauro Carvalho Chehab 	p[1] = port;
16649a0bf528SMauro Carvalho Chehab 	p[2] = msg[0].addr << 1;
16659a0bf528SMauro Carvalho Chehab 
16669a0bf528SMauro Carvalho Chehab 	p[3] = (u8) scl & 0xff;	/* scl */
16679a0bf528SMauro Carvalho Chehab 	p[4] = (u8) (scl >> 8);
16689a0bf528SMauro Carvalho Chehab 
16699a0bf528SMauro Carvalho Chehab 	p[7] = 0;
16709a0bf528SMauro Carvalho Chehab 	p[8] = 0;
16719a0bf528SMauro Carvalho Chehab 
16729a0bf528SMauro Carvalho Chehab 	p[9] = (u8) (msg[0].len);
16739a0bf528SMauro Carvalho Chehab 	p[10] = (u8) (msg[0].len >> 8);
16749a0bf528SMauro Carvalho Chehab 	if ((num > 1) && (msg[1].flags & I2C_M_RD)) {
16759a0bf528SMauro Carvalho Chehab 		p[11] = (u8) (msg[1].len);
16769a0bf528SMauro Carvalho Chehab 		p[12] = (u8) (msg[1].len >> 8);
16779a0bf528SMauro Carvalho Chehab 	} else {
16789a0bf528SMauro Carvalho Chehab 		p[11] = 0;
16799a0bf528SMauro Carvalho Chehab 		p[12] = 0;
16809a0bf528SMauro Carvalho Chehab 	}
16819a0bf528SMauro Carvalho Chehab 
16829a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
16833dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
16849a0bf528SMauro Carvalho Chehab 		return 0;
16859a0bf528SMauro Carvalho Chehab 	}
16869a0bf528SMauro Carvalho Chehab 
16879a0bf528SMauro Carvalho Chehab 	dib9000_risc_mem_write(state, FE_MM_W_COMPONENT_ACCESS, p);
16889a0bf528SMauro Carvalho Chehab 
16899a0bf528SMauro Carvalho Chehab 	{			/* write-part */
16909a0bf528SMauro Carvalho Chehab 		dib9000_risc_mem_setup_cmd(state, m->addr, msg[0].len, 0);
16919a0bf528SMauro Carvalho Chehab 		dib9000_risc_mem_write_chunks(state, msg[0].buf, msg[0].len);
16929a0bf528SMauro Carvalho Chehab 	}
16939a0bf528SMauro Carvalho Chehab 
16949a0bf528SMauro Carvalho Chehab 	/* do the transaction */
16959a0bf528SMauro Carvalho Chehab 	if (dib9000_fw_memmbx_sync(state, FE_SYNC_COMPONENT_ACCESS) < 0) {
16969a0bf528SMauro Carvalho Chehab 		mutex_unlock(&state->platform.risc.mem_mbx_lock);
16979a0bf528SMauro Carvalho Chehab 		return 0;
16989a0bf528SMauro Carvalho Chehab 	}
16999a0bf528SMauro Carvalho Chehab 
17009a0bf528SMauro Carvalho Chehab 	/* read back any possible result */
17019a0bf528SMauro Carvalho Chehab 	if ((num > 1) && (msg[1].flags & I2C_M_RD))
17029a0bf528SMauro Carvalho Chehab 		dib9000_risc_mem_read(state, FE_MM_RW_COMPONENT_ACCESS_BUFFER, msg[1].buf, msg[1].len);
17039a0bf528SMauro Carvalho Chehab 
17049a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->platform.risc.mem_mbx_lock);
17059a0bf528SMauro Carvalho Chehab 
17069a0bf528SMauro Carvalho Chehab 	return num;
17079a0bf528SMauro Carvalho Chehab }
17089a0bf528SMauro Carvalho Chehab 
dib9000_i2c_func(struct i2c_adapter * adapter)17099a0bf528SMauro Carvalho Chehab static u32 dib9000_i2c_func(struct i2c_adapter *adapter)
17109a0bf528SMauro Carvalho Chehab {
17119a0bf528SMauro Carvalho Chehab 	return I2C_FUNC_I2C;
17129a0bf528SMauro Carvalho Chehab }
17139a0bf528SMauro Carvalho Chehab 
171419779f40SGustavo A. R. Silva static const struct i2c_algorithm dib9000_tuner_algo = {
17159a0bf528SMauro Carvalho Chehab 	.master_xfer = dib9000_tuner_xfer,
17169a0bf528SMauro Carvalho Chehab 	.functionality = dib9000_i2c_func,
17179a0bf528SMauro Carvalho Chehab };
17189a0bf528SMauro Carvalho Chehab 
171919779f40SGustavo A. R. Silva static const struct i2c_algorithm dib9000_component_bus_algo = {
17209a0bf528SMauro Carvalho Chehab 	.master_xfer = dib9000_fw_component_bus_xfer,
17219a0bf528SMauro Carvalho Chehab 	.functionality = dib9000_i2c_func,
17229a0bf528SMauro Carvalho Chehab };
17239a0bf528SMauro Carvalho Chehab 
dib9000_get_tuner_interface(struct dvb_frontend * fe)17249a0bf528SMauro Carvalho Chehab struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe)
17259a0bf528SMauro Carvalho Chehab {
17269a0bf528SMauro Carvalho Chehab 	struct dib9000_state *st = fe->demodulator_priv;
17279a0bf528SMauro Carvalho Chehab 	return &st->tuner_adap;
17289a0bf528SMauro Carvalho Chehab }
17299a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib9000_get_tuner_interface);
17309a0bf528SMauro Carvalho Chehab 
dib9000_get_component_bus_interface(struct dvb_frontend * fe)17319a0bf528SMauro Carvalho Chehab struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe)
17329a0bf528SMauro Carvalho Chehab {
17339a0bf528SMauro Carvalho Chehab 	struct dib9000_state *st = fe->demodulator_priv;
17349a0bf528SMauro Carvalho Chehab 	return &st->component_bus;
17359a0bf528SMauro Carvalho Chehab }
17369a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib9000_get_component_bus_interface);
17379a0bf528SMauro Carvalho Chehab 
dib9000_get_i2c_master(struct dvb_frontend * fe,enum dibx000_i2c_interface intf,int gating)17389a0bf528SMauro Carvalho Chehab struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating)
17399a0bf528SMauro Carvalho Chehab {
17409a0bf528SMauro Carvalho Chehab 	struct dib9000_state *st = fe->demodulator_priv;
17419a0bf528SMauro Carvalho Chehab 	return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating);
17429a0bf528SMauro Carvalho Chehab }
17439a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib9000_get_i2c_master);
17449a0bf528SMauro Carvalho Chehab 
dib9000_set_i2c_adapter(struct dvb_frontend * fe,struct i2c_adapter * i2c)17459a0bf528SMauro Carvalho Chehab int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c)
17469a0bf528SMauro Carvalho Chehab {
17479a0bf528SMauro Carvalho Chehab 	struct dib9000_state *st = fe->demodulator_priv;
17489a0bf528SMauro Carvalho Chehab 
17499a0bf528SMauro Carvalho Chehab 	st->i2c.i2c_adap = i2c;
17509a0bf528SMauro Carvalho Chehab 	return 0;
17519a0bf528SMauro Carvalho Chehab }
17529a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib9000_set_i2c_adapter);
17539a0bf528SMauro Carvalho Chehab 
dib9000_cfg_gpio(struct dib9000_state * st,u8 num,u8 dir,u8 val)17549a0bf528SMauro Carvalho Chehab static int dib9000_cfg_gpio(struct dib9000_state *st, u8 num, u8 dir, u8 val)
17559a0bf528SMauro Carvalho Chehab {
17569a0bf528SMauro Carvalho Chehab 	st->gpio_dir = dib9000_read_word(st, 773);
17579a0bf528SMauro Carvalho Chehab 	st->gpio_dir &= ~(1 << num);	/* reset the direction bit */
17589a0bf528SMauro Carvalho Chehab 	st->gpio_dir |= (dir & 0x1) << num;	/* set the new direction */
17599a0bf528SMauro Carvalho Chehab 	dib9000_write_word(st, 773, st->gpio_dir);
17609a0bf528SMauro Carvalho Chehab 
17619a0bf528SMauro Carvalho Chehab 	st->gpio_val = dib9000_read_word(st, 774);
17629a0bf528SMauro Carvalho Chehab 	st->gpio_val &= ~(1 << num);	/* reset the direction bit */
17639a0bf528SMauro Carvalho Chehab 	st->gpio_val |= (val & 0x01) << num;	/* set the new value */
17649a0bf528SMauro Carvalho Chehab 	dib9000_write_word(st, 774, st->gpio_val);
17659a0bf528SMauro Carvalho Chehab 
17663dd72262SMauro Carvalho Chehab 	dprintk("gpio dir: %04x: gpio val: %04x\n", st->gpio_dir, st->gpio_val);
17679a0bf528SMauro Carvalho Chehab 
17689a0bf528SMauro Carvalho Chehab 	return 0;
17699a0bf528SMauro Carvalho Chehab }
17709a0bf528SMauro Carvalho Chehab 
dib9000_set_gpio(struct dvb_frontend * fe,u8 num,u8 dir,u8 val)17719a0bf528SMauro Carvalho Chehab int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
17729a0bf528SMauro Carvalho Chehab {
17739a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
17749a0bf528SMauro Carvalho Chehab 	return dib9000_cfg_gpio(state, num, dir, val);
17759a0bf528SMauro Carvalho Chehab }
17769a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib9000_set_gpio);
17779a0bf528SMauro Carvalho Chehab 
dib9000_fw_pid_filter_ctrl(struct dvb_frontend * fe,u8 onoff)17789a0bf528SMauro Carvalho Chehab int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
17799a0bf528SMauro Carvalho Chehab {
17809a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
17819a0bf528SMauro Carvalho Chehab 	u16 val;
17829a0bf528SMauro Carvalho Chehab 	int ret;
17839a0bf528SMauro Carvalho Chehab 
17849a0bf528SMauro Carvalho Chehab 	if ((state->pid_ctrl_index != -2) && (state->pid_ctrl_index < 9)) {
17859a0bf528SMauro Carvalho Chehab 		/* postpone the pid filtering cmd */
17863dd72262SMauro Carvalho Chehab 		dprintk("pid filter cmd postpone\n");
17879a0bf528SMauro Carvalho Chehab 		state->pid_ctrl_index++;
17889a0bf528SMauro Carvalho Chehab 		state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER_CTRL;
17899a0bf528SMauro Carvalho Chehab 		state->pid_ctrl[state->pid_ctrl_index].onoff = onoff;
17909a0bf528SMauro Carvalho Chehab 		return 0;
17919a0bf528SMauro Carvalho Chehab 	}
17929a0bf528SMauro Carvalho Chehab 
17939a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->demod_lock) < 0) {
17943dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
17959a0bf528SMauro Carvalho Chehab 		return -EINTR;
17969a0bf528SMauro Carvalho Chehab 	}
17979a0bf528SMauro Carvalho Chehab 
17989a0bf528SMauro Carvalho Chehab 	val = dib9000_read_word(state, 294 + 1) & 0xffef;
17999a0bf528SMauro Carvalho Chehab 	val |= (onoff & 0x1) << 4;
18009a0bf528SMauro Carvalho Chehab 
18013dd72262SMauro Carvalho Chehab 	dprintk("PID filter enabled %d\n", onoff);
18029a0bf528SMauro Carvalho Chehab 	ret = dib9000_write_word(state, 294 + 1, val);
18039a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->demod_lock);
18049a0bf528SMauro Carvalho Chehab 	return ret;
18059a0bf528SMauro Carvalho Chehab 
18069a0bf528SMauro Carvalho Chehab }
18079a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib9000_fw_pid_filter_ctrl);
18089a0bf528SMauro Carvalho Chehab 
dib9000_fw_pid_filter(struct dvb_frontend * fe,u8 id,u16 pid,u8 onoff)18099a0bf528SMauro Carvalho Chehab int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
18109a0bf528SMauro Carvalho Chehab {
18119a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
18129a0bf528SMauro Carvalho Chehab 	int ret;
18139a0bf528SMauro Carvalho Chehab 
18149a0bf528SMauro Carvalho Chehab 	if (state->pid_ctrl_index != -2) {
18159a0bf528SMauro Carvalho Chehab 		/* postpone the pid filtering cmd */
18163dd72262SMauro Carvalho Chehab 		dprintk("pid filter postpone\n");
18179a0bf528SMauro Carvalho Chehab 		if (state->pid_ctrl_index < 9) {
18189a0bf528SMauro Carvalho Chehab 			state->pid_ctrl_index++;
18199a0bf528SMauro Carvalho Chehab 			state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER;
18209a0bf528SMauro Carvalho Chehab 			state->pid_ctrl[state->pid_ctrl_index].id = id;
18219a0bf528SMauro Carvalho Chehab 			state->pid_ctrl[state->pid_ctrl_index].pid = pid;
18229a0bf528SMauro Carvalho Chehab 			state->pid_ctrl[state->pid_ctrl_index].onoff = onoff;
18239a0bf528SMauro Carvalho Chehab 		} else
18243dd72262SMauro Carvalho Chehab 			dprintk("can not add any more pid ctrl cmd\n");
18259a0bf528SMauro Carvalho Chehab 		return 0;
18269a0bf528SMauro Carvalho Chehab 	}
18279a0bf528SMauro Carvalho Chehab 
18289a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->demod_lock) < 0) {
18293dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
18309a0bf528SMauro Carvalho Chehab 		return -EINTR;
18319a0bf528SMauro Carvalho Chehab 	}
18323dd72262SMauro Carvalho Chehab 	dprintk("Index %x, PID %d, OnOff %d\n", id, pid, onoff);
18339a0bf528SMauro Carvalho Chehab 	ret = dib9000_write_word(state, 300 + 1 + id,
18349a0bf528SMauro Carvalho Chehab 			onoff ? (1 << 13) | pid : 0);
18359a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->demod_lock);
18369a0bf528SMauro Carvalho Chehab 	return ret;
18379a0bf528SMauro Carvalho Chehab }
18389a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib9000_fw_pid_filter);
18399a0bf528SMauro Carvalho Chehab 
dib9000_firmware_post_pll_init(struct dvb_frontend * fe)18409a0bf528SMauro Carvalho Chehab int dib9000_firmware_post_pll_init(struct dvb_frontend *fe)
18419a0bf528SMauro Carvalho Chehab {
18429a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
18439a0bf528SMauro Carvalho Chehab 	return dib9000_fw_init(state);
18449a0bf528SMauro Carvalho Chehab }
18459a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib9000_firmware_post_pll_init);
18469a0bf528SMauro Carvalho Chehab 
dib9000_release(struct dvb_frontend * demod)18479a0bf528SMauro Carvalho Chehab static void dib9000_release(struct dvb_frontend *demod)
18489a0bf528SMauro Carvalho Chehab {
18499a0bf528SMauro Carvalho Chehab 	struct dib9000_state *st = demod->demodulator_priv;
18509a0bf528SMauro Carvalho Chehab 	u8 index_frontend;
18519a0bf528SMauro Carvalho Chehab 
18529a0bf528SMauro Carvalho Chehab 	for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++)
18539a0bf528SMauro Carvalho Chehab 		dvb_frontend_detach(st->fe[index_frontend]);
18549a0bf528SMauro Carvalho Chehab 
18559a0bf528SMauro Carvalho Chehab 	dibx000_exit_i2c_master(&st->i2c_master);
18569a0bf528SMauro Carvalho Chehab 
18579a0bf528SMauro Carvalho Chehab 	i2c_del_adapter(&st->tuner_adap);
18589a0bf528SMauro Carvalho Chehab 	i2c_del_adapter(&st->component_bus);
18599a0bf528SMauro Carvalho Chehab 	kfree(st->fe[0]);
18609a0bf528SMauro Carvalho Chehab 	kfree(st);
18619a0bf528SMauro Carvalho Chehab }
18629a0bf528SMauro Carvalho Chehab 
dib9000_wakeup(struct dvb_frontend * fe)18639a0bf528SMauro Carvalho Chehab static int dib9000_wakeup(struct dvb_frontend *fe)
18649a0bf528SMauro Carvalho Chehab {
18659a0bf528SMauro Carvalho Chehab 	return 0;
18669a0bf528SMauro Carvalho Chehab }
18679a0bf528SMauro Carvalho Chehab 
dib9000_sleep(struct dvb_frontend * fe)18689a0bf528SMauro Carvalho Chehab static int dib9000_sleep(struct dvb_frontend *fe)
18699a0bf528SMauro Carvalho Chehab {
18709a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
18719a0bf528SMauro Carvalho Chehab 	u8 index_frontend;
18729a0bf528SMauro Carvalho Chehab 	int ret = 0;
18739a0bf528SMauro Carvalho Chehab 
18749a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->demod_lock) < 0) {
18753dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
18769a0bf528SMauro Carvalho Chehab 		return -EINTR;
18779a0bf528SMauro Carvalho Chehab 	}
18789a0bf528SMauro Carvalho Chehab 	for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
18799a0bf528SMauro Carvalho Chehab 		ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]);
18809a0bf528SMauro Carvalho Chehab 		if (ret < 0)
18819a0bf528SMauro Carvalho Chehab 			goto error;
18829a0bf528SMauro Carvalho Chehab 	}
18839a0bf528SMauro Carvalho Chehab 	ret = dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0);
18849a0bf528SMauro Carvalho Chehab 
18859a0bf528SMauro Carvalho Chehab error:
18869a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->demod_lock);
18879a0bf528SMauro Carvalho Chehab 	return ret;
18889a0bf528SMauro Carvalho Chehab }
18899a0bf528SMauro Carvalho Chehab 
dib9000_fe_get_tune_settings(struct dvb_frontend * fe,struct dvb_frontend_tune_settings * tune)18909a0bf528SMauro Carvalho Chehab static int dib9000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune)
18919a0bf528SMauro Carvalho Chehab {
18929a0bf528SMauro Carvalho Chehab 	tune->min_delay_ms = 1000;
18939a0bf528SMauro Carvalho Chehab 	return 0;
18949a0bf528SMauro Carvalho Chehab }
18959a0bf528SMauro Carvalho Chehab 
dib9000_get_frontend(struct dvb_frontend * fe,struct dtv_frontend_properties * c)18967e3e68bcSMauro Carvalho Chehab static int dib9000_get_frontend(struct dvb_frontend *fe,
18977e3e68bcSMauro Carvalho Chehab 				struct dtv_frontend_properties *c)
18989a0bf528SMauro Carvalho Chehab {
18999a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
19009a0bf528SMauro Carvalho Chehab 	u8 index_frontend, sub_index_frontend;
19010df289a2SMauro Carvalho Chehab 	enum fe_status stat;
19029a0bf528SMauro Carvalho Chehab 	int ret = 0;
19039a0bf528SMauro Carvalho Chehab 
19049a0bf528SMauro Carvalho Chehab 	if (state->get_frontend_internal == 0) {
19059a0bf528SMauro Carvalho Chehab 		if (mutex_lock_interruptible(&state->demod_lock) < 0) {
19063dd72262SMauro Carvalho Chehab 			dprintk("could not get the lock\n");
19079a0bf528SMauro Carvalho Chehab 			return -EINTR;
19089a0bf528SMauro Carvalho Chehab 		}
19099a0bf528SMauro Carvalho Chehab 	}
19109a0bf528SMauro Carvalho Chehab 
19119a0bf528SMauro Carvalho Chehab 	for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
19129a0bf528SMauro Carvalho Chehab 		state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat);
19139a0bf528SMauro Carvalho Chehab 		if (stat & FE_HAS_SYNC) {
19143dd72262SMauro Carvalho Chehab 			dprintk("TPS lock on the slave%i\n", index_frontend);
19159a0bf528SMauro Carvalho Chehab 
19169a0bf528SMauro Carvalho Chehab 			/* synchronize the cache with the other frontends */
19177e3e68bcSMauro Carvalho Chehab 			state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend], c);
19189a0bf528SMauro Carvalho Chehab 			for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL);
19199a0bf528SMauro Carvalho Chehab 			     sub_index_frontend++) {
19209a0bf528SMauro Carvalho Chehab 				if (sub_index_frontend != index_frontend) {
19219a0bf528SMauro Carvalho Chehab 					state->fe[sub_index_frontend]->dtv_property_cache.modulation =
19229a0bf528SMauro Carvalho Chehab 					    state->fe[index_frontend]->dtv_property_cache.modulation;
19239a0bf528SMauro Carvalho Chehab 					state->fe[sub_index_frontend]->dtv_property_cache.inversion =
19249a0bf528SMauro Carvalho Chehab 					    state->fe[index_frontend]->dtv_property_cache.inversion;
19259a0bf528SMauro Carvalho Chehab 					state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode =
19269a0bf528SMauro Carvalho Chehab 					    state->fe[index_frontend]->dtv_property_cache.transmission_mode;
19279a0bf528SMauro Carvalho Chehab 					state->fe[sub_index_frontend]->dtv_property_cache.guard_interval =
19289a0bf528SMauro Carvalho Chehab 					    state->fe[index_frontend]->dtv_property_cache.guard_interval;
19299a0bf528SMauro Carvalho Chehab 					state->fe[sub_index_frontend]->dtv_property_cache.hierarchy =
19309a0bf528SMauro Carvalho Chehab 					    state->fe[index_frontend]->dtv_property_cache.hierarchy;
19319a0bf528SMauro Carvalho Chehab 					state->fe[sub_index_frontend]->dtv_property_cache.code_rate_HP =
19329a0bf528SMauro Carvalho Chehab 					    state->fe[index_frontend]->dtv_property_cache.code_rate_HP;
19339a0bf528SMauro Carvalho Chehab 					state->fe[sub_index_frontend]->dtv_property_cache.code_rate_LP =
19349a0bf528SMauro Carvalho Chehab 					    state->fe[index_frontend]->dtv_property_cache.code_rate_LP;
19359a0bf528SMauro Carvalho Chehab 					state->fe[sub_index_frontend]->dtv_property_cache.rolloff =
19369a0bf528SMauro Carvalho Chehab 					    state->fe[index_frontend]->dtv_property_cache.rolloff;
19379a0bf528SMauro Carvalho Chehab 				}
19389a0bf528SMauro Carvalho Chehab 			}
19399a0bf528SMauro Carvalho Chehab 			ret = 0;
19409a0bf528SMauro Carvalho Chehab 			goto return_value;
19419a0bf528SMauro Carvalho Chehab 		}
19429a0bf528SMauro Carvalho Chehab 	}
19439a0bf528SMauro Carvalho Chehab 
19449a0bf528SMauro Carvalho Chehab 	/* get the channel from master chip */
19459a0bf528SMauro Carvalho Chehab 	ret = dib9000_fw_get_channel(fe);
19469a0bf528SMauro Carvalho Chehab 	if (ret != 0)
19479a0bf528SMauro Carvalho Chehab 		goto return_value;
19489a0bf528SMauro Carvalho Chehab 
19499a0bf528SMauro Carvalho Chehab 	/* synchronize the cache with the other frontends */
19509a0bf528SMauro Carvalho Chehab 	for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
19517e3e68bcSMauro Carvalho Chehab 		state->fe[index_frontend]->dtv_property_cache.inversion = c->inversion;
19527e3e68bcSMauro Carvalho Chehab 		state->fe[index_frontend]->dtv_property_cache.transmission_mode = c->transmission_mode;
19537e3e68bcSMauro Carvalho Chehab 		state->fe[index_frontend]->dtv_property_cache.guard_interval = c->guard_interval;
19547e3e68bcSMauro Carvalho Chehab 		state->fe[index_frontend]->dtv_property_cache.modulation = c->modulation;
19557e3e68bcSMauro Carvalho Chehab 		state->fe[index_frontend]->dtv_property_cache.hierarchy = c->hierarchy;
19567e3e68bcSMauro Carvalho Chehab 		state->fe[index_frontend]->dtv_property_cache.code_rate_HP = c->code_rate_HP;
19577e3e68bcSMauro Carvalho Chehab 		state->fe[index_frontend]->dtv_property_cache.code_rate_LP = c->code_rate_LP;
19587e3e68bcSMauro Carvalho Chehab 		state->fe[index_frontend]->dtv_property_cache.rolloff = c->rolloff;
19599a0bf528SMauro Carvalho Chehab 	}
19609a0bf528SMauro Carvalho Chehab 	ret = 0;
19619a0bf528SMauro Carvalho Chehab 
19629a0bf528SMauro Carvalho Chehab return_value:
19639a0bf528SMauro Carvalho Chehab 	if (state->get_frontend_internal == 0)
19649a0bf528SMauro Carvalho Chehab 		mutex_unlock(&state->demod_lock);
19659a0bf528SMauro Carvalho Chehab 	return ret;
19669a0bf528SMauro Carvalho Chehab }
19679a0bf528SMauro Carvalho Chehab 
dib9000_set_tune_state(struct dvb_frontend * fe,enum frontend_tune_state tune_state)19689a0bf528SMauro Carvalho Chehab static int dib9000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state)
19699a0bf528SMauro Carvalho Chehab {
19709a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
19719a0bf528SMauro Carvalho Chehab 	state->tune_state = tune_state;
19729a0bf528SMauro Carvalho Chehab 	if (tune_state == CT_DEMOD_START)
19739a0bf528SMauro Carvalho Chehab 		state->status = FE_STATUS_TUNE_PENDING;
19749a0bf528SMauro Carvalho Chehab 
19759a0bf528SMauro Carvalho Chehab 	return 0;
19769a0bf528SMauro Carvalho Chehab }
19779a0bf528SMauro Carvalho Chehab 
dib9000_get_status(struct dvb_frontend * fe)19789a0bf528SMauro Carvalho Chehab static u32 dib9000_get_status(struct dvb_frontend *fe)
19799a0bf528SMauro Carvalho Chehab {
19809a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
19819a0bf528SMauro Carvalho Chehab 	return state->status;
19829a0bf528SMauro Carvalho Chehab }
19839a0bf528SMauro Carvalho Chehab 
dib9000_set_channel_status(struct dvb_frontend * fe,struct dvb_frontend_parametersContext * channel_status)19849a0bf528SMauro Carvalho Chehab static int dib9000_set_channel_status(struct dvb_frontend *fe, struct dvb_frontend_parametersContext *channel_status)
19859a0bf528SMauro Carvalho Chehab {
19869a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
19879a0bf528SMauro Carvalho Chehab 
19889a0bf528SMauro Carvalho Chehab 	memcpy(&state->channel_status, channel_status, sizeof(struct dvb_frontend_parametersContext));
19899a0bf528SMauro Carvalho Chehab 	return 0;
19909a0bf528SMauro Carvalho Chehab }
19919a0bf528SMauro Carvalho Chehab 
dib9000_set_frontend(struct dvb_frontend * fe)19929a0bf528SMauro Carvalho Chehab static int dib9000_set_frontend(struct dvb_frontend *fe)
19939a0bf528SMauro Carvalho Chehab {
19949a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
19959a0bf528SMauro Carvalho Chehab 	int sleep_time, sleep_time_slave;
19969a0bf528SMauro Carvalho Chehab 	u32 frontend_status;
19979a0bf528SMauro Carvalho Chehab 	u8 nbr_pending, exit_condition, index_frontend, index_frontend_success;
19989a0bf528SMauro Carvalho Chehab 	struct dvb_frontend_parametersContext channel_status;
19999a0bf528SMauro Carvalho Chehab 
20009a0bf528SMauro Carvalho Chehab 	/* check that the correct parameters are set */
20019a0bf528SMauro Carvalho Chehab 	if (state->fe[0]->dtv_property_cache.frequency == 0) {
20023dd72262SMauro Carvalho Chehab 		dprintk("dib9000: must specify frequency\n");
20039a0bf528SMauro Carvalho Chehab 		return 0;
20049a0bf528SMauro Carvalho Chehab 	}
20059a0bf528SMauro Carvalho Chehab 
20069a0bf528SMauro Carvalho Chehab 	if (state->fe[0]->dtv_property_cache.bandwidth_hz == 0) {
20073dd72262SMauro Carvalho Chehab 		dprintk("dib9000: must specify bandwidth\n");
20089a0bf528SMauro Carvalho Chehab 		return 0;
20099a0bf528SMauro Carvalho Chehab 	}
20109a0bf528SMauro Carvalho Chehab 
20119a0bf528SMauro Carvalho Chehab 	state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */
20129a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->demod_lock) < 0) {
20133dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
20149a0bf528SMauro Carvalho Chehab 		return 0;
20159a0bf528SMauro Carvalho Chehab 	}
20169a0bf528SMauro Carvalho Chehab 
20179a0bf528SMauro Carvalho Chehab 	fe->dtv_property_cache.delivery_system = SYS_DVBT;
20189a0bf528SMauro Carvalho Chehab 
20199a0bf528SMauro Carvalho Chehab 	/* set the master status */
20209a0bf528SMauro Carvalho Chehab 	if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO ||
20219a0bf528SMauro Carvalho Chehab 	    state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO ||
20229a0bf528SMauro Carvalho Chehab 	    state->fe[0]->dtv_property_cache.modulation == QAM_AUTO ||
20239a0bf528SMauro Carvalho Chehab 	    state->fe[0]->dtv_property_cache.code_rate_HP == FEC_AUTO) {
20249a0bf528SMauro Carvalho Chehab 		/* no channel specified, autosearch the channel */
20259a0bf528SMauro Carvalho Chehab 		state->channel_status.status = CHANNEL_STATUS_PARAMETERS_UNKNOWN;
20269a0bf528SMauro Carvalho Chehab 	} else
20279a0bf528SMauro Carvalho Chehab 		state->channel_status.status = CHANNEL_STATUS_PARAMETERS_SET;
20289a0bf528SMauro Carvalho Chehab 
20299a0bf528SMauro Carvalho Chehab 	/* set mode and status for the different frontends */
20309a0bf528SMauro Carvalho Chehab 	for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
20319a0bf528SMauro Carvalho Chehab 		dib9000_fw_set_diversity_in(state->fe[index_frontend], 1);
20329a0bf528SMauro Carvalho Chehab 
20339a0bf528SMauro Carvalho Chehab 		/* synchronization of the cache */
20349a0bf528SMauro Carvalho Chehab 		memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties));
20359a0bf528SMauro Carvalho Chehab 
20369a0bf528SMauro Carvalho Chehab 		state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_DVBT;
20379a0bf528SMauro Carvalho Chehab 		dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_HIGH_Z);
20389a0bf528SMauro Carvalho Chehab 
20399a0bf528SMauro Carvalho Chehab 		dib9000_set_channel_status(state->fe[index_frontend], &state->channel_status);
20409a0bf528SMauro Carvalho Chehab 		dib9000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START);
20419a0bf528SMauro Carvalho Chehab 	}
20429a0bf528SMauro Carvalho Chehab 
20439a0bf528SMauro Carvalho Chehab 	/* actual tune */
20449a0bf528SMauro Carvalho Chehab 	exit_condition = 0;	/* 0: tune pending; 1: tune failed; 2:tune success */
20459a0bf528SMauro Carvalho Chehab 	index_frontend_success = 0;
20469a0bf528SMauro Carvalho Chehab 	do {
20479a0bf528SMauro Carvalho Chehab 		sleep_time = dib9000_fw_tune(state->fe[0]);
20489a0bf528SMauro Carvalho Chehab 		for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
20499a0bf528SMauro Carvalho Chehab 			sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend]);
20509a0bf528SMauro Carvalho Chehab 			if (sleep_time == FE_CALLBACK_TIME_NEVER)
20519a0bf528SMauro Carvalho Chehab 				sleep_time = sleep_time_slave;
20529a0bf528SMauro Carvalho Chehab 			else if ((sleep_time_slave != FE_CALLBACK_TIME_NEVER) && (sleep_time_slave > sleep_time))
20539a0bf528SMauro Carvalho Chehab 				sleep_time = sleep_time_slave;
20549a0bf528SMauro Carvalho Chehab 		}
20559a0bf528SMauro Carvalho Chehab 		if (sleep_time != FE_CALLBACK_TIME_NEVER)
20569a0bf528SMauro Carvalho Chehab 			msleep(sleep_time / 10);
20579a0bf528SMauro Carvalho Chehab 		else
20589a0bf528SMauro Carvalho Chehab 			break;
20599a0bf528SMauro Carvalho Chehab 
20609a0bf528SMauro Carvalho Chehab 		nbr_pending = 0;
20619a0bf528SMauro Carvalho Chehab 		exit_condition = 0;
20629a0bf528SMauro Carvalho Chehab 		index_frontend_success = 0;
20639a0bf528SMauro Carvalho Chehab 		for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
20649a0bf528SMauro Carvalho Chehab 			frontend_status = -dib9000_get_status(state->fe[index_frontend]);
20659a0bf528SMauro Carvalho Chehab 			if (frontend_status > -FE_STATUS_TUNE_PENDING) {
20669a0bf528SMauro Carvalho Chehab 				exit_condition = 2;	/* tune success */
20679a0bf528SMauro Carvalho Chehab 				index_frontend_success = index_frontend;
20689a0bf528SMauro Carvalho Chehab 				break;
20699a0bf528SMauro Carvalho Chehab 			}
20709a0bf528SMauro Carvalho Chehab 			if (frontend_status == -FE_STATUS_TUNE_PENDING)
20719a0bf528SMauro Carvalho Chehab 				nbr_pending++;	/* some frontends are still tuning */
20729a0bf528SMauro Carvalho Chehab 		}
20739a0bf528SMauro Carvalho Chehab 		if ((exit_condition != 2) && (nbr_pending == 0))
20749a0bf528SMauro Carvalho Chehab 			exit_condition = 1;	/* if all tune are done and no success, exit: tune failed */
20759a0bf528SMauro Carvalho Chehab 
20769a0bf528SMauro Carvalho Chehab 	} while (exit_condition == 0);
20779a0bf528SMauro Carvalho Chehab 
20789a0bf528SMauro Carvalho Chehab 	/* check the tune result */
20799a0bf528SMauro Carvalho Chehab 	if (exit_condition == 1) {	/* tune failed */
20803dd72262SMauro Carvalho Chehab 		dprintk("tune failed\n");
20819a0bf528SMauro Carvalho Chehab 		mutex_unlock(&state->demod_lock);
20829a0bf528SMauro Carvalho Chehab 		/* tune failed; put all the pid filtering cmd to junk */
20839a0bf528SMauro Carvalho Chehab 		state->pid_ctrl_index = -1;
20849a0bf528SMauro Carvalho Chehab 		return 0;
20859a0bf528SMauro Carvalho Chehab 	}
20869a0bf528SMauro Carvalho Chehab 
20873dd72262SMauro Carvalho Chehab 	dprintk("tune success on frontend%i\n", index_frontend_success);
20889a0bf528SMauro Carvalho Chehab 
20899a0bf528SMauro Carvalho Chehab 	/* synchronize all the channel cache */
20909a0bf528SMauro Carvalho Chehab 	state->get_frontend_internal = 1;
20917e3e68bcSMauro Carvalho Chehab 	dib9000_get_frontend(state->fe[0], &state->fe[0]->dtv_property_cache);
20929a0bf528SMauro Carvalho Chehab 	state->get_frontend_internal = 0;
20939a0bf528SMauro Carvalho Chehab 
20949a0bf528SMauro Carvalho Chehab 	/* retune the other frontends with the found channel */
20959a0bf528SMauro Carvalho Chehab 	channel_status.status = CHANNEL_STATUS_PARAMETERS_SET;
20969a0bf528SMauro Carvalho Chehab 	for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
20979a0bf528SMauro Carvalho Chehab 		/* only retune the frontends which was not tuned success */
20989a0bf528SMauro Carvalho Chehab 		if (index_frontend != index_frontend_success) {
20999a0bf528SMauro Carvalho Chehab 			dib9000_set_channel_status(state->fe[index_frontend], &channel_status);
21009a0bf528SMauro Carvalho Chehab 			dib9000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START);
21019a0bf528SMauro Carvalho Chehab 		}
21029a0bf528SMauro Carvalho Chehab 	}
21039a0bf528SMauro Carvalho Chehab 	do {
21049a0bf528SMauro Carvalho Chehab 		sleep_time = FE_CALLBACK_TIME_NEVER;
21059a0bf528SMauro Carvalho Chehab 		for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
21069a0bf528SMauro Carvalho Chehab 			if (index_frontend != index_frontend_success) {
21079a0bf528SMauro Carvalho Chehab 				sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend]);
21089a0bf528SMauro Carvalho Chehab 				if (sleep_time == FE_CALLBACK_TIME_NEVER)
21099a0bf528SMauro Carvalho Chehab 					sleep_time = sleep_time_slave;
21109a0bf528SMauro Carvalho Chehab 				else if ((sleep_time_slave != FE_CALLBACK_TIME_NEVER) && (sleep_time_slave > sleep_time))
21119a0bf528SMauro Carvalho Chehab 					sleep_time = sleep_time_slave;
21129a0bf528SMauro Carvalho Chehab 			}
21139a0bf528SMauro Carvalho Chehab 		}
21149a0bf528SMauro Carvalho Chehab 		if (sleep_time != FE_CALLBACK_TIME_NEVER)
21159a0bf528SMauro Carvalho Chehab 			msleep(sleep_time / 10);
21169a0bf528SMauro Carvalho Chehab 		else
21179a0bf528SMauro Carvalho Chehab 			break;
21189a0bf528SMauro Carvalho Chehab 
21199a0bf528SMauro Carvalho Chehab 		nbr_pending = 0;
21209a0bf528SMauro Carvalho Chehab 		for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
21219a0bf528SMauro Carvalho Chehab 			if (index_frontend != index_frontend_success) {
21229a0bf528SMauro Carvalho Chehab 				frontend_status = -dib9000_get_status(state->fe[index_frontend]);
21239a0bf528SMauro Carvalho Chehab 				if ((index_frontend != index_frontend_success) && (frontend_status == -FE_STATUS_TUNE_PENDING))
21249a0bf528SMauro Carvalho Chehab 					nbr_pending++;	/* some frontends are still tuning */
21259a0bf528SMauro Carvalho Chehab 			}
21269a0bf528SMauro Carvalho Chehab 		}
21279a0bf528SMauro Carvalho Chehab 	} while (nbr_pending != 0);
21289a0bf528SMauro Carvalho Chehab 
21299a0bf528SMauro Carvalho Chehab 	/* set the output mode */
21309a0bf528SMauro Carvalho Chehab 	dib9000_fw_set_output_mode(state->fe[0], state->chip.d9.cfg.output_mode);
21319a0bf528SMauro Carvalho Chehab 	for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
21329a0bf528SMauro Carvalho Chehab 		dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_DIVERSITY);
21339a0bf528SMauro Carvalho Chehab 
21349a0bf528SMauro Carvalho Chehab 	/* turn off the diversity for the last frontend */
21359a0bf528SMauro Carvalho Chehab 	dib9000_fw_set_diversity_in(state->fe[index_frontend - 1], 0);
21369a0bf528SMauro Carvalho Chehab 
21379a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->demod_lock);
21389a0bf528SMauro Carvalho Chehab 	if (state->pid_ctrl_index >= 0) {
21399a0bf528SMauro Carvalho Chehab 		u8 index_pid_filter_cmd;
21409a0bf528SMauro Carvalho Chehab 		u8 pid_ctrl_index = state->pid_ctrl_index;
21419a0bf528SMauro Carvalho Chehab 
21429a0bf528SMauro Carvalho Chehab 		state->pid_ctrl_index = -2;
21439a0bf528SMauro Carvalho Chehab 		for (index_pid_filter_cmd = 0;
21449a0bf528SMauro Carvalho Chehab 				index_pid_filter_cmd <= pid_ctrl_index;
21459a0bf528SMauro Carvalho Chehab 				index_pid_filter_cmd++) {
21469a0bf528SMauro Carvalho Chehab 			if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER_CTRL)
21479a0bf528SMauro Carvalho Chehab 				dib9000_fw_pid_filter_ctrl(state->fe[0],
21489a0bf528SMauro Carvalho Chehab 						state->pid_ctrl[index_pid_filter_cmd].onoff);
21499a0bf528SMauro Carvalho Chehab 			else if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER)
21509a0bf528SMauro Carvalho Chehab 				dib9000_fw_pid_filter(state->fe[0],
21519a0bf528SMauro Carvalho Chehab 						state->pid_ctrl[index_pid_filter_cmd].id,
21529a0bf528SMauro Carvalho Chehab 						state->pid_ctrl[index_pid_filter_cmd].pid,
21539a0bf528SMauro Carvalho Chehab 						state->pid_ctrl[index_pid_filter_cmd].onoff);
21549a0bf528SMauro Carvalho Chehab 		}
21559a0bf528SMauro Carvalho Chehab 	}
21569a0bf528SMauro Carvalho Chehab 	/* do not postpone any more the pid filtering */
21579a0bf528SMauro Carvalho Chehab 	state->pid_ctrl_index = -2;
21589a0bf528SMauro Carvalho Chehab 
21599a0bf528SMauro Carvalho Chehab 	return 0;
21609a0bf528SMauro Carvalho Chehab }
21619a0bf528SMauro Carvalho Chehab 
dib9000_read_lock(struct dvb_frontend * fe)21629a0bf528SMauro Carvalho Chehab static u16 dib9000_read_lock(struct dvb_frontend *fe)
21639a0bf528SMauro Carvalho Chehab {
21649a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
21659a0bf528SMauro Carvalho Chehab 
21669a0bf528SMauro Carvalho Chehab 	return dib9000_read_word(state, 535);
21679a0bf528SMauro Carvalho Chehab }
21689a0bf528SMauro Carvalho Chehab 
dib9000_read_status(struct dvb_frontend * fe,enum fe_status * stat)21690df289a2SMauro Carvalho Chehab static int dib9000_read_status(struct dvb_frontend *fe, enum fe_status *stat)
21709a0bf528SMauro Carvalho Chehab {
21719a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
21729a0bf528SMauro Carvalho Chehab 	u8 index_frontend;
21739a0bf528SMauro Carvalho Chehab 	u16 lock = 0, lock_slave = 0;
21749a0bf528SMauro Carvalho Chehab 
21759a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->demod_lock) < 0) {
21763dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
21779a0bf528SMauro Carvalho Chehab 		return -EINTR;
21789a0bf528SMauro Carvalho Chehab 	}
21799a0bf528SMauro Carvalho Chehab 	for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
21809a0bf528SMauro Carvalho Chehab 		lock_slave |= dib9000_read_lock(state->fe[index_frontend]);
21819a0bf528SMauro Carvalho Chehab 
21829a0bf528SMauro Carvalho Chehab 	lock = dib9000_read_word(state, 535);
21839a0bf528SMauro Carvalho Chehab 
21849a0bf528SMauro Carvalho Chehab 	*stat = 0;
21859a0bf528SMauro Carvalho Chehab 
21869a0bf528SMauro Carvalho Chehab 	if ((lock & 0x8000) || (lock_slave & 0x8000))
21879a0bf528SMauro Carvalho Chehab 		*stat |= FE_HAS_SIGNAL;
21889a0bf528SMauro Carvalho Chehab 	if ((lock & 0x3000) || (lock_slave & 0x3000))
21899a0bf528SMauro Carvalho Chehab 		*stat |= FE_HAS_CARRIER;
21909a0bf528SMauro Carvalho Chehab 	if ((lock & 0x0100) || (lock_slave & 0x0100))
21919a0bf528SMauro Carvalho Chehab 		*stat |= FE_HAS_VITERBI;
21929a0bf528SMauro Carvalho Chehab 	if (((lock & 0x0038) == 0x38) || ((lock_slave & 0x0038) == 0x38))
21939a0bf528SMauro Carvalho Chehab 		*stat |= FE_HAS_SYNC;
21949a0bf528SMauro Carvalho Chehab 	if ((lock & 0x0008) || (lock_slave & 0x0008))
21959a0bf528SMauro Carvalho Chehab 		*stat |= FE_HAS_LOCK;
21969a0bf528SMauro Carvalho Chehab 
21979a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->demod_lock);
21989a0bf528SMauro Carvalho Chehab 
21999a0bf528SMauro Carvalho Chehab 	return 0;
22009a0bf528SMauro Carvalho Chehab }
22019a0bf528SMauro Carvalho Chehab 
dib9000_read_ber(struct dvb_frontend * fe,u32 * ber)22029a0bf528SMauro Carvalho Chehab static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber)
22039a0bf528SMauro Carvalho Chehab {
22049a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
22059a0bf528SMauro Carvalho Chehab 	u16 *c;
22069a0bf528SMauro Carvalho Chehab 	int ret = 0;
22079a0bf528SMauro Carvalho Chehab 
22089a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->demod_lock) < 0) {
22093dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
22109a0bf528SMauro Carvalho Chehab 		return -EINTR;
22119a0bf528SMauro Carvalho Chehab 	}
22129a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
22133dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
22149a0bf528SMauro Carvalho Chehab 		ret = -EINTR;
22159a0bf528SMauro Carvalho Chehab 		goto error;
22169a0bf528SMauro Carvalho Chehab 	}
22179a0bf528SMauro Carvalho Chehab 	if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
22189a0bf528SMauro Carvalho Chehab 		mutex_unlock(&state->platform.risc.mem_mbx_lock);
22199a0bf528SMauro Carvalho Chehab 		ret = -EIO;
22209a0bf528SMauro Carvalho Chehab 		goto error;
22219a0bf528SMauro Carvalho Chehab 	}
22229a0bf528SMauro Carvalho Chehab 	dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR,
22239a0bf528SMauro Carvalho Chehab 			state->i2c_read_buffer, 16 * 2);
22249a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->platform.risc.mem_mbx_lock);
22259a0bf528SMauro Carvalho Chehab 
22269a0bf528SMauro Carvalho Chehab 	c = (u16 *)state->i2c_read_buffer;
22279a0bf528SMauro Carvalho Chehab 
22289a0bf528SMauro Carvalho Chehab 	*ber = c[10] << 16 | c[11];
22299a0bf528SMauro Carvalho Chehab 
22309a0bf528SMauro Carvalho Chehab error:
22319a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->demod_lock);
22329a0bf528SMauro Carvalho Chehab 	return ret;
22339a0bf528SMauro Carvalho Chehab }
22349a0bf528SMauro Carvalho Chehab 
dib9000_read_signal_strength(struct dvb_frontend * fe,u16 * strength)22359a0bf528SMauro Carvalho Chehab static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
22369a0bf528SMauro Carvalho Chehab {
22379a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
22389a0bf528SMauro Carvalho Chehab 	u8 index_frontend;
22399a0bf528SMauro Carvalho Chehab 	u16 *c = (u16 *)state->i2c_read_buffer;
22409a0bf528SMauro Carvalho Chehab 	u16 val;
22419a0bf528SMauro Carvalho Chehab 	int ret = 0;
22429a0bf528SMauro Carvalho Chehab 
22439a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->demod_lock) < 0) {
22443dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
22459a0bf528SMauro Carvalho Chehab 		return -EINTR;
22469a0bf528SMauro Carvalho Chehab 	}
22479a0bf528SMauro Carvalho Chehab 	*strength = 0;
22489a0bf528SMauro Carvalho Chehab 	for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
22499a0bf528SMauro Carvalho Chehab 		state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val);
22509a0bf528SMauro Carvalho Chehab 		if (val > 65535 - *strength)
22519a0bf528SMauro Carvalho Chehab 			*strength = 65535;
22529a0bf528SMauro Carvalho Chehab 		else
22539a0bf528SMauro Carvalho Chehab 			*strength += val;
22549a0bf528SMauro Carvalho Chehab 	}
22559a0bf528SMauro Carvalho Chehab 
22569a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
22573dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
22589a0bf528SMauro Carvalho Chehab 		ret = -EINTR;
22599a0bf528SMauro Carvalho Chehab 		goto error;
22609a0bf528SMauro Carvalho Chehab 	}
22619a0bf528SMauro Carvalho Chehab 	if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
22629a0bf528SMauro Carvalho Chehab 		mutex_unlock(&state->platform.risc.mem_mbx_lock);
22639a0bf528SMauro Carvalho Chehab 		ret = -EIO;
22649a0bf528SMauro Carvalho Chehab 		goto error;
22659a0bf528SMauro Carvalho Chehab 	}
22669a0bf528SMauro Carvalho Chehab 	dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2);
22679a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->platform.risc.mem_mbx_lock);
22689a0bf528SMauro Carvalho Chehab 
22699a0bf528SMauro Carvalho Chehab 	val = 65535 - c[4];
22709a0bf528SMauro Carvalho Chehab 	if (val > 65535 - *strength)
22719a0bf528SMauro Carvalho Chehab 		*strength = 65535;
22729a0bf528SMauro Carvalho Chehab 	else
22739a0bf528SMauro Carvalho Chehab 		*strength += val;
22749a0bf528SMauro Carvalho Chehab 
22759a0bf528SMauro Carvalho Chehab error:
22769a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->demod_lock);
22779a0bf528SMauro Carvalho Chehab 	return ret;
22789a0bf528SMauro Carvalho Chehab }
22799a0bf528SMauro Carvalho Chehab 
dib9000_get_snr(struct dvb_frontend * fe)22809a0bf528SMauro Carvalho Chehab static u32 dib9000_get_snr(struct dvb_frontend *fe)
22819a0bf528SMauro Carvalho Chehab {
22829a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
22839a0bf528SMauro Carvalho Chehab 	u16 *c = (u16 *)state->i2c_read_buffer;
22849a0bf528SMauro Carvalho Chehab 	u32 n, s, exp;
22859a0bf528SMauro Carvalho Chehab 	u16 val;
22869a0bf528SMauro Carvalho Chehab 
22879a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
22883dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
22899a0bf528SMauro Carvalho Chehab 		return 0;
22909a0bf528SMauro Carvalho Chehab 	}
22919a0bf528SMauro Carvalho Chehab 	if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
22929a0bf528SMauro Carvalho Chehab 		mutex_unlock(&state->platform.risc.mem_mbx_lock);
22939a0bf528SMauro Carvalho Chehab 		return 0;
22949a0bf528SMauro Carvalho Chehab 	}
22959a0bf528SMauro Carvalho Chehab 	dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2);
22969a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->platform.risc.mem_mbx_lock);
22979a0bf528SMauro Carvalho Chehab 
22989a0bf528SMauro Carvalho Chehab 	val = c[7];
22999a0bf528SMauro Carvalho Chehab 	n = (val >> 4) & 0xff;
23009a0bf528SMauro Carvalho Chehab 	exp = ((val & 0xf) << 2);
23019a0bf528SMauro Carvalho Chehab 	val = c[8];
23029a0bf528SMauro Carvalho Chehab 	exp += ((val >> 14) & 0x3);
23039a0bf528SMauro Carvalho Chehab 	if ((exp & 0x20) != 0)
23049a0bf528SMauro Carvalho Chehab 		exp -= 0x40;
23059a0bf528SMauro Carvalho Chehab 	n <<= exp + 16;
23069a0bf528SMauro Carvalho Chehab 
23079a0bf528SMauro Carvalho Chehab 	s = (val >> 6) & 0xFF;
23089a0bf528SMauro Carvalho Chehab 	exp = (val & 0x3F);
23099a0bf528SMauro Carvalho Chehab 	if ((exp & 0x20) != 0)
23109a0bf528SMauro Carvalho Chehab 		exp -= 0x40;
23119a0bf528SMauro Carvalho Chehab 	s <<= exp + 16;
23129a0bf528SMauro Carvalho Chehab 
23139a0bf528SMauro Carvalho Chehab 	if (n > 0) {
23149a0bf528SMauro Carvalho Chehab 		u32 t = (s / n) << 16;
23159a0bf528SMauro Carvalho Chehab 		return t + ((s << 16) - n * t) / n;
23169a0bf528SMauro Carvalho Chehab 	}
23179a0bf528SMauro Carvalho Chehab 	return 0xffffffff;
23189a0bf528SMauro Carvalho Chehab }
23199a0bf528SMauro Carvalho Chehab 
dib9000_read_snr(struct dvb_frontend * fe,u16 * snr)23209a0bf528SMauro Carvalho Chehab static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr)
23219a0bf528SMauro Carvalho Chehab {
23229a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
23239a0bf528SMauro Carvalho Chehab 	u8 index_frontend;
23249a0bf528SMauro Carvalho Chehab 	u32 snr_master;
23259a0bf528SMauro Carvalho Chehab 
23269a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->demod_lock) < 0) {
23273dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
23289a0bf528SMauro Carvalho Chehab 		return -EINTR;
23299a0bf528SMauro Carvalho Chehab 	}
23309a0bf528SMauro Carvalho Chehab 	snr_master = dib9000_get_snr(fe);
23319a0bf528SMauro Carvalho Chehab 	for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
23329a0bf528SMauro Carvalho Chehab 		snr_master += dib9000_get_snr(state->fe[index_frontend]);
23339a0bf528SMauro Carvalho Chehab 
23349a0bf528SMauro Carvalho Chehab 	if ((snr_master >> 16) != 0) {
23359a0bf528SMauro Carvalho Chehab 		snr_master = 10 * intlog10(snr_master >> 16);
23369a0bf528SMauro Carvalho Chehab 		*snr = snr_master / ((1 << 24) / 10);
23379a0bf528SMauro Carvalho Chehab 	} else
23389a0bf528SMauro Carvalho Chehab 		*snr = 0;
23399a0bf528SMauro Carvalho Chehab 
23409a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->demod_lock);
23419a0bf528SMauro Carvalho Chehab 
23429a0bf528SMauro Carvalho Chehab 	return 0;
23439a0bf528SMauro Carvalho Chehab }
23449a0bf528SMauro Carvalho Chehab 
dib9000_read_unc_blocks(struct dvb_frontend * fe,u32 * unc)23459a0bf528SMauro Carvalho Chehab static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc)
23469a0bf528SMauro Carvalho Chehab {
23479a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
23489a0bf528SMauro Carvalho Chehab 	u16 *c = (u16 *)state->i2c_read_buffer;
23499a0bf528SMauro Carvalho Chehab 	int ret = 0;
23509a0bf528SMauro Carvalho Chehab 
23519a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->demod_lock) < 0) {
23523dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
23539a0bf528SMauro Carvalho Chehab 		return -EINTR;
23549a0bf528SMauro Carvalho Chehab 	}
23559a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
23563dd72262SMauro Carvalho Chehab 		dprintk("could not get the lock\n");
23579a0bf528SMauro Carvalho Chehab 		ret = -EINTR;
23589a0bf528SMauro Carvalho Chehab 		goto error;
23599a0bf528SMauro Carvalho Chehab 	}
23609a0bf528SMauro Carvalho Chehab 	if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
23619a0bf528SMauro Carvalho Chehab 		mutex_unlock(&state->platform.risc.mem_mbx_lock);
23629a0bf528SMauro Carvalho Chehab 		ret = -EIO;
23639a0bf528SMauro Carvalho Chehab 		goto error;
23649a0bf528SMauro Carvalho Chehab 	}
23659a0bf528SMauro Carvalho Chehab 	dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2);
23669a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->platform.risc.mem_mbx_lock);
23679a0bf528SMauro Carvalho Chehab 
23689a0bf528SMauro Carvalho Chehab 	*unc = c[12];
23699a0bf528SMauro Carvalho Chehab 
23709a0bf528SMauro Carvalho Chehab error:
23719a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->demod_lock);
23729a0bf528SMauro Carvalho Chehab 	return ret;
23739a0bf528SMauro Carvalho Chehab }
23749a0bf528SMauro Carvalho Chehab 
dib9000_i2c_enumeration(struct i2c_adapter * i2c,int no_of_demods,u8 default_addr,u8 first_addr)23759a0bf528SMauro Carvalho Chehab int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, u8 first_addr)
23769a0bf528SMauro Carvalho Chehab {
23779a0bf528SMauro Carvalho Chehab 	int k = 0, ret = 0;
23789a0bf528SMauro Carvalho Chehab 	u8 new_addr = 0;
23799a0bf528SMauro Carvalho Chehab 	struct i2c_device client = {.i2c_adap = i2c };
23809a0bf528SMauro Carvalho Chehab 
23816396bb22SKees Cook 	client.i2c_write_buffer = kzalloc(4, GFP_KERNEL);
23829a0bf528SMauro Carvalho Chehab 	if (!client.i2c_write_buffer) {
23833dd72262SMauro Carvalho Chehab 		dprintk("%s: not enough memory\n", __func__);
23849a0bf528SMauro Carvalho Chehab 		return -ENOMEM;
23859a0bf528SMauro Carvalho Chehab 	}
23866396bb22SKees Cook 	client.i2c_read_buffer = kzalloc(4, GFP_KERNEL);
23879a0bf528SMauro Carvalho Chehab 	if (!client.i2c_read_buffer) {
23883dd72262SMauro Carvalho Chehab 		dprintk("%s: not enough memory\n", __func__);
23899a0bf528SMauro Carvalho Chehab 		ret = -ENOMEM;
23909a0bf528SMauro Carvalho Chehab 		goto error_memory;
23919a0bf528SMauro Carvalho Chehab 	}
23929a0bf528SMauro Carvalho Chehab 
23939a0bf528SMauro Carvalho Chehab 	client.i2c_addr = default_addr + 16;
23949a0bf528SMauro Carvalho Chehab 	dib9000_i2c_write16(&client, 1796, 0x0);
23959a0bf528SMauro Carvalho Chehab 
23969a0bf528SMauro Carvalho Chehab 	for (k = no_of_demods - 1; k >= 0; k--) {
23979a0bf528SMauro Carvalho Chehab 		/* designated i2c address */
23989a0bf528SMauro Carvalho Chehab 		new_addr = first_addr + (k << 1);
23999a0bf528SMauro Carvalho Chehab 		client.i2c_addr = default_addr;
24009a0bf528SMauro Carvalho Chehab 
24019a0bf528SMauro Carvalho Chehab 		dib9000_i2c_write16(&client, 1817, 3);
24029a0bf528SMauro Carvalho Chehab 		dib9000_i2c_write16(&client, 1796, 0);
24039a0bf528SMauro Carvalho Chehab 		dib9000_i2c_write16(&client, 1227, 1);
24049a0bf528SMauro Carvalho Chehab 		dib9000_i2c_write16(&client, 1227, 0);
24059a0bf528SMauro Carvalho Chehab 
24069a0bf528SMauro Carvalho Chehab 		client.i2c_addr = new_addr;
24079a0bf528SMauro Carvalho Chehab 		dib9000_i2c_write16(&client, 1817, 3);
24089a0bf528SMauro Carvalho Chehab 		dib9000_i2c_write16(&client, 1796, 0);
24099a0bf528SMauro Carvalho Chehab 		dib9000_i2c_write16(&client, 1227, 1);
24109a0bf528SMauro Carvalho Chehab 		dib9000_i2c_write16(&client, 1227, 0);
24119a0bf528SMauro Carvalho Chehab 
24129a0bf528SMauro Carvalho Chehab 		if (dib9000_identify(&client) == 0) {
24139a0bf528SMauro Carvalho Chehab 			client.i2c_addr = default_addr;
24149a0bf528SMauro Carvalho Chehab 			if (dib9000_identify(&client) == 0) {
24153dd72262SMauro Carvalho Chehab 				dprintk("DiB9000 #%d: not identified\n", k);
24169a0bf528SMauro Carvalho Chehab 				ret = -EIO;
24179a0bf528SMauro Carvalho Chehab 				goto error;
24189a0bf528SMauro Carvalho Chehab 			}
24199a0bf528SMauro Carvalho Chehab 		}
24209a0bf528SMauro Carvalho Chehab 
24219a0bf528SMauro Carvalho Chehab 		dib9000_i2c_write16(&client, 1795, (1 << 10) | (4 << 6));
24229a0bf528SMauro Carvalho Chehab 		dib9000_i2c_write16(&client, 1794, (new_addr << 2) | 2);
24239a0bf528SMauro Carvalho Chehab 
24243dd72262SMauro Carvalho Chehab 		dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr);
24259a0bf528SMauro Carvalho Chehab 	}
24269a0bf528SMauro Carvalho Chehab 
24279a0bf528SMauro Carvalho Chehab 	for (k = 0; k < no_of_demods; k++) {
24289a0bf528SMauro Carvalho Chehab 		new_addr = first_addr | (k << 1);
24299a0bf528SMauro Carvalho Chehab 		client.i2c_addr = new_addr;
24309a0bf528SMauro Carvalho Chehab 
24319a0bf528SMauro Carvalho Chehab 		dib9000_i2c_write16(&client, 1794, (new_addr << 2));
24329a0bf528SMauro Carvalho Chehab 		dib9000_i2c_write16(&client, 1795, 0);
24339a0bf528SMauro Carvalho Chehab 	}
24349a0bf528SMauro Carvalho Chehab 
24359a0bf528SMauro Carvalho Chehab error:
24369a0bf528SMauro Carvalho Chehab 	kfree(client.i2c_read_buffer);
24379a0bf528SMauro Carvalho Chehab error_memory:
24389a0bf528SMauro Carvalho Chehab 	kfree(client.i2c_write_buffer);
24399a0bf528SMauro Carvalho Chehab 
24409a0bf528SMauro Carvalho Chehab 	return ret;
24419a0bf528SMauro Carvalho Chehab }
24429a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib9000_i2c_enumeration);
24439a0bf528SMauro Carvalho Chehab 
dib9000_set_slave_frontend(struct dvb_frontend * fe,struct dvb_frontend * fe_slave)24449a0bf528SMauro Carvalho Chehab int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave)
24459a0bf528SMauro Carvalho Chehab {
24469a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
24479a0bf528SMauro Carvalho Chehab 	u8 index_frontend = 1;
24489a0bf528SMauro Carvalho Chehab 
24499a0bf528SMauro Carvalho Chehab 	while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL))
24509a0bf528SMauro Carvalho Chehab 		index_frontend++;
24519a0bf528SMauro Carvalho Chehab 	if (index_frontend < MAX_NUMBER_OF_FRONTENDS) {
24523dd72262SMauro Carvalho Chehab 		dprintk("set slave fe %p to index %i\n", fe_slave, index_frontend);
24539a0bf528SMauro Carvalho Chehab 		state->fe[index_frontend] = fe_slave;
24549a0bf528SMauro Carvalho Chehab 		return 0;
24559a0bf528SMauro Carvalho Chehab 	}
24569a0bf528SMauro Carvalho Chehab 
24573dd72262SMauro Carvalho Chehab 	dprintk("too many slave frontend\n");
24589a0bf528SMauro Carvalho Chehab 	return -ENOMEM;
24599a0bf528SMauro Carvalho Chehab }
24609a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib9000_set_slave_frontend);
24619a0bf528SMauro Carvalho Chehab 
dib9000_get_slave_frontend(struct dvb_frontend * fe,int slave_index)24629a0bf528SMauro Carvalho Chehab struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index)
24639a0bf528SMauro Carvalho Chehab {
24649a0bf528SMauro Carvalho Chehab 	struct dib9000_state *state = fe->demodulator_priv;
24659a0bf528SMauro Carvalho Chehab 
24669a0bf528SMauro Carvalho Chehab 	if (slave_index >= MAX_NUMBER_OF_FRONTENDS)
24679a0bf528SMauro Carvalho Chehab 		return NULL;
24689a0bf528SMauro Carvalho Chehab 	return state->fe[slave_index];
24699a0bf528SMauro Carvalho Chehab }
24709a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib9000_get_slave_frontend);
24719a0bf528SMauro Carvalho Chehab 
2472bd336e63SMax Kellermann static const struct dvb_frontend_ops dib9000_ops;
dib9000_attach(struct i2c_adapter * i2c_adap,u8 i2c_addr,const struct dib9000_config * cfg)24739a0bf528SMauro Carvalho Chehab struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg)
24749a0bf528SMauro Carvalho Chehab {
24759a0bf528SMauro Carvalho Chehab 	struct dvb_frontend *fe;
24769a0bf528SMauro Carvalho Chehab 	struct dib9000_state *st;
24779a0bf528SMauro Carvalho Chehab 	st = kzalloc(sizeof(struct dib9000_state), GFP_KERNEL);
24789a0bf528SMauro Carvalho Chehab 	if (st == NULL)
24799a0bf528SMauro Carvalho Chehab 		return NULL;
24809a0bf528SMauro Carvalho Chehab 	fe = kzalloc(sizeof(struct dvb_frontend), GFP_KERNEL);
24819a0bf528SMauro Carvalho Chehab 	if (fe == NULL) {
24829a0bf528SMauro Carvalho Chehab 		kfree(st);
24839a0bf528SMauro Carvalho Chehab 		return NULL;
24849a0bf528SMauro Carvalho Chehab 	}
24859a0bf528SMauro Carvalho Chehab 
24869a0bf528SMauro Carvalho Chehab 	memcpy(&st->chip.d9.cfg, cfg, sizeof(struct dib9000_config));
24879a0bf528SMauro Carvalho Chehab 	st->i2c.i2c_adap = i2c_adap;
24889a0bf528SMauro Carvalho Chehab 	st->i2c.i2c_addr = i2c_addr;
24899a0bf528SMauro Carvalho Chehab 	st->i2c.i2c_write_buffer = st->i2c_write_buffer;
24909a0bf528SMauro Carvalho Chehab 	st->i2c.i2c_read_buffer = st->i2c_read_buffer;
24919a0bf528SMauro Carvalho Chehab 
24929a0bf528SMauro Carvalho Chehab 	st->gpio_dir = DIB9000_GPIO_DEFAULT_DIRECTIONS;
24939a0bf528SMauro Carvalho Chehab 	st->gpio_val = DIB9000_GPIO_DEFAULT_VALUES;
24949a0bf528SMauro Carvalho Chehab 	st->gpio_pwm_pos = DIB9000_GPIO_DEFAULT_PWM_POS;
24959a0bf528SMauro Carvalho Chehab 
24969a0bf528SMauro Carvalho Chehab 	mutex_init(&st->platform.risc.mbx_if_lock);
24979a0bf528SMauro Carvalho Chehab 	mutex_init(&st->platform.risc.mbx_lock);
24989a0bf528SMauro Carvalho Chehab 	mutex_init(&st->platform.risc.mem_lock);
24999a0bf528SMauro Carvalho Chehab 	mutex_init(&st->platform.risc.mem_mbx_lock);
25009a0bf528SMauro Carvalho Chehab 	mutex_init(&st->demod_lock);
25019a0bf528SMauro Carvalho Chehab 	st->get_frontend_internal = 0;
25029a0bf528SMauro Carvalho Chehab 
25039a0bf528SMauro Carvalho Chehab 	st->pid_ctrl_index = -2;
25049a0bf528SMauro Carvalho Chehab 
25059a0bf528SMauro Carvalho Chehab 	st->fe[0] = fe;
25069a0bf528SMauro Carvalho Chehab 	fe->demodulator_priv = st;
25079a0bf528SMauro Carvalho Chehab 	memcpy(&st->fe[0]->ops, &dib9000_ops, sizeof(struct dvb_frontend_ops));
25089a0bf528SMauro Carvalho Chehab 
25099a0bf528SMauro Carvalho Chehab 	/* Ensure the output mode remains at the previous default if it's
25109a0bf528SMauro Carvalho Chehab 	 * not specifically set by the caller.
25119a0bf528SMauro Carvalho Chehab 	 */
25129a0bf528SMauro Carvalho Chehab 	if ((st->chip.d9.cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (st->chip.d9.cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK))
25139a0bf528SMauro Carvalho Chehab 		st->chip.d9.cfg.output_mode = OUTMODE_MPEG2_FIFO;
25149a0bf528SMauro Carvalho Chehab 
25159a0bf528SMauro Carvalho Chehab 	if (dib9000_identify(&st->i2c) == 0)
25169a0bf528SMauro Carvalho Chehab 		goto error;
25179a0bf528SMauro Carvalho Chehab 
25189a0bf528SMauro Carvalho Chehab 	dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c.i2c_adap, st->i2c.i2c_addr);
25199a0bf528SMauro Carvalho Chehab 
25209a0bf528SMauro Carvalho Chehab 	st->tuner_adap.dev.parent = i2c_adap->dev.parent;
252185709cbfSMauro Carvalho Chehab 	strscpy(st->tuner_adap.name, "DIB9000_FW TUNER ACCESS",
252285709cbfSMauro Carvalho Chehab 		sizeof(st->tuner_adap.name));
25239a0bf528SMauro Carvalho Chehab 	st->tuner_adap.algo = &dib9000_tuner_algo;
25249a0bf528SMauro Carvalho Chehab 	st->tuner_adap.algo_data = NULL;
25259a0bf528SMauro Carvalho Chehab 	i2c_set_adapdata(&st->tuner_adap, st);
25269a0bf528SMauro Carvalho Chehab 	if (i2c_add_adapter(&st->tuner_adap) < 0)
25279a0bf528SMauro Carvalho Chehab 		goto error;
25289a0bf528SMauro Carvalho Chehab 
25299a0bf528SMauro Carvalho Chehab 	st->component_bus.dev.parent = i2c_adap->dev.parent;
253085709cbfSMauro Carvalho Chehab 	strscpy(st->component_bus.name, "DIB9000_FW COMPONENT BUS ACCESS",
253185709cbfSMauro Carvalho Chehab 		sizeof(st->component_bus.name));
25329a0bf528SMauro Carvalho Chehab 	st->component_bus.algo = &dib9000_component_bus_algo;
25339a0bf528SMauro Carvalho Chehab 	st->component_bus.algo_data = NULL;
25349a0bf528SMauro Carvalho Chehab 	st->component_bus_speed = 340;
25359a0bf528SMauro Carvalho Chehab 	i2c_set_adapdata(&st->component_bus, st);
25369a0bf528SMauro Carvalho Chehab 	if (i2c_add_adapter(&st->component_bus) < 0)
25379a0bf528SMauro Carvalho Chehab 		goto component_bus_add_error;
25389a0bf528SMauro Carvalho Chehab 
25399a0bf528SMauro Carvalho Chehab 	dib9000_fw_reset(fe);
25409a0bf528SMauro Carvalho Chehab 
25419a0bf528SMauro Carvalho Chehab 	return fe;
25429a0bf528SMauro Carvalho Chehab 
25439a0bf528SMauro Carvalho Chehab component_bus_add_error:
25449a0bf528SMauro Carvalho Chehab 	i2c_del_adapter(&st->tuner_adap);
25459a0bf528SMauro Carvalho Chehab error:
25469a0bf528SMauro Carvalho Chehab 	kfree(st);
25479a0bf528SMauro Carvalho Chehab 	return NULL;
25489a0bf528SMauro Carvalho Chehab }
2549*86495af1SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(dib9000_attach);
25509a0bf528SMauro Carvalho Chehab 
2551bd336e63SMax Kellermann static const struct dvb_frontend_ops dib9000_ops = {
25529a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBT },
25539a0bf528SMauro Carvalho Chehab 	.info = {
25549a0bf528SMauro Carvalho Chehab 		 .name = "DiBcom 9000",
2555f1b1eabfSMauro Carvalho Chehab 		 .frequency_min_hz =  44250 * kHz,
2556f1b1eabfSMauro Carvalho Chehab 		 .frequency_max_hz = 867250 * kHz,
2557f1b1eabfSMauro Carvalho Chehab 		 .frequency_stepsize_hz = 62500,
25589a0bf528SMauro Carvalho Chehab 		 .caps = FE_CAN_INVERSION_AUTO |
25599a0bf528SMauro Carvalho Chehab 		 FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
25609a0bf528SMauro Carvalho Chehab 		 FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
25619a0bf528SMauro Carvalho Chehab 		 FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
25629a0bf528SMauro Carvalho Chehab 		 FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | FE_CAN_HIERARCHY_AUTO,
25639a0bf528SMauro Carvalho Chehab 		 },
25649a0bf528SMauro Carvalho Chehab 
25659a0bf528SMauro Carvalho Chehab 	.release = dib9000_release,
25669a0bf528SMauro Carvalho Chehab 
25679a0bf528SMauro Carvalho Chehab 	.init = dib9000_wakeup,
25689a0bf528SMauro Carvalho Chehab 	.sleep = dib9000_sleep,
25699a0bf528SMauro Carvalho Chehab 
25709a0bf528SMauro Carvalho Chehab 	.set_frontend = dib9000_set_frontend,
25719a0bf528SMauro Carvalho Chehab 	.get_tune_settings = dib9000_fe_get_tune_settings,
25729a0bf528SMauro Carvalho Chehab 	.get_frontend = dib9000_get_frontend,
25739a0bf528SMauro Carvalho Chehab 
25749a0bf528SMauro Carvalho Chehab 	.read_status = dib9000_read_status,
25759a0bf528SMauro Carvalho Chehab 	.read_ber = dib9000_read_ber,
25769a0bf528SMauro Carvalho Chehab 	.read_signal_strength = dib9000_read_signal_strength,
25779a0bf528SMauro Carvalho Chehab 	.read_snr = dib9000_read_snr,
25789a0bf528SMauro Carvalho Chehab 	.read_ucblocks = dib9000_read_unc_blocks,
25799a0bf528SMauro Carvalho Chehab };
25809a0bf528SMauro Carvalho Chehab 
258199e44da7SPatrick Boettcher MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
258299e44da7SPatrick Boettcher MODULE_AUTHOR("Olivier Grenie <olivier.grenie@parrot.com>");
25839a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("Driver for the DiBcom 9000 COFDM demodulator");
25849a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
2585