aboutsummaryrefslogtreecommitdiff
path: root/camera/sensor/cci/msm_cci.c
diff options
context:
space:
mode:
Diffstat (limited to 'camera/sensor/cci/msm_cci.c')
-rw-r--r--camera/sensor/cci/msm_cci.c2128
1 files changed, 2128 insertions, 0 deletions
diff --git a/camera/sensor/cci/msm_cci.c b/camera/sensor/cci/msm_cci.c
new file mode 100644
index 00000000..4ba2b7ea
--- /dev/null
+++ b/camera/sensor/cci/msm_cci.c
@@ -0,0 +1,2128 @@
+/* Copyright (c) 2012-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 <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include "msm_sd.h"
+#include "msm_cci.h"
+#include "msm_cam_cci_hwreg.h"
+#include "msm_camera_io_util.h"
+#include "msm_camera_dt_util.h"
+#include "cam_hw_ops.h"
+
+#define V4L2_IDENT_CCI 50005
+#define CCI_I2C_QUEUE_0_SIZE 64
+#define CCI_I2C_Q0_SIZE_128W 128
+#define CCI_I2C_QUEUE_1_SIZE 16
+#define CCI_I2C_Q1_SIZE_32W 32
+#define CYCLES_PER_MICRO_SEC_DEFAULT 4915
+#define CCI_MAX_DELAY 1000000
+
+#define CCI_TIMEOUT msecs_to_jiffies(100)
+
+/* TODO move this somewhere else */
+#define MSM_CCI_DRV_NAME "msm_cci"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+#undef CCI_DBG
+#ifdef MSM_CCI_DEBUG
+#define CCI_DBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CCI_DBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+/* Max bytes that can be read per CCI read transaction */
+#define CCI_READ_MAX 12
+#define CCI_I2C_READ_MAX_RETRIES 3
+#define CCI_I2C_MAX_READ 8192
+#define CCI_I2C_MAX_WRITE 8192
+
+#define PRIORITY_QUEUE (QUEUE_0)
+#define SYNC_QUEUE (QUEUE_1)
+
+static struct v4l2_subdev *g_cci_subdev;
+
+static void msm_cci_dump_registers(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master, enum cci_i2c_queue_t queue)
+{
+ uint32_t read_val = 0;
+ uint32_t i = 0;
+ uint32_t reg_offset = 0;
+
+ /* CCI Top Registers */
+ CCI_DBG(" **** %s : %d CCI TOP Registers ****\n", __func__, __LINE__);
+ for (i = 0; i < DEBUG_TOP_REG_COUNT; i++) {
+ reg_offset = DEBUG_TOP_REG_START + i * 4;
+ read_val = msm_camera_io_r_mb(cci_dev->base + reg_offset);
+ CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n",
+ __func__, __LINE__, reg_offset, read_val);
+ }
+
+ /* CCI Master registers */
+ CCI_DBG(" **** %s : %d CCI MASTER%d Registers ****\n",
+ __func__, __LINE__, master);
+ for (i = 0; i < DEBUG_MASTER_REG_COUNT; i++) {
+ if (i == 6)
+ continue;
+ reg_offset = DEBUG_MASTER_REG_START + master*0x100 + i * 4;
+ read_val = msm_camera_io_r_mb(cci_dev->base + reg_offset);
+ CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n",
+ __func__, __LINE__, reg_offset, read_val);
+ }
+
+ /* CCI Master Queue registers */
+ CCI_DBG(" **** %s : %d CCI MASTER%d QUEUE%d Registers ****\n",
+ __func__, __LINE__, master, queue);
+ for (i = 0; i < DEBUG_MASTER_QUEUE_REG_COUNT; i++) {
+ reg_offset = DEBUG_MASTER_QUEUE_REG_START + master*0x200 +
+ queue*0x100 + i * 4;
+ read_val = msm_camera_io_r_mb(cci_dev->base + reg_offset);
+ CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n",
+ __func__, __LINE__, reg_offset, read_val);
+ }
+
+ /* CCI Interrupt registers */
+ CCI_DBG(" **** %s : %d CCI Interrupt Registers ****\n",
+ __func__, __LINE__);
+ for (i = 0; i < DEBUG_INTR_REG_COUNT; i++) {
+ reg_offset = DEBUG_INTR_REG_START + i * 4;
+ read_val = msm_camera_io_r_mb(cci_dev->base + reg_offset);
+ CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n",
+ __func__, __LINE__, reg_offset, read_val);
+ }
+}
+
+static int32_t msm_cci_set_clk_param(struct cci_device *cci_dev,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ struct msm_cci_clk_params_t *clk_params = NULL;
+ enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master;
+ enum i2c_freq_mode_t i2c_freq_mode = c_ctrl->cci_info->i2c_freq_mode;
+
+ clk_params = &cci_dev->cci_clk_params[i2c_freq_mode];
+
+ if ((i2c_freq_mode >= I2C_MAX_MODES) || (i2c_freq_mode < 0)) {
+ pr_err("%s:%d invalid i2c_freq_mode = %d",
+ __func__, __LINE__, i2c_freq_mode);
+ return -EINVAL;
+ }
+ if (cci_dev->i2c_freq_mode[master] == i2c_freq_mode)
+ return 0;
+ if (MASTER_0 == master) {
+ msm_camera_io_w_mb(clk_params->hw_thigh << 16 |
+ clk_params->hw_tlow,
+ cci_dev->base + CCI_I2C_M0_SCL_CTL_ADDR);
+ msm_camera_io_w_mb(clk_params->hw_tsu_sto << 16 |
+ clk_params->hw_tsu_sta,
+ cci_dev->base + CCI_I2C_M0_SDA_CTL_0_ADDR);
+ msm_camera_io_w_mb(clk_params->hw_thd_dat << 16 |
+ clk_params->hw_thd_sta,
+ cci_dev->base + CCI_I2C_M0_SDA_CTL_1_ADDR);
+ msm_camera_io_w_mb(clk_params->hw_tbuf,
+ cci_dev->base + CCI_I2C_M0_SDA_CTL_2_ADDR);
+ msm_camera_io_w_mb(clk_params->hw_scl_stretch_en << 8 |
+ clk_params->hw_trdhld << 4 | clk_params->hw_tsp,
+ cci_dev->base + CCI_I2C_M0_MISC_CTL_ADDR);
+ } else if (MASTER_1 == master) {
+ msm_camera_io_w_mb(clk_params->hw_thigh << 16 |
+ clk_params->hw_tlow,
+ cci_dev->base + CCI_I2C_M1_SCL_CTL_ADDR);
+ msm_camera_io_w_mb(clk_params->hw_tsu_sto << 16 |
+ clk_params->hw_tsu_sta,
+ cci_dev->base + CCI_I2C_M1_SDA_CTL_0_ADDR);
+ msm_camera_io_w_mb(clk_params->hw_thd_dat << 16 |
+ clk_params->hw_thd_sta,
+ cci_dev->base + CCI_I2C_M1_SDA_CTL_1_ADDR);
+ msm_camera_io_w_mb(clk_params->hw_tbuf,
+ cci_dev->base + CCI_I2C_M1_SDA_CTL_2_ADDR);
+ msm_camera_io_w_mb(clk_params->hw_scl_stretch_en << 8 |
+ clk_params->hw_trdhld << 4 | clk_params->hw_tsp,
+ cci_dev->base + CCI_I2C_M1_MISC_CTL_ADDR);
+ }
+ cci_dev->i2c_freq_mode[master] = i2c_freq_mode;
+ return 0;
+}
+
+static void msm_cci_flush_queue(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master)
+{
+ int32_t rc = 0;
+
+ msm_camera_io_w_mb(1 << master, cci_dev->base + CCI_HALT_REQ_ADDR);
+ rc = wait_for_completion_timeout(
+ &cci_dev->cci_master_info[master].reset_complete, CCI_TIMEOUT);
+ if (rc < 0) {
+ pr_err("%s:%d wait failed\n", __func__, __LINE__);
+ } else if (rc == 0) {
+ pr_err("%s:%d wait timeout\n", __func__, __LINE__);
+
+ /* Set reset pending flag to TRUE */
+ cci_dev->cci_master_info[master].reset_pending = TRUE;
+
+ /* Set proper mask to RESET CMD address based on MASTER */
+ if (master == MASTER_0)
+ msm_camera_io_w_mb(CCI_M0_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ else
+ msm_camera_io_w_mb(CCI_M1_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+
+ /* wait for reset done irq */
+ rc = wait_for_completion_timeout(
+ &cci_dev->cci_master_info[master].reset_complete,
+ CCI_TIMEOUT);
+ if (rc <= 0)
+ pr_err("%s:%d wait failed %d\n", __func__, __LINE__,
+ rc);
+ }
+ return;
+}
+
+static int32_t msm_cci_validate_queue(struct cci_device *cci_dev,
+ uint32_t len,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+ uint32_t read_val = 0;
+ uint32_t reg_offset = master * 0x200 + queue * 0x100;
+ read_val = msm_camera_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ CDBG("%s line %d CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR %d len %d max %d\n",
+ __func__, __LINE__, read_val, len,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size);
+ if ((read_val + len + 1) > cci_dev->
+ cci_i2c_queue_info[master][queue].max_queue_size) {
+ uint32_t reg_val = 0;
+ uint32_t report_val = CCI_I2C_REPORT_CMD | (1 << 8);
+ CDBG("%s:%d CCI_I2C_REPORT_CMD\n", __func__, __LINE__);
+ msm_camera_io_w_mb(report_val,
+ cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ read_val++;
+ CDBG("%s:%d CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR %d, queue: %d\n",
+ __func__, __LINE__, read_val, queue);
+ msm_camera_io_w_mb(read_val, cci_dev->base +
+ CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
+ reg_val = 1 << ((master * 2) + queue);
+ CDBG("%s:%d CCI_QUEUE_START_ADDR\n", __func__, __LINE__);
+ atomic_set(&cci_dev->cci_master_info[master].
+ done_pending[queue], 1);
+ msm_camera_io_w_mb(reg_val, cci_dev->base +
+ CCI_QUEUE_START_ADDR);
+ CDBG("%s line %d wait_for_completion_timeout\n",
+ __func__, __LINE__);
+ atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
+ rc = wait_for_completion_timeout(&cci_dev->
+ cci_master_info[master].report_q[queue], CCI_TIMEOUT);
+ if (rc <= 0) {
+ pr_err("%s: wait_for_completion_timeout %d\n",
+ __func__, __LINE__);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ msm_cci_flush_queue(cci_dev, master);
+ return rc;
+ }
+ rc = cci_dev->cci_master_info[master].status;
+ if (rc < 0)
+ pr_err("%s failed rc %d\n", __func__, rc);
+ }
+ return rc;
+}
+
+static int32_t msm_cci_write_i2c_queue(struct cci_device *cci_dev,
+ uint32_t val,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+ uint32_t reg_offset = master * 0x200 + queue * 0x100;
+
+ if (!cci_dev) {
+ pr_err("%s: failed %d", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_cci_validate_queue(cci_dev, 1, master, queue);
+ if (rc < 0) {
+ pr_err("%s: failed %d", __func__, __LINE__);
+ return rc;
+ }
+ CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR:val 0x%x:0x%x\n",
+ __func__, CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset, val);
+ msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ return rc;
+}
+
+static uint32_t msm_cci_wait(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+
+ if (!cci_dev) {
+ pr_err("%s: failed %d", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ rc = wait_for_completion_timeout(&cci_dev->
+ cci_master_info[master].report_q[queue], CCI_TIMEOUT);
+ CDBG("%s line %d wait DONE_for_completion_timeout\n",
+ __func__, __LINE__);
+
+ if (rc <= 0) {
+ msm_cci_dump_registers(cci_dev, master, queue);
+ pr_err("%s: %d wait for queue: %d\n",
+ __func__, __LINE__, queue);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ msm_cci_flush_queue(cci_dev, master);
+ return rc;
+ }
+ rc = cci_dev->cci_master_info[master].status;
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ return 0;
+}
+
+static int32_t msm_cci_addr_to_num_bytes(
+ enum msm_camera_i2c_reg_addr_type addr_type)
+{
+ int32_t retVal;
+
+ switch (addr_type) {
+ case MSM_CAMERA_I2C_BYTE_ADDR:
+ retVal = 1;
+ break;
+ case MSM_CAMERA_I2C_WORD_ADDR:
+ retVal = 2;
+ break;
+ case MSM_CAMERA_I2C_3B_ADDR:
+ retVal = 3;
+ break;
+ default:
+ pr_err("%s: %d failed: %d\n", __func__, __LINE__, addr_type);
+ retVal = 1;
+ break;
+ }
+ return retVal;
+}
+
+static int32_t msm_cci_data_to_num_bytes(
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t retVal;
+
+ switch (data_type) {
+ case MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA:
+ case MSM_CAMERA_I2C_SET_BYTE_MASK:
+ case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ retVal = 1;
+ break;
+ case MSM_CAMERA_I2C_SET_WORD_MASK:
+ case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+ case MSM_CAMERA_I2C_WORD_DATA:
+ retVal = 2;
+ break;
+ case MSM_CAMERA_I2C_DWORD_DATA:
+ retVal = 4;
+ break;
+ default:
+ pr_err("%s: %d failed: %d\n", __func__, __LINE__, data_type);
+ retVal = 1;
+ break;
+ }
+ return retVal;
+}
+
+static int32_t msm_cci_calc_cmd_len(struct cci_device *cci_dev,
+ struct msm_camera_cci_ctrl *c_ctrl, uint32_t cmd_size,
+ struct msm_camera_i2c_reg_array *i2c_cmd, uint32_t *pack)
+{
+ uint8_t i;
+ uint32_t len = 0;
+ uint8_t data_len = 0, addr_len = 0;
+ uint8_t pack_max_len;
+ struct msm_camera_i2c_reg_setting *msg;
+ struct msm_camera_i2c_reg_array *cmd = i2c_cmd;
+ uint32_t size = cmd_size;
+
+ if (!cci_dev || !c_ctrl) {
+ pr_err("%s: failed %d", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ msg = &c_ctrl->cfg.cci_i2c_write_cfg;
+ *pack = 0;
+
+ if (c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) {
+ addr_len = msm_cci_addr_to_num_bytes(msg->addr_type);
+ len = (size + addr_len) <= (cci_dev->payload_size) ?
+ (size + addr_len):cci_dev->payload_size;
+ } else {
+ addr_len = msm_cci_addr_to_num_bytes(msg->addr_type);
+ data_len = msm_cci_data_to_num_bytes(msg->data_type);
+ len = data_len + addr_len;
+ pack_max_len = size < (cci_dev->payload_size-len) ?
+ size : (cci_dev->payload_size-len);
+ for (i = 0; i < pack_max_len;) {
+ if (cmd->delay || ((cmd - i2c_cmd) >= (cmd_size - 1)))
+ break;
+ if (cmd->reg_addr + 1 ==
+ (cmd+1)->reg_addr) {
+ len += data_len;
+ *pack += data_len;
+ } else
+ break;
+ i += data_len;
+ cmd++;
+ }
+ }
+
+ if (len > cci_dev->payload_size) {
+ pr_err("Len error: %d", len);
+ return -EINVAL;
+ }
+
+ len += 1; /*add i2c WR command*/
+ len = len/4 + 1;
+
+ return len;
+}
+
+static void msm_cci_load_report_cmd(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ uint32_t reg_offset = master * 0x200 + queue * 0x100;
+ uint32_t read_val = msm_camera_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ uint32_t report_val = CCI_I2C_REPORT_CMD | (1 << 8);
+
+ CDBG("%s:%d CCI_I2C_REPORT_CMD curr_w_cnt: %d\n",
+ __func__, __LINE__, read_val);
+ msm_camera_io_w_mb(report_val,
+ cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ read_val++;
+
+ CDBG("%s:%d CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR %d\n",
+ __func__, __LINE__, read_val);
+ msm_camera_io_w_mb(read_val, cci_dev->base +
+ CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
+}
+
+static int32_t msm_cci_wait_report_cmd(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ uint32_t reg_val = 1 << ((master * 2) + queue);
+ msm_cci_load_report_cmd(cci_dev, master, queue);
+ atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
+ atomic_set(&cci_dev->cci_master_info[master].done_pending[queue], 1);
+ msm_camera_io_w_mb(reg_val, cci_dev->base +
+ CCI_QUEUE_START_ADDR);
+ return msm_cci_wait(cci_dev, master, queue);
+}
+
+static void msm_cci_process_half_q(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ uint32_t reg_val = 1 << ((master * 2) + queue);
+ if (0 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) {
+ msm_cci_load_report_cmd(cci_dev, master, queue);
+ atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
+ msm_camera_io_w_mb(reg_val, cci_dev->base +
+ CCI_QUEUE_START_ADDR);
+ }
+}
+
+static int32_t msm_cci_process_full_q(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+ if (1 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) {
+ atomic_set(&cci_dev->cci_master_info[master].
+ done_pending[queue], 1);
+ rc = msm_cci_wait(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ } else {
+ rc = msm_cci_wait_report_cmd(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+static int32_t msm_cci_lock_queue(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue, uint32_t en)
+{
+ uint32_t val;
+
+ if (queue != PRIORITY_QUEUE)
+ return 0;
+
+ val = en ? CCI_I2C_LOCK_CMD : CCI_I2C_UNLOCK_CMD;
+ return msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+}
+
+static int32_t msm_cci_transfer_end(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+
+ if (0 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) {
+ rc = msm_cci_lock_queue(cci_dev, master, queue, 0);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+ rc = msm_cci_wait_report_cmd(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ } else {
+ atomic_set(&cci_dev->cci_master_info[master].
+ done_pending[queue], 1);
+ rc = msm_cci_wait(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ rc = msm_cci_lock_queue(cci_dev, master, queue, 0);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+ rc = msm_cci_wait_report_cmd(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+static int32_t msm_cci_get_queue_free_size(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ uint32_t read_val = 0;
+ uint32_t reg_offset = master * 0x200 + queue * 0x100;
+ read_val = msm_camera_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ CDBG("%s line %d CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR %d max %d\n",
+ __func__, __LINE__, read_val,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size);
+ return (cci_dev->
+ cci_i2c_queue_info[master][queue].max_queue_size) -
+ read_val;
+}
+
+static int32_t msm_cci_data_queue(struct cci_device *cci_dev,
+ struct msm_camera_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue,
+ enum cci_i2c_sync sync_en)
+{
+ uint16_t i = 0, j = 0, k = 0, h = 0, len = 0;
+ int32_t rc = 0, free_size = 0, en_seq_write = 0;
+ uint32_t cmd = 0, delay = 0;
+ uint8_t data[12];
+ uint16_t reg_addr = 0;
+ struct msm_camera_i2c_reg_setting *i2c_msg =
+ &c_ctrl->cfg.cci_i2c_write_cfg;
+ uint16_t cmd_size = i2c_msg->size;
+ struct msm_camera_i2c_reg_array *i2c_cmd = i2c_msg->reg_setting;
+ enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master;
+
+ uint32_t read_val = 0;
+ uint32_t reg_offset;
+ uint32_t val = 0;
+ uint32_t max_queue_size;
+
+ if (i2c_cmd == NULL) {
+ pr_err("%s:%d Failed line\n", __func__,
+ __LINE__);
+ return -EINVAL;
+ }
+
+ if ((!cmd_size) || (cmd_size > CCI_I2C_MAX_WRITE)) {
+ pr_err("%s:%d failed: invalid cmd_size %d\n",
+ __func__, __LINE__, cmd_size);
+ return -EINVAL;
+ }
+
+ CDBG("%s addr type %d data type %d cmd_size %d\n", __func__,
+ i2c_msg->addr_type, i2c_msg->data_type, cmd_size);
+
+ if (i2c_msg->addr_type >= MSM_CAMERA_I2C_ADDR_TYPE_MAX) {
+ pr_err("%s:%d failed: invalid addr_type 0x%X\n",
+ __func__, __LINE__, i2c_msg->addr_type);
+ return -EINVAL;
+ }
+ if (i2c_msg->data_type >= MSM_CAMERA_I2C_DATA_TYPE_MAX) {
+ pr_err("%s:%d failed: invalid data_type 0x%X\n",
+ __func__, __LINE__, i2c_msg->data_type);
+ return -EINVAL;
+ }
+ reg_offset = master * 0x200 + queue * 0x100;
+
+ msm_camera_io_w_mb(cci_dev->cci_wait_sync_cfg.cid,
+ cci_dev->base + CCI_SET_CID_SYNC_TIMER_ADDR +
+ cci_dev->cci_wait_sync_cfg.csid *
+ CCI_SET_CID_SYNC_TIMER_OFFSET);
+
+ val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 |
+ c_ctrl->cci_info->retries << 16 |
+ c_ctrl->cci_info->id_map << 18;
+
+ CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR:val 0x%x:0x%x\n",
+ __func__, CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset, val);
+ msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+
+ atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 0);
+
+ max_queue_size = cci_dev->cci_i2c_queue_info[master][queue].
+ max_queue_size;
+ reg_addr = i2c_cmd->reg_addr;
+
+ if (sync_en == MSM_SYNC_ENABLE && cci_dev->valid_sync &&
+ cmd_size < max_queue_size) {
+ val = CCI_I2C_WAIT_SYNC_CMD |
+ ((cci_dev->cci_wait_sync_cfg.line) << 4);
+ msm_camera_io_w_mb(val,
+ cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ }
+
+ rc = msm_cci_lock_queue(cci_dev, master, queue, 1);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+
+ while (cmd_size) {
+ uint32_t pack = 0;
+ len = msm_cci_calc_cmd_len(cci_dev, c_ctrl, cmd_size,
+ i2c_cmd, &pack);
+ if (len <= 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ read_val = msm_camera_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ CDBG("%s line %d CUR_WORD_CNT_ADDR %d len %d max %d\n",
+ __func__, __LINE__, read_val, len, max_queue_size);
+ /* + 1 - space alocation for Report CMD*/
+ if ((read_val + len + 1) > max_queue_size/2) {
+ if ((read_val + len + 1) > max_queue_size) {
+ rc = msm_cci_process_full_q(cci_dev,
+ master, queue);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ continue;
+ }
+ msm_cci_process_half_q(cci_dev, master, queue);
+ }
+
+ CDBG("%s cmd_size %d addr 0x%x data 0x%x\n", __func__,
+ cmd_size, i2c_cmd->reg_addr, i2c_cmd->reg_data);
+ delay = i2c_cmd->delay;
+ i = 0;
+ data[i++] = CCI_I2C_WRITE_CMD;
+
+ /* in case of multiple command
+ * MSM_CCI_I2C_WRITE : address is not continuous, so update
+ * address for a new packet.
+ * MSM_CCI_I2C_WRITE_SEQ : address is continuous, need to keep
+ * the incremented address for a
+ * new packet */
+ if (c_ctrl->cmd == MSM_CCI_I2C_WRITE ||
+ c_ctrl->cmd == MSM_CCI_I2C_WRITE_ASYNC ||
+ c_ctrl->cmd == MSM_CCI_I2C_WRITE_SYNC ||
+ c_ctrl->cmd == MSM_CCI_I2C_WRITE_SYNC_BLOCK)
+ reg_addr = i2c_cmd->reg_addr;
+
+ if (en_seq_write == 0) {
+ /* either byte or word addr */
+ if (i2c_msg->addr_type == MSM_CAMERA_I2C_BYTE_ADDR)
+ data[i++] = reg_addr;
+ else {
+ data[i++] = (reg_addr & 0xFF00) >> 8;
+ data[i++] = reg_addr & 0x00FF;
+ }
+ }
+ /* max of 10 data bytes */
+ do {
+ if (i2c_msg->data_type == MSM_CAMERA_I2C_BYTE_DATA) {
+ data[i++] = i2c_cmd->reg_data;
+ reg_addr++;
+ } else {
+ if ((i + 1) <= cci_dev->payload_size) {
+ data[i++] = (i2c_cmd->reg_data &
+ 0xFF00) >> 8; /* MSB */
+ data[i++] = i2c_cmd->reg_data &
+ 0x00FF; /* LSB */
+ reg_addr++;
+ } else
+ break;
+ }
+ i2c_cmd++;
+ --cmd_size;
+ } while (((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) || pack--) &&
+ (cmd_size > 0) && (i <= cci_dev->payload_size));
+ free_size = msm_cci_get_queue_free_size(cci_dev, master,
+ queue);
+ if ((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) &&
+ ((i-1) == MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11) &&
+ cci_dev->support_seq_write && cmd_size > 0 &&
+ free_size > BURST_MIN_FREE_SIZE) {
+ data[0] |= 0xF0;
+ en_seq_write = 1;
+ } else {
+ data[0] |= ((i-1) << 4);
+ en_seq_write = 0;
+ }
+ len = ((i-1)/4) + 1;
+
+ read_val = msm_camera_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ for (h = 0, k = 0; h < len; h++) {
+ cmd = 0;
+ for (j = 0; (j < 4 && k < i); j++)
+ cmd |= (data[k++] << (j * 8));
+ CDBG("%s LOAD_DATA_ADDR 0x%x, q: %d, len:%d, cnt: %d\n",
+ __func__, cmd, queue, len, read_val);
+ msm_camera_io_w_mb(cmd, cci_dev->base +
+ CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ master * 0x200 + queue * 0x100);
+
+ read_val += 1;
+ msm_camera_io_w_mb(read_val, cci_dev->base +
+ CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
+ }
+
+ if ((delay > 0) && (delay < CCI_MAX_DELAY) &&
+ en_seq_write == 0) {
+ cmd = (uint32_t)((delay * cci_dev->cycles_per_us) /
+ 0x100);
+ cmd <<= 4;
+ cmd |= CCI_I2C_WAIT_CMD;
+ CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x%x\n",
+ __func__, cmd);
+ msm_camera_io_w_mb(cmd, cci_dev->base +
+ CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ master * 0x200 + queue * 0x100);
+ read_val += 1;
+ msm_camera_io_w_mb(read_val, cci_dev->base +
+ CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
+ }
+ }
+
+ rc = msm_cci_transfer_end(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ return rc;
+}
+
+static int32_t msm_cci_i2c_read(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ uint32_t val = 0;
+ int32_t read_words = 0, exp_words = 0;
+ int32_t index = 0, first_byte = 0;
+ uint32_t i = 0;
+ enum cci_i2c_master_t master;
+ enum cci_i2c_queue_t queue = QUEUE_1;
+ struct cci_device *cci_dev = NULL;
+ struct msm_camera_cci_i2c_read_cfg *read_cfg = NULL;
+ CDBG("%s line %d\n", __func__, __LINE__);
+ cci_dev = v4l2_get_subdevdata(sd);
+ master = c_ctrl->cci_info->cci_i2c_master;
+ read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg;
+ mutex_lock(&cci_dev->cci_master_info[master].mutex_q[queue]);
+
+ /* Set the I2C Frequency */
+ rc = msm_cci_set_clk_param(cci_dev, c_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d msm_cci_set_clk_param failed rc = %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ /*
+ * Call validate queue to make sure queue is empty before starting.
+ * If this call fails, don't proceed with i2c_read call. This is to
+ * avoid overflow / underflow of queue
+ */
+ rc = msm_cci_validate_queue(cci_dev,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size - 1,
+ master, queue);
+ if (rc < 0) {
+ pr_err("%s:%d Initial validataion failed rc %d\n", __func__,
+ __LINE__, rc);
+ goto ERROR;
+ }
+
+ if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) {
+ pr_err("%s:%d More than max retries\n", __func__,
+ __LINE__);
+ goto ERROR;
+ }
+
+ if (read_cfg->data == NULL) {
+ pr_err("%s:%d Data ptr is NULL\n", __func__,
+ __LINE__);
+ goto ERROR;
+ }
+
+ CDBG("%s master %d, queue %d\n", __func__, master, queue);
+ CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__,
+ c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
+ c_ctrl->cci_info->id_map);
+ val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 |
+ c_ctrl->cci_info->retries << 16 |
+ c_ctrl->cci_info->id_map << 18;
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = CCI_I2C_LOCK_CMD;
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ if (read_cfg->addr_type >= MSM_CAMERA_I2C_ADDR_TYPE_MAX) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ if (read_cfg->addr_type == MSM_CAMERA_I2C_BYTE_ADDR)
+ val = CCI_I2C_WRITE_DISABLE_P_CMD | (read_cfg->addr_type << 4) |
+ ((read_cfg->addr & 0xFF) << 8);
+ if (read_cfg->addr_type == MSM_CAMERA_I2C_WORD_ADDR)
+ val = CCI_I2C_WRITE_DISABLE_P_CMD | (read_cfg->addr_type << 4) |
+ (((read_cfg->addr & 0xFF00) >> 8) << 8) |
+ ((read_cfg->addr & 0xFF) << 16);
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = CCI_I2C_READ_CMD | (read_cfg->num_byte << 4);
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = CCI_I2C_UNLOCK_CMD;
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = msm_camera_io_r_mb(cci_dev->base + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR
+ + master * 0x200 + queue * 0x100);
+ CDBG("%s cur word cnt 0x%x\n", __func__, val);
+ msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR
+ + master * 0x200 + queue * 0x100);
+
+ val = 1 << ((master * 2) + queue);
+ msm_camera_io_w_mb(val, cci_dev->base + CCI_QUEUE_START_ADDR);
+ CDBG("%s:%d E wait_for_completion_timeout\n", __func__,
+ __LINE__);
+
+ rc = wait_for_completion_timeout(&cci_dev->
+ cci_master_info[master].reset_complete, CCI_TIMEOUT);
+ if (rc <= 0) {
+ msm_cci_dump_registers(cci_dev, master, queue);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ pr_err("%s: %d wait_for_completion_timeout rc = %d\n",
+ __func__, __LINE__, rc);
+ msm_cci_flush_queue(cci_dev, master);
+ goto ERROR;
+ } else {
+ rc = 0;
+ }
+
+ read_words = msm_camera_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_READ_BUF_LEVEL_ADDR + master * 0x100);
+ exp_words = ((read_cfg->num_byte / 4) + 1);
+ if (read_words != exp_words) {
+ pr_err("%s:%d read_words = %d, exp words = %d\n", __func__,
+ __LINE__, read_words, exp_words);
+ memset(read_cfg->data, 0, read_cfg->num_byte);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ index = 0;
+ CDBG("%s index %d num_type %d\n", __func__, index,
+ read_cfg->num_byte);
+ first_byte = 0;
+ do {
+ val = msm_camera_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_READ_DATA_ADDR + master * 0x100);
+ CDBG("%s read val 0x%x\n", __func__, val);
+ for (i = 0; (i < 4) && (index < read_cfg->num_byte); i++) {
+ CDBG("%s i %d index %d\n", __func__, i, index);
+ if (!first_byte) {
+ CDBG("%s sid 0x%x\n", __func__, val & 0xFF);
+ first_byte++;
+ } else {
+ read_cfg->data[index] =
+ (val >> (i * 8)) & 0xFF;
+ CDBG("%s data[%d] 0x%x\n", __func__, index,
+ read_cfg->data[index]);
+ index++;
+ }
+ }
+ } while (--read_words > 0);
+ERROR:
+ mutex_unlock(&cci_dev->cci_master_info[master].mutex_q[queue]);
+ return rc;
+}
+
+static int32_t msm_cci_i2c_read_bytes(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ struct cci_device *cci_dev = NULL;
+ enum cci_i2c_master_t master;
+ struct msm_camera_cci_i2c_read_cfg *read_cfg = NULL;
+ uint16_t read_bytes = 0;
+
+ if (!sd || !c_ctrl) {
+ pr_err("%s:%d sd %p c_ctrl %p\n", __func__,
+ __LINE__, sd, c_ctrl);
+ return -EINVAL;
+ }
+ if (!c_ctrl->cci_info) {
+ pr_err("%s:%d cci_info NULL\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (!cci_dev) {
+ pr_err("%s:%d cci_dev NULL\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (cci_dev->cci_state != CCI_STATE_ENABLED) {
+ pr_err("%s invalid cci state %d\n",
+ __func__, cci_dev->cci_state);
+ return -EINVAL;
+ }
+
+ if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX
+ || c_ctrl->cci_info->cci_i2c_master < 0) {
+ pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ master = c_ctrl->cci_info->cci_i2c_master;
+ read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg;
+ if ((!read_cfg->num_byte) || (read_cfg->num_byte > CCI_I2C_MAX_READ)) {
+ pr_err("%s:%d read num bytes 0\n", __func__, __LINE__);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+
+ read_bytes = read_cfg->num_byte;
+ do {
+ if (read_bytes > CCI_READ_MAX)
+ read_cfg->num_byte = CCI_READ_MAX;
+ else
+ read_cfg->num_byte = read_bytes;
+ rc = msm_cci_i2c_read(sd, c_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc);
+ goto ERROR;
+ }
+ if (read_bytes > CCI_READ_MAX) {
+ read_cfg->addr += CCI_READ_MAX;
+ read_cfg->data += CCI_READ_MAX;
+ read_bytes -= CCI_READ_MAX;
+ } else {
+ read_bytes = 0;
+ }
+ } while (read_bytes);
+ERROR:
+ return rc;
+}
+
+static int32_t msm_cci_i2c_write(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue,
+ enum cci_i2c_sync sync_en)
+{
+ int32_t rc = 0;
+ struct cci_device *cci_dev;
+ enum cci_i2c_master_t master;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX
+ || c_ctrl->cci_info->cci_i2c_master < 0) {
+ pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (cci_dev->cci_state != CCI_STATE_ENABLED) {
+ pr_err("%s invalid cci state %d\n",
+ __func__, cci_dev->cci_state);
+ return -EINVAL;
+ }
+ master = c_ctrl->cci_info->cci_i2c_master;
+ CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__,
+ c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
+ c_ctrl->cci_info->id_map);
+
+ /* Set the I2C Frequency */
+ rc = msm_cci_set_clk_param(cci_dev, c_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d msm_cci_set_clk_param failed rc = %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ /*
+ * Call validate queue to make sure queue is empty before starting.
+ * If this call fails, don't proceed with i2c_write call. This is to
+ * avoid overflow / underflow of queue
+ */
+ rc = msm_cci_validate_queue(cci_dev,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size-1,
+ master, queue);
+ if (rc < 0) {
+ pr_err("%s:%d Initial validataion failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ }
+ if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) {
+ pr_err("%s:%d More than max retries\n", __func__,
+ __LINE__);
+ goto ERROR;
+ }
+ rc = msm_cci_data_queue(cci_dev, c_ctrl, queue, sync_en);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ERROR:
+ return rc;
+}
+
+static void msm_cci_write_async_helper(struct work_struct *work)
+{
+ int rc;
+ struct cci_device *cci_dev;
+ struct cci_write_async *write_async =
+ container_of(work, struct cci_write_async, work);
+ struct msm_camera_i2c_reg_setting *i2c_msg;
+ enum cci_i2c_master_t master;
+ struct msm_camera_cci_master_info *cci_master_info;
+
+ cci_dev = write_async->cci_dev;
+ i2c_msg = &write_async->c_ctrl.cfg.cci_i2c_write_cfg;
+ master = write_async->c_ctrl.cci_info->cci_i2c_master;
+ cci_master_info = &cci_dev->cci_master_info[master];
+
+ mutex_lock(&cci_master_info->mutex_q[write_async->queue]);
+ rc = msm_cci_i2c_write(&cci_dev->msm_sd.sd,
+ &write_async->c_ctrl, write_async->queue, write_async->sync_en);
+ mutex_unlock(&cci_master_info->mutex_q[write_async->queue]);
+ if (rc < 0)
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+
+ kfree(write_async->c_ctrl.cfg.cci_i2c_write_cfg.reg_setting);
+ kfree(write_async);
+
+ CDBG("%s: %d Exit\n", __func__, __LINE__);
+}
+
+static int32_t msm_cci_i2c_write_async(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue,
+ enum cci_i2c_sync sync_en)
+{
+ struct cci_write_async *write_async;
+ struct cci_device *cci_dev;
+ struct msm_camera_i2c_reg_setting *cci_i2c_write_cfg;
+ struct msm_camera_i2c_reg_setting *cci_i2c_write_cfg_w;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+
+ CDBG("%s: %d Enter\n", __func__, __LINE__);
+
+ write_async = kzalloc(sizeof(*write_async), GFP_KERNEL);
+ if (!write_async) {
+ pr_err("%s: %d Couldn't allocate memory\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ INIT_WORK(&write_async->work, msm_cci_write_async_helper);
+ write_async->cci_dev = cci_dev;
+ write_async->c_ctrl = *c_ctrl;
+ write_async->queue = queue;
+ write_async->sync_en = sync_en;
+
+ cci_i2c_write_cfg = &c_ctrl->cfg.cci_i2c_write_cfg;
+ cci_i2c_write_cfg_w = &write_async->c_ctrl.cfg.cci_i2c_write_cfg;
+
+ if (cci_i2c_write_cfg->size == 0) {
+ pr_err("%s: %d Size = 0\n", __func__, __LINE__);
+ kfree(write_async);
+ return -EINVAL;
+ }
+
+ cci_i2c_write_cfg_w->reg_setting =
+ kzalloc(sizeof(struct msm_camera_i2c_reg_array)*
+ cci_i2c_write_cfg->size, GFP_KERNEL);
+ if (!cci_i2c_write_cfg_w->reg_setting) {
+ pr_err("%s: %d Couldn't allocate memory\n", __func__, __LINE__);
+ kfree(write_async);
+ return -ENOMEM;
+ }
+ memcpy(cci_i2c_write_cfg_w->reg_setting,
+ cci_i2c_write_cfg->reg_setting,
+ (sizeof(struct msm_camera_i2c_reg_array)*
+ cci_i2c_write_cfg->size));
+
+ cci_i2c_write_cfg_w->addr_type = cci_i2c_write_cfg->addr_type;
+ cci_i2c_write_cfg_w->data_type = cci_i2c_write_cfg->data_type;
+ cci_i2c_write_cfg_w->size = cci_i2c_write_cfg->size;
+ cci_i2c_write_cfg_w->delay = cci_i2c_write_cfg->delay;
+
+ queue_work(cci_dev->write_wq[write_async->queue], &write_async->work);
+
+ CDBG("%s: %d Exit\n", __func__, __LINE__);
+
+ return 0;
+}
+
+static int32_t msm_cci_pinctrl_init(struct cci_device *cci_dev)
+{
+ struct msm_pinctrl_info *cci_pctrl = NULL;
+
+ cci_pctrl = &cci_dev->cci_pinctrl;
+ cci_pctrl->pinctrl = devm_pinctrl_get(&cci_dev->pdev->dev);
+ if (IS_ERR_OR_NULL(cci_pctrl->pinctrl)) {
+ pr_err("%s:%d devm_pinctrl_get cci_pinctrl failed\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ cci_pctrl->gpio_state_active = pinctrl_lookup_state(
+ cci_pctrl->pinctrl,
+ CCI_PINCTRL_STATE_DEFAULT);
+ if (IS_ERR_OR_NULL(cci_pctrl->gpio_state_active)) {
+ pr_err("%s:%d look up state for active state failed\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ cci_pctrl->gpio_state_suspend = pinctrl_lookup_state(
+ cci_pctrl->pinctrl,
+ CCI_PINCTRL_STATE_SLEEP);
+ if (IS_ERR_OR_NULL(cci_pctrl->gpio_state_suspend)) {
+ pr_err("%s:%d look up state for suspend state failed\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static uint32_t msm_cci_cycles_per_ms(unsigned long clk)
+{
+ uint32_t cycles_per_us;
+
+ if (clk)
+ cycles_per_us = ((clk/1000)*256)/1000;
+ else {
+ pr_err("%s:%d, failed: Can use default: %d",
+ __func__, __LINE__, CYCLES_PER_MICRO_SEC_DEFAULT);
+ cycles_per_us = CYCLES_PER_MICRO_SEC_DEFAULT;
+ }
+ return cycles_per_us;
+}
+
+static uint32_t *msm_cci_get_clk_rates(struct cci_device *cci_dev,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ uint32_t j;
+ int32_t idx;
+ uint32_t cci_clk_src;
+ unsigned long clk;
+
+ struct msm_cci_clk_params_t *clk_params = NULL;
+ enum i2c_freq_mode_t i2c_freq_mode = c_ctrl->cci_info->i2c_freq_mode;
+ struct device_node *of_node = cci_dev->pdev->dev.of_node;
+ clk_params = &cci_dev->cci_clk_params[i2c_freq_mode];
+ cci_clk_src = clk_params->cci_clk_src;
+
+ idx = of_property_match_string(of_node,
+ "clock-names", CCI_CLK_SRC_NAME);
+ if (idx < 0) {
+ cci_dev->cycles_per_us = CYCLES_PER_MICRO_SEC_DEFAULT;
+ return cci_dev->cci_clk_rates[0];
+ }
+
+ if (cci_clk_src == 0) {
+ clk = cci_dev->cci_clk_rates[0][idx];
+ cci_dev->cycles_per_us = msm_cci_cycles_per_ms(clk);
+ return cci_dev->cci_clk_rates[0];
+ }
+
+ for (j = 0; j < cci_dev->num_clk_cases; j++) {
+ clk = cci_dev->cci_clk_rates[j][idx];
+ if (clk == cci_clk_src) {
+ cci_dev->cycles_per_us = msm_cci_cycles_per_ms(clk);
+ cci_dev->cci_clk_src = cci_clk_src;
+ return cci_dev->cci_clk_rates[j];
+ }
+ }
+
+ return NULL;
+}
+
+static int32_t msm_cci_i2c_set_sync_prms(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ struct cci_device *cci_dev;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (!cci_dev || !c_ctrl) {
+ pr_err("%s:%d failed: invalid params %p %p\n", __func__,
+ __LINE__, cci_dev, c_ctrl);
+ rc = -EINVAL;
+ return rc;
+ }
+ cci_dev->cci_wait_sync_cfg = c_ctrl->cfg.cci_wait_sync_cfg;
+ cci_dev->valid_sync = cci_dev->cci_wait_sync_cfg.csid < 0 ? 0 : 1;
+
+ return rc;
+}
+
+static int32_t msm_cci_init(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ uint8_t i = 0, j = 0;
+ int32_t rc = 0, ret = 0;
+ struct cci_device *cci_dev;
+ enum cci_i2c_master_t master = MASTER_0;
+ uint32_t *clk_rates = NULL;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (!cci_dev || !c_ctrl) {
+ pr_err("%s:%d failed: invalid params %p %p\n", __func__,
+ __LINE__, cci_dev, c_ctrl);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CCI,
+ CAM_AHB_SVS_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ return rc;
+ }
+
+ if (cci_dev->ref_count++) {
+ CDBG("%s ref_count %d\n", __func__, cci_dev->ref_count);
+ master = c_ctrl->cci_info->cci_i2c_master;
+ CDBG("%s:%d master %d\n", __func__, __LINE__, master);
+ if (master < MASTER_MAX && master >= 0) {
+ mutex_lock(&cci_dev->cci_master_info[master].mutex);
+ flush_workqueue(cci_dev->write_wq[master]);
+ /* Re-initialize the completion */
+ reinit_completion(&cci_dev->
+ cci_master_info[master].reset_complete);
+ for (i = 0; i < NUM_QUEUES; i++)
+ reinit_completion(&cci_dev->
+ cci_master_info[master].report_q[i]);
+ /* Set reset pending flag to TRUE */
+ cci_dev->cci_master_info[master].reset_pending = TRUE;
+ /* Set proper mask to RESET CMD address */
+ if (master == MASTER_0)
+ msm_camera_io_w_mb(CCI_M0_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ else
+ msm_camera_io_w_mb(CCI_M1_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ /* wait for reset done irq */
+ rc = wait_for_completion_timeout(
+ &cci_dev->cci_master_info[master].
+ reset_complete,
+ CCI_TIMEOUT);
+ if (rc <= 0)
+ pr_err("%s:%d wait failed %d\n", __func__,
+ __LINE__, rc);
+ mutex_unlock(&cci_dev->cci_master_info[master].mutex);
+ }
+ return 0;
+ }
+ ret = msm_cci_pinctrl_init(cci_dev);
+ if (ret < 0) {
+ pr_err("%s:%d Initialization of pinctrl failed\n",
+ __func__, __LINE__);
+ cci_dev->cci_pinctrl_status = 0;
+ } else {
+ cci_dev->cci_pinctrl_status = 1;
+ }
+ rc = msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
+ cci_dev->cci_gpio_tbl_size, 1);
+ if (cci_dev->cci_pinctrl_status) {
+ ret = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl,
+ cci_dev->cci_pinctrl.gpio_state_active);
+ if (ret)
+ pr_err("%s:%d cannot set pin to active state\n",
+ __func__, __LINE__);
+ }
+ if (rc < 0) {
+ CDBG("%s: request gpio failed\n", __func__);
+ goto request_gpio_failed;
+ }
+
+ rc = msm_camera_config_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg,
+ cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d cci config_vreg failed\n", __func__, __LINE__);
+ goto clk_enable_failed;
+ }
+
+ rc = msm_camera_enable_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg,
+ cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d cci enable_vreg failed\n", __func__, __LINE__);
+ goto reg_enable_failed;
+ }
+
+ clk_rates = msm_cci_get_clk_rates(cci_dev, c_ctrl);
+ if (!clk_rates) {
+ pr_err("%s: clk enable failed\n", __func__);
+ goto reg_enable_failed;
+ }
+
+ for (i = 0; i < cci_dev->num_clk; i++) {
+ cci_dev->cci_clk_info[i].clk_rate =
+ clk_rates[i];
+ }
+ rc = msm_camera_clk_enable(&cci_dev->pdev->dev,
+ cci_dev->cci_clk_info, cci_dev->cci_clk,
+ cci_dev->num_clk, true);
+ if (rc < 0) {
+ pr_err("%s: clk enable failed\n", __func__);
+ goto reg_enable_failed;
+ }
+
+ /* Re-initialize the completion */
+ reinit_completion(&cci_dev->cci_master_info[master].reset_complete);
+ for (i = 0; i < NUM_QUEUES; i++)
+ reinit_completion(&cci_dev->cci_master_info[master].
+ report_q[i]);
+ rc = msm_camera_enable_irq(cci_dev->irq, true);
+ if (rc < 0)
+ pr_err("%s: irq enable failed\n", __func__);
+ cci_dev->hw_version = msm_camera_io_r_mb(cci_dev->base +
+ CCI_HW_VERSION_ADDR);
+ pr_info("%s:%d: hw_version = 0x%x\n", __func__, __LINE__,
+ cci_dev->hw_version);
+ cci_dev->payload_size =
+ MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_10;
+ cci_dev->support_seq_write = 0;
+ if (cci_dev->hw_version >= 0x10020000) {
+ cci_dev->payload_size =
+ MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11;
+ cci_dev->support_seq_write = 1;
+ }
+ for (i = 0; i < NUM_MASTERS; i++) {
+ for (j = 0; j < NUM_QUEUES; j++) {
+ if (j == QUEUE_0) {
+ if (cci_dev->hw_version >= 0x10060000)
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size =
+ CCI_I2C_Q0_SIZE_128W;
+ else
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size =
+ CCI_I2C_QUEUE_0_SIZE;
+ } else {
+ if (cci_dev->hw_version >= 0x10060000)
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size =
+ CCI_I2C_Q1_SIZE_32W;
+ else
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size =
+ CCI_I2C_QUEUE_1_SIZE;
+ }
+ CDBG("CCI Master[%d] :: Q0 size: %d Q1 size: %d\n", i,
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size,
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size);
+ }
+ }
+
+ cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
+ msm_camera_io_w_mb(CCI_RESET_CMD_RMSK, cci_dev->base +
+ CCI_RESET_CMD_ADDR);
+ msm_camera_io_w_mb(0x1, cci_dev->base + CCI_RESET_CMD_ADDR);
+ rc = wait_for_completion_timeout(
+ &cci_dev->cci_master_info[MASTER_0].reset_complete,
+ CCI_TIMEOUT);
+ if (rc <= 0) {
+ pr_err("%s: wait_for_completion_timeout %d\n",
+ __func__, __LINE__);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ goto reset_complete_failed;
+ }
+ for (i = 0; i < MASTER_MAX; i++)
+ cci_dev->i2c_freq_mode[i] = I2C_MAX_MODES;
+ msm_camera_io_w_mb(CCI_IRQ_MASK_0_RMSK,
+ cci_dev->base + CCI_IRQ_MASK_0_ADDR);
+ msm_camera_io_w_mb(CCI_IRQ_MASK_0_RMSK,
+ cci_dev->base + CCI_IRQ_CLEAR_0_ADDR);
+ msm_camera_io_w_mb(0x1, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+
+ for (i = 0; i < MASTER_MAX; i++) {
+ if (!cci_dev->write_wq[i]) {
+ pr_err("Failed to flush write wq\n");
+ rc = -ENOMEM;
+ goto reset_complete_failed;
+ } else {
+ flush_workqueue(cci_dev->write_wq[i]);
+ }
+ }
+ cci_dev->cci_state = CCI_STATE_ENABLED;
+
+ return 0;
+
+reset_complete_failed:
+ msm_camera_enable_irq(cci_dev->irq, false);
+ msm_camera_clk_enable(&cci_dev->pdev->dev, cci_dev->cci_clk_info,
+ cci_dev->cci_clk, cci_dev->num_clk, false);
+reg_enable_failed:
+ msm_camera_config_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg,
+ cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 0);
+clk_enable_failed:
+ if (cci_dev->cci_pinctrl_status) {
+ ret = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl,
+ cci_dev->cci_pinctrl.gpio_state_suspend);
+ if (ret)
+ pr_err("%s:%d cannot set pin to suspend state\n",
+ __func__, __LINE__);
+ }
+ msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
+ cci_dev->cci_gpio_tbl_size, 0);
+request_gpio_failed:
+ cci_dev->ref_count--;
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CCI,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+ return rc;
+}
+
+static int32_t msm_cci_release(struct v4l2_subdev *sd)
+{
+ uint8_t i = 0, rc = 0;
+ struct cci_device *cci_dev;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (!cci_dev->ref_count || cci_dev->cci_state != CCI_STATE_ENABLED) {
+ pr_err("%s invalid ref count %d / cci state %d\n",
+ __func__, cci_dev->ref_count, cci_dev->cci_state);
+ rc = -EINVAL;
+ goto ahb_vote_suspend;
+ }
+ if (--cci_dev->ref_count) {
+ CDBG("%s ref_count Exit %d\n", __func__, cci_dev->ref_count);
+ rc = 0;
+ goto ahb_vote_suspend;
+ }
+ for (i = 0; i < MASTER_MAX; i++)
+ if (cci_dev->write_wq[i])
+ flush_workqueue(cci_dev->write_wq[i]);
+
+ msm_camera_enable_irq(cci_dev->irq, false);
+ msm_camera_clk_enable(&cci_dev->pdev->dev, cci_dev->cci_clk_info,
+ cci_dev->cci_clk, cci_dev->num_clk, false);
+
+ rc = msm_camera_enable_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg,
+ cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 0);
+ if (rc < 0)
+ pr_err("%s:%d cci disable_vreg failed\n", __func__, __LINE__);
+
+ rc = msm_camera_config_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg,
+ cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 0);
+ if (rc < 0)
+ pr_err("%s:%d cci unconfig_vreg failed\n", __func__, __LINE__);
+
+ if (cci_dev->cci_pinctrl_status) {
+ rc = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl,
+ cci_dev->cci_pinctrl.gpio_state_suspend);
+ if (rc)
+ pr_err("%s:%d cannot set pin to active state\n",
+ __func__, __LINE__);
+ }
+ cci_dev->cci_pinctrl_status = 0;
+ msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
+ cci_dev->cci_gpio_tbl_size, 0);
+ for (i = 0; i < MASTER_MAX; i++)
+ cci_dev->i2c_freq_mode[i] = I2C_MAX_MODES;
+ cci_dev->cci_state = CCI_STATE_DISABLED;
+ cci_dev->cycles_per_us = 0;
+ cci_dev->cci_clk_src = 0;
+
+ahb_vote_suspend:
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CCI,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+ return rc;
+}
+
+static int32_t msm_cci_write(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ struct cci_device *cci_dev;
+ enum cci_i2c_master_t master;
+ struct msm_camera_cci_master_info *cci_master_info;
+ uint32_t i;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (!cci_dev || !c_ctrl) {
+ pr_err("%s:%d failed: invalid params %p %p\n", __func__,
+ __LINE__, cci_dev, c_ctrl);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ master = c_ctrl->cci_info->cci_i2c_master;
+ cci_master_info = &cci_dev->cci_master_info[master];
+
+ switch (c_ctrl->cmd) {
+ case MSM_CCI_I2C_WRITE_SYNC_BLOCK:
+ mutex_lock(&cci_master_info->mutex_q[SYNC_QUEUE]);
+ rc = msm_cci_i2c_write(sd, c_ctrl,
+ SYNC_QUEUE, MSM_SYNC_ENABLE);
+ mutex_unlock(&cci_master_info->mutex_q[SYNC_QUEUE]);
+ break;
+ case MSM_CCI_I2C_WRITE_SYNC:
+ rc = msm_cci_i2c_write_async(sd, c_ctrl,
+ SYNC_QUEUE, MSM_SYNC_ENABLE);
+ break;
+ case MSM_CCI_I2C_WRITE:
+ case MSM_CCI_I2C_WRITE_SEQ:
+ for (i = 0; i < NUM_QUEUES; i++) {
+ if (mutex_trylock(&cci_master_info->mutex_q[i])) {
+ rc = msm_cci_i2c_write(sd, c_ctrl, i,
+ MSM_SYNC_DISABLE);
+ mutex_unlock(&cci_master_info->mutex_q[i]);
+ return rc;
+ }
+ }
+ mutex_lock(&cci_master_info->mutex_q[PRIORITY_QUEUE]);
+ rc = msm_cci_i2c_write(sd, c_ctrl,
+ PRIORITY_QUEUE, MSM_SYNC_DISABLE);
+ mutex_unlock(&cci_master_info->mutex_q[PRIORITY_QUEUE]);
+ break;
+ case MSM_CCI_I2C_WRITE_ASYNC:
+ rc = msm_cci_i2c_write_async(sd, c_ctrl,
+ PRIORITY_QUEUE, MSM_SYNC_DISABLE);
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ }
+ return rc;
+}
+
+static int32_t msm_cci_config(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *cci_ctrl)
+{
+ int32_t rc = 0;
+ CDBG("%s line %d cmd %d\n", __func__, __LINE__,
+ cci_ctrl->cmd);
+ switch (cci_ctrl->cmd) {
+ case MSM_CCI_INIT:
+ rc = msm_cci_init(sd, cci_ctrl);
+ break;
+ case MSM_CCI_RELEASE:
+ rc = msm_cci_release(sd);
+ break;
+ case MSM_CCI_I2C_READ:
+ rc = msm_cci_i2c_read_bytes(sd, cci_ctrl);
+ break;
+ case MSM_CCI_I2C_WRITE:
+ case MSM_CCI_I2C_WRITE_SEQ:
+ case MSM_CCI_I2C_WRITE_SYNC:
+ case MSM_CCI_I2C_WRITE_ASYNC:
+ case MSM_CCI_I2C_WRITE_SYNC_BLOCK:
+ rc = msm_cci_write(sd, cci_ctrl);
+ break;
+ case MSM_CCI_GPIO_WRITE:
+ break;
+ case MSM_CCI_SET_SYNC_CID:
+ rc = msm_cci_i2c_set_sync_prms(sd, cci_ctrl);
+ break;
+
+ default:
+ rc = -ENOIOCTLCMD;
+ }
+ CDBG("%s line %d rc %d\n", __func__, __LINE__, rc);
+ cci_ctrl->status = rc;
+ return rc;
+}
+
+static irqreturn_t msm_cci_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ struct cci_device *cci_dev = data;
+ irq = msm_camera_io_r_mb(cci_dev->base + CCI_IRQ_STATUS_0_ADDR);
+ msm_camera_io_w_mb(irq, cci_dev->base + CCI_IRQ_CLEAR_0_ADDR);
+ msm_camera_io_w_mb(0x1, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+ CDBG("%s CCI_I2C_M0_STATUS_ADDR = 0x%x\n", __func__, irq);
+ if (irq & CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK) {
+ if (cci_dev->cci_master_info[MASTER_0].reset_pending == TRUE) {
+ cci_dev->cci_master_info[MASTER_0].reset_pending =
+ FALSE;
+ complete(&cci_dev->cci_master_info[MASTER_0].
+ reset_complete);
+ }
+ if (cci_dev->cci_master_info[MASTER_1].reset_pending == TRUE) {
+ cci_dev->cci_master_info[MASTER_1].reset_pending =
+ FALSE;
+ complete(&cci_dev->cci_master_info[MASTER_1].
+ reset_complete);
+ }
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK) {
+ cci_dev->cci_master_info[MASTER_0].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_0].reset_complete);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) {
+ struct msm_camera_cci_master_info *cci_master_info;
+ cci_master_info = &cci_dev->cci_master_info[MASTER_0];
+ atomic_set(&cci_master_info->q_free[QUEUE_0], 0);
+ cci_master_info->status = 0;
+ if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) {
+ complete(&cci_master_info->report_q[QUEUE_0]);
+ atomic_set(&cci_master_info->done_pending[QUEUE_0], 0);
+ }
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK) {
+ struct msm_camera_cci_master_info *cci_master_info;
+ cci_master_info = &cci_dev->cci_master_info[MASTER_0];
+ atomic_set(&cci_master_info->q_free[QUEUE_1], 0);
+ cci_master_info->status = 0;
+ if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) {
+ complete(&cci_master_info->report_q[QUEUE_1]);
+ atomic_set(&cci_master_info->done_pending[QUEUE_1], 0);
+ }
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) {
+ cci_dev->cci_master_info[MASTER_1].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_1].reset_complete);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) {
+ struct msm_camera_cci_master_info *cci_master_info;
+ cci_master_info = &cci_dev->cci_master_info[MASTER_1];
+ atomic_set(&cci_master_info->q_free[QUEUE_0], 0);
+ cci_master_info->status = 0;
+ if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) {
+ complete(&cci_master_info->report_q[QUEUE_0]);
+ atomic_set(&cci_master_info->done_pending[QUEUE_0], 0);
+ }
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK) {
+ struct msm_camera_cci_master_info *cci_master_info;
+ cci_master_info = &cci_dev->cci_master_info[MASTER_1];
+ atomic_set(&cci_master_info->q_free[QUEUE_1], 0);
+ cci_master_info->status = 0;
+ if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) {
+ complete(&cci_master_info->report_q[QUEUE_1]);
+ atomic_set(&cci_master_info->done_pending[QUEUE_1], 0);
+ }
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) {
+ cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
+ msm_camera_io_w_mb(CCI_M0_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK) {
+ cci_dev->cci_master_info[MASTER_1].reset_pending = TRUE;
+ msm_camera_io_w_mb(CCI_M1_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK) {
+ pr_err("%s:%d MASTER_0 error 0x%x\n", __func__, __LINE__, irq);
+ cci_dev->cci_master_info[MASTER_0].status = -EINVAL;
+ msm_camera_io_w_mb(CCI_M0_HALT_REQ_RMSK,
+ cci_dev->base + CCI_HALT_REQ_ADDR);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK) {
+ pr_err("%s:%d MASTER_1 error 0x%x\n", __func__, __LINE__, irq);
+ cci_dev->cci_master_info[MASTER_1].status = -EINVAL;
+ msm_camera_io_w_mb(CCI_M1_HALT_REQ_RMSK,
+ cci_dev->base + CCI_HALT_REQ_ADDR);
+ }
+ return IRQ_HANDLED;
+}
+
+static int msm_cci_irq_routine(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct cci_device *cci_dev = v4l2_get_subdevdata(sd);
+ irqreturn_t ret;
+ CDBG("%s line %d\n", __func__, __LINE__);
+ ret = msm_cci_irq(cci_dev->irq->start, cci_dev);
+ CDBG("%s: msm_cci_irq return %d\n", __func__, ret);
+ *handled = TRUE;
+ return 0;
+}
+
+static long msm_cci_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int32_t rc = 0;
+ CDBG("%s line %d\n", __func__, __LINE__);
+ switch (cmd) {
+ case VIDIOC_MSM_CCI_CFG:
+ rc = msm_cci_config(sd, arg);
+ break;
+ case MSM_SD_NOTIFY_FREEZE:
+ break;
+ case MSM_SD_UNNOTIFY_FREEZE:
+ break;
+ case MSM_SD_SHUTDOWN: {
+ struct msm_camera_cci_ctrl ctrl_cmd;
+ ctrl_cmd.cmd = MSM_CCI_RELEASE;
+ rc = msm_cci_config(sd, &ctrl_cmd);
+ break;
+ }
+ default:
+ rc = -ENOIOCTLCMD;
+ }
+ CDBG("%s line %d rc %d\n", __func__, __LINE__, rc);
+ return rc;
+}
+
+static struct v4l2_subdev_core_ops msm_cci_subdev_core_ops = {
+ .ioctl = &msm_cci_subdev_ioctl,
+ .interrupt_service_routine = msm_cci_irq_routine,
+};
+
+static const struct v4l2_subdev_ops msm_cci_subdev_ops = {
+ .core = &msm_cci_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_cci_internal_ops;
+
+static void msm_cci_init_cci_params(struct cci_device *new_cci_dev)
+{
+ uint8_t i = 0, j = 0;
+ for (i = 0; i < NUM_MASTERS; i++) {
+ new_cci_dev->cci_master_info[i].status = 0;
+ mutex_init(&new_cci_dev->cci_master_info[i].mutex);
+ init_completion(&new_cci_dev->
+ cci_master_info[i].reset_complete);
+
+ for (j = 0; j < NUM_QUEUES; j++) {
+ mutex_init(&new_cci_dev->cci_master_info[i].mutex_q[j]);
+ init_completion(&new_cci_dev->
+ cci_master_info[i].report_q[j]);
+ }
+ }
+ return;
+}
+
+static int32_t msm_cci_init_gpio_params(struct cci_device *cci_dev)
+{
+ int32_t rc = 0, i = 0;
+ uint32_t *val_array = NULL;
+ uint8_t tbl_size = 0;
+ struct device_node *of_node = cci_dev->pdev->dev.of_node;
+ struct gpio *gpio_tbl = NULL;
+
+ cci_dev->cci_gpio_tbl_size = tbl_size = of_gpio_count(of_node);
+ CDBG("%s gpio count %d\n", __func__, tbl_size);
+ if (!tbl_size) {
+ pr_err("%s:%d gpio count 0\n", __func__, __LINE__);
+ return 0;
+ }
+
+ gpio_tbl = cci_dev->cci_gpio_tbl =
+ kzalloc(sizeof(struct gpio) * tbl_size, GFP_KERNEL);
+ if (!gpio_tbl) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return 0;
+ }
+
+ for (i = 0; i < tbl_size; i++) {
+ gpio_tbl[i].gpio = of_get_gpio(of_node, i);
+ CDBG("%s gpio_tbl[%d].gpio = %d\n", __func__, i,
+ gpio_tbl[i].gpio);
+ }
+
+ val_array = kzalloc(sizeof(uint32_t) * tbl_size, GFP_KERNEL);
+ if (!val_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR1;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,gpio-tbl-flags",
+ val_array, tbl_size);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < tbl_size; i++) {
+ gpio_tbl[i].flags = val_array[i];
+ CDBG("%s gpio_tbl[%d].flags = %ld\n", __func__, i,
+ gpio_tbl[i].flags);
+ }
+
+ for (i = 0; i < tbl_size; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,gpio-tbl-label", i, &gpio_tbl[i].label);
+ CDBG("%s gpio_tbl[%d].label = %s\n", __func__, i,
+ gpio_tbl[i].label);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ }
+
+ kfree(val_array);
+ return rc;
+
+ERROR2:
+ kfree(val_array);
+ERROR1:
+ kfree(cci_dev->cci_gpio_tbl);
+ cci_dev->cci_gpio_tbl = NULL;
+ cci_dev->cci_gpio_tbl_size = 0;
+ return rc;
+}
+
+static void msm_cci_init_default_clk_params(struct cci_device *cci_dev,
+ uint8_t index)
+{
+ /* default clock params are for 100Khz */
+ cci_dev->cci_clk_params[index].hw_thigh = 201;
+ cci_dev->cci_clk_params[index].hw_tlow = 174;
+ cci_dev->cci_clk_params[index].hw_tsu_sto = 204;
+ cci_dev->cci_clk_params[index].hw_tsu_sta = 231;
+ cci_dev->cci_clk_params[index].hw_thd_dat = 22;
+ cci_dev->cci_clk_params[index].hw_thd_sta = 162;
+ cci_dev->cci_clk_params[index].hw_tbuf = 227;
+ cci_dev->cci_clk_params[index].hw_scl_stretch_en = 0;
+ cci_dev->cci_clk_params[index].hw_trdhld = 6;
+ cci_dev->cci_clk_params[index].hw_tsp = 3;
+ cci_dev->cci_clk_params[index].cci_clk_src = 37500000;
+}
+
+static void msm_cci_init_clk_params(struct cci_device *cci_dev)
+{
+ int32_t rc = 0;
+ uint32_t val = 0;
+ uint8_t count = 0;
+ struct device_node *of_node = cci_dev->pdev->dev.of_node;
+ struct device_node *src_node = NULL;
+
+ for (count = 0; count < I2C_MAX_MODES; count++) {
+
+ if (I2C_STANDARD_MODE == count)
+ src_node = of_find_node_by_name(of_node,
+ "qcom,i2c_standard_mode");
+ else if (I2C_FAST_MODE == count)
+ src_node = of_find_node_by_name(of_node,
+ "qcom,i2c_fast_mode");
+ else if (I2C_FAST_PLUS_MODE == count)
+ src_node = of_find_node_by_name(of_node,
+ "qcom,i2c_fast_plus_mode");
+ else
+ src_node = of_find_node_by_name(of_node,
+ "qcom,i2c_custom_mode");
+
+ rc = of_property_read_u32(src_node, "qcom,hw-thigh", &val);
+ CDBG("%s qcom,hw-thigh %d, rc %d\n", __func__, val, rc);
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_thigh = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tlow",
+ &val);
+ CDBG("%s qcom,hw-tlow %d, rc %d\n", __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tlow = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tsu-sto",
+ &val);
+ CDBG("%s qcom,hw-tsu-sto %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tsu_sto = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tsu-sta",
+ &val);
+ CDBG("%s qcom,hw-tsu-sta %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tsu_sta = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-thd-dat",
+ &val);
+ CDBG("%s qcom,hw-thd-dat %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_thd_dat = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-thd-sta",
+ &val);
+ CDBG("%s qcom,hw-thd-sta %d, rc %d\n", __func__,
+ val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_thd_sta = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tbuf",
+ &val);
+ CDBG("%s qcom,hw-tbuf %d, rc %d\n", __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tbuf = val;
+ rc = of_property_read_u32(src_node,
+ "qcom,hw-scl-stretch-en", &val);
+ CDBG("%s qcom,hw-scl-stretch-en %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_scl_stretch_en = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-trdhld",
+ &val);
+ CDBG("%s qcom,hw-trdhld %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_trdhld = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tsp",
+ &val);
+ CDBG("%s qcom,hw-tsp %d, rc %d\n", __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tsp = val;
+ val = 0;
+ rc = of_property_read_u32(src_node, "qcom,cci-clk-src",
+ &val);
+ CDBG("%s qcom,cci-clk-src %d, rc %d\n",
+ __func__, val, rc);
+ cci_dev->cci_clk_params[count].cci_clk_src = val;
+ }
+ else
+ msm_cci_init_default_clk_params(cci_dev, count);
+
+
+
+ of_node_put(src_node);
+ src_node = NULL;
+ }
+ return;
+}
+
+struct v4l2_subdev *msm_cci_get_subdev(void)
+{
+ return g_cci_subdev;
+}
+
+static int msm_cci_probe(struct platform_device *pdev)
+{
+ struct cci_device *new_cci_dev;
+ int rc = 0, i = 0;
+ CDBG("%s: pdev %p device id = %d\n", __func__, pdev, pdev->id);
+ new_cci_dev = kzalloc(sizeof(struct cci_device), GFP_KERNEL);
+ if (!new_cci_dev) {
+ pr_err("%s: no enough memory\n", __func__);
+ return -ENOMEM;
+ }
+ v4l2_subdev_init(&new_cci_dev->msm_sd.sd, &msm_cci_subdev_ops);
+ new_cci_dev->msm_sd.sd.internal_ops = &msm_cci_internal_ops;
+ snprintf(new_cci_dev->msm_sd.sd.name,
+ ARRAY_SIZE(new_cci_dev->msm_sd.sd.name), "msm_cci");
+ v4l2_set_subdevdata(&new_cci_dev->msm_sd.sd, new_cci_dev);
+ platform_set_drvdata(pdev, &new_cci_dev->msm_sd.sd);
+ CDBG("%s sd %p\n", __func__, &new_cci_dev->msm_sd.sd);
+ if (pdev->dev.of_node)
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+
+ rc = msm_camera_get_clk_info_and_rates(pdev,
+ &new_cci_dev->cci_clk_info, &new_cci_dev->cci_clk,
+ &new_cci_dev->cci_clk_rates, &new_cci_dev->num_clk_cases,
+ &new_cci_dev->num_clk);
+ if (rc < 0) {
+ pr_err("%s: msm_cci_get_clk_info() failed", __func__);
+ kfree(new_cci_dev);
+ return -EFAULT;
+ }
+
+ new_cci_dev->ref_count = 0;
+ new_cci_dev->base = msm_camera_get_reg_base(pdev, "cci", true);
+ if (!new_cci_dev->base) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto cci_no_resource;
+ }
+ new_cci_dev->irq = msm_camera_get_irq(pdev, "cci");
+ if (!new_cci_dev->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto cci_no_resource;
+ }
+ CDBG("%s line %d cci irq start %d end %d\n", __func__,
+ __LINE__,
+ (int) new_cci_dev->irq->start,
+ (int) new_cci_dev->irq->end);
+ rc = msm_camera_register_irq(pdev, new_cci_dev->irq,
+ msm_cci_irq, IRQF_TRIGGER_RISING, "cci", new_cci_dev);
+ if (rc < 0) {
+ pr_err("%s: irq request fail\n", __func__);
+ rc = -EBUSY;
+ goto cci_release_mem;
+ }
+
+ msm_camera_enable_irq(new_cci_dev->irq, false);
+ new_cci_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x6;
+ msm_sd_register(&new_cci_dev->msm_sd);
+ new_cci_dev->pdev = pdev;
+ msm_cci_init_cci_params(new_cci_dev);
+ msm_cci_init_clk_params(new_cci_dev);
+ msm_cci_init_gpio_params(new_cci_dev);
+
+ rc = msm_camera_get_dt_vreg_data(new_cci_dev->pdev->dev.of_node,
+ &(new_cci_dev->cci_vreg), &(new_cci_dev->regulator_count));
+ if (rc < 0) {
+ pr_err("%s: msm_camera_get_dt_vreg_data fail\n", __func__);
+ rc = -EFAULT;
+ goto cci_release_mem;
+ }
+
+ if ((new_cci_dev->regulator_count < 0) ||
+ (new_cci_dev->regulator_count > MAX_REGULATOR)) {
+ pr_err("%s: invalid reg count = %d, max is %d\n", __func__,
+ new_cci_dev->regulator_count, MAX_REGULATOR);
+ rc = -EFAULT;
+ goto cci_invalid_vreg_data;
+ }
+
+ rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (rc)
+ pr_err("%s: failed to add child nodes, rc=%d\n", __func__, rc);
+ new_cci_dev->cci_state = CCI_STATE_DISABLED;
+ g_cci_subdev = &new_cci_dev->msm_sd.sd;
+ for (i = 0; i < MASTER_MAX; i++) {
+ new_cci_dev->write_wq[i] = create_singlethread_workqueue(
+ "msm_cci_wq");
+ if (!new_cci_dev->write_wq[i])
+ pr_err("Failed to create write wq\n");
+ }
+ CDBG("%s cci subdev %p\n", __func__, &new_cci_dev->msm_sd.sd);
+ CDBG("%s line %d\n", __func__, __LINE__);
+ return 0;
+
+cci_invalid_vreg_data:
+ kfree(new_cci_dev->cci_vreg);
+cci_release_mem:
+ msm_camera_put_reg_base(pdev, new_cci_dev->base, "cci", true);
+cci_no_resource:
+ kfree(new_cci_dev);
+ return rc;
+}
+
+static int msm_cci_exit(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+ struct cci_device *cci_dev =
+ v4l2_get_subdevdata(subdev);
+
+ msm_camera_put_clk_info_and_rates(pdev,
+ &cci_dev->cci_clk_info, &cci_dev->cci_clk,
+ &cci_dev->cci_clk_rates, cci_dev->num_clk_cases,
+ cci_dev->num_clk);
+
+ msm_camera_put_reg_base(pdev, cci_dev->base, "cci", true);
+ kfree(cci_dev);
+ return 0;
+}
+
+static const struct of_device_id msm_cci_dt_match[] = {
+ {.compatible = "qcom,cci"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_cci_dt_match);
+
+static struct platform_driver cci_driver = {
+ .probe = msm_cci_probe,
+ .remove = msm_cci_exit,
+ .driver = {
+ .name = MSM_CCI_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_cci_dt_match,
+ },
+};
+
+static int __init msm_cci_init_module(void)
+{
+ return platform_driver_register(&cci_driver);
+}
+
+static void __exit msm_cci_exit_module(void)
+{
+ platform_driver_unregister(&cci_driver);
+}
+
+module_init(msm_cci_init_module);
+module_exit(msm_cci_exit_module);
+MODULE_DESCRIPTION("MSM CCI driver");
+MODULE_LICENSE("GPL v2");