aboutsummaryrefslogtreecommitdiff
path: root/camera/sensor/io/msm_camera_spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'camera/sensor/io/msm_camera_spi.c')
-rw-r--r--camera/sensor/io/msm_camera_spi.c836
1 files changed, 836 insertions, 0 deletions
diff --git a/camera/sensor/io/msm_camera_spi.c b/camera/sensor/io/msm_camera_spi.c
new file mode 100644
index 00000000..19a70feb
--- /dev/null
+++ b/camera/sensor/io/msm_camera_spi.c
@@ -0,0 +1,836 @@
+/* Copyright (c) 2013-2016, 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.
+ */
+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;
+}
+
+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;
+ else
+ 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 (0 == we->opcode)
+ 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[data_type];
+ 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;
+ 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;
+}
+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
+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;
+}