aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/dvb-frontends/dib9000.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/dvb-frontends/dib9000.c')
-rw-r--r--drivers/media/dvb-frontends/dib9000.c2590
1 files changed, 2590 insertions, 0 deletions
diff --git a/drivers/media/dvb-frontends/dib9000.c b/drivers/media/dvb-frontends/dib9000.c
new file mode 100644
index 00000000000..6201c59a78d
--- /dev/null
+++ b/drivers/media/dvb-frontends/dib9000.c
@@ -0,0 +1,2590 @@
+/*
+ * Linux-DVB Driver for DiBcom's DiB9000 and demodulator-family.
+ *
+ * Copyright (C) 2005-10 DiBcom (http://www.dibcom.fr/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+
+#include "dvb_math.h"
+#include "dvb_frontend.h"
+
+#include "dib9000.h"
+#include "dibx000_common.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+
+#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB9000: "); printk(args); printk("\n"); } } while (0)
+#define MAX_NUMBER_OF_FRONTENDS 6
+
+struct i2c_device {
+ struct i2c_adapter *i2c_adap;
+ u8 i2c_addr;
+ u8 *i2c_read_buffer;
+ u8 *i2c_write_buffer;
+};
+
+struct dib9000_pid_ctrl {
+#define DIB9000_PID_FILTER_CTRL 0
+#define DIB9000_PID_FILTER 1
+ u8 cmd;
+ u8 id;
+ u16 pid;
+ u8 onoff;
+};
+
+struct dib9000_state {
+ struct i2c_device i2c;
+
+ struct dibx000_i2c_master i2c_master;
+ struct i2c_adapter tuner_adap;
+ struct i2c_adapter component_bus;
+
+ u16 revision;
+ u8 reg_offs;
+
+ enum frontend_tune_state tune_state;
+ u32 status;
+ struct dvb_frontend_parametersContext channel_status;
+
+ u8 fe_id;
+
+#define DIB9000_GPIO_DEFAULT_DIRECTIONS 0xffff
+ u16 gpio_dir;
+#define DIB9000_GPIO_DEFAULT_VALUES 0x0000
+ u16 gpio_val;
+#define DIB9000_GPIO_DEFAULT_PWM_POS 0xffff
+ u16 gpio_pwm_pos;
+
+ union { /* common for all chips */
+ struct {
+ u8 mobile_mode:1;
+ } host;
+
+ struct {
+ struct dib9000_fe_memory_map {
+ u16 addr;
+ u16 size;
+ } fe_mm[18];
+ u8 memcmd;
+
+ struct mutex mbx_if_lock; /* to protect read/write operations */
+ struct mutex mbx_lock; /* to protect the whole mailbox handling */
+
+ struct mutex mem_lock; /* to protect the memory accesses */
+ struct mutex mem_mbx_lock; /* to protect the memory-based mailbox */
+
+#define MBX_MAX_WORDS (256 - 200 - 2)
+#define DIB9000_MSG_CACHE_SIZE 2
+ u16 message_cache[DIB9000_MSG_CACHE_SIZE][MBX_MAX_WORDS];
+ u8 fw_is_running;
+ } risc;
+ } platform;
+
+ union { /* common for all platforms */
+ struct {
+ struct dib9000_config cfg;
+ } d9;
+ } chip;
+
+ struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS];
+ u16 component_bus_speed;
+
+ /* for the I2C transfer */
+ struct i2c_msg msg[2];
+ u8 i2c_write_buffer[255];
+ u8 i2c_read_buffer[255];
+ struct mutex demod_lock;
+ u8 get_frontend_internal;
+ struct dib9000_pid_ctrl pid_ctrl[10];
+ s8 pid_ctrl_index; /* -1: empty list; -2: do not use the list */
+};
+
+static const u32 fe_info[44] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+enum dib9000_power_mode {
+ DIB9000_POWER_ALL = 0,
+
+ DIB9000_POWER_NO,
+ DIB9000_POWER_INTERF_ANALOG_AGC,
+ DIB9000_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD,
+ DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD,
+ DIB9000_POWER_INTERFACE_ONLY,
+};
+
+enum dib9000_out_messages {
+ OUT_MSG_HBM_ACK,
+ OUT_MSG_HOST_BUF_FAIL,
+ OUT_MSG_REQ_VERSION,
+ OUT_MSG_BRIDGE_I2C_W,
+ OUT_MSG_BRIDGE_I2C_R,
+ OUT_MSG_BRIDGE_APB_W,
+ OUT_MSG_BRIDGE_APB_R,
+ OUT_MSG_SCAN_CHANNEL,
+ OUT_MSG_MONIT_DEMOD,
+ OUT_MSG_CONF_GPIO,
+ OUT_MSG_DEBUG_HELP,
+ OUT_MSG_SUBBAND_SEL,
+ OUT_MSG_ENABLE_TIME_SLICE,
+ OUT_MSG_FE_FW_DL,
+ OUT_MSG_FE_CHANNEL_SEARCH,
+ OUT_MSG_FE_CHANNEL_TUNE,
+ OUT_MSG_FE_SLEEP,
+ OUT_MSG_FE_SYNC,
+ OUT_MSG_CTL_MONIT,
+
+ OUT_MSG_CONF_SVC,
+ OUT_MSG_SET_HBM,
+ OUT_MSG_INIT_DEMOD,
+ OUT_MSG_ENABLE_DIVERSITY,
+ OUT_MSG_SET_OUTPUT_MODE,
+ OUT_MSG_SET_PRIORITARY_CHANNEL,
+ OUT_MSG_ACK_FRG,
+ OUT_MSG_INIT_PMU,
+};
+
+enum dib9000_in_messages {
+ IN_MSG_DATA,
+ IN_MSG_FRAME_INFO,
+ IN_MSG_CTL_MONIT,
+ IN_MSG_ACK_FREE_ITEM,
+ IN_MSG_DEBUG_BUF,
+ IN_MSG_MPE_MONITOR,
+ IN_MSG_RAWTS_MONITOR,
+ IN_MSG_END_BRIDGE_I2C_RW,
+ IN_MSG_END_BRIDGE_APB_RW,
+ IN_MSG_VERSION,
+ IN_MSG_END_OF_SCAN,
+ IN_MSG_MONIT_DEMOD,
+ IN_MSG_ERROR,
+ IN_MSG_FE_FW_DL_DONE,
+ IN_MSG_EVENT,
+ IN_MSG_ACK_CHANGE_SVC,
+ IN_MSG_HBM_PROF,
+};
+
+/* memory_access requests */
+#define FE_MM_W_CHANNEL 0
+#define FE_MM_W_FE_INFO 1
+#define FE_MM_RW_SYNC 2
+
+#define FE_SYNC_CHANNEL 1
+#define FE_SYNC_W_GENERIC_MONIT 2
+#define FE_SYNC_COMPONENT_ACCESS 3
+
+#define FE_MM_R_CHANNEL_SEARCH_STATE 3
+#define FE_MM_R_CHANNEL_UNION_CONTEXT 4
+#define FE_MM_R_FE_INFO 5
+#define FE_MM_R_FE_MONITOR 6
+
+#define FE_MM_W_CHANNEL_HEAD 7
+#define FE_MM_W_CHANNEL_UNION 8
+#define FE_MM_W_CHANNEL_CONTEXT 9
+#define FE_MM_R_CHANNEL_UNION 10
+#define FE_MM_R_CHANNEL_CONTEXT 11
+#define FE_MM_R_CHANNEL_TUNE_STATE 12
+
+#define FE_MM_R_GENERIC_MONITORING_SIZE 13
+#define FE_MM_W_GENERIC_MONITORING 14
+#define FE_MM_R_GENERIC_MONITORING 15
+
+#define FE_MM_W_COMPONENT_ACCESS 16
+#define FE_MM_RW_COMPONENT_ACCESS_BUFFER 17
+static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, u8 * b, u32 len);
+static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len);
+
+static u16 to_fw_output_mode(u16 mode)
+{
+ switch (mode) {
+ case OUTMODE_HIGH_Z:
+ return 0;
+ case OUTMODE_MPEG2_PAR_GATED_CLK:
+ return 4;
+ case OUTMODE_MPEG2_PAR_CONT_CLK:
+ return 8;
+ case OUTMODE_MPEG2_SERIAL:
+ return 16;
+ case OUTMODE_DIVERSITY:
+ return 128;
+ case OUTMODE_MPEG2_FIFO:
+ return 2;
+ case OUTMODE_ANALOG_ADC:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static u16 dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 * b, u32 len, u16 attribute)
+{
+ u32 chunk_size = 126;
+ u32 l;
+ int ret;
+
+ if (state->platform.risc.fw_is_running && (reg < 1024))
+ return dib9000_risc_apb_access_read(state, reg, attribute, NULL, 0, b, len);
+
+ memset(state->msg, 0, 2 * sizeof(struct i2c_msg));
+ state->msg[0].addr = state->i2c.i2c_addr >> 1;
+ state->msg[0].flags = 0;
+ state->msg[0].buf = state->i2c_write_buffer;
+ state->msg[0].len = 2;
+ state->msg[1].addr = state->i2c.i2c_addr >> 1;
+ state->msg[1].flags = I2C_M_RD;
+ state->msg[1].buf = b;
+ state->msg[1].len = len;
+
+ state->i2c_write_buffer[0] = reg >> 8;
+ state->i2c_write_buffer[1] = reg & 0xff;
+
+ if (attribute & DATA_BUS_ACCESS_MODE_8BIT)
+ state->i2c_write_buffer[0] |= (1 << 5);
+ if (attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
+ state->i2c_write_buffer[0] |= (1 << 4);
+
+ do {
+ l = len < chunk_size ? len : chunk_size;
+ state->msg[1].len = l;
+ state->msg[1].buf = b;
+ ret = i2c_transfer(state->i2c.i2c_adap, state->msg, 2) != 2 ? -EREMOTEIO : 0;
+ if (ret != 0) {
+ dprintk("i2c read error on %d", reg);
+ return -EREMOTEIO;
+ }
+
+ b += l;
+ len -= l;
+
+ if (!(attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT))
+ reg += l / 2;
+ } while ((ret == 0) && len);
+
+ return 0;
+}
+
+static u16 dib9000_i2c_read16(struct i2c_device *i2c, u16 reg)
+{
+ struct i2c_msg msg[2] = {
+ {.addr = i2c->i2c_addr >> 1, .flags = 0,
+ .buf = i2c->i2c_write_buffer, .len = 2},
+ {.addr = i2c->i2c_addr >> 1, .flags = I2C_M_RD,
+ .buf = i2c->i2c_read_buffer, .len = 2},
+ };
+
+ i2c->i2c_write_buffer[0] = reg >> 8;
+ i2c->i2c_write_buffer[1] = reg & 0xff;
+
+ if (i2c_transfer(i2c->i2c_adap, msg, 2) != 2) {
+ dprintk("read register %x error", reg);
+ return 0;
+ }
+
+ return (i2c->i2c_read_buffer[0] << 8) | i2c->i2c_read_buffer[1];
+}
+
+static inline u16 dib9000_read_word(struct dib9000_state *state, u16 reg)
+{
+ if (dib9000_read16_attr(state, reg, state->i2c_read_buffer, 2, 0) != 0)
+ return 0;
+ return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+}
+
+static inline u16 dib9000_read_word_attr(struct dib9000_state *state, u16 reg, u16 attribute)
+{
+ if (dib9000_read16_attr(state, reg, state->i2c_read_buffer, 2,
+ attribute) != 0)
+ return 0;
+ return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+}
+
+#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)
+
+static u16 dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 * buf, u32 len, u16 attribute)
+{
+ u32 chunk_size = 126;
+ u32 l;
+ int ret;
+
+ if (state->platform.risc.fw_is_running && (reg < 1024)) {
+ if (dib9000_risc_apb_access_write
+ (state, reg, DATA_BUS_ACCESS_MODE_16BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | attribute, buf, len) != 0)
+ return -EINVAL;
+ return 0;
+ }
+
+ memset(&state->msg[0], 0, sizeof(struct i2c_msg));
+ state->msg[0].addr = state->i2c.i2c_addr >> 1;
+ state->msg[0].flags = 0;
+ state->msg[0].buf = state->i2c_write_buffer;
+ state->msg[0].len = len + 2;
+
+ state->i2c_write_buffer[0] = (reg >> 8) & 0xff;
+ state->i2c_write_buffer[1] = (reg) & 0xff;
+
+ if (attribute & DATA_BUS_ACCESS_MODE_8BIT)
+ state->i2c_write_buffer[0] |= (1 << 5);
+ if (attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
+ state->i2c_write_buffer[0] |= (1 << 4);
+
+ do {
+ l = len < chunk_size ? len : chunk_size;
+ state->msg[0].len = l + 2;
+ memcpy(&state->i2c_write_buffer[2], buf, l);
+
+ ret = i2c_transfer(state->i2c.i2c_adap, state->msg, 1) != 1 ? -EREMOTEIO : 0;
+
+ buf += l;
+ len -= l;
+
+ if (!(attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT))
+ reg += l / 2;
+ } while ((ret == 0) && len);
+
+ return ret;
+}
+
+static int dib9000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val)
+{
+ struct i2c_msg msg = {
+ .addr = i2c->i2c_addr >> 1, .flags = 0,
+ .buf = i2c->i2c_write_buffer, .len = 4
+ };
+
+ i2c->i2c_write_buffer[0] = (reg >> 8) & 0xff;
+ i2c->i2c_write_buffer[1] = reg & 0xff;
+ i2c->i2c_write_buffer[2] = (val >> 8) & 0xff;
+ i2c->i2c_write_buffer[3] = val & 0xff;
+
+ return i2c_transfer(i2c->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+
+static inline int dib9000_write_word(struct dib9000_state *state, u16 reg, u16 val)
+{
+ u8 b[2] = { val >> 8, val & 0xff };
+ return dib9000_write16_attr(state, reg, b, 2, 0);
+}
+
+static inline int dib9000_write_word_attr(struct dib9000_state *state, u16 reg, u16 val, u16 attribute)
+{
+ u8 b[2] = { val >> 8, val & 0xff };
+ return dib9000_write16_attr(state, reg, b, 2, attribute);
+}
+
+#define dib9000_write(state, reg, buf, len) dib9000_write16_attr(state, reg, buf, len, 0)
+#define dib9000_write16_noinc(state, reg, buf, len) dib9000_write16_attr(state, reg, buf, len, DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
+#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))
+
+#define dib9000_mbx_send(state, id, data, len) dib9000_mbx_send_attr(state, id, data, len, 0)
+#define dib9000_mbx_get_message(state, id, msg, len) dib9000_mbx_get_message_attr(state, id, msg, len, 0)
+
+#define MAC_IRQ (1 << 1)
+#define IRQ_POL_MSK (1 << 4)
+
+#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)
+#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)
+
+static void dib9000_risc_mem_setup_cmd(struct dib9000_state *state, u32 addr, u32 len, u8 reading)
+{
+ u8 b[14] = { 0 };
+
+/* dprintk("%d memcmd: %d %d %d\n", state->fe_id, addr, addr+len, len); */
+/* b[0] = 0 << 7; */
+ b[1] = 1;
+
+/* b[2] = 0; */
+/* b[3] = 0; */
+ b[4] = (u8) (addr >> 8);
+ b[5] = (u8) (addr & 0xff);
+
+/* b[10] = 0; */
+/* b[11] = 0; */
+ b[12] = (u8) (addr >> 8);
+ b[13] = (u8) (addr & 0xff);
+
+ addr += len;
+/* b[6] = 0; */
+/* b[7] = 0; */
+ b[8] = (u8) (addr >> 8);
+ b[9] = (u8) (addr & 0xff);
+
+ dib9000_write(state, 1056, b, 14);
+ if (reading)
+ dib9000_write_word(state, 1056, (1 << 15) | 1);
+ state->platform.risc.memcmd = -1; /* if it was called directly reset it - to force a future setup-call to set it */
+}
+
+static void dib9000_risc_mem_setup(struct dib9000_state *state, u8 cmd)
+{
+ struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd & 0x7f];
+ /* decide whether we need to "refresh" the memory controller */
+ if (state->platform.risc.memcmd == cmd && /* same command */
+ !(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 */
+ return;
+ dib9000_risc_mem_setup_cmd(state, m->addr, m->size, cmd & 0x80);
+ state->platform.risc.memcmd = cmd;
+}
+
+static int dib9000_risc_mem_read(struct dib9000_state *state, u8 cmd, u8 * b, u16 len)
+{
+ if (!state->platform.risc.fw_is_running)
+ return -EIO;
+
+ if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
+ dib9000_risc_mem_setup(state, cmd | 0x80);
+ dib9000_risc_mem_read_chunks(state, b, len);
+ mutex_unlock(&state->platform.risc.mem_lock);
+ return 0;
+}
+
+static int dib9000_risc_mem_write(struct dib9000_state *state, u8 cmd, const u8 * b)
+{
+ struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd];
+ if (!state->platform.risc.fw_is_running)
+ return -EIO;
+
+ if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
+ dib9000_risc_mem_setup(state, cmd);
+ dib9000_risc_mem_write_chunks(state, b, m->size);
+ mutex_unlock(&state->platform.risc.mem_lock);
+ return 0;
+}
+
+static int dib9000_firmware_download(struct dib9000_state *state, u8 risc_id, u16 key, const u8 * code, u32 len)
+{
+ u16 offs;
+
+ if (risc_id == 1)
+ offs = 16;
+ else
+ offs = 0;
+
+ /* config crtl reg */
+ dib9000_write_word(state, 1024 + offs, 0x000f);
+ dib9000_write_word(state, 1025 + offs, 0);
+ dib9000_write_word(state, 1031 + offs, key);
+
+ dprintk("going to download %dB of microcode", len);
+ if (dib9000_write16_noinc(state, 1026 + offs, (u8 *) code, (u16) len) != 0) {
+ dprintk("error while downloading microcode for RISC %c", 'A' + risc_id);
+ return -EIO;
+ }
+
+ dprintk("Microcode for RISC %c loaded", 'A' + risc_id);
+
+ return 0;
+}
+
+static int dib9000_mbx_host_init(struct dib9000_state *state, u8 risc_id)
+{
+ u16 mbox_offs;
+ u16 reset_reg;
+ u16 tries = 1000;
+
+ if (risc_id == 1)
+ mbox_offs = 16;
+ else
+ mbox_offs = 0;
+
+ /* Reset mailbox */
+ dib9000_write_word(state, 1027 + mbox_offs, 0x8000);
+
+ /* Read reset status */
+ do {
+ reset_reg = dib9000_read_word(state, 1027 + mbox_offs);
+ msleep(100);
+ } while ((reset_reg & 0x8000) && --tries);
+
+ if (reset_reg & 0x8000) {
+ dprintk("MBX: init ERROR, no response from RISC %c", 'A' + risc_id);
+ return -EIO;
+ }
+ dprintk("MBX: initialized");
+ return 0;
+}
+
+#define MAX_MAILBOX_TRY 100
+static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, u8 len, u16 attr)
+{
+ u8 *d, b[2];
+ u16 tmp;
+ u16 size;
+ u32 i;
+ int ret = 0;
+
+ if (!state->platform.risc.fw_is_running)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
+ tmp = MAX_MAILBOX_TRY;
+ do {
+ size = dib9000_read_word_attr(state, 1043, attr) & 0xff;
+ if ((size + len + 1) > MBX_MAX_WORDS && --tmp) {
+ dprintk("MBX: RISC mbx full, retrying");
+ msleep(100);
+ } else
+ break;
+ } while (1);
+
+ /*dprintk( "MBX: size: %d", size); */
+
+ if (tmp == 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+#ifdef DUMP_MSG
+ dprintk("--> %02x %d ", id, len + 1);
+ for (i = 0; i < len; i++)
+ dprintk("%04x ", data[i]);
+ dprintk("\n");
+#endif
+
+ /* byte-order conversion - works on big (where it is not necessary) or little endian */
+ d = (u8 *) data;
+ for (i = 0; i < len; i++) {
+ tmp = data[i];
+ *d++ = tmp >> 8;
+ *d++ = tmp & 0xff;
+ }
+
+ /* write msg */
+ b[0] = id;
+ b[1] = len + 1;
+ if (dib9000_write16_noinc_attr(state, 1045, b, 2, attr) != 0 || dib9000_write16_noinc_attr(state, 1045, (u8 *) data, len * 2, attr) != 0) {
+ ret = -EIO;
+ goto out;
+ }
+
+ /* update register nb_mes_in_RX */
+ ret = (u8) dib9000_write_word_attr(state, 1043, 1 << 14, attr);
+
+out:
+ mutex_unlock(&state->platform.risc.mbx_if_lock);
+
+ return ret;
+}
+
+static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id, u16 attr)
+{
+#ifdef DUMP_MSG
+ u16 *d = data;
+#endif
+
+ u16 tmp, i;
+ u8 size;
+ u8 mc_base;
+
+ if (!state->platform.risc.fw_is_running)
+ return 0;
+
+ if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) {
+ dprintk("could not get the lock");
+ return 0;
+ }
+ if (risc_id == 1)
+ mc_base = 16;
+ else
+ mc_base = 0;
+
+ /* Length and type in the first word */
+ *data = dib9000_read_word_attr(state, 1029 + mc_base, attr);
+
+ size = *data & 0xff;
+ if (size <= MBX_MAX_WORDS) {
+ data++;
+ size--; /* Initial word already read */
+
+ dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, size * 2, attr);
+
+ /* to word conversion */
+ for (i = 0; i < size; i++) {
+ tmp = *data;
+ *data = (tmp >> 8) | (tmp << 8);
+ data++;
+ }
+
+#ifdef DUMP_MSG
+ dprintk("<-- ");
+ for (i = 0; i < size + 1; i++)
+ dprintk("%04x ", d[i]);
+ dprintk("\n");
+#endif
+ } else {
+ dprintk("MBX: message is too big for message cache (%d), flushing message", size);
+ size--; /* Initial word already read */
+ while (size--)
+ dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, 2, attr);
+ }
+ /* Update register nb_mes_in_TX */
+ dib9000_write_word_attr(state, 1028 + mc_base, 1 << 14, attr);
+
+ mutex_unlock(&state->platform.risc.mbx_if_lock);
+
+ return size + 1;
+}
+
+static int dib9000_risc_debug_buf(struct dib9000_state *state, u16 * data, u8 size)
+{
+ u32 ts = data[1] << 16 | data[0];
+ char *b = (char *)&data[2];
+
+ b[2 * (size - 2) - 1] = '\0'; /* Bullet proof the buffer */
+ if (*b == '~') {
+ b++;
+ dprintk(b);
+ } else
+ dprintk("RISC%d: %d.%04d %s", state->fe_id, ts / 10000, ts % 10000, *b ? b : "<emtpy>");
+ return 1;
+}
+
+static int dib9000_mbx_fetch_to_cache(struct dib9000_state *state, u16 attr)
+{
+ int i;
+ u8 size;
+ u16 *block;
+ /* find a free slot */
+ for (i = 0; i < DIB9000_MSG_CACHE_SIZE; i++) {
+ block = state->platform.risc.message_cache[i];
+ if (*block == 0) {
+ size = dib9000_mbx_read(state, block, 1, attr);
+
+/* dprintk( "MBX: fetched %04x message to cache", *block); */
+
+ switch (*block >> 8) {
+ case IN_MSG_DEBUG_BUF:
+ dib9000_risc_debug_buf(state, block + 1, size); /* debug-messages are going to be printed right away */
+ *block = 0; /* free the block */
+ break;
+#if 0
+ case IN_MSG_DATA: /* FE-TRACE */
+ dib9000_risc_data_process(state, block + 1, size);
+ *block = 0;
+ break;
+#endif
+ default:
+ break;
+ }
+
+ return 1;
+ }
+ }
+ dprintk("MBX: no free cache-slot found for new message...");
+ return -1;
+}
+
+static u8 dib9000_mbx_count(struct dib9000_state *state, u8 risc_id, u16 attr)
+{
+ if (risc_id == 0)
+ return (u8) (dib9000_read_word_attr(state, 1028, attr) >> 10) & 0x1f; /* 5 bit field */
+ else
+ return (u8) (dib9000_read_word_attr(state, 1044, attr) >> 8) & 0x7f; /* 7 bit field */
+}
+
+static int dib9000_mbx_process(struct dib9000_state *state, u16 attr)
+{
+ int ret = 0;
+
+ if (!state->platform.risc.fw_is_running)
+ return -1;
+
+ if (mutex_lock_interruptible(&state->platform.risc.mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ return -1;
+ }
+
+ if (dib9000_mbx_count(state, 1, attr)) /* 1=RiscB */
+ ret = dib9000_mbx_fetch_to_cache(state, attr);
+
+ dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */
+/* if (tmp) */
+/* dprintk( "cleared IRQ: %x", tmp); */
+ mutex_unlock(&state->platform.risc.mbx_lock);
+
+ return ret;
+}
+
+static int dib9000_mbx_get_message_attr(struct dib9000_state *state, u16 id, u16 * msg, u8 * size, u16 attr)
+{
+ u8 i;
+ u16 *block;
+ u16 timeout = 30;
+
+ *msg = 0;
+ do {
+ /* dib9000_mbx_get_from_cache(); */
+ for (i = 0; i < DIB9000_MSG_CACHE_SIZE; i++) {
+ block = state->platform.risc.message_cache[i];
+ if ((*block >> 8) == id) {
+ *size = (*block & 0xff) - 1;
+ memcpy(msg, block + 1, (*size) * 2);
+ *block = 0; /* free the block */
+ i = 0; /* signal that we found a message */
+ break;
+ }
+ }
+
+ if (i == 0)
+ break;
+
+ if (dib9000_mbx_process(state, attr) == -1) /* try to fetch one message - if any */
+ return -1;
+
+ } while (--timeout);
+
+ if (timeout == 0) {
+ dprintk("waiting for message %d timed out", id);
+ return -1;
+ }
+
+ return i == 0;
+}
+
+static int dib9000_risc_check_version(struct dib9000_state *state)
+{
+ u8 r[4];
+ u8 size;
+ u16 fw_version = 0;
+
+ if (dib9000_mbx_send(state, OUT_MSG_REQ_VERSION, &fw_version, 1) != 0)
+ return -EIO;
+
+ if (dib9000_mbx_get_message(state, IN_MSG_VERSION, (u16 *) r, &size) < 0)
+ return -EIO;
+
+ fw_version = (r[0] << 8) | r[1];
+ dprintk("RISC: ver: %d.%02d (IC: %d)", fw_version >> 10, fw_version & 0x3ff, (r[2] << 8) | r[3]);
+
+ if ((fw_version >> 10) != 7)
+ return -EINVAL;
+
+ switch (fw_version & 0x3ff) {
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ break;
+ default:
+ dprintk("RISC: invalid firmware version");
+ return -EINVAL;
+ }
+
+ dprintk("RISC: valid firmware version");
+ return 0;
+}
+
+static int dib9000_fw_boot(struct dib9000_state *state, const u8 * codeA, u32 lenA, const u8 * codeB, u32 lenB)
+{
+ /* Reconfig pool mac ram */
+ dib9000_write_word(state, 1225, 0x02); /* A: 8k C, 4 k D - B: 32k C 6 k D - IRAM 96k */
+ dib9000_write_word(state, 1226, 0x05);
+
+ /* Toggles IP crypto to Host APB interface. */
+ dib9000_write_word(state, 1542, 1);
+
+ /* Set jump and no jump in the dma box */
+ dib9000_write_word(state, 1074, 0);
+ dib9000_write_word(state, 1075, 0);
+
+ /* Set MAC as APB Master. */
+ dib9000_write_word(state, 1237, 0);
+
+ /* Reset the RISCs */
+ if (codeA != NULL)
+ dib9000_write_word(state, 1024, 2);
+ else
+ dib9000_write_word(state, 1024, 15);
+ if (codeB != NULL)
+ dib9000_write_word(state, 1040, 2);
+
+ if (codeA != NULL)
+ dib9000_firmware_download(state, 0, 0x1234, codeA, lenA);
+ if (codeB != NULL)
+ dib9000_firmware_download(state, 1, 0x1234, codeB, lenB);
+
+ /* Run the RISCs */
+ if (codeA != NULL)
+ dib9000_write_word(state, 1024, 0);
+ if (codeB != NULL)
+ dib9000_write_word(state, 1040, 0);
+
+ if (codeA != NULL)
+ if (dib9000_mbx_host_init(state, 0) != 0)
+ return -EIO;
+ if (codeB != NULL)
+ if (dib9000_mbx_host_init(state, 1) != 0)
+ return -EIO;
+
+ msleep(100);
+ state->platform.risc.fw_is_running = 1;
+
+ if (dib9000_risc_check_version(state) != 0)
+ return -EINVAL;
+
+ state->platform.risc.memcmd = 0xff;
+ return 0;
+}
+
+static u16 dib9000_identify(struct i2c_device *client)
+{
+ u16 value;
+
+ value = dib9000_i2c_read16(client, 896);
+ if (value != 0x01b3) {
+ dprintk("wrong Vendor ID (0x%x)", value);
+ return 0;
+ }
+
+ value = dib9000_i2c_read16(client, 897);
+ if (value != 0x4000 && value != 0x4001 && value != 0x4002 && value != 0x4003 && value != 0x4004 && value != 0x4005) {
+ dprintk("wrong Device ID (0x%x)", value);
+ return 0;
+ }
+
+ /* protect this driver to be used with 7000PC */
+ if (value == 0x4000 && dib9000_i2c_read16(client, 769) == 0x4000) {
+ dprintk("this driver does not work with DiB7000PC");
+ return 0;
+ }
+
+ switch (value) {
+ case 0x4000:
+ dprintk("found DiB7000MA/PA/MB/PB");
+ break;
+ case 0x4001:
+ dprintk("found DiB7000HC");
+ break;
+ case 0x4002:
+ dprintk("found DiB7000MC");
+ break;
+ case 0x4003:
+ dprintk("found DiB9000A");
+ break;
+ case 0x4004:
+ dprintk("found DiB9000H");
+ break;
+ case 0x4005:
+ dprintk("found DiB9000M");
+ break;
+ }
+
+ return value;
+}
+
+static void dib9000_set_power_mode(struct dib9000_state *state, enum dib9000_power_mode mode)
+{
+ /* by default everything is going to be powered off */
+ u16 reg_903 = 0x3fff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906;
+ u8 offset;
+
+ if (state->revision == 0x4003 || state->revision == 0x4004 || state->revision == 0x4005)
+ offset = 1;
+ else
+ offset = 0;
+
+ reg_906 = dib9000_read_word(state, 906 + offset) | 0x3; /* keep settings for RISC */
+
+ /* now, depending on the requested mode, we power on */
+ switch (mode) {
+ /* power up everything in the demod */
+ case DIB9000_POWER_ALL:
+ reg_903 = 0x0000;
+ reg_904 = 0x0000;
+ reg_905 = 0x0000;
+ reg_906 = 0x0000;
+ break;
+
+ /* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */
+ case DIB9000_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C or SRAM */
+ reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2));
+ break;
+
+ case DIB9000_POWER_INTERF_ANALOG_AGC:
+ reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10));
+ reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2));
+ reg_906 &= ~((1 << 0));
+ break;
+
+ case DIB9000_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD:
+ reg_903 = 0x0000;
+ reg_904 = 0x801f;
+ reg_905 = 0x0000;
+ reg_906 &= ~((1 << 0));
+ break;
+
+ case DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD:
+ reg_903 = 0x0000;
+ reg_904 = 0x8000;
+ reg_905 = 0x010b;
+ reg_906 &= ~((1 << 0));
+ break;
+ default:
+ case DIB9000_POWER_NO:
+ break;
+ }
+
+ /* always power down unused parts */
+ if (!state->platform.host.mobile_mode)
+ reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1);
+
+ /* P_sdio_select_clk = 0 on MC and after */
+ if (state->revision != 0x4000)
+ reg_906 <<= 1;
+
+ dib9000_write_word(state, 903 + offset, reg_903);
+ dib9000_write_word(state, 904 + offset, reg_904);
+ dib9000_write_word(state, 905 + offset, reg_905);
+ dib9000_write_word(state, 906 + offset, reg_906);
+}
+
+static int dib9000_fw_reset(struct dvb_frontend *fe)
+{
+ struct dib9000_state *state = fe->demodulator_priv;
+
+ dib9000_write_word(state, 1817, 0x0003);
+
+ dib9000_write_word(state, 1227, 1);
+ dib9000_write_word(state, 1227, 0);
+
+ switch ((state->revision = dib9000_identify(&state->i2c))) {
+ case 0x4003:
+ case 0x4004:
+ case 0x4005:
+ state->reg_offs = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* reset the i2c-master to use the host interface */
+ dibx000_reset_i2c_master(&state->i2c_master);
+
+ dib9000_set_power_mode(state, DIB9000_POWER_ALL);
+
+ /* unforce divstr regardless whether i2c enumeration was done or not */
+ dib9000_write_word(state, 1794, dib9000_read_word(state, 1794) & ~(1 << 1));
+ dib9000_write_word(state, 1796, 0);
+ dib9000_write_word(state, 1805, 0x805);
+
+ /* restart all parts */
+ dib9000_write_word(state, 898, 0xffff);
+ dib9000_write_word(state, 899, 0xffff);
+ dib9000_write_word(state, 900, 0x0001);
+ dib9000_write_word(state, 901, 0xff19);
+ dib9000_write_word(state, 902, 0x003c);
+
+ dib9000_write_word(state, 898, 0);
+ dib9000_write_word(state, 899, 0);
+ dib9000_write_word(state, 900, 0);
+ dib9000_write_word(state, 901, 0);
+ dib9000_write_word(state, 902, 0);
+
+ dib9000_write_word(state, 911, state->chip.d9.cfg.if_drives);
+
+ dib9000_set_power_mode(state, DIB9000_POWER_INTERFACE_ONLY);
+
+ return 0;
+}
+
+static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, u8 * b, u32 len)
+{
+ u16 mb[10];
+ u8 i, s;
+
+ if (address >= 1024 || !state->platform.risc.fw_is_running)
+ return -EINVAL;
+
+ /* dprintk( "APB access thru rd fw %d %x", address, attribute); */
+
+ mb[0] = (u16) address;
+ mb[1] = len / 2;
+ dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_R, mb, 2, attribute);
+ switch (dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute)) {
+ case 1:
+ s--;
+ for (i = 0; i < s; i++) {
+ b[i * 2] = (mb[i + 1] >> 8) & 0xff;
+ b[i * 2 + 1] = (mb[i + 1]) & 0xff;
+ }
+ return 0;
+ default:
+ return -EIO;
+ }
+ return -EIO;
+}
+
+static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len)
+{
+ u16 mb[10];
+ u8 s, i;
+
+ if (address >= 1024 || !state->platform.risc.fw_is_running)
+ return -EINVAL;
+
+ /* dprintk( "APB access thru wr fw %d %x", address, attribute); */
+
+ mb[0] = (unsigned short)address;
+ for (i = 0; i < len && i < 20; i += 2)
+ mb[1 + (i / 2)] = (b[i] << 8 | b[i + 1]);
+
+ dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_W, mb, 1 + len / 2, attribute);
+ return dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute) == 1 ? 0 : -EINVAL;
+}
+
+static int dib9000_fw_memmbx_sync(struct dib9000_state *state, u8 i)
+{
+ u8 index_loop = 10;
+
+ if (!state->platform.risc.fw_is_running)
+ return 0;
+ dib9000_risc_mem_write(state, FE_MM_RW_SYNC, &i);
+ do {
+ dib9000_risc_mem_read(state, FE_MM_RW_SYNC, state->i2c_read_buffer, 1);
+ } while (state->i2c_read_buffer[0] && index_loop--);
+
+ if (index_loop > 0)
+ return 0;
+ return -EIO;
+}
+
+static int dib9000_fw_init(struct dib9000_state *state)
+{
+ struct dibGPIOFunction *f;
+ u16 b[40] = { 0 };
+ u8 i;
+ u8 size;
+
+ if (dib9000_fw_boot(state, NULL, 0, state->chip.d9.cfg.microcode_B_fe_buffer, state->chip.d9.cfg.microcode_B_fe_size) != 0)
+ return -EIO;
+
+ /* initialize the firmware */
+ for (i = 0; i < ARRAY_SIZE(state->chip.d9.cfg.gpio_function); i++) {
+ f = &state->chip.d9.cfg.gpio_function[i];
+ if (f->mask) {
+ switch (f->function) {
+ case BOARD_GPIO_FUNCTION_COMPONENT_ON:
+ b[0] = (u16) f->mask;
+ b[1] = (u16) f->direction;
+ b[2] = (u16) f->value;
+ break;
+ case BOARD_GPIO_FUNCTION_COMPONENT_OFF:
+ b[3] = (u16) f->mask;
+ b[4] = (u16) f->direction;
+ b[5] = (u16) f->value;
+ break;
+ }
+ }
+ }
+ if (dib9000_mbx_send(state, OUT_MSG_CONF_GPIO, b, 15) != 0)
+ return -EIO;
+
+ /* subband */
+ b[0] = state->chip.d9.cfg.subband.size; /* type == 0 -> GPIO - PWM not yet supported */
+ for (i = 0; i < state->chip.d9.cfg.subband.size; i++) {
+ b[1 + i * 4] = state->chip.d9.cfg.subband.subband[i].f_mhz;
+ b[2 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.mask;
+ b[3 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.direction;
+ b[4 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.value;
+ }
+ b[1 + i * 4] = 0; /* fe_id */
+ if (dib9000_mbx_send(state, OUT_MSG_SUBBAND_SEL, b, 2 + 4 * i) != 0)
+ return -EIO;
+
+ /* 0 - id, 1 - no_of_frontends */
+ b[0] = (0 << 8) | 1;
+ /* 0 = i2c-address demod, 0 = tuner */
+ b[1] = (0 << 8) | (0);
+ b[2] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000) >> 16) & 0xffff);
+ b[3] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000)) & 0xffff);
+ b[4] = (u16) ((state->chip.d9.cfg.vcxo_timer >> 16) & 0xffff);
+ b[5] = (u16) ((state->chip.d9.cfg.vcxo_timer) & 0xffff);
+ b[6] = (u16) ((state->chip.d9.cfg.timing_frequency >> 16) & 0xffff);
+ b[7] = (u16) ((state->chip.d9.cfg.timing_frequency) & 0xffff);
+ b[29] = state->chip.d9.cfg.if_drives;
+ if (dib9000_mbx_send(state, OUT_MSG_INIT_DEMOD, b, ARRAY_SIZE(b)) != 0)
+ return -EIO;
+
+ if (dib9000_mbx_send(state, OUT_MSG_FE_FW_DL, NULL, 0) != 0)
+ return -EIO;
+
+ if (dib9000_mbx_get_message(state, IN_MSG_FE_FW_DL_DONE, b, &size) < 0)
+ return -EIO;
+
+ if (size > ARRAY_SIZE(b)) {
+ dprintk("error : firmware returned %dbytes needed but the used buffer has only %dbytes\n Firmware init ABORTED", size,
+ (int)ARRAY_SIZE(b));
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i += 2) {
+ state->platform.risc.fe_mm[i / 2].addr = b[i + 0];
+ state->platform.risc.fe_mm[i / 2].size = b[i + 1];
+ }
+
+ return 0;
+}
+
+static void dib9000_fw_set_channel_head(struct dib9000_state *state)
+{
+ u8 b[9];
+ u32 freq = state->fe[0]->dtv_property_cache.frequency / 1000;
+ if (state->fe_id % 2)
+ freq += 101;
+
+ b[0] = (u8) ((freq >> 0) & 0xff);
+ b[1] = (u8) ((freq >> 8) & 0xff);
+ b[2] = (u8) ((freq >> 16) & 0xff);
+ b[3] = (u8) ((freq >> 24) & 0xff);
+ b[4] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 0) & 0xff);
+ b[5] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 8) & 0xff);
+ b[6] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 16) & 0xff);
+ b[7] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 24) & 0xff);
+ b[8] = 0x80; /* do not wait for CELL ID when doing autosearch */
+ if (state->fe[0]->dtv_property_cache.delivery_syst