// SPDX-License-Identifier: GPL-2.0-only
/*
* Linux-DVB Driver for DiBcom's DiB9000 and demodulator-family.
*
* Copyright (C) 2005-10 DiBcom (http://www.dibcom.fr/)
*/
#define pr_fmt(fmt) KBUILD_MODNAME
": " fmt
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/int_log.h>
#include <media/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(fmt, arg...)
do { \
if (debug) \
printk(KERN_DEBUG pr_fmt(
"%s: " fmt), \
__func__,
## arg); \
}
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
0 xffff
u16 gpio_dir;
#define DIB9000_GPIO_DEFAULT_VALUES
0 x0000
u16 gpio_val;
#define DIB9000_GPIO_DEFAULT_PWM_POS
0 xffff
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 at
tribute, 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 int 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 & 0 xff;
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 = min(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\n" , 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 & 0 xff;
if (i2c_transfer(i2c->i2c_adap, msg, 2 ) != 2 ) {
dprintk("read register %x error\n" , 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 int 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 ) & 0 xff;
state->i2c_write_buffer[1 ] = (reg) & 0 xff;
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 = min(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 ) & 0 xff;
i2c->i2c_write_buffer[1 ] = reg & 0 xff;
i2c->i2c_write_buffer[2 ] = (val >> 8 ) & 0 xff;
i2c->i2c_write_buffer[3 ] = val & 0 xff;
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 & 0 xff };
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 & 0 xff };
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 & 0 xff);
/* b[10] = 0; */
/* b[11] = 0; */
b[12 ] = (u8) (addr >> 8 );
b[13 ] = (u8) (addr & 0 xff);
addr += len;
/* b[6] = 0; */
/* b[7] = 0; */
b[8 ] = (u8) (addr >> 8 );
b[9 ] = (u8) (addr & 0 xff);
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 & 0 x7f];
/* decide whether we need to "refresh" the memory controller */
if (state->platform.risc.memcmd == cmd && /* same command */
!(cmd & 0 x80 && 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 & 0 x80);
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\n" );
return -EINTR;
}
dib9000_risc_mem_setup(state, cmd | 0 x80);
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\n" );
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, 0 x000f);
dib9000_write_word(state, 1025 + offs, 0 );
dib9000_write_word(state, 1031 + offs, key);
dprintk("going to download %dB of microcode\n" , len);
if (dib9000_write16_noinc(state, 1026 + offs, (u8 *) code, (u16) len) != 0 ) {
dprintk("error while downloading microcode for RISC %c\n" , 'A' + risc_id);
return -EIO;
}
dprintk("Microcode for RISC %c loaded\n" , '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, 0 x8000);
/* Read reset status */
do {
reset_reg = dib9000_read_word(state, 1027 + mbox_offs);
msleep(100 );
} while ((reset_reg & 0 x8000) && --tries);
if (reset_reg & 0 x8000) {
dprintk("MBX: init ERROR, no response from RISC %c\n" , 'A' + risc_id);
return -EIO;
}
dprintk("MBX: initialized\n" );
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\n" );
return -EINTR;
}
tmp = MAX_MAILBOX_TRY;
do {
size = dib9000_read_word_attr(state, 1043 , attr) & 0 xff;
if ((size + len + 1 ) > MBX_MAX_WORDS && --tmp) {
dprintk("MBX: RISC mbx full, retrying\n" );
msleep(100 );
} else
break ;
} while (1 );
/*dprintk( "MBX: size: %d\n", size); */
if (tmp == 0 ) {
ret = -EINVAL;
goto out;
}
#ifdef DUMP_MSG
dprintk("--> %02x %d %*ph\n" , id, len + 1 , len, data);
#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 & 0 xff;
}
/* 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\n" );
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 & 0 xff;
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("<--\n" );
for (i = 0 ; i < size + 1 ; i++)
dprintk("%04x\n" , d[i]);
dprintk("\n" );
#endif
} else {
dprintk("MBX: message is too big for message cache (%d), flushing message\n" , 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("%s\n" , b);
} else
dprintk("RISC%d: %d.%04d %s\n" ,
state->fe_id,
ts / 10000 , ts % 10000 , *b ? b : "<empty>" );
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\n", *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...\n" );
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 ) & 0 x1f; /* 5 bit field */
else
return (u8) (dib9000_read_word_attr(state, 1044 , attr) >> 8 ) & 0 x7f; /* 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\n" );
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\n", 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 & 0 xff) - 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\n" , 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)\n" , fw_version >> 10 , fw_version & 0 x3ff, (r[2 ] << 8 ) | r[3 ]);
if ((fw_version >> 10 ) != 7 )
return -EINVAL;
switch (fw_version & 0 x3ff) {
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 , 0 x02); /* A: 8k C, 4 k D - B: 32k C 6 k D - IRAM 96k */
dib9000_write_word(state, 1226 , 0 x05);
/* 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 , 0 x1234, codeA, lenA);
if (codeB != NULL)
dib9000_firmware_download(state, 1 , 0 x1234, 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 = 0 xff;
return 0 ;
}
static u16 dib9000_identify(struct i2c_device *client)
{
u16 value;
value = dib9000_i2c_read16(client, 896 );
if (value != 0 x01b3) {
dprintk("wrong Vendor ID (0x%x)\n" , value);
return 0 ;
}
value = dib9000_i2c_read16(client, 897 );
if (value != 0 x4000 && value != 0 x4001 && value != 0 x4002 && value != 0 x4003 && value != 0 x4004 && value != 0 x4005) {
dprintk("wrong Device ID (0x%x)\n" , value);
return 0 ;
}
/* protect this driver to be used with 7000PC */
if (value == 0 x4000 && dib9000_i2c_read16(client, 769 ) == 0 x4000) {
dprintk("this driver does not work with DiB7000PC\n" );
return 0 ;
}
switch (value) {
case 0 x4000:
dprintk("found DiB7000MA/PA/MB/PB\n" );
break ;
case 0 x4001:
dprintk("found DiB7000HC\n" );
break ;
case 0 x4002:
dprintk("found DiB7000MC\n" );
break ;
case 0 x4003:
dprintk("found DiB9000A\n" );
break ;
case 0 x4004:
dprintk("found DiB9000H\n" );
break ;
case 0 x4005:
dprintk("found DiB9000M\n" );
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 = 0 x3fff, reg_904 = 0 xffff, reg_905 = 0 xffff, reg_906;
u8 offset;
if (state->revision == 0 x4003 || state->revision == 0 x4004 || state->revision == 0 x4005)
offset = 1 ;
else
offset = 0 ;
reg_906 = dib9000_read_word(state, 906 + offset) | 0 x3; /* 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 = 0 x0000;
reg_904 = 0 x0000;
reg_905 = 0 x0000;
reg_906 = 0 x0000;
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 = 0 x0000;
reg_904 = 0 x801f;
reg_905 = 0 x0000;
reg_906 &= ~((1 << 0 ));
break ;
case DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD:
reg_903 = 0 x0000;
reg_904 = 0 x8000;
reg_905 = 0 x010b;
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 != 0 x4000)
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 , 0 x0003);
dib9000_write_word(state, 1227 , 1 );
dib9000_write_word(state, 1227 , 0 );
switch ((state->revision = dib9000_identify(&state->i2c))) {
case 0 x4003:
case 0 x4004:
case 0 x4005:
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 , 0 x805);
/* restart all parts */
dib9000_write_word(state, 898 , 0 xffff);
dib9000_write_word(state, 899 , 0 xffff);
dib9000_write_word(state, 900 , 0 x0001);
dib9000_write_word(state, 901 , 0 xff19);
dib9000_write_word(state, 902 , 0 x003c);
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 through rd fw %d %x\n", 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 ) & 0 xff;
b[i * 2 + 1 ] = (mb[i + 1 ]) & 0 xff;
}
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;
if (len > 18 )
return -EINVAL;
/* dprintk( "APB access through wr fw %d %x\n", address, attribute); */
mb[0 ] = (u16)address;
for (i = 0 ; i + 1 < len; i += 2 )
mb[1 + i / 2 ] = b[i] << 8 | b[i + 1 ];
if (len & 1 )
mb[1 + len / 2 ] = b[len - 1 ] << 8 ;
dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_W, mb, (3 + 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 ) & 0 xffff);
b[3 ] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000 )) & 0 xffff);
b[4 ] = (u16) ((state->chip.d9.cfg.vcxo_timer >> 16 ) & 0 xffff);
b[5 ] = (u16) ((state->chip.d9.cfg.vcxo_timer) & 0 xffff);
b[6 ] = (u16) ((state->chip.d9.cfg.timing_frequency >> 16 ) & 0 xffff);
b[7 ] = (u16) ((state->chip.d9.cfg.timing_frequency) & 0 xffff);
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 ) & 0 xff);
b[1 ] = (u8) ((freq >> 8 ) & 0 xff);
b[2 ] = (u8) ((freq >> 16 ) & 0 xff);
b[3 ] = (u8) ((freq >> 24 ) & 0 xff);
b[4 ] = (u8) ((state->fe[0 ]->dtv_property_cache.bandwidth_hz / 1000 >> 0 ) & 0 xff);
b[5 ] = (u8) ((state->fe[0 ]->dtv_property_cache.bandwidth_hz / 1000 >> 8 ) & 0 xff);
b[6 ] = (u8) ((state->fe[0 ]->dtv_property_cache.bandwidth_hz / 1000 >> 16 ) & 0 xff);
b[7 ] = (u8) ((state->fe[0 ]->dtv_property_cache.bandwidth_hz / 1000 >> 24 ) & 0 xff);
b[8 ] = 0 x80; /* do not wait for CELL ID when doing autosearch */
if (state->fe[0 ]->dtv_property_cache.delivery_system == SYS_DVBT)
b[8 ] |= 1 ;
dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_HEAD, b);
}
static int dib9000_fw_get_channel(struct dvb_frontend *fe)
{
struct dib9000_state *state = fe->demodulator_priv;
struct dibDVBTChannel {
s8 spectrum_inversion;
s8 nfft;
s8 guard;
s8 constellation;
s8 hrch;
s8 alpha;
s8 code_rate_hp;
s8 code_rate_lp;
s8 select_hp;
s8 intlv_native;
};
struct dibDVBTChannel *ch;
int ret = 0 ;
if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0 ) {
dprintk("could not get the lock\n" );
return -EINTR;
}
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0 ) {
ret = -EIO;
goto error;
}
dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_UNION,
state->i2c_read_buffer, sizeof (struct dibDVBTChannel));
ch = (struct dibDVBTChannel *)state->i2c_read_buffer;
switch (ch->spectrum_inversion & 0 x7) {
case 1 :
state->fe[0 ]->dtv_property_cache.inversion = INVERSION_ON;
break ;
case 0 :
state->fe[0 ]->dtv_property_cache.inversion = INVERSION_OFF;
break ;
default :
case -1 :
state->fe[0 ]->dtv_property_cache.inversion = INVERSION_AUTO;
break ;
}
switch (ch->nfft) {
case 0 :
state->fe[0 ]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_2K;
break ;
case 2 :
state->fe[0 ]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_4K;
break ;
case 1 :
state->fe[0 ]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K;
break ;
default :
case -1 :
state->fe[0 ]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_AUTO;
break ;
}
switch (ch->guard) {
case 0 :
state->fe[0 ]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_32;
break ;
case 1 :
state->fe[0 ]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_16;
break ;
case 2 :
state->fe[0 ]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8;
break ;
case 3 :
state->fe[0 ]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_4;
break ;
default :
case -1 :
state->fe[0 ]->dtv_property_cache.guard_interval = GUARD_INTERVAL_AUTO;
break ;
}
switch (ch->constellation) {
case 2 :
state->fe[0 ]->dtv_property_cache.modulation = QAM_64;
break ;
case 1 :
state->fe[0 ]->dtv_property_cache.modulation = QAM_16;
break ;
case 0 :
state->fe[0 ]->dtv_property_cache.modulation = QPSK;
break ;
default :
case -1 :
state->fe[0 ]->dtv_property_cache.modulation = QAM_AUTO;
break ;
}
switch (ch->hrch) {
case 0 :
state->fe[0 ]->dtv_property_cache.hierarchy = HIERARCHY_NONE;
break ;
case 1 :
state->fe[0 ]->dtv_property_cache.hierarchy = HIERARCHY_1;
break ;
default :
case -1 :
state->fe[0 ]->dtv_property_cache.hierarchy = HIERARCHY_AUTO;
break ;
}
switch (ch->code_rate_hp) {
case 1 :
state->fe[0 ]->dtv_property_cache.code_rate_HP = FEC_1_2;
break ;
case 2 :
state->fe[0 ]->dtv_property_cache.code_rate_HP = FEC_2_3;
break ;
case 3 :
state->fe[0 ]->dtv_property_cache.code_rate_HP = FEC_3_4;
break ;
case 5 :
state->fe[0 ]->dtv_property_cache.code_rate_HP = FEC_5_6;
break ;
case 7 :
state->fe[0 ]->dtv_property_cache.code_rate_HP = FEC_7_8;
break ;
default :
case -1 :
state->fe[0 ]->dtv_property_cache.code_rate_HP = FEC_AUTO;
break ;
}
switch (ch->code_rate_lp) {
case 1 :
state->fe[0 ]->dtv_property_cache.code_rate_LP = FEC_1_2;
break ;
case 2 :
state->fe[0 ]->dtv_property_cache.code_rate_LP = FEC_2_3;
break ;
case 3 :
state->fe[0 ]->dtv_property_cache.code_rate_LP = FEC_3_4;
break ;
case 5 :
state->fe[0 ]->dtv_property_cache.code_rate_LP = FEC_5_6;
break ;
case 7 :
state->fe[0 ]->dtv_property_cache.code_rate_LP = FEC_7_8;
break ;
default :
case -1 :
state->fe[0 ]->dtv_property_cache.code_rate_LP = FEC_AUTO;
break ;
}
error:
mutex_unlock(&state->platform.risc.mem_mbx_lock);
return ret;
}
static int dib9000_fw_set_channel_union(struct dvb_frontend *fe)
{
struct dib9000_state *state = fe->demodulator_priv;
struct dibDVBTChannel {
s8 spectrum_inversion;
s8 nfft;
s8 guard;
s8 constellation;
s8 hrch;
s8 alpha;
s8 code_rate_hp;
s8 code_rate_lp;
s8 select_hp;
s8 intlv_native;
};
struct dibDVBTChannel ch;
switch (state->fe[0 ]->dtv_property_cache.inversion) {
case INVERSION_ON:
ch.spectrum_inversion = 1 ;
break ;
case INVERSION_OFF:
ch.spectrum_inversion = 0 ;
break ;
default :
case INVERSION_AUTO:
ch.spectrum_inversion = -1 ;
break ;
}
switch (state->fe[0 ]->dtv_property_cache.transmission_mode) {
case TRANSMISSION_MODE_2K:
ch.nfft = 0 ;
break ;
case TRANSMISSION_MODE_4K:
ch.nfft = 2 ;
break ;
case TRANSMISSION_MODE_8K:
ch.nfft = 1 ;
break ;
default :
case TRANSMISSION_MODE_AUTO:
ch.nfft = 1 ;
break ;
}
switch (state->fe[0 ]->dtv_property_cache.guard_interval) {
case GUARD_INTERVAL_1_32:
ch.guard = 0 ;
break ;
case GUARD_INTERVAL_1_16:
ch.guard = 1 ;
break ;
case GUARD_INTERVAL_1_8:
ch.guard = 2 ;
break ;
case GUARD_INTERVAL_1_4:
ch.guard = 3 ;
break ;
default :
case GUARD_INTERVAL_AUTO:
ch.guard = -1 ;
break ;
}
switch (state->fe[0 ]->dtv_property_cache.modulation) {
case QAM_64:
ch.constellation = 2 ;
break ;
case QAM_16:
ch.constellation = 1 ;
break ;
case QPSK:
ch.constellation = 0 ;
break ;
default :
case QAM_AUTO:
ch.constellation = -1 ;
break ;
}
switch (state->fe[0 ]->dtv_property_cache.hierarchy) {
case HIERARCHY_NONE:
ch.hrch = 0 ;
break ;
case HIERARCHY_1:
case HIERARCHY_2:
case HIERARCHY_4:
ch.hrch = 1 ;
break ;
default :
case HIERARCHY_AUTO:
ch.hrch = -1 ;
break ;
}
ch.alpha = 1 ;
switch (state->fe[0 ]->dtv_property_cache.code_rate_HP) {
case FEC_1_2:
ch.code_rate_hp = 1 ;
break ;
case FEC_2_3:
ch.code_rate_hp = 2 ;
break ;
case FEC_3_4:
ch.code_rate_hp = 3 ;
break ;
case FEC_5_6:
ch.code_rate_hp = 5 ;
break ;
case FEC_7_8:
ch.code_rate_hp = 7 ;
break ;
default :
case FEC_AUTO:
ch.code_rate_hp = -1 ;
break ;
}
switch (state->fe[0 ]->dtv_property_cache.code_rate_LP) {
case FEC_1_2:
ch.code_rate_lp = 1 ;
break ;
case FEC_2_3:
ch.code_rate_lp = 2 ;
break ;
case FEC_3_4:
ch.code_rate_lp = 3 ;
break ;
case FEC_5_6:
ch.code_rate_lp = 5 ;
break ;
case FEC_7_8:
ch.code_rate_lp = 7 ;
break ;
default :
case FEC_AUTO:
ch.code_rate_lp = -1 ;
break ;
}
ch.select_hp = 1 ;
ch.intlv_native = 1 ;
dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_UNION, (u8 *) &ch);
return 0 ;
}
static int dib9000_fw_tune(struct dvb_frontend *fe)
{
struct dib9000_state *state = fe->demodulator_priv;
int ret = 10 , search = state->channel_status.status == CHANNEL_STATUS_PARAMETERS_UNKNOWN;
s8 i;
switch (state->tune_state) {
case CT_DEMOD_START:
dib9000_fw_set_channel_head(state);
/* write the channel context - a channel is initialized to 0, so it is OK */
dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_CONTEXT, (u8 *) fe_info);
dib9000_risc_mem_write(state, FE_MM_W_FE_INFO, (u8 *) fe_info);
if (search)
dib9000_mbx_send(state, OUT_MSG_FE_CHANNEL_SEARCH, NULL, 0 );
else {
dib9000_fw_set_channel_union(fe);
dib9000_mbx_send(state, OUT_MSG_FE_CHANNEL_TUNE, NULL, 0 );
}
state->tune_state = CT_DEMOD_STEP_1;
break ;
case CT_DEMOD_STEP_1:
if (search)
dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_SEARCH_STATE, state->i2c_read_buffer, 1 );
else
dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_TUNE_STATE, state->i2c_read_buffer, 1 );
i = (s8)state->i2c_read_buffer[0 ];
switch (i) { /* something happened */
case 0 :
break ;
case -2 : /* tps locks are "slower" than MPEG locks -> even in autosearch data is OK here */
if (search)
state->status = FE_STATUS_DEMOD_SUCCESS;
else {
state->tune_state = CT_DEMOD_STOP;
state->status = FE_STATUS_LOCKED;
}
break ;
default :
state->status = FE_STATUS_TUNE_FAILED;
state->tune_state = CT_DEMOD_STOP;
break ;
}
break ;
default :
ret = FE_CALLBACK_TIME_NEVER;
break ;
}
return ret;
}
static int dib9000_fw_set_diversity_in(struct dvb_frontend *fe, int onoff)
{
struct dib9000_state *state = fe->demodulator_priv;
u16 mode = (u16) onoff;
return dib9000_mbx_send(state, OUT_MSG_ENABLE_DIVERSITY, &mode, 1 );
}
static int dib9000_fw_set_output_mode(struct dvb_frontend *fe, int mode)
{
struct dib9000_state *state = fe->demodulator_priv;
u16 outreg, smo_mode;
dprintk("setting output mode for demod %p to %d\n" , fe, mode);
switch (mode) {
case OUTMODE_MPEG2_PAR_GATED_CLK:
outreg = (1 << 10 ); /* 0x0400 */
break ;
case OUTMODE_MPEG2_PAR_CONT_CLK:
outreg = (1 << 10 ) | (1 << 6 ); /* 0x0440 */
break ;
case OUTMODE_MPEG2_SERIAL:
outreg = (1 << 10 ) | (2 << 6 ) | (0 << 1 ); /* 0x0482 */
break ;
case OUTMODE_DIVERSITY:
outreg = (1 << 10 ) | (4 << 6 ); /* 0x0500 */
break ;
case OUTMODE_MPEG2_FIFO:
outreg = (1 << 10 ) | (5 << 6 );
break ;
case OUTMODE_HIGH_Z:
outreg = 0 ;
break ;
default :
dprintk("Unhandled output_mode passed to be set for demod %p\n" , &state->fe[0 ]);
return -EINVAL;
}
dib9000_write_word(state, 1795 , outreg);
switch (mode) {
case OUTMODE_MPEG2_PAR_GATED_CLK:
case OUTMODE_MPEG2_PAR_CONT_CLK:
case OUTMODE_MPEG2_SERIAL:
case OUTMODE_MPEG2_FIFO:
smo_mode = (dib9000_read_word(state, 295 ) & 0 x0010) | (1 << 1 );
if (state->chip.d9.cfg.output_mpeg2_in_188_bytes)
smo_mode |= (1 << 5 );
dib9000_write_word(state, 295 , smo_mode);
break ;
}
outreg = to_fw_output_mode(mode);
return dib9000_mbx_send(state, OUT_MSG_SET_OUTPUT_MODE, &outreg, 1 );
}
static int dib9000_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
{
struct dib9000_state *state = i2c_get_adapdata(i2c_adap);
u16 i, len, t, index_msg;
for (index_msg = 0 ; index_msg < num; index_msg++) {
if (msg[index_msg].flags & I2C_M_RD) { /* read */
len = msg[index_msg].len;
if (len > 16 )
len = 16 ;
if (dib9000_read_word(state, 790 ) != 0 )
dprintk("TunerITF: read busy\n" );
dib9000_write_word(state, 784 , (u16) (msg[index_msg].addr));
dib9000_write_word(state, 787 , (len / 2 ) - 1 );
dib9000_write_word(state, 786 , 1 ); /* start read */
i = 1000 ;
while (dib9000_read_word(state, 790 ) != (len / 2 ) && i)
i--;
if (i == 0 )
dprintk("TunerITF: read failed\n" );
for (i = 0 ; i < len; i += 2 ) {
t = dib9000_read_word(state, 785 );
msg[index_msg].buf[i] = (t >> 8 ) & 0 xff;
msg[index_msg].buf[i + 1 ] = (t) & 0 xff;
}
if (dib9000_read_word(state, 790 ) != 0 )
dprintk("TunerITF: read more data than expected\n" );
} else {
i = 1000 ;
while (dib9000_read_word(state, 789 ) && i)
i--;
if (i == 0 )
dprintk("TunerITF: write busy\n" );
len = msg[index_msg].len;
if (len > 16 )
len = 16 ;
for (i = 0 ; i < len; i += 2 )
dib9000_write_word(state, 785 , (msg[index_msg].buf[i] << 8 ) | msg[index_msg].buf[i + 1 ]);
dib9000_write_word(state, 784 , (u16) msg[index_msg].addr);
dib9000_write_word(state, 787 , (len / 2 ) - 1 );
dib9000_write_word(state, 786 , 0 ); /* start write */
i = 1000 ;
while (dib9000_read_word(state, 791 ) > 0 && i)
i--;
if (i == 0 )
dprintk("TunerITF: write failed\n" );
}
}
return num;
}
int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed)
{
struct dib9000_state *state = fe->demodulator_priv;
state->component_bus_speed = speed;
return 0 ;
}
EXPORT_SYMBOL(dib9000_fw_set_component_bus_speed);
static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
{
struct dib9000_state *state = i2c_get_adapdata(i2c_adap);
u8 type = 0 ; /* I2C */
u8 port = DIBX000_I2C_INTERFACE_GPIO_3_4;
u16 scl = state->component_bus_speed; /* SCL frequency */
struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[FE_MM_RW_COMPONENT_ACCESS_BUFFER];
u8 p[13 ] = { 0 };
p[0 ] = type;
p[1 ] = port;
p[2 ] = msg[0 ].addr << 1 ;
p[3 ] = (u8) scl & 0 xff; /* scl */
p[4 ] = (u8) (scl >> 8 );
p[7 ] = 0 ;
p[8 ] = 0 ;
p[9 ] = (u8) (msg[0 ].len);
p[10 ] = (u8) (msg[0 ].len >> 8 );
if ((num > 1 ) && (msg[1 ].flags & I2C_M_RD)) {
p[11 ] = (u8) (msg[1 ].len);
p[12 ] = (u8) (msg[1 ].len >> 8 );
} else {
p[11 ] = 0 ;
p[12 ] = 0 ;
}
if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0 ) {
dprintk("could not get the lock\n" );
return 0 ;
}
dib9000_risc_mem_write(state, FE_MM_W_COMPONENT_ACCESS, p);
{ /* write-part */
dib9000_risc_mem_setup_cmd(state, m->addr, msg[0 ].len, 0 );
dib9000_risc_mem_write_chunks(state, msg[0 ].buf, msg[0 ].len);
}
/* do the transaction */
if (dib9000_fw_memmbx_sync(state, FE_SYNC_COMPONENT_ACCESS) < 0 ) {
mutex_unlock(&state->platform.risc.mem_mbx_lock);
return 0 ;
}
/* read back any possible result */
if ((num > 1 ) && (msg[1 ].flags & I2C_M_RD))
dib9000_risc_mem_read(state, FE_MM_RW_COMPONENT_ACCESS_BUFFER, msg[1 ].buf, msg[1 ].len);
mutex_unlock(&state->platform.risc.mem_mbx_lock);
return num;
}
static u32 dib9000_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static const struct i2c_algorithm dib9000_tuner_algo = {
.master_xfer = dib9000_tuner_xfer,
.functionality = dib9000_i2c_func,
};
static const struct i2c_algorithm dib9000_component_bus_algo = {
.master_xfer = dib9000_fw_component_bus_xfer,
.functionality = dib9000_i2c_func,
};
struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe)
{
struct dib9000_state *st = fe->demodulator_priv;
return &st->tuner_adap;
}
EXPORT_SYMBOL(dib9000_get_tuner_interface);
struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe)
{
struct dib9000_state *st = fe->demodulator_priv;
return &st->component_bus;
}
EXPORT_SYMBOL(dib9000_get_component_bus_interface);
struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating)
{
struct dib9000_state *st = fe->demodulator_priv;
return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating);
}
EXPORT_SYMBOL(dib9000_get_i2c_master);
int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c)
{
struct dib9000_state *st = fe->demodulator_priv;
st->i2c.i2c_adap = i2c;
return 0 ;
}
EXPORT_SYMBOL(dib9000_set_i2c_adapter);
static int dib9000_cfg_gpio(struct dib9000_state *st, u8 num, u8 dir, u8 val)
{
st->gpio_dir = dib9000_read_word(st, 773 );
st->gpio_dir &= ~(1 << num); /* reset the direction bit */
st->gpio_dir |= (dir & 0 x1) << num; /* set the new direction */
dib9000_write_word(st, 773 , st->gpio_dir);
st->gpio_val = dib9000_read_word(st, 774 );
st->gpio_val &= ~(1 << num); /* reset the direction bit */
st->gpio_val |= (val & 0 x01) << num; /* set the new value */
dib9000_write_word(st, 774 , st->gpio_val);
dprintk("gpio dir: %04x: gpio val: %04x\n" , st->gpio_dir, st->gpio_val);
return 0 ;
}
int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
{
struct dib9000_state *state = fe->demodulator_priv;
return dib9000_cfg_gpio(state, num, dir, val);
}
EXPORT_SYMBOL(dib9000_set_gpio);
int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
{
struct dib9000_state *state = fe->demodulator_priv;
u16 val;
int ret;
if ((state->pid_ctrl_index != -2 ) && (state->pid_ctrl_index < 9 )) {
/* postpone the pid filtering cmd */
dprintk("pid filter cmd postpone\n" );
state->pid_ctrl_index++;
state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER_CTRL;
state->pid_ctrl[state->pid_ctrl_index].onoff = onoff;
return 0 ;
}
if (mutex_lock_interruptible(&state->demod_lock) < 0 ) {
dprintk("could not get the lock\n" );
return -EINTR;
}
val = dib9000_read_word(state, 294 + 1 ) & 0 xffef;
val |= (onoff & 0 x1) << 4 ;
dprintk("PID filter enabled %d\n" , onoff);
ret = dib9000_write_word(state, 294 + 1 , val);
mutex_unlock(&state->demod_lock);
return ret;
}
EXPORT_SYMBOL(dib9000_fw_pid_filter_ctrl);
int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
{
struct dib9000_state *state = fe->demodulator_priv;
int ret;
if (state->pid_ctrl_index != -2 ) {
/* postpone the pid filtering cmd */
dprintk("pid filter postpone\n" );
if (state->pid_ctrl_index < 9 ) {
state->pid_ctrl_index++;
state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER;
state->pid_ctrl[state->pid_ctrl_index].id = id;
state->pid_ctrl[state->pid_ctrl_index].pid = pid;
state->pid_ctrl[state->pid_ctrl_index].onoff = onoff;
} else
dprintk("can not add any more pid ctrl cmd\n" );
return 0 ;
}
if (mutex_lock_interruptible(&state->demod_lock) < 0 ) {
dprintk("could not get the lock\n" );
return -EINTR;
}
dprintk("Index %x, PID %d, OnOff %d\n" , id, pid, onoff);
ret = dib9000_write_word(state, 300 + 1 + id,
onoff ? (1 << 13 ) | pid : 0 );
mutex_unlock(&state->demod_lock);
return ret;
}
EXPORT_SYMBOL(dib9000_fw_pid_filter);
int dib9000_firmware_post_pll_init(struct dvb_frontend *fe)
{
struct dib9000_state *state = fe->demodulator_priv;
return dib9000_fw_init(state);
}
EXPORT_SYMBOL(dib9000_firmware_post_pll_init);
static void dib9000_release(struct dvb_frontend *demod)
{
struct dib9000_state *st = demod->demodulator_priv;
u8 index_frontend;
for (index_frontend = 1 ; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++)
dvb_frontend_detach(st->fe[index_frontend]);
dibx000_exit_i2c_master(&st->i2c_master);
i2c_del_adapter(&st->tuner_adap);
i2c_del_adapter(&st->component_bus);
kfree(st->fe[0 ]);
kfree(st);
}
static int dib9000_wakeup(struct dvb_frontend *fe)
{
return 0 ;
}
static int dib9000_sleep(struct dvb_frontend *fe)
{
struct dib9000_state *state = fe->demodulator_priv;
u8 index_frontend;
int ret = 0 ;
if (mutex_lock_interruptible(&state->demod_lock) < 0 ) {
dprintk("could not get the lock\n" );
return -EINTR;
}
for (index_frontend = 1 ; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]);
if (ret < 0 )
goto error;
}
ret = dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0 );
error:
mutex_unlock(&state->demod_lock);
return ret;
}
static int dib9000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune)
{
tune->min_delay_ms = 1000 ;
return 0 ;
}
static int dib9000_get_frontend(struct dvb_frontend *fe,
struct dtv_frontend_properties *c)
{
struct dib9000_state *state = fe->demodulator_priv;
u8 index_frontend, sub_index_frontend;
enum fe_status stat;
int ret = 0 ;
if (state->get_frontend_internal == 0 ) {
if (mutex_lock_interruptible(&state->demod_lock) < 0 ) {
dprintk("could not get the lock\n" );
return -EINTR;
}
}
for (index_frontend = 1 ; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat);
if (stat & FE_HAS_SYNC) {
dprintk("TPS lock on the slave%i\n" , index_frontend);
/* synchronize the cache with the other frontends */
state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend], c);
for (sub_index_frontend = 0 ; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL);
sub_index_frontend++) {
if (sub_index_frontend != index_frontend) {
state->fe[sub_index_frontend]->dtv_property_cache.modulation =
state->fe[index_frontend]->dtv_property_cache.modulation;
state->fe[sub_index_frontend]->dtv_property_cache.inversion =
state->fe[index_frontend]->dtv_property_cache.inversion;
state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode =
state->fe[index_frontend]->dtv_property_cache.transmission_mode;
state->fe[sub_index_frontend]->dtv_property_cache.guard_interval =
state->fe[index_frontend]->dtv_property_cache.guard_interval;
state->fe[sub_index_frontend]->dtv_property_cache.hierarchy =
state->fe[index_frontend]->dtv_property_cache.hierarchy;
state->fe[sub_index_frontend]->dtv_property_cache.code_rate_HP =
state->fe[index_frontend]->dtv_property_cache.code_rate_HP;
state->fe[sub_index_frontend]->dtv_property_cache.code_rate_LP =
state->fe[index_frontend]->dtv_property_cache.code_rate_LP;
state->fe[sub_index_frontend]->dtv_property_cache.rolloff =
state->fe[index_frontend]->dtv_property_cache.rolloff;
}
}
ret = 0 ;
goto return_value;
}
}
/* get the channel from master chip */
ret = dib9000_fw_get_channel(fe);
if (ret != 0 )
goto return_value;
/* synchronize the cache with the other frontends */
for (index_frontend = 1 ; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
state->fe[index_frontend]->dtv_property_cache.inversion = c->inversion;
state->fe[index_frontend]->dtv_property_cache.transmission_mode = c->transmission_mode;
state->fe[index_frontend]->dtv_property_cache.guard_interval = c->guard_interval;
state->fe[index_frontend]->dtv_property_cache.modulation = c->modulation;
state->fe[index_frontend]->dtv_property_cache.hierarchy = c->hierarchy;
state->fe[index_frontend]->dtv_property_cache.code_rate_HP = c->code_rate_HP;
state->fe[index_frontend]->dtv_property_cache.code_rate_LP = c->code_rate_LP;
state->fe[index_frontend]->dtv_property_cache.rolloff = c->rolloff;
}
ret = 0 ;
return_value:
if (state->get_frontend_internal == 0 )
mutex_unlock(&state->demod_lock);
return ret;
}
static int dib9000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state)
{
struct dib9000_state *state = fe->demodulator_priv;
state->tune_state = tune_state;
if (tune_state == CT_DEMOD_START)
state->status = FE_STATUS_TUNE_PENDING;
return 0 ;
}
static u32 dib9000_get_status(struct dvb_frontend *fe)
{
struct dib9000_state *state = fe->demodulator_priv;
return state->status;
}
static int dib9000_set_channel_status(struct dvb_frontend *fe, struct dvb_frontend_parametersContext *channel_status)
{
struct dib9000_state *state = fe->demodulator_priv;
memcpy(&state->channel_status, channel_status, sizeof (struct dvb_frontend_parametersContext));
return 0 ;
}
static int dib9000_set_frontend(struct dvb_frontend *fe)
{
struct dib9000_state *state = fe->demodulator_priv;
int sleep_time, sleep_time_slave;
u32 frontend_status;
u8 nbr_pending, exit_condition, index_frontend, index_frontend_success;
struct dvb_frontend_parametersContext channel_status;
/* check that the correct parameters are set */
if (state->fe[0 ]->dtv_property_cache.frequency == 0 ) {
dprintk("dib9000: must specify frequency\n" );
return 0 ;
}
if (state->fe[0 ]->dtv_property_cache.bandwidth_hz == 0 ) {
dprintk("dib9000: must specify bandwidth\n" );
return 0 ;
}
state->pid_ctrl_index = -1 ; /* postpone the pid filtering cmd */
if (mutex_lock_interruptible(&state->demod_lock) < 0 ) {
dprintk("could not get the lock\n" );
return 0 ;
}
fe->dtv_property_cache.delivery_system = SYS_DVBT;
/* set the master status */
if (state->fe[0 ]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO ||
state->fe[0 ]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO ||
state->fe[0 ]->dtv_property_cache.modulation == QAM_AUTO ||
state->fe[0 ]->dtv_property_cache.code_rate_HP == FEC_AUTO) {
/* no channel specified, autosearch the channel */
state->channel_status.status = CHANNEL_STATUS_PARAMETERS_UNKNOWN;
} else
state->channel_status.status = CHANNEL_STATUS_PARAMETERS_SET;
/* set mode and status for the different frontends */
for (index_frontend = 0 ; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
dib9000_fw_set_diversity_in(state->fe[index_frontend], 1 );
/* synchronization of the cache */
memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof (struct dtv_frontend_properties));
state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_DVBT;
dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_HIGH_Z);
dib9000_set_channel_status(state->fe[index_frontend], &state->channel_status);
dib9000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START);
}
/* actual tune */
exit_condition = 0 ; /* 0: tune pending; 1: tune failed; 2:tune success */
index_frontend_success = 0 ;
do {
sleep_time = dib9000_fw_tune(state->fe[0 ]);
for (index_frontend = 1 ; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend]);
if (sleep_time == FE_CALLBACK_TIME_NEVER)
sleep_time = sleep_time_slave;
else if ((sleep_time_slave != FE_CALLBACK_TIME_NEVER) && (sleep_time_slave > sleep_time))
sleep_time = sleep_time_slave;
}
if (sleep_time != FE_CALLBACK_TIME_NEVER)
msleep(sleep_time / 10 );
else
break ;
nbr_pending = 0 ;
exit_condition = 0 ;
index_frontend_success = 0 ;
for (index_frontend = 0 ; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
frontend_status = -dib9000_get_status(state->fe[index_frontend]);
if (frontend_status > -FE_STATUS_TUNE_PENDING) {
exit_condition = 2 ; /* tune success */
index_frontend_success = index_frontend;
break ;
}
if (frontend_status == -FE_STATUS_TUNE_PENDING)
nbr_pending++; /* some frontends are still tuning */
}
if ((exit_condition != 2 ) && (nbr_pending == 0 ))
exit_condition = 1 ; /* if all tune are done and no success, exit: tune failed */
} while (exit_condition == 0 );
/* check the tune result */
if (exit_condition == 1 ) { /* tune failed */
dprintk("tune failed\n" );
mutex_unlock(&state->demod_lock);
/* tune failed; put all the pid filtering cmd to junk */
state->pid_ctrl_index = -1 ;
return 0 ;
}
dprintk("tune success on frontend%i\n" , index_frontend_success);
/* synchronize all the channel cache */
state->get_frontend_internal = 1 ;
dib9000_get_frontend(state->fe[0 ], &state->fe[0 ]->dtv_property_cache);
state->get_frontend_internal = 0 ;
/* retune the other frontends with the found channel */
channel_status.status = CHANNEL_STATUS_PARAMETERS_SET;
for (index_frontend = 0 ; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
/* only retune the frontends which was not tuned success */
if (index_frontend != index_frontend_success) {
dib9000_set_channel_status(state->fe[index_frontend], &channel_status);
dib9000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START);
}
}
do {
sleep_time = FE_CALLBACK_TIME_NEVER;
for (index_frontend = 0 ; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
if (index_frontend != index_frontend_success) {
sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend]);
if (sleep_time == FE_CALLBACK_TIME_NEVER)
sleep_time = sleep_time_slave;
else if ((sleep_time_slave != FE_CALLBACK_TIME_NEVER) && (sleep_time_slave > sleep_time))
sleep_time = sleep_time_slave;
}
}
if (sleep_time != FE_CALLBACK_TIME_NEVER)
msleep(sleep_time / 10 );
else
break ;
nbr_pending = 0 ;
for (index_frontend = 0 ; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
if (index_frontend != index_frontend_success) {
frontend_status = -dib9000_get_status(state->fe[index_frontend]);
if ((index_frontend != index_frontend_success) && (frontend_status == -FE_STATUS_TUNE_PENDING))
nbr_pending++; /* some frontends are still tuning */
}
}
} while (nbr_pending != 0 );
/* set the output mode */
dib9000_fw_set_output_mode(state->fe[0 ], state->chip.d9.cfg.output_mode);
for (index_frontend = 1 ; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_DIVERSITY);
/* turn off the diversity for the last frontend */
dib9000_fw_set_diversity_in(state->fe[index_frontend - 1 ], 0 );
mutex_unlock(&state->demod_lock);
if (state->pid_ctrl_index >= 0 ) {
u8 index_pid_filter_cmd;
u8 pid_ctrl_index = state->pid_ctrl_index;
state->pid_ctrl_index = -2 ;
for (index_pid_filter_cmd = 0 ;
index_pid_filter_cmd <= pid_ctrl_index;
index_pid_filter_cmd++) {
if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER_CTRL)
dib9000_fw_pid_filter_ctrl(state->fe[0 ],
state->pid_ctrl[index_pid_filter_cmd].onoff);
else if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER)
dib9000_fw_pid_filter(state->fe[0 ],
state->pid_ctrl[index_pid_filter_cmd].id,
state->pid_ctrl[index_pid_filter_cmd].pid,
state->pid_ctrl[index_pid_filter_cmd].onoff);
}
}
/* do not postpone any more the pid filtering */
state->pid_ctrl_index = -2 ;
return 0 ;
}
static u16 dib9000_read_lock(struct dvb_frontend *fe)
{
struct dib9000_state *state = fe->demodulator_priv;
return dib9000_read_word(state, 535 );
}
static int dib9000_read_status(struct dvb_frontend *fe, enum fe_status *stat)
{
struct dib9000_state *state = fe->demodulator_priv;
u8 index_frontend;
u16 lock = 0 , lock_slave = 0 ;
if (mutex_lock_interruptible(&state->demod_lock) < 0 ) {
dprintk("could not get the lock\n" );
return -EINTR;
}
for (index_frontend = 1 ; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
lock_slave |= dib9000_read_lock(state->fe[index_frontend]);
lock = dib9000_read_word(state, 535 );
*stat = 0 ;
if ((lock & 0 x8000) || (lock_slave & 0 x8000))
*stat |= FE_HAS_SIGNAL;
if ((lock & 0 x3000) || (lock_slave & 0 x3000))
*stat |= FE_HAS_CARRIER;
if ((lock & 0 x0100) || (lock_slave & 0 x0100))
*stat |= FE_HAS_VITERBI;
if (((lock & 0 x0038) == 0 x38) || ((lock_slave & 0 x0038) == 0 x38))
*stat |= FE_HAS_SYNC;
if ((lock & 0 x0008) || (lock_slave & 0 x0008))
*stat |= FE_HAS_LOCK;
mutex_unlock(&state->demod_lock);
return 0 ;
}
static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber)
{
struct dib9000_state *state = fe->demodulator_priv;
u16 *c;
int ret = 0 ;
if (mutex_lock_interruptible(&state->demod_lock) < 0 ) {
dprintk("could not get the lock\n" );
return -EINTR;
}
if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0 ) {
dprintk("could not get the lock\n" );
ret = -EINTR;
goto error;
}
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0 ) {
mutex_unlock(&state->platform.risc.mem_mbx_lock);
ret = -EIO;
goto error;
}
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR,
state->i2c_read_buffer, 16 * 2 );
mutex_unlock(&state->platform.risc.mem_mbx_lock);
c = (u16 *)state->i2c_read_buffer;
*ber = c[10 ] << 16 | c[11 ];
error:
mutex_unlock(&state->demod_lock);
return ret;
}
static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
{
struct dib9000_state *state = fe->demodulator_priv;
u8 index_frontend;
u16 *c = (u16 *)state->i2c_read_buffer;
u16 val;
int ret = 0 ;
if (mutex_lock_interruptible(&state->demod_lock) < 0 ) {
dprintk("could not get the lock\n" );
return -EINTR;
}
*strength = 0 ;
for (index_frontend = 1 ; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val);
if (val > 65535 - *strength)
*strength = 65535 ;
else
*strength += val;
}
if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0 ) {
dprintk("could not get the lock\n" );
ret = -EINTR;
goto error;
}
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0 ) {
mutex_unlock(&state->platform.risc.mem_mbx_lock);
ret = -EIO;
goto error;
}
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2 );
mutex_unlock(&state->platform.risc.mem_mbx_lock);
val = 65535 - c[4 ];
if (val > 65535 - *strength)
*strength = 65535 ;
else
*strength += val;
error:
mutex_unlock(&state->demod_lock);
return ret;
}
static u32 dib9000_get_snr(struct dvb_frontend *fe)
{
struct dib9000_state *state = fe->demodulator_priv;
u16 *c = (u16 *)state->i2c_read_buffer;
u32 n, s, exp;
u16 val;
if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0 ) {
dprintk("could not get the lock\n" );
return 0 ;
}
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0 ) {
mutex_unlock(&state->platform.risc.mem_mbx_lock);
return 0 ;
}
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2 );
mutex_unlock(&state->platform.risc.mem_mbx_lock);
val = c[7 ];
n = (val >> 4 ) & 0 xff;
exp = ((val & 0 xf) << 2 );
val = c[8 ];
exp += ((val >> 14 ) & 0 x3);
if ((exp & 0 x20) != 0 )
exp -= 0 x40;
n <<= exp + 16 ;
s = (val >> 6 ) & 0 xFF;
exp = (val & 0 x3F);
if ((exp & 0 x20) != 0 )
exp -= 0 x40;
s <<= exp + 16 ;
if (n > 0 ) {
u32 t = (s / n) << 16 ;
return t + ((s << 16 ) - n * t) / n;
}
return 0 xffffffff;
}
static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr)
{
struct dib9000_state *state = fe->demodulator_priv;
u8 index_frontend;
u32 snr_master;
if (mutex_lock_interruptible(&state->demod_lock) < 0 ) {
dprintk("could not get the lock\n" );
return -EINTR;
}
snr_master = dib9000_get_snr(fe);
for (index_frontend = 1 ; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
snr_master += dib9000_get_snr(state->fe[index_frontend]);
if ((snr_master >> 16 ) != 0 ) {
snr_master = 10 * intlog10(snr_master >> 16 );
*snr = snr_master / ((1 << 24 ) / 10 );
} else
*snr = 0 ;
mutex_unlock(&state->demod_lock);
return 0 ;
}
static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc)
{
struct dib9000_state *state = fe->demodulator_priv;
u16 *c = (u16 *)state->i2c_read_buffer;
int ret = 0 ;
if (mutex_lock_interruptible(&state->demod_lock) < 0 ) {
dprintk("could not get the lock\n" );
return -EINTR;
}
if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0 ) {
dprintk("could not get the lock\n" );
ret = -EINTR;
goto error;
}
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0 ) {
mutex_unlock(&state->platform.risc.mem_mbx_lock);
ret = -EIO;
goto error;
}
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2 );
mutex_unlock(&state->platform.risc.mem_mbx_lock);
*unc = c[12 ];
error:
mutex_unlock(&state->demod_lock);
return ret;
}
int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, u8 first_addr)
{
int k = 0 , ret = 0 ;
u8 new_addr = 0 ;
struct i2c_device client = {.i2c_adap = i2c };
client.i2c_write_buffer = kzalloc(4 , GFP_KERNEL);
if (!client.i2c_write_buffer) {
dprintk("%s: not enough memory\n" , __func__);
return -ENOMEM;
}
client.i2c_read_buffer = kzalloc(4 , GFP_KERNEL);
if (!client.i2c_read_buffer) {
dprintk("%s: not enough memory\n" , __func__);
ret = -ENOMEM;
goto error_memory;
}
client.i2c_addr = default_addr + 16 ;
dib9000_i2c_write16(&client, 1796 , 0 x0);
for (k = no_of_demods - 1 ; k >= 0 ; k--) {
/* designated i2c address */
new_addr = first_addr + (k << 1 );
client.i2c_addr = default_addr;
dib9000_i2c_write16(&client, 1817 , 3 );
dib9000_i2c_write16(&client, 1796 , 0 );
dib9000_i2c_write16(&client, 1227 , 1 );
dib9000_i2c_write16(&client, 1227 , 0 );
client.i2c_addr = new_addr;
dib9000_i2c_write16(&client, 1817 , 3 );
dib9000_i2c_write16(&client, 1796 , 0 );
dib9000_i2c_write16(&client, 1227 , 1 );
dib9000_i2c_write16(&client, 1227 , 0 );
if (dib9000_identify(&client) == 0 ) {
client.i2c_addr = default_addr;
if (dib9000_identify(&client) == 0 ) {
dprintk("DiB9000 #%d: not identified\n" , k);
ret = -EIO;
goto error;
}
}
dib9000_i2c_write16(&client, 1795 , (1 << 10 ) | (4 << 6 ));
dib9000_i2c_write16(&client, 1794 , (new_addr << 2 ) | 2 );
dprintk("IC %d initialized (to i2c_address 0x%x)\n" , k, new_addr);
}
for (k = 0 ; k < no_of_demods; k++) {
new_addr = first_addr | (k << 1 );
client.i2c_addr = new_addr;
dib9000_i2c_write16(&client, 1794 , (new_addr << 2 ));
dib9000_i2c_write16(&client, 1795 , 0 );
}
error:
kfree(client.i2c_read_buffer);
error_memory:
kfree(client.i2c_write_buffer);
return ret;
}
EXPORT_SYMBOL(dib9000_i2c_enumeration);
int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave)
{
struct dib9000_state *state = fe->demodulator_priv;
u8 index_frontend = 1 ;
while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL))
index_frontend++;
if (index_frontend < MAX_NUMBER_OF_FRONTENDS) {
dprintk("set slave fe %p to index %i\n" , fe_slave, index_frontend);
state->fe[index_frontend] = fe_slave;
return 0 ;
}
dprintk("too many slave frontend\n" );
return -ENOMEM;
}
EXPORT_SYMBOL(dib9000_set_slave_frontend);
struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index)
{
struct dib9000_state *state = fe->demodulator_priv;
if (slave_index >= MAX_NUMBER_OF_FRONTENDS)
return NULL;
return state->fe[slave_index];
}
EXPORT_SYMBOL(dib9000_get_slave_frontend);
static const struct dvb_frontend_ops dib9000_ops;
struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg)
{
struct dvb_frontend *fe;
struct dib9000_state *st;
st = kzalloc(sizeof (struct dib9000_state), GFP_KERNEL);
if (st == NULL)
return NULL;
fe = kzalloc(sizeof (struct dvb_frontend), GFP_KERNEL);
if (fe == NULL) {
kfree(st);
return NULL;
}
memcpy(&st->chip.d9.cfg, cfg, sizeof (struct dib9000_config));
st->i2c.i2c_adap = i2c_adap;
st->i2c.i2c_addr = i2c_addr;
st->i2c.i2c_write_buffer = st->i2c_write_buffer;
st->i2c.i2c_read_buffer = st->i2c_read_buffer;
st->gpio_dir = DIB9000_GPIO_DEFAULT_DIRECTIONS;
st->gpio_val = DIB9000_GPIO_DEFAULT_VALUES;
st->gpio_pwm_pos = DIB9000_GPIO_DEFAULT_PWM_POS;
mutex_init(&st->platform.risc.mbx_if_lock);
mutex_init(&st->platform.risc.mbx_lock);
mutex_init(&st->platform.risc.mem_lock);
mutex_init(&st->platform.risc.mem_mbx_lock);
mutex_init(&st->demod_lock);
st->get_frontend_internal = 0 ;
st->pid_ctrl_index = -2 ;
st->fe[0 ] = fe;
fe->demodulator_priv = st;
memcpy(&st->fe[0 ]->ops, &dib9000_ops, sizeof (struct dvb_frontend_ops));
/* Ensure the output mode remains at the previous default if it's
* not specifically set by the caller.
*/
if ((st->chip.d9.cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (st->chip.d9.cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK))
st->chip.d9.cfg.output_mode = OUTMODE_MPEG2_FIFO;
if (dib9000_identify(&st->i2c) == 0 )
goto error;
dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c.i2c_adap, st->i2c.i2c_addr);
st->tuner_adap.dev.parent = i2c_adap->dev.parent;
strscpy(st->tuner_adap.name, "DIB9000_FW TUNER ACCESS" ,
sizeof (st->tuner_adap.name));
st->tuner_adap.algo = &dib9000_tuner_algo;
st->tuner_adap.algo_data = NULL;
i2c_set_adapdata(&st->tuner_adap, st);
if (i2c_add_adapter(&st->tuner_adap) < 0 )
goto error;
st->component_bus.dev.parent = i2c_adap->dev.parent;
strscpy(st->component_bus.name, "DIB9000_FW COMPONENT BUS ACCESS" ,
sizeof (st->component_bus.name));
st->component_bus.algo = &dib9000_component_bus_algo;
st->component_bus.algo_data = NULL;
st->component_bus_speed = 340 ;
i2c_set_adapdata(&st->component_bus, st);
if (i2c_add_adapter(&st->component_bus) < 0 )
goto component_bus_add_error;
dib9000_fw_reset(fe);
return fe;
component_bus_add_error:
i2c_del_adapter(&st->tuner_adap);
error:
kfree(st);
return NULL;
}
EXPORT_SYMBOL_GPL(dib9000_attach);
static const struct dvb_frontend_ops dib9000_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = "DiBcom 9000" ,
.frequency_min_hz = 44250 * kHz,
.frequency_max_hz = 867250 * kHz,
.frequency_stepsize_hz = 62500 ,
.caps = FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | FE_CAN_HIERARCHY_AUTO,
},
.release = dib9000_release,
.init = dib9000_wakeup,
.sleep = dib9000_sleep,
.set_frontend = dib9000_set_frontend,
.get_tune_settings = dib9000_fe_get_tune_settings,
.get_frontend = dib9000_get_frontend,
.read_status = dib9000_read_status,
.read_ber = dib9000_read_ber,
.read_signal_strength = dib9000_read_signal_strength,
.read_snr = dib9000_read_snr,
.read_ucblocks = dib9000_read_unc_blocks,
};
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>" );
MODULE_AUTHOR("Olivier Grenie <olivier.grenie@parrot.com>" );
MODULE_DESCRIPTION("Driver for the DiBcom 9000 COFDM demodulator" );
MODULE_LICENSE("GPL" );
Messung V0.5 in Prozent C=91 H=89 G=89
¤ Dauer der Verarbeitung: 0.29 Sekunden
(vorverarbeitet am 2026-06-07)
¤
*© Formatika GbR, Deutschland