diff options
Diffstat (limited to 'drivers/media/dvb-frontends/drx39xyj/drxj.c')
-rw-r--r-- | drivers/media/dvb-frontends/drx39xyj/drxj.c | 548 |
1 files changed, 548 insertions, 0 deletions
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 1e202dafe335..94c3d6f0d5b8 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -1540,6 +1540,554 @@ bool is_handled_by_aud_tr_if(u32 addr) /*============================================================================*/ +/* Functions not supported by protocol*/ + +static int drxdap_fasi_write_reg8(struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 addr, /* address of register */ + u8 data, /* data to write */ + u32 flags) +{ /* special device flags */ + return -EIO; +} + +static int drxdap_fasi_read_reg8(struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 addr, /* address of register */ + u8 *data, /* buffer to receive data */ + u32 flags) +{ /* special device flags */ + return -EIO; +} + +static int drxdap_fasi_read_modify_write_reg8(struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 waddr, /* address of register */ + u32 raddr, /* address to read back from */ + u8 datain, /* data to send */ + u8 *dataout) +{ /* data to receive back */ + return -EIO; +} + +static int drxdap_fasi_read_modify_write_reg32(struct i2c_device_addr *dev_addr, /* address of I2C device */ + u32 waddr, /* address of register */ + u32 raddr, /* address to read back from */ + u32 datain, /* data to send */ + u32 *dataout) +{ /* data to receive back */ + return -EIO; +} + + +int drxbsp_i2c_write_read(struct i2c_device_addr *w_dev_addr, + u16 w_count, + u8 *wData, + struct i2c_device_addr *r_dev_addr, + u16 r_count, u8 *r_data) +{ + struct drx39xxj_state *state; + struct i2c_msg msg[2]; + unsigned int num_msgs; + + if (w_dev_addr == NULL) { + /* Read only */ + state = r_dev_addr->user_data; + msg[0].addr = r_dev_addr->i2c_addr >> 1; + msg[0].flags = I2C_M_RD; + msg[0].buf = r_data; + msg[0].len = r_count; + num_msgs = 1; + } else if (r_dev_addr == NULL) { + /* Write only */ + state = w_dev_addr->user_data; + msg[0].addr = w_dev_addr->i2c_addr >> 1; + msg[0].flags = 0; + msg[0].buf = wData; + msg[0].len = w_count; + num_msgs = 1; + } else { + /* Both write and read */ + state = w_dev_addr->user_data; + msg[0].addr = w_dev_addr->i2c_addr >> 1; + msg[0].flags = 0; + msg[0].buf = wData; + msg[0].len = w_count; + msg[1].addr = r_dev_addr->i2c_addr >> 1; + msg[1].flags = I2C_M_RD; + msg[1].buf = r_data; + msg[1].len = r_count; + num_msgs = 2; + } + + if (state->i2c == NULL) { + pr_err("i2c was zero, aborting\n"); + return 0; + } + if (i2c_transfer(state->i2c, msg, num_msgs) != num_msgs) { + pr_warn("drx3933: I2C write/read failed\n"); + return -EREMOTEIO; + } + + return 0; + +#ifdef DJH_DEBUG + struct drx39xxj_state *state = w_dev_addr->user_data; + + struct i2c_msg msg[2] = { + {.addr = w_dev_addr->i2c_addr, + .flags = 0, .buf = wData, .len = w_count}, + {.addr = r_dev_addr->i2c_addr, + .flags = I2C_M_RD, .buf = r_data, .len = r_count}, + }; + + pr_dbg("drx3933 i2c operation addr=%x i2c=%p, wc=%x rc=%x\n", + w_dev_addr->i2c_addr, state->i2c, w_count, r_count); + + if (i2c_transfer(state->i2c, msg, 2) != 2) { + pr_warn("drx3933: I2C write/read failed\n"); + return -EREMOTEIO; + } +#endif + return 0; +} + +/*============================================================================*/ + +/****************************** +* +* int drxdap_fasi_read_block ( +* struct i2c_device_addr *dev_addr, -- address of I2C device +* u32 addr, -- address of chip register/memory +* u16 datasize, -- number of bytes to read +* u8 *data, -- data to receive +* u32 flags) -- special device flags +* +* Read block data from chip address. Because the chip is word oriented, +* the number of bytes to read must be even. +* +* Make sure that the buffer to receive the data is large enough. +* +* Although this function expects an even number of bytes, it is still byte +* oriented, and the data read back is NOT translated to the endianness of +* the target platform. +* +* Output: +* - 0 if reading was successful +* in that case: data read is in *data. +* - -EIO if anything went wrong +* +******************************/ + +static int drxdap_fasi_read_block(struct i2c_device_addr *dev_addr, + u32 addr, + u16 datasize, + u8 *data, u32 flags) +{ + u8 buf[4]; + u16 bufx; + int rc; + u16 overhead_size = 0; + + /* Check parameters ******************************************************* */ + if (dev_addr == NULL) + return -EINVAL; + + overhead_size = (IS_I2C_10BIT(dev_addr->i2c_addr) ? 2 : 1) + + (DRXDAP_FASI_LONG_FORMAT(addr) ? 4 : 2); + + if ((DRXDAP_FASI_OFFSET_TOO_LARGE(addr)) || + ((!(DRXDAPFASI_LONG_ADDR_ALLOWED)) && + DRXDAP_FASI_LONG_FORMAT(addr)) || + (overhead_size > (DRXDAP_MAX_WCHUNKSIZE)) || + ((datasize != 0) && (data == NULL)) || ((datasize & 1) == 1)) { + return -EINVAL; + } + + /* ReadModifyWrite & mode flag bits are not allowed */ + flags &= (~DRXDAP_FASI_RMW & ~DRXDAP_FASI_MODEFLAGS); +#if DRXDAP_SINGLE_MASTER + flags |= DRXDAP_FASI_SINGLE_MASTER; +#endif + + /* Read block from I2C **************************************************** */ + do { + u16 todo = (datasize < DRXDAP_MAX_RCHUNKSIZE ? + datasize : DRXDAP_MAX_RCHUNKSIZE); + + bufx = 0; + + addr &= ~DRXDAP_FASI_FLAGS; + addr |= flags; + +#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 1) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1)) + /* short format address preferred but long format otherwise */ + if (DRXDAP_FASI_LONG_FORMAT(addr)) { +#endif +#if (DRXDAPFASI_LONG_ADDR_ALLOWED == 1) + buf[bufx++] = (u8) (((addr << 1) & 0xFF) | 0x01); + buf[bufx++] = (u8) ((addr >> 16) & 0xFF); + buf[bufx++] = (u8) ((addr >> 24) & 0xFF); + buf[bufx++] = (u8) ((addr >> 7) & 0xFF); +#endif +#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 1) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1)) + } else { +#endif +#if (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1) + buf[bufx++] = (u8) ((addr << 1) & 0xFF); + buf[bufx++] = + (u8) (((addr >> 16) & 0x0F) | + ((addr >> 18) & 0xF0)); +#endif +#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 1) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1)) + } +#endif + +#if DRXDAP_SINGLE_MASTER + /* + * In single master mode, split the read and write actions. + * No special action is needed for write chunks here. + */ + rc = drxbsp_i2c_write_read(dev_addr, bufx, buf, 0, 0, 0); + if (rc == 0) + rc = drxbsp_i2c_write_read(0, 0, 0, dev_addr, todo, data); +#else + /* In multi master mode, do everything in one RW action */ + rc = drxbsp_i2c_write_read(dev_addr, bufx, buf, dev_addr, todo, + data); +#endif + data += todo; + addr += (todo >> 1); + datasize -= todo; + } while (datasize && rc == 0); + + return rc; +} + + +/****************************** +* +* int drxdap_fasi_read_reg16 ( +* struct i2c_device_addr *dev_addr, -- address of I2C device +* u32 addr, -- address of chip register/memory +* u16 *data, -- data to receive +* u32 flags) -- special device flags +* +* Read one 16-bit register or memory location. The data received back is +* converted back to the target platform's endianness. +* +* Output: +* - 0 if reading was successful +* in that case: read data is at *data +* - -EIO if anything went wrong +* +******************************/ + +static int drxdap_fasi_read_reg16(struct i2c_device_addr *dev_addr, + u32 addr, + u16 *data, u32 flags) +{ + u8 buf[sizeof(*data)]; + int rc; + + if (!data) + return -EINVAL; + + rc = drxdap_fasi_read_block(dev_addr, addr, sizeof(*data), buf, flags); + *data = buf[0] + (((u16) buf[1]) << 8); + return rc; +} + +/****************************** +* +* int drxdap_fasi_read_reg32 ( +* struct i2c_device_addr *dev_addr, -- address of I2C device +* u32 addr, -- address of chip register/memory +* u32 *data, -- data to receive +* u32 flags) -- special device flags +* +* Read one 32-bit register or memory location. The data received back is +* converted back to the target platform's endianness. +* +* Output: +* - 0 if reading was successful +* in that case: read data is at *data +* - -EIO if anything went wrong +* +******************************/ + +static int drxdap_fasi_read_reg32(struct i2c_device_addr *dev_addr, + u32 addr, + u32 *data, u32 flags) +{ + u8 buf[sizeof(*data)]; + int rc; + + if (!data) + return -EINVAL; + + rc = drxdap_fasi_read_block(dev_addr, addr, sizeof(*data), buf, flags); + *data = (((u32) buf[0]) << 0) + + (((u32) buf[1]) << 8) + + (((u32) buf[2]) << 16) + (((u32) buf[3]) << 24); + return rc; +} + +/****************************** +* +* int drxdap_fasi_write_block ( +* struct i2c_device_addr *dev_addr, -- address of I2C device +* u32 addr, -- address of chip register/memory +* u16 datasize, -- number of bytes to read +* u8 *data, -- data to receive +* u32 flags) -- special device flags +* +* Write block data to chip address. Because the chip is word oriented, +* the number of bytes to write must be even. +* +* Although this function expects an even number of bytes, it is still byte +* oriented, and the data being written is NOT translated from the endianness of +* the target platform. +* +* Output: +* - 0 if writing was successful +* - -EIO if anything went wrong +* +******************************/ + +static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr, + u32 addr, + u16 datasize, + u8 *data, u32 flags) +{ + u8 buf[DRXDAP_MAX_WCHUNKSIZE]; + int st = -EIO; + int first_err = 0; + u16 overhead_size = 0; + u16 block_size = 0; + + /* Check parameters ******************************************************* */ + if (dev_addr == NULL) + return -EINVAL; + + overhead_size = (IS_I2C_10BIT(dev_addr->i2c_addr) ? 2 : 1) + + (DRXDAP_FASI_LONG_FORMAT(addr) ? 4 : 2); + + if ((DRXDAP_FASI_OFFSET_TOO_LARGE(addr)) || + ((!(DRXDAPFASI_LONG_ADDR_ALLOWED)) && + DRXDAP_FASI_LONG_FORMAT(addr)) || + (overhead_size > (DRXDAP_MAX_WCHUNKSIZE)) || + ((datasize != 0) && (data == NULL)) || ((datasize & 1) == 1)) + return -EINVAL; + + flags &= DRXDAP_FASI_FLAGS; + flags &= ~DRXDAP_FASI_MODEFLAGS; +#if DRXDAP_SINGLE_MASTER + flags |= DRXDAP_FASI_SINGLE_MASTER; +#endif + + /* Write block to I2C ***************************************************** */ + block_size = ((DRXDAP_MAX_WCHUNKSIZE) - overhead_size) & ~1; + do { + u16 todo = 0; + u16 bufx = 0; + + /* Buffer device address */ + addr &= ~DRXDAP_FASI_FLAGS; + addr |= flags; +#if (((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1) && ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1)) + /* short format address preferred but long format otherwise */ + if (DRXDAP_FASI_LONG_FORMAT(addr)) { +#endif +#if ((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1) + buf[bufx++] = (u8) (((addr << 1) & 0xFF) | 0x01); + buf[bufx++] = (u8) ((addr >> 16) & 0xFF); + buf[bufx++] = (u8) ((addr >> 24) & 0xFF); + buf[bufx++] = (u8) ((addr >> 7) & 0xFF); +#endif +#if (((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1) && ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1)) + } else { +#endif +#if ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1) + buf[bufx++] = (u8) ((addr << 1) & 0xFF); + buf[bufx++] = + (u8) (((addr >> 16) & 0x0F) | + ((addr >> 18) & 0xF0)); +#endif +#if (((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1) && ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1)) + } +#endif + + /* + In single master mode block_size can be 0. In such a case this I2C + sequense will be visible: (1) write address {i2c addr, + 4 bytes chip address} (2) write data {i2c addr, 4 bytes data } + (3) write address (4) write data etc... + Addres must be rewriten because HI is reset after data transport and + expects an address. + */ + todo = (block_size < datasize ? block_size : datasize); + if (todo == 0) { + u16 overhead_size_i2c_addr = 0; + u16 data_block_size = 0; + + overhead_size_i2c_addr = + (IS_I2C_10BIT(dev_addr->i2c_addr) ? 2 : 1); + data_block_size = + (DRXDAP_MAX_WCHUNKSIZE - overhead_size_i2c_addr) & ~1; + + /* write device address */ + st = drxbsp_i2c_write_read(dev_addr, + (u16) (bufx), + buf, + (struct i2c_device_addr *)(NULL), + 0, (u8 *)(NULL)); + + if ((st != 0) && (first_err == 0)) { + /* at the end, return the first error encountered */ + first_err = st; + } + bufx = 0; + todo = + (data_block_size < + datasize ? data_block_size : datasize); + } + memcpy(&buf[bufx], data, todo); + /* write (address if can do and) data */ + st = drxbsp_i2c_write_read(dev_addr, + (u16) (bufx + todo), + buf, + (struct i2c_device_addr *)(NULL), + 0, (u8 *)(NULL)); + + if ((st != 0) && (first_err == 0)) { + /* at the end, return the first error encountered */ + first_err = st; + } + datasize -= todo; + data += todo; + addr += (todo >> 1); + } while (datasize); + + return first_err; +} + +/****************************** +* +* int drxdap_fasi_write_reg16 ( +* struct i2c_device_addr *dev_addr, -- address of I2C device +* u32 addr, -- address of chip register/memory +* u16 data, -- data to send +* u32 flags) -- special device flags +* +* Write one 16-bit register or memory location. The data being written is +* converted from the target platform's endianness to little endian. +* +* Output: +* - 0 if writing was successful +* - -EIO if anything went wrong +* +******************************/ + +static int drxdap_fasi_write_reg16(struct i2c_device_addr *dev_addr, + u32 addr, + u16 data, u32 flags) +{ + u8 buf[sizeof(data)]; + + buf[0] = (u8) ((data >> 0) & 0xFF); + buf[1] = (u8) ((data >> 8) & 0xFF); + + return drxdap_fasi_write_block(dev_addr, addr, sizeof(data), buf, flags); +} + +/****************************** +* +* int drxdap_fasi_read_modify_write_reg16 ( +* struct i2c_device_addr *dev_addr, -- address of I2C device +* u32 waddr, -- address of chip register/memory +* u32 raddr, -- chip address to read back from +* u16 wdata, -- data to send +* u16 *rdata) -- data to receive back +* +* Write 16-bit data, then read back the original contents of that location. +* Requires long addressing format to be allowed. +* +* Before sending data, the data is converted to little endian. The +* data received back is converted back to the target platform's endianness. +* +* WARNING: This function is only guaranteed to work if there is one +* master on the I2C bus. +* +* Output: +* - 0 if reading was successful +* in that case: read back data is at *rdata +* - -EIO if anything went wrong +* +******************************/ + +static int drxdap_fasi_read_modify_write_reg16(struct i2c_device_addr *dev_addr, + u32 waddr, + u32 raddr, + u16 wdata, u16 *rdata) +{ + int rc = -EIO; + +#if (DRXDAPFASI_LONG_ADDR_ALLOWED == 1) + if (rdata == NULL) + return -EINVAL; + + rc = drxdap_fasi_write_reg16(dev_addr, waddr, wdata, DRXDAP_FASI_RMW); + if (rc == 0) + rc = drxdap_fasi_read_reg16(dev_addr, raddr, rdata, 0); +#endif + + return rc; +} + +/****************************** +* +* int drxdap_fasi_write_reg32 ( +* struct i2c_device_addr *dev_addr, -- address of I2C device +* u32 addr, -- address of chip register/memory +* u32 data, -- data to send +* u32 flags) -- special device flags +* +* Write one 32-bit register or memory location. The data being written is +* converted from the target platform's endianness to little endian. +* +* Output: +* - 0 if writing was successful +* - -EIO if anything went wrong +* +******************************/ + +static int drxdap_fasi_write_reg32(struct i2c_device_addr *dev_addr, + u32 addr, + u32 data, u32 flags) +{ + u8 buf[sizeof(data)]; + + buf[0] = (u8) ((data >> 0) & 0xFF); + buf[1] = (u8) ((data >> 8) & 0xFF); + buf[2] = (u8) ((data >> 16) & 0xFF); + buf[3] = (u8) ((data >> 24) & 0xFF); + + return drxdap_fasi_write_block(dev_addr, addr, sizeof(data), buf, flags); +} + +/* The structure containing the protocol interface */ +struct drx_access_func drx_dap_fasi_funct_g = { + drxdap_fasi_write_block, /* Supported */ + drxdap_fasi_read_block, /* Supported */ + drxdap_fasi_write_reg8, /* Not supported */ + drxdap_fasi_read_reg8, /* Not supported */ + drxdap_fasi_read_modify_write_reg8, /* Not supported */ + drxdap_fasi_write_reg16, /* Supported */ + drxdap_fasi_read_reg16, /* Supported */ + drxdap_fasi_read_modify_write_reg16, /* Supported */ + drxdap_fasi_write_reg32, /* Supported */ + drxdap_fasi_read_reg32, /* Supported */ + drxdap_fasi_read_modify_write_reg32 /* Not supported */ +}; + static int drxj_dap_read_block(struct i2c_device_addr *dev_addr, u32 addr, u16 datasize, |