| /* Copyright (c) 2011-2013, 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 <mach/camera2.h> |
| #include "msm_camera_i2c.h" |
| #include "msm_cci.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 |
| |
| #define I2C_COMPARE_MATCH 0 |
| #define I2C_COMPARE_MISMATCH 1 |
| #define I2C_POLL_MAX_ITERATION 20 |
| |
| 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, uint16_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; |
| 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_conf 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; |
| cci_ctrl.cmd = MSM_CCI_I2C_WRITE; |
| cci_ctrl.cci_info = client->cci_client; |
| cci_ctrl.cfg.cci_i2c_write_cfg.reg_conf_tbl = ®_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, uint16_t num_byte) |
| { |
| int32_t rc = -EFAULT; |
| uint8_t i = 0; |
| struct msm_camera_cci_ctrl cci_ctrl; |
| struct msm_camera_i2c_reg_conf reg_conf_tbl[num_byte]; |
| |
| 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); |
| memset(reg_conf_tbl, 0, |
| num_byte * sizeof(struct msm_camera_i2c_reg_conf)); |
| reg_conf_tbl[0].reg_addr = addr; |
| for (i = 0; i < num_byte; i++) |
| reg_conf_tbl[i].reg_data = data[i]; |
| cci_ctrl.cmd = MSM_CCI_I2C_WRITE; |
| cci_ctrl.cci_info = client->cci_client; |
| cci_ctrl.cfg.cci_i2c_write_cfg.reg_conf_tbl = 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; |
| 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; |
| return rc; |
| } |
| |
| int32_t msm_camera_cci_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++) { |
| rc = msm_camera_cci_i2c_write(client, reg_setting->reg_addr, |
| reg_setting->reg_data, write_setting->data_type); |
| 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_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_tbl *reg_tbl, uint16_t size, |
| enum msm_camera_i2c_data_type data_type) |
| { |
| int i; |
| int32_t rc = -EFAULT; |
| |
| if (!client || !reg_tbl) |
| return rc; |
| |
| 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; |
| |
| for (i = 0; i < size; i++) { |
| rc = msm_camera_cci_i2c_write(client, reg_tbl->reg_addr, |
| reg_tbl->reg_data, data_type); |
| if (rc < 0) |
| return rc; |
| if (reg_tbl->delay) |
| usleep_range(reg_tbl->delay, reg_tbl->delay + 1000); |
| reg_tbl++; |
| } |
| 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) |
| { |
| int32_t rc; |
| int i; |
| S_I2C_DBG("%s: addr: 0x%x data: 0x%x dt: %d\n", |
| __func__, addr, data, data_type); |
| |
| for (i = 0; i < I2C_POLL_MAX_ITERATION; i++) { |
| rc = msm_camera_cci_i2c_compare(client, |
| addr, data, data_type); |
| if (rc == 0 || rc < 0) |
| break; |
| usleep_range(10000, 11000); |
| } |
| 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; |
| 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); |
| } 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; |
| } |