diff options
| author | wzedlare <vedatak01@gmail.com> | 2017-06-18 16:38:26 +0000 |
|---|---|---|
| committer | wzedlare <vedatak01@gmail.com> | 2017-06-19 16:57:11 +0000 |
| commit | c7d4e3fd588e3ba3d3fa4d5cfa224aa54bc288bf (patch) | |
| tree | b8b64cb9deb6832c1e41f58f0f143514beafc709 /camera/sensor/io | |
| parent | 28c99c87b881bb664c44bb26e80a681f87d54e60 (diff) | |
Change-Id: Ia4c94f09e29843b1af34d466243378a357e97b70
Diffstat (limited to 'camera/sensor/io')
| -rw-r--r-- | camera/sensor/io/Makefile | 5 | ||||
| -rw-r--r-- | camera/sensor/io/msm_camera_cci_i2c.c | 561 | ||||
| -rw-r--r-- | camera/sensor/io/msm_camera_dt_util.c | 1667 | ||||
| -rw-r--r-- | camera/sensor/io/msm_camera_dt_util.h | 68 | ||||
| -rw-r--r-- | camera/sensor/io/msm_camera_i2c.h | 155 | ||||
| -rw-r--r-- | camera/sensor/io/msm_camera_i2c_mux.c | 180 | ||||
| -rw-r--r-- | camera/sensor/io/msm_camera_i2c_mux.h | 42 | ||||
| -rw-r--r-- | camera/sensor/io/msm_camera_qup_i2c.c | 557 | ||||
| -rw-r--r-- | camera/sensor/io/msm_camera_spi.c | 836 | ||||
| -rw-r--r-- | camera/sensor/io/msm_camera_spi.h | 120 |
10 files changed, 4191 insertions, 0 deletions
diff --git a/camera/sensor/io/Makefile b/camera/sensor/io/Makefile new file mode 100644 index 00000000..437fd965 --- /dev/null +++ b/camera/sensor/io/Makefile @@ -0,0 +1,5 @@ +ccflags-y += -Icamera/ +ccflags-y += -Icamera/common +ccflags-y += -Icamera/sensor +ccflags-y += -Icamera/sensor/cci +obj-$(CONFIG_MSMB_CAMERA) += msm_camera_cci_i2c.o msm_camera_qup_i2c.o msm_camera_spi.o msm_camera_dt_util.o diff --git a/camera/sensor/io/msm_camera_cci_i2c.c b/camera/sensor/io/msm_camera_cci_i2c.c new file mode 100644 index 00000000..6cfadf0c --- /dev/null +++ b/camera/sensor/io/msm_camera_cci_i2c.c @@ -0,0 +1,561 @@ +/* Copyright (c) 2011-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 <lenovo_soc/qcom/camera2.h> +#include "msm_camera_i2c.h" +#include "msm_cci.h" + +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) +#define S_I2C_DBG(fmt, args...) pr_debug(fmt, ##args) + +int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t *data, + enum msm_camera_i2c_data_type data_type) +{ + int32_t rc = -EFAULT; + unsigned char buf[client->addr_type+data_type]; + struct msm_camera_cci_ctrl cci_ctrl; + + if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR + && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR) + || (data_type != MSM_CAMERA_I2C_BYTE_DATA + && data_type != MSM_CAMERA_I2C_WORD_DATA)) + return rc; + + cci_ctrl.cmd = MSM_CCI_I2C_READ; + cci_ctrl.cci_info = client->cci_client; + cci_ctrl.cfg.cci_i2c_read_cfg.addr = addr; + cci_ctrl.cfg.cci_i2c_read_cfg.addr_type = client->addr_type; + cci_ctrl.cfg.cci_i2c_read_cfg.data = buf; + cci_ctrl.cfg.cci_i2c_read_cfg.num_byte = data_type; + rc = v4l2_subdev_call(client->cci_client->cci_subdev, + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + if (rc < 0) { + pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc); + return rc; + } + rc = cci_ctrl.status; + if (data_type == MSM_CAMERA_I2C_BYTE_DATA) + *data = buf[0]; + else + *data = buf[0] << 8 | buf[1]; + + S_I2C_DBG("%s addr = 0x%x data: 0x%x\n", __func__, addr, *data); + return rc; +} + +int32_t msm_camera_cci_i2c_read_seq(struct msm_camera_i2c_client *client, + uint32_t addr, uint8_t *data, uint32_t num_byte) +{ + int32_t rc = -EFAULT; + unsigned char *buf = NULL; + int i; + struct msm_camera_cci_ctrl cci_ctrl; + + if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR + && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR) + || num_byte == 0) + return rc; + + buf = kzalloc(num_byte, GFP_KERNEL); + if (!buf) { + pr_err("%s:%d no memory\n", __func__, __LINE__); + return -ENOMEM; + } + cci_ctrl.cmd = MSM_CCI_I2C_READ; + cci_ctrl.cci_info = client->cci_client; + cci_ctrl.cfg.cci_i2c_read_cfg.addr = addr; + cci_ctrl.cfg.cci_i2c_read_cfg.addr_type = client->addr_type; + cci_ctrl.cfg.cci_i2c_read_cfg.data = buf; + cci_ctrl.cfg.cci_i2c_read_cfg.num_byte = num_byte; + cci_ctrl.status = -EFAULT; + rc = v4l2_subdev_call(client->cci_client->cci_subdev, + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + CDBG("%s line %d rc = %d\n", __func__, __LINE__, rc); + rc = cci_ctrl.status; + + S_I2C_DBG("%s addr = 0x%x", __func__, addr); + for (i = 0; i < num_byte; i++) { + data[i] = buf[i]; + S_I2C_DBG("Byte %d: 0x%x\n", i, buf[i]); + S_I2C_DBG("Data: 0x%x\n", data[i]); + } + kfree(buf); + return rc; +} + +int32_t msm_camera_cci_i2c_write(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t data, + enum msm_camera_i2c_data_type data_type) +{ + int32_t rc = -EFAULT; + struct msm_camera_cci_ctrl cci_ctrl; + struct msm_camera_i2c_reg_array reg_conf_tbl; + + if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR + && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR) + || (data_type != MSM_CAMERA_I2C_BYTE_DATA + && data_type != MSM_CAMERA_I2C_WORD_DATA)) + return rc; + + CDBG("%s:%d reg addr = 0x%x data type: %d\n", + __func__, __LINE__, addr, data_type); + reg_conf_tbl.reg_addr = addr; + reg_conf_tbl.reg_data = data; + reg_conf_tbl.delay = 0; + cci_ctrl.cmd = MSM_CCI_I2C_WRITE; + cci_ctrl.cci_info = client->cci_client; + cci_ctrl.cfg.cci_i2c_write_cfg.reg_setting = ®_conf_tbl; + cci_ctrl.cfg.cci_i2c_write_cfg.data_type = data_type; + cci_ctrl.cfg.cci_i2c_write_cfg.addr_type = client->addr_type; + cci_ctrl.cfg.cci_i2c_write_cfg.size = 1; + rc = v4l2_subdev_call(client->cci_client->cci_subdev, + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + if (rc < 0) { + pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc); + return rc; + } + rc = cci_ctrl.status; + return rc; +} + +int32_t msm_camera_cci_i2c_write_seq(struct msm_camera_i2c_client *client, + uint32_t addr, uint8_t *data, uint32_t num_byte) +{ + int32_t rc = -EFAULT; + uint32_t i = 0; + struct msm_camera_cci_ctrl cci_ctrl; + struct msm_camera_i2c_reg_array *reg_conf_tbl = NULL; + + if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR + && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR) + || num_byte == 0) + return rc; + + if (num_byte > I2C_SEQ_REG_DATA_MAX) { + pr_err("%s: num_byte=%d clamped to max supported %d\n", + __func__, num_byte, I2C_SEQ_REG_DATA_MAX); + return rc; + } + + S_I2C_DBG("%s reg addr = 0x%x num bytes: %d\n", + __func__, addr, num_byte); + + reg_conf_tbl = kzalloc(num_byte * + (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL); + if (!reg_conf_tbl) { + pr_err("%s:%d no memory\n", __func__, __LINE__); + return -ENOMEM; + } + + reg_conf_tbl[0].reg_addr = addr; + for (i = 0; i < num_byte; i++) { + reg_conf_tbl[i].reg_data = data[i]; + reg_conf_tbl[i].delay = 0; + } + cci_ctrl.cmd = MSM_CCI_I2C_WRITE_SEQ; + cci_ctrl.cci_info = client->cci_client; + cci_ctrl.cfg.cci_i2c_write_cfg.reg_setting = reg_conf_tbl; + cci_ctrl.cfg.cci_i2c_write_cfg.data_type = MSM_CAMERA_I2C_BYTE_DATA; + cci_ctrl.cfg.cci_i2c_write_cfg.addr_type = client->addr_type; + cci_ctrl.cfg.cci_i2c_write_cfg.size = num_byte; + cci_ctrl.status = -EFAULT; + rc = v4l2_subdev_call(client->cci_client->cci_subdev, + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + CDBG("%s line %d rc = %d\n", __func__, __LINE__, rc); + rc = cci_ctrl.status; + kfree(reg_conf_tbl); + reg_conf_tbl = NULL; + return rc; +} + +static int32_t msm_camera_cci_i2c_write_table_cmd( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_setting *write_setting, + enum msm_cci_cmd_type cmd) +{ + int32_t rc = -EFAULT; + struct msm_camera_cci_ctrl cci_ctrl; + + 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; + + cci_ctrl.cmd = cmd; + cci_ctrl.cci_info = client->cci_client; + cci_ctrl.cfg.cci_i2c_write_cfg.reg_setting = + write_setting->reg_setting; + cci_ctrl.cfg.cci_i2c_write_cfg.data_type = write_setting->data_type; + cci_ctrl.cfg.cci_i2c_write_cfg.addr_type = client->addr_type; + cci_ctrl.cfg.cci_i2c_write_cfg.size = write_setting->size; + rc = v4l2_subdev_call(client->cci_client->cci_subdev, + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + if (rc < 0) { + pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc); + return rc; + } + rc = cci_ctrl.status; + 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); + + return rc; +} + +int32_t msm_camera_cci_i2c_write_table_async( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_setting *write_setting) +{ + return msm_camera_cci_i2c_write_table_cmd(client, write_setting, + MSM_CCI_I2C_WRITE_ASYNC); +} + +int32_t msm_camera_cci_i2c_write_table_sync( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_setting *write_setting) +{ + return msm_camera_cci_i2c_write_table_cmd(client, write_setting, + MSM_CCI_I2C_WRITE_SYNC); +} + +int32_t msm_camera_cci_i2c_write_table_sync_block( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_setting *write_setting) +{ + return msm_camera_cci_i2c_write_table_cmd(client, write_setting, + MSM_CCI_I2C_WRITE_SYNC_BLOCK); +} + +int32_t msm_camera_cci_i2c_write_table( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_setting *write_setting) +{ + return msm_camera_cci_i2c_write_table_cmd(client, write_setting, + MSM_CCI_I2C_WRITE); +} + +int32_t msm_camera_cci_i2c_write_seq_table( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_seq_reg_setting *write_setting) +{ + int i; + int32_t rc = -EFAULT; + struct msm_camera_i2c_seq_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)) { + pr_err("%s Invalide addr type %d\n", __func__, + write_setting->addr_type); + 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++) { + rc = msm_camera_cci_i2c_write_seq(client, reg_setting->reg_addr, + reg_setting->reg_data, reg_setting->reg_data_size); + if (rc < 0) + return rc; + 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; +} + +int32_t msm_camera_cci_i2c_write_table_w_microdelay( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_setting *write_setting) +{ + int32_t rc = -EFAULT; + struct msm_camera_cci_ctrl cci_ctrl; + + if (!client || !write_setting) + return rc; + + if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR + && client->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; + + cci_ctrl.cmd = MSM_CCI_I2C_WRITE; + cci_ctrl.cci_info = client->cci_client; + cci_ctrl.cfg.cci_i2c_write_cfg.reg_setting = + write_setting->reg_setting; + cci_ctrl.cfg.cci_i2c_write_cfg.data_type = write_setting->data_type; + cci_ctrl.cfg.cci_i2c_write_cfg.addr_type = client->addr_type; + cci_ctrl.cfg.cci_i2c_write_cfg.size = write_setting->size; + rc = v4l2_subdev_call(client->cci_client->cci_subdev, + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + if (rc < 0) { + pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc); + return rc; + } + rc = cci_ctrl.status; + return rc; +} + +static int32_t msm_camera_cci_i2c_compare(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t data, + enum msm_camera_i2c_data_type data_type) +{ + int32_t rc; + uint16_t reg_data = 0; + int data_len = 0; + switch (data_type) { + case MSM_CAMERA_I2C_BYTE_DATA: + case MSM_CAMERA_I2C_WORD_DATA: + data_len = data_type; + break; + case MSM_CAMERA_I2C_SET_BYTE_MASK: + case MSM_CAMERA_I2C_UNSET_BYTE_MASK: + data_len = MSM_CAMERA_I2C_BYTE_DATA; + break; + case MSM_CAMERA_I2C_SET_WORD_MASK: + case MSM_CAMERA_I2C_UNSET_WORD_MASK: + data_len = MSM_CAMERA_I2C_WORD_DATA; + break; + default: + pr_err("%s: Unsupport data type: %d\n", __func__, data_type); + break; + } + + rc = msm_camera_cci_i2c_read(client, addr, ®_data, data_len); + if (rc < 0) + return rc; + + rc = I2C_COMPARE_MISMATCH; + switch (data_type) { + case MSM_CAMERA_I2C_BYTE_DATA: + case MSM_CAMERA_I2C_WORD_DATA: + if (data == reg_data) + rc = I2C_COMPARE_MATCH; + break; + case MSM_CAMERA_I2C_SET_BYTE_MASK: + case MSM_CAMERA_I2C_SET_WORD_MASK: + if ((reg_data & data) == data) + rc = I2C_COMPARE_MATCH; + break; + case MSM_CAMERA_I2C_UNSET_BYTE_MASK: + case MSM_CAMERA_I2C_UNSET_WORD_MASK: + if (!(reg_data & data)) + rc = I2C_COMPARE_MATCH; + break; + default: + pr_err("%s: Unsupport data type: %d\n", __func__, data_type); + break; + } + + S_I2C_DBG("%s: Register and data match result %d\n", __func__, + rc); + return rc; +} + +int32_t msm_camera_cci_i2c_poll(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t data, + enum msm_camera_i2c_data_type data_type, uint32_t delay_ms) +{ + int32_t rc = -EFAULT; + int32_t i = 0; + S_I2C_DBG("%s: addr: 0x%x data: 0x%x dt: %d\n", + __func__, addr, data, data_type); + + if (delay_ms > MAX_POLL_DELAY_MS) { + pr_err("%s:%d invalid delay = %d max_delay = %d\n", + __func__, __LINE__, delay_ms, MAX_POLL_DELAY_MS); + return -EINVAL; + } + for (i = 0; i < delay_ms; i++) { + rc = msm_camera_cci_i2c_compare(client, + addr, data, data_type); + if (!rc) + return rc; + usleep_range(1000, 1010); + } + + /* If rc is 1 then read is successful but poll is failure */ + if (rc == 1) + pr_err("%s:%d poll failed rc=%d(non-fatal)\n", + __func__, __LINE__, rc); + + if (rc < 0) + pr_err("%s:%d poll failed rc=%d\n", __func__, __LINE__, rc); + + return rc; +} + +static int32_t msm_camera_cci_i2c_set_mask(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t mask, + enum msm_camera_i2c_data_type data_type, uint16_t set_mask) +{ + int32_t rc = -EFAULT; + uint16_t reg_data; + + rc = msm_camera_cci_i2c_read(client, addr, ®_data, data_type); + if (rc < 0) { + S_I2C_DBG("%s read fail\n", __func__); + return rc; + } + S_I2C_DBG("%s addr: 0x%x data: 0x%x setmask: 0x%x\n", + __func__, addr, reg_data, mask); + + if (set_mask) + reg_data |= mask; + else + reg_data &= ~mask; + S_I2C_DBG("%s write: 0x%x\n", __func__, reg_data); + + rc = msm_camera_cci_i2c_write(client, addr, reg_data, data_type); + if (rc < 0) + S_I2C_DBG("%s write fail\n", __func__); + + return rc; +} + +static int32_t msm_camera_cci_i2c_set_write_mask_data( + struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t data, int16_t mask, + enum msm_camera_i2c_data_type data_type) +{ + int32_t rc; + uint16_t reg_data; + CDBG("%s\n", __func__); + if (mask == -1) + return 0; + if (mask == 0) { + rc = msm_camera_cci_i2c_write(client, addr, data, data_type); + } else { + rc = msm_camera_cci_i2c_read(client, addr, ®_data, + data_type); + if (rc < 0) { + CDBG("%s read fail\n", __func__); + return rc; + } + reg_data &= ~mask; + reg_data |= (data & mask); + rc = msm_camera_cci_i2c_write(client, addr, reg_data, + data_type); + if (rc < 0) + CDBG("%s write fail\n", __func__); + } + return rc; +} + +int32_t msm_camera_cci_i2c_write_conf_tbl( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size, + enum msm_camera_i2c_data_type data_type) +{ + int i; + int32_t rc = -EFAULT; + for (i = 0; i < size; i++) { + enum msm_camera_i2c_data_type dt; + if (reg_conf_tbl->cmd_type == MSM_CAMERA_I2C_CMD_POLL) { + rc = msm_camera_cci_i2c_poll(client, + reg_conf_tbl->reg_addr, + reg_conf_tbl->reg_data, + reg_conf_tbl->dt, I2C_POLL_TIME_MS); + } else { + if (reg_conf_tbl->dt == 0) + dt = data_type; + else + dt = reg_conf_tbl->dt; + switch (dt) { + case MSM_CAMERA_I2C_BYTE_DATA: + case MSM_CAMERA_I2C_WORD_DATA: + rc = msm_camera_cci_i2c_write( + client, + reg_conf_tbl->reg_addr, + reg_conf_tbl->reg_data, dt); + break; + case MSM_CAMERA_I2C_SET_BYTE_MASK: + rc = msm_camera_cci_i2c_set_mask(client, + reg_conf_tbl->reg_addr, + reg_conf_tbl->reg_data, + MSM_CAMERA_I2C_BYTE_DATA, 1); + break; + case MSM_CAMERA_I2C_UNSET_BYTE_MASK: + rc = msm_camera_cci_i2c_set_mask(client, + reg_conf_tbl->reg_addr, + reg_conf_tbl->reg_data, + MSM_CAMERA_I2C_BYTE_DATA, 0); + break; + case MSM_CAMERA_I2C_SET_WORD_MASK: + rc = msm_camera_cci_i2c_set_mask(client, + reg_conf_tbl->reg_addr, + reg_conf_tbl->reg_data, + MSM_CAMERA_I2C_WORD_DATA, 1); + break; + case MSM_CAMERA_I2C_UNSET_WORD_MASK: + rc = msm_camera_cci_i2c_set_mask(client, + reg_conf_tbl->reg_addr, + reg_conf_tbl->reg_data, + MSM_CAMERA_I2C_WORD_DATA, 0); + break; + case MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA: + rc = msm_camera_cci_i2c_set_write_mask_data( + client, + reg_conf_tbl->reg_addr, + reg_conf_tbl->reg_data, + reg_conf_tbl->mask, + MSM_CAMERA_I2C_BYTE_DATA); + break; + default: + pr_err("%s: Unsupport data type: %d\n", + __func__, dt); + break; + } + } + if (rc < 0) + break; + reg_conf_tbl++; + } + return rc; +} + +int32_t msm_sensor_cci_i2c_util(struct msm_camera_i2c_client *client, + uint16_t cci_cmd) +{ + int32_t rc = 0; + struct msm_camera_cci_ctrl cci_ctrl; + + CDBG("%s line %d\n", __func__, __LINE__); + cci_ctrl.cmd = cci_cmd; + cci_ctrl.cci_info = client->cci_client; + rc = v4l2_subdev_call(client->cci_client->cci_subdev, + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + if (rc < 0) { + pr_err("%s line %d rc = %d\n", __func__, __LINE__, rc); + return rc; + } + return cci_ctrl.status; +} diff --git a/camera/sensor/io/msm_camera_dt_util.c b/camera/sensor/io/msm_camera_dt_util.c new file mode 100644 index 00000000..b50fdbcb --- /dev/null +++ b/camera/sensor/io/msm_camera_dt_util.c @@ -0,0 +1,1667 @@ +/* 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 "msm_camera_dt_util.h" +#include "msm_camera_io_util.h" +#include "msm_camera_i2c_mux.h" +#include "msm_cci.h" + +#define CAM_SENSOR_PINCTRL_STATE_SLEEP "cam_suspend" +#define CAM_SENSOR_PINCTRL_STATE_DEFAULT "cam_default" +/*#define CONFIG_MSM_CAMERA_DT_DEBUG*/ + +#define VALIDATE_VOLTAGE(min, max, config_val) ((config_val) && \ + (config_val >= min) && (config_val <= max)) + +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +int msm_camera_fill_vreg_params(struct camera_vreg_t *cam_vreg, + int num_vreg, struct msm_sensor_power_setting *power_setting, + uint16_t power_setting_size) +{ + uint16_t i = 0; + int j = 0; + + /* Validate input parameters */ + if (!cam_vreg || !power_setting) { + pr_err("%s:%d failed: cam_vreg %p power_setting %p", __func__, + __LINE__, cam_vreg, power_setting); + return -EINVAL; + } + + /* Validate size of num_vreg */ + if (num_vreg <= 0) { + pr_err("failed: num_vreg %d", num_vreg); + return -EINVAL; + } + + for (i = 0; i < power_setting_size; i++) { + if (power_setting[i].seq_type != SENSOR_VREG) + continue; + + switch (power_setting[i].seq_val) { + case CAM_VDIG: + for (j = 0; j < num_vreg; j++) { + if (!strcmp(cam_vreg[j].reg_name, "cam_vdig")) { + CDBG("%s:%d i %d j %d cam_vdig\n", + __func__, __LINE__, i, j); + power_setting[i].seq_val = j; + if (VALIDATE_VOLTAGE( + cam_vreg[j].min_voltage, + cam_vreg[j].max_voltage, + power_setting[i].config_val)) { + cam_vreg[j].min_voltage = + cam_vreg[j].max_voltage = + power_setting[i].config_val; + } + break; + } + } + if (j == num_vreg) + power_setting[i].seq_val = INVALID_VREG; + break; + + case CAM_VIO: + for (j = 0; j < num_vreg; j++) { + if (!strcmp(cam_vreg[j].reg_name, "cam_vio")) { + CDBG("%s:%d i %d j %d cam_vio\n", + __func__, __LINE__, i, j); + power_setting[i].seq_val = j; + if (VALIDATE_VOLTAGE( + cam_vreg[j].min_voltage, + cam_vreg[j].max_voltage, + power_setting[i].config_val)) { + cam_vreg[j].min_voltage = + cam_vreg[j].max_voltage = + power_setting[i].config_val; + } + break; + } + } + if (j == num_vreg) + power_setting[i].seq_val = INVALID_VREG; + break; + + case CAM_VANA: + for (j = 0; j < num_vreg; j++) { + if (!strcmp(cam_vreg[j].reg_name, "cam_vana")) { + CDBG("%s:%d i %d j %d cam_vana\n", + __func__, __LINE__, i, j); + power_setting[i].seq_val = j; + if (VALIDATE_VOLTAGE( + cam_vreg[j].min_voltage, + cam_vreg[j].max_voltage, + power_setting[i].config_val)) { + cam_vreg[j].min_voltage = + cam_vreg[j].max_voltage = + power_setting[i].config_val; + } + break; + } + } + if (j == num_vreg) + power_setting[i].seq_val = INVALID_VREG; + break; + + case CAM_VAF: + for (j = 0; j < num_vreg; j++) { + if (!strcmp(cam_vreg[j].reg_name, "cam_vaf")) { + CDBG("%s:%d i %d j %d cam_vaf\n", + __func__, __LINE__, i, j); + power_setting[i].seq_val = j; + if (VALIDATE_VOLTAGE( + cam_vreg[j].min_voltage, + cam_vreg[j].max_voltage, + power_setting[i].config_val)) { + cam_vreg[j].min_voltage = + cam_vreg[j].max_voltage = + power_setting[i].config_val; + } + break; + } + } + if (j == num_vreg) + power_setting[i].seq_val = INVALID_VREG; + break; + + case CAM_V_CUSTOM1: + for (j = 0; j < num_vreg; j++) { + if (!strcmp(cam_vreg[j].reg_name, + "cam_v_custom1")) { + CDBG("%s:%d i %d j %d cam_vcustom1\n", + __func__, __LINE__, i, j); + power_setting[i].seq_val = j; + if (VALIDATE_VOLTAGE( + cam_vreg[j].min_voltage, + cam_vreg[j].max_voltage, + power_setting[i].config_val)) { + cam_vreg[j].min_voltage = + cam_vreg[j].max_voltage = + power_setting[i].config_val; + } + break; + } + } + if (j == num_vreg) + power_setting[i].seq_val = INVALID_VREG; + break; + case CAM_V_CUSTOM2: + for (j = 0; j < num_vreg; j++) { + if (!strcmp(cam_vreg[j].reg_name, + "cam_v_custom2")) { + CDBG("%s:%d i %d j %d cam_vcustom2\n", + __func__, __LINE__, i, j); + power_setting[i].seq_val = j; + if (VALIDATE_VOLTAGE( + cam_vreg[j].min_voltage, + cam_vreg[j].max_voltage, + power_setting[i].config_val)) { + cam_vreg[j].min_voltage = + cam_vreg[j].max_voltage = + power_setting[i].config_val; + } + break; + } + } + if (j == num_vreg) + power_setting[i].seq_val = INVALID_VREG; + break; + + default: + pr_err("%s:%d invalid seq_val %d\n", __func__, + __LINE__, power_setting[i].seq_val); + break; + } + } + return 0; +} + +int msm_sensor_get_sub_module_index(struct device_node *of_node, + struct msm_sensor_info_t **s_info) +{ + int rc = 0, i = 0; + uint32_t val = 0, count = 0; + uint32_t *val_array = NULL; + struct device_node *src_node = NULL; + struct msm_sensor_info_t *sensor_info; + + sensor_info = kzalloc(sizeof(*sensor_info), GFP_KERNEL); + if (!sensor_info) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -ENOMEM; + } + for (i = 0; i < SUB_MODULE_MAX; i++) { + sensor_info->subdev_id[i] = -1; + /* Subdev expose additional interface for same sub module*/ + sensor_info->subdev_intf[i] = -1; + } + + src_node = of_parse_phandle(of_node, "qcom,actuator-src", 0); + if (!src_node) { + CDBG("%s:%d src_node NULL\n", __func__, __LINE__); + } else { + rc = of_property_read_u32(src_node, "cell-index", &val); + CDBG("%s qcom,actuator cell index %d, rc %d\n", __func__, + val, rc); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR; + } + sensor_info->subdev_id[SUB_MODULE_ACTUATOR] = val; + of_node_put(src_node); + src_node = NULL; + } + + src_node = of_parse_phandle(of_node, "qcom,ois-src", 0); + if (!src_node) { + CDBG("%s:%d src_node NULL\n", __func__, __LINE__); + } else { + rc = of_property_read_u32(src_node, "cell-index", &val); + CDBG("%s qcom,ois cell index %d, rc %d\n", __func__, + val, rc); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR; + } + sensor_info->subdev_id[SUB_MODULE_OIS] = val; + of_node_put(src_node); + src_node = NULL; + } + + src_node = of_parse_phandle(of_node, "qcom,eeprom-src", 0); + if (!src_node) { + CDBG("%s:%d eeprom src_node NULL\n", __func__, __LINE__); + } else { + rc = of_property_read_u32(src_node, "cell-index", &val); + CDBG("%s qcom,eeprom cell index %d, rc %d\n", __func__, + val, rc); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR; + } + sensor_info->subdev_id[SUB_MODULE_EEPROM] = val; + of_node_put(src_node); + src_node = NULL; + } + + rc = of_property_read_u32(of_node, "qcom,eeprom-sd-index", &val); + if (rc != -EINVAL) { + CDBG("%s qcom,eeprom-sd-index %d, rc %d\n", __func__, val, rc); + if (rc < 0) { + pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc); + goto ERROR; + } + sensor_info->subdev_id[SUB_MODULE_EEPROM] = val; + } else { + rc = 0; + } + + src_node = of_parse_phandle(of_node, "qcom,led-flash-src", 0); + if (!src_node) { + CDBG("%s:%d src_node NULL\n", __func__, __LINE__); + } else { + rc = of_property_read_u32(src_node, "cell-index", &val); + CDBG("%s qcom,led flash cell index %d, rc %d\n", __func__, + val, rc); + if (rc < 0) { + pr_err("%s:%d failed %d\n", __func__, __LINE__, rc); + goto ERROR; + } + sensor_info->subdev_id[SUB_MODULE_LED_FLASH] = val; + of_node_put(src_node); + src_node = NULL; + } + + rc = of_property_read_u32(of_node, "qcom,strobe-flash-sd-index", &val); + if (rc != -EINVAL) { + CDBG("%s qcom,strobe-flash-sd-index %d, rc %d\n", __func__, + val, rc); + if (rc < 0) { + pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc); + goto ERROR; + } + sensor_info->subdev_id[SUB_MODULE_STROBE_FLASH] = val; + } else { + rc = 0; + } + + if (of_get_property(of_node, "qcom,csiphy-sd-index", &count)) { + count /= sizeof(uint32_t); + if (count > 2) { + pr_err("%s qcom,csiphy-sd-index count %d > 2\n", + __func__, count); + goto ERROR; + } + val_array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL); + if (!val_array) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto ERROR; + } + + rc = of_property_read_u32_array(of_node, "qcom,csiphy-sd-index", + val_array, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + kfree(val_array); + goto ERROR; + } + for (i = 0; i < count; i++) { + sensor_info->subdev_id[SUB_MODULE_CSIPHY + i] = + val_array[i]; + CDBG("%s csiphy_core[%d] = %d\n", + __func__, i, val_array[i]); + } + kfree(val_array); + } else { + pr_err("%s:%d qcom,csiphy-sd-index not present\n", __func__, + __LINE__); + rc = -EINVAL; + goto ERROR; + } + + if (of_get_property(of_node, "qcom,csid-sd-index", &count)) { + count /= sizeof(uint32_t); + if (count > 2) { + pr_err("%s qcom,csid-sd-index count %d > 2\n", + __func__, count); + rc = -EINVAL; + goto ERROR; + } + val_array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL); + if (!val_array) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto ERROR; + } + + rc = of_property_read_u32_array(of_node, "qcom,csid-sd-index", + val_array, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + kfree(val_array); + goto ERROR; + } + for (i = 0; i < count; i++) { + sensor_info->subdev_id + [SUB_MODULE_CSID + i] = val_array[i]; + CDBG("%s csid_core[%d] = %d\n", + __func__, i, val_array[i]); + } + kfree(val_array); + } else { + pr_err("%s:%d qcom,csid-sd-index not present\n", __func__, + __LINE__); + rc = -EINVAL; + goto ERROR; + } + + *s_info = sensor_info; + return rc; +ERROR: + kfree(sensor_info); + return rc; +} + +int msm_sensor_get_dt_actuator_data(struct device_node *of_node, + struct msm_actuator_info **act_info) +{ + int rc = 0; + uint32_t val = 0; + struct msm_actuator_info *actuator_info; + + rc = of_property_read_u32(of_node, "qcom,actuator-cam-name", &val); + CDBG("%s qcom,actuator-cam-name %d, rc %d\n", __func__, val, rc); + if (rc < 0) + return 0; + + actuator_info = kzalloc(sizeof(*actuator_info), GFP_KERNEL); + if (!actuator_info) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto ERROR; + } + + actuator_info->cam_name = val; + + rc = of_property_read_u32(of_node, "qcom,actuator-vcm-pwd", &val); + CDBG("%s qcom,actuator-vcm-pwd %d, rc %d\n", __func__, val, rc); + if (!rc) + actuator_info->vcm_pwd = val; + + rc = of_property_read_u32(of_node, "qcom,actuator-vcm-enable", &val); + CDBG("%s qcom,actuator-vcm-enable %d, rc %d\n", __func__, val, rc); + if (!rc) + actuator_info->vcm_enable = val; + + *act_info = actuator_info; + return 0; +ERROR: + kfree(actuator_info); + return rc; +} + +int msm_sensor_get_dt_csi_data(struct device_node *of_node, + struct msm_camera_csi_lane_params **csi_lane_params) +{ + int rc = 0; + uint32_t val = 0; + struct msm_camera_csi_lane_params *clp; + + clp = kzalloc(sizeof(*clp), GFP_KERNEL); + if (!clp) { + pr_err("%s failed %d\n", __func__, __LINE__); + return -ENOMEM; + } + *csi_lane_params = clp; + + rc = of_property_read_u32(of_node, "qcom,csi-lane-assign", &val); + CDBG("%s qcom,csi-lane-assign 0x%x, rc %d\n", __func__, val, rc); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR; + } + clp->csi_lane_assign = val; + + rc = of_property_read_u32(of_node, "qcom,csi-lane-mask", &val); + CDBG("%s qcom,csi-lane-mask 0x%x, rc %d\n", __func__, val, rc); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR; + } + clp->csi_lane_mask = val; + + return rc; +ERROR: + kfree(clp); + return rc; +} + +int msm_camera_get_dt_power_setting_data(struct device_node *of_node, + struct camera_vreg_t *cam_vreg, int num_vreg, + struct msm_camera_power_ctrl_t *power_info) +{ + int rc = 0, i, j; + int count = 0; + const char *seq_name = NULL; + uint32_t *array = NULL; + struct msm_sensor_power_setting *ps; + + struct msm_sensor_power_setting *power_setting; + uint16_t *power_setting_size, size = 0; + bool need_reverse = 0; + + if (!power_info) + return -EINVAL; + + power_setting = power_info->power_setting; + power_setting_size = &power_info->power_setting_size; + + count = of_property_count_strings(of_node, "qcom,cam-power-seq-type"); + *power_setting_size = count; + + CDBG("%s qcom,cam-power-seq-type count %d\n", __func__, count); + + if (count <= 0) + return 0; + + ps = kzalloc(sizeof(*ps) * count, GFP_KERNEL); + if (!ps) { + pr_err("%s failed %d\n", __func__, __LINE__); + return -ENOMEM; + } + power_setting = ps; + power_info->power_setting = ps; + + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(of_node, + "qcom,cam-power-seq-type", i, + &seq_name); + CDBG("%s seq_name[%d] = %s\n", __func__, i, + seq_name); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR1; + } + if (!strcmp(seq_name, "sensor_vreg")) { + ps[i].seq_type = SENSOR_VREG; + CDBG("%s:%d seq_type[%d] %d\n", __func__, __LINE__, + i, ps[i].seq_type); + } else if (!strcmp(seq_name, "sensor_gpio")) { + ps[i].seq_type = SENSOR_GPIO; + CDBG("%s:%d seq_type[%d] %d\n", __func__, __LINE__, + i, ps[i].seq_type); + } else if (!strcmp(seq_name, "sensor_clk")) { + ps[i].seq_type = SENSOR_CLK; + CDBG("%s:%d seq_type[%d] %d\n", __func__, __LINE__, + i, ps[i].seq_type); + } else if (!strcmp(seq_name, "sensor_i2c_mux")) { + ps[i].seq_type = SENSOR_I2C_MUX; + CDBG("%s:%d seq_type[%d] %d\n", __func__, __LINE__, + i, ps[i].seq_type); + } else { + CDBG("%s: unrecognized seq-type\n", __func__); + rc = -EILSEQ; + goto ERROR1; + } + } + + + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(of_node, + "qcom,cam-power-seq-val", i, + &seq_name); + CDBG("%s seq_name[%d] = %s\n", __func__, i, + seq_name); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR1; + } + switch (ps[i].seq_type) { + case SENSOR_VREG: + for (j = 0; j < num_vreg; j++) { + if (!strcmp(seq_name, cam_vreg[j].reg_name)) + break; + } + if (j < num_vreg) + ps[i].seq_val = j; + else + rc = -EILSEQ; + break; + case SENSOR_GPIO: + if (!strcmp(seq_name, "sensor_gpio_reset")) + ps[i].seq_val = SENSOR_GPIO_RESET; + else if (!strcmp(seq_name, "sensor_gpio_standby")) + ps[i].seq_val = SENSOR_GPIO_STANDBY; + else if (!strcmp(seq_name, "sensor_gpio_vdig")) + ps[i].seq_val = SENSOR_GPIO_VDIG; + else if (!strcmp(seq_name, "sensor_gpio_vana")) + ps[i].seq_val = SENSOR_GPIO_VANA; + else if (!strcmp(seq_name, "sensor_gpio_vaf")) + ps[i].seq_val = SENSOR_GPIO_VAF; + else if (!strcmp(seq_name, "sensor_gpio_vio")) + ps[i].seq_val = SENSOR_GPIO_VIO; + else if (!strcmp(seq_name, "sensor_gpio_custom1")) + ps[i].seq_val = SENSOR_GPIO_CUSTOM1; + else if (!strcmp(seq_name, "sensor_gpio_custom2")) + ps[i].seq_val = SENSOR_GPIO_CUSTOM2; + else + rc = -EILSEQ; + break; + case SENSOR_CLK: + if (!strcmp(seq_name, "sensor_cam_mclk")) + ps[i].seq_val = SENSOR_CAM_MCLK; + else if (!strcmp(seq_name, "sensor_cam_clk")) + ps[i].seq_val = SENSOR_CAM_CLK; + else + rc = -EILSEQ; + break; + case SENSOR_I2C_MUX: + if (!strcmp(seq_name, "none")) + ps[i].seq_val = 0; + else + rc = -EILSEQ; + break; + default: + rc = -EILSEQ; + break; + } + if (rc < 0) { + CDBG("%s: unrecognized seq-val\n", __func__); + goto ERROR1; + } + } + + array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL); + if (!array) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto ERROR1; + } + + + rc = of_property_read_u32_array(of_node, "qcom,cam-power-seq-cfg-val", + array, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } + for (i = 0; i < count; i++) { + if (ps[i].seq_type == SENSOR_GPIO) { + if (array[i] == 0) + ps[i].config_val = GPIO_OUT_LOW; + else if (array[i] == 1) + ps[i].config_val = GPIO_OUT_HIGH; + } else { + ps[i].config_val = array[i]; + } + CDBG("%s power_setting[%d].config_val = %ld\n", __func__, i, + ps[i].config_val); + } + + rc = of_property_read_u32_array(of_node, "qcom,cam-power-seq-delay", + array, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } + for (i = 0; i < count; i++) { + ps[i].delay = array[i]; + CDBG("%s power_setting[%d].delay = %d\n", __func__, + i, ps[i].delay); + } + kfree(array); + + size = *power_setting_size; + + if (NULL != ps && 0 != size) + need_reverse = 1; + + power_info->power_down_setting = + kzalloc(sizeof(*ps) * size, GFP_KERNEL); + + if (!power_info->power_down_setting) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto ERROR1; + } + + memcpy(power_info->power_down_setting, + ps, sizeof(*ps) * size); + + power_info->power_down_setting_size = size; + + if (need_reverse) { + int c, end = size - 1; + struct msm_sensor_power_setting power_down_setting_t; + for (c = 0; c < size/2; c++) { + power_down_setting_t = + power_info->power_down_setting[c]; + power_info->power_down_setting[c] = + power_info->power_down_setting[end]; + power_info->power_down_setting[end] = + power_down_setting_t; + end--; + } + } + return rc; +ERROR2: + kfree(array); +ERROR1: + kfree(ps); + power_setting_size = 0; + return rc; +} + +int msm_camera_get_dt_gpio_req_tbl(struct device_node *of_node, + struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, + uint16_t gpio_array_size) +{ + int rc = 0, i = 0; + uint32_t count = 0; + uint32_t *val_array = NULL; + + if (!of_get_property(of_node, "qcom,gpio-req-tbl-num", &count)) + return 0; + + count /= sizeof(uint32_t); + if (!count) { + pr_err("%s qcom,gpio-req-tbl-num 0\n", __func__); + return 0; + } + + val_array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL); + if (!val_array) { + pr_err("%s failed %d\n", __func__, __LINE__); + return -ENOMEM; + } + + gconf->cam_gpio_req_tbl = kzalloc(sizeof(struct gpio) * count, + GFP_KERNEL); + if (!gconf->cam_gpio_req_tbl) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto ERROR1; + } + gconf->cam_gpio_req_tbl_size = count; + + rc = of_property_read_u32_array(of_node, "qcom,gpio-req-tbl-num", + val_array, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } + for (i = 0; i < count; i++) { + if (val_array[i] >= gpio_array_size) { + pr_err("%s gpio req tbl index %d invalid\n", + __func__, val_array[i]); + return -EINVAL; + } + gconf->cam_gpio_req_tbl[i].gpio = gpio_array[val_array[i]]; + CDBG("%s cam_gpio_req_tbl[%d].gpio = %d\n", __func__, i, + gconf->cam_gpio_req_tbl[i].gpio); + } + + rc = of_property_read_u32_array(of_node, "qcom,gpio-req-tbl-flags", + val_array, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } + for (i = 0; i < count; i++) { + gconf->cam_gpio_req_tbl[i].flags = val_array[i]; + CDBG("%s cam_gpio_req_tbl[%d].flags = %ld\n", __func__, i, + gconf->cam_gpio_req_tbl[i].flags); + } + + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(of_node, + "qcom,gpio-req-tbl-label", i, + &gconf->cam_gpio_req_tbl[i].label); + CDBG("%s cam_gpio_req_tbl[%d].label = %s\n", __func__, i, + gconf->cam_gpio_req_tbl[i].label); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } + } + + kfree(val_array); + return rc; + +ERROR2: + kfree(gconf->cam_gpio_req_tbl); +ERROR1: + kfree(val_array); + gconf->cam_gpio_req_tbl_size = 0; + return rc; +} + +int msm_camera_init_gpio_pin_tbl(struct device_node *of_node, + struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, + uint16_t gpio_array_size) +{ + int rc = 0, val = 0; + + gconf->gpio_num_info = kzalloc(sizeof(struct msm_camera_gpio_num_info), + GFP_KERNEL); + if (!gconf->gpio_num_info) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + return rc; + } + rc = of_property_read_u32(of_node, "qcom,gpio-vana", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-vana failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-vana invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VANA] = + gpio_array[val]; + gconf->gpio_num_info->valid[SENSOR_GPIO_VANA] = 1; + CDBG("%s qcom,gpio-vana %d\n", __func__, + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VANA]); + } else { + rc = 0; + } + + rc = of_property_read_u32(of_node, "qcom,gpio-vio", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-vio failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-vio invalid %d\n", + __func__, __LINE__, val); + goto ERROR; + } + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VIO] = + gpio_array[val]; + gconf->gpio_num_info->valid[SENSOR_GPIO_VIO] = 1; + CDBG("%s qcom,gpio-vio %d\n", __func__, + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VIO]); + } else { + rc = 0; + } + + rc = of_property_read_u32(of_node, "qcom,gpio-vaf", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-vaf failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-vaf invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VAF] = + gpio_array[val]; + gconf->gpio_num_info->valid[SENSOR_GPIO_VAF] = 1; + CDBG("%s qcom,gpio-vaf %d\n", __func__, + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VAF]); + } else { + rc = 0; + } + + rc = of_property_read_u32(of_node, "qcom,gpio-vdig", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-vdig failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-vdig invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VDIG] = + gpio_array[val]; + gconf->gpio_num_info->valid[SENSOR_GPIO_VDIG] = 1; + CDBG("%s qcom,gpio-vdig %d\n", __func__, + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VDIG]); + } else { + rc = 0; + } + + rc = of_property_read_u32(of_node, "qcom,gpio-reset", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-reset failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-reset invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_RESET] = + gpio_array[val]; + gconf->gpio_num_info->valid[SENSOR_GPIO_RESET] = 1; + CDBG("%s qcom,gpio-reset %d\n", __func__, + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_RESET]); + } else { + rc = 0; + } + + rc = of_property_read_u32(of_node, "qcom,gpio-standby", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-standby failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-standby invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_STANDBY] = + gpio_array[val]; + gconf->gpio_num_info->valid[SENSOR_GPIO_STANDBY] = 1; + CDBG("%s qcom,gpio-standby %d\n", __func__, + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_STANDBY]); + } else { + rc = 0; + } + + rc = of_property_read_u32(of_node, "qcom,gpio-af-pwdm", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-af-pwdm failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-af-pwdm invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_AF_PWDM] = + gpio_array[val]; + gconf->gpio_num_info->valid[SENSOR_GPIO_AF_PWDM] = 1; + CDBG("%s qcom,gpio-af-pwdm %d\n", __func__, + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_AF_PWDM]); + } else { + rc = 0; + } + + rc = of_property_read_u32(of_node, "qcom,gpio-flash-en", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-flash-en failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-flash-en invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_FL_EN] = + gpio_array[val]; + gconf->gpio_num_info->valid[SENSOR_GPIO_FL_EN] = 1; + CDBG("%s qcom,gpio-flash-en %d\n", __func__, + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_FL_EN]); + } else { + rc = 0; + } + + rc = of_property_read_u32(of_node, "qcom,gpio-flash-now", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-flash-now failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-flash-now invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_FL_NOW] = + gpio_array[val]; + gconf->gpio_num_info->valid[SENSOR_GPIO_FL_NOW] = 1; + CDBG("%s qcom,gpio-flash-now %d\n", __func__, + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_FL_NOW]); + } else { + rc = 0; + } + + rc = of_property_read_u32(of_node, "qcom,gpio-flash-reset", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%dread qcom,gpio-flash-reset failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-flash-reset invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_FL_RESET] = + gpio_array[val]; + gconf->gpio_num_info->valid[SENSOR_GPIO_FL_RESET] = 1; + CDBG("%s qcom,gpio-flash-reset %d\n", __func__, + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_FL_RESET]); + } else + rc = 0; + + rc = of_property_read_u32(of_node, "qcom,gpio-custom1", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-custom1 failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-custom1 invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_CUSTOM1] = + gpio_array[val]; + gconf->gpio_num_info->valid[SENSOR_GPIO_CUSTOM1] = 1; + CDBG("%s qcom,gpio-custom1 %d\n", __func__, + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_CUSTOM1]); + } else { + rc = 0; + } + + rc = of_property_read_u32(of_node, "qcom,gpio-custom2", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-custom2 failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-custom2 invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_CUSTOM2] = + gpio_array[val]; + gconf->gpio_num_info->valid[SENSOR_GPIO_CUSTOM2] = 1; + CDBG("%s qcom,gpio-custom2 %d\n", __func__, + gconf->gpio_num_info->gpio_num[SENSOR_GPIO_CUSTOM2]); + } else { + rc = 0; + } + + return rc; + +ERROR: + kfree(gconf->gpio_num_info); + gconf->gpio_num_info = NULL; + return rc; +} + +int msm_camera_get_dt_vreg_data(struct device_node *of_node, + struct camera_vreg_t **cam_vreg, int *num_vreg) +{ + int rc = 0, i = 0; + int32_t count = 0; + uint32_t *vreg_array = NULL; + struct camera_vreg_t *vreg = NULL; + bool custom_vreg_name = false; + + count = of_property_count_strings(of_node, "qcom,cam-vreg-name"); + CDBG("%s qcom,cam-vreg-name count %d\n", __func__, count); + + if (!count || (count == -EINVAL)) { + pr_err("%s:%d number of entries is 0 or not present in dts\n", + __func__, __LINE__); + *num_vreg = 0; + return 0; + } + + vreg = kzalloc(sizeof(*vreg) * count, GFP_KERNEL); + if (!vreg) { + pr_err("%s failed %d\n", __func__, __LINE__); + return -ENOMEM; + } + *cam_vreg = vreg; + *num_vreg = count; + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(of_node, + "qcom,cam-vreg-name", i, + &vreg[i].reg_name); + CDBG("%s reg_name[%d] = %s\n", __func__, i, + vreg[i].reg_name); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR1; + } + } + + custom_vreg_name = of_property_read_bool(of_node, + "qcom,cam-custom-vreg-name"); + if (custom_vreg_name) { + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(of_node, + "qcom,cam-custom-vreg-name", i, + &vreg[i].custom_vreg_name); + CDBG("%s sub reg_name[%d] = %s\n", __func__, i, + vreg[i].custom_vreg_name); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR1; + } + } + } + + vreg_array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL); + if (!vreg_array) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto ERROR1; + } + + for (i = 0; i < count; i++) + vreg[i].type = VREG_TYPE_DEFAULT; + + rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-type", + vreg_array, count); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } else { + for (i = 0; i < count; i++) { + vreg[i].type = vreg_array[i]; + CDBG("%s cam_vreg[%d].type = %d\n", + __func__, i, vreg[i].type); + } + } + } else { + CDBG("%s:%d no qcom,cam-vreg-type entries in dts\n", + __func__, __LINE__); + rc = 0; + } + + rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-min-voltage", + vreg_array, count); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } else { + for (i = 0; i < count; i++) { + vreg[i].min_voltage = vreg_array[i]; + CDBG("%s cam_vreg[%d].min_voltage = %d\n", + __func__, i, vreg[i].min_voltage); + } + } + } else { + CDBG("%s:%d no qcom,cam-vreg-min-voltage entries in dts\n", + __func__, __LINE__); + rc = 0; + } + + rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-max-voltage", + vreg_array, count); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } else { + for (i = 0; i < count; i++) { + vreg[i].max_voltage = vreg_array[i]; + CDBG("%s cam_vreg[%d].max_voltage = %d\n", + __func__, i, vreg[i].max_voltage); + } + } + } else { + CDBG("%s:%d no qcom,cam-vreg-max-voltage entries in dts\n", + __func__, __LINE__); + rc = 0; + } + + rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-op-mode", + vreg_array, count); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } else { + for (i = 0; i < count; i++) { + vreg[i].op_mode = vreg_array[i]; + CDBG("%s cam_vreg[%d].op_mode = %d\n", + __func__, i, vreg[i].op_mode); + } + } + } else { + CDBG("%s:%d no qcom,cam-vreg-op-mode entries in dts\n", + __func__, __LINE__); + rc = 0; + } + + kfree(vreg_array); + return rc; +ERROR2: + kfree(vreg_array); +ERROR1: + kfree(vreg); + *num_vreg = 0; + return rc; +} + +static int msm_camera_enable_i2c_mux(struct msm_camera_i2c_conf *i2c_conf) +{ + struct v4l2_subdev *i2c_mux_sd = + dev_get_drvdata(&i2c_conf->mux_dev->dev); + v4l2_subdev_call(i2c_mux_sd, core, ioctl, + VIDIOC_MSM_I2C_MUX_INIT, NULL); + v4l2_subdev_call(i2c_mux_sd, core, ioctl, + VIDIOC_MSM_I2C_MUX_CFG, (void *)&i2c_conf->i2c_mux_mode); + return 0; +} + +static int msm_camera_disable_i2c_mux(struct msm_camera_i2c_conf *i2c_conf) +{ + struct v4l2_subdev *i2c_mux_sd = + dev_get_drvdata(&i2c_conf->mux_dev->dev); + v4l2_subdev_call(i2c_mux_sd, core, ioctl, + VIDIOC_MSM_I2C_MUX_RELEASE, NULL); + return 0; +} + +int msm_camera_pinctrl_init( + struct msm_pinctrl_info *sensor_pctrl, struct device *dev) { + + sensor_pctrl->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(sensor_pctrl->pinctrl)) { + pr_err("%s:%d Getting pinctrl handle failed\n", + __func__, __LINE__); + return -EINVAL; + } + sensor_pctrl->gpio_state_active = + pinctrl_lookup_state(sensor_pctrl->pinctrl, + CAM_SENSOR_PINCTRL_STATE_DEFAULT); + if (IS_ERR_OR_NULL(sensor_pctrl->gpio_state_active)) { + pr_err("%s:%d Failed to get the active state pinctrl handle\n", + __func__, __LINE__); + return -EINVAL; + } + sensor_pctrl->gpio_state_suspend + = pinctrl_lookup_state(sensor_pctrl->pinctrl, + CAM_SENSOR_PINCTRL_STATE_SLEEP); + if (IS_ERR_OR_NULL(sensor_pctrl->gpio_state_suspend)) { + pr_err("%s:%d Failed to get the suspend state pinctrl handle\n", + __func__, __LINE__); + return -EINVAL; + } + return 0; +} + +int msm_cam_sensor_handle_reg_gpio(int seq_val, + struct msm_camera_gpio_conf *gconf, int val) { + + int gpio_offset = -1; + + if (!gconf) { + pr_err("ERR:%s: Input Parameters are not proper\n", __func__); + return -EINVAL; + } + CDBG("%s: %d Seq val: %d, config: %d", __func__, __LINE__, + seq_val, val); + + switch (seq_val) { + case CAM_VDIG: + gpio_offset = SENSOR_GPIO_VDIG; + break; + + case CAM_VIO: + gpio_offset = SENSOR_GPIO_VIO; + break; + + case CAM_VANA: + gpio_offset = SENSOR_GPIO_VANA; + break; + + case CAM_VAF: + gpio_offset = SENSOR_GPIO_VAF; + break; + + case CAM_V_CUSTOM1: + gpio_offset = SENSOR_GPIO_CUSTOM1; + break; + + case CAM_V_CUSTOM2: + gpio_offset = SENSOR_GPIO_CUSTOM2; + break; + + default: + pr_err("%s:%d Invalid VREG seq val %d\n", __func__, + __LINE__, seq_val); + return -EINVAL; + } + + CDBG("%s: %d GPIO offset: %d, seq_val: %d\n", __func__, __LINE__, + gpio_offset, seq_val); + + if ((gconf->gpio_num_info->valid[gpio_offset] == 1)) { + gpio_set_value_cansleep( + gconf->gpio_num_info->gpio_num + [gpio_offset], val); + } + return 0; +} + +int32_t msm_sensor_driver_get_gpio_data( + struct msm_camera_gpio_conf **gpio_conf, + struct device_node *of_node) +{ + int32_t rc = 0, i = 0; + uint16_t *gpio_array = NULL; + int16_t gpio_array_size = 0; + struct msm_camera_gpio_conf *gconf = NULL; + + /* Validate input parameters */ + if (!of_node) { + pr_err("failed: invalid param of_node %pK", of_node); + return -EINVAL; + } + + gpio_array_size = of_gpio_count(of_node); + CDBG("gpio count %d\n", gpio_array_size); + if (gpio_array_size <= 0) + return 0; + + gconf = kzalloc(sizeof(struct msm_camera_gpio_conf), + GFP_KERNEL); + if (!gconf) + return -ENOMEM; + + *gpio_conf = gconf; + + gpio_array = kcalloc(gpio_array_size, sizeof(uint16_t), GFP_KERNEL); + if (!gpio_array) + goto FREE_GPIO_CONF; + + for (i = 0; i < gpio_array_size; i++) { + gpio_array[i] = of_get_gpio(of_node, i); + CDBG("gpio_array[%d] = %d", i, gpio_array[i]); + } + rc = msm_camera_get_dt_gpio_req_tbl(of_node, gconf, gpio_array, + gpio_array_size); + if (rc < 0) { + pr_err("failed in msm_camera_get_dt_gpio_req_tbl\n"); + goto FREE_GPIO_CONF; + } + + rc = msm_camera_init_gpio_pin_tbl(of_node, gconf, gpio_array, + gpio_array_size); + if (rc < 0) { + pr_err("failed in msm_camera_init_gpio_pin_tbl\n"); + goto FREE_GPIO_REQ_TBL; + } + kfree(gpio_array); + return rc; + +FREE_GPIO_REQ_TBL: + kfree(gconf->cam_gpio_req_tbl); +FREE_GPIO_CONF: + kfree(gconf); + kfree(gpio_array); + *gpio_conf = NULL; + return rc; +} + +int msm_camera_power_up(struct msm_camera_power_ctrl_t *ctrl, + enum msm_camera_device_type_t device_type, + struct msm_camera_i2c_client *sensor_i2c_client) +{ + int rc = 0, index = 0, no_gpio = 0, ret = 0; + struct msm_sensor_power_setting *power_setting = NULL; + + CDBG("%s:%d\n", __func__, __LINE__); + if (!ctrl || !sensor_i2c_client) { + pr_err("failed ctrl %p sensor_i2c_client %p\n", ctrl, + sensor_i2c_client); + return -EINVAL; + } + if (ctrl->gpio_conf->cam_gpiomux_conf_tbl != NULL) + pr_err("%s:%d mux install\n", __func__, __LINE__); + + ret = msm_camera_pinctrl_init(&(ctrl->pinctrl_info), ctrl->dev); + if (ret < 0) { + pr_err("%s:%d Initialization of pinctrl failed\n", + __func__, __LINE__); + ctrl->cam_pinctrl_status = 0; + } else { + ctrl->cam_pinctrl_status = 1; + } + rc = msm_camera_request_gpio_table( + ctrl->gpio_conf->cam_gpio_req_tbl, + ctrl->gpio_conf->cam_gpio_req_tbl_size, 1); + if (rc < 0) + no_gpio = rc; + if (ctrl->cam_pinctrl_status) { + ret = pinctrl_select_state(ctrl->pinctrl_info.pinctrl, + ctrl->pinctrl_info.gpio_state_active); + if (ret) + pr_err("%s:%d cannot set pin to active state", + __func__, __LINE__); + } + for (index = 0; index < ctrl->power_setting_size; index++) { + CDBG("%s index %d\n", __func__, index); + power_setting = &ctrl->power_setting[index]; + CDBG("%s type %d\n", __func__, power_setting->seq_type); + switch (power_setting->seq_type) { + case SENSOR_CLK: + if (power_setting->seq_val >= ctrl->clk_info_size) { + pr_err("%s clk index %d >= max %zu\n", __func__, + power_setting->seq_val, + ctrl->clk_info_size); + goto power_up_failed; + } + if (power_setting->config_val) + ctrl->clk_info[power_setting->seq_val]. + clk_rate = power_setting->config_val; + rc = msm_camera_clk_enable(ctrl->dev, + ctrl->clk_info, ctrl->clk_ptr, + ctrl->clk_info_size, true); + if (rc < 0) { + pr_err("%s: clk enable failed\n", __func__); + goto power_up_failed; + } + break; + case SENSOR_GPIO: + if (no_gpio) { + pr_err("%s: request gpio failed\n", __func__); + return no_gpio; + } + if (power_setting->seq_val >= SENSOR_GPIO_MAX || + !ctrl->gpio_conf->gpio_num_info) { + pr_err("%s gpio index %d >= max %d\n", __func__, + power_setting->seq_val, + SENSOR_GPIO_MAX); + goto power_up_failed; + } + if (!ctrl->gpio_conf->gpio_num_info->valid + [power_setting->seq_val]) + continue; + CDBG("%s:%d gpio set val %d\n", __func__, __LINE__, + ctrl->gpio_conf->gpio_num_info->gpio_num + [power_setting->seq_val]); + gpio_set_value_cansleep( + ctrl->gpio_conf->gpio_num_info->gpio_num + [power_setting->seq_val], + (int) power_setting->config_val); + break; + case SENSOR_VREG: + if (power_setting->seq_val == INVALID_VREG) + break; + + if (power_setting->seq_val >= CAM_VREG_MAX) { + pr_err("%s vreg index %d >= max %d\n", __func__, + power_setting->seq_val, + SENSOR_GPIO_MAX); + goto power_up_failed; + } + if (power_setting->seq_val < ctrl->num_vreg) + msm_camera_config_single_vreg(ctrl->dev, + &ctrl->cam_vreg + [power_setting->seq_val], + (struct regulator **) + &power_setting->data[0], + 1); + else + pr_err("%s: %d usr_idx:%d dts_idx:%d\n", + __func__, __LINE__, + power_setting->seq_val, ctrl->num_vreg); + + rc = msm_cam_sensor_handle_reg_gpio( + power_setting->seq_val, + ctrl->gpio_conf, 1); + if (rc < 0) { + pr_err("ERR:%s Error in handling VREG GPIO\n", + __func__); + goto power_up_failed; + } + break; + case SENSOR_I2C_MUX: + if (ctrl->i2c_conf && ctrl->i2c_conf->use_i2c_mux) + msm_camera_enable_i2c_mux(ctrl->i2c_conf); + break; + default: + pr_err("%s error power seq type %d\n", __func__, + power_setting->seq_type); + break; + } + if (power_setting->delay > 20) { + msleep(power_setting->delay); + } else if (power_setting->delay) { + usleep_range(power_setting->delay * 1000, + (power_setting->delay * 1000) + 1000); + } + } + + if (device_type == MSM_CAMERA_PLATFORM_DEVICE) { + rc = sensor_i2c_client->i2c_func_tbl->i2c_util( + sensor_i2c_client, MSM_CCI_INIT); + if (rc < 0) { + pr_err("%s cci_init failed\n", __func__); + goto power_up_failed; + } + } + CDBG("%s exit\n", __func__); + return 0; +power_up_failed: + pr_err("%s:%d failed\n", __func__, __LINE__); + for (index--; index >= 0; index--) { + CDBG("%s index %d\n", __func__, index); + power_setting = &ctrl->power_setting[index]; + CDBG("%s type %d\n", __func__, power_setting->seq_type); + switch (power_setting->seq_type) { + case SENSOR_GPIO: + if (!ctrl->gpio_conf->gpio_num_info) + continue; + if (!ctrl->gpio_conf->gpio_num_info->valid + [power_setting->seq_val]) + continue; + gpio_set_value_cansleep( + ctrl->gpio_conf->gpio_num_info->gpio_num + [power_setting->seq_val], GPIOF_OUT_INIT_LOW); + break; + case SENSOR_VREG: + if (power_setting->seq_val < ctrl->num_vreg) + msm_camera_config_single_vreg(ctrl->dev, + &ctrl->cam_vreg + [power_setting->seq_val], + (struct regulator **) + &power_setting->data[0], + 0); + else + pr_err("%s:%d:seq_val: %d > num_vreg: %d\n", + __func__, __LINE__, + power_setting->seq_val, ctrl->num_vreg); + + msm_cam_sensor_handle_reg_gpio(power_setting->seq_val, + ctrl->gpio_conf, GPIOF_OUT_INIT_LOW); + break; + case SENSOR_I2C_MUX: + if (ctrl->i2c_conf && ctrl->i2c_conf->use_i2c_mux) + msm_camera_disable_i2c_mux(ctrl->i2c_conf); + break; + default: + pr_err("%s error power seq type %d\n", __func__, + power_setting->seq_type); + break; + } + if (power_setting->delay > 20) { + msleep(power_setting->delay); + } else if (power_setting->delay) { + usleep_range(power_setting->delay * 1000, + (power_setting->delay * 1000) + 1000); + } + } + if (ctrl->cam_pinctrl_status) { + ret = pinctrl_select_state(ctrl->pinctrl_info.pinctrl, + ctrl->pinctrl_info.gpio_state_suspend); + if (ret) + pr_err("%s:%d cannot set pin to suspend state\n", + __func__, __LINE__); + devm_pinctrl_put(ctrl->pinctrl_info.pinctrl); + } + ctrl->cam_pinctrl_status = 0; + msm_camera_request_gpio_table( + ctrl->gpio_conf->cam_gpio_req_tbl, + ctrl->gpio_conf->cam_gpio_req_tbl_size, 0); + return rc; +} + +static struct msm_sensor_power_setting* +msm_camera_get_power_settings(struct msm_camera_power_ctrl_t *ctrl, + enum msm_sensor_power_seq_type_t seq_type, + uint16_t seq_val) +{ + struct msm_sensor_power_setting *power_setting, *ps = NULL; + int idx; + + for (idx = 0; idx < ctrl->power_setting_size; idx++) { + power_setting = &ctrl->power_setting[idx]; + if (power_setting->seq_type == seq_type && + power_setting->seq_val == seq_val) { + ps = power_setting; + return ps; + } + + } + return ps; +} + +int msm_camera_power_down(struct msm_camera_power_ctrl_t *ctrl, + enum msm_camera_device_type_t device_type, + struct msm_camera_i2c_client *sensor_i2c_client) +{ + int index = 0, ret = 0; + struct msm_sensor_power_setting *pd = NULL; + struct msm_sensor_power_setting *ps; + + CDBG("%s:%d\n", __func__, __LINE__); + if (!ctrl || !sensor_i2c_client) { + pr_err("failed ctrl %p sensor_i2c_client %p\n", ctrl, + sensor_i2c_client); + return -EINVAL; + } + if (device_type == MSM_CAMERA_PLATFORM_DEVICE) + sensor_i2c_client->i2c_func_tbl->i2c_util( + sensor_i2c_client, MSM_CCI_RELEASE); + + for (index = 0; index < ctrl->power_down_setting_size; index++) { + CDBG("%s index %d\n", __func__, index); + pd = &ctrl->power_down_setting[index]; + ps = NULL; + CDBG("%s type %d\n", __func__, pd->seq_type); + switch (pd->seq_type) { + case SENSOR_CLK: + msm_camera_clk_enable(ctrl->dev, + ctrl->clk_info, ctrl->clk_ptr, + ctrl->clk_info_size, false); + break; + case SENSOR_GPIO: + if (pd->seq_val >= SENSOR_GPIO_MAX || + !ctrl->gpio_conf->gpio_num_info) { + pr_err("%s gpio index %d >= max %d\n", __func__, + pd->seq_val, + SENSOR_GPIO_MAX); + continue; + } + if (!ctrl->gpio_conf->gpio_num_info->valid + [pd->seq_val]) + continue; + gpio_set_value_cansleep( + ctrl->gpio_conf->gpio_num_info->gpio_num + [pd->seq_val], + (int) pd->config_val); + break; + case SENSOR_VREG: + if (pd->seq_val == INVALID_VREG) + break; + if (pd->seq_val >= CAM_VREG_MAX) { + pr_err("%s vreg index %d >= max %d\n", __func__, + pd->seq_val, + SENSOR_GPIO_MAX); + continue; + } + + ps = msm_camera_get_power_settings(ctrl, + pd->seq_type, + pd->seq_val); + if (ps) { + if (pd->seq_val < ctrl->num_vreg) + msm_camera_config_single_vreg(ctrl->dev, + &ctrl->cam_vreg + [pd->seq_val], + (struct regulator **) + &ps->data[0], + 0); + else + pr_err("%s:%d:seq_val:%d > num_vreg: %d\n", + __func__, __LINE__, pd->seq_val, + ctrl->num_vreg); + } else + pr_err("%s error in power up/down seq data\n", + __func__); + ret = msm_cam_sensor_handle_reg_gpio(pd->seq_val, + ctrl->gpio_conf, GPIOF_OUT_INIT_LOW); + if (ret < 0) + pr_err("ERR:%s Error while disabling VREG GPIO\n", + __func__); + break; + case SENSOR_I2C_MUX: + if (ctrl->i2c_conf && ctrl->i2c_conf->use_i2c_mux) + msm_camera_disable_i2c_mux(ctrl->i2c_conf); + break; + default: + pr_err("%s error power seq type %d\n", __func__, + pd->seq_type); + break; + } + if (pd->delay > 20) { + msleep(pd->delay); + } else if (pd->delay) { + usleep_range(pd->delay * 1000, + (pd->delay * 1000) + 1000); + } + } + if (ctrl->cam_pinctrl_status) { + ret = pinctrl_select_state(ctrl->pinctrl_info.pinctrl, + ctrl->pinctrl_info.gpio_state_suspend); + if (ret) + pr_err("%s:%d cannot set pin to suspend state", + __func__, __LINE__); + devm_pinctrl_put(ctrl->pinctrl_info.pinctrl); + } + ctrl->cam_pinctrl_status = 0; + msm_camera_request_gpio_table( + ctrl->gpio_conf->cam_gpio_req_tbl, + ctrl->gpio_conf->cam_gpio_req_tbl_size, 0); + CDBG("%s exit\n", __func__); + return 0; +} + diff --git a/camera/sensor/io/msm_camera_dt_util.h b/camera/sensor/io/msm_camera_dt_util.h new file mode 100644 index 00000000..da931ec6 --- /dev/null +++ b/camera/sensor/io/msm_camera_dt_util.h @@ -0,0 +1,68 @@ +/* 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. + */ + +#ifndef MSM_CAMERA_DT_UTIL_H__ +#define MSM_CAMERA_DT_UTIL_H__ + +#include <lenovo_soc/qcom/camera2.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/of.h> +#include "msm_camera_i2c.h" +#include "cam_soc_api.h" + + +#define INVALID_VREG 100 + +int msm_sensor_get_sub_module_index(struct device_node *of_node, + struct msm_sensor_info_t **s_info); + +int msm_sensor_get_dt_actuator_data(struct device_node *of_node, + struct msm_actuator_info **act_info); + +int msm_sensor_get_dt_csi_data(struct device_node *of_node, + struct msm_camera_csi_lane_params **csi_lane_params); + +int msm_camera_get_dt_power_setting_data(struct device_node *of_node, + struct camera_vreg_t *cam_vreg, int num_vreg, + struct msm_camera_power_ctrl_t *power_info); + +int msm_camera_get_dt_gpio_req_tbl(struct device_node *of_node, + struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, + uint16_t gpio_array_size); + +int msm_camera_init_gpio_pin_tbl(struct device_node *of_node, + struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, + uint16_t gpio_array_size); + +int msm_camera_get_dt_vreg_data(struct device_node *of_node, + struct camera_vreg_t **cam_vreg, int *num_vreg); + +int msm_camera_power_up(struct msm_camera_power_ctrl_t *ctrl, + enum msm_camera_device_type_t device_type, + struct msm_camera_i2c_client *sensor_i2c_client); + +int msm_camera_power_down(struct msm_camera_power_ctrl_t *ctrl, + enum msm_camera_device_type_t device_type, + struct msm_camera_i2c_client *sensor_i2c_client); + +int msm_camera_fill_vreg_params(struct camera_vreg_t *cam_vreg, + int num_vreg, struct msm_sensor_power_setting *power_setting, + uint16_t power_setting_size); + +int msm_camera_pinctrl_init + (struct msm_pinctrl_info *sensor_pctrl, struct device *dev); + +int32_t msm_sensor_driver_get_gpio_data( + struct msm_camera_gpio_conf **gpio_conf, + struct device_node *of_node); +#endif diff --git a/camera/sensor/io/msm_camera_i2c.h b/camera/sensor/io/msm_camera_i2c.h new file mode 100644 index 00000000..507448e0 --- /dev/null +++ b/camera/sensor/io/msm_camera_i2c.h @@ -0,0 +1,155 @@ +/* Copyright (c) 2011-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. + */ + +#ifndef MSM_CAMERA_CCI_I2C_H +#define MSM_CAMERA_CCI_I2C_H + +#include <linux/delay.h> +#include <media/v4l2-subdev.h> +#include <lenovo_media/msm_cam_sensor.h> + +#define I2C_POLL_TIME_MS 5 +#define MAX_POLL_DELAY_MS 100 + +#define I2C_COMPARE_MATCH 0 +#define I2C_COMPARE_MISMATCH 1 + +struct msm_camera_i2c_client { + struct msm_camera_i2c_fn_t *i2c_func_tbl; + struct i2c_client *client; + struct msm_camera_cci_client *cci_client; + struct msm_camera_spi_client *spi_client; + enum msm_camera_i2c_reg_addr_type addr_type; +}; + +struct msm_camera_i2c_fn_t { + int (*i2c_read)(struct msm_camera_i2c_client *, uint32_t, uint16_t *, + enum msm_camera_i2c_data_type); + int32_t (*i2c_read_seq)(struct msm_camera_i2c_client *, uint32_t, + uint8_t *, uint32_t); + int (*i2c_write)(struct msm_camera_i2c_client *, uint32_t, uint16_t, + enum msm_camera_i2c_data_type); + int (*i2c_write_seq)(struct msm_camera_i2c_client *, uint32_t , + uint8_t *, uint32_t); + int32_t (*i2c_write_table)(struct msm_camera_i2c_client *, + struct msm_camera_i2c_reg_setting *); + int32_t (*i2c_write_seq_table)(struct msm_camera_i2c_client *, + struct msm_camera_i2c_seq_reg_setting *); + int32_t (*i2c_write_table_w_microdelay) + (struct msm_camera_i2c_client *, + struct msm_camera_i2c_reg_setting *); + int32_t (*i2c_util)(struct msm_camera_i2c_client *, uint16_t); + int32_t (*i2c_write_conf_tbl)(struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size, + enum msm_camera_i2c_data_type data_type); + int32_t (*i2c_poll)(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t data, + enum msm_camera_i2c_data_type data_type, uint32_t delay_ms); + int32_t (*i2c_read_burst)(struct msm_camera_i2c_client *client, + uint32_t read_byte, uint8_t *buffer, uint32_t addr, + enum msm_camera_i2c_data_type data_type); + int32_t (*i2c_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 addr, + enum msm_camera_i2c_data_type data_type); + int32_t (*i2c_write_table_async)(struct msm_camera_i2c_client *, + struct msm_camera_i2c_reg_setting *); + int32_t (*i2c_write_table_sync)(struct msm_camera_i2c_client *, + struct msm_camera_i2c_reg_setting *); + int32_t (*i2c_write_table_sync_block)(struct msm_camera_i2c_client *, + struct msm_camera_i2c_reg_setting *); +}; + +int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t *data, + enum msm_camera_i2c_data_type data_type); + +int32_t msm_camera_cci_i2c_read_seq(struct msm_camera_i2c_client *client, + uint32_t addr, uint8_t *data, uint32_t num_byte); + +int32_t msm_camera_cci_i2c_write(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t data, + enum msm_camera_i2c_data_type data_type); + +int32_t msm_camera_cci_i2c_write_seq(struct msm_camera_i2c_client *client, + uint32_t addr, uint8_t *data, uint32_t num_byte); + +int32_t msm_camera_cci_i2c_write_table( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_setting *write_setting); + +int32_t msm_camera_cci_i2c_write_table_async( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_setting *write_setting); + +int32_t msm_camera_cci_i2c_write_table_sync( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_setting *write_setting); + +int32_t msm_camera_cci_i2c_write_table_sync_block( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_setting *write_setting); + +int32_t msm_camera_cci_i2c_write_seq_table( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_seq_reg_setting *write_setting); + +int32_t msm_camera_cci_i2c_write_table_w_microdelay( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_setting *write_setting); + +int32_t msm_camera_cci_i2c_write_conf_tbl( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size, + enum msm_camera_i2c_data_type data_type); + +int32_t msm_sensor_cci_i2c_util(struct msm_camera_i2c_client *client, + uint16_t cci_cmd); + +int32_t msm_camera_cci_i2c_poll(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t data, + enum msm_camera_i2c_data_type data_type, uint32_t delay_ms); + +int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t *data, + enum msm_camera_i2c_data_type data_type); + +int32_t msm_camera_qup_i2c_read_seq(struct msm_camera_i2c_client *client, + uint32_t addr, uint8_t *data, uint32_t num_byte); + +int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t data, + enum msm_camera_i2c_data_type data_type); + +int32_t msm_camera_qup_i2c_write_seq(struct msm_camera_i2c_client *client, + uint32_t addr, uint8_t *data, uint32_t num_byte); + +int32_t msm_camera_qup_i2c_write_table(struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_setting *write_setting); + +int32_t msm_camera_qup_i2c_write_seq_table(struct msm_camera_i2c_client *client, + struct msm_camera_i2c_seq_reg_setting *write_setting); + +int32_t msm_camera_qup_i2c_write_table_w_microdelay( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_setting *write_setting); + +int32_t msm_camera_qup_i2c_write_conf_tbl( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size, + enum msm_camera_i2c_data_type data_type); + +int32_t msm_camera_qup_i2c_poll(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t data, + enum msm_camera_i2c_data_type data_type, uint32_t delay_ms); + +#endif diff --git a/camera/sensor/io/msm_camera_i2c_mux.c b/camera/sensor/io/msm_camera_i2c_mux.c new file mode 100644 index 00000000..a1cc8d5f --- /dev/null +++ b/camera/sensor/io/msm_camera_i2c_mux.c @@ -0,0 +1,180 @@ +/* Copyright (c) 2011-2014, 2016, The Linux Foundatation. 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 "msm_camera_i2c_mux.h" + +/* TODO move this somewhere else */ +#define MSM_I2C_MUX_DRV_NAME "msm_cam_i2c_mux" +static int msm_i2c_mux_config(struct i2c_mux_device *mux_device, uint8_t *mode) +{ + uint32_t val; + val = msm_camera_io_r(mux_device->ctl_base); + if (*mode == MODE_DUAL) { + msm_camera_io_w(val | 0x3, mux_device->ctl_base); + } else if (*mode == MODE_L) { + msm_camera_io_w(((val | 0x2) & ~(0x1)), mux_device->ctl_base); + val = msm_camera_io_r(mux_device->ctl_base); + CDBG("the camio mode config left value is %d\n", val); + } else { + msm_camera_io_w(((val | 0x1) & ~(0x2)), mux_device->ctl_base); + val = msm_camera_io_r(mux_device->ctl_base); + CDBG("the camio mode config right value is %d\n", val); + } + return 0; +} + +static int msm_i2c_mux_init(struct i2c_mux_device *mux_device) +{ + int rc = 0, val = 0; + if (mux_device->use_count == 0) { + val = msm_camera_io_r(mux_device->rw_base); + msm_camera_io_w((val | 0x200), mux_device->rw_base); + } + mux_device->use_count++; + return 0; +}; + +static int msm_i2c_mux_release(struct i2c_mux_device *mux_device) +{ + int val = 0; + mux_device->use_count--; + if (mux_device->use_count == 0) { + val = msm_camera_io_r(mux_device->rw_base); + msm_camera_io_w((val & ~0x200), mux_device->rw_base); + } + return 0; +} + +static long msm_i2c_mux_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct i2c_mux_device *mux_device; + int rc = 0; + mux_device = v4l2_get_subdevdata(sd); + if (mux_device == NULL) { + rc = -ENOMEM; + return rc; + } + mutex_lock(&mux_device->mutex); + switch (cmd) { + case VIDIOC_MSM_I2C_MUX_CFG: + rc = msm_i2c_mux_config(mux_device, (uint8_t *) arg); + break; + case VIDIOC_MSM_I2C_MUX_INIT: + rc = msm_i2c_mux_init(mux_device); + break; + case VIDIOC_MSM_I2C_MUX_RELEASE: + rc = msm_i2c_mux_release(mux_device); + break; + default: + rc = -ENOIOCTLCMD; + } + mutex_unlock(&mux_device->mutex); + return rc; +} + +static struct v4l2_subdev_core_ops msm_i2c_mux_subdev_core_ops = { + .ioctl = &msm_i2c_mux_subdev_ioctl, +}; + +static const struct v4l2_subdev_ops msm_i2c_mux_subdev_ops = { + .core = &msm_i2c_mux_subdev_core_ops, +}; + +static int i2c_mux_probe(struct platform_device *pdev) +{ + struct i2c_mux_device *mux_device; + int rc = 0; + CDBG("%s: device id = %d\n", __func__, pdev->id); + mux_device = kzalloc(sizeof(struct i2c_mux_device), GFP_KERNEL); + if (!mux_device) { + pr_err("%s: no enough memory\n", __func__); + return -ENOMEM; + } + + v4l2_subdev_init(&mux_device->subdev, &msm_i2c_mux_subdev_ops); + v4l2_set_subdevdata(&mux_device->subdev, mux_device); + platform_set_drvdata(pdev, &mux_device->subdev); + mutex_init(&mux_device->mutex); + + mux_device->ctl_base = msm_camera_get_reg_base(pdev, + "i2c_mux_ctl", true); + if (!mux_device->ctl_base) { + pr_err("%s: no mem resource?\n", __func__); + rc = -ENODEV; + goto ctl_base_failed; + } + mux_device->rw_base = msm_camera_get_reg_base(pdev, "i2c_mux_rw", true); + if (!mux_device->rw_mem) { + pr_err("%s: no mem resource?\n", __func__); + rc = -ENODEV; + goto rw_base_failed; + } + mux_device->pdev = pdev; + return 0; + +rw_base_failed: + msm_camera_put_reg_base(pdev, mux_device->ctl_base, + "i2c_mux_ctl", true); +ctl_base_failed: + mutex_destroy(&mux_device->mutex); + kfree(mux_device); + return 0; +} + +static int i2c_mux_remove(struct platform_device *pdev) +{ + struct v4l2_subdev *sub_dev = platform_get_drvdata(pdev); + struct i2c_mux_device *mux_device; + + if (!sub_dev) { + pr_err("%s: sub device is NULL\n", __func__); + return 0; + } + + mux_device = (struct mux_device *)v4l2_get_subdevdata(sub_dev); + if (!mux_device) { + pr_err("%s: sub device is NULL\n", __func__); + return 0; + } + + msm_camera_put_reg_base(pdev, mux_device->rw_base, "i2c_mux_ctl", true); + msm_camera_put_reg_base(pdev, mux_device->ctl_base, "i2c_mux_rw", true); +} + +static struct platform_driver i2c_mux_driver = { + .probe = i2c_mux_probe, + .remove = i2c_mux_remove, + .driver = { + .name = MSM_I2C_MUX_DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init msm_camera_i2c_mux_init_module(void) +{ + return platform_driver_register(&i2c_mux_driver); +} + +static void __exit msm_camera_i2c_mux_exit_module(void) +{ + platform_driver_unregister(&i2c_mux_driver); +} + +module_init(msm_camera_i2c_mux_init_module); +module_exit(msm_camera_i2c_mux_exit_module); +MODULE_DESCRIPTION("MSM Camera I2C mux driver"); +MODULE_LICENSE("GPL v2"); diff --git a/camera/sensor/io/msm_camera_i2c_mux.h b/camera/sensor/io/msm_camera_i2c_mux.h new file mode 100644 index 00000000..422de4ef --- /dev/null +++ b/camera/sensor/io/msm_camera_i2c_mux.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2011-2014, 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. + */ + +#ifndef MSM_I2C_MUX_H +#define MSM_I2C_MUX_H + +#include <linux/io.h> +#include <media/v4l2-subdev.h> + +struct i2c_mux_device { + struct platform_device *pdev; + struct v4l2_subdev subdev; + void __iomem *ctl_base; + void __iomem *rw_base; + struct mutex mutex; + unsigned use_count; +}; + +struct i2c_mux_cfg_params { + struct v4l2_subdev *subdev; + void *parms; +}; + +#define VIDIOC_MSM_I2C_MUX_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct i2c_mux_cfg_params) + +#define VIDIOC_MSM_I2C_MUX_INIT \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 14, struct v4l2_subdev*) + +#define VIDIOC_MSM_I2C_MUX_RELEASE \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct v4l2_subdev*) + +#endif diff --git a/camera/sensor/io/msm_camera_qup_i2c.c b/camera/sensor/io/msm_camera_qup_i2c.c new file mode 100644 index 00000000..3b101798 --- /dev/null +++ b/camera/sensor/io/msm_camera_qup_i2c.c @@ -0,0 +1,557 @@ +/* Copyright (c) 2011, 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_i2c.h" + +#undef CDBG +#ifdef CONFIG_MSMB_CAMERA_DEBUG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) +#define S_I2C_DBG(fmt, args...) pr_debug(fmt, ##args) +#else +#define CDBG(fmt, args...) do { } while (0) +#define S_I2C_DBG(fmt, args...) do { } while (0) +#endif + +static int32_t msm_camera_qup_i2c_rxdata( + struct msm_camera_i2c_client *dev_client, unsigned char *rxdata, + int data_length) +{ + int32_t rc = 0; + uint16_t saddr = dev_client->client->addr >> 1; + struct i2c_msg msgs[] = { + { + .addr = saddr, + .flags = 0, + .len = dev_client->addr_type, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = data_length, + .buf = rxdata, + }, + }; + rc = i2c_transfer(dev_client->client->adapter, msgs, 2); + if (rc < 0) + S_I2C_DBG("msm_camera_qup_i2c_rxdata failed 0x%x\n", saddr); + return rc; +} + +static int32_t msm_camera_qup_i2c_txdata( + struct msm_camera_i2c_client *dev_client, unsigned char *txdata, + int length) +{ + int32_t rc = 0; + uint16_t saddr = dev_client->client->addr >> 1; + struct i2c_msg msg[] = { + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, + }; + rc = i2c_transfer(dev_client->client->adapter, msg, 1); + if (rc < 0) + S_I2C_DBG("msm_camera_qup_i2c_txdata faild 0x%x\n", saddr); + return rc; +} + +int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t *data, + enum msm_camera_i2c_data_type data_type) +{ + int32_t rc = -EFAULT; + unsigned char buf[client->addr_type+data_type]; + + if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR + && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR) + || (data_type != MSM_CAMERA_I2C_BYTE_DATA + && data_type != MSM_CAMERA_I2C_WORD_DATA)) + return rc; + + if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) { + buf[0] = addr; + } else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) { + buf[0] = addr >> BITS_PER_BYTE; + buf[1] = addr; + } + rc = msm_camera_qup_i2c_rxdata(client, buf, data_type); + if (rc < 0) { + S_I2C_DBG("%s fail\n", __func__); + return rc; + } + + if (data_type == MSM_CAMERA_I2C_BYTE_DATA) + *data = buf[0]; + else + *data = buf[0] << 8 | buf[1]; + + S_I2C_DBG("%s addr = 0x%x data: 0x%x\n", __func__, addr, *data); + return rc; +} + +int32_t msm_camera_qup_i2c_read_seq(struct msm_camera_i2c_client *client, + uint32_t addr, uint8_t *data, uint32_t num_byte) +{ + int32_t rc = -EFAULT; + unsigned char buf[client->addr_type+num_byte]; + int i; + + if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR + && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR) + || num_byte == 0) + return rc; + + if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) { + buf[0] = addr; + } else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) { + buf[0] = addr >> BITS_PER_BYTE; + buf[1] = addr; + } + rc = msm_camera_qup_i2c_rxdata(client, buf, num_byte); + if (rc < 0) { + S_I2C_DBG("%s fail\n", __func__); + return rc; + } + + S_I2C_DBG("%s addr = 0x%x", __func__, addr); + for (i = 0; i < num_byte; i++) { + data[i] = buf[i]; + S_I2C_DBG("Byte %d: 0x%x\n", i, buf[i]); + S_I2C_DBG("Data: 0x%x\n", data[i]); + } + return rc; +} + +int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t data, + enum msm_camera_i2c_data_type data_type) +{ + int32_t rc = -EFAULT; + unsigned char buf[client->addr_type+data_type]; + uint8_t len = 0; + + if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR + && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR) + || (data_type != MSM_CAMERA_I2C_BYTE_DATA + && data_type != MSM_CAMERA_I2C_WORD_DATA)) + return rc; + + S_I2C_DBG("%s reg addr = 0x%x data type: %d\n", + __func__, addr, data_type); + if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) { + buf[0] = addr; + S_I2C_DBG("%s byte %d: 0x%x\n", __func__, + len, buf[len]); + len = 1; + } else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) { + buf[0] = addr >> BITS_PER_BYTE; + buf[1] = addr; + S_I2C_DBG("%s byte %d: 0x%x\n", __func__, + len, buf[len]); + S_I2C_DBG("%s byte %d: 0x%x\n", __func__, + len+1, buf[len+1]); + len = 2; + } + S_I2C_DBG("Data: 0x%x\n", data); + if (data_type == MSM_CAMERA_I2C_BYTE_DATA) { + buf[len] = data; + S_I2C_DBG("Byte %d: 0x%x\n", len, buf[len]); + len += 1; + } else if (data_type == MSM_CAMERA_I2C_WORD_DATA) { + buf[len] = data >> BITS_PER_BYTE; + buf[len+1] = data; + S_I2C_DBG("Byte %d: 0x%x\n", len, buf[len]); + S_I2C_DBG("Byte %d: 0x%x\n", len+1, buf[len+1]); + len += 2; + } + rc = msm_camera_qup_i2c_txdata(client, buf, len); + if (rc < 0) + S_I2C_DBG("%s fail\n", __func__); + return rc; +} + +int32_t msm_camera_qup_i2c_write_seq(struct msm_camera_i2c_client *client, + uint32_t addr, uint8_t *data, uint32_t num_byte) +{ + int32_t rc = -EFAULT; + unsigned char buf[client->addr_type+num_byte]; + uint8_t len = 0, i = 0; + + if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR + && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR) + || num_byte == 0) + return rc; + + S_I2C_DBG("%s reg addr = 0x%x num bytes: %d\n", + __func__, addr, num_byte); + if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) { + buf[0] = addr; + S_I2C_DBG("%s byte %d: 0x%x\n", __func__, + len, buf[len]); + len = 1; + } else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) { + buf[0] = addr >> BITS_PER_BYTE; + buf[1] = addr; + S_I2C_DBG("%s byte %d: 0x%x\n", __func__, + len, buf[len]); + S_I2C_DBG("%s byte %d: 0x%x\n", __func__, + len+1, buf[len+1]); + len = 2; + } + if (num_byte > I2C_SEQ_REG_DATA_MAX) { + pr_err("%s: num_byte=%d clamped to max supported %d\n", + __func__, num_byte, I2C_SEQ_REG_DATA_MAX); + num_byte = I2C_SEQ_REG_DATA_MAX; + } + for (i = 0; i < num_byte; i++) { + buf[i+len] = data[i]; + S_I2C_DBG("Byte %d: 0x%x\n", i+len, buf[i+len]); + S_I2C_DBG("Data: 0x%x\n", data[i]); + } + rc = msm_camera_qup_i2c_txdata(client, buf, len+num_byte); + if (rc < 0) + S_I2C_DBG("%s fail\n", __func__); + return rc; +} + +int32_t msm_camera_qup_i2c_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++) { + CDBG("%s addr 0x%x data 0x%x\n", __func__, + reg_setting->reg_addr, reg_setting->reg_data); + + rc = msm_camera_qup_i2c_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; +} + +int32_t msm_camera_qup_i2c_write_seq_table(struct msm_camera_i2c_client *client, + struct msm_camera_i2c_seq_reg_setting *write_setting) +{ + int i; + int32_t rc = -EFAULT; + struct msm_camera_i2c_seq_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)) { + pr_err("%s Invalide addr type %d\n", __func__, + write_setting->addr_type); + 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++) { + rc = msm_camera_qup_i2c_write_seq(client, reg_setting->reg_addr, + reg_setting->reg_data, reg_setting->reg_data_size); + 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; +} + +int32_t msm_camera_qup_i2c_write_table_w_microdelay( + 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 = NULL; + + if (!client || !write_setting) + return rc; + + if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR + && client->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; + for (i = 0; i < write_setting->size; i++) { + rc = msm_camera_qup_i2c_write(client, reg_setting->reg_addr, + reg_setting->reg_data, write_setting->data_type); + if (rc < 0) + break; + if (reg_setting->delay) + usleep_range(reg_setting->delay, + reg_setting->delay + 1000); + reg_setting++; + } + return rc; +} + +static int32_t msm_camera_qup_i2c_compare( + struct msm_camera_i2c_client *client, uint32_t addr, uint16_t data, + enum msm_camera_i2c_data_type data_type) +{ + int32_t rc; + uint16_t reg_data = 0; + int data_len = 0; + switch (data_type) { + case MSM_CAMERA_I2C_BYTE_DATA: + case MSM_CAMERA_I2C_WORD_DATA: + data_len = data_type; + break; + case MSM_CAMERA_I2C_SET_BYTE_MASK: + case MSM_CAMERA_I2C_UNSET_BYTE_MASK: + data_len = MSM_CAMERA_I2C_BYTE_DATA; + break; + case MSM_CAMERA_I2C_SET_WORD_MASK: + case MSM_CAMERA_I2C_UNSET_WORD_MASK: + data_len = MSM_CAMERA_I2C_WORD_DATA; + break; + default: + pr_err("%s: Unsupport data type: %d\n", __func__, data_type); + break; + } + + rc = msm_camera_qup_i2c_read(client, addr, ®_data, data_len); + if (rc < 0) + return rc; + + rc = I2C_COMPARE_MISMATCH; + switch (data_type) { + case MSM_CAMERA_I2C_BYTE_DATA: + case MSM_CAMERA_I2C_WORD_DATA: + if (data == reg_data) + rc = I2C_COMPARE_MATCH; + break; + case MSM_CAMERA_I2C_SET_BYTE_MASK: + case MSM_CAMERA_I2C_SET_WORD_MASK: + if ((reg_data & data) == data) + rc = I2C_COMPARE_MATCH; + break; + case MSM_CAMERA_I2C_UNSET_BYTE_MASK: + case MSM_CAMERA_I2C_UNSET_WORD_MASK: + if (!(reg_data & data)) + rc = I2C_COMPARE_MATCH; + break; + default: + pr_err("%s: Unsupport data type: %d\n", __func__, data_type); + break; + } + + S_I2C_DBG("%s: Register and data match result %d\n", __func__, + rc); + return rc; +} + +int32_t msm_camera_qup_i2c_poll(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t data, + enum msm_camera_i2c_data_type data_type, uint32_t delay_ms) +{ + int32_t rc = 0; + int i; + S_I2C_DBG("%s: addr: 0x%x data: 0x%x dt: %d\n", + __func__, addr, data, data_type); + + if (delay_ms > MAX_POLL_DELAY_MS) { + pr_err("%s:%d invalid delay = %d max_delay = %d\n", + __func__, __LINE__, delay_ms, MAX_POLL_DELAY_MS); + return -EINVAL; + } + + for (i = 0; i < delay_ms; i++) { + rc = msm_camera_qup_i2c_compare(client, + addr, data, data_type); + if (rc < 0) { + pr_err("%s:%d qup_i2c_compare failed rc = %d", __func__, + __LINE__, rc); + break; + } + if (rc == I2C_COMPARE_MISMATCH) + break; + usleep_range(1000, 1010); + } + return rc; +} + +static int32_t msm_camera_qup_i2c_set_mask(struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t mask, + enum msm_camera_i2c_data_type data_type, uint16_t set_mask) +{ + int32_t rc; + uint16_t reg_data; + + rc = msm_camera_qup_i2c_read(client, addr, ®_data, data_type); + if (rc < 0) { + S_I2C_DBG("%s read fail\n", __func__); + return rc; + } + S_I2C_DBG("%s addr: 0x%x data: 0x%x setmask: 0x%x\n", + __func__, addr, reg_data, mask); + + if (set_mask) + reg_data |= mask; + else + reg_data &= ~mask; + S_I2C_DBG("%s write: 0x%x\n", __func__, reg_data); + + rc = msm_camera_qup_i2c_write(client, addr, reg_data, data_type); + if (rc < 0) + S_I2C_DBG("%s write fail\n", __func__); + + return rc; +} + +static int32_t msm_camera_qup_i2c_set_write_mask_data( + struct msm_camera_i2c_client *client, + uint32_t addr, uint16_t data, int16_t mask, + enum msm_camera_i2c_data_type data_type) +{ + int32_t rc; + uint16_t reg_data; + CDBG("%s\n", __func__); + if (mask == -1) + return 0; + if (mask == 0) { + rc = msm_camera_qup_i2c_write(client, addr, data, data_type); + } else { + rc = msm_camera_qup_i2c_read(client, addr, ®_data, + data_type); + if (rc < 0) { + CDBG("%s read fail\n", __func__); + return rc; + } + reg_data &= ~mask; + reg_data |= (data & mask); + rc = msm_camera_qup_i2c_write(client, addr, reg_data, + data_type); + if (rc < 0) + CDBG("%s write fail\n", __func__); + } + return rc; +} + + +int32_t msm_camera_qup_i2c_write_conf_tbl( + struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size, + enum msm_camera_i2c_data_type data_type) +{ + int i; + int32_t rc = -EFAULT; + pr_err("%s, E. ", __func__); + for (i = 0; i < size; i++) { + enum msm_camera_i2c_data_type dt; + if (reg_conf_tbl->cmd_type == MSM_CAMERA_I2C_CMD_POLL) { + rc = msm_camera_qup_i2c_poll(client, + reg_conf_tbl->reg_addr, + reg_conf_tbl->reg_data, + reg_conf_tbl->dt, I2C_POLL_TIME_MS); + } else { + if (reg_conf_tbl->dt == 0) + dt = data_type; + else + dt = reg_conf_tbl->dt; + switch (dt) { + case MSM_CAMERA_I2C_BYTE_DATA: + case MSM_CAMERA_I2C_WORD_DATA: + rc = msm_camera_qup_i2c_write( + client, + reg_conf_tbl->reg_addr, + reg_conf_tbl->reg_data, dt); + break; + case MSM_CAMERA_I2C_SET_BYTE_MASK: + rc = msm_camera_qup_i2c_set_mask(client, + reg_conf_tbl->reg_addr, + reg_conf_tbl->reg_data, + MSM_CAMERA_I2C_BYTE_DATA, 1); + break; + case MSM_CAMERA_I2C_UNSET_BYTE_MASK: + rc = msm_camera_qup_i2c_set_mask(client, + reg_conf_tbl->reg_addr, + reg_conf_tbl->reg_data, + MSM_CAMERA_I2C_BYTE_DATA, 0); + break; + case MSM_CAMERA_I2C_SET_WORD_MASK: + rc = msm_camera_qup_i2c_set_mask(client, + reg_conf_tbl->reg_addr, + reg_conf_tbl->reg_data, + MSM_CAMERA_I2C_WORD_DATA, 1); + break; + case MSM_CAMERA_I2C_UNSET_WORD_MASK: + rc = msm_camera_qup_i2c_set_mask(client, + reg_conf_tbl->reg_addr, + reg_conf_tbl->reg_data, + MSM_CAMERA_I2C_WORD_DATA, 0); + break; + case MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA: + rc = msm_camera_qup_i2c_set_write_mask_data( + client, + reg_conf_tbl->reg_addr, + reg_conf_tbl->reg_data, + reg_conf_tbl->mask, + MSM_CAMERA_I2C_BYTE_DATA); + break; + default: + pr_err("%s: Unsupport data type: %d\n", + __func__, dt); + break; + } + } + if (rc < 0) + break; + reg_conf_tbl++; + } + return rc; +} + 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; +} diff --git a/camera/sensor/io/msm_camera_spi.h b/camera/sensor/io/msm_camera_spi.h new file mode 100644 index 00000000..5e0dc425 --- /dev/null +++ b/camera/sensor/io/msm_camera_spi.h @@ -0,0 +1,120 @@ +/* Copyright (c) 2013-2014, 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. + */ + +#ifndef __MSM_CAMERA_SPI_H +#define __MSM_CAMERA_SPI_H + +#include <linux/spi/spi.h> +#include <lenovo_media/msm_cam_sensor.h> +#include "msm_camera_i2c.h" + +#define MAX_SPI_SIZE 110 +#define SPI_DYNAMIC_ALLOC + +/** + * Common SPI communication scheme + * tx: <opcode>[addr][wait][write buffer] + * rx: [read buffer] + * Some inst require polling busy reg until it's done + */ +struct msm_camera_spi_inst { + uint8_t opcode; /* one-byte opcode */ + uint8_t addr_len; /* addr len in bytes */ + uint8_t dummy_len; /* setup cycles */ + uint8_t delay_intv; /* delay intv for this inst (ms) */ + uint8_t delay_count; /* total delay count for this inst */ +}; + +struct msm_spi_write_burst_data { + u8 data_msb; + u8 data_lsb; +}; + +struct msm_spi_write_burst_packet { + u8 cmd; + u8 addr_msb; + u8 addr_lsb; + struct msm_spi_write_burst_data data_arr[MAX_SPI_SIZE]; +}; + +struct msm_camera_burst_info { + uint32_t burst_addr; + uint32_t burst_start; + uint32_t burst_len; + uint32_t chunk_size; +}; + +struct msm_camera_spi_inst_tbl { + struct msm_camera_spi_inst read; + struct msm_camera_spi_inst read_seq; + struct msm_camera_spi_inst query_id; + struct msm_camera_spi_inst page_program; + struct msm_camera_spi_inst write_enable; + struct msm_camera_spi_inst read_status; + struct msm_camera_spi_inst erase; +}; + +struct msm_camera_spi_client { + struct spi_device *spi_master; + struct msm_camera_spi_inst_tbl cmd_tbl; + uint8_t device_id0; + uint8_t device_id1; + uint8_t mfr_id0; + uint8_t mfr_id1; + uint8_t retry_delay; /* ms */ + uint8_t retries; /* retry times upon failure */ + uint8_t busy_mask; /* busy bit in status reg */ + uint16_t page_size; /* page size for page program */ + uint32_t erase_size; /* minimal erase size */ +}; + +static __always_inline +uint16_t msm_camera_spi_get_hlen(struct msm_camera_spi_inst *inst) +{ + return sizeof(inst->opcode) + inst->addr_len + inst->dummy_len; +} + +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 msm_camera_spi_read_seq(struct msm_camera_i2c_client *client, + uint32_t addr, uint8_t *data, uint32_t num_byte); + +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); + +int32_t msm_camera_spi_query_id(struct msm_camera_i2c_client *client, + uint32_t addr, uint8_t *data, uint32_t num_byte); + +int32_t msm_camera_spi_write_seq(struct msm_camera_i2c_client *client, + uint32_t addr, uint8_t *data, uint32_t num_byte); + +int32_t msm_camera_spi_erase(struct msm_camera_i2c_client *client, + uint32_t addr, uint32_t size); + +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); + +int32_t msm_camera_spi_write_table(struct msm_camera_i2c_client *client, + struct msm_camera_i2c_reg_setting *write_setting); + +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 addr, + enum msm_camera_i2c_data_type data_type); + +int32_t msm_camera_spi_read_burst(struct msm_camera_i2c_client *client, + uint32_t read_byte, uint8_t *buffer, uint32_t addr, + enum msm_camera_i2c_data_type data_type); + +#endif |
