blob: 713ca06197822df7e28df9579da1e00dd29ca142 [file] [log] [blame]
/* Copyright (c) 2013-2016, 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <soc/qcom/camera2.h>
#include "msm_camera_spi.h"
#undef SPIDBG
#ifdef CONFIG_MSMB_CAMERA_DEBUG
#define SPIDBG(fmt, args...) pr_debug(fmt, ##args)
#define S_I2C_DBG(fmt, args...) pr_debug(fmt, ##args)
#else
#define SPIDBG(fmt, args...) do { } while (0)
#define S_I2C_DBG(fmt, args...) do { } while (0)
#endif
static int msm_camera_spi_txfr(struct spi_device *spi, char *txbuf,
char *rxbuf, int num_byte)
{
struct spi_transfer t;
struct spi_message m;
memset(&t, 0, sizeof(t));
t.tx_buf = txbuf;
t.rx_buf = rxbuf;
t.len = num_byte;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spi_sync(spi, &m);
}
static int msm_camera_spi_txfr_read(struct spi_device *spi, char *txbuf,
char *rxbuf, int txlen, int rxlen)
{
struct spi_transfer tx;
struct spi_transfer rx;
struct spi_message m;
memset(&tx, 0, sizeof(tx));
memset(&rx, 0, sizeof(rx));
tx.tx_buf = txbuf;
rx.rx_buf = rxbuf;
tx.len = txlen;
rx.len = rxlen;
spi_message_init(&m);
spi_message_add_tail(&tx, &m);
spi_message_add_tail(&rx, &m);
return spi_sync(spi, &m);
}
/*
* msm_camera_set_addr() - helper function to set transfer address
* @addr: device address
* @addr_len: the addr field length of an instruction
* @type: type (i.e. byte-length) of @addr
* @str: shifted address output, must be zeroed when passed in
*
* This helper function sets @str based on the addr field length of an
* instruction and the data length.
*/
static void msm_camera_set_addr(uint32_t addr, uint8_t addr_len,
enum msm_camera_i2c_reg_addr_type type,
char *str)
{
int i, len;
if (!addr_len)
return;
if (addr_len < type)
SPIDBG("%s: omitting higher bits in address\n", __func__);
/* only support transfer MSB first for now */
len = addr_len - type;
for (i = len; i < addr_len; i++) {
if (i >= 0)
str[i] = (addr >> (BITS_PER_BYTE * (addr_len - i - 1)))
& 0xFF;
}
}
/*
* msm_camera_spi_tx_helper() - wrapper for SPI transaction
* @client: io client
* @inst: inst of this transaction
* @addr: device addr following the inst
* @data: output byte array (could be NULL)
* @num_byte: size of @data
* @tx, rx: optional transfer buffer. It must be at least header
* + @num_byte long.
*
* This is the core function for SPI transaction, except for writes. It first
* checks address type, then allocates required memory for tx/rx buffers.
* It sends out <opcode><addr>, and optionally receives @num_byte of response,
* if @data is not NULL. This function does not check for wait conditions,
* and will return immediately once bus transaction finishes.
*
* This function will allocate buffers of header + @num_byte long. For
* large transfers, the allocation could fail. External buffer @tx, @rx
* should be passed in to bypass allocation. The size of buffer should be
* at least header + num_byte long. Since buffer is managed externally,
* @data will be ignored, and read results will be in @rx.
* @tx, @rx also can be used for repeated transfers to improve performance.
*/
static int32_t msm_camera_spi_tx_helper(struct msm_camera_i2c_client *client,
struct msm_camera_spi_inst *inst, uint32_t addr, uint8_t *data,
uint32_t num_byte, char *tx, char *rx)
{
int32_t rc = -EINVAL;
struct spi_device *spi = client->spi_client->spi_master;
char *ctx = NULL, *crx = NULL;
uint32_t len, hlen;
uint8_t retries = client->spi_client->retries;
if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR)
&& (client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
&& (client->addr_type != MSM_CAMERA_I2C_3B_ADDR))
return rc;
hlen = msm_camera_spi_get_hlen(inst);
len = hlen + num_byte;
if (tx)
ctx = tx;
else
ctx = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!ctx)
return -ENOMEM;
if (num_byte) {
if (rx)
crx = rx;
else
crx = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!crx) {
if (!tx)
kfree(ctx);
return -ENOMEM;
}
} else {
crx = NULL;
}
ctx[0] = inst->opcode;
msm_camera_set_addr(addr, inst->addr_len, client->addr_type, ctx + 1);
while ((rc = msm_camera_spi_txfr(spi, ctx, crx, len)) && retries) {
retries--;
msleep(client->spi_client->retry_delay);
}
if (rc < 0) {
SPIDBG("%s: failed %d\n", __func__, rc);
goto out;
}
if (data && num_byte && !rx)
memcpy(data, crx + hlen, num_byte);
out:
if (!tx)
kfree(ctx);
if (!rx)
kfree(crx);
return rc;
}
static int32_t msm_camera_spi_tx_read(struct msm_camera_i2c_client *client,
struct msm_camera_spi_inst *inst, uint32_t addr, uint8_t *data,
uint32_t num_byte, char *tx, char *rx)
{
int32_t rc = -EINVAL;
struct spi_device *spi = client->spi_client->spi_master;
char *ctx = NULL, *crx = NULL;
uint32_t hlen;
uint8_t retries = client->spi_client->retries;
if ((client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
&& (client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR)
&& (client->addr_type != MSM_CAMERA_I2C_3B_ADDR))
return rc;
hlen = msm_camera_spi_get_hlen(inst);
if (tx)
ctx = tx;
else
ctx = kzalloc(hlen, GFP_KERNEL | GFP_DMA);
if (!ctx)
return -ENOMEM;
if (num_byte) {
if (rx)
crx = rx;
else
crx = kzalloc(num_byte, GFP_KERNEL | GFP_DMA);
if (!crx) {
if (!tx)
kfree(ctx);
return -ENOMEM;
}
} else {
crx = NULL;
}
ctx[0] = inst->opcode;
if (client->addr_type == MSM_CAMERA_I2C_3B_ADDR) {
msm_camera_set_addr(addr, inst->addr_len, client->addr_type,
ctx + 1);
} else {
ctx[1] = (addr >> BITS_PER_BYTE) & 0xFF;
ctx[2] = (addr & 0xFF);
ctx[3] = 0;
}
SPIDBG("%s: tx(%u): %02x %02x %02x %02x\n", __func__,
hlen, ctx[0], ctx[1], ctx[2], ctx[3]);
while ((rc = msm_camera_spi_txfr_read(spi, ctx, crx, hlen, num_byte))
&& retries) {
retries--;
msleep(client->spi_client->retry_delay);
}
if (rc < 0) {
pr_err("%s: failed %d\n", __func__, rc);
goto out;
}
if (data && num_byte && !rx)
memcpy(data, crx, num_byte);
out:
if (!tx)
kfree(ctx);
if (!rx)
kfree(crx);
return rc;
}
int32_t msm_camera_spi_read(struct msm_camera_i2c_client *client,
uint32_t addr, uint16_t *data,
enum msm_camera_i2c_data_type data_type)
{
int32_t rc = -EINVAL;
uint8_t temp[2];
if ((data_type != MSM_CAMERA_I2C_BYTE_DATA)
&& (data_type != MSM_CAMERA_I2C_WORD_DATA))
return rc;
rc = msm_camera_spi_tx_read(client,
&client->spi_client->cmd_tbl.read, addr, &temp[0],
data_type, NULL, NULL);
if (rc < 0) {
pr_err("%s: failed %d\n", __func__, rc);
return rc;
}
if (data_type == MSM_CAMERA_I2C_BYTE_DATA)
*data = temp[0];
else
*data = (temp[0] << BITS_PER_BYTE) | temp[1];
SPIDBG("%s: addr 0x%x, data %u\n", __func__, addr, *data);
return rc;
}
int32_t msm_camera_spi_read_seq(struct msm_camera_i2c_client *client,
uint32_t addr, uint8_t *data, uint32_t num_byte)
{
return msm_camera_spi_tx_helper(client,
&client->spi_client->cmd_tbl.read_seq, addr, data, num_byte,
NULL, NULL);
}
/*
* msm_camera_spi_read_seq_l()- function for large SPI reads
* @client: io client
* @addr: device address to read
* @num_byte: read length
* @tx,rx: pre-allocated SPI buffer. Its size must be at least
* header + num_byte
*
* This function is used for large transactions. Instead of allocating SPI
* buffer each time, caller is responsible for pre-allocating memory buffers.
* Memory buffer must be at least header + num_byte. Header length can be
* obtained by msm_camera_spi_get_hlen().
*/
int32_t msm_camera_spi_read_seq_l(struct msm_camera_i2c_client *client,
uint32_t addr, uint32_t num_byte, char *tx, char *rx)
{
return msm_camera_spi_tx_helper(client,
&client->spi_client->cmd_tbl.read_seq, addr, NULL, num_byte,
tx, rx);
}
int32_t msm_camera_spi_query_id(struct msm_camera_i2c_client *client,
uint32_t addr, uint8_t *data, uint32_t num_byte)
{
return msm_camera_spi_tx_helper(client,
&client->spi_client->cmd_tbl.query_id, addr, data, num_byte,
NULL, NULL);
}
static int32_t msm_camera_spi_read_status_reg(
struct msm_camera_i2c_client *client, uint8_t *status)
{
struct msm_camera_spi_inst *rs =
&client->spi_client->cmd_tbl.read_status;
if (rs->addr_len != 0) {
pr_err("%s: not implemented yet\n", __func__);
return -EINVAL;
}
return msm_camera_spi_tx_helper(client, rs, 0, status, 1, NULL, NULL);
}
static int32_t msm_camera_spi_device_busy(struct msm_camera_i2c_client *client,
uint8_t *busy)
{
int rc;
uint8_t st = 0;
rc = msm_camera_spi_read_status_reg(client, &st);
if (rc < 0) {
pr_err("%s: failed to read status reg\n", __func__);
return rc;
}
*busy = st & client->spi_client->busy_mask;
return 0;
}
static int32_t msm_camera_spi_wait(struct msm_camera_i2c_client *client,
struct msm_camera_spi_inst *inst)
{
uint8_t busy;
int i, rc;
SPIDBG("%s: op 0x%x wait start\n", __func__, inst->opcode);
for (i = 0; i < inst->delay_count; i++) {
rc = msm_camera_spi_device_busy(client, &busy);
if (rc < 0)
return rc;
if (!busy)
break;
msleep(inst->delay_intv);
SPIDBG("%s: op 0x%x wait\n", __func__, inst->opcode);
}
if (i > inst->delay_count) {
pr_err("%s: op %x timed out\n", __func__, inst->opcode);
return -ETIMEDOUT;
}
SPIDBG("%s: op %x finished\n", __func__, inst->opcode);
return 0;
}
static int32_t msm_camera_spi_write_enable(
struct msm_camera_i2c_client *client)
{
struct msm_camera_spi_inst *we =
&client->spi_client->cmd_tbl.write_enable;
int rc;
if (we->opcode == 0)
return 0;
if (we->addr_len != 0) {
pr_err("%s: not implemented yet\n", __func__);
return -EINVAL;
}
rc = msm_camera_spi_tx_helper(client, we, 0, NULL, 0, NULL, NULL);
if (rc < 0)
pr_err("%s: write enable failed\n", __func__);
return rc;
}
int32_t msm_camera_spi_erase(struct msm_camera_i2c_client *client,
uint32_t addr, uint32_t size)
{
struct msm_camera_spi_inst *se = &client->spi_client->cmd_tbl.erase;
int rc = 0;
uint32_t cur;
uint32_t end = addr + size;
uint32_t erase_size = client->spi_client->erase_size;
end = addr + size;
for (cur = rounddown(addr, erase_size); cur < end; cur += erase_size) {
SPIDBG("%s: erasing 0x%x\n", __func__, cur);
rc = msm_camera_spi_write_enable(client);
if (rc < 0)
return rc;
rc = msm_camera_spi_tx_helper(client, se, cur, NULL, 0,
NULL, NULL);
if (rc < 0) {
pr_err("%s: erase failed\n", __func__);
return rc;
}
rc = msm_camera_spi_wait(client, se);
if (rc < 0) {
pr_err("%s: erase timedout\n", __func__);
return rc;
}
}
return rc;
}
/*
* msm_camera_spi_page_program() - core function to perform write
* @client: need for obtaining SPI device
* @addr: address to program on device
* @data: data to write
* @len: size of data
* @tx: tx buffer, size >= header + len
*
* This function performs SPI write, and has no boundary check. Writing range
* should not cross page boundary, or data will be corrupted. Transaction is
* guaranteed to be finished when it returns. This function should never be
* used outside msm_camera_spi_write_seq().
*/
static int32_t msm_camera_spi_page_program(struct msm_camera_i2c_client *client,
uint32_t addr, uint8_t *data, uint16_t len, uint8_t *tx)
{
int rc;
struct msm_camera_spi_inst *pg =
&client->spi_client->cmd_tbl.page_program;
struct spi_device *spi = client->spi_client->spi_master;
uint8_t retries = client->spi_client->retries;
uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len;
SPIDBG("%s: addr 0x%x, size 0x%x\n", __func__, addr, len);
rc = msm_camera_spi_write_enable(client);
if (rc < 0)
return rc;
memset(tx, 0, header_len);
tx[0] = pg->opcode;
msm_camera_set_addr(addr, pg->addr_len, client->addr_type, tx + 1);
memcpy(tx + header_len, data, len);
SPIDBG("%s: tx(%u): %02x %02x %02x %02x\n", __func__,
len, tx[0], tx[1], tx[2], tx[3]);
while ((rc = spi_write(spi, tx, len + header_len)) && retries) {
rc = msm_camera_spi_wait(client, pg);
msleep(client->spi_client->retry_delay);
retries--;
}
if (rc < 0) {
pr_err("%s: failed %d\n", __func__, rc);
return rc;
}
rc = msm_camera_spi_wait(client, pg);
return rc;
}
int32_t msm_camera_spi_write_seq(struct msm_camera_i2c_client *client,
uint32_t addr, uint8_t *data, uint32_t num_byte)
{
struct msm_camera_spi_inst *pg =
&client->spi_client->cmd_tbl.page_program;
const uint32_t page_size = client->spi_client->page_size;
uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len;
uint16_t len;
uint32_t cur_len, end;
char *tx, *pdata = data;
int rc = -EINVAL;
if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR)
&& (client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
&& (client->addr_type != MSM_CAMERA_I2C_3B_ADDR))
return rc;
/* single page write */
if ((addr % page_size) + num_byte <= page_size) {
len = header_len + num_byte;
tx = kmalloc(len, GFP_KERNEL | GFP_DMA);
if (!tx)
goto NOMEM;
rc = msm_camera_spi_page_program(client, addr, data,
num_byte, tx);
if (rc < 0)
goto ERROR;
goto OUT;
}
/* multi page write */
len = header_len + page_size;
tx = kmalloc(len, GFP_KERNEL | GFP_DMA);
if (!tx)
goto NOMEM;
while (num_byte) {
end = min(page_size, (addr % page_size) + num_byte);
cur_len = end - (addr % page_size);
rc = msm_camera_spi_page_program(client, addr, pdata,
cur_len, tx);
if (rc < 0)
goto ERROR;
addr += cur_len;
pdata += cur_len;
num_byte -= cur_len;
}
goto OUT;
NOMEM:
pr_err("%s: memory allocation failed\n", __func__);
return -ENOMEM;
ERROR:
pr_err("%s: error write\n", __func__);
OUT:
kfree(tx);
return rc;
}
int32_t msm_camera_spi_write(struct msm_camera_i2c_client *client,
uint32_t addr, uint16_t data, enum msm_camera_i2c_data_type data_type)
{
struct msm_camera_spi_inst *pg =
&client->spi_client->cmd_tbl.page_program;
uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len;
uint16_t len = 0;
char *buf = NULL;
char *tx;
int rc = -EINVAL;
if (((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR)
&& (client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
&& (client->addr_type != MSM_CAMERA_I2C_3B_ADDR))
|| (data_type != MSM_CAMERA_I2C_BYTE_DATA
&& data_type != MSM_CAMERA_I2C_WORD_DATA))
return rc;
buf = kzalloc(data_type, GFP_KERNEL);
if (!buf)
goto NOMEM;
S_I2C_DBG("Data: 0x%x\n", data);
len = header_len + (uint8_t)data_type;
tx = kmalloc(len, GFP_KERNEL | GFP_DMA);
if (!tx)
goto NOMEM;
if (data_type == MSM_CAMERA_I2C_BYTE_DATA) {
buf[0] = data;
SPIDBG("Byte %d: 0x%x\n", len, buf[0]);
} else if (data_type == MSM_CAMERA_I2C_WORD_DATA) {
buf[0] = (data >> BITS_PER_BYTE) & 0x00FF;
buf[1] = (data & 0x00FF);
}
rc = msm_camera_spi_page_program(client, addr, buf,
(uint16_t)data_type, tx);
if (rc < 0)
goto ERROR;
goto OUT;
NOMEM:
pr_err("%s: memory allocation failed\n", __func__);
return -ENOMEM;
ERROR:
pr_err("%s: error write\n", __func__);
OUT:
kfree(tx);
return rc;
}
int32_t msm_camera_spi_write_table(struct msm_camera_i2c_client *client,
struct msm_camera_i2c_reg_setting *write_setting)
{
int i;
int32_t rc = -EFAULT;
struct msm_camera_i2c_reg_array *reg_setting;
uint16_t client_addr_type;
if (!client || !write_setting)
return rc;
if ((write_setting->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
&& write_setting->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
|| (write_setting->data_type != MSM_CAMERA_I2C_BYTE_DATA
&& write_setting->data_type != MSM_CAMERA_I2C_WORD_DATA))
return rc;
reg_setting = write_setting->reg_setting;
client_addr_type = client->addr_type;
client->addr_type = write_setting->addr_type;
for (i = 0; i < write_setting->size; i++) {
SPIDBG("%s addr %x data %x\n", __func__,
reg_setting->reg_addr, reg_setting->reg_data);
rc = msm_camera_spi_write(client, reg_setting->reg_addr,
reg_setting->reg_data, write_setting->data_type);
if (rc < 0)
break;
reg_setting++;
}
if (write_setting->delay > 20)
msleep(write_setting->delay);
else if (write_setting->delay)
usleep_range(write_setting->delay * 1000,
(write_setting->delay
* 1000) + 1000);
client->addr_type = client_addr_type;
return rc;
}
static uint32_t msm_get_burst_size(struct msm_camera_i2c_reg_array *reg_setting,
uint32_t reg_size, uint32_t index, uint16_t burst_addr)
{
uint32_t i;
uint32_t cnt = 0;
for (i = index; i < reg_size; i++) {
if (reg_setting[i].reg_addr == burst_addr)
cnt++;
else
break;
}
return cnt;
}
#ifdef SPI_DYNAMIC_ALLOC
static int32_t msm_camera_spi_send_burst(struct msm_camera_i2c_client *client,
struct msm_camera_i2c_reg_array *reg_setting, uint32_t reg_size,
struct msm_camera_burst_info *info,
enum msm_camera_i2c_data_type data_type)
{
uint32_t i, j, k;
int32_t rc = 0;
uint32_t chunk_num, residue;
struct msm_camera_spi_inst *pg =
&client->spi_client->cmd_tbl.page_program;
uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len;
uint8_t *ctx, *data;
uint32_t len;
if (info->burst_len == 0 || info->chunk_size == 0) {
pr_err("%s:%d Invalid argument\n", __func__, __LINE__);
return rc;
}
if (info->burst_start + info->burst_len > reg_size) {
pr_err("%s too big burst size, index=%d, size=%d\n", __func__,
info->burst_start, info->burst_len);
return rc;
}
chunk_num = info->burst_len / info->chunk_size;
residue = info->burst_len % info->chunk_size;
SPIDBG("%s header_len=%d, chunk nb=%d, residue=%d\n",
__func__, header_len, chunk_num, residue);
len = info->chunk_size * data_type + header_len;
SPIDBG("buffer allocation size = %d\n", len);
ctx = kmalloc(len, GFP_KERNEL | GFP_DMA);
if (!ctx) {
pr_err("%s %d memory alloc fail!\n", __func__, __LINE__);
return rc;
}
ctx[0] = pg->opcode;
ctx[1] = (info->burst_addr >> 8) & 0xff;
ctx[2] = info->burst_addr & 0xff;
k = info->burst_start;
for (i = 0; i < chunk_num; i++) {
data = ctx + header_len;
for (j = 0; j < info->chunk_size; j++) {
*data++ = (reg_setting[k+j].reg_data >> 8) & 0xff;
*data++ = reg_setting[k+j].reg_data & 0xff;
}
rc = msm_camera_spi_txfr(client->spi_client->spi_master,
(void *) ctx, NULL,
info->chunk_size * data_type + header_len);
if (rc < 0) {
pr_err("%s %d spi sending error = %d!!\n",
__func__, __LINE__, rc);
goto fail;
}
k += info->chunk_size;
}
SPIDBG("%s burst chunk start=%d, residue=%d\n",
__func__, k, residue);
if (residue) {
data = ctx + header_len;
for (j = 0; j < residue; j++) {
*data++ = (reg_setting[k+j].reg_data >> 8) & 0xff;
*data++ = reg_setting[k+j].reg_data & 0xff;
}
rc = msm_camera_spi_txfr(client->spi_client->spi_master,
(void *)ctx, NULL,
residue*data_type+header_len);
if (rc < 0) {
pr_err("%s %d spi sending error = %d!!\n", __func__,
__LINE__, rc);
goto fail;
}
}
fail:
kfree(ctx);
return rc;
}
#else /* SPI_DYNAMIC_ALLOC */
int32_t msm_camera_spi_send_burst(struct msm_camera_i2c_client *client,
struct msm_camera_i2c_reg_array *reg_setting, uint32_t reg_size,
struct msm_camera_burst_info *info,
enum msm_camera_i2c_data_type data_type)
{
uint32_t i, j, k;
int32_t rc = 0;
uint32_t chunk_num, residue;
struct msm_camera_spi_inst *pg =
&client->spi_client->cmd_tbl.page_program;
uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len;
struct msm_spi_write_burst_packet tx_buf;
if (info->burst_len == 0 || info->burst_len == 0
|| info->chunk_size == 0) {
pr_err("%s %d Invalid argument\n", __func__, __LINE__);
return rc;
}
if (info->burst_start + info->burst_len > reg_size) {
pr_err("%s too big burst size, index=%d, size=%d\n", __func__,
info->burst_start, info->burst_len);
return rc;
}
chunk_num = info->burst_len / info->chunk_size;
residue = info->burst_len % info->chunk_size;
SPIDBG("%s header_len=%d, chunk nb=%d, residue=%d\n",
__func__, header_len, chunk_num, residue);
tx_buf.cmd = pg->opcode;
tx_buf.addr_msb = (info->burst_addr >> 8) & 0xff;
tx_buf.addr_lsb = info->burst_addr & 0xff;
SPIDBG("%s cmd=%d, addr_msb=0x%x, addr_lsb=0x%x\n", __func__,
tx_buf.cmd, tx_buf.addr_msb, tx_buf.addr_lsb);
k = info->burst_start;
for (i = 0; i < chunk_num; i++) {
SPIDBG("%s burst chunk start=%d, chunk_size=%d, chunk_num=%d\n",
__func__,
k, info->chunk_size, i);
for (j = 0; j < info->chunk_size; j++) {
tx_buf.data_arr[j].data_msb =
(reg_setting[k+j].reg_data >> 8) & 0xff;
tx_buf.data_arr[j].data_lsb =
reg_setting[k+j].reg_data & 0xff;
}
rc = msm_camera_spi_txfr(client->spi_client->spi_master,
(void *)&tx_buf, NULL,
info->chunk_size * data_type+header_len);
if (rc < 0) {
pr_err("%s %d spi sending error = %d!!\n", __func__,
__LINE__, rc);
goto fail;
}
k += info->chunk_size;
}
SPIDBG("%s burst chunk start=%d, residue=%d\n", __func__, k, residue);
if (residue) {
for (j = 0; j < residue; j++) {
tx_buf.data_arr[j].data_msb = (reg_setting[k+j].reg_data
>> 8) & 0xff;
tx_buf.data_arr[j].data_lsb = reg_setting[k+j].reg_data
& 0xff;
}
rc = msm_camera_spi_txfr(client->spi_client->spi_master,
(void *)&tx_buf, NULL,
residue * data_type+header_len);
if (rc < 0) {
pr_err("%s %d spi sending error = %d!!\n", __func__,
__LINE__, rc);
goto fail;
}
}
fail:
return rc;
}
#endif /* SPI_DYNAMIC_ALLOC */
int32_t msm_camera_spi_write_burst(struct msm_camera_i2c_client *client,
struct msm_camera_i2c_reg_array *reg_setting, uint32_t reg_size,
uint32_t buf_len, uint32_t burst_addr,
enum msm_camera_i2c_data_type data_type)
{
int k = 0;
int32_t rc = -EFAULT;
struct msm_camera_burst_info burst_info;
SPIDBG(" %s: start\n", __func__);
if (buf_len <= 0) {
pr_err("%s Invalid parameter, buf_len = %d\n",
__func__, buf_len);
return rc;
}
if (reg_size <= 0 || reg_setting == NULL) {
pr_err("%s Invalid parameter, array_size = %d\n",
__func__, reg_size);
return rc;
}
if ((client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
|| (data_type != MSM_CAMERA_I2C_WORD_DATA))
return rc;
SPIDBG(" %s: buf_len=%d, reg_size=%d\n", __func__, buf_len, reg_size);
while (k < reg_size) {
if (reg_setting[k].reg_addr == burst_addr) {
memset(&burst_info, 0x00,
sizeof(struct msm_camera_burst_info));
burst_info.burst_addr = burst_addr;
burst_info.burst_start = k;
burst_info.chunk_size = buf_len;
burst_info.burst_len =
msm_get_burst_size(reg_setting, reg_size, k,
burst_addr);
SPIDBG("%s burst start = %d, length = %d\n", __func__,
k, burst_info.burst_len);
rc = msm_camera_spi_send_burst(client, reg_setting,
reg_size, &burst_info, data_type);
if (rc < 0) {
pr_err("[%s::%d][spi_sync Error::%d]\n",
__func__, __LINE__, rc);
return rc;
}
k += burst_info.burst_len;
} else {
SPIDBG("%s word write, start = %d\n", __func__, k);
msm_camera_spi_write(client, reg_setting[k].reg_addr,
reg_setting[k].reg_data, data_type);
k++;
}
}
SPIDBG("%s: end\n", __func__);
return rc;
}
int32_t msm_camera_spi_read_burst(struct msm_camera_i2c_client *client,
uint32_t read_byte, uint8_t *buffer, uint32_t burst_addr,
enum msm_camera_i2c_data_type data_type)
{
int32_t rc = -EFAULT;
struct msm_camera_spi_inst *pg =
&client->spi_client->cmd_tbl.read;
uint32_t len = msm_camera_spi_get_hlen(pg);
uint8_t *tx_buf = NULL;
uint8_t *r = buffer;
SPIDBG("%s: start\n", __func__);
if (buffer == NULL || read_byte == 0 || len == 0) {
pr_err("%s %d Invalid parameters!!\n", __func__, __LINE__);
return rc;
}
if ((client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
|| (data_type != MSM_CAMERA_I2C_WORD_DATA))
return rc;
tx_buf = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!tx_buf)
return -ENOMEM;
tx_buf[0] = pg->opcode;
tx_buf[1] = (burst_addr >> 8) & 0xff;
tx_buf[2] = burst_addr & 0xff;
tx_buf[3] = 0; /* dummy */
rc = msm_camera_spi_txfr_read(client->spi_client->spi_master,
&tx_buf[0], r, len, read_byte);
if (rc < 0)
pr_err("[%s::%d][spi_sync Error::%d]\n", __func__,
__LINE__, rc);
kfree(tx_buf);
SPIDBG("%s: end\n", __func__);
return rc;
}