diff options
Diffstat (limited to 'camera/sensor')
59 files changed, 20927 insertions, 0 deletions
diff --git a/camera/sensor/Makefile b/camera/sensor/Makefile new file mode 100644 index 00000000..62804249 --- /dev/null +++ b/camera/sensor/Makefile @@ -0,0 +1,8 @@ +ccflags-y += -Icamera +ccflags-y += -Icamera/common +ccflags-y += -Icamera/msm_vb2 +ccflags-y += -Icamera/camera +ccflags-y += -Icamera/sensor/io +ccflags-y += -Icamera/sensor/cci +obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/ actuator/ eeprom/ ois/ flash/ +obj-$(CONFIG_MSM_CAMERA_SENSOR) += msm_sensor_init.o msm_sensor_driver.o msm_sensor.o diff --git a/camera/sensor/actuator/Makefile b/camera/sensor/actuator/Makefile new file mode 100644 index 00000000..0934cb65 --- /dev/null +++ b/camera/sensor/actuator/Makefile @@ -0,0 +1,5 @@ +ccflags-y += -Icamera +ccflags-y += -Icamera/common +ccflags-y += -Icamera/sensor/io +ccflags-y += -Icamera/sensor/cci +obj-$(CONFIG_MSMB_CAMERA) += msm_actuator.o diff --git a/camera/sensor/actuator/msm_actuator.c b/camera/sensor/actuator/msm_actuator.c new file mode 100644 index 00000000..950a211b --- /dev/null +++ b/camera/sensor/actuator/msm_actuator.c @@ -0,0 +1,2107 @@ +/* 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. + */ + +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include <linux/module.h> +#include "msm_sd.h" +#include "msm_actuator.h" +#include "msm_cci.h" + +DEFINE_MSM_MUTEX(msm_actuator_mutex); + +#undef CDBG +#ifdef MSM_ACTUATOR_DEBUG +#define CDBG(fmt, args...) pr_err(fmt, ##args) +#else +#define CDBG(fmt, args...) pr_debug(fmt, ##args) +#endif + +#include "msm_mot_actuator.c" + +#define PARK_LENS_LONG_STEP 7 +#define PARK_LENS_MID_STEP 5 +#define PARK_LENS_SMALL_STEP 3 +#define MAX_QVALUE 4096 + +static struct v4l2_file_operations msm_actuator_v4l2_subdev_fops; +static int32_t msm_actuator_power_up(struct msm_actuator_ctrl_t *a_ctrl); +static int32_t msm_actuator_power_down(struct msm_actuator_ctrl_t *a_ctrl); + +static struct msm_actuator msm_vcm_actuator_table; +static struct msm_actuator msm_piezo_actuator_table; +static struct msm_actuator msm_hvcm_actuator_table; +static struct msm_actuator msm_bivcm_actuator_table; +static struct msm_actuator msm_mot_hvcm_actuator_table; + +static struct i2c_driver msm_actuator_i2c_driver; +static struct msm_actuator *actuators[] = { + &msm_vcm_actuator_table, + &msm_piezo_actuator_table, + &msm_hvcm_actuator_table, + &msm_bivcm_actuator_table, + &msm_mot_hvcm_actuator_table, +}; + +static int32_t msm_actuator_piezo_set_default_focus( + struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_move_params_t *move_params) +{ + int32_t rc = 0; + struct msm_camera_i2c_reg_setting reg_setting; + CDBG("Enter\n"); + + if (a_ctrl->curr_step_pos != 0) { + a_ctrl->i2c_tbl_index = 0; + a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl, + a_ctrl->initial_code, 0, 0); + a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl, + a_ctrl->initial_code, 0, 0); + reg_setting.reg_setting = a_ctrl->i2c_reg_tbl; + reg_setting.data_type = a_ctrl->i2c_data_type; + reg_setting.size = a_ctrl->i2c_tbl_index; + rc = a_ctrl->i2c_client.i2c_func_tbl-> + i2c_write_table_w_microdelay( + &a_ctrl->i2c_client, ®_setting); + if (rc < 0) { + pr_err("%s: i2c write error:%d\n", + __func__, rc); + return rc; + } + a_ctrl->i2c_tbl_index = 0; + a_ctrl->curr_step_pos = 0; + } + CDBG("Exit\n"); + return rc; +} + +static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t *a_ctrl, + int16_t next_lens_position, uint32_t hw_params, uint16_t delay) +{ + struct msm_actuator_reg_params_t *write_arr = NULL; + uint32_t hw_dword = hw_params; + uint16_t i2c_byte1 = 0, i2c_byte2 = 0; + uint16_t value = 0; + uint32_t size = 0, i = 0; + struct msm_camera_i2c_reg_array *i2c_tbl = NULL; + CDBG("Enter\n"); + + if (a_ctrl == NULL) { + pr_err("failed. actuator ctrl is NULL"); + return; + } + + size = a_ctrl->reg_tbl_size; + write_arr = a_ctrl->reg_tbl; + i2c_tbl = a_ctrl->i2c_reg_tbl; + + for (i = 0; i < size; i++) { + /* check that the index into i2c_tbl cannot grow larger that + the allocated size of i2c_tbl */ + if ((a_ctrl->total_steps + 1) < (a_ctrl->i2c_tbl_index)) + break; + + if (write_arr[i].reg_write_type == MSM_ACTUATOR_WRITE_DAC) { + value = (next_lens_position << + write_arr[i].data_shift) | + ((hw_dword & write_arr[i].hw_mask) >> + write_arr[i].hw_shift); + + if (write_arr[i].reg_addr != 0xFFFF) { + i2c_byte1 = write_arr[i].reg_addr; + i2c_byte2 = value; + if (size != (i+1)) { + i2c_byte2 = value & 0xFF; + CDBG("byte1:0x%x, byte2:0x%x\n", + i2c_byte1, i2c_byte2); + i2c_tbl[a_ctrl->i2c_tbl_index]. + reg_addr = i2c_byte1; + i2c_tbl[a_ctrl->i2c_tbl_index]. + reg_data = i2c_byte2; + i2c_tbl[a_ctrl->i2c_tbl_index]. + delay = 0; + a_ctrl->i2c_tbl_index++; + i++; + i2c_byte1 = write_arr[i].reg_addr; + i2c_byte2 = (value & 0xFF00) >> 8; + } + } else { + i2c_byte1 = (value & 0xFF00) >> 8; + i2c_byte2 = value & 0xFF; + } + } else { + i2c_byte1 = write_arr[i].reg_addr; + i2c_byte2 = (hw_dword & write_arr[i].hw_mask) >> + write_arr[i].hw_shift; + } + CDBG("i2c_byte1:0x%x, i2c_byte2:0x%x\n", i2c_byte1, i2c_byte2); + i2c_tbl[a_ctrl->i2c_tbl_index].reg_addr = i2c_byte1; + i2c_tbl[a_ctrl->i2c_tbl_index].reg_data = i2c_byte2; + i2c_tbl[a_ctrl->i2c_tbl_index].delay = delay; + a_ctrl->i2c_tbl_index++; + } + CDBG("Exit\n"); +} + +static int msm_actuator_bivcm_handle_i2c_ops( + struct msm_actuator_ctrl_t *a_ctrl, + int16_t next_lens_position, uint32_t hw_params, uint16_t delay) +{ + struct msm_actuator_reg_params_t *write_arr = a_ctrl->reg_tbl; + uint32_t hw_dword = hw_params; + uint16_t i2c_byte1 = 0, i2c_byte2 = 0; + uint16_t value = 0, reg_data = 0; + uint32_t size = a_ctrl->reg_tbl_size, i = 0; + int32_t rc = 0; + struct msm_camera_i2c_reg_array i2c_tbl; + struct msm_camera_i2c_reg_setting reg_setting; + enum msm_camera_i2c_reg_addr_type save_addr_type = + a_ctrl->i2c_client.addr_type; + static int16_t last_lens_position = 1; + + CDBG("Enter\n"); + + for (i = 0; i < size; i++) { + reg_setting.size = 1; + switch (write_arr[i].reg_write_type) { + case MSM_ACTUATOR_WRITE_DAC: + value = (next_lens_position << + write_arr[i].data_shift) | + ((hw_dword & write_arr[i].hw_mask) >> + write_arr[i].hw_shift); + if (write_arr[i].reg_addr != 0xFFFF) { + i2c_byte1 = write_arr[i].reg_addr; + i2c_byte2 = value; + } else { + i2c_byte1 = (value & 0xFF00) >> 8; + i2c_byte2 = value & 0xFF; + } + i2c_tbl.reg_addr = i2c_byte1; + i2c_tbl.reg_data = i2c_byte2; + i2c_tbl.delay = delay; + a_ctrl->i2c_tbl_index++; + + reg_setting.reg_setting = &i2c_tbl; + reg_setting.data_type = write_arr[i].data_type; + CDBG("MSM_ACTUATOR_WRITE_DAC reg_setting.data_type:%d\n", reg_setting.data_type); + rc = a_ctrl->i2c_client. + i2c_func_tbl->i2c_write_table_w_microdelay( + &a_ctrl->i2c_client, ®_setting); + if (rc < 0) { + pr_err("i2c write error:%d\n", rc); + return rc; + } + break; + case MSM_ACTUATOR_WRITE: + i2c_tbl.reg_data = write_arr[i].reg_data; + i2c_tbl.reg_addr = write_arr[i].reg_addr; + i2c_tbl.delay = write_arr[i].delay; + reg_setting.reg_setting = &i2c_tbl; + reg_setting.data_type = write_arr[i].data_type; + switch (write_arr[i].addr_type) { + case MSM_ACTUATOR_BYTE_ADDR: + a_ctrl->i2c_client.addr_type = + MSM_CAMERA_I2C_BYTE_ADDR; + break; + case MSM_ACTUATOR_WORD_ADDR: + a_ctrl->i2c_client.addr_type = + MSM_CAMERA_I2C_WORD_ADDR; + break; + default: + pr_err("Unsupport addr type: %d\n", + write_arr[i].addr_type); + break; + } + + rc = a_ctrl->i2c_client. + i2c_func_tbl->i2c_write_table_w_microdelay( + &a_ctrl->i2c_client, ®_setting); + if (rc < 0) { + pr_err("i2c write error:%d\n", rc); + return rc; + } + break; + case MSM_ACTUATOR_WRITE_DIR_REG: + if (next_lens_position > last_lens_position) + i2c_tbl.reg_data = + (uint16_t)(0+write_arr[i].reg_data); + else + i2c_tbl.reg_data = + (uint16_t)(0-write_arr[i].reg_data); + + i2c_tbl.reg_addr = write_arr[i].reg_addr; + i2c_tbl.delay = write_arr[i].delay; + reg_setting.reg_setting = &i2c_tbl; + reg_setting.data_type = write_arr[i].data_type; + switch (write_arr[i].addr_type) { + case MSM_ACTUATOR_BYTE_ADDR: + a_ctrl->i2c_client.addr_type = + MSM_CAMERA_I2C_BYTE_ADDR; + break; + case MSM_ACTUATOR_WORD_ADDR: + a_ctrl->i2c_client.addr_type = + MSM_CAMERA_I2C_WORD_ADDR; + break; + default: + pr_err("Unsupport addr type: %d\n", + write_arr[i].addr_type); + break; + } + + rc = a_ctrl->i2c_client. + i2c_func_tbl->i2c_write_table_w_microdelay( + &a_ctrl->i2c_client, ®_setting); + if (rc < 0) { + pr_err("i2c write error:%d\n", rc); + return rc; + } + break; + case MSM_ACTUATOR_POLL: + switch (write_arr[i].addr_type) { + case MSM_ACTUATOR_BYTE_ADDR: + a_ctrl->i2c_client.addr_type = + MSM_CAMERA_I2C_BYTE_ADDR; + break; + case MSM_ACTUATOR_WORD_ADDR: + a_ctrl->i2c_client.addr_type = + MSM_CAMERA_I2C_WORD_ADDR; + break; + default: + pr_err("Unsupport addr type: %d\n", + write_arr[i].addr_type); + break; + } + + rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_poll( + &a_ctrl->i2c_client, + write_arr[i].reg_addr, + write_arr[i].reg_data, + write_arr[i].data_type, + write_arr[i].delay); + if (rc < 0) { + pr_err("i2c poll error:%d\n", rc); + return rc; + } + break; + case MSM_ACTUATOR_READ_WRITE: + i2c_tbl.reg_addr = write_arr[i].reg_addr; + i2c_tbl.delay = write_arr[i].delay; + reg_setting.reg_setting = &i2c_tbl; + reg_setting.data_type = write_arr[i].data_type; + + switch (write_arr[i].addr_type) { + case MSM_ACTUATOR_BYTE_ADDR: + a_ctrl->i2c_client.addr_type = + MSM_CAMERA_I2C_BYTE_ADDR; + break; + case MSM_ACTUATOR_WORD_ADDR: + a_ctrl->i2c_client.addr_type = + MSM_CAMERA_I2C_WORD_ADDR; + break; + default: + pr_err("Unsupport addr type: %d\n", + write_arr[i].addr_type); + break; + } + rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_read( + &a_ctrl->i2c_client, + write_arr[i].reg_addr, + ®_data, + write_arr[i].data_type); + if (rc < 0) { + pr_err("i2c poll error:%d\n", rc); + return rc; + } + + i2c_tbl.reg_addr = write_arr[i].reg_data; + i2c_tbl.reg_data = reg_data; + i2c_tbl.delay = write_arr[i].delay; + reg_setting.reg_setting = &i2c_tbl; + reg_setting.data_type = write_arr[i].data_type; + + rc = a_ctrl->i2c_client. + i2c_func_tbl->i2c_write_table_w_microdelay( + &a_ctrl->i2c_client, ®_setting); + if (rc < 0) { + pr_err("i2c write error:%d\n", rc); + return rc; + } + break; + case MSM_ACTUATOR_WRITE_HW_DAMP: + i2c_tbl.reg_addr = write_arr[i].reg_addr; + i2c_tbl.reg_data = (hw_dword & write_arr[i].hw_mask) >> + write_arr[i].hw_shift; + i2c_tbl.delay = 0; + reg_setting.reg_setting = &i2c_tbl; + reg_setting.data_type = write_arr[i].data_type; + CDBG("MSM_ACTUATOR_WRITE_HW_DAMP reg_setting.data_type:%d\n", reg_setting.data_type); + rc = a_ctrl->i2c_client. + i2c_func_tbl->i2c_write_table_w_microdelay( + &a_ctrl->i2c_client, ®_setting); + if (rc < 0) { + pr_err("i2c write error:%d\n", rc); + return rc; + } + break; + default: + pr_err("%s:%d Invalid selection\n", + __func__, __LINE__); + return -EINVAL; + } + a_ctrl->i2c_client.addr_type = save_addr_type; + } + last_lens_position = next_lens_position; + CDBG("Exit (last_lens_position=%d\n", last_lens_position); + return rc; +} + +static int32_t msm_actuator_init_focus(struct msm_actuator_ctrl_t *a_ctrl, + uint16_t size, struct reg_settings_t *settings) +{ + int32_t rc = -EFAULT; + int32_t i = 0; + enum msm_camera_i2c_reg_addr_type save_addr_type; + uint16_t read_position = 0x0; + CDBG("Enter\n"); + + save_addr_type = a_ctrl->i2c_client.addr_type; + for (i = 0; i < size; i++) { + + switch (settings[i].addr_type) { + case MSM_ACTUATOR_BYTE_ADDR: + a_ctrl->i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR; + break; + case MSM_ACTUATOR_WORD_ADDR: + a_ctrl->i2c_client.addr_type = MSM_CAMERA_I2C_WORD_ADDR; + break; + default: + pr_err("Unsupport addr type: %d\n", + settings[i].addr_type); + break; + } + + switch (settings[i].i2c_operation) { + case MSM_ACT_WRITE: + rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write( + &a_ctrl->i2c_client, + settings[i].reg_addr, + settings[i].reg_data, + settings[i].data_type); + if (settings[i].delay > 20) + msleep(settings[i].delay); + else if (0 != settings[i].delay) + usleep_range(settings[i].delay * 1000, + (settings[i].delay * 1000) + 1000); + break; + case MSM_ACT_POLL: + rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_poll( + &a_ctrl->i2c_client, + settings[i].reg_addr, + settings[i].reg_data, + settings[i].data_type, + settings[i].delay); + break; + case MSM_ACT_READ_SET: + rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_read( + &a_ctrl->i2c_client, + settings[i].reg_addr, + &read_position, + settings[i].data_type); + if (rc < 0) { + pr_err("%s: Unable to read!\n", + __func__); + break; + } + rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write( + &a_ctrl->i2c_client, + settings[i].reg_data, + read_position, + settings[i].data_type); + break; + default: + pr_err("Unsupport i2c_operation: %d\n", + settings[i].i2c_operation); + break; + } + + if (rc < 0) { + pr_err("%s:%d fail addr = 0X%X, data = 0X%X, dt = %d", + __func__, __LINE__, settings[i].reg_addr, + settings[i].reg_data, settings[i].data_type); + break; + } + } + + a_ctrl->curr_step_pos = 0; + /* + * Recover register addr_type after the init + * settings are written. + */ + a_ctrl->i2c_client.addr_type = save_addr_type; + CDBG("Exit\n"); + return rc; +} + +static void msm_actuator_write_focus( + struct msm_actuator_ctrl_t *a_ctrl, + uint16_t curr_lens_pos, + struct damping_params_t *damping_params, + int8_t sign_direction, + int16_t code_boundary) +{ + CDBG("Enter\n"); + + if (curr_lens_pos != code_boundary) { + a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl, + code_boundary, damping_params->hw_params, + damping_params->damping_delay); + } + CDBG("Exit\n"); +} + +static int msm_actuator_bivcm_write_focus( + struct msm_actuator_ctrl_t *a_ctrl, + uint16_t curr_lens_pos, + struct damping_params_t *damping_params, + int8_t sign_direction, + int16_t code_boundary) +{ + int16_t next_lens_pos = 0; + uint16_t damping_code_step = 0; + uint16_t wait_time = 0; + int32_t rc = 0; + CDBG("Enter\n"); + + damping_code_step = damping_params->damping_step; + wait_time = damping_params->damping_delay; + + /* Write code based on damping_code_step in a loop */ + for (next_lens_pos = + curr_lens_pos + (sign_direction * damping_code_step); + (sign_direction * next_lens_pos) <= + (sign_direction * code_boundary); + next_lens_pos = + (next_lens_pos + + (sign_direction * damping_code_step))) { + rc = msm_actuator_bivcm_handle_i2c_ops(a_ctrl, + next_lens_pos, damping_params->hw_params, wait_time); + if (rc < 0) { + pr_err("%s:%d msm_actuator_bivcm_handle_i2c_ops failed\n", + __func__, __LINE__); + return rc; + } + curr_lens_pos = next_lens_pos; + } + + if (curr_lens_pos != code_boundary) { + rc = msm_actuator_bivcm_handle_i2c_ops(a_ctrl, + code_boundary, damping_params->hw_params, wait_time); + if (rc < 0) { + pr_err("%s:%d msm_actuator_bivcm_handle_i2c_ops failed\n", + __func__, __LINE__); + return rc; + } + } + CDBG("Exit\n"); + return rc; +} + +static int32_t msm_actuator_piezo_move_focus( + struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_move_params_t *move_params) +{ + int32_t dest_step_position = move_params->dest_step_pos; + struct damping_params_t ringing_params_kernel; + int32_t rc = 0; + int32_t num_steps = move_params->num_steps; + struct msm_camera_i2c_reg_setting reg_setting; + CDBG("Enter\n"); + + if (copy_from_user(&ringing_params_kernel, + &(move_params->ringing_params[0]), + sizeof(struct damping_params_t))) { + pr_err("copy_from_user failed\n"); + return -EFAULT; + } + + if (num_steps <= 0 || num_steps > MAX_NUMBER_OF_STEPS) { + pr_err("num_steps out of range = %d\n", + num_steps); + return -EFAULT; + } + + a_ctrl->i2c_tbl_index = 0; + a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl, + (num_steps * + a_ctrl->region_params[0].code_per_step), + ringing_params_kernel.hw_params, 0); + + reg_setting.reg_setting = a_ctrl->i2c_reg_tbl; + reg_setting.data_type = a_ctrl->i2c_data_type; + reg_setting.size = a_ctrl->i2c_tbl_index; + rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write_table_w_microdelay( + &a_ctrl->i2c_client, ®_setting); + if (rc < 0) { + pr_err("i2c write error:%d\n", rc); + return rc; + } + a_ctrl->i2c_tbl_index = 0; + a_ctrl->curr_step_pos = dest_step_position; + CDBG("Exit\n"); + return rc; +} + +static int32_t msm_actuator_move_focus( + struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_move_params_t *move_params) +{ + int32_t rc = 0; + struct damping_params_t *ringing_params_kernel = NULL; + int8_t sign_dir = move_params->sign_dir; + uint16_t step_boundary = 0; + uint16_t target_step_pos = 0; + int16_t target_lens_pos = 0; + int16_t dest_step_pos = move_params->dest_step_pos; + int16_t curr_lens_pos = 0; + int dir = move_params->dir; + int32_t num_steps = move_params->num_steps; + struct msm_camera_i2c_reg_setting reg_setting; + + CDBG("called, dir %d, num_steps %d\n", dir, num_steps); + + if (dest_step_pos == a_ctrl->curr_step_pos) + return rc; + + if ((sign_dir > MSM_ACTUATOR_MOVE_SIGNED_NEAR) || + (sign_dir < MSM_ACTUATOR_MOVE_SIGNED_FAR)) { + pr_err("Invalid sign_dir = %d\n", sign_dir); + return -EFAULT; + } + if ((dir > MOVE_FAR) || (dir < MOVE_NEAR)) { + pr_err("Invalid direction = %d\n", dir); + return -EFAULT; + } + if (dest_step_pos >= a_ctrl->total_steps) { + pr_err("Step pos greater than total steps = %d\n", + dest_step_pos); + return -EFAULT; + } + if ((a_ctrl->region_size <= 0) || + (a_ctrl->region_size > MAX_ACTUATOR_REGION) || + (!move_params->ringing_params)) { + pr_err("Invalid-region size = %d, ringing_params = %p\n", + a_ctrl->region_size, move_params->ringing_params); + return -EFAULT; + } + /*Allocate memory for damping parameters of all regions*/ + ringing_params_kernel = kmalloc( + sizeof(struct damping_params_t)*(a_ctrl->region_size), + GFP_KERNEL); + if (!ringing_params_kernel) { + pr_err("kmalloc for damping parameters failed\n"); + return -EFAULT; + } + if (copy_from_user(ringing_params_kernel, + &(move_params->ringing_params[0]), + (sizeof(struct damping_params_t))*(a_ctrl->region_size))) { + pr_err("copy_from_user failed\n"); + /*Free the allocated memory for damping parameters*/ + kfree(ringing_params_kernel); + return -EFAULT; + } + curr_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos]; + a_ctrl->i2c_tbl_index = 0; + CDBG("curr_step_pos =%d dest_step_pos =%d curr_lens_pos=%d\n", + a_ctrl->curr_step_pos, dest_step_pos, curr_lens_pos); + + while (a_ctrl->curr_step_pos != dest_step_pos) { + step_boundary = + a_ctrl->region_params[a_ctrl->curr_region_index]. + step_bound[dir]; + if ((dest_step_pos * sign_dir) <= + (step_boundary * sign_dir)) { + + target_step_pos = dest_step_pos; + target_lens_pos = + a_ctrl->step_position_table[target_step_pos]; + a_ctrl->func_tbl->actuator_write_focus(a_ctrl, + curr_lens_pos, + &ringing_params_kernel + [a_ctrl->curr_region_index], + sign_dir, + target_lens_pos); + curr_lens_pos = target_lens_pos; + + } else { + target_step_pos = step_boundary; + target_lens_pos = + a_ctrl->step_position_table[target_step_pos]; + a_ctrl->func_tbl->actuator_write_focus(a_ctrl, + curr_lens_pos, + &ringing_params_kernel + [a_ctrl->curr_region_index], + sign_dir, + target_lens_pos); + curr_lens_pos = target_lens_pos; + + a_ctrl->curr_region_index += sign_dir; + } + a_ctrl->curr_step_pos = target_step_pos; + } + /*Free the memory allocated for damping parameters*/ + kfree(ringing_params_kernel); + + move_params->curr_lens_pos = curr_lens_pos; + reg_setting.reg_setting = a_ctrl->i2c_reg_tbl; + reg_setting.data_type = a_ctrl->i2c_data_type; + reg_setting.size = a_ctrl->i2c_tbl_index; + rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write_table_w_microdelay( + &a_ctrl->i2c_client, ®_setting); + if (rc < 0) { + pr_err("i2c write error:%d\n", rc); + return rc; + } + a_ctrl->i2c_tbl_index = 0; + CDBG("Exit\n"); + + return rc; +} + +static int32_t msm_actuator_bivcm_move_focus( + struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_move_params_t *move_params) +{ + int32_t rc = 0; + struct damping_params_t *ringing_params_kernel = NULL; + int8_t sign_dir = move_params->sign_dir; + uint16_t step_boundary = 0; + uint16_t target_step_pos = 0; + uint16_t target_lens_pos = 0; + int16_t dest_step_pos = move_params->dest_step_pos; + uint16_t curr_lens_pos = 0; + int dir = move_params->dir; + int32_t num_steps = move_params->num_steps; + + if (a_ctrl->step_position_table == NULL) { + pr_err("Step Position Table is NULL"); + return -EFAULT; + } + + CDBG("called, dir %d, num_steps %d\n", dir, num_steps); + + if (dest_step_pos == a_ctrl->curr_step_pos) + return rc; + + if ((sign_dir > MSM_ACTUATOR_MOVE_SIGNED_NEAR) || + (sign_dir < MSM_ACTUATOR_MOVE_SIGNED_FAR)) { + pr_err("Invalid sign_dir = %d\n", sign_dir); + return -EFAULT; + } + if ((dir > MOVE_FAR) || (dir < MOVE_NEAR)) { + pr_err("Invalid direction = %d\n", dir); + return -EFAULT; + } + if (dest_step_pos > a_ctrl->total_steps) { + pr_err("Step pos greater than total steps = %d\n", + dest_step_pos); + return -EFAULT; + } + if ((a_ctrl->region_size <= 0) || + (a_ctrl->region_size > MAX_ACTUATOR_REGION) || + (!move_params->ringing_params)) { + pr_err("Invalid-region size = %d, ringing_params = %p\n", + a_ctrl->region_size, move_params->ringing_params); + return -EFAULT; + } + /*Allocate memory for damping parameters of all regions*/ + ringing_params_kernel = kmalloc( + sizeof(struct damping_params_t)*(a_ctrl->region_size), + GFP_KERNEL); + if (!ringing_params_kernel) { + pr_err("kmalloc for damping parameters failed\n"); + return -EFAULT; + } + if (copy_from_user(ringing_params_kernel, + &(move_params->ringing_params[0]), + (sizeof(struct damping_params_t))*(a_ctrl->region_size))) { + pr_err("copy_from_user failed\n"); + /*Free the allocated memory for damping parameters*/ + kfree(ringing_params_kernel); + return -EFAULT; + } + curr_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos]; + a_ctrl->i2c_tbl_index = 0; + CDBG("curr_step_pos =%d dest_step_pos =%d curr_lens_pos=%d\n", + a_ctrl->curr_step_pos, dest_step_pos, curr_lens_pos); + + while (a_ctrl->curr_step_pos != dest_step_pos) { + step_boundary = + a_ctrl->region_params[a_ctrl->curr_region_index]. + step_bound[dir]; + if ((dest_step_pos * sign_dir) <= + (step_boundary * sign_dir)) { + + target_step_pos = dest_step_pos; + target_lens_pos = + a_ctrl->step_position_table[target_step_pos]; + rc = msm_actuator_bivcm_write_focus(a_ctrl, + curr_lens_pos, + &ringing_params_kernel + [a_ctrl->curr_region_index], + sign_dir, + target_lens_pos); + if (rc < 0) { + kfree(ringing_params_kernel); + return rc; + } + curr_lens_pos = target_lens_pos; + } else { + target_step_pos = step_boundary; + target_lens_pos = + a_ctrl->step_position_table[target_step_pos]; + rc = msm_actuator_bivcm_write_focus(a_ctrl, + curr_lens_pos, + &ringing_params_kernel + [a_ctrl->curr_region_index], + sign_dir, + target_lens_pos); + if (rc < 0) { + kfree(ringing_params_kernel); + return rc; + } + curr_lens_pos = target_lens_pos; + + a_ctrl->curr_region_index += sign_dir; + } + a_ctrl->curr_step_pos = target_step_pos; + } + /*Free the memory allocated for damping parameters*/ + kfree(ringing_params_kernel); + + move_params->curr_lens_pos = curr_lens_pos; + a_ctrl->i2c_tbl_index = 0; + CDBG("Exit\n"); + return rc; +} + +static int32_t msm_actuator_park_lens(struct msm_actuator_ctrl_t *a_ctrl) +{ + int32_t rc = 0; + uint16_t next_lens_pos = 0; + struct msm_camera_i2c_reg_setting reg_setting; + + a_ctrl->i2c_tbl_index = 0; + if ((a_ctrl->curr_step_pos > a_ctrl->total_steps) || + (!a_ctrl->park_lens.max_step) || + (!a_ctrl->step_position_table) || + (!a_ctrl->i2c_reg_tbl) || + (!a_ctrl->func_tbl) || + (!a_ctrl->func_tbl->actuator_parse_i2c_params)) { + pr_err("%s:%d Failed to park lens.\n", + __func__, __LINE__); + return 0; + } + + if (a_ctrl->park_lens.max_step > a_ctrl->max_code_size) + a_ctrl->park_lens.max_step = a_ctrl->max_code_size; + + next_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos]; + while (next_lens_pos) { + /* conditions which help to reduce park lens time */ + if (next_lens_pos > (a_ctrl->park_lens.max_step * + PARK_LENS_LONG_STEP)) { + next_lens_pos = next_lens_pos - + (a_ctrl->park_lens.max_step * + PARK_LENS_LONG_STEP); + } else if (next_lens_pos > (a_ctrl->park_lens.max_step * + PARK_LENS_MID_STEP)) { + next_lens_pos = next_lens_pos - + (a_ctrl->park_lens.max_step * + PARK_LENS_MID_STEP); + } else if (next_lens_pos > (a_ctrl->park_lens.max_step * + PARK_LENS_SMALL_STEP)) { + next_lens_pos = next_lens_pos - + (a_ctrl->park_lens.max_step * + PARK_LENS_SMALL_STEP); + } else { + next_lens_pos = (next_lens_pos > + a_ctrl->park_lens.max_step) ? + (next_lens_pos - a_ctrl->park_lens. + max_step) : 0; + } + a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl, + next_lens_pos, a_ctrl->park_lens.hw_params, + a_ctrl->park_lens.damping_delay); + + reg_setting.reg_setting = a_ctrl->i2c_reg_tbl; + reg_setting.size = a_ctrl->i2c_tbl_index; + reg_setting.data_type = a_ctrl->i2c_data_type; + + rc = a_ctrl->i2c_client.i2c_func_tbl-> + i2c_write_table_w_microdelay( + &a_ctrl->i2c_client, ®_setting); + if (rc < 0) { + pr_err("%s Failed I2C write Line %d\n", + __func__, __LINE__); + return rc; + } + a_ctrl->i2c_tbl_index = 0; + /* Use typical damping time delay to avoid tick sound */ + usleep_range(10000, 12000); + } + + return 0; +} + +static int32_t msm_actuator_bivcm_init_step_table( + struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_set_info_t *set_info) +{ + int16_t code_per_step = 0; + int16_t cur_code = 0; + uint16_t step_index = 0, region_index = 0; + uint16_t step_boundary = 0; + uint32_t max_code_size = 1; + uint16_t data_size = set_info->actuator_params.data_size; + uint16_t mask = 0, i = 0; + uint32_t qvalue = 0; + CDBG("Enter\n"); + + for (; data_size > 0; data_size--) { + max_code_size *= 2; + mask |= (1 << i++); + } + + a_ctrl->max_code_size = max_code_size; + kfree(a_ctrl->step_position_table); + a_ctrl->step_position_table = NULL; + + if (set_info->af_tuning_params.total_steps + > MAX_ACTUATOR_AF_TOTAL_STEPS) { + pr_err("Max actuator totalsteps exceeded = %d\n", + set_info->af_tuning_params.total_steps); + return -EFAULT; + } + /* Fill step position table */ + a_ctrl->step_position_table = + kmalloc(sizeof(uint16_t) * + (set_info->af_tuning_params.total_steps + 1), GFP_KERNEL); + + if (a_ctrl->step_position_table == NULL) + return -ENOMEM; + + cur_code = set_info->af_tuning_params.initial_code; + a_ctrl->step_position_table[step_index++] = cur_code; + for (region_index = 0; + region_index < a_ctrl->region_size; + region_index++) { + code_per_step = + a_ctrl->region_params[region_index].code_per_step; + step_boundary = + a_ctrl->region_params[region_index]. + step_bound[MOVE_NEAR]; + if (step_boundary > + set_info->af_tuning_params.total_steps) { + pr_err("invalid step_boundary = %d, max_val = %d", + step_boundary, + set_info->af_tuning_params.total_steps); + kfree(a_ctrl->step_position_table); + a_ctrl->step_position_table = NULL; + return -EINVAL; + } + qvalue = a_ctrl->region_params[region_index].qvalue; + for (; step_index <= step_boundary; + step_index++) { + if (qvalue > 1 && qvalue <= MAX_QVALUE) + cur_code = step_index * code_per_step / qvalue; + else + cur_code = step_index * code_per_step; + cur_code = (set_info->af_tuning_params.initial_code + + cur_code) & mask; + if (cur_code < max_code_size) + a_ctrl->step_position_table[step_index] = + cur_code; + else { + for (; step_index < + set_info->af_tuning_params.total_steps; + step_index++) + a_ctrl-> + step_position_table[ + step_index] = + max_code_size; + } + CDBG("step_position_table[%d] = %d\n", step_index, + a_ctrl->step_position_table[step_index]); + } + } + CDBG("Exit\n"); + return 0; +} + +static int32_t msm_actuator_init_step_table(struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_set_info_t *set_info) +{ + int16_t code_per_step = 0; + uint32_t qvalue = 0; + int16_t cur_code = 0; + uint16_t step_index = 0, region_index = 0; + uint16_t step_boundary = 0; + uint32_t max_code_size = 1; + uint16_t data_size = set_info->actuator_params.data_size; + CDBG("Enter\n"); + + /* validate the actuator state */ + if (a_ctrl->actuator_state != ACT_OPS_ACTIVE) { + pr_err("%s:%d invalid actuator_state %d\n" + , __func__, __LINE__, a_ctrl->actuator_state); + return -EINVAL; + } + for (; data_size > 0; data_size--) + max_code_size *= 2; + + a_ctrl->max_code_size = max_code_size; + + /* free the step_position_table to allocate a new one */ + kfree(a_ctrl->step_position_table); + a_ctrl->step_position_table = NULL; + + if (set_info->af_tuning_params.total_steps + > MAX_ACTUATOR_AF_TOTAL_STEPS) { + pr_err("Max actuator totalsteps exceeded = %d\n", + set_info->af_tuning_params.total_steps); + return -EFAULT; + } + /* Fill step position table */ + a_ctrl->step_position_table = + kmalloc(sizeof(uint16_t) * + (set_info->af_tuning_params.total_steps + 1), GFP_KERNEL); + + if (a_ctrl->step_position_table == NULL) + return -ENOMEM; + + cur_code = set_info->af_tuning_params.initial_code; + a_ctrl->step_position_table[step_index++] = cur_code; + for (region_index = 0; + region_index < a_ctrl->region_size; + region_index++) { + code_per_step = + a_ctrl->region_params[region_index].code_per_step; + qvalue = + a_ctrl->region_params[region_index].qvalue; + step_boundary = + a_ctrl->region_params[region_index]. + step_bound[MOVE_NEAR]; + if (step_boundary > + set_info->af_tuning_params.total_steps) { + pr_err("invalid step_boundary = %d, max_val = %d", + step_boundary, + set_info->af_tuning_params.total_steps); + kfree(a_ctrl->step_position_table); + a_ctrl->step_position_table = NULL; + return -EINVAL; + } + for (; step_index <= step_boundary; + step_index++) { + if (qvalue > 1 && qvalue <= MAX_QVALUE) + cur_code = step_index * code_per_step / qvalue; + else + cur_code = step_index * code_per_step; + cur_code += set_info->af_tuning_params.initial_code; + if (cur_code < max_code_size) + a_ctrl->step_position_table[step_index] = + cur_code; + else { + for (; step_index < + set_info->af_tuning_params.total_steps; + step_index++) + a_ctrl-> + step_position_table[ + step_index] = + max_code_size; + } + CDBG("step_position_table[%d] = %d\n", step_index, + a_ctrl->step_position_table[step_index]); + } + } + CDBG("Exit\n"); + return 0; +} + +static int32_t msm_actuator_set_default_focus( + struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_move_params_t *move_params) +{ + int32_t rc = 0; + CDBG("Enter\n"); + + if (a_ctrl->curr_step_pos != 0) + rc = a_ctrl->func_tbl->actuator_move_focus(a_ctrl, move_params); + CDBG("Exit\n"); + return rc; +} + +static int32_t msm_actuator_vreg_control(struct msm_actuator_ctrl_t *a_ctrl, + int config) +{ + int rc = 0, i, cnt; + struct msm_actuator_vreg *vreg_cfg; + + vreg_cfg = &a_ctrl->vreg_cfg; + cnt = vreg_cfg->num_vreg; + if (!cnt) + return 0; + + if (cnt >= MSM_ACTUATOR_MAX_VREGS) { + pr_err("%s failed %d cnt %d\n", __func__, __LINE__, cnt); + return -EINVAL; + } + + for (i = 0; i < cnt; i++) { + if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE) { + rc = msm_camera_config_single_vreg(&(a_ctrl->pdev->dev), + &vreg_cfg->cam_vreg[i], + (struct regulator **)&vreg_cfg->data[i], + config); + } else if (a_ctrl->act_device_type == + MSM_CAMERA_I2C_DEVICE) { + rc = msm_camera_config_single_vreg( + &(a_ctrl->i2c_client.client->dev), + &vreg_cfg->cam_vreg[i], + (struct regulator **)&vreg_cfg->data[i], + config); + } + } + return rc; +} + +static int32_t msm_actuator_power_down(struct msm_actuator_ctrl_t *a_ctrl) +{ + int32_t rc = 0; + enum msm_sensor_power_seq_gpio_t gpio; + + CDBG("Enter\n"); + if (a_ctrl->actuator_state != ACT_DISABLE_STATE) { + + if (a_ctrl->func_tbl && a_ctrl->func_tbl->actuator_park_lens) { + rc = a_ctrl->func_tbl->actuator_park_lens(a_ctrl); + if (rc < 0) + pr_err("%s:%d Lens park failed.\n", + __func__, __LINE__); + } + + rc = msm_actuator_vreg_control(a_ctrl, 0); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + return rc; + } + + for (gpio = SENSOR_GPIO_AF_PWDM; + gpio < SENSOR_GPIO_MAX; gpio++) { + if (a_ctrl->gconf && + a_ctrl->gconf->gpio_num_info && + a_ctrl->gconf->gpio_num_info-> + valid[gpio] == 1) { + + gpio_set_value_cansleep( + a_ctrl->gconf->gpio_num_info-> + gpio_num[gpio], + GPIOF_OUT_INIT_LOW); + + if (a_ctrl->cam_pinctrl_status) { + rc = pinctrl_select_state( + a_ctrl->pinctrl_info.pinctrl, + a_ctrl->pinctrl_info. + gpio_state_suspend); + if (rc < 0) + pr_err("ERR:%s:%d cannot set pin to suspend state: %d", + __func__, __LINE__, rc); + + devm_pinctrl_put( + a_ctrl->pinctrl_info.pinctrl); + } + a_ctrl->cam_pinctrl_status = 0; + rc = msm_camera_request_gpio_table( + a_ctrl->gconf->cam_gpio_req_tbl, + a_ctrl->gconf->cam_gpio_req_tbl_size, + 0); + if (rc < 0) + pr_err("ERR:%s:Failed in selecting state in actuator power down: %d\n", + __func__, rc); + } + } + + kfree(a_ctrl->step_position_table); + a_ctrl->step_position_table = NULL; + kfree(a_ctrl->i2c_reg_tbl); + a_ctrl->i2c_reg_tbl = NULL; + a_ctrl->i2c_tbl_index = 0; + a_ctrl->actuator_state = ACT_OPS_INACTIVE; + } + CDBG("Exit\n"); + return rc; +} + +static int32_t msm_actuator_set_position( + struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_set_position_t *set_pos) +{ + int32_t rc = 0; + int32_t index; + uint16_t next_lens_position; + uint16_t delay; + uint32_t hw_params = 0; + struct msm_camera_i2c_reg_setting reg_setting; + CDBG("%s Enter %d\n", __func__, __LINE__); + if (set_pos->number_of_steps <= 0 || + set_pos->number_of_steps > MAX_NUMBER_OF_STEPS) { + pr_err("num_steps out of range = %d\n", + set_pos->number_of_steps); + return -EFAULT; + } + + if (!a_ctrl || !a_ctrl->func_tbl || + !a_ctrl->func_tbl->actuator_parse_i2c_params) { + pr_err("failed. NULL actuator pointers."); + return -EFAULT; + } + + if (a_ctrl->actuator_state != ACT_OPS_ACTIVE) { + pr_err("failed. Invalid actuator state."); + return -EFAULT; + } + + a_ctrl->i2c_tbl_index = 0; + for (index = 0; index < set_pos->number_of_steps; index++) { + next_lens_position = set_pos->pos[index]; + delay = set_pos->delay[index]; + a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl, + next_lens_position, hw_params, delay); + + reg_setting.reg_setting = a_ctrl->i2c_reg_tbl; + reg_setting.size = a_ctrl->i2c_tbl_index; + reg_setting.data_type = a_ctrl->i2c_data_type; + + rc = a_ctrl->i2c_client.i2c_func_tbl-> + i2c_write_table_w_microdelay( + &a_ctrl->i2c_client, ®_setting); + if (rc < 0) { + pr_err("%s Failed I2C write Line %d\n", + __func__, __LINE__); + return rc; + } + a_ctrl->i2c_tbl_index = 0; + } + CDBG("%s exit %d\n", __func__, __LINE__); + return rc; +} + +static int32_t msm_actuator_bivcm_set_position( + struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_set_position_t *set_pos) +{ + int32_t rc = 0; + int32_t index; + uint16_t next_lens_position; + uint16_t delay; + uint32_t hw_params = 0; + CDBG("%s Enter %d\n", __func__, __LINE__); + if (set_pos->number_of_steps <= 0 || + set_pos->number_of_steps > MAX_NUMBER_OF_STEPS) { + pr_err("num_steps out of range = %d\n", + set_pos->number_of_steps); + return -EFAULT; + } + + if (!a_ctrl) { + pr_err("failed. NULL actuator pointers."); + return -EFAULT; + } + + if (a_ctrl->actuator_state != ACT_OPS_ACTIVE) { + pr_err("failed. Invalid actuator state."); + return -EFAULT; + } + + a_ctrl->i2c_tbl_index = 0; + hw_params = set_pos->hw_params; + for (index = 0; index < set_pos->number_of_steps; index++) { + next_lens_position = set_pos->pos[index]; + delay = set_pos->delay[index]; + rc = msm_actuator_bivcm_handle_i2c_ops(a_ctrl, + next_lens_position, hw_params, delay); + a_ctrl->i2c_tbl_index = 0; + } + CDBG("%s exit %d\n", __func__, __LINE__); + return rc; +} + +static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_set_info_t *set_info) { + struct reg_settings_t *init_settings = NULL; + int32_t rc = -EFAULT; + uint16_t i = 0; + struct msm_camera_cci_client *cci_client = NULL; + CDBG("Enter\n"); + + for (i = 0; i < ARRAY_SIZE(actuators); i++) { + if (set_info->actuator_params.act_type == + actuators[i]->act_type) { + a_ctrl->func_tbl = &actuators[i]->func_tbl; + rc = 0; + } + } + + if (rc < 0) { + pr_err("Actuator function table not found\n"); + return rc; + } + if (set_info->af_tuning_params.total_steps + > MAX_ACTUATOR_AF_TOTAL_STEPS) { + pr_err("Max actuator totalsteps exceeded = %d\n", + set_info->af_tuning_params.total_steps); + return -EFAULT; + } + if (set_info->af_tuning_params.region_size + > MAX_ACTUATOR_REGION) { + pr_err("MAX_ACTUATOR_REGION is exceeded.\n"); + return -EFAULT; + } + + a_ctrl->region_size = set_info->af_tuning_params.region_size; + a_ctrl->pwd_step = set_info->af_tuning_params.pwd_step; + a_ctrl->total_steps = set_info->af_tuning_params.total_steps; + + if (copy_from_user(&a_ctrl->region_params, + (void *)set_info->af_tuning_params.region_params, + a_ctrl->region_size * sizeof(struct region_params_t))) + return -EFAULT; + + if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE) { + cci_client = a_ctrl->i2c_client.cci_client; + cci_client->sid = + set_info->actuator_params.i2c_addr >> 1; + cci_client->retries = 3; + cci_client->id_map = 0; + cci_client->cci_i2c_master = a_ctrl->cci_master; + cci_client->i2c_freq_mode = + set_info->actuator_params.i2c_freq_mode; + } else { + a_ctrl->i2c_client.client->addr = + set_info->actuator_params.i2c_addr; + } + + a_ctrl->i2c_data_type = set_info->actuator_params.i2c_data_type; + a_ctrl->i2c_client.addr_type = set_info->actuator_params.i2c_addr_type; + if (set_info->actuator_params.reg_tbl_size <= + MAX_ACTUATOR_REG_TBL_SIZE) { + a_ctrl->reg_tbl_size = set_info->actuator_params.reg_tbl_size; + } else { + a_ctrl->reg_tbl_size = 0; + pr_err("MAX_ACTUATOR_REG_TBL_SIZE is exceeded.\n"); + return -EFAULT; + } + + if ((a_ctrl->actuator_state == ACT_OPS_ACTIVE) && + (a_ctrl->i2c_reg_tbl != NULL)) { + kfree(a_ctrl->i2c_reg_tbl); + } + a_ctrl->i2c_reg_tbl = NULL; + a_ctrl->i2c_reg_tbl = + kmalloc(sizeof(struct msm_camera_i2c_reg_array) * + (set_info->af_tuning_params.total_steps + 1), GFP_KERNEL); + if (!a_ctrl->i2c_reg_tbl) { + pr_err("kmalloc fail\n"); + return -ENOMEM; + } + + if (copy_from_user(&a_ctrl->reg_tbl, + (void *)set_info->actuator_params.reg_tbl_params, + a_ctrl->reg_tbl_size * + sizeof(struct msm_actuator_reg_params_t))) { + kfree(a_ctrl->i2c_reg_tbl); + a_ctrl->i2c_reg_tbl = NULL; + return -EFAULT; + } + + if (set_info->actuator_params.init_setting_size && + set_info->actuator_params.init_setting_size + <= MAX_ACTUATOR_INIT_SET) { + if (a_ctrl->func_tbl->actuator_init_focus) { + init_settings = kmalloc(sizeof(struct reg_settings_t) * + (set_info->actuator_params.init_setting_size), + GFP_KERNEL); + if (init_settings == NULL) { + kfree(a_ctrl->i2c_reg_tbl); + a_ctrl->i2c_reg_tbl = NULL; + pr_err("Error allocating memory for init_settings\n"); + return -EFAULT; + } + if (copy_from_user(init_settings, + (void *)set_info->actuator_params.init_settings, + set_info->actuator_params.init_setting_size * + sizeof(struct reg_settings_t))) { + kfree(init_settings); + kfree(a_ctrl->i2c_reg_tbl); + a_ctrl->i2c_reg_tbl = NULL; + pr_err("Error copying init_settings\n"); + return -EFAULT; + } + rc = a_ctrl->func_tbl->actuator_init_focus(a_ctrl, + set_info->actuator_params.init_setting_size, + init_settings); + kfree(init_settings); + if (rc < 0) { + kfree(a_ctrl->i2c_reg_tbl); + a_ctrl->i2c_reg_tbl = NULL; + pr_err("Error actuator_init_focus\n"); + return -EFAULT; + } + } + } + + /* Park lens data */ + a_ctrl->park_lens = set_info->actuator_params.park_lens; + a_ctrl->initial_code = set_info->af_tuning_params.initial_code; + if (a_ctrl->func_tbl->actuator_init_step_table) + rc = a_ctrl->func_tbl-> + actuator_init_step_table(a_ctrl, set_info); + + a_ctrl->curr_step_pos = 0; + a_ctrl->curr_region_index = 0; + CDBG("Exit\n"); + + return rc; +} + +static int msm_actuator_init(struct msm_actuator_ctrl_t *a_ctrl) +{ + int rc = 0; + CDBG("Enter\n"); + if (!a_ctrl) { + pr_err("failed\n"); + return -EINVAL; + } + if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE) { + rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_util( + &a_ctrl->i2c_client, MSM_CCI_INIT); + if (rc < 0) + pr_err("cci_init failed\n"); + } + a_ctrl->actuator_state = ACT_OPS_ACTIVE; + CDBG("Exit\n"); + return rc; +} + +static int32_t msm_actuator_config(struct msm_actuator_ctrl_t *a_ctrl, + void __user *argp) +{ + struct msm_actuator_cfg_data *cdata = + (struct msm_actuator_cfg_data *)argp; + int32_t rc = 0; + mutex_lock(a_ctrl->actuator_mutex); + CDBG("Enter\n"); + CDBG("%s type %d\n", __func__, cdata->cfgtype); + + if (cdata->cfgtype != CFG_ACTUATOR_INIT && + cdata->cfgtype != CFG_ACTUATOR_POWERUP && + a_ctrl->actuator_state == ACT_DISABLE_STATE) { + pr_err("actuator disabled %d\n", rc); + mutex_unlock(a_ctrl->actuator_mutex); + return -EINVAL; + } + + switch (cdata->cfgtype) { + case CFG_ACTUATOR_INIT: + rc = msm_actuator_init(a_ctrl); + if (rc < 0) + pr_err("msm_actuator_init failed %d\n", rc); + break; + case CFG_GET_ACTUATOR_INFO: + cdata->is_af_supported = 1; + cdata->cfg.cam_name = a_ctrl->cam_name; + break; + + case CFG_SET_ACTUATOR_INFO: + rc = msm_actuator_set_param(a_ctrl, &cdata->cfg.set_info); + if (rc < 0) + pr_err("init table failed %d\n", rc); + break; + + case CFG_SET_DEFAULT_FOCUS: + rc = a_ctrl->func_tbl->actuator_set_default_focus(a_ctrl, + &cdata->cfg.move); + if (rc < 0) + pr_err("move focus failed %d\n", rc); + break; + + case CFG_MOVE_FOCUS: + rc = a_ctrl->func_tbl->actuator_move_focus(a_ctrl, + &cdata->cfg.move); + if (rc < 0) + pr_err("move focus failed %d\n", rc); + break; + case CFG_ACTUATOR_POWERDOWN: + rc = msm_actuator_power_down(a_ctrl); + if (rc < 0) + pr_err("msm_actuator_power_down failed %d\n", rc); + break; + + case CFG_SET_POSITION: + rc = a_ctrl->func_tbl->actuator_set_position(a_ctrl, + &cdata->cfg.setpos); + if (rc < 0) + pr_err("actuator_set_position failed %d\n", rc); + break; + + case CFG_ACTUATOR_POWERUP: + rc = msm_actuator_power_up(a_ctrl); + if (rc < 0) + pr_err("Failed actuator power up%d\n", rc); + break; + + default: + break; + } + mutex_unlock(a_ctrl->actuator_mutex); + CDBG("Exit\n"); + return rc; +} + +static int32_t msm_actuator_get_subdev_id(struct msm_actuator_ctrl_t *a_ctrl, + void *arg) +{ + uint32_t *subdev_id = (uint32_t *)arg; + CDBG("Enter\n"); + if (!subdev_id) { + pr_err("failed\n"); + return -EINVAL; + } + if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE) + *subdev_id = a_ctrl->pdev->id; + else + *subdev_id = a_ctrl->subdev_id; + + CDBG("subdev_id %d\n", *subdev_id); + CDBG("Exit\n"); + return 0; +} + +static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = { + .i2c_read = msm_camera_cci_i2c_read, + .i2c_read_seq = msm_camera_cci_i2c_read_seq, + .i2c_write = msm_camera_cci_i2c_write, + .i2c_write_table = msm_camera_cci_i2c_write_table, + .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_cci_i2c_write_table_w_microdelay, + .i2c_util = msm_sensor_cci_i2c_util, + .i2c_poll = msm_camera_cci_i2c_poll, +}; + +static struct msm_camera_i2c_fn_t msm_sensor_qup_func_tbl = { + .i2c_read = msm_camera_qup_i2c_read, + .i2c_read_seq = msm_camera_qup_i2c_read_seq, + .i2c_write = msm_camera_qup_i2c_write, + .i2c_write_table = msm_camera_qup_i2c_write_table, + .i2c_write_seq_table = msm_camera_qup_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_qup_i2c_write_table_w_microdelay, + .i2c_poll = msm_camera_qup_i2c_poll, +}; + +static int msm_actuator_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) { + int rc = 0; + struct msm_actuator_ctrl_t *a_ctrl = v4l2_get_subdevdata(sd); + CDBG("Enter\n"); + if (!a_ctrl) { + pr_err("failed\n"); + return -EINVAL; + } + mutex_lock(a_ctrl->actuator_mutex); + if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE && + a_ctrl->actuator_state != ACT_DISABLE_STATE) { + rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_util( + &a_ctrl->i2c_client, MSM_CCI_RELEASE); + if (rc < 0) + pr_err("cci_init failed\n"); + } + kfree(a_ctrl->i2c_reg_tbl); + a_ctrl->i2c_reg_tbl = NULL; + a_ctrl->actuator_state = ACT_DISABLE_STATE; + mutex_unlock(a_ctrl->actuator_mutex); + CDBG("Exit\n"); + return rc; +} + +static const struct v4l2_subdev_internal_ops msm_actuator_internal_ops = { + .close = msm_actuator_close, +}; + +static long msm_actuator_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int rc; + struct msm_actuator_ctrl_t *a_ctrl = v4l2_get_subdevdata(sd); + void __user *argp = (void __user *)arg; + CDBG("Enter\n"); + CDBG("%s:%d a_ctrl %p argp %p\n", __func__, __LINE__, a_ctrl, argp); + switch (cmd) { + case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID: + return msm_actuator_get_subdev_id(a_ctrl, argp); + case VIDIOC_MSM_ACTUATOR_CFG: + return msm_actuator_config(a_ctrl, argp); + case MSM_SD_NOTIFY_FREEZE: + return 0; + case MSM_SD_UNNOTIFY_FREEZE: + return 0; + case MSM_SD_SHUTDOWN: + if (!a_ctrl->i2c_client.i2c_func_tbl) { + pr_err("a_ctrl->i2c_client.i2c_func_tbl NULL\n"); + return -EINVAL; + } + rc = msm_actuator_power_down(a_ctrl); + if (rc < 0) { + pr_err("%s:%d Actuator Power down failed\n", + __func__, __LINE__); + } + return msm_actuator_close(sd, NULL); + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static long msm_actuator_subdev_do_ioctl( + struct file *file, unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + struct msm_actuator_cfg_data32 *u32 = + (struct msm_actuator_cfg_data32 *)arg; + struct msm_actuator_cfg_data actuator_data; + void *parg = arg; + long rc; + + switch (cmd) { + case VIDIOC_MSM_ACTUATOR_CFG32: + cmd = VIDIOC_MSM_ACTUATOR_CFG; + switch (u32->cfgtype) { + case CFG_SET_ACTUATOR_INFO: + actuator_data.cfgtype = u32->cfgtype; + actuator_data.is_af_supported = u32->is_af_supported; + actuator_data.cfg.set_info.actuator_params.act_type = + u32->cfg.set_info.actuator_params.act_type; + + actuator_data.cfg.set_info.actuator_params + .reg_tbl_size = + u32->cfg.set_info.actuator_params.reg_tbl_size; + + actuator_data.cfg.set_info.actuator_params.data_size = + u32->cfg.set_info.actuator_params.data_size; + + actuator_data.cfg.set_info.actuator_params + .init_setting_size = + u32->cfg.set_info.actuator_params + .init_setting_size; + + actuator_data.cfg.set_info.actuator_params.i2c_addr = + u32->cfg.set_info.actuator_params.i2c_addr; + + actuator_data.cfg.set_info.actuator_params. + i2c_freq_mode = + u32->cfg.set_info.actuator_params.i2c_freq_mode; + + actuator_data.cfg.set_info.actuator_params + .i2c_addr_type = + u32->cfg.set_info.actuator_params.i2c_addr_type; + + actuator_data.cfg.set_info.actuator_params + .i2c_data_type = + u32->cfg.set_info.actuator_params.i2c_data_type; + + actuator_data.cfg.set_info.actuator_params + .reg_tbl_params = + compat_ptr( + u32->cfg.set_info.actuator_params + .reg_tbl_params); + + actuator_data.cfg.set_info.actuator_params + .init_settings = + compat_ptr( + u32->cfg.set_info.actuator_params + .init_settings); + + actuator_data.cfg.set_info.af_tuning_params + .initial_code = + u32->cfg.set_info.af_tuning_params.initial_code; + + actuator_data.cfg.set_info.af_tuning_params.pwd_step = + u32->cfg.set_info.af_tuning_params.pwd_step; + + actuator_data.cfg.set_info.af_tuning_params + .region_size = + u32->cfg.set_info.af_tuning_params.region_size; + + actuator_data.cfg.set_info.af_tuning_params + .total_steps = + u32->cfg.set_info.af_tuning_params.total_steps; + + actuator_data.cfg.set_info.af_tuning_params + .region_params = compat_ptr( + u32->cfg.set_info.af_tuning_params + .region_params); + + actuator_data.cfg.set_info.actuator_params.park_lens = + u32->cfg.set_info.actuator_params.park_lens; + + actuator_data.cfg.set_info.mot_af_tuning_params. + macro_dac = + u32->cfg.set_info.mot_af_tuning_params. + macro_dac; + + actuator_data.cfg.set_info.mot_af_tuning_params. + infinity_dac = + u32->cfg.set_info.mot_af_tuning_params. + infinity_dac; + + parg = &actuator_data; + break; + case CFG_SET_DEFAULT_FOCUS: + case CFG_MOVE_FOCUS: + actuator_data.cfgtype = u32->cfgtype; + actuator_data.is_af_supported = u32->is_af_supported; + actuator_data.cfg.move.dir = u32->cfg.move.dir; + + actuator_data.cfg.move.sign_dir = + u32->cfg.move.sign_dir; + + actuator_data.cfg.move.dest_step_pos = + u32->cfg.move.dest_step_pos; + + actuator_data.cfg.move.num_steps = + u32->cfg.move.num_steps; + + actuator_data.cfg.move.curr_lens_pos = + u32->cfg.move.curr_lens_pos; + + actuator_data.cfg.move.ringing_params = + compat_ptr(u32->cfg.move.ringing_params); + parg = &actuator_data; + break; + case CFG_SET_POSITION: + actuator_data.cfgtype = u32->cfgtype; + actuator_data.is_af_supported = u32->is_af_supported; + memcpy(&actuator_data.cfg.setpos, &(u32->cfg.setpos), + sizeof(struct msm_actuator_set_position_t)); + break; + default: + actuator_data.cfgtype = u32->cfgtype; + parg = &actuator_data; + break; + } + } + + rc = msm_actuator_subdev_ioctl(sd, cmd, parg); + + switch (cmd) { + + case VIDIOC_MSM_ACTUATOR_CFG: + + switch (u32->cfgtype) { + + case CFG_SET_DEFAULT_FOCUS: + case CFG_MOVE_FOCUS: + u32->cfg.move.curr_lens_pos = + actuator_data.cfg.move.curr_lens_pos; + break; + default: + break; + } + } + + return rc; +} + +static long msm_actuator_subdev_fops_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return video_usercopy(file, cmd, arg, msm_actuator_subdev_do_ioctl); +} +#endif + +static int32_t msm_actuator_power_up(struct msm_actuator_ctrl_t *a_ctrl) +{ + int rc = 0; + enum msm_sensor_power_seq_gpio_t gpio; + + CDBG("%s called\n", __func__); + + rc = msm_actuator_vreg_control(a_ctrl, 1); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + return rc; + } + + for (gpio = SENSOR_GPIO_AF_PWDM; gpio < SENSOR_GPIO_MAX; gpio++) { + if (a_ctrl->gconf && + a_ctrl->gconf->gpio_num_info && + a_ctrl->gconf->gpio_num_info->valid[gpio] == 1) { + rc = msm_camera_request_gpio_table( + a_ctrl->gconf->cam_gpio_req_tbl, + a_ctrl->gconf->cam_gpio_req_tbl_size, 1); + if (rc < 0) { + pr_err("ERR:%s:Failed in selecting state for actuator: %d\n", + __func__, rc); + return rc; + } + if (a_ctrl->cam_pinctrl_status) { + rc = pinctrl_select_state( + a_ctrl->pinctrl_info.pinctrl, + a_ctrl->pinctrl_info.gpio_state_active); + if (rc < 0) + pr_err("ERR:%s:%d cannot set pin to active state: %d", + __func__, __LINE__, rc); + } + + gpio_set_value_cansleep( + a_ctrl->gconf->gpio_num_info->gpio_num[gpio], + 1); + } + } + + /* VREG needs some delay to power up */ + usleep_range(2000, 3000); + a_ctrl->actuator_state = ACT_ENABLE_STATE; + + CDBG("Exit\n"); + return rc; +} + +static struct v4l2_subdev_core_ops msm_actuator_subdev_core_ops = { + .ioctl = msm_actuator_subdev_ioctl, +}; + +static struct v4l2_subdev_ops msm_actuator_subdev_ops = { + .core = &msm_actuator_subdev_core_ops, +}; + +static const struct i2c_device_id msm_actuator_i2c_id[] = { + {"qcom,actuator", (kernel_ulong_t)NULL}, + { } +}; + +static int32_t msm_actuator_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + struct msm_actuator_ctrl_t *act_ctrl_t = NULL; + struct msm_actuator_vreg *vreg_cfg = NULL; + CDBG("Enter\n"); + + if (client == NULL) { + pr_err("msm_actuator_i2c_probe: client is null\n"); + return -EINVAL; + } + + act_ctrl_t = kzalloc(sizeof(struct msm_actuator_ctrl_t), + GFP_KERNEL); + if (!act_ctrl_t) { + pr_err("%s:%d failed no memory\n", __func__, __LINE__); + return -ENOMEM; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("i2c_check_functionality failed\n"); + goto probe_failure; + } + + CDBG("client = 0x%p\n", client); + + rc = of_property_read_u32(client->dev.of_node, "cell-index", + &act_ctrl_t->subdev_id); + CDBG("cell-index %d, rc %d\n", act_ctrl_t->subdev_id, rc); + if (rc < 0) { + pr_err("failed rc %d\n", rc); + goto probe_failure; + } + + if (of_find_property(client->dev.of_node, + "qcom,cam-vreg-name", NULL)) { + vreg_cfg = &act_ctrl_t->vreg_cfg; + rc = msm_camera_get_dt_vreg_data(client->dev.of_node, + &vreg_cfg->cam_vreg, &vreg_cfg->num_vreg); + if (rc < 0) { + pr_err("failed rc %d\n", rc); + goto probe_failure; + } + } + + act_ctrl_t->i2c_driver = &msm_actuator_i2c_driver; + act_ctrl_t->i2c_client.client = client; + act_ctrl_t->curr_step_pos = 0, + act_ctrl_t->curr_region_index = 0, + /* Set device type as I2C */ + act_ctrl_t->act_device_type = MSM_CAMERA_I2C_DEVICE; + act_ctrl_t->i2c_client.i2c_func_tbl = &msm_sensor_qup_func_tbl; + act_ctrl_t->act_v4l2_subdev_ops = &msm_actuator_subdev_ops; + act_ctrl_t->actuator_mutex = &msm_actuator_mutex; + act_ctrl_t->cam_name = act_ctrl_t->subdev_id; + CDBG("act_ctrl_t->cam_name: %d", act_ctrl_t->cam_name); + /* Assign name for sub device */ + snprintf(act_ctrl_t->msm_sd.sd.name, sizeof(act_ctrl_t->msm_sd.sd.name), + "%s", act_ctrl_t->i2c_driver->driver.name); + + /* Initialize sub device */ + v4l2_i2c_subdev_init(&act_ctrl_t->msm_sd.sd, + act_ctrl_t->i2c_client.client, + act_ctrl_t->act_v4l2_subdev_ops); + v4l2_set_subdevdata(&act_ctrl_t->msm_sd.sd, act_ctrl_t); + act_ctrl_t->msm_sd.sd.internal_ops = &msm_actuator_internal_ops; + act_ctrl_t->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + media_entity_init(&act_ctrl_t->msm_sd.sd.entity, 0, NULL, 0); + act_ctrl_t->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + act_ctrl_t->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_ACTUATOR; + act_ctrl_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2; + msm_sd_register(&act_ctrl_t->msm_sd); + msm_cam_copy_v4l2_subdev_fops(&msm_actuator_v4l2_subdev_fops); +#ifdef CONFIG_COMPAT + msm_actuator_v4l2_subdev_fops.compat_ioctl32 = + msm_actuator_subdev_fops_ioctl; +#endif + act_ctrl_t->msm_sd.sd.devnode->fops = + &msm_actuator_v4l2_subdev_fops; + act_ctrl_t->actuator_state = ACT_DISABLE_STATE; + pr_info("msm_actuator_i2c_probe: succeeded\n"); + CDBG("Exit\n"); + + return 0; + +probe_failure: + kfree(act_ctrl_t); + return rc; +} + +static int32_t msm_actuator_platform_probe(struct platform_device *pdev) +{ + int32_t rc = 0; + struct msm_camera_cci_client *cci_client = NULL; + struct msm_actuator_ctrl_t *msm_actuator_t = NULL; + struct msm_actuator_vreg *vreg_cfg; + CDBG("Enter\n"); + + if (!pdev->dev.of_node) { + pr_err("of_node NULL\n"); + return -EINVAL; + } + + msm_actuator_t = kzalloc(sizeof(struct msm_actuator_ctrl_t), + GFP_KERNEL); + if (!msm_actuator_t) { + pr_err("%s:%d failed no memory\n", __func__, __LINE__); + return -ENOMEM; + } + rc = of_property_read_u32((&pdev->dev)->of_node, "cell-index", + &pdev->id); + CDBG("cell-index %d, rc %d\n", pdev->id, rc); + if (rc < 0) { + kfree(msm_actuator_t); + pr_err("failed rc %d\n", rc); + return rc; + } + + rc = of_property_read_u32((&pdev->dev)->of_node, "qcom,cci-master", + &msm_actuator_t->cci_master); + CDBG("qcom,cci-master %d, rc %d\n", msm_actuator_t->cci_master, rc); + if (rc < 0 || msm_actuator_t->cci_master >= MASTER_MAX) { + kfree(msm_actuator_t); + pr_err("failed rc %d\n", rc); + return rc; + } + + if (of_find_property((&pdev->dev)->of_node, + "qcom,cam-vreg-name", NULL)) { + vreg_cfg = &msm_actuator_t->vreg_cfg; + rc = msm_camera_get_dt_vreg_data((&pdev->dev)->of_node, + &vreg_cfg->cam_vreg, &vreg_cfg->num_vreg); + if (rc < 0) { + kfree(msm_actuator_t); + pr_err("failed rc %d\n", rc); + return rc; + } + } + rc = msm_sensor_driver_get_gpio_data(&(msm_actuator_t->gconf), + (&pdev->dev)->of_node); + if (rc < 0) { + pr_err("%s: No/Error Actuator GPIOs\n", __func__); + } else { + msm_actuator_t->cam_pinctrl_status = 1; + rc = msm_camera_pinctrl_init( + &(msm_actuator_t->pinctrl_info), &(pdev->dev)); + if (rc < 0) { + pr_err("ERR:%s: Error in reading actuator pinctrl\n", + __func__); + msm_actuator_t->cam_pinctrl_status = 0; + } + } + + msm_actuator_t->act_v4l2_subdev_ops = &msm_actuator_subdev_ops; + msm_actuator_t->actuator_mutex = &msm_actuator_mutex; + msm_actuator_t->cam_name = pdev->id; + + /* Set platform device handle */ + msm_actuator_t->pdev = pdev; + /* Set device type as platform device */ + msm_actuator_t->act_device_type = MSM_CAMERA_PLATFORM_DEVICE; + msm_actuator_t->i2c_client.i2c_func_tbl = &msm_sensor_cci_func_tbl; + msm_actuator_t->i2c_client.cci_client = kzalloc(sizeof( + struct msm_camera_cci_client), GFP_KERNEL); + if (!msm_actuator_t->i2c_client.cci_client) { + kfree(msm_actuator_t->vreg_cfg.cam_vreg); + kfree(msm_actuator_t); + pr_err("failed no memory\n"); + return -ENOMEM; + } + + cci_client = msm_actuator_t->i2c_client.cci_client; + cci_client->cci_subdev = msm_cci_get_subdev(); + cci_client->cci_i2c_master = msm_actuator_t->cci_master; + v4l2_subdev_init(&msm_actuator_t->msm_sd.sd, + msm_actuator_t->act_v4l2_subdev_ops); + v4l2_set_subdevdata(&msm_actuator_t->msm_sd.sd, msm_actuator_t); + msm_actuator_t->msm_sd.sd.internal_ops = &msm_actuator_internal_ops; + msm_actuator_t->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(msm_actuator_t->msm_sd.sd.name, + ARRAY_SIZE(msm_actuator_t->msm_sd.sd.name), "msm_actuator"); + media_entity_init(&msm_actuator_t->msm_sd.sd.entity, 0, NULL, 0); + msm_actuator_t->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + msm_actuator_t->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_ACTUATOR; + msm_actuator_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2; + msm_sd_register(&msm_actuator_t->msm_sd); + msm_actuator_t->actuator_state = ACT_DISABLE_STATE; + msm_cam_copy_v4l2_subdev_fops(&msm_actuator_v4l2_subdev_fops); +#ifdef CONFIG_COMPAT + msm_actuator_v4l2_subdev_fops.compat_ioctl32 = + msm_actuator_subdev_fops_ioctl; +#endif + msm_actuator_t->msm_sd.sd.devnode->fops = + &msm_actuator_v4l2_subdev_fops; + + CDBG("Exit\n"); + return rc; +} + +static const struct of_device_id msm_actuator_i2c_dt_match[] = { + {.compatible = "qcom,actuator"}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_actuator_i2c_dt_match); + +static struct i2c_driver msm_actuator_i2c_driver = { + .id_table = msm_actuator_i2c_id, + .probe = msm_actuator_i2c_probe, + .remove = __exit_p(msm_actuator_i2c_remove), + .driver = { + .name = "qcom,actuator", + .owner = THIS_MODULE, + .of_match_table = msm_actuator_i2c_dt_match, + }, +}; + +static const struct of_device_id msm_actuator_dt_match[] = { + {.compatible = "qcom,actuator", .data = NULL}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_actuator_dt_match); + +static struct platform_driver msm_actuator_platform_driver = { + .probe = msm_actuator_platform_probe, + .driver = { + .name = "qcom,actuator", + .owner = THIS_MODULE, + .of_match_table = msm_actuator_dt_match, + }, +}; + +static int __init msm_actuator_init_module(void) +{ + int32_t rc = 0; + CDBG("Enter\n"); + rc = platform_driver_register(&msm_actuator_platform_driver); + if (!rc) + return rc; + + CDBG("%s:%d rc %d\n", __func__, __LINE__, rc); + return i2c_add_driver(&msm_actuator_i2c_driver); +} + +static struct msm_actuator msm_vcm_actuator_table = { + .act_type = ACTUATOR_VCM, + .func_tbl = { + .actuator_init_step_table = msm_actuator_init_step_table, + .actuator_move_focus = msm_actuator_move_focus, + .actuator_write_focus = msm_actuator_write_focus, + .actuator_set_default_focus = msm_actuator_set_default_focus, + .actuator_init_focus = msm_actuator_init_focus, + .actuator_parse_i2c_params = msm_actuator_parse_i2c_params, + .actuator_set_position = msm_actuator_set_position, + .actuator_park_lens = msm_actuator_park_lens, + }, +}; + +static struct msm_actuator msm_piezo_actuator_table = { + .act_type = ACTUATOR_PIEZO, + .func_tbl = { + .actuator_init_step_table = NULL, + .actuator_move_focus = msm_actuator_piezo_move_focus, + .actuator_write_focus = NULL, + .actuator_set_default_focus = + msm_actuator_piezo_set_default_focus, + .actuator_init_focus = msm_actuator_init_focus, + .actuator_parse_i2c_params = msm_actuator_parse_i2c_params, + .actuator_park_lens = NULL, + }, +}; + +static struct msm_actuator msm_hvcm_actuator_table = { + .act_type = ACTUATOR_HVCM, + .func_tbl = { + .actuator_init_step_table = msm_actuator_init_step_table, + .actuator_move_focus = msm_actuator_move_focus, + .actuator_write_focus = msm_actuator_write_focus, + .actuator_set_default_focus = msm_actuator_set_default_focus, + .actuator_init_focus = msm_actuator_init_focus, + .actuator_parse_i2c_params = msm_actuator_parse_i2c_params, + .actuator_set_position = msm_actuator_set_position, + .actuator_park_lens = msm_actuator_park_lens, + }, +}; + +static struct msm_actuator msm_bivcm_actuator_table = { + .act_type = ACTUATOR_BIVCM, + .func_tbl = { + .actuator_init_step_table = msm_actuator_bivcm_init_step_table, + .actuator_move_focus = msm_actuator_bivcm_move_focus, + .actuator_write_focus = NULL, + .actuator_set_default_focus = msm_actuator_set_default_focus, + .actuator_init_focus = msm_actuator_init_focus, + .actuator_parse_i2c_params = NULL, + .actuator_set_position = msm_actuator_bivcm_set_position, + .actuator_park_lens = NULL, + }, +}; + +module_init(msm_actuator_init_module); +MODULE_DESCRIPTION("MSM ACTUATOR"); +MODULE_LICENSE("GPL v2"); diff --git a/camera/sensor/actuator/msm_actuator.h b/camera/sensor/actuator/msm_actuator.h new file mode 100644 index 00000000..4fb5a3ba --- /dev/null +++ b/camera/sensor/actuator/msm_actuator.h @@ -0,0 +1,113 @@ +/* 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_ACTUATOR_H +#define MSM_ACTUATOR_H + +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <lenovo_soc/qcom/camera2.h> +#include <media/v4l2-subdev.h> +#include <lenovo_media/msmb_camera.h> +#include "msm_camera_i2c.h" +#include "msm_camera_dt_util.h" +#include "msm_camera_io_util.h" + + +#define DEFINE_MSM_MUTEX(mutexname) \ + static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) + +#define MSM_ACTUATOR_MAX_VREGS (10) +#define ACTUATOR_MAX_POLL_COUNT 10 + +struct msm_actuator_ctrl_t; + +enum msm_actuator_state_t { + ACT_ENABLE_STATE, + ACT_OPS_ACTIVE, + ACT_OPS_INACTIVE, + ACT_DISABLE_STATE, +}; + +struct msm_actuator_func_tbl { + int32_t (*actuator_i2c_write_b_af)(struct msm_actuator_ctrl_t *, + uint8_t, + uint8_t); + int32_t (*actuator_init_step_table)(struct msm_actuator_ctrl_t *, + struct msm_actuator_set_info_t *); + int32_t (*actuator_init_focus)(struct msm_actuator_ctrl_t *, + uint16_t, struct reg_settings_t *); + int32_t (*actuator_set_default_focus)(struct msm_actuator_ctrl_t *, + struct msm_actuator_move_params_t *); + int32_t (*actuator_move_focus)(struct msm_actuator_ctrl_t *, + struct msm_actuator_move_params_t *); + void (*actuator_parse_i2c_params)(struct msm_actuator_ctrl_t *, + int16_t, uint32_t, uint16_t); + void (*actuator_write_focus)(struct msm_actuator_ctrl_t *, + uint16_t, + struct damping_params_t *, + int8_t, + int16_t); + int32_t (*actuator_set_position)(struct msm_actuator_ctrl_t *, + struct msm_actuator_set_position_t *); + int32_t (*actuator_park_lens)(struct msm_actuator_ctrl_t *); +}; + +struct msm_actuator { + enum actuator_type act_type; + struct msm_actuator_func_tbl func_tbl; +}; + +struct msm_actuator_vreg { + struct camera_vreg_t *cam_vreg; + void *data[MSM_ACTUATOR_MAX_VREGS]; + int num_vreg; +}; + +struct msm_actuator_ctrl_t { + struct i2c_driver *i2c_driver; + struct platform_driver *pdriver; + struct platform_device *pdev; + struct msm_camera_i2c_client i2c_client; + enum msm_camera_device_type_t act_device_type; + struct msm_sd_subdev msm_sd; + enum af_camera_name cam_name; + struct mutex *actuator_mutex; + struct msm_actuator_func_tbl *func_tbl; + enum msm_actuator_data_type i2c_data_type; + struct v4l2_subdev sdev; + struct v4l2_subdev_ops *act_v4l2_subdev_ops; + + int16_t curr_step_pos; + uint16_t curr_region_index; + uint16_t *step_position_table; + struct region_params_t region_params[MAX_ACTUATOR_REGION]; + uint16_t reg_tbl_size; + struct msm_actuator_reg_params_t reg_tbl[MAX_ACTUATOR_REG_TBL_SIZE]; + uint16_t region_size; + void *user_data; + uint32_t total_steps; + uint16_t pwd_step; + uint16_t initial_code; + struct msm_camera_i2c_reg_array *i2c_reg_tbl; + uint16_t i2c_tbl_index; + enum cci_i2c_master_t cci_master; + uint32_t subdev_id; + enum msm_actuator_state_t actuator_state; + struct msm_actuator_vreg vreg_cfg; + struct park_lens_data_t park_lens; + uint32_t max_code_size; + struct msm_camera_gpio_conf *gconf; + struct msm_pinctrl_info pinctrl_info; + uint8_t cam_pinctrl_status; +}; + +#endif diff --git a/camera/sensor/actuator/msm_mot_actuator.c b/camera/sensor/actuator/msm_mot_actuator.c new file mode 100644 index 00000000..3844cc63 --- /dev/null +++ b/camera/sensor/actuator/msm_mot_actuator.c @@ -0,0 +1,253 @@ +/* Copyright (c) 2011-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. + */ + +#include "msm_actuator.h" + +static int32_t msm_mot_actuator_init_step_table( + struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_set_info_t *set_info); + +static void msm_mot_actuator_write_focus( + struct msm_actuator_ctrl_t *a_ctrl, + uint16_t curr_lens_pos, + struct damping_params_t *damping_params, + int8_t sign_direction, + int16_t code_boundary); + +static int32_t msm_actuator_set_default_focus( + struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_move_params_t *move_params); + +static int32_t msm_actuator_init_focus(struct msm_actuator_ctrl_t *a_ctrl, + uint16_t size, struct reg_settings_t *settings); + +static int msm_actuator_bivcm_handle_i2c_ops( + struct msm_actuator_ctrl_t *a_ctrl, + int16_t next_lens_position, uint32_t hw_params, uint16_t delay); + +static void msm_mot_actuator_write_focus( + struct msm_actuator_ctrl_t *a_ctrl, + uint16_t curr_lens_pos, + struct damping_params_t *damping_params, + int8_t sign_direction, + int16_t code_boundary) +{ + uint16_t wait_time = 0; + + CDBG("Enter %s\n", __func__); + + wait_time = damping_params->damping_delay; + + if (curr_lens_pos != code_boundary) { + msm_actuator_bivcm_handle_i2c_ops(a_ctrl, + code_boundary, damping_params->hw_params, wait_time); + } + CDBG("Exit %s\n", __func__); +} + +static int32_t msm_mot_actuator_move_focus( + struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_move_params_t *move_params) +{ + int32_t rc = 0; + struct damping_params_t ringing_params_kernel; + int8_t sign_dir = move_params->sign_dir; + uint16_t step_boundary = 0; + uint16_t target_step_pos = 0; + int16_t target_lens_pos = 0; + int16_t dest_step_pos = move_params->dest_step_pos; + int16_t curr_lens_pos = 0; + int dir = move_params->dir; + int32_t num_steps = move_params->num_steps; + + CDBG("Enter %s\n", __func__); + + if (copy_from_user(&ringing_params_kernel, + &(move_params->ringing_params[a_ctrl->curr_region_index]), + sizeof(struct damping_params_t))) { + pr_err("%s: copy_from_user failed\n", __func__); + return -EFAULT; + } + + CDBG("called, dir %d, num_steps %d\n", dir, num_steps); + + if (dest_step_pos == a_ctrl->curr_step_pos) + return rc; + + if ((sign_dir > MSM_ACTUATOR_MOVE_SIGNED_NEAR) || + (sign_dir < MSM_ACTUATOR_MOVE_SIGNED_FAR)) { + pr_err("Invalid sign_dir = %d\n", sign_dir); + return -EFAULT; + } + if ((dir > MOVE_FAR) || (dir < MOVE_NEAR)) { + pr_err("Invalid direction = %d\n", dir); + return -EFAULT; + } + if (dest_step_pos >= a_ctrl->total_steps) { + pr_err("Step pos greater than total steps = %d\n", + dest_step_pos); + return -EFAULT; + } + curr_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos]; + a_ctrl->i2c_tbl_index = 0; + CDBG("curr_step_pos =%d dest_step_pos =%d curr_lens_pos=%d\n", + a_ctrl->curr_step_pos, dest_step_pos, curr_lens_pos); + + while (a_ctrl->curr_step_pos != dest_step_pos) { + step_boundary = + a_ctrl->region_params[a_ctrl->curr_region_index]. + step_bound[dir]; + if ((dest_step_pos * sign_dir) <= + (step_boundary * sign_dir)) { + + target_step_pos = dest_step_pos; + target_lens_pos = + a_ctrl->step_position_table[target_step_pos]; + a_ctrl->func_tbl->actuator_write_focus(a_ctrl, + curr_lens_pos, + &ringing_params_kernel, + sign_dir, + target_lens_pos); + curr_lens_pos = target_lens_pos; + + } else { + target_step_pos = step_boundary; + target_lens_pos = + a_ctrl->step_position_table[target_step_pos]; + a_ctrl->func_tbl->actuator_write_focus(a_ctrl, + curr_lens_pos, + &ringing_params_kernel, + sign_dir, + target_lens_pos); + curr_lens_pos = target_lens_pos; + + a_ctrl->curr_region_index += sign_dir; + } + a_ctrl->curr_step_pos = target_step_pos; + a_ctrl->i2c_tbl_index = 0; + } + + move_params->curr_lens_pos = curr_lens_pos; + + CDBG("Exit\n"); + return rc; +} + +static int32_t msm_mot_actuator_set_position( + struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_set_position_t *set_pos) +{ + int32_t rc = 0; + int32_t index; + uint16_t next_lens_position; + uint16_t delay; + uint32_t hw_params = 0; + + CDBG("%s Enter %d\n", __func__, __LINE__); + if (set_pos->number_of_steps == 0) + return rc; + + a_ctrl->i2c_tbl_index = 0; + for (index = 0; index < set_pos->number_of_steps; index++) { + next_lens_position = set_pos->pos[index]; + delay = set_pos->delay[index]; + msm_actuator_bivcm_handle_i2c_ops(a_ctrl, + next_lens_position, hw_params, delay); + + a_ctrl->i2c_tbl_index = 0; + } + CDBG("%s exit %d\n", __func__, __LINE__); + return rc; +} + +static int32_t msm_mot_actuator_init_step_table( + struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_set_info_t *set_info) +{ + int macro_dac = 0; + int inf_dac = 0; + int cur_code = 0; + int16_t step_index = 0, region_index = 0; + uint16_t step_boundary = 0; + int32_t max_code_size = 1; + uint16_t data_size = set_info->actuator_params.data_size; + + CDBG("Enter %s\n", __func__); + + for (; data_size > 0; data_size--) + max_code_size *= 2; + + a_ctrl->max_code_size = max_code_size; + kfree(a_ctrl->step_position_table); + a_ctrl->step_position_table = NULL; + + if (set_info->af_tuning_params.total_steps + > MAX_ACTUATOR_AF_TOTAL_STEPS) { + pr_err("Max actuator totalsteps exceeded = %d\n", + set_info->af_tuning_params.total_steps); + return -EFAULT; + } + + /* Fill step position table */ + a_ctrl->step_position_table = + kmalloc(sizeof(uint16_t) * + (set_info->af_tuning_params.total_steps), GFP_KERNEL); + + if (a_ctrl->step_position_table == NULL) + return -ENOMEM; + + inf_dac = set_info->mot_af_tuning_params.infinity_dac; + macro_dac = set_info->mot_af_tuning_params.macro_dac; + + for (region_index = 0; + region_index < a_ctrl->region_size; + region_index++) { + step_boundary = + a_ctrl->region_params[region_index]. + step_bound[MOVE_NEAR]; + for (step_index = 0; step_index < step_boundary; + step_index++) { + cur_code = + ((int)step_index * (macro_dac - inf_dac)) / + (set_info->af_tuning_params.total_steps - 1) + + inf_dac; + if (step_index < + set_info->af_tuning_params.total_steps) { + if (cur_code > max_code_size) { + a_ctrl->step_position_table[step_index] + = (uint16_t)max_code_size; + } else { + a_ctrl->step_position_table[step_index] + = (uint16_t)cur_code; + } + } + } + } + + CDBG("Exit %s\n", __func__); + return 0; +} + +static struct msm_actuator msm_mot_hvcm_actuator_table = { + .act_type = ACTUATOR_MOT_HVCM, + .func_tbl = { + .actuator_init_step_table = msm_mot_actuator_init_step_table, + .actuator_move_focus = msm_mot_actuator_move_focus, + .actuator_write_focus = msm_mot_actuator_write_focus, + .actuator_set_default_focus = msm_actuator_set_default_focus, + .actuator_init_focus = msm_actuator_init_focus, + .actuator_parse_i2c_params = NULL, + .actuator_set_position = msm_mot_actuator_set_position, + .actuator_park_lens = NULL, + }, +}; + diff --git a/camera/sensor/cci/Makefile b/camera/sensor/cci/Makefile new file mode 100644 index 00000000..eee4cecf --- /dev/null +++ b/camera/sensor/cci/Makefile @@ -0,0 +1,4 @@ +ccflags-y += -Icamera +ccflags-y += -Icamera/common +ccflags-y += -Icamera/sensor/io +obj-$(CONFIG_MSM_CCI) += msm_cci.o diff --git a/camera/sensor/cci/msm_cam_cci_hwreg.h b/camera/sensor/cci/msm_cam_cci_hwreg.h new file mode 100644 index 00000000..d70b8e34 --- /dev/null +++ b/camera/sensor/cci/msm_cam_cci_hwreg.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2012-2015 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_CAM_CCI_HWREG__ +#define __MSM_CAM_CCI_HWREG__ + +#define CCI_HW_VERSION_ADDR 0x00000000 +#define CCI_RESET_CMD_ADDR 0x00000004 +#define CCI_RESET_CMD_RMSK 0x0f73f3f7 +#define CCI_M0_RESET_RMSK 0x3F1 +#define CCI_M1_RESET_RMSK 0x3F001 +#define CCI_QUEUE_START_ADDR 0x00000008 +#define CCI_SET_CID_SYNC_TIMER_ADDR 0x00000010 +#define CCI_SET_CID_SYNC_TIMER_OFFSET 0x00000004 +#define CCI_I2C_M0_SCL_CTL_ADDR 0x00000100 +#define CCI_I2C_M0_SDA_CTL_0_ADDR 0x00000104 +#define CCI_I2C_M0_SDA_CTL_1_ADDR 0x00000108 +#define CCI_I2C_M0_SDA_CTL_2_ADDR 0x0000010c +#define CCI_I2C_M0_READ_DATA_ADDR 0x00000118 +#define CCI_I2C_M0_MISC_CTL_ADDR 0x00000110 +#define CCI_I2C_M0_READ_BUF_LEVEL_ADDR 0x0000011C +#define CCI_HALT_REQ_ADDR 0x00000034 +#define CCI_M0_HALT_REQ_RMSK 0x1 +#define CCI_M1_HALT_REQ_RMSK 0x2 +#define CCI_I2C_M1_SCL_CTL_ADDR 0x00000200 +#define CCI_I2C_M1_SDA_CTL_0_ADDR 0x00000204 +#define CCI_I2C_M1_SDA_CTL_1_ADDR 0x00000208 +#define CCI_I2C_M1_SDA_CTL_2_ADDR 0x0000020c +#define CCI_I2C_M1_MISC_CTL_ADDR 0x00000210 +#define CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR 0x00000304 +#define CCI_I2C_M0_Q0_CUR_CMD_ADDR 0x00000308 +#define CCI_I2C_M0_Q0_REPORT_STATUS_ADDR 0x0000030c +#define CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR 0x00000300 +#define CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x00000310 +#define CCI_IRQ_MASK_0_ADDR 0x00000c04 +#define CCI_IRQ_MASK_0_RMSK 0x7fff7ff7 +#define CCI_IRQ_CLEAR_0_ADDR 0x00000c08 +#define CCI_IRQ_STATUS_0_ADDR 0x00000c0c +#define CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK 0x4000000 +#define CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK 0x2000000 +#define CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK 0x1000000 +#define CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK 0x100000 +#define CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK 0x10000 +#define CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK 0x1000 +#define CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK 0x100 +#define CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK 0x10 +#define CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK 0x18000EE6 +#define CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK 0x60EE6000 +#define CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK 0x1 +#define CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x00000c00 + +#define DEBUG_TOP_REG_START 0x0 +#define DEBUG_TOP_REG_COUNT 14 +#define DEBUG_MASTER_REG_START 0x100 +#define DEBUG_MASTER_REG_COUNT 8 +#define DEBUG_MASTER_QUEUE_REG_START 0x300 +#define DEBUG_MASTER_QUEUE_REG_COUNT 6 +#define DEBUG_INTR_REG_START 0xC00 +#define DEBUG_INTR_REG_COUNT 7 +#endif /* __MSM_CAM_CCI_HWREG__ */ diff --git a/camera/sensor/cci/msm_cci.c b/camera/sensor/cci/msm_cci.c new file mode 100644 index 00000000..4ba2b7ea --- /dev/null +++ b/camera/sensor/cci/msm_cci.c @@ -0,0 +1,2128 @@ +/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_platform.h> +#include "msm_sd.h" +#include "msm_cci.h" +#include "msm_cam_cci_hwreg.h" +#include "msm_camera_io_util.h" +#include "msm_camera_dt_util.h" +#include "cam_hw_ops.h" + +#define V4L2_IDENT_CCI 50005 +#define CCI_I2C_QUEUE_0_SIZE 64 +#define CCI_I2C_Q0_SIZE_128W 128 +#define CCI_I2C_QUEUE_1_SIZE 16 +#define CCI_I2C_Q1_SIZE_32W 32 +#define CYCLES_PER_MICRO_SEC_DEFAULT 4915 +#define CCI_MAX_DELAY 1000000 + +#define CCI_TIMEOUT msecs_to_jiffies(100) + +/* TODO move this somewhere else */ +#define MSM_CCI_DRV_NAME "msm_cci" + +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +#undef CCI_DBG +#ifdef MSM_CCI_DEBUG +#define CCI_DBG(fmt, args...) pr_err(fmt, ##args) +#else +#define CCI_DBG(fmt, args...) pr_debug(fmt, ##args) +#endif + +/* Max bytes that can be read per CCI read transaction */ +#define CCI_READ_MAX 12 +#define CCI_I2C_READ_MAX_RETRIES 3 +#define CCI_I2C_MAX_READ 8192 +#define CCI_I2C_MAX_WRITE 8192 + +#define PRIORITY_QUEUE (QUEUE_0) +#define SYNC_QUEUE (QUEUE_1) + +static struct v4l2_subdev *g_cci_subdev; + +static void msm_cci_dump_registers(struct cci_device *cci_dev, + enum cci_i2c_master_t master, enum cci_i2c_queue_t queue) +{ + uint32_t read_val = 0; + uint32_t i = 0; + uint32_t reg_offset = 0; + + /* CCI Top Registers */ + CCI_DBG(" **** %s : %d CCI TOP Registers ****\n", __func__, __LINE__); + for (i = 0; i < DEBUG_TOP_REG_COUNT; i++) { + reg_offset = DEBUG_TOP_REG_START + i * 4; + read_val = msm_camera_io_r_mb(cci_dev->base + reg_offset); + CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n", + __func__, __LINE__, reg_offset, read_val); + } + + /* CCI Master registers */ + CCI_DBG(" **** %s : %d CCI MASTER%d Registers ****\n", + __func__, __LINE__, master); + for (i = 0; i < DEBUG_MASTER_REG_COUNT; i++) { + if (i == 6) + continue; + reg_offset = DEBUG_MASTER_REG_START + master*0x100 + i * 4; + read_val = msm_camera_io_r_mb(cci_dev->base + reg_offset); + CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n", + __func__, __LINE__, reg_offset, read_val); + } + + /* CCI Master Queue registers */ + CCI_DBG(" **** %s : %d CCI MASTER%d QUEUE%d Registers ****\n", + __func__, __LINE__, master, queue); + for (i = 0; i < DEBUG_MASTER_QUEUE_REG_COUNT; i++) { + reg_offset = DEBUG_MASTER_QUEUE_REG_START + master*0x200 + + queue*0x100 + i * 4; + read_val = msm_camera_io_r_mb(cci_dev->base + reg_offset); + CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n", + __func__, __LINE__, reg_offset, read_val); + } + + /* CCI Interrupt registers */ + CCI_DBG(" **** %s : %d CCI Interrupt Registers ****\n", + __func__, __LINE__); + for (i = 0; i < DEBUG_INTR_REG_COUNT; i++) { + reg_offset = DEBUG_INTR_REG_START + i * 4; + read_val = msm_camera_io_r_mb(cci_dev->base + reg_offset); + CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n", + __func__, __LINE__, reg_offset, read_val); + } +} + +static int32_t msm_cci_set_clk_param(struct cci_device *cci_dev, + struct msm_camera_cci_ctrl *c_ctrl) +{ + struct msm_cci_clk_params_t *clk_params = NULL; + enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master; + enum i2c_freq_mode_t i2c_freq_mode = c_ctrl->cci_info->i2c_freq_mode; + + clk_params = &cci_dev->cci_clk_params[i2c_freq_mode]; + + if ((i2c_freq_mode >= I2C_MAX_MODES) || (i2c_freq_mode < 0)) { + pr_err("%s:%d invalid i2c_freq_mode = %d", + __func__, __LINE__, i2c_freq_mode); + return -EINVAL; + } + if (cci_dev->i2c_freq_mode[master] == i2c_freq_mode) + return 0; + if (MASTER_0 == master) { + msm_camera_io_w_mb(clk_params->hw_thigh << 16 | + clk_params->hw_tlow, + cci_dev->base + CCI_I2C_M0_SCL_CTL_ADDR); + msm_camera_io_w_mb(clk_params->hw_tsu_sto << 16 | + clk_params->hw_tsu_sta, + cci_dev->base + CCI_I2C_M0_SDA_CTL_0_ADDR); + msm_camera_io_w_mb(clk_params->hw_thd_dat << 16 | + clk_params->hw_thd_sta, + cci_dev->base + CCI_I2C_M0_SDA_CTL_1_ADDR); + msm_camera_io_w_mb(clk_params->hw_tbuf, + cci_dev->base + CCI_I2C_M0_SDA_CTL_2_ADDR); + msm_camera_io_w_mb(clk_params->hw_scl_stretch_en << 8 | + clk_params->hw_trdhld << 4 | clk_params->hw_tsp, + cci_dev->base + CCI_I2C_M0_MISC_CTL_ADDR); + } else if (MASTER_1 == master) { + msm_camera_io_w_mb(clk_params->hw_thigh << 16 | + clk_params->hw_tlow, + cci_dev->base + CCI_I2C_M1_SCL_CTL_ADDR); + msm_camera_io_w_mb(clk_params->hw_tsu_sto << 16 | + clk_params->hw_tsu_sta, + cci_dev->base + CCI_I2C_M1_SDA_CTL_0_ADDR); + msm_camera_io_w_mb(clk_params->hw_thd_dat << 16 | + clk_params->hw_thd_sta, + cci_dev->base + CCI_I2C_M1_SDA_CTL_1_ADDR); + msm_camera_io_w_mb(clk_params->hw_tbuf, + cci_dev->base + CCI_I2C_M1_SDA_CTL_2_ADDR); + msm_camera_io_w_mb(clk_params->hw_scl_stretch_en << 8 | + clk_params->hw_trdhld << 4 | clk_params->hw_tsp, + cci_dev->base + CCI_I2C_M1_MISC_CTL_ADDR); + } + cci_dev->i2c_freq_mode[master] = i2c_freq_mode; + return 0; +} + +static void msm_cci_flush_queue(struct cci_device *cci_dev, + enum cci_i2c_master_t master) +{ + int32_t rc = 0; + + msm_camera_io_w_mb(1 << master, cci_dev->base + CCI_HALT_REQ_ADDR); + rc = wait_for_completion_timeout( + &cci_dev->cci_master_info[master].reset_complete, CCI_TIMEOUT); + if (rc < 0) { + pr_err("%s:%d wait failed\n", __func__, __LINE__); + } else if (rc == 0) { + pr_err("%s:%d wait timeout\n", __func__, __LINE__); + + /* Set reset pending flag to TRUE */ + cci_dev->cci_master_info[master].reset_pending = TRUE; + + /* Set proper mask to RESET CMD address based on MASTER */ + if (master == MASTER_0) + msm_camera_io_w_mb(CCI_M0_RESET_RMSK, + cci_dev->base + CCI_RESET_CMD_ADDR); + else + msm_camera_io_w_mb(CCI_M1_RESET_RMSK, + cci_dev->base + CCI_RESET_CMD_ADDR); + + /* wait for reset done irq */ + rc = wait_for_completion_timeout( + &cci_dev->cci_master_info[master].reset_complete, + CCI_TIMEOUT); + if (rc <= 0) + pr_err("%s:%d wait failed %d\n", __func__, __LINE__, + rc); + } + return; +} + +static int32_t msm_cci_validate_queue(struct cci_device *cci_dev, + uint32_t len, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + int32_t rc = 0; + uint32_t read_val = 0; + uint32_t reg_offset = master * 0x200 + queue * 0x100; + read_val = msm_camera_io_r_mb(cci_dev->base + + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset); + CDBG("%s line %d CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR %d len %d max %d\n", + __func__, __LINE__, read_val, len, + cci_dev->cci_i2c_queue_info[master][queue].max_queue_size); + if ((read_val + len + 1) > cci_dev-> + cci_i2c_queue_info[master][queue].max_queue_size) { + uint32_t reg_val = 0; + uint32_t report_val = CCI_I2C_REPORT_CMD | (1 << 8); + CDBG("%s:%d CCI_I2C_REPORT_CMD\n", __func__, __LINE__); + msm_camera_io_w_mb(report_val, + cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + reg_offset); + read_val++; + CDBG("%s:%d CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR %d, queue: %d\n", + __func__, __LINE__, read_val, queue); + msm_camera_io_w_mb(read_val, cci_dev->base + + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset); + reg_val = 1 << ((master * 2) + queue); + CDBG("%s:%d CCI_QUEUE_START_ADDR\n", __func__, __LINE__); + atomic_set(&cci_dev->cci_master_info[master]. + done_pending[queue], 1); + msm_camera_io_w_mb(reg_val, cci_dev->base + + CCI_QUEUE_START_ADDR); + CDBG("%s line %d wait_for_completion_timeout\n", + __func__, __LINE__); + atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1); + rc = wait_for_completion_timeout(&cci_dev-> + cci_master_info[master].report_q[queue], CCI_TIMEOUT); + if (rc <= 0) { + pr_err("%s: wait_for_completion_timeout %d\n", + __func__, __LINE__); + if (rc == 0) + rc = -ETIMEDOUT; + msm_cci_flush_queue(cci_dev, master); + return rc; + } + rc = cci_dev->cci_master_info[master].status; + if (rc < 0) + pr_err("%s failed rc %d\n", __func__, rc); + } + return rc; +} + +static int32_t msm_cci_write_i2c_queue(struct cci_device *cci_dev, + uint32_t val, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + int32_t rc = 0; + uint32_t reg_offset = master * 0x200 + queue * 0x100; + + if (!cci_dev) { + pr_err("%s: failed %d", __func__, __LINE__); + return -EINVAL; + } + + CDBG("%s:%d called\n", __func__, __LINE__); + rc = msm_cci_validate_queue(cci_dev, 1, master, queue); + if (rc < 0) { + pr_err("%s: failed %d", __func__, __LINE__); + return rc; + } + CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR:val 0x%x:0x%x\n", + __func__, CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + reg_offset, val); + msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + reg_offset); + return rc; +} + +static uint32_t msm_cci_wait(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + int32_t rc = 0; + + if (!cci_dev) { + pr_err("%s: failed %d", __func__, __LINE__); + return -EINVAL; + } + + rc = wait_for_completion_timeout(&cci_dev-> + cci_master_info[master].report_q[queue], CCI_TIMEOUT); + CDBG("%s line %d wait DONE_for_completion_timeout\n", + __func__, __LINE__); + + if (rc <= 0) { + msm_cci_dump_registers(cci_dev, master, queue); + pr_err("%s: %d wait for queue: %d\n", + __func__, __LINE__, queue); + if (rc == 0) + rc = -ETIMEDOUT; + msm_cci_flush_queue(cci_dev, master); + return rc; + } + rc = cci_dev->cci_master_info[master].status; + if (rc < 0) { + pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc); + return rc; + } + return 0; +} + +static int32_t msm_cci_addr_to_num_bytes( + enum msm_camera_i2c_reg_addr_type addr_type) +{ + int32_t retVal; + + switch (addr_type) { + case MSM_CAMERA_I2C_BYTE_ADDR: + retVal = 1; + break; + case MSM_CAMERA_I2C_WORD_ADDR: + retVal = 2; + break; + case MSM_CAMERA_I2C_3B_ADDR: + retVal = 3; + break; + default: + pr_err("%s: %d failed: %d\n", __func__, __LINE__, addr_type); + retVal = 1; + break; + } + return retVal; +} + +static int32_t msm_cci_data_to_num_bytes( + enum msm_camera_i2c_data_type data_type) +{ + int32_t retVal; + + switch (data_type) { + case MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA: + case MSM_CAMERA_I2C_SET_BYTE_MASK: + case MSM_CAMERA_I2C_UNSET_BYTE_MASK: + case MSM_CAMERA_I2C_BYTE_DATA: + retVal = 1; + break; + case MSM_CAMERA_I2C_SET_WORD_MASK: + case MSM_CAMERA_I2C_UNSET_WORD_MASK: + case MSM_CAMERA_I2C_WORD_DATA: + retVal = 2; + break; + case MSM_CAMERA_I2C_DWORD_DATA: + retVal = 4; + break; + default: + pr_err("%s: %d failed: %d\n", __func__, __LINE__, data_type); + retVal = 1; + break; + } + return retVal; +} + +static int32_t msm_cci_calc_cmd_len(struct cci_device *cci_dev, + struct msm_camera_cci_ctrl *c_ctrl, uint32_t cmd_size, + struct msm_camera_i2c_reg_array *i2c_cmd, uint32_t *pack) +{ + uint8_t i; + uint32_t len = 0; + uint8_t data_len = 0, addr_len = 0; + uint8_t pack_max_len; + struct msm_camera_i2c_reg_setting *msg; + struct msm_camera_i2c_reg_array *cmd = i2c_cmd; + uint32_t size = cmd_size; + + if (!cci_dev || !c_ctrl) { + pr_err("%s: failed %d", __func__, __LINE__); + return -EINVAL; + } + + msg = &c_ctrl->cfg.cci_i2c_write_cfg; + *pack = 0; + + if (c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) { + addr_len = msm_cci_addr_to_num_bytes(msg->addr_type); + len = (size + addr_len) <= (cci_dev->payload_size) ? + (size + addr_len):cci_dev->payload_size; + } else { + addr_len = msm_cci_addr_to_num_bytes(msg->addr_type); + data_len = msm_cci_data_to_num_bytes(msg->data_type); + len = data_len + addr_len; + pack_max_len = size < (cci_dev->payload_size-len) ? + size : (cci_dev->payload_size-len); + for (i = 0; i < pack_max_len;) { + if (cmd->delay || ((cmd - i2c_cmd) >= (cmd_size - 1))) + break; + if (cmd->reg_addr + 1 == + (cmd+1)->reg_addr) { + len += data_len; + *pack += data_len; + } else + break; + i += data_len; + cmd++; + } + } + + if (len > cci_dev->payload_size) { + pr_err("Len error: %d", len); + return -EINVAL; + } + + len += 1; /*add i2c WR command*/ + len = len/4 + 1; + + return len; +} + +static void msm_cci_load_report_cmd(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + uint32_t reg_offset = master * 0x200 + queue * 0x100; + uint32_t read_val = msm_camera_io_r_mb(cci_dev->base + + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset); + uint32_t report_val = CCI_I2C_REPORT_CMD | (1 << 8); + + CDBG("%s:%d CCI_I2C_REPORT_CMD curr_w_cnt: %d\n", + __func__, __LINE__, read_val); + msm_camera_io_w_mb(report_val, + cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + reg_offset); + read_val++; + + CDBG("%s:%d CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR %d\n", + __func__, __LINE__, read_val); + msm_camera_io_w_mb(read_val, cci_dev->base + + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset); +} + +static int32_t msm_cci_wait_report_cmd(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + uint32_t reg_val = 1 << ((master * 2) + queue); + msm_cci_load_report_cmd(cci_dev, master, queue); + atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1); + atomic_set(&cci_dev->cci_master_info[master].done_pending[queue], 1); + msm_camera_io_w_mb(reg_val, cci_dev->base + + CCI_QUEUE_START_ADDR); + return msm_cci_wait(cci_dev, master, queue); +} + +static void msm_cci_process_half_q(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + uint32_t reg_val = 1 << ((master * 2) + queue); + if (0 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) { + msm_cci_load_report_cmd(cci_dev, master, queue); + atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1); + msm_camera_io_w_mb(reg_val, cci_dev->base + + CCI_QUEUE_START_ADDR); + } +} + +static int32_t msm_cci_process_full_q(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + int32_t rc = 0; + if (1 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) { + atomic_set(&cci_dev->cci_master_info[master]. + done_pending[queue], 1); + rc = msm_cci_wait(cci_dev, master, queue); + if (rc < 0) { + pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc); + return rc; + } + } else { + rc = msm_cci_wait_report_cmd(cci_dev, master, queue); + if (rc < 0) { + pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc); + return rc; + } + } + return rc; +} + +static int32_t msm_cci_lock_queue(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue, uint32_t en) +{ + uint32_t val; + + if (queue != PRIORITY_QUEUE) + return 0; + + val = en ? CCI_I2C_LOCK_CMD : CCI_I2C_UNLOCK_CMD; + return msm_cci_write_i2c_queue(cci_dev, val, master, queue); +} + +static int32_t msm_cci_transfer_end(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + int32_t rc = 0; + + if (0 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) { + rc = msm_cci_lock_queue(cci_dev, master, queue, 0); + if (rc < 0) { + pr_err("%s failed line %d\n", __func__, __LINE__); + return rc; + } + rc = msm_cci_wait_report_cmd(cci_dev, master, queue); + if (rc < 0) { + pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc); + return rc; + } + } else { + atomic_set(&cci_dev->cci_master_info[master]. + done_pending[queue], 1); + rc = msm_cci_wait(cci_dev, master, queue); + if (rc < 0) { + pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc); + return rc; + } + rc = msm_cci_lock_queue(cci_dev, master, queue, 0); + if (rc < 0) { + pr_err("%s failed line %d\n", __func__, __LINE__); + return rc; + } + rc = msm_cci_wait_report_cmd(cci_dev, master, queue); + if (rc < 0) { + pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc); + return rc; + } + } + return rc; +} + +static int32_t msm_cci_get_queue_free_size(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + uint32_t read_val = 0; + uint32_t reg_offset = master * 0x200 + queue * 0x100; + read_val = msm_camera_io_r_mb(cci_dev->base + + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset); + CDBG("%s line %d CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR %d max %d\n", + __func__, __LINE__, read_val, + cci_dev->cci_i2c_queue_info[master][queue].max_queue_size); + return (cci_dev-> + cci_i2c_queue_info[master][queue].max_queue_size) - + read_val; +} + +static int32_t msm_cci_data_queue(struct cci_device *cci_dev, + struct msm_camera_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue, + enum cci_i2c_sync sync_en) +{ + uint16_t i = 0, j = 0, k = 0, h = 0, len = 0; + int32_t rc = 0, free_size = 0, en_seq_write = 0; + uint32_t cmd = 0, delay = 0; + uint8_t data[12]; + uint16_t reg_addr = 0; + struct msm_camera_i2c_reg_setting *i2c_msg = + &c_ctrl->cfg.cci_i2c_write_cfg; + uint16_t cmd_size = i2c_msg->size; + struct msm_camera_i2c_reg_array *i2c_cmd = i2c_msg->reg_setting; + enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master; + + uint32_t read_val = 0; + uint32_t reg_offset; + uint32_t val = 0; + uint32_t max_queue_size; + + if (i2c_cmd == NULL) { + pr_err("%s:%d Failed line\n", __func__, + __LINE__); + return -EINVAL; + } + + if ((!cmd_size) || (cmd_size > CCI_I2C_MAX_WRITE)) { + pr_err("%s:%d failed: invalid cmd_size %d\n", + __func__, __LINE__, cmd_size); + return -EINVAL; + } + + CDBG("%s addr type %d data type %d cmd_size %d\n", __func__, + i2c_msg->addr_type, i2c_msg->data_type, cmd_size); + + if (i2c_msg->addr_type >= MSM_CAMERA_I2C_ADDR_TYPE_MAX) { + pr_err("%s:%d failed: invalid addr_type 0x%X\n", + __func__, __LINE__, i2c_msg->addr_type); + return -EINVAL; + } + if (i2c_msg->data_type >= MSM_CAMERA_I2C_DATA_TYPE_MAX) { + pr_err("%s:%d failed: invalid data_type 0x%X\n", + __func__, __LINE__, i2c_msg->data_type); + return -EINVAL; + } + reg_offset = master * 0x200 + queue * 0x100; + + msm_camera_io_w_mb(cci_dev->cci_wait_sync_cfg.cid, + cci_dev->base + CCI_SET_CID_SYNC_TIMER_ADDR + + cci_dev->cci_wait_sync_cfg.csid * + CCI_SET_CID_SYNC_TIMER_OFFSET); + + val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 | + c_ctrl->cci_info->retries << 16 | + c_ctrl->cci_info->id_map << 18; + + CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR:val 0x%x:0x%x\n", + __func__, CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + reg_offset, val); + msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + reg_offset); + + atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 0); + + max_queue_size = cci_dev->cci_i2c_queue_info[master][queue]. + max_queue_size; + reg_addr = i2c_cmd->reg_addr; + + if (sync_en == MSM_SYNC_ENABLE && cci_dev->valid_sync && + cmd_size < max_queue_size) { + val = CCI_I2C_WAIT_SYNC_CMD | + ((cci_dev->cci_wait_sync_cfg.line) << 4); + msm_camera_io_w_mb(val, + cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + reg_offset); + } + + rc = msm_cci_lock_queue(cci_dev, master, queue, 1); + if (rc < 0) { + pr_err("%s failed line %d\n", __func__, __LINE__); + return rc; + } + + while (cmd_size) { + uint32_t pack = 0; + len = msm_cci_calc_cmd_len(cci_dev, c_ctrl, cmd_size, + i2c_cmd, &pack); + if (len <= 0) { + pr_err("%s failed line %d\n", __func__, __LINE__); + return -EINVAL; + } + + read_val = msm_camera_io_r_mb(cci_dev->base + + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset); + CDBG("%s line %d CUR_WORD_CNT_ADDR %d len %d max %d\n", + __func__, __LINE__, read_val, len, max_queue_size); + /* + 1 - space alocation for Report CMD*/ + if ((read_val + len + 1) > max_queue_size/2) { + if ((read_val + len + 1) > max_queue_size) { + rc = msm_cci_process_full_q(cci_dev, + master, queue); + if (rc < 0) { + pr_err("%s failed line %d\n", + __func__, __LINE__); + return rc; + } + continue; + } + msm_cci_process_half_q(cci_dev, master, queue); + } + + CDBG("%s cmd_size %d addr 0x%x data 0x%x\n", __func__, + cmd_size, i2c_cmd->reg_addr, i2c_cmd->reg_data); + delay = i2c_cmd->delay; + i = 0; + data[i++] = CCI_I2C_WRITE_CMD; + + /* in case of multiple command + * MSM_CCI_I2C_WRITE : address is not continuous, so update + * address for a new packet. + * MSM_CCI_I2C_WRITE_SEQ : address is continuous, need to keep + * the incremented address for a + * new packet */ + if (c_ctrl->cmd == MSM_CCI_I2C_WRITE || + c_ctrl->cmd == MSM_CCI_I2C_WRITE_ASYNC || + c_ctrl->cmd == MSM_CCI_I2C_WRITE_SYNC || + c_ctrl->cmd == MSM_CCI_I2C_WRITE_SYNC_BLOCK) + reg_addr = i2c_cmd->reg_addr; + + if (en_seq_write == 0) { + /* either byte or word addr */ + if (i2c_msg->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) + data[i++] = reg_addr; + else { + data[i++] = (reg_addr & 0xFF00) >> 8; + data[i++] = reg_addr & 0x00FF; + } + } + /* max of 10 data bytes */ + do { + if (i2c_msg->data_type == MSM_CAMERA_I2C_BYTE_DATA) { + data[i++] = i2c_cmd->reg_data; + reg_addr++; + } else { + if ((i + 1) <= cci_dev->payload_size) { + data[i++] = (i2c_cmd->reg_data & + 0xFF00) >> 8; /* MSB */ + data[i++] = i2c_cmd->reg_data & + 0x00FF; /* LSB */ + reg_addr++; + } else + break; + } + i2c_cmd++; + --cmd_size; + } while (((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) || pack--) && + (cmd_size > 0) && (i <= cci_dev->payload_size)); + free_size = msm_cci_get_queue_free_size(cci_dev, master, + queue); + if ((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) && + ((i-1) == MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11) && + cci_dev->support_seq_write && cmd_size > 0 && + free_size > BURST_MIN_FREE_SIZE) { + data[0] |= 0xF0; + en_seq_write = 1; + } else { + data[0] |= ((i-1) << 4); + en_seq_write = 0; + } + len = ((i-1)/4) + 1; + + read_val = msm_camera_io_r_mb(cci_dev->base + + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset); + for (h = 0, k = 0; h < len; h++) { + cmd = 0; + for (j = 0; (j < 4 && k < i); j++) + cmd |= (data[k++] << (j * 8)); + CDBG("%s LOAD_DATA_ADDR 0x%x, q: %d, len:%d, cnt: %d\n", + __func__, cmd, queue, len, read_val); + msm_camera_io_w_mb(cmd, cci_dev->base + + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + master * 0x200 + queue * 0x100); + + read_val += 1; + msm_camera_io_w_mb(read_val, cci_dev->base + + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset); + } + + if ((delay > 0) && (delay < CCI_MAX_DELAY) && + en_seq_write == 0) { + cmd = (uint32_t)((delay * cci_dev->cycles_per_us) / + 0x100); + cmd <<= 4; + cmd |= CCI_I2C_WAIT_CMD; + CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x%x\n", + __func__, cmd); + msm_camera_io_w_mb(cmd, cci_dev->base + + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + master * 0x200 + queue * 0x100); + read_val += 1; + msm_camera_io_w_mb(read_val, cci_dev->base + + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset); + } + } + + rc = msm_cci_transfer_end(cci_dev, master, queue); + if (rc < 0) { + pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc); + return rc; + } + return rc; +} + +static int32_t msm_cci_i2c_read(struct v4l2_subdev *sd, + struct msm_camera_cci_ctrl *c_ctrl) +{ + int32_t rc = 0; + uint32_t val = 0; + int32_t read_words = 0, exp_words = 0; + int32_t index = 0, first_byte = 0; + uint32_t i = 0; + enum cci_i2c_master_t master; + enum cci_i2c_queue_t queue = QUEUE_1; + struct cci_device *cci_dev = NULL; + struct msm_camera_cci_i2c_read_cfg *read_cfg = NULL; + CDBG("%s line %d\n", __func__, __LINE__); + cci_dev = v4l2_get_subdevdata(sd); + master = c_ctrl->cci_info->cci_i2c_master; + read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg; + mutex_lock(&cci_dev->cci_master_info[master].mutex_q[queue]); + + /* Set the I2C Frequency */ + rc = msm_cci_set_clk_param(cci_dev, c_ctrl); + if (rc < 0) { + pr_err("%s:%d msm_cci_set_clk_param failed rc = %d\n", + __func__, __LINE__, rc); + return rc; + } + + /* + * Call validate queue to make sure queue is empty before starting. + * If this call fails, don't proceed with i2c_read call. This is to + * avoid overflow / underflow of queue + */ + rc = msm_cci_validate_queue(cci_dev, + cci_dev->cci_i2c_queue_info[master][queue].max_queue_size - 1, + master, queue); + if (rc < 0) { + pr_err("%s:%d Initial validataion failed rc %d\n", __func__, + __LINE__, rc); + goto ERROR; + } + + if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) { + pr_err("%s:%d More than max retries\n", __func__, + __LINE__); + goto ERROR; + } + + if (read_cfg->data == NULL) { + pr_err("%s:%d Data ptr is NULL\n", __func__, + __LINE__); + goto ERROR; + } + + CDBG("%s master %d, queue %d\n", __func__, master, queue); + CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__, + c_ctrl->cci_info->sid, c_ctrl->cci_info->retries, + c_ctrl->cci_info->id_map); + val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 | + c_ctrl->cci_info->retries << 16 | + c_ctrl->cci_info->id_map << 18; + rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue); + if (rc < 0) { + CDBG("%s failed line %d\n", __func__, __LINE__); + goto ERROR; + } + + val = CCI_I2C_LOCK_CMD; + rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue); + if (rc < 0) { + CDBG("%s failed line %d\n", __func__, __LINE__); + goto ERROR; + } + + if (read_cfg->addr_type >= MSM_CAMERA_I2C_ADDR_TYPE_MAX) { + CDBG("%s failed line %d\n", __func__, __LINE__); + goto ERROR; + } + + if (read_cfg->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) + val = CCI_I2C_WRITE_DISABLE_P_CMD | (read_cfg->addr_type << 4) | + ((read_cfg->addr & 0xFF) << 8); + if (read_cfg->addr_type == MSM_CAMERA_I2C_WORD_ADDR) + val = CCI_I2C_WRITE_DISABLE_P_CMD | (read_cfg->addr_type << 4) | + (((read_cfg->addr & 0xFF00) >> 8) << 8) | + ((read_cfg->addr & 0xFF) << 16); + rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue); + if (rc < 0) { + CDBG("%s failed line %d\n", __func__, __LINE__); + goto ERROR; + } + + val = CCI_I2C_READ_CMD | (read_cfg->num_byte << 4); + rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue); + if (rc < 0) { + CDBG("%s failed line %d\n", __func__, __LINE__); + goto ERROR; + } + + val = CCI_I2C_UNLOCK_CMD; + rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue); + if (rc < 0) { + CDBG("%s failed line %d\n", __func__, __LINE__); + goto ERROR; + } + + val = msm_camera_io_r_mb(cci_dev->base + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + + master * 0x200 + queue * 0x100); + CDBG("%s cur word cnt 0x%x\n", __func__, val); + msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + + master * 0x200 + queue * 0x100); + + val = 1 << ((master * 2) + queue); + msm_camera_io_w_mb(val, cci_dev->base + CCI_QUEUE_START_ADDR); + CDBG("%s:%d E wait_for_completion_timeout\n", __func__, + __LINE__); + + rc = wait_for_completion_timeout(&cci_dev-> + cci_master_info[master].reset_complete, CCI_TIMEOUT); + if (rc <= 0) { + msm_cci_dump_registers(cci_dev, master, queue); + if (rc == 0) + rc = -ETIMEDOUT; + pr_err("%s: %d wait_for_completion_timeout rc = %d\n", + __func__, __LINE__, rc); + msm_cci_flush_queue(cci_dev, master); + goto ERROR; + } else { + rc = 0; + } + + read_words = msm_camera_io_r_mb(cci_dev->base + + CCI_I2C_M0_READ_BUF_LEVEL_ADDR + master * 0x100); + exp_words = ((read_cfg->num_byte / 4) + 1); + if (read_words != exp_words) { + pr_err("%s:%d read_words = %d, exp words = %d\n", __func__, + __LINE__, read_words, exp_words); + memset(read_cfg->data, 0, read_cfg->num_byte); + rc = -EINVAL; + goto ERROR; + } + index = 0; + CDBG("%s index %d num_type %d\n", __func__, index, + read_cfg->num_byte); + first_byte = 0; + do { + val = msm_camera_io_r_mb(cci_dev->base + + CCI_I2C_M0_READ_DATA_ADDR + master * 0x100); + CDBG("%s read val 0x%x\n", __func__, val); + for (i = 0; (i < 4) && (index < read_cfg->num_byte); i++) { + CDBG("%s i %d index %d\n", __func__, i, index); + if (!first_byte) { + CDBG("%s sid 0x%x\n", __func__, val & 0xFF); + first_byte++; + } else { + read_cfg->data[index] = + (val >> (i * 8)) & 0xFF; + CDBG("%s data[%d] 0x%x\n", __func__, index, + read_cfg->data[index]); + index++; + } + } + } while (--read_words > 0); +ERROR: + mutex_unlock(&cci_dev->cci_master_info[master].mutex_q[queue]); + return rc; +} + +static int32_t msm_cci_i2c_read_bytes(struct v4l2_subdev *sd, + struct msm_camera_cci_ctrl *c_ctrl) +{ + int32_t rc = 0; + struct cci_device *cci_dev = NULL; + enum cci_i2c_master_t master; + struct msm_camera_cci_i2c_read_cfg *read_cfg = NULL; + uint16_t read_bytes = 0; + + if (!sd || !c_ctrl) { + pr_err("%s:%d sd %p c_ctrl %p\n", __func__, + __LINE__, sd, c_ctrl); + return -EINVAL; + } + if (!c_ctrl->cci_info) { + pr_err("%s:%d cci_info NULL\n", __func__, __LINE__); + return -EINVAL; + } + cci_dev = v4l2_get_subdevdata(sd); + if (!cci_dev) { + pr_err("%s:%d cci_dev NULL\n", __func__, __LINE__); + return -EINVAL; + } + if (cci_dev->cci_state != CCI_STATE_ENABLED) { + pr_err("%s invalid cci state %d\n", + __func__, cci_dev->cci_state); + return -EINVAL; + } + + if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX + || c_ctrl->cci_info->cci_i2c_master < 0) { + pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__); + return -EINVAL; + } + + master = c_ctrl->cci_info->cci_i2c_master; + read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg; + if ((!read_cfg->num_byte) || (read_cfg->num_byte > CCI_I2C_MAX_READ)) { + pr_err("%s:%d read num bytes 0\n", __func__, __LINE__); + rc = -EINVAL; + goto ERROR; + } + + read_bytes = read_cfg->num_byte; + do { + if (read_bytes > CCI_READ_MAX) + read_cfg->num_byte = CCI_READ_MAX; + else + read_cfg->num_byte = read_bytes; + rc = msm_cci_i2c_read(sd, c_ctrl); + if (rc < 0) { + pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc); + goto ERROR; + } + if (read_bytes > CCI_READ_MAX) { + read_cfg->addr += CCI_READ_MAX; + read_cfg->data += CCI_READ_MAX; + read_bytes -= CCI_READ_MAX; + } else { + read_bytes = 0; + } + } while (read_bytes); +ERROR: + return rc; +} + +static int32_t msm_cci_i2c_write(struct v4l2_subdev *sd, + struct msm_camera_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue, + enum cci_i2c_sync sync_en) +{ + int32_t rc = 0; + struct cci_device *cci_dev; + enum cci_i2c_master_t master; + + cci_dev = v4l2_get_subdevdata(sd); + if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX + || c_ctrl->cci_info->cci_i2c_master < 0) { + pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__); + return -EINVAL; + } + if (cci_dev->cci_state != CCI_STATE_ENABLED) { + pr_err("%s invalid cci state %d\n", + __func__, cci_dev->cci_state); + return -EINVAL; + } + master = c_ctrl->cci_info->cci_i2c_master; + CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__, + c_ctrl->cci_info->sid, c_ctrl->cci_info->retries, + c_ctrl->cci_info->id_map); + + /* Set the I2C Frequency */ + rc = msm_cci_set_clk_param(cci_dev, c_ctrl); + if (rc < 0) { + pr_err("%s:%d msm_cci_set_clk_param failed rc = %d\n", + __func__, __LINE__, rc); + return rc; + } + /* + * Call validate queue to make sure queue is empty before starting. + * If this call fails, don't proceed with i2c_write call. This is to + * avoid overflow / underflow of queue + */ + rc = msm_cci_validate_queue(cci_dev, + cci_dev->cci_i2c_queue_info[master][queue].max_queue_size-1, + master, queue); + if (rc < 0) { + pr_err("%s:%d Initial validataion failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } + if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) { + pr_err("%s:%d More than max retries\n", __func__, + __LINE__); + goto ERROR; + } + rc = msm_cci_data_queue(cci_dev, c_ctrl, queue, sync_en); + if (rc < 0) { + CDBG("%s failed line %d\n", __func__, __LINE__); + goto ERROR; + } + +ERROR: + return rc; +} + +static void msm_cci_write_async_helper(struct work_struct *work) +{ + int rc; + struct cci_device *cci_dev; + struct cci_write_async *write_async = + container_of(work, struct cci_write_async, work); + struct msm_camera_i2c_reg_setting *i2c_msg; + enum cci_i2c_master_t master; + struct msm_camera_cci_master_info *cci_master_info; + + cci_dev = write_async->cci_dev; + i2c_msg = &write_async->c_ctrl.cfg.cci_i2c_write_cfg; + master = write_async->c_ctrl.cci_info->cci_i2c_master; + cci_master_info = &cci_dev->cci_master_info[master]; + + mutex_lock(&cci_master_info->mutex_q[write_async->queue]); + rc = msm_cci_i2c_write(&cci_dev->msm_sd.sd, + &write_async->c_ctrl, write_async->queue, write_async->sync_en); + mutex_unlock(&cci_master_info->mutex_q[write_async->queue]); + if (rc < 0) + pr_err("%s: %d failed\n", __func__, __LINE__); + + kfree(write_async->c_ctrl.cfg.cci_i2c_write_cfg.reg_setting); + kfree(write_async); + + CDBG("%s: %d Exit\n", __func__, __LINE__); +} + +static int32_t msm_cci_i2c_write_async(struct v4l2_subdev *sd, + struct msm_camera_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue, + enum cci_i2c_sync sync_en) +{ + struct cci_write_async *write_async; + struct cci_device *cci_dev; + struct msm_camera_i2c_reg_setting *cci_i2c_write_cfg; + struct msm_camera_i2c_reg_setting *cci_i2c_write_cfg_w; + + cci_dev = v4l2_get_subdevdata(sd); + + CDBG("%s: %d Enter\n", __func__, __LINE__); + + write_async = kzalloc(sizeof(*write_async), GFP_KERNEL); + if (!write_async) { + pr_err("%s: %d Couldn't allocate memory\n", __func__, __LINE__); + return -ENOMEM; + } + + INIT_WORK(&write_async->work, msm_cci_write_async_helper); + write_async->cci_dev = cci_dev; + write_async->c_ctrl = *c_ctrl; + write_async->queue = queue; + write_async->sync_en = sync_en; + + cci_i2c_write_cfg = &c_ctrl->cfg.cci_i2c_write_cfg; + cci_i2c_write_cfg_w = &write_async->c_ctrl.cfg.cci_i2c_write_cfg; + + if (cci_i2c_write_cfg->size == 0) { + pr_err("%s: %d Size = 0\n", __func__, __LINE__); + kfree(write_async); + return -EINVAL; + } + + cci_i2c_write_cfg_w->reg_setting = + kzalloc(sizeof(struct msm_camera_i2c_reg_array)* + cci_i2c_write_cfg->size, GFP_KERNEL); + if (!cci_i2c_write_cfg_w->reg_setting) { + pr_err("%s: %d Couldn't allocate memory\n", __func__, __LINE__); + kfree(write_async); + return -ENOMEM; + } + memcpy(cci_i2c_write_cfg_w->reg_setting, + cci_i2c_write_cfg->reg_setting, + (sizeof(struct msm_camera_i2c_reg_array)* + cci_i2c_write_cfg->size)); + + cci_i2c_write_cfg_w->addr_type = cci_i2c_write_cfg->addr_type; + cci_i2c_write_cfg_w->data_type = cci_i2c_write_cfg->data_type; + cci_i2c_write_cfg_w->size = cci_i2c_write_cfg->size; + cci_i2c_write_cfg_w->delay = cci_i2c_write_cfg->delay; + + queue_work(cci_dev->write_wq[write_async->queue], &write_async->work); + + CDBG("%s: %d Exit\n", __func__, __LINE__); + + return 0; +} + +static int32_t msm_cci_pinctrl_init(struct cci_device *cci_dev) +{ + struct msm_pinctrl_info *cci_pctrl = NULL; + + cci_pctrl = &cci_dev->cci_pinctrl; + cci_pctrl->pinctrl = devm_pinctrl_get(&cci_dev->pdev->dev); + if (IS_ERR_OR_NULL(cci_pctrl->pinctrl)) { + pr_err("%s:%d devm_pinctrl_get cci_pinctrl failed\n", + __func__, __LINE__); + return -EINVAL; + } + cci_pctrl->gpio_state_active = pinctrl_lookup_state( + cci_pctrl->pinctrl, + CCI_PINCTRL_STATE_DEFAULT); + if (IS_ERR_OR_NULL(cci_pctrl->gpio_state_active)) { + pr_err("%s:%d look up state for active state failed\n", + __func__, __LINE__); + return -EINVAL; + } + cci_pctrl->gpio_state_suspend = pinctrl_lookup_state( + cci_pctrl->pinctrl, + CCI_PINCTRL_STATE_SLEEP); + if (IS_ERR_OR_NULL(cci_pctrl->gpio_state_suspend)) { + pr_err("%s:%d look up state for suspend state failed\n", + __func__, __LINE__); + return -EINVAL; + } + return 0; +} + +static uint32_t msm_cci_cycles_per_ms(unsigned long clk) +{ + uint32_t cycles_per_us; + + if (clk) + cycles_per_us = ((clk/1000)*256)/1000; + else { + pr_err("%s:%d, failed: Can use default: %d", + __func__, __LINE__, CYCLES_PER_MICRO_SEC_DEFAULT); + cycles_per_us = CYCLES_PER_MICRO_SEC_DEFAULT; + } + return cycles_per_us; +} + +static uint32_t *msm_cci_get_clk_rates(struct cci_device *cci_dev, + struct msm_camera_cci_ctrl *c_ctrl) +{ + uint32_t j; + int32_t idx; + uint32_t cci_clk_src; + unsigned long clk; + + struct msm_cci_clk_params_t *clk_params = NULL; + enum i2c_freq_mode_t i2c_freq_mode = c_ctrl->cci_info->i2c_freq_mode; + struct device_node *of_node = cci_dev->pdev->dev.of_node; + clk_params = &cci_dev->cci_clk_params[i2c_freq_mode]; + cci_clk_src = clk_params->cci_clk_src; + + idx = of_property_match_string(of_node, + "clock-names", CCI_CLK_SRC_NAME); + if (idx < 0) { + cci_dev->cycles_per_us = CYCLES_PER_MICRO_SEC_DEFAULT; + return cci_dev->cci_clk_rates[0]; + } + + if (cci_clk_src == 0) { + clk = cci_dev->cci_clk_rates[0][idx]; + cci_dev->cycles_per_us = msm_cci_cycles_per_ms(clk); + return cci_dev->cci_clk_rates[0]; + } + + for (j = 0; j < cci_dev->num_clk_cases; j++) { + clk = cci_dev->cci_clk_rates[j][idx]; + if (clk == cci_clk_src) { + cci_dev->cycles_per_us = msm_cci_cycles_per_ms(clk); + cci_dev->cci_clk_src = cci_clk_src; + return cci_dev->cci_clk_rates[j]; + } + } + + return NULL; +} + +static int32_t msm_cci_i2c_set_sync_prms(struct v4l2_subdev *sd, + struct msm_camera_cci_ctrl *c_ctrl) +{ + int32_t rc = 0; + struct cci_device *cci_dev; + + cci_dev = v4l2_get_subdevdata(sd); + if (!cci_dev || !c_ctrl) { + pr_err("%s:%d failed: invalid params %p %p\n", __func__, + __LINE__, cci_dev, c_ctrl); + rc = -EINVAL; + return rc; + } + cci_dev->cci_wait_sync_cfg = c_ctrl->cfg.cci_wait_sync_cfg; + cci_dev->valid_sync = cci_dev->cci_wait_sync_cfg.csid < 0 ? 0 : 1; + + return rc; +} + +static int32_t msm_cci_init(struct v4l2_subdev *sd, + struct msm_camera_cci_ctrl *c_ctrl) +{ + uint8_t i = 0, j = 0; + int32_t rc = 0, ret = 0; + struct cci_device *cci_dev; + enum cci_i2c_master_t master = MASTER_0; + uint32_t *clk_rates = NULL; + + cci_dev = v4l2_get_subdevdata(sd); + if (!cci_dev || !c_ctrl) { + pr_err("%s:%d failed: invalid params %p %p\n", __func__, + __LINE__, cci_dev, c_ctrl); + rc = -EINVAL; + return rc; + } + + rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CCI, + CAM_AHB_SVS_VOTE); + if (rc < 0) { + pr_err("%s: failed to vote for AHB\n", __func__); + return rc; + } + + if (cci_dev->ref_count++) { + CDBG("%s ref_count %d\n", __func__, cci_dev->ref_count); + master = c_ctrl->cci_info->cci_i2c_master; + CDBG("%s:%d master %d\n", __func__, __LINE__, master); + if (master < MASTER_MAX && master >= 0) { + mutex_lock(&cci_dev->cci_master_info[master].mutex); + flush_workqueue(cci_dev->write_wq[master]); + /* Re-initialize the completion */ + reinit_completion(&cci_dev-> + cci_master_info[master].reset_complete); + for (i = 0; i < NUM_QUEUES; i++) + reinit_completion(&cci_dev-> + cci_master_info[master].report_q[i]); + /* Set reset pending flag to TRUE */ + cci_dev->cci_master_info[master].reset_pending = TRUE; + /* Set proper mask to RESET CMD address */ + if (master == MASTER_0) + msm_camera_io_w_mb(CCI_M0_RESET_RMSK, + cci_dev->base + CCI_RESET_CMD_ADDR); + else + msm_camera_io_w_mb(CCI_M1_RESET_RMSK, + cci_dev->base + CCI_RESET_CMD_ADDR); + /* wait for reset done irq */ + rc = wait_for_completion_timeout( + &cci_dev->cci_master_info[master]. + reset_complete, + CCI_TIMEOUT); + if (rc <= 0) + pr_err("%s:%d wait failed %d\n", __func__, + __LINE__, rc); + mutex_unlock(&cci_dev->cci_master_info[master].mutex); + } + return 0; + } + ret = msm_cci_pinctrl_init(cci_dev); + if (ret < 0) { + pr_err("%s:%d Initialization of pinctrl failed\n", + __func__, __LINE__); + cci_dev->cci_pinctrl_status = 0; + } else { + cci_dev->cci_pinctrl_status = 1; + } + rc = msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl, + cci_dev->cci_gpio_tbl_size, 1); + if (cci_dev->cci_pinctrl_status) { + ret = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl, + cci_dev->cci_pinctrl.gpio_state_active); + if (ret) + pr_err("%s:%d cannot set pin to active state\n", + __func__, __LINE__); + } + if (rc < 0) { + CDBG("%s: request gpio failed\n", __func__); + goto request_gpio_failed; + } + + rc = msm_camera_config_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg, + cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 1); + if (rc < 0) { + pr_err("%s:%d cci config_vreg failed\n", __func__, __LINE__); + goto clk_enable_failed; + } + + rc = msm_camera_enable_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg, + cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 1); + if (rc < 0) { + pr_err("%s:%d cci enable_vreg failed\n", __func__, __LINE__); + goto reg_enable_failed; + } + + clk_rates = msm_cci_get_clk_rates(cci_dev, c_ctrl); + if (!clk_rates) { + pr_err("%s: clk enable failed\n", __func__); + goto reg_enable_failed; + } + + for (i = 0; i < cci_dev->num_clk; i++) { + cci_dev->cci_clk_info[i].clk_rate = + clk_rates[i]; + } + rc = msm_camera_clk_enable(&cci_dev->pdev->dev, + cci_dev->cci_clk_info, cci_dev->cci_clk, + cci_dev->num_clk, true); + if (rc < 0) { + pr_err("%s: clk enable failed\n", __func__); + goto reg_enable_failed; + } + + /* Re-initialize the completion */ + reinit_completion(&cci_dev->cci_master_info[master].reset_complete); + for (i = 0; i < NUM_QUEUES; i++) + reinit_completion(&cci_dev->cci_master_info[master]. + report_q[i]); + rc = msm_camera_enable_irq(cci_dev->irq, true); + if (rc < 0) + pr_err("%s: irq enable failed\n", __func__); + cci_dev->hw_version = msm_camera_io_r_mb(cci_dev->base + + CCI_HW_VERSION_ADDR); + pr_info("%s:%d: hw_version = 0x%x\n", __func__, __LINE__, + cci_dev->hw_version); + cci_dev->payload_size = + MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_10; + cci_dev->support_seq_write = 0; + if (cci_dev->hw_version >= 0x10020000) { + cci_dev->payload_size = + MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11; + cci_dev->support_seq_write = 1; + } + for (i = 0; i < NUM_MASTERS; i++) { + for (j = 0; j < NUM_QUEUES; j++) { + if (j == QUEUE_0) { + if (cci_dev->hw_version >= 0x10060000) + cci_dev->cci_i2c_queue_info[i][j]. + max_queue_size = + CCI_I2C_Q0_SIZE_128W; + else + cci_dev->cci_i2c_queue_info[i][j]. + max_queue_size = + CCI_I2C_QUEUE_0_SIZE; + } else { + if (cci_dev->hw_version >= 0x10060000) + cci_dev->cci_i2c_queue_info[i][j]. + max_queue_size = + CCI_I2C_Q1_SIZE_32W; + else + cci_dev->cci_i2c_queue_info[i][j]. + max_queue_size = + CCI_I2C_QUEUE_1_SIZE; + } + CDBG("CCI Master[%d] :: Q0 size: %d Q1 size: %d\n", i, + cci_dev->cci_i2c_queue_info[i][j]. + max_queue_size, + cci_dev->cci_i2c_queue_info[i][j]. + max_queue_size); + } + } + + cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE; + msm_camera_io_w_mb(CCI_RESET_CMD_RMSK, cci_dev->base + + CCI_RESET_CMD_ADDR); + msm_camera_io_w_mb(0x1, cci_dev->base + CCI_RESET_CMD_ADDR); + rc = wait_for_completion_timeout( + &cci_dev->cci_master_info[MASTER_0].reset_complete, + CCI_TIMEOUT); + if (rc <= 0) { + pr_err("%s: wait_for_completion_timeout %d\n", + __func__, __LINE__); + if (rc == 0) + rc = -ETIMEDOUT; + goto reset_complete_failed; + } + for (i = 0; i < MASTER_MAX; i++) + cci_dev->i2c_freq_mode[i] = I2C_MAX_MODES; + msm_camera_io_w_mb(CCI_IRQ_MASK_0_RMSK, + cci_dev->base + CCI_IRQ_MASK_0_ADDR); + msm_camera_io_w_mb(CCI_IRQ_MASK_0_RMSK, + cci_dev->base + CCI_IRQ_CLEAR_0_ADDR); + msm_camera_io_w_mb(0x1, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR); + + for (i = 0; i < MASTER_MAX; i++) { + if (!cci_dev->write_wq[i]) { + pr_err("Failed to flush write wq\n"); + rc = -ENOMEM; + goto reset_complete_failed; + } else { + flush_workqueue(cci_dev->write_wq[i]); + } + } + cci_dev->cci_state = CCI_STATE_ENABLED; + + return 0; + +reset_complete_failed: + msm_camera_enable_irq(cci_dev->irq, false); + msm_camera_clk_enable(&cci_dev->pdev->dev, cci_dev->cci_clk_info, + cci_dev->cci_clk, cci_dev->num_clk, false); +reg_enable_failed: + msm_camera_config_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg, + cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 0); +clk_enable_failed: + if (cci_dev->cci_pinctrl_status) { + ret = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl, + cci_dev->cci_pinctrl.gpio_state_suspend); + if (ret) + pr_err("%s:%d cannot set pin to suspend state\n", + __func__, __LINE__); + } + msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl, + cci_dev->cci_gpio_tbl_size, 0); +request_gpio_failed: + cci_dev->ref_count--; + if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CCI, + CAM_AHB_SUSPEND_VOTE) < 0) + pr_err("%s: failed to remove vote for AHB\n", __func__); + return rc; +} + +static int32_t msm_cci_release(struct v4l2_subdev *sd) +{ + uint8_t i = 0, rc = 0; + struct cci_device *cci_dev; + + cci_dev = v4l2_get_subdevdata(sd); + if (!cci_dev->ref_count || cci_dev->cci_state != CCI_STATE_ENABLED) { + pr_err("%s invalid ref count %d / cci state %d\n", + __func__, cci_dev->ref_count, cci_dev->cci_state); + rc = -EINVAL; + goto ahb_vote_suspend; + } + if (--cci_dev->ref_count) { + CDBG("%s ref_count Exit %d\n", __func__, cci_dev->ref_count); + rc = 0; + goto ahb_vote_suspend; + } + for (i = 0; i < MASTER_MAX; i++) + if (cci_dev->write_wq[i]) + flush_workqueue(cci_dev->write_wq[i]); + + msm_camera_enable_irq(cci_dev->irq, false); + msm_camera_clk_enable(&cci_dev->pdev->dev, cci_dev->cci_clk_info, + cci_dev->cci_clk, cci_dev->num_clk, false); + + rc = msm_camera_enable_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg, + cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 0); + if (rc < 0) + pr_err("%s:%d cci disable_vreg failed\n", __func__, __LINE__); + + rc = msm_camera_config_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg, + cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 0); + if (rc < 0) + pr_err("%s:%d cci unconfig_vreg failed\n", __func__, __LINE__); + + if (cci_dev->cci_pinctrl_status) { + rc = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl, + cci_dev->cci_pinctrl.gpio_state_suspend); + if (rc) + pr_err("%s:%d cannot set pin to active state\n", + __func__, __LINE__); + } + cci_dev->cci_pinctrl_status = 0; + msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl, + cci_dev->cci_gpio_tbl_size, 0); + for (i = 0; i < MASTER_MAX; i++) + cci_dev->i2c_freq_mode[i] = I2C_MAX_MODES; + cci_dev->cci_state = CCI_STATE_DISABLED; + cci_dev->cycles_per_us = 0; + cci_dev->cci_clk_src = 0; + +ahb_vote_suspend: + if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CCI, + CAM_AHB_SUSPEND_VOTE) < 0) + pr_err("%s: failed to remove vote for AHB\n", __func__); + return rc; +} + +static int32_t msm_cci_write(struct v4l2_subdev *sd, + struct msm_camera_cci_ctrl *c_ctrl) +{ + int32_t rc = 0; + struct cci_device *cci_dev; + enum cci_i2c_master_t master; + struct msm_camera_cci_master_info *cci_master_info; + uint32_t i; + + cci_dev = v4l2_get_subdevdata(sd); + if (!cci_dev || !c_ctrl) { + pr_err("%s:%d failed: invalid params %p %p\n", __func__, + __LINE__, cci_dev, c_ctrl); + rc = -EINVAL; + return rc; + } + + master = c_ctrl->cci_info->cci_i2c_master; + cci_master_info = &cci_dev->cci_master_info[master]; + + switch (c_ctrl->cmd) { + case MSM_CCI_I2C_WRITE_SYNC_BLOCK: + mutex_lock(&cci_master_info->mutex_q[SYNC_QUEUE]); + rc = msm_cci_i2c_write(sd, c_ctrl, + SYNC_QUEUE, MSM_SYNC_ENABLE); + mutex_unlock(&cci_master_info->mutex_q[SYNC_QUEUE]); + break; + case MSM_CCI_I2C_WRITE_SYNC: + rc = msm_cci_i2c_write_async(sd, c_ctrl, + SYNC_QUEUE, MSM_SYNC_ENABLE); + break; + case MSM_CCI_I2C_WRITE: + case MSM_CCI_I2C_WRITE_SEQ: + for (i = 0; i < NUM_QUEUES; i++) { + if (mutex_trylock(&cci_master_info->mutex_q[i])) { + rc = msm_cci_i2c_write(sd, c_ctrl, i, + MSM_SYNC_DISABLE); + mutex_unlock(&cci_master_info->mutex_q[i]); + return rc; + } + } + mutex_lock(&cci_master_info->mutex_q[PRIORITY_QUEUE]); + rc = msm_cci_i2c_write(sd, c_ctrl, + PRIORITY_QUEUE, MSM_SYNC_DISABLE); + mutex_unlock(&cci_master_info->mutex_q[PRIORITY_QUEUE]); + break; + case MSM_CCI_I2C_WRITE_ASYNC: + rc = msm_cci_i2c_write_async(sd, c_ctrl, + PRIORITY_QUEUE, MSM_SYNC_DISABLE); + break; + default: + rc = -ENOIOCTLCMD; + } + return rc; +} + +static int32_t msm_cci_config(struct v4l2_subdev *sd, + struct msm_camera_cci_ctrl *cci_ctrl) +{ + int32_t rc = 0; + CDBG("%s line %d cmd %d\n", __func__, __LINE__, + cci_ctrl->cmd); + switch (cci_ctrl->cmd) { + case MSM_CCI_INIT: + rc = msm_cci_init(sd, cci_ctrl); + break; + case MSM_CCI_RELEASE: + rc = msm_cci_release(sd); + break; + case MSM_CCI_I2C_READ: + rc = msm_cci_i2c_read_bytes(sd, cci_ctrl); + break; + case MSM_CCI_I2C_WRITE: + case MSM_CCI_I2C_WRITE_SEQ: + case MSM_CCI_I2C_WRITE_SYNC: + case MSM_CCI_I2C_WRITE_ASYNC: + case MSM_CCI_I2C_WRITE_SYNC_BLOCK: + rc = msm_cci_write(sd, cci_ctrl); + break; + case MSM_CCI_GPIO_WRITE: + break; + case MSM_CCI_SET_SYNC_CID: + rc = msm_cci_i2c_set_sync_prms(sd, cci_ctrl); + break; + + default: + rc = -ENOIOCTLCMD; + } + CDBG("%s line %d rc %d\n", __func__, __LINE__, rc); + cci_ctrl->status = rc; + return rc; +} + +static irqreturn_t msm_cci_irq(int irq_num, void *data) +{ + uint32_t irq; + struct cci_device *cci_dev = data; + irq = msm_camera_io_r_mb(cci_dev->base + CCI_IRQ_STATUS_0_ADDR); + msm_camera_io_w_mb(irq, cci_dev->base + CCI_IRQ_CLEAR_0_ADDR); + msm_camera_io_w_mb(0x1, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR); + CDBG("%s CCI_I2C_M0_STATUS_ADDR = 0x%x\n", __func__, irq); + if (irq & CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK) { + if (cci_dev->cci_master_info[MASTER_0].reset_pending == TRUE) { + cci_dev->cci_master_info[MASTER_0].reset_pending = + FALSE; + complete(&cci_dev->cci_master_info[MASTER_0]. + reset_complete); + } + if (cci_dev->cci_master_info[MASTER_1].reset_pending == TRUE) { + cci_dev->cci_master_info[MASTER_1].reset_pending = + FALSE; + complete(&cci_dev->cci_master_info[MASTER_1]. + reset_complete); + } + } + if (irq & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK) { + cci_dev->cci_master_info[MASTER_0].status = 0; + complete(&cci_dev->cci_master_info[MASTER_0].reset_complete); + } + if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) { + struct msm_camera_cci_master_info *cci_master_info; + cci_master_info = &cci_dev->cci_master_info[MASTER_0]; + atomic_set(&cci_master_info->q_free[QUEUE_0], 0); + cci_master_info->status = 0; + if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) { + complete(&cci_master_info->report_q[QUEUE_0]); + atomic_set(&cci_master_info->done_pending[QUEUE_0], 0); + } + } + if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK) { + struct msm_camera_cci_master_info *cci_master_info; + cci_master_info = &cci_dev->cci_master_info[MASTER_0]; + atomic_set(&cci_master_info->q_free[QUEUE_1], 0); + cci_master_info->status = 0; + if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) { + complete(&cci_master_info->report_q[QUEUE_1]); + atomic_set(&cci_master_info->done_pending[QUEUE_1], 0); + } + } + if (irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) { + cci_dev->cci_master_info[MASTER_1].status = 0; + complete(&cci_dev->cci_master_info[MASTER_1].reset_complete); + } + if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) { + struct msm_camera_cci_master_info *cci_master_info; + cci_master_info = &cci_dev->cci_master_info[MASTER_1]; + atomic_set(&cci_master_info->q_free[QUEUE_0], 0); + cci_master_info->status = 0; + if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) { + complete(&cci_master_info->report_q[QUEUE_0]); + atomic_set(&cci_master_info->done_pending[QUEUE_0], 0); + } + } + if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK) { + struct msm_camera_cci_master_info *cci_master_info; + cci_master_info = &cci_dev->cci_master_info[MASTER_1]; + atomic_set(&cci_master_info->q_free[QUEUE_1], 0); + cci_master_info->status = 0; + if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) { + complete(&cci_master_info->report_q[QUEUE_1]); + atomic_set(&cci_master_info->done_pending[QUEUE_1], 0); + } + } + if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) { + cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE; + msm_camera_io_w_mb(CCI_M0_RESET_RMSK, + cci_dev->base + CCI_RESET_CMD_ADDR); + } + if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK) { + cci_dev->cci_master_info[MASTER_1].reset_pending = TRUE; + msm_camera_io_w_mb(CCI_M1_RESET_RMSK, + cci_dev->base + CCI_RESET_CMD_ADDR); + } + if (irq & CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK) { + pr_err("%s:%d MASTER_0 error 0x%x\n", __func__, __LINE__, irq); + cci_dev->cci_master_info[MASTER_0].status = -EINVAL; + msm_camera_io_w_mb(CCI_M0_HALT_REQ_RMSK, + cci_dev->base + CCI_HALT_REQ_ADDR); + } + if (irq & CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK) { + pr_err("%s:%d MASTER_1 error 0x%x\n", __func__, __LINE__, irq); + cci_dev->cci_master_info[MASTER_1].status = -EINVAL; + msm_camera_io_w_mb(CCI_M1_HALT_REQ_RMSK, + cci_dev->base + CCI_HALT_REQ_ADDR); + } + return IRQ_HANDLED; +} + +static int msm_cci_irq_routine(struct v4l2_subdev *sd, u32 status, + bool *handled) +{ + struct cci_device *cci_dev = v4l2_get_subdevdata(sd); + irqreturn_t ret; + CDBG("%s line %d\n", __func__, __LINE__); + ret = msm_cci_irq(cci_dev->irq->start, cci_dev); + CDBG("%s: msm_cci_irq return %d\n", __func__, ret); + *handled = TRUE; + return 0; +} + +static long msm_cci_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int32_t rc = 0; + CDBG("%s line %d\n", __func__, __LINE__); + switch (cmd) { + case VIDIOC_MSM_CCI_CFG: + rc = msm_cci_config(sd, arg); + break; + case MSM_SD_NOTIFY_FREEZE: + break; + case MSM_SD_UNNOTIFY_FREEZE: + break; + case MSM_SD_SHUTDOWN: { + struct msm_camera_cci_ctrl ctrl_cmd; + ctrl_cmd.cmd = MSM_CCI_RELEASE; + rc = msm_cci_config(sd, &ctrl_cmd); + break; + } + default: + rc = -ENOIOCTLCMD; + } + CDBG("%s line %d rc %d\n", __func__, __LINE__, rc); + return rc; +} + +static struct v4l2_subdev_core_ops msm_cci_subdev_core_ops = { + .ioctl = &msm_cci_subdev_ioctl, + .interrupt_service_routine = msm_cci_irq_routine, +}; + +static const struct v4l2_subdev_ops msm_cci_subdev_ops = { + .core = &msm_cci_subdev_core_ops, +}; + +static const struct v4l2_subdev_internal_ops msm_cci_internal_ops; + +static void msm_cci_init_cci_params(struct cci_device *new_cci_dev) +{ + uint8_t i = 0, j = 0; + for (i = 0; i < NUM_MASTERS; i++) { + new_cci_dev->cci_master_info[i].status = 0; + mutex_init(&new_cci_dev->cci_master_info[i].mutex); + init_completion(&new_cci_dev-> + cci_master_info[i].reset_complete); + + for (j = 0; j < NUM_QUEUES; j++) { + mutex_init(&new_cci_dev->cci_master_info[i].mutex_q[j]); + init_completion(&new_cci_dev-> + cci_master_info[i].report_q[j]); + } + } + return; +} + +static int32_t msm_cci_init_gpio_params(struct cci_device *cci_dev) +{ + int32_t rc = 0, i = 0; + uint32_t *val_array = NULL; + uint8_t tbl_size = 0; + struct device_node *of_node = cci_dev->pdev->dev.of_node; + struct gpio *gpio_tbl = NULL; + + cci_dev->cci_gpio_tbl_size = tbl_size = of_gpio_count(of_node); + CDBG("%s gpio count %d\n", __func__, tbl_size); + if (!tbl_size) { + pr_err("%s:%d gpio count 0\n", __func__, __LINE__); + return 0; + } + + gpio_tbl = cci_dev->cci_gpio_tbl = + kzalloc(sizeof(struct gpio) * tbl_size, GFP_KERNEL); + if (!gpio_tbl) { + pr_err("%s failed %d\n", __func__, __LINE__); + return 0; + } + + for (i = 0; i < tbl_size; i++) { + gpio_tbl[i].gpio = of_get_gpio(of_node, i); + CDBG("%s gpio_tbl[%d].gpio = %d\n", __func__, i, + gpio_tbl[i].gpio); + } + + val_array = kzalloc(sizeof(uint32_t) * tbl_size, GFP_KERNEL); + if (!val_array) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto ERROR1; + } + + rc = of_property_read_u32_array(of_node, "qcom,gpio-tbl-flags", + val_array, tbl_size); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } + for (i = 0; i < tbl_size; i++) { + gpio_tbl[i].flags = val_array[i]; + CDBG("%s gpio_tbl[%d].flags = %ld\n", __func__, i, + gpio_tbl[i].flags); + } + + for (i = 0; i < tbl_size; i++) { + rc = of_property_read_string_index(of_node, + "qcom,gpio-tbl-label", i, &gpio_tbl[i].label); + CDBG("%s gpio_tbl[%d].label = %s\n", __func__, i, + gpio_tbl[i].label); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } + } + + kfree(val_array); + return rc; + +ERROR2: + kfree(val_array); +ERROR1: + kfree(cci_dev->cci_gpio_tbl); + cci_dev->cci_gpio_tbl = NULL; + cci_dev->cci_gpio_tbl_size = 0; + return rc; +} + +static void msm_cci_init_default_clk_params(struct cci_device *cci_dev, + uint8_t index) +{ + /* default clock params are for 100Khz */ + cci_dev->cci_clk_params[index].hw_thigh = 201; + cci_dev->cci_clk_params[index].hw_tlow = 174; + cci_dev->cci_clk_params[index].hw_tsu_sto = 204; + cci_dev->cci_clk_params[index].hw_tsu_sta = 231; + cci_dev->cci_clk_params[index].hw_thd_dat = 22; + cci_dev->cci_clk_params[index].hw_thd_sta = 162; + cci_dev->cci_clk_params[index].hw_tbuf = 227; + cci_dev->cci_clk_params[index].hw_scl_stretch_en = 0; + cci_dev->cci_clk_params[index].hw_trdhld = 6; + cci_dev->cci_clk_params[index].hw_tsp = 3; + cci_dev->cci_clk_params[index].cci_clk_src = 37500000; +} + +static void msm_cci_init_clk_params(struct cci_device *cci_dev) +{ + int32_t rc = 0; + uint32_t val = 0; + uint8_t count = 0; + struct device_node *of_node = cci_dev->pdev->dev.of_node; + struct device_node *src_node = NULL; + + for (count = 0; count < I2C_MAX_MODES; count++) { + + if (I2C_STANDARD_MODE == count) + src_node = of_find_node_by_name(of_node, + "qcom,i2c_standard_mode"); + else if (I2C_FAST_MODE == count) + src_node = of_find_node_by_name(of_node, + "qcom,i2c_fast_mode"); + else if (I2C_FAST_PLUS_MODE == count) + src_node = of_find_node_by_name(of_node, + "qcom,i2c_fast_plus_mode"); + else + src_node = of_find_node_by_name(of_node, + "qcom,i2c_custom_mode"); + + rc = of_property_read_u32(src_node, "qcom,hw-thigh", &val); + CDBG("%s qcom,hw-thigh %d, rc %d\n", __func__, val, rc); + if (!rc) { + cci_dev->cci_clk_params[count].hw_thigh = val; + rc = of_property_read_u32(src_node, "qcom,hw-tlow", + &val); + CDBG("%s qcom,hw-tlow %d, rc %d\n", __func__, val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_tlow = val; + rc = of_property_read_u32(src_node, "qcom,hw-tsu-sto", + &val); + CDBG("%s qcom,hw-tsu-sto %d, rc %d\n", + __func__, val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_tsu_sto = val; + rc = of_property_read_u32(src_node, "qcom,hw-tsu-sta", + &val); + CDBG("%s qcom,hw-tsu-sta %d, rc %d\n", + __func__, val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_tsu_sta = val; + rc = of_property_read_u32(src_node, "qcom,hw-thd-dat", + &val); + CDBG("%s qcom,hw-thd-dat %d, rc %d\n", + __func__, val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_thd_dat = val; + rc = of_property_read_u32(src_node, "qcom,hw-thd-sta", + &val); + CDBG("%s qcom,hw-thd-sta %d, rc %d\n", __func__, + val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_thd_sta = val; + rc = of_property_read_u32(src_node, "qcom,hw-tbuf", + &val); + CDBG("%s qcom,hw-tbuf %d, rc %d\n", __func__, val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_tbuf = val; + rc = of_property_read_u32(src_node, + "qcom,hw-scl-stretch-en", &val); + CDBG("%s qcom,hw-scl-stretch-en %d, rc %d\n", + __func__, val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_scl_stretch_en = val; + rc = of_property_read_u32(src_node, "qcom,hw-trdhld", + &val); + CDBG("%s qcom,hw-trdhld %d, rc %d\n", + __func__, val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_trdhld = val; + rc = of_property_read_u32(src_node, "qcom,hw-tsp", + &val); + CDBG("%s qcom,hw-tsp %d, rc %d\n", __func__, val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_tsp = val; + val = 0; + rc = of_property_read_u32(src_node, "qcom,cci-clk-src", + &val); + CDBG("%s qcom,cci-clk-src %d, rc %d\n", + __func__, val, rc); + cci_dev->cci_clk_params[count].cci_clk_src = val; + } + else + msm_cci_init_default_clk_params(cci_dev, count); + + + + of_node_put(src_node); + src_node = NULL; + } + return; +} + +struct v4l2_subdev *msm_cci_get_subdev(void) +{ + return g_cci_subdev; +} + +static int msm_cci_probe(struct platform_device *pdev) +{ + struct cci_device *new_cci_dev; + int rc = 0, i = 0; + CDBG("%s: pdev %p device id = %d\n", __func__, pdev, pdev->id); + new_cci_dev = kzalloc(sizeof(struct cci_device), GFP_KERNEL); + if (!new_cci_dev) { + pr_err("%s: no enough memory\n", __func__); + return -ENOMEM; + } + v4l2_subdev_init(&new_cci_dev->msm_sd.sd, &msm_cci_subdev_ops); + new_cci_dev->msm_sd.sd.internal_ops = &msm_cci_internal_ops; + snprintf(new_cci_dev->msm_sd.sd.name, + ARRAY_SIZE(new_cci_dev->msm_sd.sd.name), "msm_cci"); + v4l2_set_subdevdata(&new_cci_dev->msm_sd.sd, new_cci_dev); + platform_set_drvdata(pdev, &new_cci_dev->msm_sd.sd); + CDBG("%s sd %p\n", __func__, &new_cci_dev->msm_sd.sd); + if (pdev->dev.of_node) + of_property_read_u32((&pdev->dev)->of_node, + "cell-index", &pdev->id); + + rc = msm_camera_get_clk_info_and_rates(pdev, + &new_cci_dev->cci_clk_info, &new_cci_dev->cci_clk, + &new_cci_dev->cci_clk_rates, &new_cci_dev->num_clk_cases, + &new_cci_dev->num_clk); + if (rc < 0) { + pr_err("%s: msm_cci_get_clk_info() failed", __func__); + kfree(new_cci_dev); + return -EFAULT; + } + + new_cci_dev->ref_count = 0; + new_cci_dev->base = msm_camera_get_reg_base(pdev, "cci", true); + if (!new_cci_dev->base) { + pr_err("%s: no mem resource?\n", __func__); + rc = -ENODEV; + goto cci_no_resource; + } + new_cci_dev->irq = msm_camera_get_irq(pdev, "cci"); + if (!new_cci_dev->irq) { + pr_err("%s: no irq resource?\n", __func__); + rc = -ENODEV; + goto cci_no_resource; + } + CDBG("%s line %d cci irq start %d end %d\n", __func__, + __LINE__, + (int) new_cci_dev->irq->start, + (int) new_cci_dev->irq->end); + rc = msm_camera_register_irq(pdev, new_cci_dev->irq, + msm_cci_irq, IRQF_TRIGGER_RISING, "cci", new_cci_dev); + if (rc < 0) { + pr_err("%s: irq request fail\n", __func__); + rc = -EBUSY; + goto cci_release_mem; + } + + msm_camera_enable_irq(new_cci_dev->irq, false); + new_cci_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x6; + msm_sd_register(&new_cci_dev->msm_sd); + new_cci_dev->pdev = pdev; + msm_cci_init_cci_params(new_cci_dev); + msm_cci_init_clk_params(new_cci_dev); + msm_cci_init_gpio_params(new_cci_dev); + + rc = msm_camera_get_dt_vreg_data(new_cci_dev->pdev->dev.of_node, + &(new_cci_dev->cci_vreg), &(new_cci_dev->regulator_count)); + if (rc < 0) { + pr_err("%s: msm_camera_get_dt_vreg_data fail\n", __func__); + rc = -EFAULT; + goto cci_release_mem; + } + + if ((new_cci_dev->regulator_count < 0) || + (new_cci_dev->regulator_count > MAX_REGULATOR)) { + pr_err("%s: invalid reg count = %d, max is %d\n", __func__, + new_cci_dev->regulator_count, MAX_REGULATOR); + rc = -EFAULT; + goto cci_invalid_vreg_data; + } + + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (rc) + pr_err("%s: failed to add child nodes, rc=%d\n", __func__, rc); + new_cci_dev->cci_state = CCI_STATE_DISABLED; + g_cci_subdev = &new_cci_dev->msm_sd.sd; + for (i = 0; i < MASTER_MAX; i++) { + new_cci_dev->write_wq[i] = create_singlethread_workqueue( + "msm_cci_wq"); + if (!new_cci_dev->write_wq[i]) + pr_err("Failed to create write wq\n"); + } + CDBG("%s cci subdev %p\n", __func__, &new_cci_dev->msm_sd.sd); + CDBG("%s line %d\n", __func__, __LINE__); + return 0; + +cci_invalid_vreg_data: + kfree(new_cci_dev->cci_vreg); +cci_release_mem: + msm_camera_put_reg_base(pdev, new_cci_dev->base, "cci", true); +cci_no_resource: + kfree(new_cci_dev); + return rc; +} + +static int msm_cci_exit(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev = platform_get_drvdata(pdev); + struct cci_device *cci_dev = + v4l2_get_subdevdata(subdev); + + msm_camera_put_clk_info_and_rates(pdev, + &cci_dev->cci_clk_info, &cci_dev->cci_clk, + &cci_dev->cci_clk_rates, cci_dev->num_clk_cases, + cci_dev->num_clk); + + msm_camera_put_reg_base(pdev, cci_dev->base, "cci", true); + kfree(cci_dev); + return 0; +} + +static const struct of_device_id msm_cci_dt_match[] = { + {.compatible = "qcom,cci"}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_cci_dt_match); + +static struct platform_driver cci_driver = { + .probe = msm_cci_probe, + .remove = msm_cci_exit, + .driver = { + .name = MSM_CCI_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = msm_cci_dt_match, + }, +}; + +static int __init msm_cci_init_module(void) +{ + return platform_driver_register(&cci_driver); +} + +static void __exit msm_cci_exit_module(void) +{ + platform_driver_unregister(&cci_driver); +} + +module_init(msm_cci_init_module); +module_exit(msm_cci_exit_module); +MODULE_DESCRIPTION("MSM CCI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/camera/sensor/cci/msm_cci.h b/camera/sensor/cci/msm_cci.h new file mode 100644 index 00000000..a51aacfd --- /dev/null +++ b/camera/sensor/cci/msm_cci.h @@ -0,0 +1,238 @@ +/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef MSM_CCI_H +#define MSM_CCI_H + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <media/v4l2-subdev.h> +#include <linux/workqueue.h> +#include <lenovo_media/msm_cam_sensor.h> +#include <lenovo_soc/qcom/camera2.h> +#include "msm_sd.h" +#include "cam_soc_api.h" + +#define NUM_MASTERS 2 +#define NUM_QUEUES 2 + +#define TRUE 1 +#define FALSE 0 + +#define CCI_PINCTRL_STATE_DEFAULT "cci_default" +#define CCI_PINCTRL_STATE_SLEEP "cci_suspend" + +#define CCI_NUM_CLK_MAX 16 +#define CCI_NUM_CLK_CASES 5 +#define CCI_CLK_SRC_NAME "cci_src_clk" +#define MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_10 10 +#define MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11 11 +#define BURST_MIN_FREE_SIZE 8 + +enum cci_i2c_sync { + MSM_SYNC_DISABLE, + MSM_SYNC_ENABLE, +}; + +enum cci_i2c_queue_t { + QUEUE_0, + QUEUE_1, + QUEUE_INVALID, +}; + +struct msm_camera_cci_client { + struct v4l2_subdev *cci_subdev; + uint32_t freq; + enum i2c_freq_mode_t i2c_freq_mode; + enum cci_i2c_master_t cci_i2c_master; + uint16_t sid; + uint16_t cid; + uint32_t timeout; + uint16_t retries; + uint16_t id_map; +}; + +enum msm_cci_cmd_type { + MSM_CCI_INIT, + MSM_CCI_RELEASE, + MSM_CCI_SET_SID, + MSM_CCI_SET_FREQ, + MSM_CCI_SET_SYNC_CID, + MSM_CCI_I2C_READ, + MSM_CCI_I2C_WRITE, + MSM_CCI_I2C_WRITE_SEQ, + MSM_CCI_I2C_WRITE_ASYNC, + MSM_CCI_GPIO_WRITE, + MSM_CCI_I2C_WRITE_SYNC, + MSM_CCI_I2C_WRITE_SYNC_BLOCK, +}; + +struct msm_camera_cci_wait_sync_cfg { + uint16_t cid; + int16_t csid; + uint16_t line; + uint16_t delay; +}; + +struct msm_camera_cci_gpio_cfg { + uint16_t gpio_queue; + uint16_t i2c_queue; +}; + +struct msm_camera_cci_i2c_read_cfg { + uint16_t addr; + enum msm_camera_i2c_reg_addr_type addr_type; + uint8_t *data; + uint16_t num_byte; +}; + +struct msm_camera_cci_i2c_queue_info { + uint32_t max_queue_size; + uint32_t report_id; + uint32_t irq_en; + uint32_t capture_rep_data; +}; + +struct msm_camera_cci_ctrl { + int32_t status; + struct msm_camera_cci_client *cci_info; + enum msm_cci_cmd_type cmd; + union { + struct msm_camera_i2c_reg_setting cci_i2c_write_cfg; + struct msm_camera_cci_i2c_read_cfg cci_i2c_read_cfg; + struct msm_camera_cci_wait_sync_cfg cci_wait_sync_cfg; + struct msm_camera_cci_gpio_cfg gpio_cfg; + } cfg; +}; + +struct msm_camera_cci_master_info { + uint32_t status; + atomic_t q_free[NUM_QUEUES]; + uint8_t q_lock[NUM_QUEUES]; + uint8_t reset_pending; + struct mutex mutex; + struct completion reset_complete; + struct mutex mutex_q[NUM_QUEUES]; + struct completion report_q[NUM_QUEUES]; + atomic_t done_pending[NUM_QUEUES]; +}; + +struct msm_cci_clk_params_t { + uint16_t hw_thigh; + uint16_t hw_tlow; + uint16_t hw_tsu_sto; + uint16_t hw_tsu_sta; + uint16_t hw_thd_dat; + uint16_t hw_thd_sta; + uint16_t hw_tbuf; + uint8_t hw_scl_stretch_en; + uint8_t hw_trdhld; + uint8_t hw_tsp; + uint32_t cci_clk_src; +}; + +enum msm_cci_state_t { + CCI_STATE_ENABLED, + CCI_STATE_DISABLED, +}; + +struct cci_device { + struct platform_device *pdev; + struct msm_sd_subdev msm_sd; + struct v4l2_subdev subdev; + struct resource *irq; + void __iomem *base; + + uint32_t hw_version; + uint8_t ref_count; + enum msm_cci_state_t cci_state; + size_t num_clk; + size_t num_clk_cases; + struct clk **cci_clk; + uint32_t **cci_clk_rates; + struct msm_cam_clk_info *cci_clk_info; + struct msm_camera_cci_i2c_queue_info + cci_i2c_queue_info[NUM_MASTERS][NUM_QUEUES]; + struct msm_camera_cci_master_info cci_master_info[NUM_MASTERS]; + enum i2c_freq_mode_t i2c_freq_mode[NUM_MASTERS]; + struct msm_cci_clk_params_t cci_clk_params[I2C_MAX_MODES]; + struct gpio *cci_gpio_tbl; + uint8_t cci_gpio_tbl_size; + struct msm_pinctrl_info cci_pinctrl; + uint8_t cci_pinctrl_status; + uint32_t cycles_per_us; + uint32_t cci_clk_src; + struct camera_vreg_t *cci_vreg; + struct regulator *cci_reg_ptr[MAX_REGULATOR]; + int32_t regulator_count; + uint8_t payload_size; + uint8_t support_seq_write; + struct workqueue_struct *write_wq[MASTER_MAX]; + struct msm_camera_cci_wait_sync_cfg cci_wait_sync_cfg; + uint8_t valid_sync; +}; + +enum msm_cci_i2c_cmd_type { + CCI_I2C_SET_PARAM_CMD = 1, + CCI_I2C_WAIT_CMD, + CCI_I2C_WAIT_SYNC_CMD, + CCI_I2C_WAIT_GPIO_EVENT_CMD, + CCI_I2C_TRIG_I2C_EVENT_CMD, + CCI_I2C_LOCK_CMD, + CCI_I2C_UNLOCK_CMD, + CCI_I2C_REPORT_CMD, + CCI_I2C_WRITE_CMD, + CCI_I2C_READ_CMD, + CCI_I2C_WRITE_DISABLE_P_CMD, + CCI_I2C_READ_DISABLE_P_CMD, + CCI_I2C_WRITE_CMD2, + CCI_I2C_WRITE_CMD3, + CCI_I2C_REPEAT_CMD, + CCI_I2C_INVALID_CMD, +}; + +enum msm_cci_gpio_cmd_type { + CCI_GPIO_SET_PARAM_CMD = 1, + CCI_GPIO_WAIT_CMD, + CCI_GPIO_WAIT_SYNC_CMD, + CCI_GPIO_WAIT_GPIO_IN_EVENT_CMD, + CCI_GPIO_WAIT_I2C_Q_TRIG_EVENT_CMD, + CCI_GPIO_OUT_CMD, + CCI_GPIO_TRIG_EVENT_CMD, + CCI_GPIO_REPORT_CMD, + CCI_GPIO_REPEAT_CMD, + CCI_GPIO_CONTINUE_CMD, + CCI_GPIO_INVALID_CMD, +}; + +struct cci_write_async { + struct cci_device *cci_dev; + struct msm_camera_cci_ctrl c_ctrl; + enum cci_i2c_queue_t queue; + struct work_struct work; + enum cci_i2c_sync sync_en; +}; + +#ifdef CONFIG_MSM_CCI +struct v4l2_subdev *msm_cci_get_subdev(void); +#else +static inline struct v4l2_subdev *msm_cci_get_subdev(void) +{ + return NULL; +} +#endif + +#define VIDIOC_MSM_CCI_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 23, struct msm_camera_cci_ctrl *) + +#endif diff --git a/camera/sensor/csid/Makefile b/camera/sensor/csid/Makefile new file mode 100644 index 00000000..507054fe --- /dev/null +++ b/camera/sensor/csid/Makefile @@ -0,0 +1,4 @@ +ccflags-y += -Icamera +ccflags-y += -Icamera/common +ccflags-y += -Icamera/sensor/io +obj-$(CONFIG_MSM_CSID) += msm_csid.o diff --git a/camera/sensor/csid/include/msm_csid_2_0_hwreg.h b/camera/sensor/csid/include/msm_csid_2_0_hwreg.h new file mode 100644 index 00000000..dbe672ac --- /dev/null +++ b/camera/sensor/csid/include/msm_csid_2_0_hwreg.h @@ -0,0 +1,65 @@ +/* Copyright (c) 2014-2015, 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_CSID_2_0_HWREG_H +#define MSM_CSID_2_0_HWREG_H + +#include <sensor/csid/msm_csid.h> + +uint8_t csid_lane_assign_v2_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; + +struct csid_reg_parms_t csid_v2_0 = { + + /* MIPI CSID registers */ + 0x0, + 0x4, + 0x4, + 0x8, + 0xc, + 0x10, + 0x14, + 0x18, + 0x1C, + 0x5c, + 0x60, + 0x64, + 0x68, + 0x6c, + 0x70, + 0x74, + 0x78, + 0x7C, + 0x80, + 0x84, + 0x88, + 0x8C, + 0x90, + 0x94, + 0x9C, + 0xA0, + 0xA8, + 0xAC, + 0xB0, + 11, + 0x7FFF, + 0x2, + 17, + 0x02000011, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0x7f010800, + 20, + 0xFFFFFFFF, + 0xFFFFFFFF, +}; +#endif diff --git a/camera/sensor/csid/include/msm_csid_2_2_hwreg.h b/camera/sensor/csid/include/msm_csid_2_2_hwreg.h new file mode 100644 index 00000000..94c62957 --- /dev/null +++ b/camera/sensor/csid/include/msm_csid_2_2_hwreg.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2014-2015, 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_CSID_2_2_HWREG_H +#define MSM_CSID_2_2_HWREG_H + +#include <sensor/csid/msm_csid.h> + +uint8_t csid_lane_assign_v2_2[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; + +struct csid_reg_parms_t csid_v2_2 = { + /* MIPI CSID registers */ + 0x0, + 0x4, + 0x4, + 0x8, + 0xc, + 0x10, + 0x14, + 0x18, + 0x1C, + 0x5c, + 0x60, + 0x64, + 0x68, + 0x6c, + 0x70, + 0x74, + 0x78, + 0x7C, + 0x80, + 0x84, + 0x88, + 0x8C, + 0x90, + 0x94, + 0x9C, + 0xA0, + 0xA8, + 0xAC, + 0xB0, + 11, + 0x7FFF, + 0x2, + 17, + 0x02001000, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0x7f010800, + 20, + 0xFFFFFFFF, + 0xFFFFFFFF, +}; +#endif diff --git a/camera/sensor/csid/include/msm_csid_3_0_hwreg.h b/camera/sensor/csid/include/msm_csid_3_0_hwreg.h new file mode 100644 index 00000000..74f79af7 --- /dev/null +++ b/camera/sensor/csid/include/msm_csid_3_0_hwreg.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2014-2015, 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_CSID_3_0_HWREG_H +#define MSM_CSID_3_0_HWREG_H + +#include <sensor/csid/msm_csid.h> + +uint8_t csid_lane_assign_v3_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; + +struct csid_reg_parms_t csid_v3_0 = { + /* MIPI CSID registers */ + 0x0, + 0x4, + 0x8, + 0xC, + 0x10, + 0x14, + 0x18, + 0x1C, + 0x20, + 0x60, + 0x64, + 0x68, + 0x6C, + 0x70, + 0x74, + 0x78, + 0x7C, + 0x80, + 0x84, + 0x88, + 0x8C, + 0x90, + 0x94, + 0x98, + 0xA0, + 0xA4, + 0xAC, + 0xB0, + 0xB4, + 11, + 0x7FFF, + 0x4, + 17, + 0x30000000, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0x7f010800, + 20, + 0xFFFFFFFF, + 0xFFFFFFFF, +}; +#endif diff --git a/camera/sensor/csid/include/msm_csid_3_1_hwreg.h b/camera/sensor/csid/include/msm_csid_3_1_hwreg.h new file mode 100644 index 00000000..23553779 --- /dev/null +++ b/camera/sensor/csid/include/msm_csid_3_1_hwreg.h @@ -0,0 +1,64 @@ + /* Copyright (c) 2014-2015 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_CSID_3_1_HWREG_H +#define MSM_CSID_3_1_HWREG_H + +#include <sensor/csid/msm_csid.h> + +uint8_t csid_lane_assign_v3_1[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; + +struct csid_reg_parms_t csid_v3_1 = { + /* MIPI CSID registers */ + 0x0, + 0x4, + 0x8, + 0xC, + 0x10, + 0x14, + 0x18, + 0x1C, + 0x20, + 0x60, + 0x64, + 0x68, + 0x6C, + 0x70, + 0x74, + 0x78, + 0x7C, + 0x80, + 0x84, + 0x88, + 0x8C, + 0x90, + 0x94, + 0x98, + 0xA0, + 0xA4, + 0xAC, + 0xB0, + 0xB4, + 11, + 0x7FFF, + 0x4, + 17, + 0x30010000, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0x7f010800, + 20, + 0xFFFFFFFF, + 0xFFFFFFFF, +}; +#endif diff --git a/camera/sensor/csid/include/msm_csid_3_2_hwreg.h b/camera/sensor/csid/include/msm_csid_3_2_hwreg.h new file mode 100644 index 00000000..efab52a0 --- /dev/null +++ b/camera/sensor/csid/include/msm_csid_3_2_hwreg.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2014-2015, 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_CSID_3_2_HWREG_H +#define MSM_CSID_3_2_HWREG_H + +#include <sensor/csid/msm_csid.h> + +uint8_t csid_lane_assign_v3_2[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; + +struct csid_reg_parms_t csid_v3_2 = { + /* MIPI CSID registers */ + 0x0, + 0x4, + 0x8, + 0xC, + 0x10, + 0x14, + 0x18, + 0x1C, + 0x20, + 0x60, + 0x64, + 0x68, + 0x6C, + 0x70, + 0x74, + 0x78, + 0x7C, + 0x80, + 0x84, + 0x88, + 0x8C, + 0x90, + 0x94, + 0x98, + 0xA0, + 0xA4, + 0xAC, + 0xB0, + 0xB4, + 11, + 0x7FFF, + 0x4, + 17, + 0x30020000, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0x7f010800, + 20, + 0xFFFFFFFF, + 0xFFFFFFFF, +}; +#endif diff --git a/camera/sensor/csid/include/msm_csid_3_4_1_hwreg.h b/camera/sensor/csid/include/msm_csid_3_4_1_hwreg.h new file mode 100644 index 00000000..31a0fda0 --- /dev/null +++ b/camera/sensor/csid/include/msm_csid_3_4_1_hwreg.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2015, 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_CSID_3_4_1_HWREG_H +#define MSM_CSID_3_4_1_HWREG_H + +#include <sensor/csid/msm_csid.h> +uint8_t csid_lane_assign_v3_4_1[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; + +struct csid_reg_parms_t csid_v3_4_1 = { + /* MIPI CSID registers */ + 0x0, + 0x4, + 0x8, + 0xC, + 0x10, + 0x14, + 0x18, + 0x1C, + 0x20, + 0x60, + 0x64, + 0x68, + 0x6C, + 0x70, + 0x74, + 0x78, + 0x7C, + 0x80, + 0x84, + 0x88, + 0x8C, + 0x90, + 0x94, + 0x98, + 0xA0, + 0xA4, + 0xAC, + 0xB0, + 0xB4, + 11, + 0x7FFF, + 0x4, + 17, + 0x30040001, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0x7f010800, + 20, + 0xFFFFFFFF, + 0xFFFFFFFF, +}; +#endif diff --git a/camera/sensor/csid/include/msm_csid_3_4_2_hwreg.h b/camera/sensor/csid/include/msm_csid_3_4_2_hwreg.h new file mode 100644 index 00000000..70c23edf --- /dev/null +++ b/camera/sensor/csid/include/msm_csid_3_4_2_hwreg.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2015, 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_CSID_3_4_2_HWREG_H +#define MSM_CSID_3_4_2_HWREG_H + +#include <sensor/csid/msm_csid.h> + +uint8_t csid_lane_assign_v3_4_2[PHY_LANE_MAX] = {0, 4, 1, 2, 3}; +struct csid_reg_parms_t csid_v3_4_2 = { + /* MIPI CSID registers */ + 0x0, + 0x4, + 0x8, + 0xC, + 0x10, + 0x14, + 0x18, + 0x1C, + 0x20, + 0x60, + 0x64, + 0x68, + 0x6C, + 0x70, + 0x74, + 0x78, + 0x7C, + 0x80, + 0x84, + 0x88, + 0x8C, + 0x90, + 0x94, + 0x98, + 0xA0, + 0xA4, + 0xAC, + 0xB0, + 0xB4, + 11, + 0x7FFF, + 0x4, + 17, + 0x30040002, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0x7f010800, + 20, + 0xFFFFFFFF, + 0xFFFFFFFF, +}; +#endif diff --git a/camera/sensor/csid/include/msm_csid_3_4_3_hwreg.h b/camera/sensor/csid/include/msm_csid_3_4_3_hwreg.h new file mode 100644 index 00000000..b5d21a50 --- /dev/null +++ b/camera/sensor/csid/include/msm_csid_3_4_3_hwreg.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2015, 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_CSID_3_4_3_HWREG_H +#define MSM_CSID_3_4_3_HWREG_H + +#include <sensor/csid/msm_csid.h> + +uint8_t csid_lane_assign_v3_4_3[PHY_LANE_MAX] = {0, 4, 1, 2, 3}; +struct csid_reg_parms_t csid_v3_4_3 = { + /* MIPI CSID registers */ + 0x0, + 0x4, + 0x8, + 0xC, + 0x10, + 0x14, + 0x18, + 0x1C, + 0x20, + 0x60, + 0x64, + 0x68, + 0x6C, + 0x70, + 0x74, + 0x78, + 0x7C, + 0x80, + 0x84, + 0x88, + 0x8C, + 0x90, + 0x94, + 0x98, + 0xA0, + 0xA4, + 0xAC, + 0xB0, + 0xB4, + 11, + 0x7FFF, + 0x4, + 17, + 0x30040003, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0x7f010800, + 20, + 0xFFFFFFFF, + 0xFFFFFFFF, +}; +#endif diff --git a/camera/sensor/csid/include/msm_csid_3_5_1_hwreg.h b/camera/sensor/csid/include/msm_csid_3_5_1_hwreg.h new file mode 100644 index 00000000..e7b83eec --- /dev/null +++ b/camera/sensor/csid/include/msm_csid_3_5_1_hwreg.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2015, 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_CSID_3_5_1_HWREG_H +#define MSM_CSID_3_5_1_HWREG_H + +#include <sensor/csid/msm_csid.h> + +uint8_t csid_lane_assign_v3_5_1[PHY_LANE_MAX] = {0, 4, 1, 2, 3}; + +struct csid_reg_parms_t csid_v3_5_1 = { + /* MIPI CSID registers */ + 0x0, + 0x4, + 0x8, + 0x10, + 0x14, + 0x18, + 0x1C, + 0x20, + 0x24, + 0x64, + 0x68, + 0x6C, + 0x70, + 0x74, + 0x78, + 0x7C, + 0x80, + 0x88, + 0x8C, + 0x90, + 0x94, + 0x98, + 0x9C, + 0xA0, + 0xA8, + 0xAC, + 0xB4, + 0xB8, + 0xBC, + 11, + 0x7FFF, + 0x4, + 17, + 0x30050001, + 0xC, + 0x84, + 0xA4, + 0x7f010800, + 20, + 17, + 16, +}; +#endif diff --git a/camera/sensor/csid/include/msm_csid_3_5_hwreg.h b/camera/sensor/csid/include/msm_csid_3_5_hwreg.h new file mode 100644 index 00000000..e1623e5a --- /dev/null +++ b/camera/sensor/csid/include/msm_csid_3_5_hwreg.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2015, 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_CSID_3_5_HWREG_H +#define MSM_CSID_3_5_HWREG_H + +#include <sensor/csid/msm_csid.h> + +uint8_t csid_lane_assign_v3_5[PHY_LANE_MAX] = {0, 4, 1, 2, 3}; + +struct csid_reg_parms_t csid_v3_5 = { + /* MIPI CSID registers */ + 0x0, + 0x4, + 0x8, + 0x10, + 0x14, + 0x18, + 0x1C, + 0x20, + 0x24, + 0x64, + 0x68, + 0x6C, + 0x70, + 0x74, + 0x78, + 0x7C, + 0x80, + 0x88, + 0x8C, + 0x90, + 0x94, + 0x98, + 0x9C, + 0xA0, + 0xA8, + 0xAC, + 0xB4, + 0xB8, + 0xBC, + 11, + 0x7FFF, + 0x4, + 17, + 0x30050000, + 0xC, + 0x84, + 0xA4, + 0x7f010800, + 20, + 17, + 16, +}; +#endif diff --git a/camera/sensor/csid/include/msm_csid_3_6_0_hwreg.h b/camera/sensor/csid/include/msm_csid_3_6_0_hwreg.h new file mode 100644 index 00000000..879b0ba0 --- /dev/null +++ b/camera/sensor/csid/include/msm_csid_3_6_0_hwreg.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2015, 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_CSID_3_6_0_HWREG_H +#define MSM_CSID_3_6_0_HWREG_H + +#include <sensor/csid/msm_csid.h> + +uint8_t csid_lane_assign_v3_6_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; +struct csid_reg_parms_t csid_v3_6_0 = { + /* MIPI CSID registers */ + 0x0, + 0x4, + 0x8, + 0xC, + 0x10, + 0x14, + 0x18, + 0x1C, + 0x20, + 0x60, + 0x64, + 0x68, + 0x6C, + 0x70, + 0x74, + 0x78, + 0x7C, + 0x80, + 0x84, + 0x88, + 0x8C, + 0x90, + 0x94, + 0x98, + 0xA0, + 0xA4, + 0xAC, + 0xB0, + 0xB4, + 11, + 0x7FFF, + 0x4, + 17, + 0x30060000, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0x7f010800, + 20, + 0xFFFFFFFF, + 0xFFFFFFFF, +}; +#endif diff --git a/camera/sensor/csid/msm_csid.c b/camera/sensor/csid/msm_csid.c new file mode 100644 index 00000000..ef07a540 --- /dev/null +++ b/camera/sensor/csid/msm_csid.c @@ -0,0 +1,1218 @@ +/* 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 <linux/delay.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/irqreturn.h> +#include "msm_csid.h" +#include "msm_sd.h" +#include "msm_camera_io_util.h" +#include "msm_camera_dt_util.h" +#include "include/msm_csid_2_0_hwreg.h" +#include "include/msm_csid_2_2_hwreg.h" +#include "include/msm_csid_3_0_hwreg.h" +#include "include/msm_csid_3_1_hwreg.h" +#include "include/msm_csid_3_2_hwreg.h" +#include "include/msm_csid_3_5_hwreg.h" +#include "include/msm_csid_3_4_1_hwreg.h" +#include "include/msm_csid_3_4_2_hwreg.h" +#include "include/msm_csid_3_4_3_hwreg.h" +#include "include/msm_csid_3_6_0_hwreg.h" +#include "include/msm_csid_3_5_1_hwreg.h" +#include "cam_hw_ops.h" + +#define V4L2_IDENT_CSID 50002 +#define CSID_VERSION_V20 0x02000011 +#define CSID_VERSION_V22 0x02001000 +#define CSID_VERSION_V30 0x30000000 +#define CSID_VERSION_V31 0x30010000 +#define CSID_VERSION_V31_1 0x30010001 +#define CSID_VERSION_V31_3 0x30010003 +#define CSID_VERSION_V32 0x30020000 +#define CSID_VERSION_V33 0x30030000 +#define CSID_VERSION_V34 0x30040000 +#define CSID_VERSION_V34_1 0x30040001 +#define CSID_VERSION_V34_2 0x30040002 +#define CSID_VERSION_V34_3 0x30040003 +#define CSID_VERSION_V36 0x30060000 +#define CSID_VERSION_V37 0x30070000 +#define CSID_VERSION_V35 0x30050000 +#define CSID_VERSION_V35_1 0x30050001 +#define CSID_VERSION_V40 0x40000000 +#define MSM_CSID_DRV_NAME "msm_csid" + +#define DBG_CSID 0 +#define SHORT_PKT_CAPTURE 0 +#define SHORT_PKT_OFFSET 0x200 +#define ENABLE_3P_BIT 1 +#define SOF_DEBUG_ENABLE 1 +#define SOF_DEBUG_DISABLE 0 + +#define TRUE 1 +#define FALSE 0 + +#define CSID_TIMEOUT msecs_to_jiffies(100) + +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +static struct camera_vreg_t csid_vreg_info[] = { + {"qcom,mipi-csi-vdd", 0, 0, 12000}, +}; + +#ifdef CONFIG_COMPAT +static struct v4l2_file_operations msm_csid_v4l2_subdev_fops; +#endif + +static int msm_csid_cid_lut( + struct msm_camera_csid_lut_params *csid_lut_params, + struct csid_device *csid_dev) +{ + int rc = 0, i = 0; + uint32_t val = 0; + + if (!csid_lut_params) { + pr_err("%s:%d csid_lut_params NULL\n", __func__, __LINE__); + return -EINVAL; + } + if (csid_lut_params->num_cid > MAX_CID) { + pr_err("%s:%d num_cid exceeded limit num_cid = %d max = %d\n", + __func__, __LINE__, csid_lut_params->num_cid, MAX_CID); + return -EINVAL; + } + for (i = 0; i < csid_lut_params->num_cid; i++) { + if (csid_lut_params->vc_cfg[i]->cid >= MAX_CID) { + pr_err("%s: cid outside range %d\n", + __func__, csid_lut_params->vc_cfg[i]->cid); + return -EINVAL; + } + CDBG("%s lut params num_cid = %d, cid = %d\n", + __func__, + csid_lut_params->num_cid, + csid_lut_params->vc_cfg[i]->cid); + CDBG("%s lut params dt = 0x%x, df = %d\n", __func__, + csid_lut_params->vc_cfg[i]->dt, + csid_lut_params->vc_cfg[i]->decode_format); + if (csid_lut_params->vc_cfg[i]->dt < 0x12 || + csid_lut_params->vc_cfg[i]->dt > 0x37) { + pr_err("%s: unsupported data type 0x%x\n", + __func__, csid_lut_params->vc_cfg[i]->dt); + return rc; + } + val = msm_camera_io_r(csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_cid_lut_vc_0_addr + + (csid_lut_params->vc_cfg[i]->cid >> 2) * 4) + & ~(0xFF << ((csid_lut_params->vc_cfg[i]->cid % 4) * + 8)); + val |= (csid_lut_params->vc_cfg[i]->dt << + ((csid_lut_params->vc_cfg[i]->cid % 4) * 8)); + msm_camera_io_w(val, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_cid_lut_vc_0_addr + + (csid_lut_params->vc_cfg[i]->cid >> 2) * 4); + + val = (csid_lut_params->vc_cfg[i]->decode_format << 4) | 0x3; + msm_camera_io_w(val, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_cid_n_cfg_addr + + (csid_lut_params->vc_cfg[i]->cid * 4)); + } + return rc; +} + +#if (DBG_CSID) +static void msm_csid_set_debug_reg(struct csid_device *csid_dev, + struct msm_camera_csid_params *csid_params) +{ + uint32_t val = 0; + + if ((csid_dev->hw_dts_version == CSID_VERSION_V34_1) || + (csid_dev->hw_dts_version == CSID_VERSION_V36)) { + val = ((1 << csid_params->lane_cnt) - 1) << 20; + msm_camera_io_w(0x7f010800 | val, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_mask_addr); + msm_camera_io_w(0x7f010800 | val, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr); + } else { + if (csid_dev->csid_3p_enabled == 1) { + val = ((1 << csid_params->lane_cnt) - 1) << + csid_dev->ctrl_reg-> + csid_reg.csid_err_lane_overflow_offset_3p; + } else { + val = ((1 << csid_params->lane_cnt) - 1) << + csid_dev->ctrl_reg-> + csid_reg.csid_err_lane_overflow_offset_2p; + } + val |= csid_dev->ctrl_reg->csid_reg.csid_irq_mask_val; + msm_camera_io_w(val, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_mask_addr); + msm_camera_io_w(val, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr); + } +} +#elif(SHORT_PKT_CAPTURE) +static void msm_csid_set_debug_reg(struct csid_device *csid_dev, + struct msm_camera_csid_params *csid_params) +{ + uint32_t val = 0; + + if ((csid_dev->hw_dts_version == CSID_VERSION_V34_1) || + (csid_dev->hw_dts_version == CSID_VERSION_V36)) { + val = ((1 << csid_params->lane_cnt) - 1) << 20; + msm_camera_io_w(0x7f010a00 | val, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_mask_addr); + msm_camera_io_w(0x7f010a00 | val, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr); + } else { + if (csid_dev->csid_3p_enabled == 1) { + val = ((1 << csid_params->lane_cnt) - 1) << + csid_dev->ctrl_reg-> + csid_reg.csid_err_lane_overflow_offset_3p; + } else { + val = ((1 << csid_params->lane_cnt) - 1) << + csid_dev->ctrl_reg-> + csid_reg.csid_err_lane_overflow_offset_2p; + } + val |= csid_dev->ctrl_reg->csid_reg.csid_irq_mask_val; + val |= SHORT_PKT_OFFSET; + msm_camera_io_w(val, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_mask_addr); + msm_camera_io_w(val, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr); + } +} +#else +static void msm_csid_set_debug_reg(struct csid_device *csid_dev, + struct msm_camera_csid_params *csid_params) {} +#endif + +static void msm_csid_set_sof_freeze_debug_reg(struct csid_device *csid_dev) +{ + uint32_t val = 0; + + if (csid_dev->csid_3p_enabled == 1) { + val = ((1 << csid_dev->current_csid_params.lane_cnt) - 1) << + csid_dev->ctrl_reg-> + csid_reg.csid_err_lane_overflow_offset_3p; + } else { + val = ((1 << csid_dev->current_csid_params.lane_cnt) - 1) << + csid_dev->ctrl_reg-> + csid_reg.csid_err_lane_overflow_offset_2p; + } + val |= csid_dev->ctrl_reg->csid_reg.csid_irq_mask_val; + val |= SHORT_PKT_OFFSET; + msm_camera_io_w(val, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_mask_addr); + msm_camera_io_w(val, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr); +} + +static int msm_csid_reset(struct csid_device *csid_dev) +{ + int32_t rc = 0; + msm_camera_io_w(csid_dev->ctrl_reg->csid_reg.csid_rst_stb_all, + csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_rst_cmd_addr); + rc = wait_for_completion_timeout(&csid_dev->reset_complete, + CSID_TIMEOUT); + if (rc <= 0) { + pr_err("wait_for_completion in msm_csid_reset fail rc = %d\n", + rc); + if (rc == 0) + rc = -ETIMEDOUT; + } + return rc; +} + +static bool msm_csid_find_max_clk_rate(struct csid_device *csid_dev) +{ + int i; + bool ret = FALSE; + + for (i = 0; i < csid_dev->num_clk; i++) { + if (!strcmp(csid_dev->csid_clk_info[i].clk_name, + "csi_src_clk")) { + CDBG("%s:%d, copy csi_src_clk, clk_rate[%d] = %ld", + __func__, __LINE__, i, + csid_dev->csid_clk_info[i].clk_rate); + csid_dev->csid_max_clk = + csid_dev->csid_clk_info[i].clk_rate; + csid_dev->csid_clk_index = i; + ret = TRUE; + break; + } + } + return ret; +} +static int msm_csid_config(struct csid_device *csid_dev, + struct msm_camera_csid_params *csid_params) +{ + int rc = 0; + uint32_t val = 0; + long clk_rate = 0; + uint32_t input_sel; + uint32_t lane_assign = 0; + uint8_t lane_num = 0; + uint8_t i, j; + void __iomem *csidbase; + csidbase = csid_dev->base; + if (!csidbase || !csid_params) { + pr_err("%s:%d csidbase %p, csid params %p\n", __func__, + __LINE__, csidbase, csid_params); + return -EINVAL; + } + + CDBG("%s csid_params, lane_cnt = %d, lane_assign = 0x%x\n", + __func__, + csid_params->lane_cnt, + csid_params->lane_assign); + CDBG("%s csid_params phy_sel = %d\n", __func__, + csid_params->phy_sel); + + csid_dev->csid_lane_cnt = csid_params->lane_cnt; + rc = msm_csid_reset(csid_dev); + if (rc < 0) { + pr_err("%s:%d msm_csid_reset failed\n", __func__, __LINE__); + return rc; + } + + if (!msm_csid_find_max_clk_rate(csid_dev)) + pr_err("msm_csid_find_max_clk_rate failed\n"); + + clk_rate = (csid_params->csi_clk > 0) ? + (csid_params->csi_clk) : csid_dev->csid_max_clk; + + clk_rate = msm_camera_clk_set_rate(&csid_dev->pdev->dev, + csid_dev->csid_clk[csid_dev->csid_clk_index], clk_rate); + if (clk_rate < 0) { + pr_err("csi_src_clk set failed\n"); + return -EINVAL; + } + + if (csid_dev->is_testmode == 1) { + struct msm_camera_csid_testmode_parms *tm; + tm = &csid_dev->testmode_params; + + /* 31:24 V blank, 23:13 H blank, 3:2 num of active DT, 1:0 VC */ + val = ((tm->v_blanking_count & 0xFF) << 24) | + ((tm->h_blanking_count & 0x7FF) << 13); + msm_camera_io_w(val, csidbase + + csid_dev->ctrl_reg->csid_reg.csid_tg_vc_cfg_addr); + CDBG("[TG] CSID_TG_VC_CFG_ADDR 0x%08x\n", val); + + /* 28:16 bytes per lines, 12:0 num of lines */ + val = ((tm->num_bytes_per_line & 0x1FFF) << 16) | + (tm->num_lines & 0x1FFF); + msm_camera_io_w(val, csidbase + + csid_dev->ctrl_reg->csid_reg.csid_tg_dt_n_cfg_0_addr); + CDBG("[TG] CSID_TG_DT_n_CFG_0_ADDR 0x%08x\n", val); + + /* 5:0 data type */ + val = csid_params->lut_params.vc_cfg[0]->dt; + msm_camera_io_w(val, csidbase + + csid_dev->ctrl_reg->csid_reg.csid_tg_dt_n_cfg_1_addr); + CDBG("[TG] CSID_TG_DT_n_CFG_1_ADDR 0x%08x\n", val); + + /* 2:0 output random */ + msm_camera_io_w(csid_dev->testmode_params.payload_mode, + csidbase + + csid_dev->ctrl_reg->csid_reg.csid_tg_dt_n_cfg_2_addr); + } else { + val = csid_params->lane_cnt - 1; + + for (i = 0, j = 0; i < PHY_LANE_MAX; i++) { + if (i == PHY_LANE_CLK) + continue; + lane_num = (csid_params->lane_assign >> j) & 0xF; + if (lane_num >= PHY_LANE_MAX) { + pr_err("%s:%d invalid lane number %d\n", + __func__, __LINE__, lane_num); + return -EINVAL; + } + if (csid_dev->ctrl_reg->csid_lane_assign[lane_num] >= + PHY_LANE_MAX){ + pr_err("%s:%d invalid lane map %d\n", + __func__, __LINE__, + csid_dev->ctrl_reg-> + csid_lane_assign[lane_num]); + return -EINVAL; + } + lane_assign |= + csid_dev->ctrl_reg->csid_lane_assign[lane_num] + << j; + j += 4; + } + + CDBG("%s csid_params calculated lane_assign = 0x%X\n", + __func__, lane_assign); + + val |= lane_assign << + csid_dev->ctrl_reg->csid_reg.csid_dl_input_sel_shift; + if (csid_dev->hw_version < CSID_VERSION_V30) { + val |= (0xF << 10); + msm_camera_io_w(val, csidbase + + csid_dev->ctrl_reg->csid_reg.csid_core_ctrl_0_addr); + } else { + msm_camera_io_w(val, csidbase + + csid_dev->ctrl_reg->csid_reg.csid_core_ctrl_0_addr); + val = csid_params->phy_sel << + csid_dev->ctrl_reg->csid_reg.csid_phy_sel_shift; + val |= 0xF; + msm_camera_io_w(val, csidbase + + csid_dev->ctrl_reg->csid_reg.csid_core_ctrl_1_addr); + } + if (csid_dev->hw_version == CSID_VERSION_V35 && + csid_params->csi_3p_sel == 1) { + csid_dev->csid_3p_enabled = 1; + val = (csid_params->lane_cnt - 1) << ENABLE_3P_BIT; + + for (i = 0; i < csid_params->lane_cnt; i++) { + input_sel = + (csid_params->lane_assign >> (4*i)) + & 0xF; + val |= input_sel << (4*(i+1)); + } + val |= csid_params->phy_sel << + csid_dev->ctrl_reg->csid_reg.csid_phy_sel_shift_3p; + val |= ENABLE_3P_BIT; + msm_camera_io_w(val, csidbase + csid_dev->ctrl_reg + ->csid_reg.csid_3p_ctrl_0_addr); + } + } + + rc = msm_csid_cid_lut(&csid_params->lut_params, csid_dev); + if (rc < 0) { + pr_err("%s:%d config cid lut failed\n", __func__, __LINE__); + return rc; + } + msm_csid_set_debug_reg(csid_dev, csid_params); + + if (csid_dev->is_testmode == 1) + msm_camera_io_w(0x00A06437, csidbase + + csid_dev->ctrl_reg->csid_reg.csid_tg_ctrl_addr); + + return rc; +} + +#if SHORT_PKT_CAPTURE +static irqreturn_t msm_csid_irq(int irq_num, void *data) +{ + uint32_t irq; + uint32_t short_dt = 0; + uint32_t count = 0, dt = 0; + struct csid_device *csid_dev = data; + + if (!csid_dev) { + pr_err("%s:%d csid_dev NULL\n", __func__, __LINE__); + return IRQ_HANDLED; + } + irq = msm_camera_io_r(csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_status_addr); + CDBG("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n", + __func__, csid_dev->pdev->id, irq); + if (irq & (0x1 << + csid_dev->ctrl_reg->csid_reg.csid_rst_done_irq_bitshift)) + complete(&csid_dev->reset_complete); + if (irq & SHORT_PKT_OFFSET) { + short_dt = msm_camera_io_r(csid_dev->base + + csid_dev->ctrl_reg-> + csid_reg.csid_captured_short_pkt_addr); + count = (short_dt >> 8) & 0xffff; + dt = short_dt >> 24; + CDBG("CSID:: %s:%d core %d dt: 0x%x, count: %d\n", + __func__, __LINE__, csid_dev->pdev->id, dt, count); + msm_camera_io_w(0x101, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_rst_cmd_addr); + } + msm_camera_io_w(irq, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr); + return IRQ_HANDLED; +} +#else +static irqreturn_t msm_csid_irq(int irq_num, void *data) +{ + uint32_t irq; + struct csid_device *csid_dev = data; + + if (!csid_dev) { + pr_err("%s:%d csid_dev NULL\n", __func__, __LINE__); + return IRQ_HANDLED; + } + irq = msm_camera_io_r(csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_status_addr); + pr_err_ratelimited("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n", + __func__, csid_dev->pdev->id, irq); + if (irq & (0x1 << + csid_dev->ctrl_reg->csid_reg.csid_rst_done_irq_bitshift)) + complete(&csid_dev->reset_complete); + msm_camera_io_w(irq, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr); + return IRQ_HANDLED; +} +#endif + +static int msm_csid_irq_routine(struct v4l2_subdev *sd, u32 status, + bool *handled) +{ + struct csid_device *csid_dev = v4l2_get_subdevdata(sd); + irqreturn_t ret; + CDBG("%s E\n", __func__); + ret = msm_csid_irq(csid_dev->irq->start, csid_dev); + *handled = TRUE; + return 0; +} + +static int msm_csid_init(struct csid_device *csid_dev, uint32_t *csid_version) +{ + int rc = 0; + + if (!csid_version) { + pr_err("%s:%d csid_version NULL\n", __func__, __LINE__); + rc = -EINVAL; + return rc; + } + + csid_dev->reg_ptr = NULL; + + if (csid_dev->csid_state == CSID_POWER_UP) { + pr_err("%s: csid invalid state %d\n", __func__, + csid_dev->csid_state); + return -EINVAL; + } + + rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSID, + CAM_AHB_SVS_VOTE); + if (rc < 0) { + pr_err("%s: failed to vote for AHB\n", __func__); + return rc; + } + + pr_info("%s: CSID_VERSION = 0x%x\n", __func__, + csid_dev->ctrl_reg->csid_reg.csid_version); + /* power up */ + rc = msm_camera_config_vreg(&csid_dev->pdev->dev, csid_dev->csid_vreg, + csid_dev->regulator_count, NULL, 0, + &csid_dev->csid_reg_ptr[0], 1); + if (rc < 0) { + pr_err("%s:%d csid config_vreg failed\n", __func__, __LINE__); + goto top_vreg_config_failed; + } + + rc = msm_camera_config_vreg(&csid_dev->pdev->dev, + csid_vreg_info, ARRAY_SIZE(csid_vreg_info), + NULL, 0, &csid_dev->csi_vdd, 1); + if (rc < 0) { + pr_err("%s: regulator on failed\n", __func__); + goto csid_vreg_config_failed; + } + + rc = msm_camera_enable_vreg(&csid_dev->pdev->dev, csid_dev->csid_vreg, + csid_dev->regulator_count, NULL, 0, + &csid_dev->csid_reg_ptr[0], 1); + if (rc < 0) { + pr_err("%s:%d csid enable_vreg failed\n", __func__, __LINE__); + goto top_vreg_enable_failed; + } + + rc = msm_camera_enable_vreg(&csid_dev->pdev->dev, + csid_vreg_info, ARRAY_SIZE(csid_vreg_info), + NULL, 0, &csid_dev->csi_vdd, 1); + if (rc < 0) { + pr_err("%s: regulator enable failed\n", __func__); + goto csid_vreg_enable_failed; + } + rc = msm_camera_clk_enable(&csid_dev->pdev->dev, + csid_dev->csid_clk_info, csid_dev->csid_clk, + csid_dev->num_clk, true); + if (rc < 0) { + pr_err("%s:%d clock enable failed\n", + __func__, __LINE__); + goto clk_enable_failed; + } + CDBG("%s:%d called\n", __func__, __LINE__); + csid_dev->hw_version = + msm_camera_io_r(csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_hw_version_addr); + CDBG("%s:%d called csid_dev->hw_version %x\n", __func__, __LINE__, + csid_dev->hw_version); + *csid_version = csid_dev->hw_version; + csid_dev->csid_sof_debug = SOF_DEBUG_DISABLE; + + csid_dev->is_testmode = 0; + + init_completion(&csid_dev->reset_complete); + + rc = msm_camera_enable_irq(csid_dev->irq, true); + if (rc < 0) + pr_err("%s: irq enable failed\n", __func__); + rc = msm_csid_reset(csid_dev); + if (rc < 0) { + pr_err("%s:%d msm_csid_reset failed\n", __func__, __LINE__); + goto msm_csid_reset_fail; + } + + csid_dev->csid_state = CSID_POWER_UP; + return rc; + +msm_csid_reset_fail: + msm_camera_enable_irq(csid_dev->irq, false); + msm_camera_clk_enable(&csid_dev->pdev->dev, csid_dev->csid_clk_info, + csid_dev->csid_clk, csid_dev->num_clk, false); +clk_enable_failed: + msm_camera_enable_vreg(&csid_dev->pdev->dev, + csid_vreg_info, ARRAY_SIZE(csid_vreg_info), + NULL, 0, &csid_dev->csi_vdd, 0); +csid_vreg_enable_failed: + msm_camera_enable_vreg(&csid_dev->pdev->dev, csid_dev->csid_vreg, + csid_dev->regulator_count, NULL, 0, + &csid_dev->csid_reg_ptr[0], 0); +top_vreg_enable_failed: + msm_camera_config_vreg(&csid_dev->pdev->dev, + csid_vreg_info, ARRAY_SIZE(csid_vreg_info), + NULL, 0, &csid_dev->csi_vdd, 0); +csid_vreg_config_failed: + msm_camera_config_vreg(&csid_dev->pdev->dev, csid_dev->csid_vreg, + csid_dev->regulator_count, NULL, 0, + &csid_dev->csid_reg_ptr[0], 0); +top_vreg_config_failed: + if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSID, + CAM_AHB_SUSPEND_VOTE) < 0) + pr_err("%s: failed to remove vote from AHB\n", __func__); + return rc; +} + +static int msm_csid_release(struct csid_device *csid_dev) +{ + uint32_t irq; + + if (csid_dev->csid_state != CSID_POWER_UP) { + pr_err("%s: csid invalid state %d\n", __func__, + csid_dev->csid_state); + return -EINVAL; + } + + CDBG("%s:%d, hw_version = 0x%x\n", __func__, __LINE__, + csid_dev->hw_version); + + irq = msm_camera_io_r(csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_status_addr); + msm_camera_io_w(irq, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr); + msm_camera_io_w(0, csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_mask_addr); + + msm_camera_enable_irq(csid_dev->irq, false); + + msm_camera_clk_enable(&csid_dev->pdev->dev, + csid_dev->csid_clk_info, + csid_dev->csid_clk, + csid_dev->num_clk, false); + + msm_camera_enable_vreg(&csid_dev->pdev->dev, + csid_vreg_info, ARRAY_SIZE(csid_vreg_info), + NULL, 0, &csid_dev->csi_vdd, 0); + + msm_camera_enable_vreg(&csid_dev->pdev->dev, + csid_dev->csid_vreg, csid_dev->regulator_count, NULL, + 0, &csid_dev->csid_reg_ptr[0], 0); + + msm_camera_config_vreg(&csid_dev->pdev->dev, + csid_vreg_info, ARRAY_SIZE(csid_vreg_info), + NULL, 0, &csid_dev->csi_vdd, 0); + + msm_camera_config_vreg(&csid_dev->pdev->dev, + csid_dev->csid_vreg, csid_dev->regulator_count, NULL, + 0, &csid_dev->csid_reg_ptr[0], 0); + + if (!IS_ERR_OR_NULL(csid_dev->reg_ptr)) { + regulator_disable(csid_dev->reg_ptr); + regulator_put(csid_dev->reg_ptr); + } + + csid_dev->csid_state = CSID_POWER_DOWN; + + if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSID, + CAM_AHB_SUSPEND_VOTE) < 0) + pr_err("%s: failed to remove vote from AHB\n", __func__); + return 0; +} + +static int32_t msm_csid_cmd(struct csid_device *csid_dev, void __user *arg) +{ + int rc = 0; + struct csid_cfg_data *cdata = (struct csid_cfg_data *)arg; + + if (!csid_dev || !cdata) { + pr_err("%s:%d csid_dev %p, cdata %p\n", __func__, __LINE__, + csid_dev, cdata); + return -EINVAL; + } + CDBG("%s cfgtype = %d\n", __func__, cdata->cfgtype); + switch (cdata->cfgtype) { + case CSID_INIT: + rc = msm_csid_init(csid_dev, &cdata->cfg.csid_version); + CDBG("%s csid version 0x%x\n", __func__, + cdata->cfg.csid_version); + break; + case CSID_TESTMODE_CFG: { + csid_dev->is_testmode = 1; + if (copy_from_user(&csid_dev->testmode_params, + (void *)cdata->cfg.csid_testmode_params, + sizeof(struct msm_camera_csid_testmode_parms))) { + pr_err("%s: %d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + break; + } + case CSID_CFG: { + struct msm_camera_csid_params csid_params; + struct msm_camera_csid_vc_cfg *vc_cfg = NULL; + int i = 0; + if (copy_from_user(&csid_params, + (void *)cdata->cfg.csid_params, + sizeof(struct msm_camera_csid_params))) { + pr_err("%s: %d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + if (csid_params.lut_params.num_cid < 1 || + csid_params.lut_params.num_cid > MAX_CID) { + pr_err("%s: %d num_cid outside range\n", + __func__, __LINE__); + rc = -EINVAL; + break; + } + for (i = 0; i < csid_params.lut_params.num_cid; i++) { + vc_cfg = kzalloc(sizeof(struct msm_camera_csid_vc_cfg), + GFP_KERNEL); + if (!vc_cfg) { + pr_err("%s: %d failed\n", __func__, __LINE__); + rc = -ENOMEM; + goto MEM_CLEAN; + } + if (copy_from_user(vc_cfg, + (void *)csid_params.lut_params.vc_cfg[i], + sizeof(struct msm_camera_csid_vc_cfg))) { + pr_err("%s: %d failed\n", __func__, __LINE__); + kfree(vc_cfg); + rc = -EFAULT; + goto MEM_CLEAN; + } + csid_params.lut_params.vc_cfg[i] = vc_cfg; + } + csid_dev->current_csid_params = csid_params; + csid_dev->csid_sof_debug = SOF_DEBUG_DISABLE; + rc = msm_csid_config(csid_dev, &csid_params); +MEM_CLEAN: + for (i--; i >= 0; i--) + kfree(csid_params.lut_params.vc_cfg[i]); + break; + } + case CSID_RELEASE: + rc = msm_csid_release(csid_dev); + break; + default: + pr_err("%s: %d failed\n", __func__, __LINE__); + rc = -ENOIOCTLCMD; + break; + } + return rc; +} + +static int32_t msm_csid_get_subdev_id(struct csid_device *csid_dev, void *arg) +{ + uint32_t *subdev_id = (uint32_t *)arg; + if (!subdev_id) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -EINVAL; + } + *subdev_id = csid_dev->pdev->id; + pr_debug("%s:%d subdev_id %d\n", __func__, __LINE__, *subdev_id); + return 0; +} + +static long msm_csid_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int rc = -ENOIOCTLCMD; + struct csid_device *csid_dev = v4l2_get_subdevdata(sd); + + mutex_lock(&csid_dev->mutex); + CDBG("%s:%d id %d\n", __func__, __LINE__, csid_dev->pdev->id); + switch (cmd) { + case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID: + rc = msm_csid_get_subdev_id(csid_dev, arg); + break; + case VIDIOC_MSM_CSID_IO_CFG: + rc = msm_csid_cmd(csid_dev, arg); + break; + case MSM_SD_NOTIFY_FREEZE: + if (csid_dev->csid_state != CSID_POWER_UP) + break; + if (csid_dev->csid_sof_debug == SOF_DEBUG_DISABLE) { + csid_dev->csid_sof_debug = SOF_DEBUG_ENABLE; + msm_csid_set_sof_freeze_debug_reg(csid_dev); + } + break; + case MSM_SD_UNNOTIFY_FREEZE: + if (csid_dev->csid_state != CSID_POWER_UP) + break; + csid_dev->csid_sof_debug = SOF_DEBUG_DISABLE; + break; + case VIDIOC_MSM_CSID_RELEASE: + case MSM_SD_SHUTDOWN: + rc = msm_csid_release(csid_dev); + break; + default: + pr_err_ratelimited("%s: command not found\n", __func__); + } + CDBG("%s:%d\n", __func__, __LINE__); + mutex_unlock(&csid_dev->mutex); + return rc; +} + + +#ifdef CONFIG_COMPAT +static int32_t msm_csid_cmd32(struct csid_device *csid_dev, void __user *arg) +{ + int rc = 0; + struct csid_cfg_data *cdata; + struct csid_cfg_data32 *arg32 = (struct csid_cfg_data32 *) (arg); + struct csid_cfg_data local_arg; + local_arg.cfgtype = arg32->cfgtype; + cdata = &local_arg; + + if (!csid_dev || !cdata) { + pr_err("%s:%d csid_dev %p, cdata %p\n", __func__, __LINE__, + csid_dev, cdata); + return -EINVAL; + } + + CDBG("%s cfgtype = %d\n", __func__, cdata->cfgtype); + switch (cdata->cfgtype) { + case CSID_INIT: + rc = msm_csid_init(csid_dev, &cdata->cfg.csid_version); + arg32->cfg.csid_version = local_arg.cfg.csid_version; + CDBG("%s csid version 0x%x\n", __func__, + cdata->cfg.csid_version); + break; + case CSID_TESTMODE_CFG: { + csid_dev->is_testmode = 1; + if (copy_from_user(&csid_dev->testmode_params, + (void *)compat_ptr(arg32->cfg.csid_testmode_params), + sizeof(struct msm_camera_csid_testmode_parms))) { + pr_err("%s: %d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + break; + } + case CSID_CFG: { + + struct msm_camera_csid_params csid_params; + struct msm_camera_csid_vc_cfg *vc_cfg = NULL; + int i = 0; + struct msm_camera_csid_lut_params32 lut_par32; + struct msm_camera_csid_params32 csid_params32; + struct msm_camera_csid_vc_cfg vc_cfg32; + + if (copy_from_user(&csid_params32, + (void *)compat_ptr(arg32->cfg.csid_params), + sizeof(struct msm_camera_csid_params32))) { + pr_err("%s: %d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + csid_params.lane_cnt = csid_params32.lane_cnt; + csid_params.lane_assign = csid_params32.lane_assign; + csid_params.phy_sel = csid_params32.phy_sel; + csid_params.csi_clk = csid_params32.csi_clk; + csid_params.csi_3p_sel = csid_params32.csi_3p_sel; + + lut_par32 = csid_params32.lut_params; + csid_params.lut_params.num_cid = lut_par32.num_cid; + + if (csid_params.lut_params.num_cid < 1 || + csid_params.lut_params.num_cid > MAX_CID) { + pr_err("%s: %d num_cid outside range %d\n", __func__, + __LINE__, csid_params.lut_params.num_cid); + rc = -EINVAL; + break; + } + + for (i = 0; i < lut_par32.num_cid; i++) { + vc_cfg = kzalloc(sizeof(struct msm_camera_csid_vc_cfg), + GFP_KERNEL); + if (!vc_cfg) { + pr_err("%s: %d failed\n", __func__, __LINE__); + rc = -ENOMEM; + goto MEM_CLEAN32; + } + /* msm_camera_csid_vc_cfg size + * does not change in COMPAT MODE + */ + if (copy_from_user(&vc_cfg32, + (void *)compat_ptr(lut_par32.vc_cfg[i]), + sizeof(vc_cfg32))) { + pr_err("%s: %d failed\n", __func__, __LINE__); + kfree(vc_cfg); + vc_cfg = NULL; + rc = -EFAULT; + goto MEM_CLEAN32; + } + vc_cfg->cid = vc_cfg32.cid; + vc_cfg->dt = vc_cfg32.dt; + vc_cfg->decode_format = vc_cfg32.decode_format; + csid_params.lut_params.vc_cfg[i] = vc_cfg; + } + rc = msm_csid_config(csid_dev, &csid_params); + csid_dev->current_csid_params = csid_params; + +MEM_CLEAN32: + for (i--; i >= 0; i--) { + kfree(csid_params.lut_params.vc_cfg[i]); + csid_params.lut_params.vc_cfg[i] = NULL; + } + break; + } + case CSID_RELEASE: + rc = msm_csid_release(csid_dev); + break; + default: + pr_err("%s: %d failed\n", __func__, __LINE__); + rc = -ENOIOCTLCMD; + break; + } + return rc; +} + +static long msm_csid_subdev_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int rc = -ENOIOCTLCMD; + struct csid_device *csid_dev = v4l2_get_subdevdata(sd); + + mutex_lock(&csid_dev->mutex); + CDBG("%s:%d id %d\n", __func__, __LINE__, csid_dev->pdev->id); + switch (cmd) { + case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID: + rc = msm_csid_get_subdev_id(csid_dev, arg); + break; + case VIDIOC_MSM_CSID_IO_CFG32: + rc = msm_csid_cmd32(csid_dev, arg); + break; + case MSM_SD_NOTIFY_FREEZE: + if (csid_dev->csid_state != CSID_POWER_UP) + break; + if (csid_dev->csid_sof_debug == SOF_DEBUG_DISABLE) { + csid_dev->csid_sof_debug = SOF_DEBUG_ENABLE; + msm_csid_set_sof_freeze_debug_reg(csid_dev); + } + break; + case MSM_SD_UNNOTIFY_FREEZE: + if (csid_dev->csid_state != CSID_POWER_UP) + break; + csid_dev->csid_sof_debug = SOF_DEBUG_DISABLE; + break; + case VIDIOC_MSM_CSID_RELEASE: + case MSM_SD_SHUTDOWN: + rc = msm_csid_release(csid_dev); + break; + default: + pr_err_ratelimited("%s: command not found\n", __func__); + } + CDBG("%s:%d\n", __func__, __LINE__); + mutex_unlock(&csid_dev->mutex); + return rc; +} + +static long msm_csid_subdev_do_ioctl32( + struct file *file, unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + + return msm_csid_subdev_ioctl32(sd, cmd, arg); +} + +static long msm_csid_subdev_fops_ioctl32(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return video_usercopy(file, cmd, arg, msm_csid_subdev_do_ioctl32); +} +#endif +static const struct v4l2_subdev_internal_ops msm_csid_internal_ops; + +static struct v4l2_subdev_core_ops msm_csid_subdev_core_ops = { + .ioctl = &msm_csid_subdev_ioctl, + .interrupt_service_routine = msm_csid_irq_routine, +}; + +static const struct v4l2_subdev_ops msm_csid_subdev_ops = { + .core = &msm_csid_subdev_core_ops, +}; + +static int csid_probe(struct platform_device *pdev) +{ + struct csid_device *new_csid_dev; + uint32_t csi_vdd_voltage = 0; + int rc = 0; + new_csid_dev = kzalloc(sizeof(struct csid_device), GFP_KERNEL); + if (!new_csid_dev) { + pr_err("%s: no enough memory\n", __func__); + return -ENOMEM; + } + + new_csid_dev->csid_3p_enabled = 0; + new_csid_dev->ctrl_reg = NULL; + new_csid_dev->ctrl_reg = kzalloc(sizeof(struct csid_ctrl_t), + GFP_KERNEL); + if (!new_csid_dev->ctrl_reg) { + pr_err("%s:%d kzalloc failed\n", __func__, __LINE__); + kfree(new_csid_dev); + return -ENOMEM; + } + + v4l2_subdev_init(&new_csid_dev->msm_sd.sd, &msm_csid_subdev_ops); + v4l2_set_subdevdata(&new_csid_dev->msm_sd.sd, new_csid_dev); + platform_set_drvdata(pdev, &new_csid_dev->msm_sd.sd); + mutex_init(&new_csid_dev->mutex); + + if (pdev->dev.of_node) { + rc = of_property_read_u32((&pdev->dev)->of_node, + "cell-index", &pdev->id); + if (rc < 0) { + pr_err("%s:%d failed to read cell-index\n", __func__, + __LINE__); + goto csid_no_resource; + } + CDBG("%s device id %d\n", __func__, pdev->id); + + rc = of_property_read_u32((&pdev->dev)->of_node, + "qcom,csi-vdd-voltage", &csi_vdd_voltage); + if (rc < 0) { + pr_err("%s:%d failed to read qcom,csi-vdd-voltage\n", + __func__, __LINE__); + goto csid_no_resource; + } + CDBG("%s:%d reading mipi_csi_vdd is %d\n", __func__, __LINE__, + csi_vdd_voltage); + + csid_vreg_info[0].min_voltage = csi_vdd_voltage; + csid_vreg_info[0].max_voltage = csi_vdd_voltage; + } + + rc = msm_camera_get_clk_info(pdev, &new_csid_dev->csid_clk_info, + &new_csid_dev->csid_clk, &new_csid_dev->num_clk); + if (rc < 0) { + pr_err("%s: msm_camera_get_clk_info failed", __func__); + rc = -EFAULT; + goto csid_no_resource; + } + + rc = msm_camera_get_dt_vreg_data(pdev->dev.of_node, + &(new_csid_dev->csid_vreg), &(new_csid_dev->regulator_count)); + if (rc < 0) { + pr_err("%s: get vreg data from dtsi fail\n", __func__); + rc = -EFAULT; + goto csid_no_resource; + } + + if ((new_csid_dev->regulator_count < 0) || + (new_csid_dev->regulator_count > MAX_REGULATOR)) { + pr_err("%s: invalid reg count = %d, max is %d\n", __func__, + new_csid_dev->regulator_count, MAX_REGULATOR); + rc = -EFAULT; + goto csid_no_resource; + } + + new_csid_dev->base = msm_camera_get_reg_base(pdev, "csid", true); + if (!new_csid_dev->base) { + pr_err("%s: no mem resource?\n", __func__); + rc = -ENODEV; + goto csid_invalid_vreg_data; + } + new_csid_dev->irq = msm_camera_get_irq(pdev, "csid"); + if (!new_csid_dev->irq) { + pr_err("%s: no irq resource?\n", __func__); + rc = -ENODEV; + goto csid_invalid_irq; + } + new_csid_dev->pdev = pdev; + new_csid_dev->msm_sd.sd.internal_ops = &msm_csid_internal_ops; + new_csid_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(new_csid_dev->msm_sd.sd.name, + ARRAY_SIZE(new_csid_dev->msm_sd.sd.name), "msm_csid"); + media_entity_init(&new_csid_dev->msm_sd.sd.entity, 0, NULL, 0); + new_csid_dev->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + new_csid_dev->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_CSID; + new_csid_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x5; + msm_sd_register(&new_csid_dev->msm_sd); + +#ifdef CONFIG_COMPAT + msm_cam_copy_v4l2_subdev_fops(&msm_csid_v4l2_subdev_fops); + msm_csid_v4l2_subdev_fops.compat_ioctl32 = msm_csid_subdev_fops_ioctl32; + new_csid_dev->msm_sd.sd.devnode->fops = &msm_csid_v4l2_subdev_fops; +#endif + + rc = msm_camera_register_irq(pdev, new_csid_dev->irq, + msm_csid_irq, IRQF_TRIGGER_RISING, "csid", new_csid_dev); + if (rc < 0) { + pr_err("%s: irq request fail\n", __func__); + rc = -EBUSY; + goto csid_invalid_irq; + } + rc = msm_camera_enable_irq(new_csid_dev->irq, false); + if (rc < 0) { + pr_err("%s Error registering irq ", __func__); + rc = -EBUSY; + goto csid_invalid_irq; + } + + if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node, + "qcom,csid-v2.0")) { + new_csid_dev->ctrl_reg->csid_reg = csid_v2_0; + new_csid_dev->ctrl_reg->csid_lane_assign = + csid_lane_assign_v2_0; + new_csid_dev->hw_dts_version = CSID_VERSION_V20; + } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node, + "qcom,csid-v2.2")) { + new_csid_dev->ctrl_reg->csid_reg = csid_v2_2; + new_csid_dev->ctrl_reg->csid_lane_assign = + csid_lane_assign_v2_2; + new_csid_dev->hw_dts_version = CSID_VERSION_V22; + } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node, + "qcom,csid-v3.0")) { + new_csid_dev->ctrl_reg->csid_reg = csid_v3_0; + new_csid_dev->ctrl_reg->csid_lane_assign = + csid_lane_assign_v3_0; + new_csid_dev->hw_dts_version = CSID_VERSION_V30; + } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node, + "qcom,csid-v4.0")) { + new_csid_dev->ctrl_reg->csid_reg = csid_v3_0; + new_csid_dev->ctrl_reg->csid_lane_assign = + csid_lane_assign_v3_0; + new_csid_dev->hw_dts_version = CSID_VERSION_V40; + } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node, + "qcom,csid-v3.1")) { + new_csid_dev->ctrl_reg->csid_reg = csid_v3_1; + new_csid_dev->ctrl_reg->csid_lane_assign = + csid_lane_assign_v3_1; + new_csid_dev->hw_dts_version = CSID_VERSION_V31; + } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node, + "qcom,csid-v3.2")) { + new_csid_dev->ctrl_reg->csid_reg = csid_v3_2; + new_csid_dev->ctrl_reg->csid_lane_assign = + csid_lane_assign_v3_2; + new_csid_dev->hw_dts_version = CSID_VERSION_V32; + } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node, + "qcom,csid-v3.4.1")) { + new_csid_dev->ctrl_reg->csid_reg = csid_v3_4_1; + new_csid_dev->hw_dts_version = CSID_VERSION_V34_1; + new_csid_dev->ctrl_reg->csid_lane_assign = + csid_lane_assign_v3_4_1; + } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node, + "qcom,csid-v3.4.2")) { + new_csid_dev->ctrl_reg->csid_reg = csid_v3_4_2; + new_csid_dev->hw_dts_version = CSID_VERSION_V34_2; + new_csid_dev->ctrl_reg->csid_lane_assign = + csid_lane_assign_v3_4_2; + } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node, + "qcom,csid-v3.4.3")) { + new_csid_dev->ctrl_reg->csid_reg = csid_v3_4_3; + new_csid_dev->hw_dts_version = CSID_VERSION_V34_3; + new_csid_dev->ctrl_reg->csid_lane_assign = + csid_lane_assign_v3_4_3; + } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node, + "qcom,csid-v3.6.0")) { + new_csid_dev->ctrl_reg->csid_reg = csid_v3_6_0; + new_csid_dev->hw_dts_version = CSID_VERSION_V36; + new_csid_dev->ctrl_reg->csid_lane_assign = + csid_lane_assign_v3_6_0; + } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node, + "qcom,csid-v3.5")) { + new_csid_dev->ctrl_reg->csid_reg = csid_v3_5; + new_csid_dev->ctrl_reg->csid_lane_assign = + csid_lane_assign_v3_5; + new_csid_dev->hw_dts_version = CSID_VERSION_V35; + } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node, + "qcom,csid-v3.5.1")) { + new_csid_dev->ctrl_reg->csid_reg = csid_v3_5_1; + new_csid_dev->ctrl_reg->csid_lane_assign = + csid_lane_assign_v3_5_1; + new_csid_dev->hw_dts_version = CSID_VERSION_V35_1; + } else { + pr_err("%s:%d, invalid hw version : 0x%x", __func__, __LINE__, + new_csid_dev->hw_dts_version); + rc = -EINVAL; + goto csid_invalid_irq; + } + + new_csid_dev->csid_state = CSID_POWER_DOWN; + return 0; + +csid_invalid_irq: + msm_camera_put_reg_base(pdev, new_csid_dev->base, "csid", true); +csid_invalid_vreg_data: + kfree(new_csid_dev->csid_vreg); +csid_no_resource: + mutex_destroy(&new_csid_dev->mutex); + kfree(new_csid_dev->ctrl_reg); + kfree(new_csid_dev); + return rc; +} + +static int msm_csid_exit(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev = platform_get_drvdata(pdev); + struct csid_device *csid_dev = + v4l2_get_subdevdata(subdev); + + msm_camera_put_clk_info(pdev, &csid_dev->csid_clk_info, + &csid_dev->csid_clk, csid_dev->num_clk); + msm_camera_put_reg_base(pdev, csid_dev->base, "csid", true); + kfree(csid_dev); + return 0; +} + +static const struct of_device_id msm_csid_dt_match[] = { + {.compatible = "qcom,csid"}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_csid_dt_match); + +static struct platform_driver csid_driver = { + .probe = csid_probe, + .remove = msm_csid_exit, + .driver = { + .name = MSM_CSID_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = msm_csid_dt_match, + }, +}; + +static int __init msm_csid_init_module(void) +{ + return platform_driver_register(&csid_driver); +} + +static void __exit msm_csid_exit_module(void) +{ + platform_driver_unregister(&csid_driver); +} + +module_init(msm_csid_init_module); +module_exit(msm_csid_exit_module); +MODULE_DESCRIPTION("MSM CSID driver"); +MODULE_LICENSE("GPL v2"); diff --git a/camera/sensor/csid/msm_csid.h b/camera/sensor/csid/msm_csid.h new file mode 100644 index 00000000..c8937b14 --- /dev/null +++ b/camera/sensor/csid/msm_csid.h @@ -0,0 +1,119 @@ +/* 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_CSID_H +#define MSM_CSID_H + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <media/v4l2-subdev.h> +#include <lenovo_media/msm_cam_sensor.h> +#include "msm_sd.h" +#include "cam_soc_api.h" + +enum csiphy_lane_assign { + PHY_LANE_D0, + PHY_LANE_CLK, + PHY_LANE_D1, + PHY_LANE_D2, + PHY_LANE_D3, + PHY_LANE_MAX, +}; + +struct csid_reg_parms_t { +/* MIPI CSID registers */ + uint32_t csid_hw_version_addr; + uint32_t csid_core_ctrl_0_addr; + uint32_t csid_core_ctrl_1_addr; + uint32_t csid_rst_cmd_addr; + uint32_t csid_cid_lut_vc_0_addr; + uint32_t csid_cid_lut_vc_1_addr; + uint32_t csid_cid_lut_vc_2_addr; + uint32_t csid_cid_lut_vc_3_addr; + uint32_t csid_cid_n_cfg_addr; + uint32_t csid_irq_clear_cmd_addr; + uint32_t csid_irq_mask_addr; + uint32_t csid_irq_status_addr; + uint32_t csid_captured_unmapped_long_pkt_hdr_addr; + uint32_t csid_captured_mmaped_long_pkt_hdr_addr; + uint32_t csid_captured_short_pkt_addr; + uint32_t csid_captured_long_pkt_hdr_addr; + uint32_t csid_captured_long_pkt_ftr_addr; + uint32_t csid_pif_misr_dl0_addr; + uint32_t csid_pif_misr_dl1_addr; + uint32_t csid_pif_misr_dl2_addr; + uint32_t csid_pif_misr_dl3_addr; + uint32_t csid_stats_total_pkts_rcvd_addr; + uint32_t csid_stats_ecc_addr; + uint32_t csid_stats_crc_addr; + uint32_t csid_tg_ctrl_addr; + uint32_t csid_tg_vc_cfg_addr; + uint32_t csid_tg_dt_n_cfg_0_addr; + uint32_t csid_tg_dt_n_cfg_1_addr; + uint32_t csid_tg_dt_n_cfg_2_addr; + uint32_t csid_rst_done_irq_bitshift; + uint32_t csid_rst_stb_all; + uint32_t csid_dl_input_sel_shift; + uint32_t csid_phy_sel_shift; + uint32_t csid_version; + uint32_t csid_3p_ctrl_0_addr; + uint32_t csid_3p_pkt_hdr_addr; + uint32_t csid_test_bus_ctrl; + uint32_t csid_irq_mask_val; + uint32_t csid_err_lane_overflow_offset_2p; + uint32_t csid_err_lane_overflow_offset_3p; + uint32_t csid_phy_sel_shift_3p; +}; + +struct csid_ctrl_t { + struct csid_reg_parms_t csid_reg; + uint8_t *csid_lane_assign; +}; + +enum msm_csid_state_t { + CSID_POWER_UP, + CSID_POWER_DOWN, +}; + +struct csid_device { + struct platform_device *pdev; + struct msm_sd_subdev msm_sd; + struct resource *irq; + struct regulator *csi_vdd; + void __iomem *base; + struct mutex mutex; + struct completion reset_complete; + uint32_t hw_version; + uint32_t hw_dts_version; + enum msm_csid_state_t csid_state; + struct csid_ctrl_t *ctrl_reg; + struct regulator *reg_ptr; + size_t num_clk; + struct clk **csid_clk; + struct msm_cam_clk_info *csid_clk_info; + uint32_t csid_clk_index; + uint32_t csid_max_clk; + uint32_t csid_3p_enabled; + struct camera_vreg_t *csid_vreg; + struct regulator *csid_reg_ptr[MAX_REGULATOR]; + int32_t regulator_count; + uint8_t is_testmode; + struct msm_camera_csid_testmode_parms testmode_params; + struct msm_camera_csid_params current_csid_params; + uint32_t csid_sof_debug; + uint32_t csid_lane_cnt; +}; + +#define VIDIOC_MSM_CSID_RELEASE \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 12, struct v4l2_subdev*) +#endif diff --git a/camera/sensor/csiphy/Makefile b/camera/sensor/csiphy/Makefile new file mode 100644 index 00000000..9ddc89ff --- /dev/null +++ b/camera/sensor/csiphy/Makefile @@ -0,0 +1,4 @@ +ccflags-y += -Icamera +ccflags-y += -Icamera/common +ccflags-y += -Icamera/sensor/io +obj-$(CONFIG_MSM_CSIPHY) += msm_csiphy.o diff --git a/camera/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h b/camera/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h new file mode 100644 index 00000000..3b9213c4 --- /dev/null +++ b/camera/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h @@ -0,0 +1,46 @@ +/* Copyright (c) 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_CSIPHY_2_0_HWREG_H +#define MSM_CSIPHY_2_0_HWREG_H + +#include <sensor/csiphy/msm_csiphy.h> + +struct csiphy_reg_parms_t csiphy_v2_0 = { + /*MIPI CSI PHY registers*/ + 0x17C, + 0x0, + 0x4, + 0x8, + 0xC, + 0x10, + 0x100, + 0x104, + 0x108, + 0x10C, + 0x110, + 0x128, + 0x140, + 0x144, + 0x164, + 0x180, + 0x1A0, + 0x6F, + 0x1A4, + 0x1C0, + 0x1C4, + 0x4, + 0x1E0, + 0x1E8, + 0x0, +}; +#endif diff --git a/camera/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h b/camera/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h new file mode 100644 index 00000000..7ed88c56 --- /dev/null +++ b/camera/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h @@ -0,0 +1,46 @@ +/* Copyright (c) 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_CSIPHY_2_2_HWREG_H +#define MSM_CSIPHY_2_2_HWREG_H + +#include <sensor/csiphy/msm_csiphy.h> + +struct csiphy_reg_parms_t csiphy_v2_2 = { + /*MIPI CSI PHY registers*/ + 0x17C, + 0x0, + 0x4, + 0x8, + 0xC, + 0x10, + 0x100, + 0x104, + 0x108, + 0x10C, + 0x110, + 0x128, + 0x140, + 0x144, + 0x164, + 0x180, + 0x1A0, + 0x6F, + 0x1A4, + 0x1C0, + 0x1C4, + 0x4, + 0x1E0, + 0x1E8, + 0x1, +}; +#endif diff --git a/camera/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h b/camera/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h new file mode 100644 index 00000000..238fef0f --- /dev/null +++ b/camera/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h @@ -0,0 +1,46 @@ +/* Copyright (c) 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_CSIPHY_3_0_HWREG_H +#define MSM_CSIPHY_3_0_HWREG_H + +#include <sensor/csiphy/msm_csiphy.h> + +struct csiphy_reg_parms_t csiphy_v3_0 = { + /*MIPI CSI PHY registers*/ + 0x0, + 0x4, + 0x8, + 0xC, + 0x10, + 0x100, + 0x104, + 0x108, + 0x10C, + 0x110, + 0x128, + 0x140, + 0x144, + 0x164, + 0x188, + 0x18C, + 0x1AC, + 0x3F, + 0x1AC, + 0x1CC, + 0x1CC, + 0x4, + 0x1EC, + 0x1F4, + 0x10, +}; +#endif diff --git a/camera/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h b/camera/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h new file mode 100644 index 00000000..b9c3c28a --- /dev/null +++ b/camera/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h @@ -0,0 +1,46 @@ +/* Copyright (c) 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_CSIPHY_3_1_HWREG_H +#define MSM_CSIPHY_3_1_HWREG_H + +#include <sensor/csiphy/msm_csiphy.h> + +struct csiphy_reg_parms_t csiphy_v3_1 = { + /*MIPI CSI PHY registers*/ + 0x0, + 0x4, + 0x8, + 0xC, + 0x10, + 0x100, + 0x104, + 0x108, + 0x10C, + 0x1C, + 0x28, + 0x140, + 0x144, + 0x164, + 0x188, + 0x18C, + 0x1AC, + 0x3F, + 0x1AC, + 0x1CC, + 0x1CC, + 0x4, + 0x1EC, + 0x1F4, + 0x31, +}; +#endif diff --git a/camera/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h b/camera/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h new file mode 100644 index 00000000..77129f08 --- /dev/null +++ b/camera/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h @@ -0,0 +1,46 @@ +/* Copyright (c) 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_CSIPHY_3_2_HWREG_H +#define MSM_CSIPHY_3_2_HWREG_H + +#include <sensor/csiphy/msm_csiphy.h> + +struct csiphy_reg_parms_t csiphy_v3_2 = { + /*MIPI CSI PHY registers*/ + 0x0, + 0x4, + 0x8, + 0xC, + 0x10, + 0x100, + 0x104, + 0x108, + 0x10C, + 0x110, + 0x128, + 0x140, + 0x144, + 0x164, + 0x188, + 0x18C, + 0x1AC, + 0x3F, + 0x1AC, + 0x1CC, + 0x1CC, + 0x4, + 0x1EC, + 0x1F4, + 0x32, +}; +#endif diff --git a/camera/sensor/csiphy/include/msm_csiphy_3_4_2_1_hwreg.h b/camera/sensor/csiphy/include/msm_csiphy_3_4_2_1_hwreg.h new file mode 100644 index 00000000..55f252cd --- /dev/null +++ b/camera/sensor/csiphy/include/msm_csiphy_3_4_2_1_hwreg.h @@ -0,0 +1,95 @@ +/* Copyright (c) 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_CSIPHY_3_4_2_1_HWREG_H +#define MSM_CSIPHY_3_4_2_1_HWREG_H + +#define ULPM_WAKE_UP_TIMER_MODE 2 +#define GLITCH_ELIMINATION_NUM 0x12 /* bit [6:4] */ + +#include <sensor/csiphy/msm_csiphy.h> + +struct csiphy_reg_parms_t csiphy_v3_4_2_1 = { + .mipi_csiphy_interrupt_status0_addr = 0x8B0, + .mipi_csiphy_interrupt_clear0_addr = 0x858, + .mipi_csiphy_glbl_irq_cmd_addr = 0x828, + .combo_clk_mask = 0x10, +}; + +struct csiphy_reg_3ph_parms_t csiphy_v3_4_2_1_3ph = { + /*MIPI CSI PHY registers*/ + {0x814, 0x0}, + {0x818, 0x1}, + {0x188, 0x7F}, + {0x18C, 0x7F}, + {0x190, 0x0}, + {0x104, 0x6}, + {0x108, 0x0}, + {0x10c, 0x0}, + {0x114, 0x20}, + {0x118, 0x3E}, + {0x11c, 0x41}, + {0x120, 0x41}, + {0x124, 0x7F}, + {0x128, 0x0}, + {0x12c, 0x0}, + {0x130, 0x1}, + {0x134, 0x0}, + {0x138, 0x0}, + {0x13C, 0x10}, + {0x140, 0x1}, + {0x144, GLITCH_ELIMINATION_NUM}, + {0x148, 0xFE}, + {0x14C, 0x1}, + {0x154, 0x0}, + {0x15C, 0x33}, + {0x160, ULPM_WAKE_UP_TIMER_MODE}, + {0x164, 0x48}, + {0x168, 0xA0}, + {0x16C, 0x17}, + {0x170, 0x41}, + {0x174, 0x41}, + {0x178, 0x3E}, + {0x17C, 0x0}, + {0x180, 0x0}, + {0x184, 0x7F}, + {0x1cc, 0x10}, + {0x81c, 0x6}, + {0x82c, 0xFF}, + {0x830, 0xFF}, + {0x834, 0xFB}, + {0x838, 0xFF}, + {0x83c, 0x7F}, + {0x840, 0xFF}, + {0x844, 0xFF}, + {0x848, 0xEF}, + {0x84c, 0xFF}, + {0x850, 0xFF}, + {0x854, 0xFF}, + {0x28, 0x0}, + {0x800, 0x2}, + {0x0, 0x88}, + {0x4, 0x8}, + {0x8, 0x0}, + {0xC, 0xFF}, + {0x10, 0x56}, + {0x2C, 0x1}, + {0x30, 0x0}, + {0x34, 0x3}, + {0x38, 0xfe}, + {0x3C, 0xB8}, + {0x1C, 0xE7}, + {0x14, 0x0}, + {0x14, 0x60}, + {0x700, 0x80} +}; +#endif diff --git a/camera/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h b/camera/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h new file mode 100644 index 00000000..aa8a6b9c --- /dev/null +++ b/camera/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h @@ -0,0 +1,95 @@ +/* Copyright (c) 2015, 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_CSIPHY_3_4_2_HWREG_H +#define MSM_CSIPHY_3_4_2_HWREG_H + +#define ULPM_WAKE_UP_TIMER_MODE 2 +#define GLITCH_ELIMINATION_NUM 0x12 /* bit [6:4] */ + +#include <sensor/csiphy/msm_csiphy.h> + +struct csiphy_reg_parms_t csiphy_v3_4_2 = { + .mipi_csiphy_interrupt_status0_addr = 0x8B0, + .mipi_csiphy_interrupt_clear0_addr = 0x858, + .mipi_csiphy_glbl_irq_cmd_addr = 0x828, + .combo_clk_mask = 0x10, +}; + +struct csiphy_reg_3ph_parms_t csiphy_v3_4_2_3ph = { + /*MIPI CSI PHY registers*/ + {0x814, 0x0}, + {0x818, 0x1}, + {0x188, 0x7F}, + {0x18C, 0x7F}, + {0x190, 0x0}, + {0x104, 0x6}, + {0x108, 0x0}, + {0x10c, 0x0}, + {0x114, 0x20}, + {0x118, 0x3E}, + {0x11c, 0x41}, + {0x120, 0x41}, + {0x124, 0x7F}, + {0x128, 0x0}, + {0x12c, 0x0}, + {0x130, 0x1}, + {0x134, 0x0}, + {0x138, 0x0}, + {0x13C, 0x10}, + {0x140, 0x1}, + {0x144, GLITCH_ELIMINATION_NUM}, + {0x148, 0xFE}, + {0x14C, 0x1}, + {0x154, 0x0}, + {0x15C, 0x33}, + {0x160, ULPM_WAKE_UP_TIMER_MODE}, + {0x164, 0x48}, + {0x168, 0xA0}, + {0x16C, 0x17}, + {0x170, 0x41}, + {0x174, 0x41}, + {0x178, 0x3E}, + {0x17C, 0x0}, + {0x180, 0x0}, + {0x184, 0x7F}, + {0x1cc, 0x10}, + {0x81c, 0x6}, + {0x82c, 0xFF}, + {0x830, 0xFF}, + {0x834, 0xFB}, + {0x838, 0xFF}, + {0x83c, 0x7F}, + {0x840, 0xFF}, + {0x844, 0xFF}, + {0x848, 0xEF}, + {0x84c, 0xFF}, + {0x850, 0xFF}, + {0x854, 0xFF}, + {0x28, 0x0}, + {0x800, 0x2}, + {0x0, 0x8E}, + {0x4, 0x8}, + {0x8, 0x0}, + {0xC, 0xFF}, + {0x10, 0x56}, + {0x2C, 0x1}, + {0x30, 0x0}, + {0x34, 0x3}, + {0x38, 0xfe}, + {0x3C, 0xB8}, + {0x1C, 0xE7}, + {0x14, 0x0}, + {0x14, 0x60}, + {0x700, 0x80} +}; +#endif diff --git a/camera/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h b/camera/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h new file mode 100644 index 00000000..98203066 --- /dev/null +++ b/camera/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h @@ -0,0 +1,95 @@ +/* Copyright (c) 2015-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_CSIPHY_3_5_HWREG_H +#define MSM_CSIPHY_3_5_HWREG_H + +#define ULPM_WAKE_UP_TIMER_MODE 2 +#define GLITCH_ELIMINATION_NUM 0x12 /* bit [6:4] */ + +#include <sensor/csiphy/msm_csiphy.h> + +struct csiphy_reg_parms_t csiphy_v3_5 = { + .mipi_csiphy_interrupt_status0_addr = 0x8B0, + .mipi_csiphy_interrupt_clear0_addr = 0x858, + .mipi_csiphy_glbl_irq_cmd_addr = 0x828, + .combo_clk_mask = 0x10, +}; + +struct csiphy_reg_3ph_parms_t csiphy_v3_5_3ph = { + /*MIPI CSI PHY registers*/ + {0x814, 0x0}, + {0x818, 0x1}, + {0x188, 0x7F}, + {0x18C, 0x7F}, + {0x190, 0x0}, + {0x104, 0x6}, + {0x108, 0x0}, + {0x10c, 0x0}, + {0x114, 0x20}, + {0x118, 0x3E}, + {0x11c, 0x41}, + {0x120, 0x41}, + {0x124, 0x7F}, + {0x128, 0x0}, + {0x12c, 0x0}, + {0x130, 0x1}, + {0x134, 0x0}, + {0x138, 0x0}, + {0x13C, 0x10}, + {0x140, 0x1}, + {0x144, GLITCH_ELIMINATION_NUM}, + {0x148, 0xFE}, + {0x14C, 0x1}, + {0x154, 0x0}, + {0x15C, 0x33}, + {0x160, ULPM_WAKE_UP_TIMER_MODE}, + {0x164, 0x48}, + {0x168, 0xA0}, + {0x16C, 0x17}, + {0x170, 0x41}, + {0x174, 0x41}, + {0x178, 0x3E}, + {0x17C, 0x0}, + {0x180, 0x0}, + {0x184, 0x7F}, + {0x1cc, 0x10}, + {0x81c, 0x6}, + {0x82c, 0xFF}, + {0x830, 0xFF}, + {0x834, 0xFB}, + {0x838, 0xFF}, + {0x83c, 0x7F}, + {0x840, 0xFF}, + {0x844, 0xFF}, + {0x848, 0xEF}, + {0x84c, 0xFF}, + {0x850, 0xFF}, + {0x854, 0xFF}, + {0x28, 0x0}, + {0x800, 0x0}, + {0x0, 0xD7}, + {0x4, 0x8}, + {0x8, 0x0}, + {0xC, 0xA5}, + {0x10, 0x52}, + {0x2C, 0x1}, + {0x30, 0x2}, + {0x34, 0x3}, + {0x38, 0x1}, + {0x3C, 0xB8}, + {0x1C, 0xA}, + {0x14, 0x0}, + {0x0, 0x0}, + {0x700, 0xC0}, +}; +#endif diff --git a/camera/sensor/csiphy/msm_csiphy.c b/camera/sensor/csiphy/msm_csiphy.c new file mode 100644 index 00000000..92df81a4 --- /dev/null +++ b/camera/sensor/csiphy/msm_csiphy.c @@ -0,0 +1,1520 @@ +/* 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 <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/module.h> +#include <linux/irqreturn.h> +#include "msm_csiphy.h" +#include "msm_sd.h" +#include "include/msm_csiphy_2_0_hwreg.h" +#include "include/msm_csiphy_2_2_hwreg.h" +#include "include/msm_csiphy_3_0_hwreg.h" +#include "include/msm_csiphy_3_1_hwreg.h" +#include "include/msm_csiphy_3_2_hwreg.h" +#include "include/msm_csiphy_3_4_2_hwreg.h" +#include "include/msm_csiphy_3_4_2_1_hwreg.h" +#include "include/msm_csiphy_3_5_hwreg.h" +#include "cam_hw_ops.h" + +#define DBG_CSIPHY 0 +#define SOF_DEBUG_ENABLE 1 +#define SOF_DEBUG_DISABLE 0 + +#define V4L2_IDENT_CSIPHY 50003 +#define CSIPHY_VERSION_V22 0x01 +#define CSIPHY_VERSION_V20 0x00 +#define CSIPHY_VERSION_V30 0x10 +#define CSIPHY_VERSION_V31 0x31 +#define CSIPHY_VERSION_V32 0x32 +#define CSIPHY_VERSION_V342 0x342 +#define CSIPHY_VERSION_V342_1 0x3421 +#define CSIPHY_VERSION_V35 0x35 +#define MSM_CSIPHY_DRV_NAME "msm_csiphy" +#define CLK_LANE_OFFSET 1 +#define NUM_LANES_OFFSET 4 + +#define CSI_3PHASE_HW 1 +#define MAX_LANES 4 +#define CLOCK_OFFSET 0x700 +#define CSIPHY_SOF_DEBUG_COUNT 2 + +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +static struct v4l2_file_operations msm_csiphy_v4l2_subdev_fops; + +static void msm_csiphy_cphy_irq_config( + struct csiphy_device *csiphy_dev, + struct msm_camera_csiphy_params *csiphy_params) +{ + void __iomem *csiphybase; + + csiphybase = csiphy_dev->base; + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl11.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl11.addr); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl12.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl12.addr); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl13.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl13.addr); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl14.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl14.addr); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl15.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl15.addr); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl16.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl16.addr); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl17.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl17.addr); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl18.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl18.addr); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl19.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl19.addr); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl20.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl20.addr); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl21.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl21.addr); +} + +static int msm_csiphy_3phase_lane_config( + struct csiphy_device *csiphy_dev, + struct msm_camera_csiphy_params *csiphy_params) +{ + uint8_t i = 0; + uint16_t lane_mask = 0, lane_enable = 0, temp; + void __iomem *csiphybase; + + csiphybase = csiphy_dev->base; + lane_mask = csiphy_params->lane_mask & 0x7; + while (lane_mask != 0) { + temp = (i << 1)+1; + lane_enable |= ((lane_mask & 0x1) << temp); + lane_mask >>= 1; + i++; + } + msm_camera_io_w(lane_enable, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl5.addr); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl6.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl6.addr); + lane_mask = csiphy_params->lane_mask & 0x7; + i = 0; + while (lane_mask & 0x7) { + if (!(lane_mask & 0x1)) { + i++; + lane_mask >>= 1; + continue; + } + + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl21.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl21.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl23.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl23.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl26.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl26.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl27.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl27.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl1.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl1.addr + 0x200*i); + msm_camera_io_w(((csiphy_params->settle_cnt >> 8) & 0xff), + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl2.addr + 0x200*i); + msm_camera_io_w((csiphy_params->settle_cnt & 0xff), + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl3.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl5.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl5.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl6.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl6.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl7.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl7.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl8.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl8.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl9.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl9.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl10.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl10.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl11.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl11.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl12.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl12.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl15.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl15.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl16.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl16.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl17.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl17.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl18.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl18.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl19.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl19.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl23.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl23.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl24.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl24.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl28.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl28.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl29.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl29.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl30.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl30.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl33.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl33.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl34.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl34.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl35.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl35.addr + 0x200*i); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl36.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl36.addr + 0x200*i); + + if (ULPM_WAKE_UP_TIMER_MODE == 0x22) { + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl51.data, + csiphybase + csiphy_dev->ctrl_reg-> + csiphy_3ph_reg.mipi_csiphy_3ph_lnn_ctrl51.addr + + 0x200*i); + } + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl25.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl25.addr + 0x200*i); + + lane_mask >>= 1; + i++; + } + if (csiphy_params->combo_mode == 1) { + msm_camera_io_w(0x2, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl7.addr); + } else { + msm_camera_io_w(0x6, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl7.addr); + } + /* Delay for stabilizing the regulator*/ + usleep_range(10, 15); + msm_csiphy_cphy_irq_config(csiphy_dev, csiphy_params); + return 0; +} + +static int msm_csiphy_2phase_lane_config( + struct csiphy_device *csiphy_dev, + struct msm_camera_csiphy_params *csiphy_params) +{ + uint32_t val = 0, lane_enable = 0, clk_lane, mask = 1; + uint16_t lane_mask = 0, i = 0, offset; + void __iomem *csiphybase; + + csiphybase = csiphy_dev->base; + lane_mask = csiphy_params->lane_mask & 0x1f; + for (i = 0; i < MAX_LANES; i++) { + if (mask == 0x2) { + if (lane_mask & mask) + lane_enable |= 0x80; + i--; + } else if (lane_mask & mask) + lane_enable |= 0x1 << (i<<1); + mask <<= 1; + } + CDBG("%s:%d lane_enable: %d\n", __func__, __LINE__, lane_enable); + + msm_camera_io_w(lane_enable, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl5.addr); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl6.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl6.addr); + + for (i = 0, mask = 0x1; i < MAX_LANES; i++) { + if (!(lane_mask & mask)) { + if (mask == 0x2) + i--; + mask <<= 0x1; + continue; + } + if (mask == 0x2) { + val = 4; + offset = CLOCK_OFFSET; + clk_lane = 1; + i--; + } else { + offset = 0x200*i; + val = 0; + clk_lane = 0; + } + + if (csiphy_params->combo_mode == 1) { + val |= 0xA; + if (mask == csiphy_dev->ctrl_reg-> + csiphy_reg.combo_clk_mask) { + val |= 0x4; + clk_lane = 1; + } + } + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg7.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg7.addr + offset); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg6.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg6.addr + offset); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg8.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg8.addr + offset); + msm_camera_io_w(val, csiphybase + + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_misc1.addr + offset); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_ctrl15.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_ctrl15.addr + offset); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg2.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg2.addr + offset); + + msm_camera_io_w((csiphy_params->settle_cnt & 0xFF), + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg3.addr + offset); + + if (clk_lane == 1) { + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnck_cfg1.data, csiphybase + + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnck_cfg1.addr); + + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg4.data, csiphybase + + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg4.addr + offset); + } else { + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg1.data, + csiphybase + + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg1.addr + offset); + } + if ((csiphy_dev->hw_version == CSIPHY_VERSION_V342 || + csiphy_dev->hw_version == CSIPHY_VERSION_V342_1) && + csiphy_params->combo_mode == 1) { + msm_camera_io_w(0x52, + csiphybase + + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg5.addr + offset); + } else { + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg5.data, + csiphybase + + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg5.addr + offset); + } + if (clk_lane == 1 && + (csiphy_dev->hw_version == CSIPHY_VERSION_V342 || + csiphy_dev->hw_version == CSIPHY_VERSION_V342_1)) { + msm_camera_io_w(0x1f, + csiphybase + + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg9.addr + offset); + } else { + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg9.data, + csiphybase + + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_cfg9.addr + offset); + } + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_test_imp.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_test_imp.addr + offset); + if ((csiphy_dev->hw_version == CSIPHY_VERSION_V342 || + csiphy_dev->hw_version == CSIPHY_VERSION_V342_1)) { + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_ctrl5.data, + csiphybase + + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_ctrl5.addr + offset); + } + mask <<= 1; + } + if ((csiphy_dev->hw_version == CSIPHY_VERSION_V342 || + csiphy_dev->hw_version == CSIPHY_VERSION_V342_1) && + csiphy_params->combo_mode != 1) { + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl0.data, + csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl0.addr); + } + msm_csiphy_cphy_irq_config(csiphy_dev, csiphy_params); + return 0; +} + +static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev, + struct msm_camera_csiphy_params *csiphy_params) +{ + int rc = 0; + int j = 0, curr_lane = 0; + uint32_t val = 0; + long clk_rate = 0; + uint8_t lane_cnt = 0; + uint16_t lane_mask = 0; + void __iomem *csiphybase; + uint8_t csiphy_id = csiphy_dev->pdev->id; + int32_t lane_val = 0, lane_right = 0, num_lanes = 0; + int ratio = 1; + + csiphybase = csiphy_dev->base; + if (!csiphybase) { + pr_err("%s: csiphybase NULL\n", __func__); + return -EINVAL; + } + + csiphy_dev->lane_mask[csiphy_id] |= csiphy_params->lane_mask; + lane_mask = csiphy_dev->lane_mask[csiphy_id]; + lane_cnt = csiphy_params->lane_cnt; + if (csiphy_params->lane_cnt < 1 || csiphy_params->lane_cnt > 4) { + pr_err("%s: unsupported lane cnt %d\n", + __func__, csiphy_params->lane_cnt); + return rc; + } + + clk_rate = (csiphy_params->csiphy_clk > 0) + ? csiphy_params->csiphy_clk : + csiphy_dev->csiphy_max_clk; + clk_rate = msm_camera_clk_set_rate(&csiphy_dev->pdev->dev, + csiphy_dev->csiphy_clk[csiphy_dev->csiphy_clk_index], + clk_rate); + if (clk_rate < 0) { + pr_err("csiphy_clk_set_rate failed\n"); + return -EINVAL; + } + + if (clk_rate < csiphy_dev->csiphy_max_clk && + clk_rate > 0) { + ratio = csiphy_dev->csiphy_max_clk/clk_rate; + csiphy_params->settle_cnt = csiphy_params->settle_cnt/ratio; + } + CDBG("%s csiphy_params, mask = 0x%x cnt = %d\n", + __func__, + csiphy_params->lane_mask, + csiphy_params->lane_cnt); + CDBG("%s csiphy_params, settle cnt = 0x%x csid %d\n", + __func__, csiphy_params->settle_cnt, + csiphy_params->csid_core); + + if (csiphy_dev->hw_version >= CSIPHY_VERSION_V30) { + val = msm_camera_io_r(csiphy_dev->clk_mux_base); + if (csiphy_params->combo_mode && + (csiphy_params->lane_mask & 0x18) == 0x18) { + val &= ~0xf0; + val |= csiphy_params->csid_core << 4; + } else { + val &= ~0xf; + val |= csiphy_params->csid_core; + } + msm_camera_io_w(val, csiphy_dev->clk_mux_base); + CDBG("%s clk mux addr %p val 0x%x\n", __func__, + csiphy_dev->clk_mux_base, val); + /* ensure write is done */ + mb(); + } + + csiphy_dev->csi_3phase = csiphy_params->csi_3phase; + if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW) { + if (csiphy_dev->csi_3phase == 1) { + rc = msm_camera_clk_enable(&csiphy_dev->pdev->dev, + csiphy_dev->csiphy_3p_clk_info, + csiphy_dev->csiphy_3p_clk, 2, true); + rc = msm_csiphy_3phase_lane_config(csiphy_dev, + csiphy_params); + csiphy_dev->num_irq_registers = 20; + } else { + rc = msm_csiphy_2phase_lane_config(csiphy_dev, + csiphy_params); + csiphy_dev->num_irq_registers = 11; + } + if (rc < 0) { + pr_err("%s:%d: Error in setting lane configuration\n", + __func__, __LINE__); + } + return rc; + } + + msm_camera_io_w(0x1, csiphybase + csiphy_dev->ctrl_reg-> + csiphy_reg.mipi_csiphy_glbl_t_init_cfg0_addr); + msm_camera_io_w(0x1, csiphybase + csiphy_dev->ctrl_reg-> + csiphy_reg.mipi_csiphy_t_wakeup_cfg0_addr); + + if (csiphy_dev->hw_version < CSIPHY_VERSION_V30) { + val = 0x3; + msm_camera_io_w((lane_mask << 2) | val, + csiphybase + + csiphy_dev->ctrl_reg-> + csiphy_reg.mipi_csiphy_glbl_pwr_cfg_addr); + msm_camera_io_w(0x10, csiphybase + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnck_cfg2_addr); + msm_camera_io_w(csiphy_params->settle_cnt, + csiphybase + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnck_cfg3_addr); + msm_camera_io_w(0x24, + csiphybase + csiphy_dev->ctrl_reg-> + csiphy_reg.mipi_csiphy_interrupt_mask0_addr); + msm_camera_io_w(0x24, + csiphybase + csiphy_dev->ctrl_reg-> + csiphy_reg.mipi_csiphy_interrupt_clear0_addr); + } else { + val = 0x1; + msm_camera_io_w((lane_mask << 1) | val, + csiphybase + + csiphy_dev->ctrl_reg-> + csiphy_reg.mipi_csiphy_glbl_pwr_cfg_addr); + msm_camera_io_w(csiphy_params->combo_mode << + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_mode_config_shift, + csiphybase + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_glbl_reset_addr); + } + + lane_mask &= 0x1f; + while (lane_mask & 0x1f) { + if (!(lane_mask & 0x1)) { + j++; + lane_mask >>= 1; + continue; + } + msm_camera_io_w(0x10, + csiphybase + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnn_cfg2_addr + 0x40*j); + msm_camera_io_w(csiphy_params->settle_cnt, + csiphybase + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnn_cfg3_addr + 0x40*j); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_interrupt_mask_val, csiphybase + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_interrupt_mask_addr + 0x4*j); + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_interrupt_mask_val, csiphybase + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_interrupt_clear_addr + 0x4*j); + if (csiphy_dev->is_3_1_20nm_hw == 1) { + if (j > CLK_LANE_OFFSET) { + lane_right = 0x8; + num_lanes = (lane_cnt - curr_lane) + << NUM_LANES_OFFSET; + if (lane_cnt < curr_lane) { + pr_err("%s: Lane_cnt is less than curr_lane number\n", + __func__); + return -EINVAL; + } + lane_val = lane_right|num_lanes; + } else if (j == 1) { + lane_val = 0x4; + } + if (csiphy_params->combo_mode == 1) { + /* + * In the case of combo mode, the clock is always + * 4th lane for the second sensor. + * So check whether the sensor is of one lane + * sensor and curr_lane for 0. + */ + if (curr_lane == 0 && + ((csiphy_params->lane_mask & + 0x18) == 0x18)) + lane_val = 0x4; + } + msm_camera_io_w(lane_val, csiphybase + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnn_misc1_addr + 0x40*j); + msm_camera_io_w(0x17, csiphybase + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnn_test_imp + 0x40*j); + curr_lane++; + } + j++; + lane_mask >>= 1; + } + return rc; +} + +void msm_csiphy_disable_irq( + struct csiphy_device *csiphy_dev) +{ + void __iomem *csiphybase; + + csiphybase = csiphy_dev->base; + msm_camera_io_w(0, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl11.addr); + msm_camera_io_w(0, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl12.addr); + msm_camera_io_w(0, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl13.addr); + msm_camera_io_w(0, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl14.addr); + msm_camera_io_w(0, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl15.addr); + msm_camera_io_w(0, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl16.addr); + msm_camera_io_w(0, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl17.addr); + msm_camera_io_w(0, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl18.addr); + msm_camera_io_w(0, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl19.addr); + msm_camera_io_w(0, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl20.addr); + msm_camera_io_w(0, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl21.addr); +} + +static irqreturn_t msm_csiphy_irq(int irq_num, void *data) +{ + uint32_t irq; + int i; + struct csiphy_device *csiphy_dev = data; + + if (csiphy_dev->csiphy_sof_debug == SOF_DEBUG_ENABLE) { + if (csiphy_dev->csiphy_sof_debug_count < CSIPHY_SOF_DEBUG_COUNT) + csiphy_dev->csiphy_sof_debug_count++; + else { + msm_csiphy_disable_irq(csiphy_dev); + return IRQ_HANDLED; + } + } + + for (i = 0; i < csiphy_dev->num_irq_registers; i++) { + irq = msm_camera_io_r( + csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_interrupt_status0_addr + 0x4*i); + msm_camera_io_w(irq, + csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_interrupt_clear0_addr + 0x4*i); + pr_err_ratelimited( + "%s CSIPHY%d_IRQ_STATUS_ADDR%d = 0x%x\n", + __func__, csiphy_dev->pdev->id, i, irq); + msm_camera_io_w(0x0, + csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_interrupt_clear0_addr + 0x4*i); + } + msm_camera_io_w(0x1, csiphy_dev->base + + csiphy_dev->ctrl_reg-> + csiphy_reg.mipi_csiphy_glbl_irq_cmd_addr); + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg-> + csiphy_reg.mipi_csiphy_glbl_irq_cmd_addr); + return IRQ_HANDLED; +} + +static void msm_csiphy_reset(struct csiphy_device *csiphy_dev) +{ + msm_camera_io_w(0x1, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg.mipi_csiphy_glbl_reset_addr); + usleep_range(5000, 8000); + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg.mipi_csiphy_glbl_reset_addr); +} + +static void msm_csiphy_3ph_reset(struct csiphy_device *csiphy_dev) +{ + msm_camera_io_w(0x1, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl0.addr); + usleep_range(5000, 8000); + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl0.addr); +} + +#if DBG_CSIPHY +static int msm_csiphy_init(struct csiphy_device *csiphy_dev) +{ + int rc = 0; + if (csiphy_dev == NULL) { + pr_err("%s: csiphy_dev NULL\n", __func__); + rc = -ENOMEM; + return rc; + } + + CDBG("%s:%d called\n", __func__, __LINE__); + if (csiphy_dev->csiphy_state == CSIPHY_POWER_UP) { + pr_err("%s: csiphy invalid state %d\n", __func__, + csiphy_dev->csiphy_state); + rc = -EINVAL; + return rc; + } + CDBG("%s:%d called\n", __func__, __LINE__); + + if (csiphy_dev->ref_count++) { + CDBG("%s csiphy refcount = %d\n", __func__, + csiphy_dev->ref_count); + return rc; + } + CDBG("%s:%d called\n", __func__, __LINE__); + + rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY, + CAM_AHB_SVS_VOTE); + if (rc < 0) { + csiphy_dev->ref_count--; + pr_err("%s: failed to vote for AHB\n", __func__); + return rc; + } + + CDBG("%s:%d called\n", __func__, __LINE__); + + rc = msm_camera_clk_enable(&csiphy_dev->pdev->dev, + csiphy_dev->csiphy_clk_info, csiphy_dev->csiphy_clk, + csiphy_dev->num_clk, true); + + CDBG("%s:%d called\n", __func__, __LINE__); + if (rc < 0) { + pr_err("%s: csiphy clk enable failed\n", __func__); + csiphy_dev->ref_count--; + goto csiphy_resource_fail; + } + CDBG("%s:%d called\n", __func__, __LINE__); + + rc = msm_camera_enable_irq(csiphy_dev->irq, true); + if (rc < 0) + pr_err("%s: irq enable failed\n", __func__); + + if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW) + msm_csiphy_3ph_reset(csiphy_dev); + else + msm_csiphy_reset(csiphy_dev); + + CDBG("%s:%d called\n", __func__, __LINE__); + + if (csiphy_dev->hw_dts_version == CSIPHY_VERSION_V30) + csiphy_dev->hw_version = + msm_camera_io_r(csiphy_dev->base + + csiphy_dev->ctrl_reg-> + csiphy_reg.mipi_csiphy_hw_version_addr); + else + csiphy_dev->hw_version = csiphy_dev->hw_dts_version; + + CDBG("%s:%d called csiphy_dev->hw_version 0x%x\n", __func__, __LINE__, + csiphy_dev->hw_version); + csiphy_dev->csiphy_state = CSIPHY_POWER_UP; + return 0; + +csiphy_resource_fail: + if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY, + CAM_AHB_SUSPEND_VOTE) < 0) + pr_err("%s: failed to vote for AHB\n", __func__); + return rc; +} +#else +static int msm_csiphy_init(struct csiphy_device *csiphy_dev) +{ + int rc = 0; + if (csiphy_dev == NULL) { + pr_err("%s: csiphy_dev NULL\n", __func__); + rc = -ENOMEM; + return rc; + } + csiphy_dev->csiphy_sof_debug_count = 0; + CDBG("%s:%d called\n", __func__, __LINE__); + if (csiphy_dev->csiphy_state == CSIPHY_POWER_UP) { + pr_err("%s: csiphy invalid state %d\n", __func__, + csiphy_dev->csiphy_state); + rc = -EINVAL; + return rc; + } + CDBG("%s:%d called\n", __func__, __LINE__); + + if (csiphy_dev->ref_count++) { + CDBG("%s csiphy refcount = %d\n", __func__, + csiphy_dev->ref_count); + return rc; + } + CDBG("%s:%d called\n", __func__, __LINE__); + rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY, + CAM_AHB_SVS_VOTE); + if (rc < 0) { + csiphy_dev->ref_count--; + pr_err("%s: failed to vote for AHB\n", __func__); + return rc; + } + + rc = msm_camera_clk_enable(&csiphy_dev->pdev->dev, + csiphy_dev->csiphy_clk_info, csiphy_dev->csiphy_clk, + csiphy_dev->num_clk, true); + + CDBG("%s:%d called\n", __func__, __LINE__); + if (rc < 0) { + pr_err("%s: csiphy clk enable failed\n", __func__); + csiphy_dev->ref_count--; + goto csiphy_resource_fail; + } + CDBG("%s:%d called\n", __func__, __LINE__); + + if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW) + msm_csiphy_3ph_reset(csiphy_dev); + else + msm_csiphy_reset(csiphy_dev); + + CDBG("%s:%d called\n", __func__, __LINE__); + + if (csiphy_dev->hw_dts_version == CSIPHY_VERSION_V30) + csiphy_dev->hw_version = + msm_camera_io_r(csiphy_dev->base + + csiphy_dev->ctrl_reg-> + csiphy_reg.mipi_csiphy_hw_version_addr); + else + csiphy_dev->hw_version = csiphy_dev->hw_dts_version; + + csiphy_dev->csiphy_sof_debug = SOF_DEBUG_DISABLE; + CDBG("%s:%d called csiphy_dev->hw_version 0x%x\n", __func__, __LINE__, + csiphy_dev->hw_version); + csiphy_dev->csiphy_state = CSIPHY_POWER_UP; + return 0; + +csiphy_resource_fail: + if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY, + CAM_AHB_SUSPEND_VOTE) < 0) + pr_err("%s: failed to vote for AHB\n", __func__); + return rc; +} +#endif + +#if DBG_CSIPHY +static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg) +{ + int i = 0; + int rc = 0; + struct msm_camera_csi_lane_params *csi_lane_params; + uint16_t csi_lane_mask; + csi_lane_params = (struct msm_camera_csi_lane_params *)arg; + + if (!csiphy_dev || !csiphy_dev->ref_count) { + pr_err("%s csiphy dev NULL / ref_count ZERO\n", __func__); + return 0; + } + + if (csiphy_dev->csiphy_state != CSIPHY_POWER_UP) { + pr_err("%s: csiphy invalid state %d\n", __func__, + csiphy_dev->csiphy_state); + return -EINVAL; + } + + if (--csiphy_dev->ref_count) { + CDBG("%s csiphy refcount = %d\n", __func__, + csiphy_dev->ref_count); + return 0; + } + + if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW) { + msm_camera_io_w(0x0, + csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl5.addr); + msm_camera_io_w(0x0, + csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl6.addr); + } else if (csiphy_dev->hw_version < CSIPHY_VERSION_V30) { + csiphy_dev->lane_mask[csiphy_dev->pdev->id] = 0; + for (i = 0; i < 4; i++) + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnn_cfg2_addr + 0x40*i); + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnck_cfg2_addr); + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_glbl_pwr_cfg_addr); + } else { + if (!csi_lane_params) { + pr_err("%s:%d failed: csi_lane_params %p\n", __func__, + __LINE__, csi_lane_params); + return -EINVAL; + } + csi_lane_mask = (csi_lane_params->csi_lane_mask & 0x1F); + + CDBG("%s csiphy_params, lane assign 0x%x mask = 0x%x\n", + __func__, + csi_lane_params->csi_lane_assign, + csi_lane_params->csi_lane_mask); + + if (!csi_lane_mask) + csi_lane_mask = 0x1f; + + csiphy_dev->lane_mask[csiphy_dev->pdev->id] &= + ~(csi_lane_mask); + i = 0; + while (csi_lane_mask) { + if (csi_lane_mask & 0x1) { + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnn_cfg2_addr + 0x40*i); + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnn_misc1_addr + 0x40*i); + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnn_test_imp + 0x40*i); + } + csi_lane_mask >>= 1; + i++; + } + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnck_cfg2_addr); + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_glbl_pwr_cfg_addr); + } + + rc = msm_camera_enable_irq(csiphy_dev->irq, false); + + msm_camera_clk_enable(&csiphy_dev->pdev->dev, + csiphy_dev->csiphy_clk_info, csiphy_dev->csiphy_clk, + csiphy_dev->num_clk, false); + + if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW && + csiphy_dev->csi_3phase == 1) { + msm_camera_clk_enable(&csiphy_dev->pdev->dev, + csiphy_dev->csiphy_3p_clk_info, + csiphy_dev->csiphy_3p_clk, 2, false); + } + + csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN; + + if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY, + CAM_AHB_SUSPEND_VOTE) < 0) + pr_err("%s: failed to remove vote for AHB\n", __func__); + return 0; +} +#else +static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg) +{ + int i = 0, rc = 0; + struct msm_camera_csi_lane_params *csi_lane_params; + uint16_t csi_lane_mask; + csi_lane_params = (struct msm_camera_csi_lane_params *)arg; + + if (!csiphy_dev || !csiphy_dev->ref_count) { + pr_err("%s csiphy dev NULL / ref_count ZERO\n", __func__); + return 0; + } + + if (csiphy_dev->csiphy_state != CSIPHY_POWER_UP) { + pr_err("%s: csiphy invalid state %d\n", __func__, + csiphy_dev->csiphy_state); + return -EINVAL; + } + + if (--csiphy_dev->ref_count) { + CDBG("%s csiphy refcount = %d\n", __func__, + csiphy_dev->ref_count); + return 0; + } + + if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW) { + msm_camera_io_w(0x0, + csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl5.addr); + msm_camera_io_w(0x0, + csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_cmn_ctrl6.addr); + } else if (csiphy_dev->hw_version < CSIPHY_VERSION_V30) { + csiphy_dev->lane_mask[csiphy_dev->pdev->id] = 0; + for (i = 0; i < 4; i++) + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnn_cfg2_addr + 0x40*i); + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnck_cfg2_addr); + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_glbl_pwr_cfg_addr); + } else { + if (!csi_lane_params) { + pr_err("%s:%d failed: csi_lane_params %p\n", __func__, + __LINE__, csi_lane_params); + return -EINVAL; + } + csi_lane_mask = (csi_lane_params->csi_lane_mask & 0x1F); + + CDBG("%s csiphy_params, lane assign 0x%x mask = 0x%x\n", + __func__, + csi_lane_params->csi_lane_assign, + csi_lane_params->csi_lane_mask); + + if (!csi_lane_mask) + csi_lane_mask = 0x1f; + + csiphy_dev->lane_mask[csiphy_dev->pdev->id] &= + ~(csi_lane_mask); + i = 0; + while (csi_lane_mask) { + if (csi_lane_mask & 0x1) { + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnn_cfg2_addr + 0x40*i); + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnn_misc1_addr + 0x40*i); + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnn_test_imp + 0x40*i); + } + csi_lane_mask >>= 1; + i++; + } + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_lnck_cfg2_addr); + msm_camera_io_w(0x0, csiphy_dev->base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_glbl_pwr_cfg_addr); + } + if (csiphy_dev->csiphy_sof_debug == SOF_DEBUG_ENABLE) { + rc = msm_camera_enable_irq(csiphy_dev->irq, false); + } + + msm_camera_clk_enable(&csiphy_dev->pdev->dev, + csiphy_dev->csiphy_clk_info, csiphy_dev->csiphy_clk, + csiphy_dev->num_clk, false); + if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW && + csiphy_dev->csi_3phase == 1) { + msm_camera_clk_enable(&csiphy_dev->pdev->dev, + csiphy_dev->csiphy_3p_clk_info, + csiphy_dev->csiphy_3p_clk, 2, false); + } + + csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN; + + if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY, + CAM_AHB_SUSPEND_VOTE) < 0) + pr_err("%s: failed to remove vote for AHB\n", __func__); + return 0; +} + +#endif +static int32_t msm_csiphy_cmd(struct csiphy_device *csiphy_dev, void *arg) +{ + int rc = 0; + struct csiphy_cfg_data *cdata = (struct csiphy_cfg_data *)arg; + struct msm_camera_csiphy_params csiphy_params; + struct msm_camera_csi_lane_params csi_lane_params; + if (!csiphy_dev || !cdata) { + pr_err("%s: csiphy_dev NULL\n", __func__); + return -EINVAL; + } + + switch (cdata->cfgtype) { + case CSIPHY_INIT: + rc = msm_csiphy_init(csiphy_dev); + break; + case CSIPHY_CFG: + if (copy_from_user(&csiphy_params, + (void *)cdata->cfg.csiphy_params, + sizeof(struct msm_camera_csiphy_params))) { + pr_err("%s: %d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + csiphy_dev->csiphy_sof_debug = SOF_DEBUG_DISABLE; + rc = msm_csiphy_lane_config(csiphy_dev, &csiphy_params); + break; + case CSIPHY_RELEASE: + if (copy_from_user(&csi_lane_params, + (void *)cdata->cfg.csi_lane_params, + sizeof(struct msm_camera_csi_lane_params))) { + pr_err("%s: %d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + rc = msm_csiphy_release(csiphy_dev, &csi_lane_params); + break; + default: + pr_err("%s: %d failed\n", __func__, __LINE__); + rc = -ENOIOCTLCMD; + break; + } + return rc; +} + +static int32_t msm_csiphy_get_subdev_id(struct csiphy_device *csiphy_dev, + void *arg) +{ + uint32_t *subdev_id = (uint32_t *)arg; + if (!subdev_id) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -EINVAL; + } + *subdev_id = csiphy_dev->pdev->id; + pr_debug("%s:%d subdev_id %d\n", __func__, __LINE__, *subdev_id); + return 0; +} + +static long msm_csiphy_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int rc = -ENOIOCTLCMD; + struct csiphy_device *csiphy_dev = v4l2_get_subdevdata(sd); + if (!csiphy_dev) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -EINVAL; + } + mutex_lock(&csiphy_dev->mutex); + switch (cmd) { + case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID: + rc = msm_csiphy_get_subdev_id(csiphy_dev, arg); + break; + case VIDIOC_MSM_CSIPHY_IO_CFG: + rc = msm_csiphy_cmd(csiphy_dev, arg); + break; + case VIDIOC_MSM_CSIPHY_RELEASE: + case MSM_SD_SHUTDOWN: + rc = msm_csiphy_release(csiphy_dev, arg); + break; + case MSM_SD_NOTIFY_FREEZE: + if (!csiphy_dev || !csiphy_dev->ctrl_reg || + !csiphy_dev->ref_count) + break; + if (csiphy_dev->csiphy_sof_debug == SOF_DEBUG_DISABLE) { + csiphy_dev->csiphy_sof_debug = SOF_DEBUG_ENABLE; + rc = msm_camera_enable_irq(csiphy_dev->irq, true); + } + break; + case MSM_SD_UNNOTIFY_FREEZE: + if (!csiphy_dev || !csiphy_dev->ctrl_reg || + !csiphy_dev->ref_count) + break; + csiphy_dev->csiphy_sof_debug = SOF_DEBUG_DISABLE; + rc = msm_camera_enable_irq(csiphy_dev->irq, false); + break; + default: + pr_err_ratelimited("%s: command not found\n", __func__); + } + mutex_unlock(&csiphy_dev->mutex); + CDBG("%s:%d\n", __func__, __LINE__); + return rc; +} + +#ifdef CONFIG_COMPAT +static long msm_csiphy_subdev_do_ioctl( + struct file *file, unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + struct csiphy_cfg_data32 *u32 = + (struct csiphy_cfg_data32 *)arg; + struct csiphy_cfg_data csiphy_data; + + switch (cmd) { + case VIDIOC_MSM_CSIPHY_IO_CFG32: + cmd = VIDIOC_MSM_CSIPHY_IO_CFG; + csiphy_data.cfgtype = u32->cfgtype; + csiphy_data.cfg.csiphy_params = + compat_ptr(u32->cfg.csiphy_params); + return msm_csiphy_subdev_ioctl(sd, cmd, &csiphy_data); + default: + return msm_csiphy_subdev_ioctl(sd, cmd, arg); + } +} + +static long msm_csiphy_subdev_fops_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return video_usercopy(file, cmd, arg, msm_csiphy_subdev_do_ioctl); +} +#endif + +static const struct v4l2_subdev_internal_ops msm_csiphy_internal_ops; + +static struct v4l2_subdev_core_ops msm_csiphy_subdev_core_ops = { + .ioctl = &msm_csiphy_subdev_ioctl, +}; + +static const struct v4l2_subdev_ops msm_csiphy_subdev_ops = { + .core = &msm_csiphy_subdev_core_ops, +}; + +static int msm_csiphy_get_clk_info(struct csiphy_device *csiphy_dev, + struct platform_device *pdev) +{ + int i, rc; + char *csi_3p_clk_name = "csi_phy_3p_clk"; + char *csi_3p_clk_src_name = "csiphy_3p_clk_src"; + uint32_t clk_cnt = 0; + + rc = msm_camera_get_clk_info(csiphy_dev->pdev, + &csiphy_dev->csiphy_all_clk_info, + &csiphy_dev->csiphy_all_clk, + &csiphy_dev->num_all_clk); + if (rc < 0) { + pr_err("%s:%d, failed\n", __func__, __LINE__); + return rc; + } + if (csiphy_dev->num_all_clk > CSIPHY_NUM_CLK_MAX) { + pr_err("%s: invalid count=%zu, max is %d\n", __func__, + csiphy_dev->num_all_clk, CSIPHY_NUM_CLK_MAX); + goto MAX_CLK_ERROR; + } + + for (i = 0; i < csiphy_dev->num_all_clk; i++) { + if (!strcmp(csiphy_dev->csiphy_all_clk_info[i].clk_name, + csi_3p_clk_src_name)) { + csiphy_dev->csiphy_3p_clk_info[0].clk_name = + csiphy_dev->csiphy_all_clk_info[i].clk_name; + csiphy_dev->csiphy_3p_clk_info[0].clk_rate = + csiphy_dev->csiphy_all_clk_info[i].clk_rate; + csiphy_dev->csiphy_3p_clk[0] = + csiphy_dev->csiphy_all_clk[i]; + continue; + } else if (!strcmp(csiphy_dev->csiphy_all_clk_info[i].clk_name, + csi_3p_clk_name)) { + csiphy_dev->csiphy_3p_clk_info[1].clk_name = + csiphy_dev->csiphy_all_clk_info[i].clk_name; + csiphy_dev->csiphy_3p_clk_info[1].clk_rate = + csiphy_dev->csiphy_all_clk_info[i].clk_rate; + csiphy_dev->csiphy_3p_clk[1] = + csiphy_dev->csiphy_all_clk[i]; + continue; + } + csiphy_dev->csiphy_clk_info[clk_cnt].clk_name = + csiphy_dev->csiphy_all_clk_info[i].clk_name; + csiphy_dev->csiphy_clk_info[clk_cnt].clk_rate = + csiphy_dev->csiphy_all_clk_info[i].clk_rate; + csiphy_dev->csiphy_clk[clk_cnt] = + csiphy_dev->csiphy_all_clk[clk_cnt]; + if (!strcmp(csiphy_dev->csiphy_clk_info[clk_cnt].clk_name, + "csiphy_timer_src_clk")) { + CDBG("%s:%d, copy csiphy_timer_src_clk", + __func__, __LINE__); + csiphy_dev->csiphy_max_clk = + csiphy_dev->csiphy_clk_info[clk_cnt].clk_rate; + csiphy_dev->csiphy_clk_index = clk_cnt; + } + CDBG("%s: clk_rate[%d] = %ld\n", __func__, clk_cnt, + csiphy_dev->csiphy_clk_info[clk_cnt].clk_rate); + clk_cnt++; + } + + csiphy_dev->num_clk = clk_cnt; +MAX_CLK_ERROR: + msm_camera_put_clk_info(csiphy_dev->pdev, + &csiphy_dev->csiphy_all_clk_info, + &csiphy_dev->csiphy_all_clk, + csiphy_dev->num_all_clk); + + return 0; +} + +static int csiphy_probe(struct platform_device *pdev) +{ + struct csiphy_device *new_csiphy_dev; + int rc = 0; + + new_csiphy_dev = kzalloc(sizeof(struct csiphy_device), GFP_KERNEL); + if (!new_csiphy_dev) { + pr_err("%s: no enough memory\n", __func__); + return -ENOMEM; + } + new_csiphy_dev->is_3_1_20nm_hw = 0; + new_csiphy_dev->ctrl_reg = NULL; + new_csiphy_dev->ctrl_reg = kzalloc(sizeof(struct csiphy_ctrl_t), + GFP_KERNEL); + if (!new_csiphy_dev->ctrl_reg) { + pr_err("%s:%d kzalloc failed\n", __func__, __LINE__); + return -ENOMEM; + } + v4l2_subdev_init(&new_csiphy_dev->msm_sd.sd, &msm_csiphy_subdev_ops); + v4l2_set_subdevdata(&new_csiphy_dev->msm_sd.sd, new_csiphy_dev); + platform_set_drvdata(pdev, &new_csiphy_dev->msm_sd.sd); + + mutex_init(&new_csiphy_dev->mutex); + + if (pdev->dev.of_node) { + of_property_read_u32((&pdev->dev)->of_node, + "cell-index", &pdev->id); + CDBG("%s: device id = %d\n", __func__, pdev->id); + } + + new_csiphy_dev->pdev = pdev; + new_csiphy_dev->msm_sd.sd.internal_ops = &msm_csiphy_internal_ops; + new_csiphy_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(new_csiphy_dev->msm_sd.sd.name, + ARRAY_SIZE(new_csiphy_dev->msm_sd.sd.name), "msm_csiphy"); + media_entity_init(&new_csiphy_dev->msm_sd.sd.entity, 0, NULL, 0); + new_csiphy_dev->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + new_csiphy_dev->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_CSIPHY; + new_csiphy_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x4; + msm_sd_register(&new_csiphy_dev->msm_sd); + + new_csiphy_dev->csiphy_3phase = 0; + new_csiphy_dev->num_irq_registers = 0x8; + + if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node, + "qcom,csiphy-v2.0")) { + new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v2_0; + new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V20; + } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node, + "qcom,csiphy-v2.2")) { + new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v2_2; + new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V22; + } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node, + "qcom,csiphy-v3.0")) { + new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v3_0; + new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V30; + } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node, + "qcom,csiphy-v3.1")) { + new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v3_1; + new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V31; + } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node, + "qcom,csiphy-v3.1.1")) { + new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v3_1; + new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V31; + new_csiphy_dev->is_3_1_20nm_hw = 1; + } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node, + "qcom,csiphy-v3.2")) { + new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v3_2; + new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V32; + } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node, + "qcom,csiphy-v3.4.2")) { + new_csiphy_dev->ctrl_reg->csiphy_3ph_reg = csiphy_v3_4_2_3ph; + new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v3_4_2; + new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V342; + new_csiphy_dev->csiphy_3phase = CSI_3PHASE_HW; + } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node, + "qcom,csiphy-v3.4.2.1")) { + new_csiphy_dev->ctrl_reg->csiphy_3ph_reg = csiphy_v3_4_2_1_3ph; + new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v3_4_2_1; + new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V342_1; + new_csiphy_dev->csiphy_3phase = CSI_3PHASE_HW; + } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node, + "qcom,csiphy-v3.5")) { + new_csiphy_dev->ctrl_reg->csiphy_3ph_reg = csiphy_v3_5_3ph; + new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v3_5; + new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V35; + new_csiphy_dev->csiphy_3phase = CSI_3PHASE_HW; + } else { + pr_err("%s:%d, invalid hw version : 0x%x\n", __func__, __LINE__, + new_csiphy_dev->hw_dts_version); + rc = -EINVAL; + goto csiphy_no_resource; + } + + /* ToDo: Enable 3phase clock for dynamic clock enable/disable */ + rc = msm_csiphy_get_clk_info(new_csiphy_dev, pdev); + if (rc < 0) { + pr_err("%s: msm_csiphy_get_clk_info() failed", __func__); + rc = -EFAULT; + goto csiphy_no_resource; + } + + new_csiphy_dev->base = msm_camera_get_reg_base(pdev, "csiphy", true); + if (!new_csiphy_dev->base) { + pr_err("%s: no mem resource?\n", __func__); + rc = -ENODEV; + goto csiphy_no_resource; + } + + if (new_csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V30) { + new_csiphy_dev->clk_mux_base = msm_camera_get_reg_base(pdev, + "csiphy_clk_mux", true); + if (!new_csiphy_dev->clk_mux_base) { + pr_err("%s: no mem resource?\n", __func__); + rc = -ENODEV; + goto csiphy_no_mux_resource; + } + } + new_csiphy_dev->irq = msm_camera_get_irq(pdev, "csiphy"); + if (!new_csiphy_dev->irq) { + pr_err("%s: no irq resource?\n", __func__); + rc = -ENODEV; + goto csiphy_no_irq_resource; + } + rc = msm_camera_register_irq(pdev, new_csiphy_dev->irq, + msm_csiphy_irq, IRQF_TRIGGER_RISING, "csiphy", new_csiphy_dev); + if (rc < 0) { + pr_err("%s: irq request fail\n", __func__); + rc = -EBUSY; + goto csiphy_no_irq_resource; + } + msm_camera_enable_irq(new_csiphy_dev->irq, false); + + msm_cam_copy_v4l2_subdev_fops(&msm_csiphy_v4l2_subdev_fops); +#ifdef CONFIG_COMPAT + msm_csiphy_v4l2_subdev_fops.compat_ioctl32 = + msm_csiphy_subdev_fops_ioctl; +#endif + new_csiphy_dev->msm_sd.sd.devnode->fops = + &msm_csiphy_v4l2_subdev_fops; + new_csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN; + return 0; + +csiphy_no_irq_resource: + if (new_csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V30) { + msm_camera_put_reg_base(pdev, new_csiphy_dev->clk_mux_base, + "csiphy_clk_mux", true); + } +csiphy_no_mux_resource: + msm_camera_put_reg_base(pdev, new_csiphy_dev->base, "csiphy", true); +csiphy_no_resource: + mutex_destroy(&new_csiphy_dev->mutex); + kfree(new_csiphy_dev->ctrl_reg); + kfree(new_csiphy_dev); + return rc; +} + +static int msm_csiphy_exit(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev = platform_get_drvdata(pdev); + struct csiphy_device *csiphy_dev = + v4l2_get_subdevdata(subdev); + + msm_camera_put_clk_info(pdev, + &csiphy_dev->csiphy_all_clk_info, + &csiphy_dev->csiphy_all_clk, + csiphy_dev->num_all_clk); + + msm_camera_put_reg_base(pdev, csiphy_dev->base, "csid", true); + if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V30) { + msm_camera_put_reg_base(pdev, csiphy_dev->clk_mux_base, + "csiphy_clk_mux", true); + } + kfree(csiphy_dev); + return 0; +} + +static const struct of_device_id msm_csiphy_dt_match[] = { + {.compatible = "qcom,csiphy"}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_csiphy_dt_match); + +static struct platform_driver csiphy_driver = { + .probe = csiphy_probe, + .remove = msm_csiphy_exit, + .driver = { + .name = MSM_CSIPHY_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = msm_csiphy_dt_match, + }, +}; + +static int __init msm_csiphy_init_module(void) +{ + return platform_driver_register(&csiphy_driver); +} + +static void __exit msm_csiphy_exit_module(void) +{ + platform_driver_unregister(&csiphy_driver); +} + +module_init(msm_csiphy_init_module); +module_exit(msm_csiphy_exit_module); +MODULE_DESCRIPTION("MSM CSIPHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/camera/sensor/csiphy/msm_csiphy.h b/camera/sensor/csiphy/msm_csiphy.h new file mode 100644 index 00000000..5d7d6fe1 --- /dev/null +++ b/camera/sensor/csiphy/msm_csiphy.h @@ -0,0 +1,175 @@ +/* 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_CSIPHY_H +#define MSM_CSIPHY_H + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <media/v4l2-subdev.h> +#include <lenovo_media/msm_cam_sensor.h> +#include "msm_sd.h" +#include "msm_camera_io_util.h" +#include "cam_soc_api.h" + +#define MAX_CSIPHY 3 +#define CSIPHY_NUM_CLK_MAX 16 + +struct csiphy_reg_t { + uint32_t addr; + uint32_t data; +}; + +struct csiphy_reg_parms_t { +/*MIPI CSI PHY registers*/ + uint32_t mipi_csiphy_lnn_cfg1_addr; + uint32_t mipi_csiphy_lnn_cfg2_addr; + uint32_t mipi_csiphy_lnn_cfg3_addr; + uint32_t mipi_csiphy_lnn_cfg4_addr; + uint32_t mipi_csiphy_lnn_cfg5_addr; + uint32_t mipi_csiphy_lnck_cfg1_addr; + uint32_t mipi_csiphy_lnck_cfg2_addr; + uint32_t mipi_csiphy_lnck_cfg3_addr; + uint32_t mipi_csiphy_lnck_cfg4_addr; + uint32_t mipi_csiphy_lnn_test_imp; + uint32_t mipi_csiphy_lnn_misc1_addr; + uint32_t mipi_csiphy_glbl_reset_addr; + uint32_t mipi_csiphy_glbl_pwr_cfg_addr; + uint32_t mipi_csiphy_glbl_irq_cmd_addr; + uint32_t mipi_csiphy_hw_version_addr; + uint32_t mipi_csiphy_interrupt_status0_addr; + uint32_t mipi_csiphy_interrupt_mask0_addr; + uint32_t mipi_csiphy_interrupt_mask_val; + uint32_t mipi_csiphy_interrupt_mask_addr; + uint32_t mipi_csiphy_interrupt_clear0_addr; + uint32_t mipi_csiphy_interrupt_clear_addr; + uint32_t mipi_csiphy_mode_config_shift; + uint32_t mipi_csiphy_glbl_t_init_cfg0_addr; + uint32_t mipi_csiphy_t_wakeup_cfg0_addr; + uint32_t csiphy_version; + uint32_t combo_clk_mask; +}; + +struct csiphy_reg_3ph_parms_t { +/*MIPI CSI PHY registers*/ + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl5; + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl6; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl34; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl35; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl36; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl1; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl2; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl3; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl5; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl6; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl7; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl8; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl9; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl10; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl11; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl12; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl13; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl14; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl15; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl16; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl17; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl18; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl19; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl21; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl23; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl24; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl25; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl26; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl27; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl28; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl29; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl30; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl31; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl32; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl33; + struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl51; + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl7; + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl11; + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl12; + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl13; + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl14; + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl15; + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl16; + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl17; + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl18; + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl19; + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl20; + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl21; + struct csiphy_reg_t mipi_csiphy_2ph_lnn_misc1; + struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl0; + struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg1; + struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg2; + struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg3; + struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg4; + struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg5; + struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg6; + struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg7; + struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg8; + struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg9; + struct csiphy_reg_t mipi_csiphy_2ph_lnn_ctrl15; + struct csiphy_reg_t mipi_csiphy_2ph_lnn_test_imp; + struct csiphy_reg_t mipi_csiphy_2ph_lnn_test_force; + struct csiphy_reg_t mipi_csiphy_2ph_lnn_ctrl5; + struct csiphy_reg_t mipi_csiphy_3ph_lnck_cfg1; +}; + +struct csiphy_ctrl_t { + struct csiphy_reg_parms_t csiphy_reg; + struct csiphy_reg_3ph_parms_t csiphy_3ph_reg; +}; + +enum msm_csiphy_state_t { + CSIPHY_POWER_UP, + CSIPHY_POWER_DOWN, +}; + +struct csiphy_device { + struct platform_device *pdev; + struct msm_sd_subdev msm_sd; + struct v4l2_subdev subdev; + struct resource *irq; + void __iomem *base; + void __iomem *clk_mux_base; + struct mutex mutex; + uint32_t hw_version; + uint32_t hw_dts_version; + enum msm_csiphy_state_t csiphy_state; + struct csiphy_ctrl_t *ctrl_reg; + size_t num_all_clk; + struct clk **csiphy_all_clk; + struct msm_cam_clk_info *csiphy_all_clk_info; + uint32_t num_clk; + struct clk *csiphy_clk[CSIPHY_NUM_CLK_MAX]; + struct msm_cam_clk_info csiphy_clk_info[CSIPHY_NUM_CLK_MAX]; + struct clk *csiphy_3p_clk[2]; + struct msm_cam_clk_info csiphy_3p_clk_info[2]; + unsigned char csi_3phase; + int32_t ref_count; + uint16_t lane_mask[MAX_CSIPHY]; + uint32_t is_3_1_20nm_hw; + uint32_t csiphy_clk_index; + uint32_t csiphy_max_clk; + uint8_t csiphy_3phase; + uint8_t num_irq_registers; + uint32_t csiphy_sof_debug; + uint32_t csiphy_sof_debug_count; +}; + +#define VIDIOC_MSM_CSIPHY_RELEASE \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 9, void *) +#endif diff --git a/camera/sensor/eeprom/Makefile b/camera/sensor/eeprom/Makefile new file mode 100644 index 00000000..7806bfc9 --- /dev/null +++ b/camera/sensor/eeprom/Makefile @@ -0,0 +1,5 @@ +ccflags-y += -Icamera +ccflags-y += -Icamera/common +ccflags-y += -Icamera/sensor/io +ccflags-y += -Icamera/sensor/cci +obj-$(CONFIG_MSM_EEPROM) += msm_eeprom.o diff --git a/camera/sensor/eeprom/msm_eeprom.c b/camera/sensor/eeprom/msm_eeprom.c new file mode 100644 index 00000000..d416e4bf --- /dev/null +++ b/camera/sensor/eeprom/msm_eeprom.c @@ -0,0 +1,1902 @@ +/* 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 <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/delay.h> +#include <linux/crc32.h> +#include "msm_sd.h" +#include "msm_cci.h" +#include "msm_eeprom.h" +#ifdef CONFIG_CAMERA_WT_FACTORY_SUPPORTED +#include <linux/hardware_info.h> +#endif +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +DEFINE_MSM_MUTEX(msm_eeprom_mutex); +#ifdef CONFIG_COMPAT +static struct v4l2_file_operations msm_eeprom_v4l2_subdev_fops; +#endif + +/** + * msm_get_read_mem_size - Get the total size for allocation + * @eeprom_map_array: mem map + * + * Returns size after computation size, returns error in case of error + */ +static int msm_get_read_mem_size + (struct msm_eeprom_memory_map_array *eeprom_map_array) { + int size = 0, i, j; + struct msm_eeprom_mem_map_t *eeprom_map; + + if (eeprom_map_array->msm_size_of_max_mappings > + MSM_EEPROM_MAX_MEM_MAP_CNT) { + pr_err("%s:%d Memory map cnt greter then expected: %d", + __func__, __LINE__, + eeprom_map_array->msm_size_of_max_mappings); + return -EINVAL; + } + for (j = 0; j < eeprom_map_array->msm_size_of_max_mappings; j++) { + eeprom_map = &(eeprom_map_array->memory_map[j]); + if (eeprom_map->memory_map_size > + MSM_EEPROM_MEMORY_MAP_MAX_SIZE) { + pr_err("%s:%d Memory map size greter then expected: %d", + __func__, __LINE__, + eeprom_map->memory_map_size); + return -EINVAL; + } + for (i = 0; i < eeprom_map->memory_map_size; i++) { + if (eeprom_map->mem_settings[i].i2c_operation == + MSM_CAM_READ) { + size += eeprom_map->mem_settings[i].reg_data; + } + } + } + CDBG("Total Data Size: %d\n", size); + return size; +} + +/** + * msm_eeprom_verify_sum - verify crc32 checksum + * @mem: data buffer + * @size: size of data buffer + * @sum: expected checksum + * + * Returns 0 if checksum match, -EINVAL otherwise. + */ +static int msm_eeprom_verify_sum(const char *mem, uint32_t size, uint32_t sum) +{ + uint32_t crc = ~0; + + /* check overflow */ + if (size > crc - sizeof(uint32_t)) + return -EINVAL; + + crc = crc32_le(crc, mem, size); + if (~crc != sum) { + CDBG("%s: expect 0x%x, result 0x%x\n", __func__, sum, ~crc); + return -EINVAL; + } + CDBG("%s: checksum pass 0x%x\n", __func__, sum); + return 0; +} + +/** + * msm_eeprom_match_crc - verify multiple regions using crc + * @data: data block to be verified + * + * Iterates through all regions stored in @data. Regions with odd index + * are treated as data, and its next region is treated as checksum. Thus + * regions of even index must have valid_size of 4 or 0 (skip verification). + * Returns a bitmask of verified regions, starting from LSB. 1 indicates + * a checksum match, while 0 indicates checksum mismatch or not verified. + */ +static uint32_t msm_eeprom_match_crc(struct msm_eeprom_memory_block_t *data) +{ + int j, rc; + uint32_t *sum; + uint32_t ret = 0; + uint8_t *memptr; + struct msm_eeprom_memory_map_t *map; + + if (!data) { + pr_err("%s data is NULL", __func__); + return -EINVAL; + } + map = data->map; + memptr = data->mapdata; + + for (j = 0; j + 1 < data->num_map; j += 2) { + /* empty table or no checksum */ + if (!map[j].mem.valid_size || !map[j+1].mem.valid_size) { + memptr += map[j].mem.valid_size + + map[j+1].mem.valid_size; + continue; + } + if (map[j+1].mem.valid_size != sizeof(uint32_t)) { + CDBG("%s: malformatted data mapping\n", __func__); + return -EINVAL; + } + sum = (uint32_t *) (memptr + map[j].mem.valid_size); + rc = msm_eeprom_verify_sum(memptr, map[j].mem.valid_size, + *sum); + if (!rc) + ret |= 1 << (j/2); + memptr += map[j].mem.valid_size + map[j+1].mem.valid_size; + } + return ret; +} + +/** + * read_eeprom_memory() - read map data into buffer + * @e_ctrl: eeprom control struct + * @block: block to be read + * + * This function iterates through blocks stored in block->map, reads each + * region and concatenate them into the pre-allocated block->mapdata + */ +static int read_eeprom_memory(struct msm_eeprom_ctrl_t *e_ctrl, + struct msm_eeprom_memory_block_t *block) +{ + int rc = 0; + int j; + struct msm_eeprom_memory_map_t *emap = block->map; + struct msm_eeprom_board_info *eb_info; + uint8_t *memptr = block->mapdata; + + if (!e_ctrl) { + pr_err("%s e_ctrl is NULL", __func__); + return -EINVAL; + } + + eb_info = e_ctrl->eboard_info; + + for (j = 0; j < block->num_map; j++) { + if (emap[j].saddr.addr) { + eb_info->i2c_slaveaddr = emap[j].saddr.addr; + e_ctrl->i2c_client.cci_client->sid = + eb_info->i2c_slaveaddr >> 1; + pr_err("qcom,slave-addr = 0x%X\n", + eb_info->i2c_slaveaddr); + } + + if (emap[j].page.valid_size) { + e_ctrl->i2c_client.addr_type = emap[j].page.addr_t; + rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write( + &(e_ctrl->i2c_client), emap[j].page.addr, + emap[j].page.data, emap[j].page.data_t); + msleep(emap[j].page.delay); + if (rc < 0) { + pr_err("%s: page write failed\n", __func__); + return rc; + } + } + if (emap[j].pageen.valid_size) { + e_ctrl->i2c_client.addr_type = emap[j].pageen.addr_t; + rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write( + &(e_ctrl->i2c_client), emap[j].pageen.addr, + emap[j].pageen.data, emap[j].pageen.data_t); + msleep(emap[j].pageen.delay); + if (rc < 0) { + pr_err("%s: page enable failed\n", __func__); + return rc; + } + } + if (emap[j].poll.valid_size) { + e_ctrl->i2c_client.addr_type = emap[j].poll.addr_t; + rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_poll( + &(e_ctrl->i2c_client), emap[j].poll.addr, + emap[j].poll.data, emap[j].poll.data_t, + emap[j].poll.delay); + if (rc < 0) { + pr_err("%s: poll failed\n", __func__); + return rc; + } + } + + if (emap[j].mem.valid_size) { + e_ctrl->i2c_client.addr_type = emap[j].mem.addr_t; + rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_read_seq( + &(e_ctrl->i2c_client), emap[j].mem.addr, + memptr, emap[j].mem.valid_size); + if (rc < 0) { + pr_err("%s: read failed\n", __func__); + return rc; + } + memptr += emap[j].mem.valid_size; + } + if (emap[j].pageen.valid_size) { + e_ctrl->i2c_client.addr_type = emap[j].pageen.addr_t; + rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write( + &(e_ctrl->i2c_client), emap[j].pageen.addr, + 0, emap[j].pageen.data_t); + if (rc < 0) { + pr_err("%s: page disable failed\n", __func__); + return rc; + } + } + } + return rc; +} +/** + * msm_eeprom_parse_memory_map() - parse memory map in device node + * @of: device node + * @data: memory block for output + * + * This functions parses @of to fill @data. It allocates map itself, parses + * the @of node, calculate total data length, and allocates required buffer. + * It only fills the map, but does not perform actual reading. + */ +static int msm_eeprom_parse_memory_map(struct device_node *of, + struct msm_eeprom_memory_block_t *data) +{ + int i, rc = 0; + char property[PROPERTY_MAXSIZE]; + uint32_t count = 6; + struct msm_eeprom_memory_map_t *map; + + snprintf(property, PROPERTY_MAXSIZE, "qcom,num-blocks"); + rc = of_property_read_u32(of, property, &data->num_map); + CDBG("%s: %s %d\n", __func__, property, data->num_map); + if (rc < 0) { + pr_err("%s failed rc %d\n", __func__, rc); + return rc; + } + + map = kzalloc((sizeof(*map) * data->num_map), GFP_KERNEL); + if (!map) { + rc = -ENOMEM; + pr_err("%s failed line %d\n", __func__, __LINE__); + return rc; + } + data->map = map; + + for (i = 0; i < data->num_map; i++) { + snprintf(property, PROPERTY_MAXSIZE, "qcom,page%d", i); + rc = of_property_read_u32_array(of, property, + (uint32_t *) &map[i].page, count); + if (rc < 0) { + pr_err("%s: failed %d\n", __func__, __LINE__); + goto ERROR; + } + + snprintf(property, PROPERTY_MAXSIZE, + "qcom,pageen%d", i); + rc = of_property_read_u32_array(of, property, + (uint32_t *) &map[i].pageen, count); + if (rc < 0) + CDBG("%s: pageen not needed\n", __func__); + + snprintf(property, PROPERTY_MAXSIZE, "qcom,saddr%d", i); + rc = of_property_read_u32_array(of, property, + (uint32_t *) &map[i].saddr.addr, 1); + if (rc < 0) + CDBG("%s: saddr not needed - block %d\n", __func__, i); + + snprintf(property, PROPERTY_MAXSIZE, "qcom,poll%d", i); + rc = of_property_read_u32_array(of, property, + (uint32_t *) &map[i].poll, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR; + } + + snprintf(property, PROPERTY_MAXSIZE, "qcom,mem%d", i); + rc = of_property_read_u32_array(of, property, + (uint32_t *) &map[i].mem, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR; + } + data->num_data += map[i].mem.valid_size; + } + + CDBG("%s num_bytes %d\n", __func__, data->num_data); + + data->mapdata = kzalloc(data->num_data, GFP_KERNEL); + if (!data->mapdata) { + rc = -ENOMEM; + pr_err("%s failed line %d\n", __func__, __LINE__); + goto ERROR; + } + return rc; + +ERROR: + kfree(data->map); + memset(data, 0, sizeof(*data)); + return rc; +} + +/** + * eeprom_parse_memory_map - Parse mem map + * @e_ctrl: ctrl structure + * @eeprom_map_array: eeprom map + * + * Returns success or failure + */ +static int eeprom_parse_memory_map(struct msm_eeprom_ctrl_t *e_ctrl, + struct msm_eeprom_memory_map_array *eeprom_map_array) +{ + int rc = 0, i, j; + uint8_t *memptr; + struct msm_eeprom_mem_map_t *eeprom_map; +#ifdef CONFIG_CAMERA_WT_FACTORY_SUPPORTED + static char module_id[20] = {0}; + int32_t module_id_index = 0x000C; +#endif + + e_ctrl->cal_data.mapdata = NULL; + e_ctrl->cal_data.num_data = msm_get_read_mem_size(eeprom_map_array); + if (e_ctrl->cal_data.num_data <= 0) { + pr_err("%s:%d Error in reading mem size\n", + __func__, __LINE__); + e_ctrl->cal_data.num_data = 0; + return -EINVAL; + } + e_ctrl->cal_data.mapdata = + kzalloc(e_ctrl->cal_data.num_data, GFP_KERNEL); + if (!e_ctrl->cal_data.mapdata) + return -ENOMEM; + + memptr = e_ctrl->cal_data.mapdata; + for (j = 0; j < eeprom_map_array->msm_size_of_max_mappings; j++) { + eeprom_map = &(eeprom_map_array->memory_map[j]); + if (e_ctrl->i2c_client.cci_client) { + e_ctrl->i2c_client.cci_client->sid = + eeprom_map->slave_addr >> 1; + } else if (e_ctrl->i2c_client.client) { + e_ctrl->i2c_client.client->addr = + eeprom_map->slave_addr >> 1; + } + CDBG("Slave Addr: 0x%X\n", eeprom_map->slave_addr); + CDBG("Memory map Size: %d", + eeprom_map->memory_map_size); + for (i = 0; i < eeprom_map->memory_map_size; i++) { + switch (eeprom_map->mem_settings[i].i2c_operation) { + case MSM_CAM_WRITE: { + e_ctrl->i2c_client.addr_type = + eeprom_map->mem_settings[i].addr_type; + rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write( + &(e_ctrl->i2c_client), + eeprom_map->mem_settings[i].reg_addr, + eeprom_map->mem_settings[i].reg_data, + eeprom_map->mem_settings[i].data_type); + msleep(eeprom_map->mem_settings[i].delay); + if (rc < 0) { + pr_err("%s: page write failed\n", + __func__); + goto clean_up; + } + } + break; + case MSM_CAM_POLL: { + e_ctrl->i2c_client.addr_type = + eeprom_map->mem_settings[i].addr_type; + rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_poll( + &(e_ctrl->i2c_client), + eeprom_map->mem_settings[i].reg_addr, + eeprom_map->mem_settings[i].reg_data, + eeprom_map->mem_settings[i].data_type, + eeprom_map->mem_settings[i].delay); + if (rc < 0) { + pr_err("%s: poll failed\n", + __func__); + goto clean_up; + } + } + break; + case MSM_CAM_READ: { + e_ctrl->i2c_client.addr_type = + eeprom_map->mem_settings[i].addr_type; + rc = e_ctrl->i2c_client.i2c_func_tbl-> + i2c_read_seq(&(e_ctrl->i2c_client), + eeprom_map->mem_settings[i].reg_addr, + memptr, + eeprom_map->mem_settings[i].reg_data); + msleep(eeprom_map->mem_settings[i].delay); + if (rc < 0) { + pr_err("%s: read failed\n", + __func__); + goto clean_up; + } + memptr += eeprom_map->mem_settings[i].reg_data; + } + break; + default: + pr_err("%s: %d Invalid i2c operation LC:%d\n", + __func__, __LINE__, i); + return -EINVAL; + } + } + } + memptr = e_ctrl->cal_data.mapdata; + for (i = 0; i < e_ctrl->cal_data.num_data; i++) + CDBG("memory_data[%d] = 0x%X\n", i, memptr[i]); +#ifdef CONFIG_CAMERA_WT_FACTORY_SUPPORTED + if (e_ctrl->pdev->id == 1) { + if (((e_ctrl->cal_data.mapdata[0] >> 6) & 0x3) == 1) + module_id_index = 0x3205 - 0x3204; + else if (((e_ctrl->cal_data.mapdata[0] >> 4) & 0x3) == 1) + module_id_index = 0x3211 - 0x3204; + sprintf(module_id, "%x", e_ctrl->cal_data.mapdata[module_id_index]); + hardwareinfo_set_prop(HARDWARE_FRONT_CAM_MOUDULE_ID, module_id); + } else if (e_ctrl->pdev->id == 0) { + module_id_index = 0x000C; + sprintf(module_id, "%x", e_ctrl->cal_data.mapdata[module_id_index]); + hardwareinfo_set_prop(HARDWARE_BACK_CAM_MOUDULE_ID, module_id); + } + CDBG("module_id: %s index: 0x%x\n", module_id, module_id_index); +#endif + return rc; + +clean_up: + kfree(e_ctrl->cal_data.mapdata); + e_ctrl->cal_data.num_data = 0; + e_ctrl->cal_data.mapdata = NULL; + return rc; +} + +/** + * msm_eeprom_power_up - Do eeprom power up here + * @e_ctrl: ctrl structure + * @power_info: power up info for eeprom + * + * Returns success or failure + */ +static int msm_eeprom_power_up(struct msm_eeprom_ctrl_t *e_ctrl, + struct msm_camera_power_ctrl_t *power_info) { + int32_t rc = 0; + + rc = msm_camera_fill_vreg_params( + power_info->cam_vreg, power_info->num_vreg, + power_info->power_setting, power_info->power_setting_size); + if (rc < 0) { + pr_err("%s:%d failed in camera_fill_vreg_params rc %d", + __func__, __LINE__, rc); + return rc; + } + + /* Parse and fill vreg params for powerdown settings*/ + rc = msm_camera_fill_vreg_params( + power_info->cam_vreg, power_info->num_vreg, + power_info->power_down_setting, + power_info->power_down_setting_size); + if (rc < 0) { + pr_err("%s:%d failed msm_camera_fill_vreg_params for PDOWN rc %d", + __func__, __LINE__, rc); + return rc; + } + + rc = msm_camera_power_up(power_info, e_ctrl->eeprom_device_type, + &e_ctrl->i2c_client); + if (rc) { + pr_err("%s:%d failed in eeprom Power up rc %d\n", + __func__, __LINE__, rc); + return rc; + } + return rc; +} + +/** + * msm_eeprom_power_up - Do power up, parse and power down + * @e_ctrl: ctrl structure + * Returns success or failure + */ +static int eeprom_init_config(struct msm_eeprom_ctrl_t *e_ctrl, + void __user *argp) +{ + int rc = 0; + struct msm_eeprom_cfg_data *cdata = argp; + struct msm_sensor_power_setting_array *power_setting_array = NULL; + struct msm_camera_power_ctrl_t *power_info; + struct msm_eeprom_memory_map_array *memory_map_arr = NULL; + + power_setting_array = + kzalloc(sizeof(struct msm_sensor_power_setting_array), + GFP_KERNEL); + if (!power_setting_array) { + pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__); + rc = -ENOMEM; + return rc; + } + memory_map_arr = kzalloc(sizeof(struct msm_eeprom_memory_map_array), + GFP_KERNEL); + if (!memory_map_arr) { + rc = -ENOMEM; + pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__); + goto free_mem; + } + + if (copy_from_user(power_setting_array, + cdata->cfg.eeprom_info.power_setting_array, + sizeof(struct msm_sensor_power_setting_array))) { + pr_err("%s copy_from_user failed %d\n", + __func__, __LINE__); + goto free_mem; + } + CDBG("%s:%d Size of power setting array: %d\n", + __func__, __LINE__, power_setting_array->size); + if (copy_from_user(memory_map_arr, + cdata->cfg.eeprom_info.mem_map_array, + sizeof(struct msm_eeprom_memory_map_array))) { + rc = -EINVAL; + pr_err("%s copy_from_user failed for memory map%d\n", + __func__, __LINE__); + goto free_mem; + } + + power_info = &(e_ctrl->eboard_info->power_info); + + power_info->power_setting = + power_setting_array->power_setting_a; + power_info->power_down_setting = + power_setting_array->power_down_setting_a; + + power_info->power_setting_size = + power_setting_array->size; + power_info->power_down_setting_size = + power_setting_array->size_down; + + if ((power_info->power_setting_size > + MAX_POWER_CONFIG) || + (power_info->power_down_setting_size > + MAX_POWER_CONFIG) || + (!power_info->power_down_setting_size) || + (!power_info->power_setting_size)) { + rc = -EINVAL; + pr_err("%s:%d Invalid power setting size :%d, %d\n", + __func__, __LINE__, + power_info->power_setting_size, + power_info->power_down_setting_size); + goto free_mem; + } + + if (e_ctrl->i2c_client.cci_client) { + e_ctrl->i2c_client.cci_client->i2c_freq_mode = + cdata->cfg.eeprom_info.i2c_freq_mode; + if (e_ctrl->i2c_client.cci_client->i2c_freq_mode > + I2C_MAX_MODES) { + pr_err("%s::%d Improper I2C freq mode\n", + __func__, __LINE__); + e_ctrl->i2c_client.cci_client->i2c_freq_mode = + I2C_STANDARD_MODE; + } + } + + /* Fill vreg power info and power up here */ + rc = msm_eeprom_power_up(e_ctrl, power_info); + if (rc < 0) { + pr_err("Power Up failed for eeprom\n"); + goto free_mem; + } + + rc = eeprom_parse_memory_map(e_ctrl, memory_map_arr); + if (rc < 0) { + pr_err("%s::%d memory map parse failed\n", __func__, __LINE__); + goto free_mem; + } + + rc = msm_camera_power_down(power_info, e_ctrl->eeprom_device_type, + &e_ctrl->i2c_client); + if (rc < 0) { + pr_err("%s:%d Power down failed rc %d\n", + __func__, __LINE__, rc); + goto free_mem; + } + +free_mem: + kfree(power_setting_array); + kfree(memory_map_arr); + power_setting_array = NULL; + memory_map_arr = NULL; + return rc; +} + +static int msm_eeprom_get_cmm_data(struct msm_eeprom_ctrl_t *e_ctrl, + struct msm_eeprom_cfg_data *cdata) +{ + int rc = 0; + struct msm_eeprom_cmm_t *cmm_data = &e_ctrl->eboard_info->cmm_data; + cdata->cfg.get_cmm_data.cmm_support = cmm_data->cmm_support; + cdata->cfg.get_cmm_data.cmm_compression = cmm_data->cmm_compression; + cdata->cfg.get_cmm_data.cmm_size = cmm_data->cmm_size; + return rc; +} + +static int eeprom_config_read_cal_data(struct msm_eeprom_ctrl_t *e_ctrl, + struct msm_eeprom_cfg_data *cdata) +{ + int rc; + + /* check range */ + if (cdata->cfg.read_data.num_bytes > + e_ctrl->cal_data.num_data) { + CDBG("%s: Invalid size. exp %u, req %u\n", __func__, + e_ctrl->cal_data.num_data, + cdata->cfg.read_data.num_bytes); + return -EINVAL; + } + + rc = copy_to_user(cdata->cfg.read_data.dbuffer, + e_ctrl->cal_data.mapdata, + cdata->cfg.read_data.num_bytes); + + return rc; +} + +static int msm_eeprom_config(struct msm_eeprom_ctrl_t *e_ctrl, + void __user *argp) +{ + struct msm_eeprom_cfg_data *cdata = + (struct msm_eeprom_cfg_data *)argp; + int rc = 0; + + CDBG("%s E\n", __func__); + switch (cdata->cfgtype) { + case CFG_EEPROM_GET_INFO: + if (e_ctrl->userspace_probe == 1) { + pr_err("%s:%d Eeprom name should be module driver", + __func__, __LINE__); + rc = -EINVAL; + break; + } + CDBG("%s E CFG_EEPROM_GET_INFO\n", __func__); + cdata->is_supported = e_ctrl->is_supported; + memcpy(cdata->cfg.eeprom_name, + e_ctrl->eboard_info->eeprom_name, + sizeof(cdata->cfg.eeprom_name)); + break; + case CFG_EEPROM_GET_CAL_DATA: + CDBG("%s E CFG_EEPROM_GET_CAL_DATA\n", __func__); + cdata->cfg.get_data.num_bytes = + e_ctrl->cal_data.num_data; + break; + case CFG_EEPROM_READ_CAL_DATA: + CDBG("%s E CFG_EEPROM_READ_CAL_DATA\n", __func__); + rc = eeprom_config_read_cal_data(e_ctrl, cdata); + break; + case CFG_EEPROM_GET_MM_INFO: + CDBG("%s E CFG_EEPROM_GET_MM_INFO\n", __func__); + rc = msm_eeprom_get_cmm_data(e_ctrl, cdata); + break; + case CFG_EEPROM_INIT: + if (e_ctrl->userspace_probe == 0) { + pr_err("%s:%d Eeprom already probed at kernel boot", + __func__, __LINE__); + rc = -EINVAL; + break; + } + if (e_ctrl->cal_data.num_data == 0) { + rc = eeprom_init_config(e_ctrl, argp); + if (rc < 0) { + pr_err("%s:%d Eeprom init failed\n", + __func__, __LINE__); + return rc; + } + } else { + CDBG("%s:%d Already read eeprom\n", + __func__, __LINE__); + } + break; + default: + break; + } + + CDBG("%s X rc: %d\n", __func__, rc); + return rc; +} + +static int msm_eeprom_get_subdev_id(struct msm_eeprom_ctrl_t *e_ctrl, + void *arg) +{ + uint32_t *subdev_id = (uint32_t *)arg; + CDBG("%s E\n", __func__); + if (!subdev_id) { + pr_err("%s failed\n", __func__); + return -EINVAL; + } + *subdev_id = e_ctrl->subdev_id; + CDBG("subdev_id %d\n", *subdev_id); + CDBG("%s X\n", __func__); + return 0; +} + +static long msm_eeprom_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct msm_eeprom_ctrl_t *e_ctrl = v4l2_get_subdevdata(sd); + void __user *argp = (void __user *)arg; + CDBG("%s E\n", __func__); + CDBG("%s:%d a_ctrl %p argp %p\n", __func__, __LINE__, e_ctrl, argp); + switch (cmd) { + case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID: + return msm_eeprom_get_subdev_id(e_ctrl, argp); + case VIDIOC_MSM_EEPROM_CFG: + return msm_eeprom_config(e_ctrl, argp); + default: + return -ENOIOCTLCMD; + } + + CDBG("%s X\n", __func__); +} + +static struct msm_camera_i2c_fn_t msm_eeprom_cci_func_tbl = { + .i2c_read = msm_camera_cci_i2c_read, + .i2c_read_seq = msm_camera_cci_i2c_read_seq, + .i2c_write = msm_camera_cci_i2c_write, + .i2c_write_seq = msm_camera_cci_i2c_write_seq, + .i2c_write_table = msm_camera_cci_i2c_write_table, + .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_cci_i2c_write_table_w_microdelay, + .i2c_util = msm_sensor_cci_i2c_util, + .i2c_poll = msm_camera_cci_i2c_poll, +}; + +static struct msm_camera_i2c_fn_t msm_eeprom_qup_func_tbl = { + .i2c_read = msm_camera_qup_i2c_read, + .i2c_read_seq = msm_camera_qup_i2c_read_seq, + .i2c_write = msm_camera_qup_i2c_write, + .i2c_write_table = msm_camera_qup_i2c_write_table, + .i2c_write_seq_table = msm_camera_qup_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_qup_i2c_write_table_w_microdelay, +}; + +static struct msm_camera_i2c_fn_t msm_eeprom_spi_func_tbl = { + .i2c_read = msm_camera_spi_read, + .i2c_read_seq = msm_camera_spi_read_seq, +}; + +static int msm_eeprom_open(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) { + int rc = 0; + struct msm_eeprom_ctrl_t *e_ctrl = v4l2_get_subdevdata(sd); + CDBG("%s E\n", __func__); + if (!e_ctrl) { + pr_err("%s failed e_ctrl is NULL\n", __func__); + return -EINVAL; + } + CDBG("%s X\n", __func__); + return rc; +} + +static int msm_eeprom_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) { + int rc = 0; + struct msm_eeprom_ctrl_t *e_ctrl = v4l2_get_subdevdata(sd); + CDBG("%s E\n", __func__); + if (!e_ctrl) { + pr_err("%s failed e_ctrl is NULL\n", __func__); + return -EINVAL; + } + CDBG("%s X\n", __func__); + return rc; +} + +static const struct v4l2_subdev_internal_ops msm_eeprom_internal_ops = { + .open = msm_eeprom_open, + .close = msm_eeprom_close, +}; + +static struct v4l2_subdev_core_ops msm_eeprom_subdev_core_ops = { + .ioctl = msm_eeprom_subdev_ioctl, +}; + +static struct v4l2_subdev_ops msm_eeprom_subdev_ops = { + .core = &msm_eeprom_subdev_core_ops, +}; + +static int msm_eeprom_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + struct msm_eeprom_ctrl_t *e_ctrl = NULL; + CDBG("%s E\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s i2c_check_functionality failed\n", __func__); + goto probe_failure; + } + + e_ctrl = kzalloc(sizeof(*e_ctrl), GFP_KERNEL); + if (!e_ctrl) { + pr_err("%s:%d kzalloc failed\n", __func__, __LINE__); + return -ENOMEM; + } + e_ctrl->eeprom_v4l2_subdev_ops = &msm_eeprom_subdev_ops; + e_ctrl->eeprom_mutex = &msm_eeprom_mutex; + CDBG("%s client = 0x%p\n", __func__, client); + e_ctrl->eboard_info = (struct msm_eeprom_board_info *)(id->driver_data); + if (!e_ctrl->eboard_info) { + pr_err("%s:%d board info NULL\n", __func__, __LINE__); + rc = -EINVAL; + goto ectrl_free; + } + e_ctrl->i2c_client.client = client; + e_ctrl->cal_data.mapdata = NULL; + e_ctrl->cal_data.map = NULL; + e_ctrl->userspace_probe = 0; + e_ctrl->is_supported = 1; + + /* Set device type as I2C */ + e_ctrl->eeprom_device_type = MSM_CAMERA_I2C_DEVICE; + e_ctrl->i2c_client.i2c_func_tbl = &msm_eeprom_qup_func_tbl; + + if (e_ctrl->eboard_info->i2c_slaveaddr != 0) + e_ctrl->i2c_client.client->addr = + e_ctrl->eboard_info->i2c_slaveaddr; + + /*Get clocks information*/ + rc = msm_camera_get_clk_info(e_ctrl->pdev, + &e_ctrl->eboard_info->power_info.clk_info, + &e_ctrl->eboard_info->power_info.clk_ptr, + &e_ctrl->eboard_info->power_info.clk_info_size); + if (rc < 0) { + pr_err("failed: msm_camera_get_clk_info rc %d", rc); + goto ectrl_free; + } + + /*IMPLEMENT READING PART*/ + /* Initialize sub device */ + v4l2_i2c_subdev_init(&e_ctrl->msm_sd.sd, + e_ctrl->i2c_client.client, + e_ctrl->eeprom_v4l2_subdev_ops); + v4l2_set_subdevdata(&e_ctrl->msm_sd.sd, e_ctrl); + e_ctrl->msm_sd.sd.internal_ops = &msm_eeprom_internal_ops; + e_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + media_entity_init(&e_ctrl->msm_sd.sd.entity, 0, NULL, 0); + e_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + e_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_EEPROM; + msm_sd_register(&e_ctrl->msm_sd); + CDBG("%s success result=%d X\n", __func__, rc); + return rc; + +ectrl_free: + kfree(e_ctrl); +probe_failure: + pr_err("%s failed! rc = %d\n", __func__, rc); + return rc; +} + +static int msm_eeprom_i2c_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct msm_eeprom_ctrl_t *e_ctrl; + if (!sd) { + pr_err("%s: Subdevice is NULL\n", __func__); + return 0; + } + + e_ctrl = (struct msm_eeprom_ctrl_t *)v4l2_get_subdevdata(sd); + if (!e_ctrl) { + pr_err("%s: eeprom device is NULL\n", __func__); + return 0; + } + + if (!e_ctrl->eboard_info) { + pr_err("%s: eboard_info is NULL\n", __func__); + return 0; + } + + msm_camera_put_clk_info(e_ctrl->pdev, + &e_ctrl->eboard_info->power_info.clk_info, + &e_ctrl->eboard_info->power_info.clk_ptr, + e_ctrl->eboard_info->power_info.clk_info_size); + + kfree(e_ctrl->cal_data.mapdata); + kfree(e_ctrl->cal_data.map); + if (e_ctrl->eboard_info) { + kfree(e_ctrl->eboard_info->power_info.gpio_conf); + kfree(e_ctrl->eboard_info); + } + e_ctrl->cal_data.mapdata = NULL; + kfree(e_ctrl); + e_ctrl = NULL; + + return 0; +} + +#define msm_eeprom_spi_parse_cmd(spic, str, name, out, size) \ + { \ + if (of_property_read_u32_array( \ + spic->spi_master->dev.of_node, \ + str, out, size)) { \ + return -EFAULT; \ + } else { \ + spic->cmd_tbl.name.opcode = out[0]; \ + spic->cmd_tbl.name.addr_len = out[1]; \ + spic->cmd_tbl.name.dummy_len = out[2]; \ + } \ + } + +static int msm_eeprom_spi_parse_of(struct msm_camera_spi_client *spic) +{ + int rc = -EFAULT; + uint32_t tmp[3]; + msm_eeprom_spi_parse_cmd(spic, "qcom,spiop,read", read, tmp, 3); + msm_eeprom_spi_parse_cmd(spic, "qcom,spiop,readseq", read_seq, tmp, 3); + msm_eeprom_spi_parse_cmd(spic, "qcom,spiop,queryid", query_id, tmp, 3); + + rc = of_property_read_u32_array(spic->spi_master->dev.of_node, + "qcom,eeprom-id", tmp, 2); + if (rc) { + pr_err("%s: Failed to get eeprom id\n", __func__); + return rc; + } + spic->mfr_id0 = tmp[0]; + spic->device_id0 = tmp[1]; + + return 0; +} + +static int msm_eeprom_match_id(struct msm_eeprom_ctrl_t *e_ctrl) +{ + int rc; + struct msm_camera_i2c_client *client = &e_ctrl->i2c_client; + uint8_t id[2]; + + rc = msm_camera_spi_query_id(client, 0, &id[0], 2); + if (rc < 0) + return rc; + CDBG("%s: read 0x%x 0x%x, check 0x%x 0x%x\n", __func__, id[0], + id[1], client->spi_client->mfr_id0, + client->spi_client->device_id0); + if (id[0] != client->spi_client->mfr_id0 + || id[1] != client->spi_client->device_id0) + return -ENODEV; + + return 0; +} + +static int msm_eeprom_get_dt_data(struct msm_eeprom_ctrl_t *e_ctrl) +{ + int rc = 0, i = 0; + struct msm_eeprom_board_info *eb_info; + struct msm_camera_power_ctrl_t *power_info = + &e_ctrl->eboard_info->power_info; + struct device_node *of_node = NULL; + struct msm_camera_gpio_conf *gconf = NULL; + int8_t gpio_array_size = 0; + uint16_t *gpio_array = NULL; + + eb_info = e_ctrl->eboard_info; + if (e_ctrl->eeprom_device_type == MSM_CAMERA_SPI_DEVICE) + of_node = e_ctrl->i2c_client. + spi_client->spi_master->dev.of_node; + else if (e_ctrl->eeprom_device_type == MSM_CAMERA_PLATFORM_DEVICE) + of_node = e_ctrl->pdev->dev.of_node; + + if (!of_node) { + pr_err("%s: %d of_node is NULL\n", __func__ , __LINE__); + return -ENOMEM; + } + rc = msm_camera_get_dt_vreg_data(of_node, &power_info->cam_vreg, + &power_info->num_vreg); + if (rc < 0) + return rc; + + if (e_ctrl->userspace_probe == 0) { + rc = msm_camera_get_dt_power_setting_data(of_node, + power_info->cam_vreg, power_info->num_vreg, + power_info); + if (rc < 0) + goto ERROR1; + } + + power_info->gpio_conf = kzalloc(sizeof(struct msm_camera_gpio_conf), + GFP_KERNEL); + if (!power_info->gpio_conf) { + rc = -ENOMEM; + goto ERROR2; + } + gconf = power_info->gpio_conf; + gpio_array_size = of_gpio_count(of_node); + CDBG("%s gpio count %d\n", __func__, gpio_array_size); + + if (gpio_array_size > 0) { + gpio_array = kzalloc(sizeof(uint16_t) * gpio_array_size, + GFP_KERNEL); + if (!gpio_array) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR3; + } + for (i = 0; i < gpio_array_size; i++) { + gpio_array[i] = of_get_gpio(of_node, i); + CDBG("%s gpio_array[%d] = %d\n", __func__, 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("%s failed %d\n", __func__, __LINE__); + goto ERROR4; + } + + rc = msm_camera_init_gpio_pin_tbl(of_node, gconf, + gpio_array, gpio_array_size); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR4; + } + kfree(gpio_array); + } + + return rc; +ERROR4: + kfree(gpio_array); +ERROR3: + kfree(power_info->gpio_conf); +ERROR2: + kfree(power_info->cam_vreg); +ERROR1: + kfree(power_info->power_setting); + return rc; +} + + +static int msm_eeprom_cmm_dts(struct msm_eeprom_board_info *eb_info, + struct device_node *of_node) +{ + int rc = 0; + struct msm_eeprom_cmm_t *cmm_data = &eb_info->cmm_data; + + cmm_data->cmm_support = + of_property_read_bool(of_node, "qcom,cmm-data-support"); + if (!cmm_data->cmm_support) + return -EINVAL; + cmm_data->cmm_compression = + of_property_read_bool(of_node, "qcom,cmm-data-compressed"); + if (!cmm_data->cmm_compression) + CDBG("No MM compression data\n"); + + rc = of_property_read_u32(of_node, "qcom,cmm-data-offset", + &cmm_data->cmm_offset); + if (rc < 0) + CDBG("No MM offset data\n"); + + rc = of_property_read_u32(of_node, "qcom,cmm-data-size", + &cmm_data->cmm_size); + if (rc < 0) + CDBG("No MM size data\n"); + + CDBG("cmm_support: cmm_compr %d, cmm_offset %d, cmm_size %d\n", + cmm_data->cmm_compression, + cmm_data->cmm_offset, + cmm_data->cmm_size); + return 0; +} + +static int msm_eeprom_spi_setup(struct spi_device *spi) +{ + struct msm_eeprom_ctrl_t *e_ctrl = NULL; + struct msm_camera_i2c_client *client = NULL; + struct msm_camera_spi_client *spi_client; + struct msm_eeprom_board_info *eb_info; + struct msm_camera_power_ctrl_t *power_info = NULL; + int rc = 0; + + e_ctrl = kzalloc(sizeof(*e_ctrl), GFP_KERNEL); + if (!e_ctrl) { + pr_err("%s:%d kzalloc failed\n", __func__, __LINE__); + return -ENOMEM; + } + e_ctrl->eeprom_v4l2_subdev_ops = &msm_eeprom_subdev_ops; + e_ctrl->eeprom_mutex = &msm_eeprom_mutex; + client = &e_ctrl->i2c_client; + e_ctrl->is_supported = 0; + e_ctrl->userspace_probe = 0; + e_ctrl->cal_data.mapdata = NULL; + e_ctrl->cal_data.map = NULL; + + spi_client = kzalloc(sizeof(*spi_client), GFP_KERNEL); + if (!spi_client) { + pr_err("%s:%d kzalloc failed\n", __func__, __LINE__); + kfree(e_ctrl); + return -ENOMEM; + } + + rc = of_property_read_u32(spi->dev.of_node, "cell-index", + &e_ctrl->subdev_id); + CDBG("cell-index %d, rc %d\n", e_ctrl->subdev_id, rc); + if (rc < 0) { + pr_err("failed rc %d\n", rc); + return rc; + } + + eb_info = kzalloc(sizeof(*eb_info), GFP_KERNEL); + if (!eb_info) + goto spi_free; + e_ctrl->eboard_info = eb_info; + + rc = of_property_read_string(spi->dev.of_node, "qcom,eeprom-name", + &eb_info->eeprom_name); + CDBG("%s qcom,eeprom-name %s, rc %d\n", __func__, + eb_info->eeprom_name, rc); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + e_ctrl->userspace_probe = 1; + goto board_free; + } + + e_ctrl->eeprom_device_type = MSM_CAMERA_SPI_DEVICE; + client->spi_client = spi_client; + spi_client->spi_master = spi; + client->i2c_func_tbl = &msm_eeprom_spi_func_tbl; + client->addr_type = MSM_CAMERA_I2C_3B_ADDR; + + rc = msm_eeprom_cmm_dts(e_ctrl->eboard_info, spi->dev.of_node); + if (rc < 0) + CDBG("%s MM data miss:%d\n", __func__, __LINE__); + + power_info = &eb_info->power_info; + power_info->dev = &spi->dev; + + /*Get clocks information*/ + rc = msm_camera_get_clk_info(e_ctrl->pdev, + &power_info->clk_info, + &power_info->clk_ptr, + &power_info->clk_info_size); + if (rc < 0) { + pr_err("failed: msm_camera_get_clk_info rc %d", rc); + goto board_free; + } + + rc = msm_eeprom_get_dt_data(e_ctrl); + if (rc < 0) + goto board_free; + + /* set spi instruction info */ + spi_client->retry_delay = 1; + spi_client->retries = 0; + + rc = msm_eeprom_spi_parse_of(spi_client); + if (rc < 0) { + dev_err(&spi->dev, + "%s: Error parsing device properties\n", __func__); + goto board_free; + } + + if (e_ctrl->userspace_probe == 0) { + /* prepare memory buffer */ + rc = msm_eeprom_parse_memory_map(spi->dev.of_node, + &e_ctrl->cal_data); + if (rc < 0) + CDBG("%s: no cal memory map\n", __func__); + + /* power up eeprom for reading */ + rc = msm_camera_power_up(power_info, e_ctrl->eeprom_device_type, + &e_ctrl->i2c_client); + if (rc < 0) { + pr_err("failed rc %d\n", rc); + goto caldata_free; + } + + /* check eeprom id */ + rc = msm_eeprom_match_id(e_ctrl); + if (rc < 0) { + CDBG("%s: eeprom not matching %d\n", __func__, rc); + goto power_down; + } + /* read eeprom */ + if (e_ctrl->cal_data.map) { + rc = read_eeprom_memory(e_ctrl, &e_ctrl->cal_data); + if (rc < 0) { + pr_err("%s: read cal data failed\n", __func__); + goto power_down; + } + e_ctrl->is_supported |= msm_eeprom_match_crc( + &e_ctrl->cal_data); + } + + rc = msm_camera_power_down(power_info, + e_ctrl->eeprom_device_type, &e_ctrl->i2c_client); + if (rc < 0) { + pr_err("failed rc %d\n", rc); + goto caldata_free; + } + } else + e_ctrl->is_supported = 1; + + /* initiazlie subdev */ + v4l2_spi_subdev_init(&e_ctrl->msm_sd.sd, + e_ctrl->i2c_client.spi_client->spi_master, + e_ctrl->eeprom_v4l2_subdev_ops); + v4l2_set_subdevdata(&e_ctrl->msm_sd.sd, e_ctrl); + e_ctrl->msm_sd.sd.internal_ops = &msm_eeprom_internal_ops; + e_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + media_entity_init(&e_ctrl->msm_sd.sd.entity, 0, NULL, 0); + e_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + e_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_EEPROM; + msm_sd_register(&e_ctrl->msm_sd); + e_ctrl->is_supported = (e_ctrl->is_supported << 1) | 1; + CDBG("%s success result=%d supported=%x X\n", __func__, rc, + e_ctrl->is_supported); + + return 0; + +power_down: + msm_camera_power_down(power_info, e_ctrl->eeprom_device_type, + &e_ctrl->i2c_client); +caldata_free: + kfree(e_ctrl->cal_data.mapdata); + kfree(e_ctrl->cal_data.map); +board_free: + kfree(e_ctrl->eboard_info); +spi_free: + kfree(spi_client); + kfree(e_ctrl); + return rc; +} + +static int msm_eeprom_spi_probe(struct spi_device *spi) +{ + int irq, cs, cpha, cpol, cs_high; + + CDBG("%s\n", __func__); + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + spi_setup(spi); + + irq = spi->irq; + cs = spi->chip_select; + cpha = (spi->mode & SPI_CPHA) ? 1 : 0; + cpol = (spi->mode & SPI_CPOL) ? 1 : 0; + cs_high = (spi->mode & SPI_CS_HIGH) ? 1 : 0; + CDBG("%s: irq[%d] cs[%x] CPHA[%x] CPOL[%x] CS_HIGH[%x]\n", + __func__, irq, cs, cpha, cpol, cs_high); + CDBG("%s: max_speed[%u]\n", __func__, spi->max_speed_hz); + + return msm_eeprom_spi_setup(spi); +} + +static int msm_eeprom_spi_remove(struct spi_device *sdev) +{ + struct v4l2_subdev *sd = spi_get_drvdata(sdev); + struct msm_eeprom_ctrl_t *e_ctrl; + if (!sd) { + pr_err("%s: Subdevice is NULL\n", __func__); + return 0; + } + + e_ctrl = (struct msm_eeprom_ctrl_t *)v4l2_get_subdevdata(sd); + if (!e_ctrl) { + pr_err("%s: eeprom device is NULL\n", __func__); + return 0; + } + + if (!e_ctrl->eboard_info) { + pr_err("%s: eboard_info is NULL\n", __func__); + return 0; + } + msm_camera_put_clk_info(e_ctrl->pdev, + &e_ctrl->eboard_info->power_info.clk_info, + &e_ctrl->eboard_info->power_info.clk_ptr, + e_ctrl->eboard_info->power_info.clk_info_size); + + kfree(e_ctrl->i2c_client.spi_client); + kfree(e_ctrl->cal_data.mapdata); + kfree(e_ctrl->cal_data.map); + if (e_ctrl->eboard_info) { + kfree(e_ctrl->eboard_info->power_info.gpio_conf); + kfree(e_ctrl->eboard_info); + } + e_ctrl->cal_data.mapdata = NULL; + kfree(e_ctrl); + e_ctrl = NULL; + + return 0; +} + +#ifdef CONFIG_COMPAT +static void msm_eeprom_copy_power_settings_compat( + struct msm_sensor_power_setting_array *ps, + struct msm_sensor_power_setting_array32 *ps32) +{ + uint16_t i = 0; + + ps->size = ps32->size; + for (i = 0; i < ps32->size; i++) { + ps->power_setting_a[i].config_val = + ps32->power_setting_a[i].config_val; + ps->power_setting_a[i].delay = + ps32->power_setting_a[i].delay; + ps->power_setting_a[i].seq_type = + ps32->power_setting_a[i].seq_type; + ps->power_setting_a[i].seq_val = + ps32->power_setting_a[i].seq_val; + } + + ps->size_down = ps32->size_down; + for (i = 0; i < ps32->size_down; i++) { + ps->power_down_setting_a[i].config_val = + ps32->power_down_setting_a[i].config_val; + ps->power_down_setting_a[i].delay = + ps32->power_down_setting_a[i].delay; + ps->power_down_setting_a[i].seq_type = + ps32->power_down_setting_a[i].seq_type; + ps->power_down_setting_a[i].seq_val = + ps32->power_down_setting_a[i].seq_val; + } +} + +static int eeprom_config_read_cal_data32(struct msm_eeprom_ctrl_t *e_ctrl, + void __user *arg) +{ + int rc; + uint8_t *ptr_dest = NULL; + struct msm_eeprom_cfg_data32 *cdata32 = + (struct msm_eeprom_cfg_data32 *) arg; + struct msm_eeprom_cfg_data cdata; + + cdata.cfgtype = cdata32->cfgtype; + cdata.is_supported = cdata32->is_supported; + cdata.cfg.read_data.num_bytes = cdata32->cfg.read_data.num_bytes; + /* check range */ + if (cdata.cfg.read_data.num_bytes > + e_ctrl->cal_data.num_data) { + CDBG("%s: Invalid size. exp %u, req %u\n", __func__, + e_ctrl->cal_data.num_data, + cdata.cfg.read_data.num_bytes); + return -EINVAL; + } + if (!e_ctrl->cal_data.mapdata) + return -EFAULT; + + ptr_dest = (uint8_t *) compat_ptr(cdata32->cfg.read_data.dbuffer); + + rc = copy_to_user(ptr_dest, e_ctrl->cal_data.mapdata, + cdata.cfg.read_data.num_bytes); + + return rc; +} + +static int eeprom_init_config32(struct msm_eeprom_ctrl_t *e_ctrl, + void __user *argp) +{ + int rc = 0; + struct msm_eeprom_cfg_data32 *cdata32 = argp; + struct msm_sensor_power_setting_array *power_setting_array = NULL; + struct msm_sensor_power_setting_array32 *power_setting_array32 = NULL; + struct msm_camera_power_ctrl_t *power_info = NULL; + struct msm_eeprom_memory_map_array *mem_map_array = NULL; + + power_setting_array32 = + kzalloc(sizeof(struct msm_sensor_power_setting_array32), + GFP_KERNEL); + if (!power_setting_array32) { + pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__); + rc = -ENOMEM; + return rc; + } + power_setting_array = + kzalloc(sizeof(struct msm_sensor_power_setting_array), + GFP_KERNEL); + if (power_setting_array == NULL) { + pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__); + rc = -ENOMEM; + goto free_mem; + } + mem_map_array = + kzalloc(sizeof(struct msm_eeprom_memory_map_array), + GFP_KERNEL); + if (mem_map_array == NULL) { + pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__); + rc = -ENOMEM; + goto free_mem; + } + + if (copy_from_user(power_setting_array32, + (void *)compat_ptr(cdata32->cfg.eeprom_info. + power_setting_array), + sizeof(struct msm_sensor_power_setting_array32))) { + pr_err("%s:%d copy_from_user failed\n", + __func__, __LINE__); + goto free_mem; + } + CDBG("%s:%d Size of power setting array: %d", + __func__, __LINE__, power_setting_array32->size); + if (copy_from_user(mem_map_array, + (void *)compat_ptr(cdata32->cfg.eeprom_info.mem_map_array), + sizeof(struct msm_eeprom_memory_map_array))) { + pr_err("%s:%d copy_from_user failed for memory map\n", + __func__, __LINE__); + goto free_mem; + } + + power_info = &(e_ctrl->eboard_info->power_info); + + msm_eeprom_copy_power_settings_compat( + power_setting_array, + power_setting_array32); + + power_info->power_setting = + power_setting_array->power_setting_a; + power_info->power_down_setting = + power_setting_array->power_down_setting_a; + + power_info->power_setting_size = + power_setting_array->size; + power_info->power_down_setting_size = + power_setting_array->size_down; + + if ((power_info->power_setting_size > + MAX_POWER_CONFIG) || + (power_info->power_down_setting_size > + MAX_POWER_CONFIG) || + (!power_info->power_down_setting_size) || + (!power_info->power_setting_size)) { + rc = -EINVAL; + pr_err("%s:%d Invalid power setting size :%d, %d\n", + __func__, __LINE__, + power_info->power_setting_size, + power_info->power_down_setting_size); + goto free_mem; + } + + if (e_ctrl->i2c_client.cci_client) { + e_ctrl->i2c_client.cci_client->i2c_freq_mode = + cdata32->cfg.eeprom_info.i2c_freq_mode; + if (e_ctrl->i2c_client.cci_client->i2c_freq_mode > + I2C_MAX_MODES) { + pr_err("%s::%d Improper I2C Freq Mode\n", + __func__, __LINE__); + e_ctrl->i2c_client.cci_client->i2c_freq_mode = + I2C_STANDARD_MODE; + } + CDBG("%s:%d Not CCI probe", __func__, __LINE__); + } + /* Fill vreg power info and power up here */ + rc = msm_eeprom_power_up(e_ctrl, power_info); + if (rc < 0) { + pr_err("%s:%d Power Up failed for eeprom\n", + __func__, __LINE__); + goto free_mem; + } + + rc = eeprom_parse_memory_map(e_ctrl, mem_map_array); + if (rc < 0) { + pr_err("%s:%d memory map parse failed\n", + __func__, __LINE__); + goto free_mem; + } + + rc = msm_camera_power_down(power_info, + e_ctrl->eeprom_device_type, &e_ctrl->i2c_client); + if (rc < 0) + pr_err("%s:%d Power down failed rc %d\n", + __func__, __LINE__, rc); + +free_mem: + kfree(power_setting_array32); + kfree(power_setting_array); + kfree(mem_map_array); + power_setting_array32 = NULL; + power_setting_array = NULL; + mem_map_array = NULL; + return rc; +} + +static int msm_eeprom_config32(struct msm_eeprom_ctrl_t *e_ctrl, + void __user *argp) +{ + struct msm_eeprom_cfg_data32 *cdata = + (struct msm_eeprom_cfg_data32 *)argp; + int rc = 0; + + CDBG("%s E\n", __func__); + switch (cdata->cfgtype) { + case CFG_EEPROM_GET_INFO: + if (e_ctrl->userspace_probe == 1) { + pr_err("%s:%d Eeprom name should be module driver", + __func__, __LINE__); + rc = -EINVAL; + break; + } + CDBG("%s E CFG_EEPROM_GET_INFO\n", __func__); + cdata->is_supported = e_ctrl->is_supported; + memcpy(cdata->cfg.eeprom_name, + e_ctrl->eboard_info->eeprom_name, + sizeof(cdata->cfg.eeprom_name)); + break; + case CFG_EEPROM_GET_CAL_DATA: + CDBG("%s E CFG_EEPROM_GET_CAL_DATA\n", __func__); + cdata->cfg.get_data.num_bytes = + e_ctrl->cal_data.num_data; + break; + case CFG_EEPROM_READ_CAL_DATA: + CDBG("%s E CFG_EEPROM_READ_CAL_DATA\n", __func__); + rc = eeprom_config_read_cal_data32(e_ctrl, argp); + break; + case CFG_EEPROM_INIT: + if (e_ctrl->userspace_probe == 0) { + pr_err("%s:%d Eeprom already probed at kernel boot", + __func__, __LINE__); + rc = -EINVAL; + break; + } + if (e_ctrl->cal_data.num_data == 0) { + rc = eeprom_init_config32(e_ctrl, argp); + if (rc < 0) + pr_err("%s:%d Eeprom init failed\n", + __func__, __LINE__); + } else { + CDBG("%s:%d Already read eeprom\n", + __func__, __LINE__); + } + break; + default: + break; + } + + CDBG("%s X rc: %d\n", __func__, rc); + return rc; +} + +static long msm_eeprom_subdev_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct msm_eeprom_ctrl_t *e_ctrl = v4l2_get_subdevdata(sd); + void __user *argp = (void __user *)arg; + + CDBG("%s E\n", __func__); + CDBG("%s:%d a_ctrl %p argp %p\n", __func__, __LINE__, e_ctrl, argp); + switch (cmd) { + case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID: + return msm_eeprom_get_subdev_id(e_ctrl, argp); + case VIDIOC_MSM_EEPROM_CFG32: + return msm_eeprom_config32(e_ctrl, argp); + default: + return -ENOIOCTLCMD; + } + + CDBG("%s X\n", __func__); +} + +static long msm_eeprom_subdev_do_ioctl32( + struct file *file, unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + + return msm_eeprom_subdev_ioctl32(sd, cmd, arg); +} + +static long msm_eeprom_subdev_fops_ioctl32(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return video_usercopy(file, cmd, arg, msm_eeprom_subdev_do_ioctl32); +} + +#endif + +static int msm_eeprom_platform_probe(struct platform_device *pdev) +{ + int rc = 0; + int j = 0; + uint32_t temp; +#ifdef CONFIG_CAMERA_WT_FACTORY_SUPPORTED + static char module_id[20] = {0}; + int32_t module_id_index = 0x000C; +#endif + + struct msm_camera_cci_client *cci_client = NULL; + struct msm_eeprom_ctrl_t *e_ctrl = NULL; + struct msm_eeprom_board_info *eb_info = NULL; + struct device_node *of_node = pdev->dev.of_node; + struct msm_camera_power_ctrl_t *power_info = NULL; + + CDBG("%s E\n", __func__); + + e_ctrl = kzalloc(sizeof(*e_ctrl), GFP_KERNEL); + if (!e_ctrl) { + pr_err("%s:%d kzalloc failed\n", __func__, __LINE__); + return -ENOMEM; + } + e_ctrl->eeprom_v4l2_subdev_ops = &msm_eeprom_subdev_ops; + e_ctrl->eeprom_mutex = &msm_eeprom_mutex; + + e_ctrl->cal_data.mapdata = NULL; + e_ctrl->cal_data.map = NULL; + e_ctrl->userspace_probe = 0; + e_ctrl->is_supported = 0; + if (!of_node) { + pr_err("%s dev.of_node NULL\n", __func__); + rc = -EINVAL; + goto ectrl_free; + } + + /* Set platform device handle */ + e_ctrl->pdev = pdev; + /* Set device type as platform device */ + e_ctrl->eeprom_device_type = MSM_CAMERA_PLATFORM_DEVICE; + e_ctrl->i2c_client.i2c_func_tbl = &msm_eeprom_cci_func_tbl; + e_ctrl->i2c_client.cci_client = kzalloc(sizeof( + struct msm_camera_cci_client), GFP_KERNEL); + if (!e_ctrl->i2c_client.cci_client) { + pr_err("%s failed no memory\n", __func__); + rc = -ENOMEM; + goto ectrl_free; + } + + e_ctrl->eboard_info = kzalloc(sizeof( + struct msm_eeprom_board_info), GFP_KERNEL); + if (!e_ctrl->eboard_info) { + pr_err("%s failed line %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto cciclient_free; + } + + eb_info = e_ctrl->eboard_info; + power_info = &eb_info->power_info; + cci_client = e_ctrl->i2c_client.cci_client; + cci_client->cci_subdev = msm_cci_get_subdev(); + cci_client->retries = 3; + cci_client->id_map = 0; + power_info->dev = &pdev->dev; + + /*Get clocks information*/ + rc = msm_camera_get_clk_info(e_ctrl->pdev, + &power_info->clk_info, + &power_info->clk_ptr, + &power_info->clk_info_size); + if (rc < 0) { + pr_err("failed: msm_camera_get_clk_info rc %d", rc); + goto board_free; + } + + rc = of_property_read_u32(of_node, "cell-index", + &pdev->id); + CDBG("cell-index %d, rc %d\n", pdev->id, rc); + if (rc < 0) { + pr_err("failed rc %d\n", rc); + goto board_free; + } + e_ctrl->subdev_id = pdev->id; + + rc = of_property_read_u32(of_node, "qcom,cci-master", + &e_ctrl->cci_master); + CDBG("qcom,cci-master %d, rc %d\n", e_ctrl->cci_master, rc); + if (rc < 0) { + pr_err("%s failed rc %d\n", __func__, rc); + goto board_free; + } + cci_client->cci_i2c_master = e_ctrl->cci_master; + + rc = of_property_read_string(of_node, "qcom,eeprom-name", + &eb_info->eeprom_name); + CDBG("%s qcom,eeprom-name %s, rc %d\n", __func__, + eb_info->eeprom_name, rc); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + e_ctrl->userspace_probe = 1; + } + + rc = msm_eeprom_get_dt_data(e_ctrl); + if (rc < 0) + goto board_free; + + if (e_ctrl->userspace_probe == 0) { + rc = of_property_read_u32(of_node, "qcom,slave-addr", + &temp); + if (rc < 0) { + pr_err("%s failed rc %d\n", __func__, rc); + goto board_free; + } + + rc = of_property_read_u32(of_node, "qcom,i2c-freq-mode", + &e_ctrl->i2c_freq_mode); + CDBG("qcom,i2c_freq_mode %d, rc %d\n", + e_ctrl->i2c_freq_mode, rc); + if (rc < 0) { + pr_err("%s qcom,i2c-freq-mode read fail. Setting to 0 %d\n", + __func__, rc); + e_ctrl->i2c_freq_mode = 0; + } + if (e_ctrl->i2c_freq_mode >= I2C_MAX_MODES) { + pr_err("%s:%d invalid i2c_freq_mode = %d\n", + __func__, __LINE__, e_ctrl->i2c_freq_mode); + e_ctrl->i2c_freq_mode = 0; + } + eb_info->i2c_slaveaddr = temp; + CDBG("qcom,slave-addr = 0x%X\n", eb_info->i2c_slaveaddr); + eb_info->i2c_freq_mode = e_ctrl->i2c_freq_mode; + cci_client->i2c_freq_mode = e_ctrl->i2c_freq_mode; + cci_client->sid = eb_info->i2c_slaveaddr >> 1; + + rc = msm_eeprom_parse_memory_map(of_node, &e_ctrl->cal_data); + if (rc < 0) + goto board_free; + + rc = msm_camera_power_up(power_info, e_ctrl->eeprom_device_type, + &e_ctrl->i2c_client); + if (rc) { + pr_err("failed rc %d\n", rc); + goto memdata_free; + } + rc = read_eeprom_memory(e_ctrl, &e_ctrl->cal_data); + if (rc < 0) { + pr_err("%s read_eeprom_memory failed\n", __func__); + goto power_down; + } +#ifdef CONFIG_CAMERA_WT_FACTORY_SUPPORTED + else { + if (pdev->id == 1) { + if (((e_ctrl->cal_data.mapdata[0] >> 6) & 0x3) == 1) + module_id_index = 0x3205 - 0x3204; + else if (((e_ctrl->cal_data.mapdata[0] >> 4) & 0x3) == 1) + module_id_index = 0x3211 - 0x3204; + sprintf(module_id, "%x", e_ctrl->cal_data.mapdata[module_id_index]); + hardwareinfo_set_prop(HARDWARE_FRONT_CAM_MOUDULE_ID, module_id); + } else if (pdev->id == 0) { + module_id_index = 0x000C; + sprintf(module_id, "%x", e_ctrl->cal_data.mapdata[module_id_index]); + hardwareinfo_set_prop(HARDWARE_BACK_CAM_MOUDULE_ID, module_id); + } + + CDBG("module_id: %s index: 0x%x\n", module_id, module_id_index); + } +#endif + for (j = 0; j < e_ctrl->cal_data.num_data; j++) + CDBG("memory_data[%d] = 0x%X\n", j, + e_ctrl->cal_data.mapdata[j]); + + e_ctrl->is_supported |= msm_eeprom_match_crc(&e_ctrl->cal_data); + + rc = msm_camera_power_down(power_info, + e_ctrl->eeprom_device_type, &e_ctrl->i2c_client); + if (rc) { + pr_err("failed rc %d\n", rc); + goto memdata_free; + } + } else + e_ctrl->is_supported = 1; + + v4l2_subdev_init(&e_ctrl->msm_sd.sd, + e_ctrl->eeprom_v4l2_subdev_ops); + v4l2_set_subdevdata(&e_ctrl->msm_sd.sd, e_ctrl); + platform_set_drvdata(pdev, &e_ctrl->msm_sd.sd); + e_ctrl->msm_sd.sd.internal_ops = &msm_eeprom_internal_ops; + e_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(e_ctrl->msm_sd.sd.name, + ARRAY_SIZE(e_ctrl->msm_sd.sd.name), "msm_eeprom"); + media_entity_init(&e_ctrl->msm_sd.sd.entity, 0, NULL, 0); + e_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + e_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_EEPROM; + msm_sd_register(&e_ctrl->msm_sd); + +#ifdef CONFIG_COMPAT + msm_cam_copy_v4l2_subdev_fops(&msm_eeprom_v4l2_subdev_fops); + msm_eeprom_v4l2_subdev_fops.compat_ioctl32 = + msm_eeprom_subdev_fops_ioctl32; + e_ctrl->msm_sd.sd.devnode->fops = &msm_eeprom_v4l2_subdev_fops; +#endif + + e_ctrl->is_supported = (e_ctrl->is_supported << 1) | 1; + CDBG("%s X\n", __func__); + return rc; + +power_down: + msm_camera_power_down(power_info, e_ctrl->eeprom_device_type, + &e_ctrl->i2c_client); +memdata_free: + kfree(e_ctrl->cal_data.mapdata); + kfree(e_ctrl->cal_data.map); +board_free: + kfree(e_ctrl->eboard_info); +cciclient_free: + kfree(e_ctrl->i2c_client.cci_client); +ectrl_free: + kfree(e_ctrl); + return rc; +} + +static int msm_eeprom_platform_remove(struct platform_device *pdev) +{ + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct msm_eeprom_ctrl_t *e_ctrl; + if (!sd) { + pr_err("%s: Subdevice is NULL\n", __func__); + return 0; + } + + e_ctrl = (struct msm_eeprom_ctrl_t *)v4l2_get_subdevdata(sd); + if (!e_ctrl) { + pr_err("%s: eeprom device is NULL\n", __func__); + return 0; + } + + if (!e_ctrl->eboard_info) { + pr_err("%s: eboard_info is NULL\n", __func__); + return 0; + } + msm_camera_put_clk_info(e_ctrl->pdev, + &e_ctrl->eboard_info->power_info.clk_info, + &e_ctrl->eboard_info->power_info.clk_ptr, + e_ctrl->eboard_info->power_info.clk_info_size); + + kfree(e_ctrl->i2c_client.cci_client); + kfree(e_ctrl->cal_data.mapdata); + kfree(e_ctrl->cal_data.map); + if (e_ctrl->eboard_info) { + kfree(e_ctrl->eboard_info->power_info.gpio_conf); + kfree(e_ctrl->eboard_info); + } + kfree(e_ctrl); + return 0; +} + +static const struct of_device_id msm_eeprom_dt_match[] = { + { .compatible = "qcom,eeprom" }, + { } +}; + +MODULE_DEVICE_TABLE(of, msm_eeprom_dt_match); + +static struct platform_driver msm_eeprom_platform_driver = { + .driver = { + .name = "qcom,eeprom", + .owner = THIS_MODULE, + .of_match_table = msm_eeprom_dt_match, + }, + .probe = msm_eeprom_platform_probe, + .remove = msm_eeprom_platform_remove, +}; + +static const struct i2c_device_id msm_eeprom_i2c_id[] = { + { "msm_eeprom", (kernel_ulong_t)NULL}, + { } +}; + +static struct i2c_driver msm_eeprom_i2c_driver = { + .id_table = msm_eeprom_i2c_id, + .probe = msm_eeprom_i2c_probe, + .remove = msm_eeprom_i2c_remove, + .driver = { + .name = "msm_eeprom", + }, +}; + +static struct spi_driver msm_eeprom_spi_driver = { + .driver = { + .name = "qcom_eeprom", + .owner = THIS_MODULE, + .of_match_table = msm_eeprom_dt_match, + }, + .probe = msm_eeprom_spi_probe, + .remove = msm_eeprom_spi_remove, +}; + +static int __init msm_eeprom_init_module(void) +{ + int rc = 0; + CDBG("%s E\n", __func__); + rc = platform_driver_register(&msm_eeprom_platform_driver); + CDBG("%s:%d platform rc %d\n", __func__, __LINE__, rc); + rc = spi_register_driver(&msm_eeprom_spi_driver); + CDBG("%s:%d spi rc %d\n", __func__, __LINE__, rc); + return i2c_add_driver(&msm_eeprom_i2c_driver); +} + +static void __exit msm_eeprom_exit_module(void) +{ + platform_driver_unregister(&msm_eeprom_platform_driver); + spi_unregister_driver(&msm_eeprom_spi_driver); + i2c_del_driver(&msm_eeprom_i2c_driver); +} + +module_init(msm_eeprom_init_module); +module_exit(msm_eeprom_exit_module); +MODULE_DESCRIPTION("MSM EEPROM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/camera/sensor/eeprom/msm_eeprom.h b/camera/sensor/eeprom/msm_eeprom.h new file mode 100644 index 00000000..dae77655 --- /dev/null +++ b/camera/sensor/eeprom/msm_eeprom.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2011-2015, 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_EEPROM_H +#define MSM_EEPROM_H + +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <lenovo_soc/qcom/camera2.h> +#include <media/v4l2-subdev.h> +#include <lenovo_media/msmb_camera.h> +#include "msm_camera_i2c.h" +#include "msm_camera_spi.h" +#include "msm_camera_io_util.h" +#include "msm_camera_dt_util.h" + +struct msm_eeprom_ctrl_t; + +#define DEFINE_MSM_MUTEX(mutexname) \ + static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) + +#define PROPERTY_MAXSIZE 32 + +struct msm_eeprom_ctrl_t { + struct platform_device *pdev; + struct mutex *eeprom_mutex; + + struct v4l2_subdev sdev; + struct v4l2_subdev_ops *eeprom_v4l2_subdev_ops; + enum msm_camera_device_type_t eeprom_device_type; + struct msm_sd_subdev msm_sd; + enum cci_i2c_master_t cci_master; + enum i2c_freq_mode_t i2c_freq_mode; + + struct msm_camera_i2c_client i2c_client; + struct msm_eeprom_board_info *eboard_info; + uint32_t subdev_id; + int32_t userspace_probe; + struct msm_eeprom_memory_block_t cal_data; + uint8_t is_supported; +}; + +#endif diff --git a/camera/sensor/flash/Makefile b/camera/sensor/flash/Makefile new file mode 100644 index 00000000..3ffd2060 --- /dev/null +++ b/camera/sensor/flash/Makefile @@ -0,0 +1,5 @@ +ccflags-y += -Icamera/sensor/cci +ccflags-y += -Icamera +ccflags-y += -Icamera/common +ccflags-y += -Icamera/sensor/io +obj-$(CONFIG_MSMB_CAMERA) += msm_flash.o diff --git a/camera/sensor/flash/msm_flash.c b/camera/sensor/flash/msm_flash.c new file mode 100644 index 00000000..20f216c7 --- /dev/null +++ b/camera/sensor/flash/msm_flash.c @@ -0,0 +1,1222 @@ +/* Copyright (c) 2009-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. + * + */ + +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include <linux/module.h> +#include <linux/of_gpio.h> +#include "msm_flash.h" +#include "msm_camera_dt_util.h" +#include "msm_cci.h" + +#undef CDBG +#define CDBG(fmt, args...) pr_err(fmt, ##args) + +DEFINE_MSM_MUTEX(msm_flash_mutex); + +static struct v4l2_file_operations msm_flash_v4l2_subdev_fops; +static struct led_trigger *torch_trigger; + +static const struct of_device_id msm_flash_dt_match[] = { + {.compatible = "qcom,camera-flash", .data = NULL}, + {} +}; + +static struct msm_flash_table msm_i2c_flash_table; +static struct msm_flash_table msm_gpio_flash_table; +static struct msm_flash_table msm_pmic_flash_table; + +static struct msm_flash_table *flash_table[] = { + &msm_i2c_flash_table, + &msm_gpio_flash_table, + &msm_pmic_flash_table +}; + +static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = { + .i2c_read = msm_camera_cci_i2c_read, + .i2c_read_seq = msm_camera_cci_i2c_read_seq, + .i2c_write = msm_camera_cci_i2c_write, + .i2c_write_table = msm_camera_cci_i2c_write_table, + .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_cci_i2c_write_table_w_microdelay, + .i2c_util = msm_sensor_cci_i2c_util, + .i2c_poll = msm_camera_cci_i2c_poll, +}; + +void msm_torch_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + if (!torch_trigger) { + pr_err("No torch trigger found, can't set brightness\n"); + return; + } + + led_trigger_event(torch_trigger, value); +}; + +static struct led_classdev msm_torch_led[MAX_LED_TRIGGERS] = { + { + .name = "torch-light0", + .brightness_set = msm_torch_brightness_set, + .brightness = LED_OFF, + }, + { + .name = "torch-light1", + .brightness_set = msm_torch_brightness_set, + .brightness = LED_OFF, + }, + { + .name = "torch-light2", + .brightness_set = msm_torch_brightness_set, + .brightness = LED_OFF, + }, +}; + +static int32_t msm_torch_create_classdev(struct platform_device *pdev, + void *data) +{ + int32_t rc = 0; + int32_t i = 0; + struct msm_flash_ctrl_t *fctrl = + (struct msm_flash_ctrl_t *)data; + + if (!fctrl) { + pr_err("Invalid fctrl\n"); + return -EINVAL; + } + + for (i = 0; i < fctrl->torch_num_sources; i++) { + if (fctrl->torch_trigger[i]) { + torch_trigger = fctrl->torch_trigger[i]; + CDBG("%s:%d msm_torch_brightness_set for torch %d", + __func__, __LINE__, i); + msm_torch_brightness_set(&msm_torch_led[i], + LED_OFF); + + rc = led_classdev_register(&pdev->dev, + &msm_torch_led[i]); + if (rc) { + pr_err("Failed to register %d led dev. rc = %d\n", + i, rc); + return rc; + } + } else { + pr_err("Invalid fctrl->torch_trigger[%d]\n", i); + return -EINVAL; + } + } + + return 0; +}; + +static int32_t msm_flash_get_subdev_id( + struct msm_flash_ctrl_t *flash_ctrl, void *arg) +{ + uint32_t *subdev_id = (uint32_t *)arg; + CDBG("Enter\n"); + if (!subdev_id) { + pr_err("failed\n"); + return -EINVAL; + } + if (flash_ctrl->flash_device_type == MSM_CAMERA_PLATFORM_DEVICE) + *subdev_id = flash_ctrl->pdev->id; + else + *subdev_id = flash_ctrl->subdev_id; + + CDBG("subdev_id %d\n", *subdev_id); + CDBG("Exit\n"); + return 0; +} + +static int32_t msm_flash_i2c_write_table( + struct msm_flash_ctrl_t *flash_ctrl, + struct msm_camera_i2c_reg_setting_array *settings) +{ + struct msm_camera_i2c_reg_setting conf_array; + + conf_array.addr_type = settings->addr_type; + conf_array.data_type = settings->data_type; + conf_array.delay = settings->delay; + conf_array.reg_setting = settings->reg_setting_a; + conf_array.size = settings->size; + flash_ctrl->flash_i2c_client.addr_type = conf_array.addr_type; + + return flash_ctrl->flash_i2c_client.i2c_func_tbl->i2c_write_table( + &flash_ctrl->flash_i2c_client, &conf_array); +} + +#ifdef CONFIG_COMPAT +static void msm_flash_copy_power_settings_compat( + struct msm_sensor_power_setting *ps, + struct msm_sensor_power_setting32 *ps32, uint32_t size) +{ + uint16_t i = 0; + + for (i = 0; i < size; i++) { + ps[i].config_val = ps32[i].config_val; + ps[i].delay = ps32[i].delay; + ps[i].seq_type = ps32[i].seq_type; + ps[i].seq_val = ps32[i].seq_val; + } +} +#endif + +static int32_t msm_flash_i2c_init( + struct msm_flash_ctrl_t *flash_ctrl, + struct msm_flash_cfg_data_t *flash_data) +{ + int32_t rc = 0; + struct msm_flash_init_info_t *flash_init_info = + flash_data->cfg.flash_init_info; + struct msm_camera_i2c_reg_setting_array *settings = NULL; + struct msm_camera_cci_client *cci_client = NULL; +#ifdef CONFIG_COMPAT + struct msm_sensor_power_setting_array32 *power_setting_array32 = NULL; +#endif + if (!flash_init_info || !flash_init_info->power_setting_array) { + pr_err("%s:%d failed: Null pointer\n", __func__, __LINE__); + return -EFAULT; + } + +#ifdef CONFIG_COMPAT + if (is_compat_task()) { + power_setting_array32 = kzalloc( + sizeof(struct msm_sensor_power_setting_array32), + GFP_KERNEL); + if (!power_setting_array32) { + pr_err("%s mem allocation failed %d\n", + __func__, __LINE__); + return -ENOMEM; + } + + if (copy_from_user(power_setting_array32, + (void *)flash_init_info->power_setting_array, + sizeof(struct msm_sensor_power_setting_array32))) { + pr_err("%s copy_from_user failed %d\n", + __func__, __LINE__); + kfree(power_setting_array32); + return -EFAULT; + } + + flash_ctrl->power_setting_array.size = + power_setting_array32->size; + flash_ctrl->power_setting_array.size_down = + power_setting_array32->size_down; + flash_ctrl->power_setting_array.power_down_setting = + compat_ptr(power_setting_array32->power_down_setting); + flash_ctrl->power_setting_array.power_setting = + compat_ptr(power_setting_array32->power_setting); + + /* Validate power_up array size and power_down array size */ + if ((!flash_ctrl->power_setting_array.size) || + (flash_ctrl->power_setting_array.size > + MAX_POWER_CONFIG) || + (!flash_ctrl->power_setting_array.size_down) || + (flash_ctrl->power_setting_array.size_down > + MAX_POWER_CONFIG)) { + + pr_err("failed: invalid size %d, size_down %d", + flash_ctrl->power_setting_array.size, + flash_ctrl->power_setting_array.size_down); + kfree(power_setting_array32); + power_setting_array32 = NULL; + return -EINVAL; + } + /* Copy the settings from compat struct to regular struct */ + msm_flash_copy_power_settings_compat( + flash_ctrl->power_setting_array.power_setting_a, + power_setting_array32->power_setting_a, + flash_ctrl->power_setting_array.size); + + msm_flash_copy_power_settings_compat( + flash_ctrl->power_setting_array.power_down_setting_a, + power_setting_array32->power_down_setting_a, + flash_ctrl->power_setting_array.size_down); + } else +#endif + if (copy_from_user(&flash_ctrl->power_setting_array, + (void *)flash_init_info->power_setting_array, + sizeof(struct msm_sensor_power_setting_array))) { + pr_err("%s copy_from_user failed %d\n", __func__, __LINE__); + return -EFAULT; + } + + if (flash_ctrl->flash_device_type == MSM_CAMERA_PLATFORM_DEVICE) { + cci_client = flash_ctrl->flash_i2c_client.cci_client; + cci_client->sid = flash_init_info->slave_addr >> 1; + cci_client->retries = 3; + cci_client->id_map = 0; + cci_client->i2c_freq_mode = flash_init_info->i2c_freq_mode; + } + + flash_ctrl->power_info.power_setting = + flash_ctrl->power_setting_array.power_setting_a; + flash_ctrl->power_info.power_down_setting = + flash_ctrl->power_setting_array.power_down_setting_a; + flash_ctrl->power_info.power_setting_size = + flash_ctrl->power_setting_array.size; + flash_ctrl->power_info.power_down_setting_size = + flash_ctrl->power_setting_array.size_down; + + rc = msm_camera_fill_vreg_params( + flash_ctrl->power_info.cam_vreg, + flash_ctrl->power_info.num_vreg, + flash_ctrl->power_info.power_setting, + flash_ctrl->power_info.power_setting_size); + if (rc < 0) { + pr_err("%s:%d failed in camera_fill_vreg_params rc %d", + __func__, __LINE__, rc); + return rc; + } + + /* Parse and fill vreg params for powerdown settings*/ + rc = msm_camera_fill_vreg_params( + flash_ctrl->power_info.cam_vreg, + flash_ctrl->power_info.num_vreg, + flash_ctrl->power_info.power_down_setting, + flash_ctrl->power_info.power_down_setting_size); + if (rc < 0) { + pr_err("%s:%d failed msm_camera_fill_vreg_params for PDOWN rc %d", + __func__, __LINE__, rc); + return rc; + } + + rc = msm_camera_power_up(&flash_ctrl->power_info, + flash_ctrl->flash_device_type, + &flash_ctrl->flash_i2c_client); + if (rc < 0) { + pr_err("%s msm_camera_power_up failed %d\n", + __func__, __LINE__); + goto msm_flash_i2c_init_fail; + } + + if (flash_data->cfg.flash_init_info->settings) { + settings = kzalloc(sizeof( + struct msm_camera_i2c_reg_setting_array), GFP_KERNEL); + if (!settings) { + pr_err("%s mem allocation failed %d\n", + __func__, __LINE__); + return -ENOMEM; + } + + if (copy_from_user(settings, (void *)flash_init_info->settings, + sizeof(struct msm_camera_i2c_reg_setting_array))) { + kfree(settings); + pr_err("%s copy_from_user failed %d\n", + __func__, __LINE__); + return -EFAULT; + } + + rc = msm_flash_i2c_write_table(flash_ctrl, settings); + kfree(settings); + + if (rc < 0) { + pr_err("%s:%d msm_flash_i2c_write_table rc %d failed\n", + __func__, __LINE__, rc); + } + } + + return 0; + +msm_flash_i2c_init_fail: + return rc; +} + +static int32_t msm_flash_gpio_init( + struct msm_flash_ctrl_t *flash_ctrl, + struct msm_flash_cfg_data_t *flash_data) +{ + int32_t i = 0; + int32_t rc = 0; + + CDBG("Enter"); + for (i = 0; i < flash_ctrl->flash_num_sources; i++) + flash_ctrl->flash_op_current[i] = LED_FULL; + + for (i = 0; i < flash_ctrl->torch_num_sources; i++) + flash_ctrl->torch_op_current[i] = LED_HALF; + + for (i = 0; i < flash_ctrl->torch_num_sources; i++) { + if (!flash_ctrl->torch_trigger[i]) { + if (i < flash_ctrl->flash_num_sources) + flash_ctrl->torch_trigger[i] = + flash_ctrl->flash_trigger[i]; + else + flash_ctrl->torch_trigger[i] = + flash_ctrl->flash_trigger[ + flash_ctrl->flash_num_sources - 1]; + } + } + + rc = flash_ctrl->func_tbl->camera_flash_off(flash_ctrl, flash_data); + + CDBG("Exit"); + return rc; +} + +static int32_t msm_flash_i2c_release( + struct msm_flash_ctrl_t *flash_ctrl) +{ + int32_t rc = 0; + + if (!(&flash_ctrl->power_info) || !(&flash_ctrl->flash_i2c_client)) { + pr_err("%s:%d failed: %p %p\n", + __func__, __LINE__, &flash_ctrl->power_info, + &flash_ctrl->flash_i2c_client); + return -EINVAL; + } + + rc = msm_camera_power_down(&flash_ctrl->power_info, + flash_ctrl->flash_device_type, + &flash_ctrl->flash_i2c_client); + if (rc < 0) { + pr_err("%s msm_camera_power_down failed %d\n", + __func__, __LINE__); + return -EINVAL; + } + flash_ctrl->flash_state = MSM_CAMERA_FLASH_RELEASE; + return 0; +} + +static int32_t msm_flash_off(struct msm_flash_ctrl_t *flash_ctrl, + struct msm_flash_cfg_data_t *flash_data) +{ + int32_t i = 0; + + CDBG("Enter\n"); + + for (i = 0; i < flash_ctrl->flash_num_sources; i++) + if (flash_ctrl->flash_trigger[i]) + led_trigger_event(flash_ctrl->flash_trigger[i], 0); + + for (i = 0; i < flash_ctrl->torch_num_sources; i++) + if (flash_ctrl->torch_trigger[i]) + led_trigger_event(flash_ctrl->torch_trigger[i], 0); + if (flash_ctrl->switch_trigger) + led_trigger_event(flash_ctrl->switch_trigger, 0); + + CDBG("Exit\n"); + return 0; +} +static int32_t msm_flash_i2c_read_setting_array( + struct msm_flash_ctrl_t *flash_ctrl, + struct msm_flash_cfg_data_t *flash_data) +{ + if (!flash_data->cfg.read_config) { + pr_err("%s:%d failed: Null pointer\n", __func__, __LINE__); + return -EFAULT; + } + + return flash_ctrl->flash_i2c_client.i2c_func_tbl->i2c_read( + &flash_ctrl->flash_i2c_client, + flash_data->cfg.read_config->reg_addr, + &flash_data->cfg.read_config->data, + MSM_CAMERA_I2C_BYTE_DATA); +} + +static int32_t msm_flash_i2c_write_setting_array( + struct msm_flash_ctrl_t *flash_ctrl, + struct msm_flash_cfg_data_t *flash_data) +{ + int32_t rc = 0; + struct msm_camera_i2c_reg_setting_array *settings = NULL; + + if (!flash_data->cfg.settings) { + pr_err("%s:%d failed: Null pointer\n", __func__, __LINE__); + return -EFAULT; + } + + settings = kzalloc(sizeof(struct msm_camera_i2c_reg_setting_array), + GFP_KERNEL); + if (!settings) { + pr_err("%s mem allocation failed %d\n", __func__, __LINE__); + return -ENOMEM; + } + + if (copy_from_user(settings, (void *)flash_data->cfg.settings, + sizeof(struct msm_camera_i2c_reg_setting_array))) { + kfree(settings); + pr_err("%s copy_from_user failed %d\n", __func__, __LINE__); + return -EFAULT; + } + + rc = msm_flash_i2c_write_table(flash_ctrl, settings); + kfree(settings); + + if (rc < 0) { + pr_err("%s:%d msm_flash_i2c_write_table rc = %d failed\n", + __func__, __LINE__, rc); + } + return rc; +} + +static int32_t msm_flash_init( + struct msm_flash_ctrl_t *flash_ctrl, + struct msm_flash_cfg_data_t *flash_data) +{ + uint32_t i = 0; + int32_t rc = -EFAULT; + enum msm_flash_driver_type flash_driver_type = FLASH_DRIVER_DEFAULT; + + CDBG("Enter"); + + if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT) { + pr_err("%s:%d Invalid flash state = %d", + __func__, __LINE__, flash_ctrl->flash_state); + return 0; + } + + if (flash_data->cfg.flash_init_info->flash_driver_type == + FLASH_DRIVER_DEFAULT) { + flash_driver_type = flash_ctrl->flash_driver_type; + for (i = 0; i < MAX_LED_TRIGGERS; i++) { + flash_data->flash_current[i] = + flash_ctrl->flash_max_current[i]; + flash_data->flash_duration[i] = + flash_ctrl->flash_max_duration[i]; + } + } else if (flash_data->cfg.flash_init_info->flash_driver_type == + flash_ctrl->flash_driver_type) { + flash_driver_type = flash_ctrl->flash_driver_type; + for (i = 0; i < MAX_LED_TRIGGERS; i++) { + flash_ctrl->flash_max_current[i] = + flash_data->flash_current[i]; + flash_ctrl->flash_max_duration[i] = + flash_data->flash_duration[i]; + } + } + + if (flash_driver_type == FLASH_DRIVER_DEFAULT) { + pr_err("%s:%d invalid flash_driver_type", __func__, __LINE__); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(flash_table); i++) { + if (flash_driver_type == flash_table[i]->flash_driver_type) { + flash_ctrl->func_tbl = &flash_table[i]->func_tbl; + rc = 0; + } + } + + if (rc < 0) { + pr_err("%s:%d failed invalid flash_driver_type %d\n", + __func__, __LINE__, + flash_data->cfg.flash_init_info->flash_driver_type); + } + + if (flash_ctrl->func_tbl->camera_flash_init) { + rc = flash_ctrl->func_tbl->camera_flash_init( + flash_ctrl, flash_data); + if (rc < 0) { + pr_err("%s:%d camera_flash_init failed rc = %d", + __func__, __LINE__, rc); + return rc; + } + } + + flash_ctrl->flash_state = MSM_CAMERA_FLASH_INIT; + + CDBG("Exit"); + return 0; +} + +static int32_t msm_flash_low( + struct msm_flash_ctrl_t *flash_ctrl, + struct msm_flash_cfg_data_t *flash_data) +{ + uint32_t curr = 0, max_current = 0; + int32_t i = 0; + + CDBG("Enter\n"); + /* Turn off flash triggers */ + for (i = 0; i < flash_ctrl->flash_num_sources; i++) + if (flash_ctrl->flash_trigger[i]) + led_trigger_event(flash_ctrl->flash_trigger[i], 0); + + /* Turn on flash triggers */ + for (i = 0; i < flash_ctrl->torch_num_sources; i++) { + if (flash_ctrl->torch_trigger[i]) { + max_current = flash_ctrl->torch_max_current[i]; + if (flash_data->flash_current[i] >= 0 && + flash_data->flash_current[i] <= + max_current) { + curr = flash_data->flash_current[i]; + } else { + curr = flash_ctrl->torch_op_current[i]; + CDBG("LED current clamped to %d\n", + curr); + } + CDBG("low_flash_current[%d] = %d", i, curr); + led_trigger_event(flash_ctrl->torch_trigger[i], + curr); + } + } + if (flash_ctrl->switch_trigger) + led_trigger_event(flash_ctrl->switch_trigger, 1); + CDBG("Exit\n"); + return 0; +} + +static int32_t msm_flash_high( + struct msm_flash_ctrl_t *flash_ctrl, + struct msm_flash_cfg_data_t *flash_data) +{ + int32_t curr = 0; + int32_t max_current = 0; + int32_t i = 0; + + /* Turn off torch triggers */ + for (i = 0; i < flash_ctrl->torch_num_sources; i++) + if (flash_ctrl->torch_trigger[i]) + led_trigger_event(flash_ctrl->torch_trigger[i], 0); + + /* Turn on flash triggers */ + for (i = 0; i < flash_ctrl->flash_num_sources; i++) { + if (flash_ctrl->flash_trigger[i]) { + max_current = flash_ctrl->flash_max_current[i]; + if (flash_data->flash_current[i] >= 0 && + flash_data->flash_current[i] <= + max_current) { + curr = flash_data->flash_current[i]; + } else { + curr = flash_ctrl->flash_op_current[i]; + CDBG("LED flash_current[%d] clamped %d\n", + i, curr); + } + CDBG("high_flash_current[%d] = %d", i, curr); + led_trigger_event(flash_ctrl->flash_trigger[i], + curr); + } + } + if (flash_ctrl->switch_trigger) + led_trigger_event(flash_ctrl->switch_trigger, 1); + return 0; +} + +static int32_t msm_flash_release( + struct msm_flash_ctrl_t *flash_ctrl) +{ + int32_t rc = 0; + if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_RELEASE) { + pr_err("%s:%d Invalid flash state = %d", + __func__, __LINE__, flash_ctrl->flash_state); + return 0; + } + + rc = flash_ctrl->func_tbl->camera_flash_off(flash_ctrl, NULL); + if (rc < 0) { + pr_err("%s:%d camera_flash_init failed rc = %d", + __func__, __LINE__, rc); + return rc; + } + flash_ctrl->flash_state = MSM_CAMERA_FLASH_RELEASE; + return 0; +} + +static int32_t msm_flash_config(struct msm_flash_ctrl_t *flash_ctrl, + void __user *argp) +{ + int32_t rc = -EINVAL; + struct msm_flash_cfg_data_t *flash_data = + (struct msm_flash_cfg_data_t *) argp; + + mutex_lock(flash_ctrl->flash_mutex); + + CDBG("Enter %s type %d\n", __func__, flash_data->cfg_type); + + switch (flash_data->cfg_type) { + case CFG_FLASH_INIT: + rc = msm_flash_init(flash_ctrl, flash_data); + break; + case CFG_FLASH_RELEASE: + if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT) + rc = flash_ctrl->func_tbl->camera_flash_release( + flash_ctrl); + break; + case CFG_FLASH_OFF: + if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT) + rc = flash_ctrl->func_tbl->camera_flash_off( + flash_ctrl, flash_data); + break; + case CFG_FLASH_LOW: + if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT) + rc = flash_ctrl->func_tbl->camera_flash_low( + flash_ctrl, flash_data); + break; + case CFG_FLASH_HIGH: + if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT) + rc = flash_ctrl->func_tbl->camera_flash_high( + flash_ctrl, flash_data); + break; + case CFG_FLASH_READ_I2C: + if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT) + rc = flash_ctrl->func_tbl->camera_flash_read( + flash_ctrl, flash_data); + break; + case CFG_FLASH_WRITE_I2C: + if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT) + rc = flash_ctrl->func_tbl->camera_flash_write( + flash_ctrl, flash_data); + break; + default: + rc = -EFAULT; + break; + } + + mutex_unlock(flash_ctrl->flash_mutex); + + CDBG("Exit %s type %d\n", __func__, flash_data->cfg_type); + + return rc; +} + +static long msm_flash_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct msm_flash_ctrl_t *fctrl = NULL; + void __user *argp = (void __user *)arg; + + CDBG("Enter\n"); + + if (!sd) { + pr_err("sd NULL\n"); + return -EINVAL; + } + fctrl = v4l2_get_subdevdata(sd); + if (!fctrl) { + pr_err("fctrl NULL\n"); + return -EINVAL; + } + switch (cmd) { + case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID: + return msm_flash_get_subdev_id(fctrl, argp); + case VIDIOC_MSM_FLASH_CFG: + return msm_flash_config(fctrl, argp); + case MSM_SD_NOTIFY_FREEZE: + return 0; + case MSM_SD_UNNOTIFY_FREEZE: + return 0; + case MSM_SD_SHUTDOWN: + if (!fctrl->func_tbl) { + pr_err("fctrl->func_tbl NULL\n"); + return -EINVAL; + } else { + return fctrl->func_tbl->camera_flash_release(fctrl); + } + default: + pr_err_ratelimited("invalid cmd %d\n", cmd); + return -ENOIOCTLCMD; + } + CDBG("Exit\n"); +} + +static struct v4l2_subdev_core_ops msm_flash_subdev_core_ops = { + .ioctl = msm_flash_subdev_ioctl, +}; + +static struct v4l2_subdev_ops msm_flash_subdev_ops = { + .core = &msm_flash_subdev_core_ops, +}; + +static const struct v4l2_subdev_internal_ops msm_flash_internal_ops; + +static int32_t msm_flash_get_pmic_source_info( + struct device_node *of_node, + struct msm_flash_ctrl_t *fctrl) +{ + int32_t rc = 0; + uint32_t count = 0, i = 0; + struct device_node *flash_src_node = NULL; + struct device_node *torch_src_node = NULL; + struct device_node *switch_src_node = NULL; + + switch_src_node = of_parse_phandle(of_node, "qcom,switch-source", 0); + if (!switch_src_node) { + CDBG("%s:%d switch_src_node NULL\n", __func__, __LINE__); + } else { + rc = of_property_read_string(switch_src_node, + "qcom,default-led-trigger", + &fctrl->switch_trigger_name); + if (rc < 0) { + rc = of_property_read_string(switch_src_node, + "linux,default-trigger", + &fctrl->switch_trigger_name); + if (rc < 0) + pr_err("default-trigger read failed\n"); + } + of_node_put(switch_src_node); + switch_src_node = NULL; + if (!rc) { + CDBG("switch trigger %s\n", + fctrl->switch_trigger_name); + led_trigger_register_simple( + fctrl->switch_trigger_name, + &fctrl->switch_trigger); + } + } + + if (of_get_property(of_node, "qcom,flash-source", &count)) { + count /= sizeof(uint32_t); + CDBG("count %d\n", count); + if (count > MAX_LED_TRIGGERS) { + pr_err("invalid count\n"); + return -EINVAL; + } + fctrl->flash_num_sources = count; + CDBG("%s:%d flash_num_sources = %d", + __func__, __LINE__, fctrl->flash_num_sources); + for (i = 0; i < count; i++) { + flash_src_node = of_parse_phandle(of_node, + "qcom,flash-source", i); + if (!flash_src_node) { + pr_err("flash_src_node NULL\n"); + continue; + } + + rc = of_property_read_string(flash_src_node, + "qcom,default-led-trigger", + &fctrl->flash_trigger_name[i]); + if (rc < 0) { + rc = of_property_read_string(flash_src_node, + "linux,default-trigger", + &fctrl->flash_trigger_name[i]); + if (rc < 0) { + pr_err("default-trigger read failed\n"); + of_node_put(flash_src_node); + continue; + } + } + + CDBG("default trigger %s\n", + fctrl->flash_trigger_name[i]); + + /* Read operational-current */ + rc = of_property_read_u32(flash_src_node, + "qcom,current", + &fctrl->flash_op_current[i]); + if (rc < 0) { + pr_err("current: read failed\n"); + of_node_put(flash_src_node); + continue; + } + + /* Read max-current */ + rc = of_property_read_u32(flash_src_node, + "qcom,max-current", + &fctrl->flash_max_current[i]); + if (rc < 0) { + pr_err("current: read failed\n"); + of_node_put(flash_src_node); + continue; + } + + /* Read max-duration */ + rc = of_property_read_u32(flash_src_node, + "qcom,duration", + &fctrl->flash_max_duration[i]); + if (rc < 0) { + pr_err("duration: read failed\n"); + of_node_put(flash_src_node); + /* Non-fatal; this property is optional */ + } + + of_node_put(flash_src_node); + + CDBG("max_current[%d] %d\n", + i, fctrl->flash_op_current[i]); + + led_trigger_register_simple( + fctrl->flash_trigger_name[i], + &fctrl->flash_trigger[i]); + } + if (fctrl->flash_driver_type == FLASH_DRIVER_DEFAULT) + fctrl->flash_driver_type = FLASH_DRIVER_PMIC; + CDBG("%s:%d fctrl->flash_driver_type = %d", __func__, __LINE__, + fctrl->flash_driver_type); + } + + if (of_get_property(of_node, "qcom,torch-source", &count)) { + count /= sizeof(uint32_t); + CDBG("count %d\n", count); + if (count > MAX_LED_TRIGGERS) { + pr_err("invalid count\n"); + return -EINVAL; + } + fctrl->torch_num_sources = count; + CDBG("%s:%d torch_num_sources = %d", + __func__, __LINE__, fctrl->torch_num_sources); + for (i = 0; i < count; i++) { + torch_src_node = of_parse_phandle(of_node, + "qcom,torch-source", i); + if (!torch_src_node) { + pr_err("torch_src_node NULL\n"); + continue; + } + + rc = of_property_read_string(torch_src_node, + "qcom,default-led-trigger", + &fctrl->torch_trigger_name[i]); + if (rc < 0) { + rc = of_property_read_string(torch_src_node, + "linux,default-trigger", + &fctrl->torch_trigger_name[i]); + if (rc < 0) { + pr_err("default-trigger read failed\n"); + of_node_put(torch_src_node); + continue; + } + } + + CDBG("default trigger %s\n", + fctrl->torch_trigger_name[i]); + + /* Read operational-current */ + rc = of_property_read_u32(torch_src_node, + "qcom,current", + &fctrl->torch_op_current[i]); + if (rc < 0) { + pr_err("current: read failed\n"); + of_node_put(torch_src_node); + continue; + } + + /* Read max-current */ + rc = of_property_read_u32(torch_src_node, + "qcom,max-current", + &fctrl->torch_max_current[i]); + if (rc < 0) { + pr_err("current: read failed\n"); + of_node_put(torch_src_node); + continue; + } + + of_node_put(torch_src_node); + + CDBG("max_current[%d] %d\n", + i, fctrl->torch_op_current[i]); + + led_trigger_register_simple( + fctrl->torch_trigger_name[i], + &fctrl->torch_trigger[i]); + } + if (fctrl->flash_driver_type == FLASH_DRIVER_DEFAULT) + fctrl->flash_driver_type = FLASH_DRIVER_PMIC; + CDBG("%s:%d fctrl->flash_driver_type = %d", __func__, __LINE__, + fctrl->flash_driver_type); + } + + return 0; +} + +static int32_t msm_flash_get_dt_data(struct device_node *of_node, + struct msm_flash_ctrl_t *fctrl) +{ + int32_t rc = 0; + struct msm_camera_power_ctrl_t *power_info = + &fctrl->power_info; + + CDBG("called\n"); + + if (!of_node) { + pr_err("of_node NULL\n"); + return -EINVAL; + } + + /* Read the sub device */ + rc = of_property_read_u32(of_node, "cell-index", &fctrl->pdev->id); + if (rc < 0) { + pr_err("failed rc %d\n", rc); + return rc; + } + + CDBG("subdev id %d\n", fctrl->subdev_id); + + fctrl->flash_driver_type = FLASH_DRIVER_DEFAULT; + + /* Read the CCI master. Use M0 if not available in the node */ + rc = of_property_read_u32(of_node, "qcom,cci-master", + &fctrl->cci_i2c_master); + CDBG("%s qcom,cci-master %d, rc %d\n", __func__, fctrl->cci_i2c_master, + rc); + if (rc < 0) { + /* Set default master 0 */ + fctrl->cci_i2c_master = MASTER_0; + rc = 0; + } else { + fctrl->flash_driver_type = FLASH_DRIVER_I2C; + } + + /* Read the gpio information from device tree */ + rc = msm_sensor_driver_get_gpio_data( + &(fctrl->power_info.gpio_conf), of_node); + if (rc < 0) { + pr_err("%s:%d msm_sensor_driver_get_gpio_data failed rc %d\n", + __func__, __LINE__, rc); + return rc; + } + + if (fctrl->flash_driver_type == FLASH_DRIVER_DEFAULT) +/*+Begin. wangdw10. Change flash type to PMIC for karate/karateplus project.2016.06.23*/ + fctrl->flash_driver_type = FLASH_DRIVER_PMIC; +/*+End.*/ + CDBG("%s:%d fctrl->flash_driver_type = %d", __func__, __LINE__, + fctrl->flash_driver_type); + + /* Read the regulator information from device tree */ + rc = msm_camera_get_dt_vreg_data(of_node, &power_info->cam_vreg, + &power_info->num_vreg); + if (rc < 0) { + pr_err("%s:%d msm_camera_get_dt_vreg_data failed rc %d\n", + __func__, __LINE__, rc); + return rc; + } + + /* Read the flash and torch source info from device tree node */ + rc = msm_flash_get_pmic_source_info(of_node, fctrl); + if (rc < 0) { + pr_err("%s:%d msm_flash_get_pmic_source_info failed rc %d\n", + __func__, __LINE__, rc); + return rc; + } + return rc; +} + +#ifdef CONFIG_COMPAT +static long msm_flash_subdev_do_ioctl( + struct file *file, unsigned int cmd, void *arg) +{ + int32_t i = 0; + int32_t rc = 0; + struct video_device *vdev; + struct v4l2_subdev *sd; + struct msm_flash_cfg_data_t32 *u32; + struct msm_flash_cfg_data_t flash_data; + struct msm_flash_init_info_t32 flash_init_info32; + struct msm_flash_init_info_t flash_init_info; + + CDBG("Enter"); + + if (!file || !arg) { + pr_err("%s:failed NULL parameter\n", __func__); + return -EINVAL; + } + vdev = video_devdata(file); + sd = vdev_to_v4l2_subdev(vdev); + u32 = (struct msm_flash_cfg_data_t32 *)arg; + + flash_data.cfg_type = u32->cfg_type; + for (i = 0; i < MAX_LED_TRIGGERS; i++) { + flash_data.flash_current[i] = u32->flash_current[i]; + flash_data.flash_duration[i] = u32->flash_duration[i]; + } + switch (cmd) { + case VIDIOC_MSM_FLASH_CFG32: + cmd = VIDIOC_MSM_FLASH_CFG; + switch (flash_data.cfg_type) { + case CFG_FLASH_OFF: + case CFG_FLASH_LOW: + case CFG_FLASH_HIGH: + case CFG_FLASH_READ_I2C: + case CFG_FLASH_WRITE_I2C: + flash_data.cfg.settings = compat_ptr(u32->cfg.settings); + break; + case CFG_FLASH_INIT: + flash_data.cfg.flash_init_info = &flash_init_info; + if (copy_from_user(&flash_init_info32, + (void *)compat_ptr(u32->cfg.flash_init_info), + sizeof(struct msm_flash_init_info_t32))) { + pr_err("%s copy_from_user failed %d\n", + __func__, __LINE__); + return -EFAULT; + } + flash_init_info.flash_driver_type = + flash_init_info32.flash_driver_type; + flash_init_info.slave_addr = + flash_init_info32.slave_addr; + flash_init_info.i2c_freq_mode = + flash_init_info32.i2c_freq_mode; + flash_init_info.settings = + compat_ptr(flash_init_info32.settings); + flash_init_info.power_setting_array = + compat_ptr( + flash_init_info32.power_setting_array); + break; + default: + break; + } + break; + default: + return msm_flash_subdev_ioctl(sd, cmd, arg); + } + + rc = msm_flash_subdev_ioctl(sd, cmd, &flash_data); + for (i = 0; i < MAX_LED_TRIGGERS; i++) { + u32->flash_current[i] = flash_data.flash_current[i]; + u32->flash_duration[i] = flash_data.flash_duration[i]; + } + CDBG("Exit"); + return rc; +} + +static long msm_flash_subdev_fops_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(file, cmd, arg, msm_flash_subdev_do_ioctl); +} +#endif +static int32_t msm_flash_platform_probe(struct platform_device *pdev) +{ + int32_t rc = 0; + struct msm_flash_ctrl_t *flash_ctrl = NULL; + struct msm_camera_cci_client *cci_client = NULL; + + CDBG("Enter"); + if (!pdev->dev.of_node) { + pr_err("of_node NULL\n"); + return -EINVAL; + } + + flash_ctrl = kzalloc(sizeof(struct msm_flash_ctrl_t), GFP_KERNEL); + if (!flash_ctrl) { + pr_err("%s:%d failed no memory\n", __func__, __LINE__); + return -ENOMEM; + } + + memset(flash_ctrl, 0, sizeof(struct msm_flash_ctrl_t)); + + flash_ctrl->pdev = pdev; + + rc = msm_flash_get_dt_data(pdev->dev.of_node, flash_ctrl); + if (rc < 0) { + pr_err("%s:%d msm_flash_get_dt_data failed\n", + __func__, __LINE__); + kfree(flash_ctrl); + return -EINVAL; + } + + flash_ctrl->flash_state = MSM_CAMERA_FLASH_RELEASE; + flash_ctrl->power_info.dev = &flash_ctrl->pdev->dev; + flash_ctrl->flash_device_type = MSM_CAMERA_PLATFORM_DEVICE; + flash_ctrl->flash_mutex = &msm_flash_mutex; + flash_ctrl->flash_i2c_client.i2c_func_tbl = &msm_sensor_cci_func_tbl; + flash_ctrl->flash_i2c_client.cci_client = kzalloc( + sizeof(struct msm_camera_cci_client), GFP_KERNEL); + if (!flash_ctrl->flash_i2c_client.cci_client) { + kfree(flash_ctrl); + pr_err("failed no memory\n"); + return -ENOMEM; + } + + cci_client = flash_ctrl->flash_i2c_client.cci_client; + cci_client->cci_subdev = msm_cci_get_subdev(); + cci_client->cci_i2c_master = flash_ctrl->cci_i2c_master; + + /* Initialize sub device */ + v4l2_subdev_init(&flash_ctrl->msm_sd.sd, &msm_flash_subdev_ops); + v4l2_set_subdevdata(&flash_ctrl->msm_sd.sd, flash_ctrl); + + flash_ctrl->msm_sd.sd.internal_ops = &msm_flash_internal_ops; + flash_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(flash_ctrl->msm_sd.sd.name, + ARRAY_SIZE(flash_ctrl->msm_sd.sd.name), + "msm_camera_flash"); + media_entity_init(&flash_ctrl->msm_sd.sd.entity, 0, NULL, 0); + flash_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + flash_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_FLASH; + flash_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1; + msm_sd_register(&flash_ctrl->msm_sd); + + CDBG("%s:%d flash sd name = %s", __func__, __LINE__, + flash_ctrl->msm_sd.sd.entity.name); + msm_cam_copy_v4l2_subdev_fops(&msm_flash_v4l2_subdev_fops); +#ifdef CONFIG_COMPAT + msm_flash_v4l2_subdev_fops.compat_ioctl32 = + msm_flash_subdev_fops_ioctl; +#endif + flash_ctrl->msm_sd.sd.devnode->fops = &msm_flash_v4l2_subdev_fops; + + if (flash_ctrl->flash_driver_type == FLASH_DRIVER_PMIC) + rc = msm_torch_create_classdev(pdev, flash_ctrl); + + CDBG("probe success\n"); + return rc; +} + +MODULE_DEVICE_TABLE(of, msm_flash_dt_match); + +static struct platform_driver msm_flash_platform_driver = { + .probe = msm_flash_platform_probe, + .driver = { + .name = "qcom,camera-flash", + .owner = THIS_MODULE, + .of_match_table = msm_flash_dt_match, + }, +}; + +static int __init msm_flash_init_module(void) +{ + int32_t rc = 0; + CDBG("Enter\n"); + rc = platform_driver_register(&msm_flash_platform_driver); + if (rc) + pr_err("platform probe for flash failed"); + + return rc; +} + +static void __exit msm_flash_exit_module(void) +{ + platform_driver_unregister(&msm_flash_platform_driver); + return; +} + +static struct msm_flash_table msm_pmic_flash_table = { + .flash_driver_type = FLASH_DRIVER_PMIC, + .func_tbl = { + .camera_flash_init = NULL, + .camera_flash_release = msm_flash_release, + .camera_flash_off = msm_flash_off, + .camera_flash_low = msm_flash_low, + .camera_flash_high = msm_flash_high, + }, +}; + +static struct msm_flash_table msm_gpio_flash_table = { + .flash_driver_type = FLASH_DRIVER_GPIO, + .func_tbl = { + .camera_flash_init = msm_flash_gpio_init, + .camera_flash_release = msm_flash_release, + .camera_flash_off = msm_flash_off, + .camera_flash_low = msm_flash_low, + .camera_flash_high = msm_flash_high, + }, +}; + +static struct msm_flash_table msm_i2c_flash_table = { + .flash_driver_type = FLASH_DRIVER_I2C, + .func_tbl = { + .camera_flash_init = msm_flash_i2c_init, + .camera_flash_release = msm_flash_i2c_release, + .camera_flash_off = msm_flash_i2c_write_setting_array, + .camera_flash_low = msm_flash_i2c_write_setting_array, + .camera_flash_high = msm_flash_i2c_write_setting_array, + .camera_flash_read = msm_flash_i2c_read_setting_array, + .camera_flash_write = msm_flash_i2c_write_setting_array, + }, +}; + +module_init(msm_flash_init_module); +module_exit(msm_flash_exit_module); +MODULE_DESCRIPTION("MSM FLASH"); +MODULE_LICENSE("GPL v2"); diff --git a/camera/sensor/flash/msm_flash.h b/camera/sensor/flash/msm_flash.h new file mode 100644 index 00000000..df84e8d9 --- /dev/null +++ b/camera/sensor/flash/msm_flash.h @@ -0,0 +1,124 @@ +/* Copyright (c) 2009-2015, 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_FLASH_H +#define MSM_FLASH_H + +#include <linux/leds.h> +#include <linux/platform_device.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-ioctl.h> +#include <lenovo_media/msm_cam_sensor.h> +#include <lenovo_soc/qcom/camera2.h> +#include "msm_camera_i2c.h" +#include "msm_sd.h" + +#define DEFINE_MSM_MUTEX(mutexname) \ + static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) + +enum msm_camera_flash_state_t { + MSM_CAMERA_FLASH_INIT, + MSM_CAMERA_FLASH_RELEASE, +}; + +struct msm_flash_ctrl_t; + +struct msm_flash_func_t { + int32_t (*camera_flash_init)(struct msm_flash_ctrl_t *, + struct msm_flash_cfg_data_t *); + int32_t (*camera_flash_release)(struct msm_flash_ctrl_t *); + int32_t (*camera_flash_off)(struct msm_flash_ctrl_t *, + struct msm_flash_cfg_data_t *); + int32_t (*camera_flash_low)(struct msm_flash_ctrl_t *, + struct msm_flash_cfg_data_t *); + int32_t (*camera_flash_high)(struct msm_flash_ctrl_t *, + struct msm_flash_cfg_data_t *); + int32_t (*camera_flash_read)(struct msm_flash_ctrl_t *, + struct msm_flash_cfg_data_t *); + int32_t (*camera_flash_write)(struct msm_flash_ctrl_t *, + struct msm_flash_cfg_data_t *); +}; + +struct msm_flash_table { + enum msm_flash_driver_type flash_driver_type; + struct msm_flash_func_t func_tbl; +}; + +struct msm_flash_reg_t { + struct msm_camera_i2c_reg_setting *init_setting; + struct msm_camera_i2c_reg_setting *off_setting; + struct msm_camera_i2c_reg_setting *release_setting; + struct msm_camera_i2c_reg_setting *low_setting; + struct msm_camera_i2c_reg_setting *high_setting; +}; + +struct msm_flash_ctrl_t { + struct msm_camera_i2c_client flash_i2c_client; + struct msm_sd_subdev msm_sd; + struct platform_device *pdev; + struct msm_flash_func_t *func_tbl; + struct msm_camera_power_ctrl_t power_info; + + /* Switch node to trigger led */ + const char *switch_trigger_name; + struct led_trigger *switch_trigger; + + /* Flash */ + uint32_t flash_num_sources; + const char *flash_trigger_name[MAX_LED_TRIGGERS]; + struct led_trigger *flash_trigger[MAX_LED_TRIGGERS]; + uint32_t flash_op_current[MAX_LED_TRIGGERS]; + uint32_t flash_max_current[MAX_LED_TRIGGERS]; + uint32_t flash_max_duration[MAX_LED_TRIGGERS]; + + /* Torch */ + uint32_t torch_num_sources; + const char *torch_trigger_name[MAX_LED_TRIGGERS]; + struct led_trigger *torch_trigger[MAX_LED_TRIGGERS]; + uint32_t torch_op_current[MAX_LED_TRIGGERS]; + uint32_t torch_max_current[MAX_LED_TRIGGERS]; + + void *data; + enum msm_camera_device_type_t flash_device_type; + enum cci_i2c_master_t cci_i2c_master; + uint32_t subdev_id; + struct mutex *flash_mutex; + struct msm_sensor_power_setting_array power_setting_array; + + /* flash driver type */ + enum msm_flash_driver_type flash_driver_type; + + /* flash state */ + enum msm_camera_flash_state_t flash_state; +}; + +int msm_flash_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +int msm_flash_probe(struct platform_device *pdev, const void *data); + +int32_t msm_flash_create_v4lsubdev(struct platform_device *pdev, + void *data); +int32_t msm_led_i2c_flash_create_v4lsubdev(void *data); + +int32_t msm_led_i2c_trigger_get_subdev_id(struct msm_flash_ctrl_t *fctrl, + void *arg); + +int32_t msm_led_i2c_trigger_config(struct msm_flash_ctrl_t *fctrl, + void *data); + +int msm_flash_led_init(struct msm_flash_ctrl_t *fctrl); +int msm_flash_led_release(struct msm_flash_ctrl_t *fctrl); +int msm_flash_led_off(struct msm_flash_ctrl_t *fctrl); +int msm_flash_led_low(struct msm_flash_ctrl_t *fctrl); +int msm_flash_led_high(struct msm_flash_ctrl_t *fctrl); +#endif 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 diff --git a/camera/sensor/msm_sensor.c b/camera/sensor/msm_sensor.c new file mode 100644 index 00000000..cce2b378 --- /dev/null +++ b/camera/sensor/msm_sensor.c @@ -0,0 +1,1537 @@ +/* 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 "msm_sensor.h" +#include "msm_sd.h" +#include "camera.h" +#include "msm_cci.h" +#include "msm_camera_io_util.h" +#include "msm_camera_i2c_mux.h" +#include <linux/regulator/rpm-smd-regulator.h> +#include <linux/regulator/consumer.h> + +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +static void msm_sensor_adjust_mclk(struct msm_camera_power_ctrl_t *ctrl) +{ + int idx; + struct msm_sensor_power_setting *power_setting; + for (idx = 0; idx < ctrl->power_setting_size; idx++) { + power_setting = &ctrl->power_setting[idx]; + if (power_setting->seq_type == SENSOR_CLK && + power_setting->seq_val == SENSOR_CAM_MCLK) { + if (power_setting->config_val == 24000000) { + power_setting->config_val = 23880000; + CDBG("%s MCLK request adjusted to 23.88MHz\n" + , __func__); + } + break; + } + } + + return; +} + +static void msm_sensor_misc_regulator( + struct msm_sensor_ctrl_t *sctrl, uint32_t enable) +{ + int32_t rc = 0; + if (enable) { + sctrl->misc_regulator = (void *)rpm_regulator_get( + &sctrl->pdev->dev, sctrl->sensordata->misc_regulator); + if (sctrl->misc_regulator) { + rc = rpm_regulator_set_mode(sctrl->misc_regulator, + RPM_REGULATOR_MODE_HPM); + if (rc < 0) { + pr_err("%s: Failed to set for rpm regulator on %s: %d\n", + __func__, + sctrl->sensordata->misc_regulator, rc); + rpm_regulator_put(sctrl->misc_regulator); + } + } else { + pr_err("%s: Failed to vote for rpm regulator on %s: %d\n", + __func__, + sctrl->sensordata->misc_regulator, rc); + } + } else { + if (sctrl->misc_regulator) { + rc = rpm_regulator_set_mode( + (struct rpm_regulator *)sctrl->misc_regulator, + RPM_REGULATOR_MODE_AUTO); + if (rc < 0) + pr_err("%s: Failed to set for rpm regulator on %s: %d\n", + __func__, + sctrl->sensordata->misc_regulator, rc); + rpm_regulator_put(sctrl->misc_regulator); + } + } +} + +int32_t msm_sensor_free_sensor_data(struct msm_sensor_ctrl_t *s_ctrl) +{ + if (!s_ctrl->pdev && !s_ctrl->sensor_i2c_client->client) + return 0; + kfree(s_ctrl->sensordata->slave_info); + kfree(s_ctrl->sensordata->cam_slave_info); + kfree(s_ctrl->sensordata->actuator_info); + kfree(s_ctrl->sensordata->power_info.gpio_conf->gpio_num_info); + kfree(s_ctrl->sensordata->power_info.gpio_conf->cam_gpio_req_tbl); + kfree(s_ctrl->sensordata->power_info.gpio_conf); + kfree(s_ctrl->sensordata->power_info.cam_vreg); + kfree(s_ctrl->sensordata->power_info.power_setting); + kfree(s_ctrl->sensordata->power_info.power_down_setting); + kfree(s_ctrl->sensordata->csi_lane_params); + kfree(s_ctrl->sensordata->sensor_info); + msm_camera_put_clk_info(s_ctrl->pdev, + &s_ctrl->sensordata->power_info.clk_info, + &s_ctrl->sensordata->power_info.clk_ptr, + s_ctrl->sensordata->power_info.clk_info_size); + kfree(s_ctrl->sensordata); + return 0; +} + +int msm_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl) +{ + struct msm_camera_power_ctrl_t *power_info; + enum msm_camera_device_type_t sensor_device_type; + struct msm_camera_i2c_client *sensor_i2c_client; + + if (!s_ctrl) { + pr_err("%s:%d failed: s_ctrl %p\n", + __func__, __LINE__, s_ctrl); + return -EINVAL; + } + + if (s_ctrl->is_csid_tg_mode) + return 0; + + power_info = &s_ctrl->sensordata->power_info; + sensor_device_type = s_ctrl->sensor_device_type; + sensor_i2c_client = s_ctrl->sensor_i2c_client; + + if (!power_info || !sensor_i2c_client) { + pr_err("%s:%d failed: power_info %p sensor_i2c_client %p\n", + __func__, __LINE__, power_info, sensor_i2c_client); + return -EINVAL; + } + return msm_camera_power_down(power_info, sensor_device_type, + sensor_i2c_client); +} + +int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl) +{ + int rc; + struct msm_camera_power_ctrl_t *power_info; + struct msm_camera_i2c_client *sensor_i2c_client; + struct msm_camera_slave_info *slave_info; + const char *sensor_name; + uint32_t retry = 0; + + if (!s_ctrl) { + pr_err("%s:%d failed: %p\n", + __func__, __LINE__, s_ctrl); + return -EINVAL; + } + + if (s_ctrl->is_csid_tg_mode) + return 0; + + power_info = &s_ctrl->sensordata->power_info; + sensor_i2c_client = s_ctrl->sensor_i2c_client; + slave_info = s_ctrl->sensordata->slave_info; + sensor_name = s_ctrl->sensordata->sensor_name; + + if (!power_info || !sensor_i2c_client || !slave_info || + !sensor_name) { + pr_err("%s:%d failed: %p %p %p %p\n", + __func__, __LINE__, power_info, + sensor_i2c_client, slave_info, sensor_name); + return -EINVAL; + } + + if (s_ctrl->set_mclk_23880000) + msm_sensor_adjust_mclk(power_info); + + for (retry = 0; retry < 3; retry++) { + rc = msm_camera_power_up(power_info, s_ctrl->sensor_device_type, + sensor_i2c_client); + if (rc < 0) + return rc; + rc = msm_sensor_check_id(s_ctrl); + if (rc < 0) { + msm_camera_power_down(power_info, + s_ctrl->sensor_device_type, sensor_i2c_client); + msleep(20); + continue; + } else { + break; + } + } + + if (rc < 0 && s_ctrl->sensordata->cam_slave_info->slave_addr2 > 0) { + sensor_i2c_client->cci_client->sid = + s_ctrl->sensordata->cam_slave_info->slave_addr2 >> 1; + slave_info->sensor_slave_addr = + s_ctrl->sensordata->cam_slave_info->slave_addr2; + for (retry = 0; retry < 3; retry++) { + rc = msm_camera_power_up(power_info, + s_ctrl->sensor_device_type, + sensor_i2c_client); + if (rc < 0) + return rc; + rc = msm_sensor_check_id(s_ctrl); + if (rc < 0) { + msm_camera_power_down(power_info, + s_ctrl->sensor_device_type, + sensor_i2c_client); + msleep(20); + continue; + } else { + break; + } + } + } + + return rc; +} + +static uint16_t msm_sensor_id_by_mask(struct msm_sensor_ctrl_t *s_ctrl, + uint16_t chipid) +{ + uint16_t sensor_id = chipid; + int16_t sensor_id_mask = s_ctrl->sensordata->slave_info->sensor_id_mask; + + if (!sensor_id_mask) + sensor_id_mask = ~sensor_id_mask; + + sensor_id &= sensor_id_mask; + sensor_id_mask &= -sensor_id_mask; + sensor_id_mask -= 1; + while (sensor_id_mask) { + sensor_id_mask >>= 1; + sensor_id >>= 1; + } + return sensor_id; +} + +int msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl) +{ + int rc = 0; + uint16_t chipid = 0; + uint16_t chipid_mask = 0; + struct msm_camera_i2c_client *sensor_i2c_client; + struct msm_camera_slave_info *slave_info; + const char *sensor_name; + + if (!s_ctrl) { + pr_err("%s:%d failed: %p\n", + __func__, __LINE__, s_ctrl); + return -EINVAL; + } + sensor_i2c_client = s_ctrl->sensor_i2c_client; + slave_info = s_ctrl->sensordata->slave_info; + sensor_name = s_ctrl->sensordata->sensor_name; + + if (!sensor_i2c_client || !slave_info || !sensor_name) { + pr_err("%s:%d failed: %p %p %p\n", + __func__, __LINE__, sensor_i2c_client, slave_info, + sensor_name); + return -EINVAL; + } + + rc = sensor_i2c_client->i2c_func_tbl->i2c_read( + sensor_i2c_client, slave_info->sensor_id_reg_addr, + &chipid, MSM_CAMERA_I2C_WORD_DATA); + if (rc < 0) { + pr_err("%s: %s: read id failed\n", __func__, sensor_name); + return rc; + } + + CDBG("%s: read id: 0x%x expected id 0x%x: or 0x%x\n", + __func__, chipid, + slave_info->sensor_id, slave_info->sensor_id2); + chipid_mask = msm_sensor_id_by_mask(s_ctrl, chipid); + if (chipid_mask != slave_info->sensor_id) { + if (slave_info->sensor_id2 > 0) { + if (chipid_mask == slave_info->sensor_id2) + return rc; + } + pr_err("%s chip id %x does not match %x\n", + __func__, chipid, slave_info->sensor_id); + return -ENODEV; + } + return rc; +} + +static struct msm_sensor_ctrl_t *get_sctrl(struct v4l2_subdev *sd) +{ + return container_of(container_of(sd, struct msm_sd_subdev, sd), + struct msm_sensor_ctrl_t, msm_sd); +} + +static void msm_sensor_stop_stream(struct msm_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + + mutex_lock(s_ctrl->msm_sensor_mutex); + if (s_ctrl->sensor_state == MSM_SENSOR_POWER_UP) { + s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table( + s_ctrl->sensor_i2c_client, &s_ctrl->stop_setting); + kfree(s_ctrl->stop_setting.reg_setting); + s_ctrl->stop_setting.reg_setting = NULL; + + if (s_ctrl->func_tbl->sensor_power_down) { + if (s_ctrl->sensordata->misc_regulator) + msm_sensor_misc_regulator(s_ctrl, 0); + + rc = s_ctrl->func_tbl->sensor_power_down(s_ctrl); + if (rc < 0) { + pr_err("%s:%d failed rc %d\n", __func__, + __LINE__, rc); + } + s_ctrl->sensor_state = MSM_SENSOR_POWER_DOWN; + CDBG("%s:%d sensor state %d\n", __func__, __LINE__, + s_ctrl->sensor_state); + } else { + pr_err("s_ctrl->func_tbl NULL\n"); + } + } + mutex_unlock(s_ctrl->msm_sensor_mutex); + return; +} + +static int msm_sensor_get_af_status(struct msm_sensor_ctrl_t *s_ctrl, + void __user *argp) +{ + /* TO-DO: Need to set AF status register address and expected value + We need to check the AF status in the sensor register and + set the status in the *status variable accordingly*/ + return 0; +} + +static long msm_sensor_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int rc = 0; + struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd); + void __user *argp = (void __user *)arg; + if (!s_ctrl) { + pr_err("%s s_ctrl NULL\n", __func__); + return -EBADF; + } + switch (cmd) { + case VIDIOC_MSM_SENSOR_CFG: +#ifdef CONFIG_COMPAT + if (is_compat_task()) + rc = s_ctrl->func_tbl->sensor_config32(s_ctrl, argp); + else +#endif + rc = s_ctrl->func_tbl->sensor_config(s_ctrl, argp); + return rc; + case VIDIOC_MSM_SENSOR_GET_AF_STATUS: + return msm_sensor_get_af_status(s_ctrl, argp); + case VIDIOC_MSM_SENSOR_RELEASE: + case MSM_SD_SHUTDOWN: + msm_sensor_stop_stream(s_ctrl); + return 0; + case MSM_SD_NOTIFY_FREEZE: + return 0; + case MSM_SD_UNNOTIFY_FREEZE: + return 0; + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static long msm_sensor_subdev_do_ioctl( + struct file *file, unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + switch (cmd) { + case VIDIOC_MSM_SENSOR_CFG32: + cmd = VIDIOC_MSM_SENSOR_CFG; + default: + return msm_sensor_subdev_ioctl(sd, cmd, arg); + } +} + +long msm_sensor_subdev_fops_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(file, cmd, arg, msm_sensor_subdev_do_ioctl); +} + +static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, + void __user *argp) +{ + struct sensorb_cfg_data32 *cdata = (struct sensorb_cfg_data32 *)argp; + int32_t rc = 0; + int32_t i = 0; + mutex_lock(s_ctrl->msm_sensor_mutex); + CDBG("%s:%d %s cfgtype = %d\n", __func__, __LINE__, + s_ctrl->sensordata->sensor_name, cdata->cfgtype); + switch (cdata->cfgtype) { + case CFG_GET_SENSOR_INFO: + memcpy(cdata->cfg.sensor_info.sensor_name, + s_ctrl->sensordata->sensor_name, + sizeof(cdata->cfg.sensor_info.sensor_name)); + cdata->cfg.sensor_info.session_id = + s_ctrl->sensordata->sensor_info->session_id; + for (i = 0; i < SUB_MODULE_MAX; i++) { + cdata->cfg.sensor_info.subdev_id[i] = + s_ctrl->sensordata->sensor_info->subdev_id[i]; + cdata->cfg.sensor_info.subdev_intf[i] = + s_ctrl->sensordata->sensor_info->subdev_intf[i]; + } + cdata->cfg.sensor_info.is_mount_angle_valid = + s_ctrl->sensordata->sensor_info->is_mount_angle_valid; + cdata->cfg.sensor_info.sensor_mount_angle = + s_ctrl->sensordata->sensor_info->sensor_mount_angle; + cdata->cfg.sensor_info.position = + s_ctrl->sensordata->sensor_info->position; + cdata->cfg.sensor_info.modes_supported = + s_ctrl->sensordata->sensor_info->modes_supported; + CDBG("%s:%d sensor name %s\n", __func__, __LINE__, + cdata->cfg.sensor_info.sensor_name); + CDBG("%s:%d session id %d\n", __func__, __LINE__, + cdata->cfg.sensor_info.session_id); + for (i = 0; i < SUB_MODULE_MAX; i++) { + CDBG("%s:%d subdev_id[%d] %d\n", __func__, __LINE__, i, + cdata->cfg.sensor_info.subdev_id[i]); + CDBG("%s:%d subdev_intf[%d] %d\n", __func__, __LINE__, + i, cdata->cfg.sensor_info.subdev_intf[i]); + } + CDBG("%s:%d mount angle valid %d value %d\n", __func__, + __LINE__, cdata->cfg.sensor_info.is_mount_angle_valid, + cdata->cfg.sensor_info.sensor_mount_angle); + + break; + case CFG_GET_SENSOR_INIT_PARAMS: + cdata->cfg.sensor_init_params.modes_supported = + s_ctrl->sensordata->sensor_info->modes_supported; + cdata->cfg.sensor_init_params.position = + s_ctrl->sensordata->sensor_info->position; + cdata->cfg.sensor_init_params.sensor_mount_angle = + s_ctrl->sensordata->sensor_info->sensor_mount_angle; + CDBG("%s:%d init params mode %d pos %d mount %d\n", __func__, + __LINE__, + cdata->cfg.sensor_init_params.modes_supported, + cdata->cfg.sensor_init_params.position, + cdata->cfg.sensor_init_params.sensor_mount_angle); + break; + case CFG_WRITE_I2C_ARRAY: + case CFG_WRITE_I2C_ARRAY_SYNC: + case CFG_WRITE_I2C_ARRAY_SYNC_BLOCK: + case CFG_WRITE_I2C_ARRAY_ASYNC: { + struct msm_camera_i2c_reg_setting32 conf_array32; + struct msm_camera_i2c_reg_setting conf_array; + struct msm_camera_i2c_reg_array *reg_setting = NULL; + + if (s_ctrl->is_csid_tg_mode) + goto DONE; + + if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) { + pr_err("%s:%d failed: invalid state %d\n", __func__, + __LINE__, s_ctrl->sensor_state); + rc = -EFAULT; + break; + } + + if (copy_from_user(&conf_array32, + (void *)compat_ptr(cdata->cfg.setting), + sizeof(struct msm_camera_i2c_reg_setting32))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + conf_array.addr_type = conf_array32.addr_type; + conf_array.data_type = conf_array32.data_type; + conf_array.delay = conf_array32.delay; + conf_array.size = conf_array32.size; + conf_array.reg_setting = compat_ptr(conf_array32.reg_setting); + + if (!conf_array.size || + conf_array.size > I2C_REG_DATA_MAX) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + reg_setting = kzalloc(conf_array.size * + (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL); + if (!reg_setting) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -ENOMEM; + break; + } + if (copy_from_user(reg_setting, + (void *)(conf_array.reg_setting), + conf_array.size * + sizeof(struct msm_camera_i2c_reg_array))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + kfree(reg_setting); + rc = -EFAULT; + break; + } + + conf_array.reg_setting = reg_setting; + + if (CFG_WRITE_I2C_ARRAY == cdata->cfgtype) + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl-> + i2c_write_table(s_ctrl->sensor_i2c_client, + &conf_array); + else if (CFG_WRITE_I2C_ARRAY_ASYNC == cdata->cfgtype) + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl-> + i2c_write_table_async(s_ctrl->sensor_i2c_client, + &conf_array); + else if (CFG_WRITE_I2C_ARRAY_SYNC_BLOCK == cdata->cfgtype) + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl-> + i2c_write_table_sync_block( + s_ctrl->sensor_i2c_client, + &conf_array); + else + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl-> + i2c_write_table_sync(s_ctrl->sensor_i2c_client, + &conf_array); + + kfree(reg_setting); + break; + } + case CFG_SLAVE_READ_I2C: { + struct msm_camera_i2c_read_config read_config; + struct msm_camera_i2c_read_config *read_config_ptr = NULL; + uint16_t local_data = 0; + uint16_t orig_slave_addr = 0, read_slave_addr = 0; + uint16_t orig_addr_type = 0, read_addr_type = 0; + + if (s_ctrl->is_csid_tg_mode) + goto DONE; + + read_config_ptr = + (struct msm_camera_i2c_read_config *) + compat_ptr(cdata->cfg.setting); + + if (copy_from_user(&read_config, read_config_ptr, + sizeof(struct msm_camera_i2c_read_config))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + read_slave_addr = read_config.slave_addr; + read_addr_type = read_config.addr_type; + + CDBG("%s:CFG_SLAVE_READ_I2C:", __func__); + CDBG("%s:slave_addr=0x%x reg_addr=0x%x, data_type=%d\n", + __func__, read_config.slave_addr, + read_config.reg_addr, read_config.data_type); + if (s_ctrl->sensor_i2c_client->cci_client) { + orig_slave_addr = + s_ctrl->sensor_i2c_client->cci_client->sid; + s_ctrl->sensor_i2c_client->cci_client->sid = + read_slave_addr >> 1; + } else if (s_ctrl->sensor_i2c_client->client) { + orig_slave_addr = + s_ctrl->sensor_i2c_client->client->addr; + s_ctrl->sensor_i2c_client->client->addr = + read_slave_addr >> 1; + } else { + pr_err("%s: error: no i2c/cci client found.", __func__); + rc = -EFAULT; + break; + } + CDBG("%s:orig_slave_addr=0x%x, new_slave_addr=0x%x", + __func__, orig_slave_addr, + read_slave_addr >> 1); + + orig_addr_type = s_ctrl->sensor_i2c_client->addr_type; + s_ctrl->sensor_i2c_client->addr_type = read_addr_type; + + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read( + s_ctrl->sensor_i2c_client, + read_config.reg_addr, + &local_data, read_config.data_type); + if (s_ctrl->sensor_i2c_client->cci_client) { + s_ctrl->sensor_i2c_client->cci_client->sid = + orig_slave_addr; + } else if (s_ctrl->sensor_i2c_client->client) { + s_ctrl->sensor_i2c_client->client->addr = + orig_slave_addr; + } + s_ctrl->sensor_i2c_client->addr_type = orig_addr_type; + + pr_debug("slave_read %x %x %x\n", read_slave_addr, + read_config.reg_addr, local_data); + + if (rc < 0) { + pr_err("%s:%d: i2c_read failed\n", __func__, __LINE__); + break; + } + read_config_ptr->data = local_data; + break; + } + case CFG_SLAVE_WRITE_I2C_ARRAY: { + struct msm_camera_i2c_array_write_config32 write_config32; + struct msm_camera_i2c_array_write_config write_config; + struct msm_camera_i2c_reg_array *reg_setting = NULL; + uint16_t orig_slave_addr = 0, write_slave_addr = 0; + uint16_t orig_addr_type = 0, write_addr_type = 0; + + if (s_ctrl->is_csid_tg_mode) + goto DONE; + + if (copy_from_user(&write_config32, + (void *)compat_ptr(cdata->cfg.setting), + sizeof( + struct msm_camera_i2c_array_write_config32))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + write_config.slave_addr = write_config32.slave_addr; + write_config.conf_array.addr_type = + write_config32.conf_array.addr_type; + write_config.conf_array.data_type = + write_config32.conf_array.data_type; + write_config.conf_array.delay = + write_config32.conf_array.delay; + write_config.conf_array.size = + write_config32.conf_array.size; + write_config.conf_array.reg_setting = + compat_ptr(write_config32.conf_array.reg_setting); + + pr_debug("%s:CFG_SLAVE_WRITE_I2C_ARRAY:\n", __func__); + pr_debug("%s:slave_addr=0x%x, array_size=%d addr_type=%d data_type=%d\n", + __func__, + write_config.slave_addr, + write_config.conf_array.size, + write_config.conf_array.addr_type, + write_config.conf_array.data_type); + + if (!write_config.conf_array.size || + write_config.conf_array.size > I2C_REG_DATA_MAX) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + reg_setting = kzalloc(write_config.conf_array.size * + (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL); + if (!reg_setting) { + rc = -ENOMEM; + break; + } + if (copy_from_user(reg_setting, + (void *)(write_config.conf_array.reg_setting), + write_config.conf_array.size * + sizeof(struct msm_camera_i2c_reg_array))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + kfree(reg_setting); + rc = -EFAULT; + break; + } + write_config.conf_array.reg_setting = reg_setting; + write_slave_addr = write_config.slave_addr; + write_addr_type = write_config.conf_array.addr_type; + + if (s_ctrl->sensor_i2c_client->cci_client) { + orig_slave_addr = + s_ctrl->sensor_i2c_client->cci_client->sid; + s_ctrl->sensor_i2c_client->cci_client->sid = + write_slave_addr >> 1; + } else if (s_ctrl->sensor_i2c_client->client) { + orig_slave_addr = + s_ctrl->sensor_i2c_client->client->addr; + s_ctrl->sensor_i2c_client->client->addr = + write_slave_addr >> 1; + } else { + pr_err("%s: error: no i2c/cci client found.", + __func__); + kfree(reg_setting); + rc = -EFAULT; + break; + } + + pr_debug("%s:orig_slave_addr=0x%x, new_slave_addr=0x%x\n", + __func__, orig_slave_addr, + write_slave_addr >> 1); + orig_addr_type = s_ctrl->sensor_i2c_client->addr_type; + s_ctrl->sensor_i2c_client->addr_type = write_addr_type; + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table( + s_ctrl->sensor_i2c_client, &(write_config.conf_array)); + + s_ctrl->sensor_i2c_client->addr_type = orig_addr_type; + if (s_ctrl->sensor_i2c_client->cci_client) { + s_ctrl->sensor_i2c_client->cci_client->sid = + orig_slave_addr; + } else if (s_ctrl->sensor_i2c_client->client) { + s_ctrl->sensor_i2c_client->client->addr = + orig_slave_addr; + } else { + pr_err("%s: error: no i2c/cci client found.\n", + __func__); + kfree(reg_setting); + rc = -EFAULT; + break; + } + kfree(reg_setting); + break; + } + case CFG_WRITE_I2C_SEQ_ARRAY: { + struct msm_camera_i2c_seq_reg_setting32 conf_array32; + struct msm_camera_i2c_seq_reg_setting conf_array; + struct msm_camera_i2c_seq_reg_array *reg_setting = NULL; + + if (s_ctrl->is_csid_tg_mode) + goto DONE; + + if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) { + pr_err("%s:%d failed: invalid state %d\n", __func__, + __LINE__, s_ctrl->sensor_state); + rc = -EFAULT; + break; + } + + if (copy_from_user(&conf_array32, + (void *)compat_ptr(cdata->cfg.setting), + sizeof(struct msm_camera_i2c_seq_reg_setting32))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + conf_array.addr_type = conf_array32.addr_type; + conf_array.delay = conf_array32.delay; + conf_array.size = conf_array32.size; + conf_array.reg_setting = compat_ptr(conf_array32.reg_setting); + + if (!conf_array.size || + conf_array.size > I2C_SEQ_REG_DATA_MAX) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + reg_setting = kzalloc(conf_array.size * + (sizeof(struct msm_camera_i2c_seq_reg_array)), + GFP_KERNEL); + if (!reg_setting) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -ENOMEM; + break; + } + if (copy_from_user(reg_setting, (void *)conf_array.reg_setting, + conf_array.size * + sizeof(struct msm_camera_i2c_seq_reg_array))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + kfree(reg_setting); + rc = -EFAULT; + break; + } + + conf_array.reg_setting = reg_setting; + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl-> + i2c_write_seq_table(s_ctrl->sensor_i2c_client, + &conf_array); + kfree(reg_setting); + break; + } + + case CFG_POWER_UP: + if (s_ctrl->is_csid_tg_mode) + goto DONE; + + if (s_ctrl->sensor_state != MSM_SENSOR_POWER_DOWN) { + pr_err("%s:%d failed: invalid state %d\n", __func__, + __LINE__, s_ctrl->sensor_state); + rc = -EFAULT; + break; + } + if (s_ctrl->func_tbl->sensor_power_up) { + if (s_ctrl->sensordata->misc_regulator) + msm_sensor_misc_regulator(s_ctrl, 1); + + rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl); + if (rc < 0) { + pr_err("%s:%d failed rc %d\n", __func__, + __LINE__, rc); + break; + } + s_ctrl->sensor_state = MSM_SENSOR_POWER_UP; + CDBG("%s:%d sensor state %d\n", __func__, __LINE__, + s_ctrl->sensor_state); + } else { + rc = -EFAULT; + } + break; + case CFG_POWER_DOWN: + if (s_ctrl->is_csid_tg_mode) + goto DONE; + + kfree(s_ctrl->stop_setting.reg_setting); + s_ctrl->stop_setting.reg_setting = NULL; + if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) { + pr_err("%s:%d failed: invalid state %d\n", __func__, + __LINE__, s_ctrl->sensor_state); + rc = -EFAULT; + break; + } + if (s_ctrl->func_tbl->sensor_power_down) { + if (s_ctrl->sensordata->misc_regulator) + msm_sensor_misc_regulator(s_ctrl, 0); + + rc = s_ctrl->func_tbl->sensor_power_down(s_ctrl); + if (rc < 0) { + pr_err("%s:%d failed rc %d\n", __func__, + __LINE__, rc); + break; + } + s_ctrl->sensor_state = MSM_SENSOR_POWER_DOWN; + CDBG("%s:%d sensor state %d\n", __func__, __LINE__, + s_ctrl->sensor_state); + } else { + rc = -EFAULT; + } + break; + case CFG_SET_STOP_STREAM_SETTING: { + struct msm_camera_i2c_reg_setting32 stop_setting32; + struct msm_camera_i2c_reg_setting *stop_setting = + &s_ctrl->stop_setting; + struct msm_camera_i2c_reg_array *reg_setting = NULL; + + if (s_ctrl->is_csid_tg_mode) + goto DONE; + + if (copy_from_user(&stop_setting32, + (void *)compat_ptr((cdata->cfg.setting)), + sizeof(struct msm_camera_i2c_reg_setting32))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + stop_setting->addr_type = stop_setting32.addr_type; + stop_setting->data_type = stop_setting32.data_type; + stop_setting->delay = stop_setting32.delay; + stop_setting->size = stop_setting32.size; + + reg_setting = compat_ptr(stop_setting32.reg_setting); + + if (!stop_setting->size) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + stop_setting->reg_setting = kzalloc(stop_setting->size * + (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL); + if (!stop_setting->reg_setting) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -ENOMEM; + break; + } + if (copy_from_user(stop_setting->reg_setting, + (void *)reg_setting, + stop_setting->size * + sizeof(struct msm_camera_i2c_reg_array))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + kfree(stop_setting->reg_setting); + stop_setting->reg_setting = NULL; + stop_setting->size = 0; + rc = -EFAULT; + break; + } + break; + } + + case CFG_SET_I2C_SYNC_PARAM: { + struct msm_camera_cci_ctrl cci_ctrl; + + s_ctrl->sensor_i2c_client->cci_client->cid = + cdata->cfg.sensor_i2c_sync_params.cid; + s_ctrl->sensor_i2c_client->cci_client->id_map = + cdata->cfg.sensor_i2c_sync_params.csid; + + CDBG("I2C_SYNC_PARAM CID:%d, line:%d delay:%d, cdid:%d\n", + s_ctrl->sensor_i2c_client->cci_client->cid, + cdata->cfg.sensor_i2c_sync_params.line, + cdata->cfg.sensor_i2c_sync_params.delay, + cdata->cfg.sensor_i2c_sync_params.csid); + + cci_ctrl.cmd = MSM_CCI_SET_SYNC_CID; + cci_ctrl.cfg.cci_wait_sync_cfg.line = + cdata->cfg.sensor_i2c_sync_params.line; + cci_ctrl.cfg.cci_wait_sync_cfg.delay = + cdata->cfg.sensor_i2c_sync_params.delay; + cci_ctrl.cfg.cci_wait_sync_cfg.cid = + cdata->cfg.sensor_i2c_sync_params.cid; + cci_ctrl.cfg.cci_wait_sync_cfg.csid = + cdata->cfg.sensor_i2c_sync_params.csid; + rc = v4l2_subdev_call(s_ctrl->sensor_i2c_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); + rc = -EFAULT; + break; + } + break; + } + + default: + rc = -EFAULT; + break; + } + +DONE: + mutex_unlock(s_ctrl->msm_sensor_mutex); + + return rc; +} +#endif + +int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp) +{ + struct sensorb_cfg_data *cdata = (struct sensorb_cfg_data *)argp; + int32_t rc = 0; + int32_t i = 0; + mutex_lock(s_ctrl->msm_sensor_mutex); + CDBG("%s:%d %s cfgtype = %d\n", __func__, __LINE__, + s_ctrl->sensordata->sensor_name, cdata->cfgtype); + switch (cdata->cfgtype) { + case CFG_GET_SENSOR_INFO: + memcpy(cdata->cfg.sensor_info.sensor_name, + s_ctrl->sensordata->sensor_name, + sizeof(cdata->cfg.sensor_info.sensor_name)); + cdata->cfg.sensor_info.session_id = + s_ctrl->sensordata->sensor_info->session_id; + for (i = 0; i < SUB_MODULE_MAX; i++) { + cdata->cfg.sensor_info.subdev_id[i] = + s_ctrl->sensordata->sensor_info->subdev_id[i]; + cdata->cfg.sensor_info.subdev_intf[i] = + s_ctrl->sensordata->sensor_info->subdev_intf[i]; + } + cdata->cfg.sensor_info.is_mount_angle_valid = + s_ctrl->sensordata->sensor_info->is_mount_angle_valid; + cdata->cfg.sensor_info.sensor_mount_angle = + s_ctrl->sensordata->sensor_info->sensor_mount_angle; + cdata->cfg.sensor_info.position = + s_ctrl->sensordata->sensor_info->position; + cdata->cfg.sensor_info.modes_supported = + s_ctrl->sensordata->sensor_info->modes_supported; + CDBG("%s:%d sensor name %s\n", __func__, __LINE__, + cdata->cfg.sensor_info.sensor_name); + CDBG("%s:%d session id %d\n", __func__, __LINE__, + cdata->cfg.sensor_info.session_id); + for (i = 0; i < SUB_MODULE_MAX; i++) { + CDBG("%s:%d subdev_id[%d] %d\n", __func__, __LINE__, i, + cdata->cfg.sensor_info.subdev_id[i]); + CDBG("%s:%d subdev_intf[%d] %d\n", __func__, __LINE__, + i, cdata->cfg.sensor_info.subdev_intf[i]); + } + CDBG("%s:%d mount angle valid %d value %d\n", __func__, + __LINE__, cdata->cfg.sensor_info.is_mount_angle_valid, + cdata->cfg.sensor_info.sensor_mount_angle); + + break; + case CFG_GET_SENSOR_INIT_PARAMS: + cdata->cfg.sensor_init_params.modes_supported = + s_ctrl->sensordata->sensor_info->modes_supported; + cdata->cfg.sensor_init_params.position = + s_ctrl->sensordata->sensor_info->position; + cdata->cfg.sensor_init_params.sensor_mount_angle = + s_ctrl->sensordata->sensor_info->sensor_mount_angle; + CDBG("%s:%d init params mode %d pos %d mount %d\n", __func__, + __LINE__, + cdata->cfg.sensor_init_params.modes_supported, + cdata->cfg.sensor_init_params.position, + cdata->cfg.sensor_init_params.sensor_mount_angle); + break; + + case CFG_WRITE_I2C_ARRAY: + case CFG_WRITE_I2C_ARRAY_SYNC: + case CFG_WRITE_I2C_ARRAY_SYNC_BLOCK: + case CFG_WRITE_I2C_ARRAY_ASYNC: { + struct msm_camera_i2c_reg_setting conf_array; + struct msm_camera_i2c_reg_array *reg_setting = NULL; + + if (s_ctrl->is_csid_tg_mode) + goto DONE; + + if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) { + pr_err("%s:%d failed: invalid state %d\n", __func__, + __LINE__, s_ctrl->sensor_state); + rc = -EFAULT; + break; + } + + if (copy_from_user(&conf_array, + (void *)cdata->cfg.setting, + sizeof(struct msm_camera_i2c_reg_setting))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + if (!conf_array.size || + conf_array.size > I2C_REG_DATA_MAX) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + reg_setting = kzalloc(conf_array.size * + (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL); + if (!reg_setting) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -ENOMEM; + break; + } + if (copy_from_user(reg_setting, (void *)conf_array.reg_setting, + conf_array.size * + sizeof(struct msm_camera_i2c_reg_array))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + kfree(reg_setting); + rc = -EFAULT; + break; + } + + conf_array.reg_setting = reg_setting; + if (cdata->cfgtype == CFG_WRITE_I2C_ARRAY) + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl-> + i2c_write_table(s_ctrl->sensor_i2c_client, + &conf_array); + else if (CFG_WRITE_I2C_ARRAY_ASYNC == cdata->cfgtype) + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl-> + i2c_write_table_async(s_ctrl->sensor_i2c_client, + &conf_array); + else if (CFG_WRITE_I2C_ARRAY_SYNC_BLOCK == cdata->cfgtype) + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl-> + i2c_write_table_sync_block( + s_ctrl->sensor_i2c_client, + &conf_array); + else + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl-> + i2c_write_table_sync(s_ctrl->sensor_i2c_client, + &conf_array); + + kfree(reg_setting); + break; + } + case CFG_SLAVE_READ_I2C: { + struct msm_camera_i2c_read_config read_config; + struct msm_camera_i2c_read_config *read_config_ptr = NULL; + uint16_t local_data = 0; + uint16_t orig_slave_addr = 0, read_slave_addr = 0; + uint16_t orig_addr_type = 0, read_addr_type = 0; + + if (s_ctrl->is_csid_tg_mode) + goto DONE; + + read_config_ptr = + (struct msm_camera_i2c_read_config *)cdata->cfg.setting; + if (copy_from_user(&read_config, read_config_ptr, + sizeof(struct msm_camera_i2c_read_config))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + read_slave_addr = read_config.slave_addr; + read_addr_type = read_config.addr_type; + CDBG("%s:CFG_SLAVE_READ_I2C:", __func__); + CDBG("%s:slave_addr=0x%x reg_addr=0x%x, data_type=%d\n", + __func__, read_config.slave_addr, + read_config.reg_addr, read_config.data_type); + if (s_ctrl->sensor_i2c_client->cci_client) { + orig_slave_addr = + s_ctrl->sensor_i2c_client->cci_client->sid; + s_ctrl->sensor_i2c_client->cci_client->sid = + read_slave_addr >> 1; + } else if (s_ctrl->sensor_i2c_client->client) { + orig_slave_addr = + s_ctrl->sensor_i2c_client->client->addr; + s_ctrl->sensor_i2c_client->client->addr = + read_slave_addr >> 1; + } else { + pr_err("%s: error: no i2c/cci client found.", __func__); + rc = -EFAULT; + break; + } + CDBG("%s:orig_slave_addr=0x%x, new_slave_addr=0x%x", + __func__, orig_slave_addr, + read_slave_addr >> 1); + + orig_addr_type = s_ctrl->sensor_i2c_client->addr_type; + s_ctrl->sensor_i2c_client->addr_type = read_addr_type; + + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read( + s_ctrl->sensor_i2c_client, + read_config.reg_addr, + &local_data, read_config.data_type); + if (s_ctrl->sensor_i2c_client->cci_client) { + s_ctrl->sensor_i2c_client->cci_client->sid = + orig_slave_addr; + } else if (s_ctrl->sensor_i2c_client->client) { + s_ctrl->sensor_i2c_client->client->addr = + orig_slave_addr; + } + s_ctrl->sensor_i2c_client->addr_type = orig_addr_type; + + if (rc < 0) { + pr_err("%s:%d: i2c_read failed\n", __func__, __LINE__); + break; + } + read_config_ptr->data = local_data; + break; + } + case CFG_SLAVE_WRITE_I2C_ARRAY: { + struct msm_camera_i2c_array_write_config write_config; + struct msm_camera_i2c_reg_array *reg_setting = NULL; + uint16_t orig_slave_addr = 0, write_slave_addr = 0; + uint16_t orig_addr_type = 0, write_addr_type = 0; + + if (s_ctrl->is_csid_tg_mode) + goto DONE; + + if (copy_from_user(&write_config, + (void *)cdata->cfg.setting, + sizeof(struct msm_camera_i2c_array_write_config))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + CDBG("%s:CFG_SLAVE_WRITE_I2C_ARRAY:", __func__); + CDBG("%s:slave_addr=0x%x, array_size=%d\n", __func__, + write_config.slave_addr, + write_config.conf_array.size); + + if (!write_config.conf_array.size || + write_config.conf_array.size > I2C_REG_DATA_MAX) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + reg_setting = kzalloc(write_config.conf_array.size * + (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL); + if (!reg_setting) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -ENOMEM; + break; + } + if (copy_from_user(reg_setting, + (void *)(write_config.conf_array.reg_setting), + write_config.conf_array.size * + sizeof(struct msm_camera_i2c_reg_array))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + kfree(reg_setting); + rc = -EFAULT; + break; + } + write_config.conf_array.reg_setting = reg_setting; + write_slave_addr = write_config.slave_addr; + write_addr_type = write_config.conf_array.addr_type; + if (s_ctrl->sensor_i2c_client->cci_client) { + orig_slave_addr = + s_ctrl->sensor_i2c_client->cci_client->sid; + s_ctrl->sensor_i2c_client->cci_client->sid = + write_slave_addr >> 1; + } else if (s_ctrl->sensor_i2c_client->client) { + orig_slave_addr = + s_ctrl->sensor_i2c_client->client->addr; + s_ctrl->sensor_i2c_client->client->addr = + write_slave_addr >> 1; + } else { + pr_err("%s: error: no i2c/cci client found.", __func__); + kfree(reg_setting); + rc = -EFAULT; + break; + } + CDBG("%s:orig_slave_addr=0x%x, new_slave_addr=0x%x", + __func__, orig_slave_addr, + write_slave_addr >> 1); + orig_addr_type = s_ctrl->sensor_i2c_client->addr_type; + s_ctrl->sensor_i2c_client->addr_type = write_addr_type; + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table( + s_ctrl->sensor_i2c_client, &(write_config.conf_array)); + s_ctrl->sensor_i2c_client->addr_type = orig_addr_type; + if (s_ctrl->sensor_i2c_client->cci_client) { + s_ctrl->sensor_i2c_client->cci_client->sid = + orig_slave_addr; + } else if (s_ctrl->sensor_i2c_client->client) { + s_ctrl->sensor_i2c_client->client->addr = + orig_slave_addr; + } else { + pr_err("%s: error: no i2c/cci client found.", __func__); + kfree(reg_setting); + rc = -EFAULT; + break; + } + kfree(reg_setting); + break; + } + case CFG_WRITE_I2C_SEQ_ARRAY: { + struct msm_camera_i2c_seq_reg_setting conf_array; + struct msm_camera_i2c_seq_reg_array *reg_setting = NULL; + + if (s_ctrl->is_csid_tg_mode) + goto DONE; + + if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) { + pr_err("%s:%d failed: invalid state %d\n", __func__, + __LINE__, s_ctrl->sensor_state); + rc = -EFAULT; + break; + } + + if (copy_from_user(&conf_array, + (void *)cdata->cfg.setting, + sizeof(struct msm_camera_i2c_seq_reg_setting))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + if (!conf_array.size || + conf_array.size > I2C_SEQ_REG_DATA_MAX) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + reg_setting = kzalloc(conf_array.size * + (sizeof(struct msm_camera_i2c_seq_reg_array)), + GFP_KERNEL); + if (!reg_setting) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -ENOMEM; + break; + } + if (copy_from_user(reg_setting, (void *)conf_array.reg_setting, + conf_array.size * + sizeof(struct msm_camera_i2c_seq_reg_array))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + kfree(reg_setting); + rc = -EFAULT; + break; + } + + conf_array.reg_setting = reg_setting; + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl-> + i2c_write_seq_table(s_ctrl->sensor_i2c_client, + &conf_array); + kfree(reg_setting); + break; + } + + case CFG_POWER_UP: + if (s_ctrl->is_csid_tg_mode) + goto DONE; + + if (s_ctrl->sensor_state != MSM_SENSOR_POWER_DOWN) { + pr_err("%s:%d failed: invalid state %d\n", __func__, + __LINE__, s_ctrl->sensor_state); + rc = -EFAULT; + break; + } + if (s_ctrl->func_tbl->sensor_power_up) { + if (s_ctrl->sensordata->misc_regulator) + msm_sensor_misc_regulator(s_ctrl, 1); + + rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl); + if (rc < 0) { + pr_err("%s:%d failed rc %d\n", __func__, + __LINE__, rc); + break; + } + s_ctrl->sensor_state = MSM_SENSOR_POWER_UP; + CDBG("%s:%d sensor state %d\n", __func__, __LINE__, + s_ctrl->sensor_state); + } else { + rc = -EFAULT; + } + break; + + case CFG_POWER_DOWN: + if (s_ctrl->is_csid_tg_mode) + goto DONE; + + kfree(s_ctrl->stop_setting.reg_setting); + s_ctrl->stop_setting.reg_setting = NULL; + if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) { + pr_err("%s:%d failed: invalid state %d\n", __func__, + __LINE__, s_ctrl->sensor_state); + rc = -EFAULT; + break; + } + if (s_ctrl->func_tbl->sensor_power_down) { + if (s_ctrl->sensordata->misc_regulator) + msm_sensor_misc_regulator(s_ctrl, 0); + + rc = s_ctrl->func_tbl->sensor_power_down(s_ctrl); + if (rc < 0) { + pr_err("%s:%d failed rc %d\n", __func__, + __LINE__, rc); + break; + } + s_ctrl->sensor_state = MSM_SENSOR_POWER_DOWN; + CDBG("%s:%d sensor state %d\n", __func__, __LINE__, + s_ctrl->sensor_state); + } else { + rc = -EFAULT; + } + break; + + case CFG_SET_STOP_STREAM_SETTING: { + struct msm_camera_i2c_reg_setting *stop_setting = + &s_ctrl->stop_setting; + struct msm_camera_i2c_reg_array *reg_setting = NULL; + + if (s_ctrl->is_csid_tg_mode) + goto DONE; + + if (copy_from_user(stop_setting, + (void *)cdata->cfg.setting, + sizeof(struct msm_camera_i2c_reg_setting))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + reg_setting = stop_setting->reg_setting; + + if (!stop_setting->size) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + stop_setting->reg_setting = kzalloc(stop_setting->size * + (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL); + if (!stop_setting->reg_setting) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -ENOMEM; + break; + } + if (copy_from_user(stop_setting->reg_setting, + (void *)reg_setting, + stop_setting->size * + sizeof(struct msm_camera_i2c_reg_array))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + kfree(stop_setting->reg_setting); + stop_setting->reg_setting = NULL; + stop_setting->size = 0; + rc = -EFAULT; + break; + } + break; + } + + case CFG_SET_I2C_SYNC_PARAM: { + struct msm_camera_cci_ctrl cci_ctrl; + + s_ctrl->sensor_i2c_client->cci_client->cid = + cdata->cfg.sensor_i2c_sync_params.cid; + s_ctrl->sensor_i2c_client->cci_client->id_map = + cdata->cfg.sensor_i2c_sync_params.csid; + + CDBG("I2C_SYNC_PARAM CID:%d, line:%d delay:%d, cdid:%d\n", + s_ctrl->sensor_i2c_client->cci_client->cid, + cdata->cfg.sensor_i2c_sync_params.line, + cdata->cfg.sensor_i2c_sync_params.delay, + cdata->cfg.sensor_i2c_sync_params.csid); + + cci_ctrl.cmd = MSM_CCI_SET_SYNC_CID; + cci_ctrl.cfg.cci_wait_sync_cfg.line = + cdata->cfg.sensor_i2c_sync_params.line; + cci_ctrl.cfg.cci_wait_sync_cfg.delay = + cdata->cfg.sensor_i2c_sync_params.delay; + cci_ctrl.cfg.cci_wait_sync_cfg.cid = + cdata->cfg.sensor_i2c_sync_params.cid; + cci_ctrl.cfg.cci_wait_sync_cfg.csid = + cdata->cfg.sensor_i2c_sync_params.csid; + rc = v4l2_subdev_call(s_ctrl->sensor_i2c_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); + rc = -EFAULT; + break; + } + break; + } + + default: + rc = -EFAULT; + break; + } + +DONE: + mutex_unlock(s_ctrl->msm_sensor_mutex); + + return rc; +} + +int msm_sensor_check_id(struct msm_sensor_ctrl_t *s_ctrl) +{ + int rc; + + if (s_ctrl->func_tbl->sensor_match_id) + rc = s_ctrl->func_tbl->sensor_match_id(s_ctrl); + else + rc = msm_sensor_match_id(s_ctrl); + if (rc < 0) + pr_err("%s:%d match id failed rc %d\n", __func__, __LINE__, rc); + return rc; +} + +static int msm_sensor_power(struct v4l2_subdev *sd, int on) +{ + int rc = 0; + struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd); + mutex_lock(s_ctrl->msm_sensor_mutex); + if (!on && s_ctrl->sensor_state == MSM_SENSOR_POWER_UP) { + s_ctrl->func_tbl->sensor_power_down(s_ctrl); + s_ctrl->sensor_state = MSM_SENSOR_POWER_DOWN; + } + mutex_unlock(s_ctrl->msm_sensor_mutex); + return rc; +} + +static int msm_sensor_v4l2_enum_fmt(struct v4l2_subdev *sd, + unsigned int index, enum v4l2_mbus_pixelcode *code) +{ + struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd); + + if ((unsigned int)index >= s_ctrl->sensor_v4l2_subdev_info_size) + return -EINVAL; + + *code = s_ctrl->sensor_v4l2_subdev_info[index].code; + return 0; +} + +static struct v4l2_subdev_core_ops msm_sensor_subdev_core_ops = { + .ioctl = msm_sensor_subdev_ioctl, + .s_power = msm_sensor_power, +}; + +static struct v4l2_subdev_video_ops msm_sensor_subdev_video_ops = { + .enum_mbus_fmt = msm_sensor_v4l2_enum_fmt, +}; + +static struct v4l2_subdev_ops msm_sensor_subdev_ops = { + .core = &msm_sensor_subdev_core_ops, + .video = &msm_sensor_subdev_video_ops, +}; + +static struct msm_sensor_fn_t msm_sensor_func_tbl = { + .sensor_config = msm_sensor_config, +#ifdef CONFIG_COMPAT + .sensor_config32 = msm_sensor_config32, +#endif + .sensor_power_up = msm_sensor_power_up, + .sensor_power_down = msm_sensor_power_down, + .sensor_match_id = msm_sensor_match_id, +}; + +static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = { + .i2c_read = msm_camera_cci_i2c_read, + .i2c_read_seq = msm_camera_cci_i2c_read_seq, + .i2c_write = msm_camera_cci_i2c_write, + .i2c_write_table = msm_camera_cci_i2c_write_table, + .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_cci_i2c_write_table_w_microdelay, + .i2c_util = msm_sensor_cci_i2c_util, + .i2c_write_conf_tbl = msm_camera_cci_i2c_write_conf_tbl, + .i2c_write_table_async = msm_camera_cci_i2c_write_table_async, + .i2c_write_table_sync = msm_camera_cci_i2c_write_table_sync, + .i2c_write_table_sync_block = msm_camera_cci_i2c_write_table_sync_block, + +}; + +static struct msm_camera_i2c_fn_t msm_sensor_qup_func_tbl = { + .i2c_read = msm_camera_qup_i2c_read, + .i2c_read_seq = msm_camera_qup_i2c_read_seq, + .i2c_write = msm_camera_qup_i2c_write, + .i2c_write_table = msm_camera_qup_i2c_write_table, + .i2c_write_seq_table = msm_camera_qup_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_qup_i2c_write_table_w_microdelay, + .i2c_write_conf_tbl = msm_camera_qup_i2c_write_conf_tbl, + .i2c_write_table_async = msm_camera_qup_i2c_write_table, + .i2c_write_table_sync = msm_camera_qup_i2c_write_table, + .i2c_write_table_sync_block = msm_camera_qup_i2c_write_table, +}; + +int32_t msm_sensor_init_default_params(struct msm_sensor_ctrl_t *s_ctrl) +{ + struct msm_camera_cci_client *cci_client = NULL; + unsigned long mount_pos = 0; + + /* Validate input parameters */ + if (!s_ctrl) { + pr_err("%s:%d failed: invalid params s_ctrl %p\n", __func__, + __LINE__, s_ctrl); + return -EINVAL; + } + + if (!s_ctrl->sensor_i2c_client) { + pr_err("%s:%d failed: invalid params sensor_i2c_client %p\n", + __func__, __LINE__, s_ctrl->sensor_i2c_client); + return -EINVAL; + } + + /* Initialize cci_client */ + s_ctrl->sensor_i2c_client->cci_client = kzalloc(sizeof( + struct msm_camera_cci_client), GFP_KERNEL); + if (!s_ctrl->sensor_i2c_client->cci_client) { + pr_err("%s:%d failed: no memory cci_client %p\n", __func__, + __LINE__, s_ctrl->sensor_i2c_client->cci_client); + return -ENOMEM; + } + + if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE) { + cci_client = s_ctrl->sensor_i2c_client->cci_client; + + /* Get CCI subdev */ + cci_client->cci_subdev = msm_cci_get_subdev(); + + /* Update CCI / I2C function table */ + if (!s_ctrl->sensor_i2c_client->i2c_func_tbl) + s_ctrl->sensor_i2c_client->i2c_func_tbl = + &msm_sensor_cci_func_tbl; + } else { + if (!s_ctrl->sensor_i2c_client->i2c_func_tbl) { + CDBG("%s:%d\n", __func__, __LINE__); + s_ctrl->sensor_i2c_client->i2c_func_tbl = + &msm_sensor_qup_func_tbl; + } + } + + /* Update function table driven by ioctl */ + if (!s_ctrl->func_tbl) + s_ctrl->func_tbl = &msm_sensor_func_tbl; + + /* Update v4l2 subdev ops table */ + if (!s_ctrl->sensor_v4l2_subdev_ops) + s_ctrl->sensor_v4l2_subdev_ops = &msm_sensor_subdev_ops; + + /* Update sensor mount angle and position in media entity flag */ + mount_pos = s_ctrl->sensordata->sensor_info->position << 16; + mount_pos = mount_pos | ((s_ctrl->sensordata->sensor_info-> + sensor_mount_angle / 90) << 8); + s_ctrl->msm_sd.sd.entity.flags = mount_pos | MEDIA_ENT_FL_DEFAULT; + + return 0; +} diff --git a/camera/sensor/msm_sensor.h b/camera/sensor/msm_sensor.h new file mode 100644 index 00000000..f64df5ac --- /dev/null +++ b/camera/sensor/msm_sensor.h @@ -0,0 +1,125 @@ +/* Copyright (c) 2011-2015, 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_SENSOR_H +#define MSM_SENSOR_H + +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/gpio.h> +#include <lenovo_soc/qcom/camera2.h> +#include <lenovo_media/msm_cam_sensor.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-ioctl.h> +#include "msm_camera_i2c.h" +#include "msm_camera_dt_util.h" +#include "msm_sd.h" + +#define DEFINE_MSM_MUTEX(mutexname) \ + static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) + +enum msm_sensor_sensor_slave_info_type { + MSM_SENSOR_SLAVEADDR_DATA, + MSM_SENSOR_IDREGADDR_DATA, + MSM_SENSOR_SENSOR_ID_DATA, + MSM_SENSOR_SENIDMASK_DATA, + MSM_SENSOR_NUM_ID_INFO_DATA, +}; + +struct msm_sensor_ctrl_t; + +enum msm_sensor_state_t { + MSM_SENSOR_POWER_DOWN, + MSM_SENSOR_POWER_UP, +}; + +struct msm_sensor_fn_t { + int (*sensor_config)(struct msm_sensor_ctrl_t *, void __user *); +#ifdef CONFIG_COMPAT + int (*sensor_config32)(struct msm_sensor_ctrl_t *, void __user *); +#endif + int (*sensor_power_down)(struct msm_sensor_ctrl_t *); + int (*sensor_power_up)(struct msm_sensor_ctrl_t *); + int (*sensor_match_id)(struct msm_sensor_ctrl_t *); +}; + +struct msm_sensor_ctrl_t { + struct platform_device *pdev; + struct mutex *msm_sensor_mutex; + + enum msm_camera_device_type_t sensor_device_type; + struct msm_camera_sensor_board_info *sensordata; + struct msm_sensor_power_setting_array power_setting_array; + struct msm_sensor_packed_cfg_t *cfg_override; + struct msm_sd_subdev msm_sd; + enum cci_i2c_master_t cci_i2c_master; + + struct msm_camera_i2c_client *sensor_i2c_client; + struct v4l2_subdev_info *sensor_v4l2_subdev_info; + uint8_t sensor_v4l2_subdev_info_size; + struct v4l2_subdev_ops *sensor_v4l2_subdev_ops; + struct msm_sensor_fn_t *func_tbl; + struct msm_camera_i2c_reg_setting stop_setting; + void *misc_regulator; + enum msm_sensor_state_t sensor_state; + uint8_t is_probe_succeed; + uint32_t id; + struct device_node *of_node; + enum msm_camera_stream_type_t camera_stream_type; + uint32_t set_mclk_23880000; + uint8_t is_csid_tg_mode; +}; + +int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp); + +int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl); + +int msm_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl); + +int msm_sensor_check_id(struct msm_sensor_ctrl_t *s_ctrl); + +int msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl); + +int msm_sensor_update_cfg(struct msm_sensor_ctrl_t *s_ctrl); + +int msm_sensor_free_sensor_data(struct msm_sensor_ctrl_t *s_ctrl); + +int32_t msm_sensor_init_default_params(struct msm_sensor_ctrl_t *s_ctrl); + +int32_t msm_sensor_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); + +int32_t msm_sensor_get_dt_gpio_set_tbl(struct device_node *of_node, + struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, + uint16_t gpio_array_size); + +int32_t msm_sensor_init_gpio_pin_tbl(struct device_node *of_node, + struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, + uint16_t gpio_array_size); +#ifdef CONFIG_COMPAT +long msm_sensor_subdev_fops_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg); +#endif +#endif diff --git a/camera/sensor/msm_sensor_driver.c b/camera/sensor/msm_sensor_driver.c new file mode 100644 index 00000000..b69008f4 --- /dev/null +++ b/camera/sensor/msm_sensor_driver.c @@ -0,0 +1,1342 @@ +/* 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. + */ + +#define SENSOR_DRIVER_I2C "i2c_camera" +/* Header file declaration */ +#include "msm_sensor.h" +#include "msm_sd.h" +#include "camera.h" +#include "msm_cci.h" +#include "msm_camera_dt_util.h" +#ifdef CONFIG_CAMERA_WT_FACTORY_SUPPORTED +#include <linux/hardware_info.h> +#endif +/* Logging macro */ +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +#define SENSOR_MAX_MOUNTANGLE (360) + +static struct v4l2_file_operations msm_sensor_v4l2_subdev_fops; +static int32_t msm_sensor_driver_platform_probe(struct platform_device *pdev); + +/* Static declaration */ +static struct msm_sensor_ctrl_t *g_sctrl[MAX_CAMERAS]; + +static int msm_sensor_platform_remove(struct platform_device *pdev) +{ + struct msm_sensor_ctrl_t *s_ctrl; + + pr_err("%s: sensor FREE\n", __func__); + + s_ctrl = g_sctrl[pdev->id]; + if (!s_ctrl) { + pr_err("%s: sensor device is NULL\n", __func__); + return 0; + } + + msm_sensor_free_sensor_data(s_ctrl); + kfree(s_ctrl->msm_sensor_mutex); + kfree(s_ctrl->sensor_i2c_client); + kfree(s_ctrl); + g_sctrl[pdev->id] = NULL; + + return 0; +} + + +static const struct of_device_id msm_sensor_driver_dt_match[] = { + {.compatible = "qcom,camera"}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_sensor_driver_dt_match); + +static struct platform_driver msm_sensor_platform_driver = { + .probe = msm_sensor_driver_platform_probe, + .driver = { + .name = "qcom,camera", + .owner = THIS_MODULE, + .of_match_table = msm_sensor_driver_dt_match, + }, + .remove = msm_sensor_platform_remove, +}; + +static struct v4l2_subdev_info msm_sensor_driver_subdev_info[] = { + { + .code = V4L2_MBUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_JPEG, + .fmt = 1, + .order = 0, + }, +}; + +static int32_t msm_sensor_driver_create_i2c_v4l_subdev + (struct msm_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + uint32_t session_id = 0; + struct i2c_client *client = s_ctrl->sensor_i2c_client->client; + + CDBG("%s %s I2c probe succeeded\n", __func__, client->name); + rc = camera_init_v4l2(&client->dev, &session_id); + if (rc < 0) { + pr_err("failed: camera_init_i2c_v4l2 rc %d", rc); + return rc; + } + CDBG("%s rc %d session_id %d\n", __func__, rc, session_id); + snprintf(s_ctrl->msm_sd.sd.name, + sizeof(s_ctrl->msm_sd.sd.name), "%s", + s_ctrl->sensordata->sensor_name); + v4l2_i2c_subdev_init(&s_ctrl->msm_sd.sd, client, + s_ctrl->sensor_v4l2_subdev_ops); + v4l2_set_subdevdata(&s_ctrl->msm_sd.sd, client); + s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + media_entity_init(&s_ctrl->msm_sd.sd.entity, 0, NULL, 0); + s_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + s_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_SENSOR; + s_ctrl->msm_sd.sd.entity.name = s_ctrl->msm_sd.sd.name; + s_ctrl->sensordata->sensor_info->session_id = session_id; + s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3; + msm_sd_register(&s_ctrl->msm_sd); + msm_sensor_v4l2_subdev_fops = v4l2_subdev_fops; +#ifdef CONFIG_COMPAT + msm_sensor_v4l2_subdev_fops.compat_ioctl32 = + msm_sensor_subdev_fops_ioctl; +#endif + s_ctrl->msm_sd.sd.devnode->fops = + &msm_sensor_v4l2_subdev_fops; + CDBG("%s:%d\n", __func__, __LINE__); + return rc; +} + +static int32_t msm_sensor_driver_create_v4l_subdev + (struct msm_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + uint32_t session_id = 0; + + rc = camera_init_v4l2(&s_ctrl->pdev->dev, &session_id); + if (rc < 0) { + pr_err("failed: camera_init_v4l2 rc %d", rc); + return rc; + } + CDBG("rc %d session_id %d", rc, session_id); + s_ctrl->sensordata->sensor_info->session_id = session_id; + + /* Create /dev/v4l-subdevX device */ + v4l2_subdev_init(&s_ctrl->msm_sd.sd, s_ctrl->sensor_v4l2_subdev_ops); + snprintf(s_ctrl->msm_sd.sd.name, sizeof(s_ctrl->msm_sd.sd.name), "%s", + s_ctrl->sensordata->sensor_name); + v4l2_set_subdevdata(&s_ctrl->msm_sd.sd, s_ctrl->pdev); + s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + media_entity_init(&s_ctrl->msm_sd.sd.entity, 0, NULL, 0); + s_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + s_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_SENSOR; + s_ctrl->msm_sd.sd.entity.name = s_ctrl->msm_sd.sd.name; + s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3; + msm_sd_register(&s_ctrl->msm_sd); + msm_cam_copy_v4l2_subdev_fops(&msm_sensor_v4l2_subdev_fops); +#ifdef CONFIG_COMPAT + msm_sensor_v4l2_subdev_fops.compat_ioctl32 = + msm_sensor_subdev_fops_ioctl; +#endif + s_ctrl->msm_sd.sd.devnode->fops = + &msm_sensor_v4l2_subdev_fops; + + return rc; +} + +static int32_t msm_sensor_fill_eeprom_subdevid_by_name( + struct msm_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + const char *eeprom_name; + struct device_node *src_node = NULL; + uint32_t val = 0, eeprom_name_len; + int32_t *eeprom_subdev_id, i, userspace_probe = 0; + int32_t count = 0; + struct msm_sensor_info_t *sensor_info; + struct device_node *of_node = s_ctrl->of_node; + const void *p; + + if (!s_ctrl->sensordata->eeprom_name || !of_node) + return -EINVAL; + + eeprom_name_len = strlen(s_ctrl->sensordata->eeprom_name); + if (eeprom_name_len >= MAX_SENSOR_NAME) + return -EINVAL; + + sensor_info = s_ctrl->sensordata->sensor_info; + eeprom_subdev_id = &sensor_info->subdev_id[SUB_MODULE_EEPROM]; + /* + * string for eeprom name is valid, set sudev id to -1 + * and try to found new id + */ + *eeprom_subdev_id = -1; + + if (0 == eeprom_name_len) + return 0; + + p = of_get_property(of_node, "qcom,eeprom-src", &count); + if (!p || !count) + return 0; + + count /= sizeof(uint32_t); + for (i = 0; i < count; i++) { + userspace_probe = 0; + eeprom_name = NULL; + src_node = of_parse_phandle(of_node, "qcom,eeprom-src", i); + if (!src_node) { + pr_err("eeprom src node NULL\n"); + continue; + } + /* In the case of eeprom probe from kernel eeprom name + should be present, Otherwise it will throw as errors */ + rc = of_property_read_string(src_node, "qcom,eeprom-name", + &eeprom_name); + if (rc < 0) { + pr_err("%s:%d Eeprom userspace probe for %s\n", + __func__, __LINE__, + s_ctrl->sensordata->eeprom_name); + of_node_put(src_node); + userspace_probe = 1; + if (count > 1) + return -EINVAL; + } + if (!userspace_probe && + strcmp(eeprom_name, s_ctrl->sensordata->eeprom_name)) + continue; + + rc = of_property_read_u32(src_node, "cell-index", &val); + if (rc < 0) { + pr_err("%s qcom,eeprom cell index %d, rc %d\n", + __func__, val, rc); + of_node_put(src_node); + if (userspace_probe) + return -EINVAL; + continue; + } + + *eeprom_subdev_id = val; + CDBG("%s:%d Eeprom subdevice id is %d\n", + __func__, __LINE__, val); + of_node_put(src_node); + src_node = NULL; + break; + } + + return rc; +} + +static int32_t msm_sensor_fill_actuator_subdevid_by_name( + struct msm_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + struct device_node *src_node = NULL; + uint32_t val = 0, actuator_name_len; + int32_t *actuator_subdev_id; + struct msm_sensor_info_t *sensor_info; + struct device_node *of_node = s_ctrl->of_node; + + if (!s_ctrl->sensordata->actuator_name || !of_node) + return -EINVAL; + + actuator_name_len = strlen(s_ctrl->sensordata->actuator_name); + if (actuator_name_len >= MAX_SENSOR_NAME) + return -EINVAL; + + sensor_info = s_ctrl->sensordata->sensor_info; + actuator_subdev_id = &sensor_info->subdev_id[SUB_MODULE_ACTUATOR]; + /* + * string for actuator name is valid, set sudev id to -1 + * and try to found new id + */ + *actuator_subdev_id = -1; + + if (0 == actuator_name_len) + return 0; + + 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__); + return -EINVAL; + } + *actuator_subdev_id = val; + of_node_put(src_node); + src_node = NULL; + } + + return rc; +} + +static int32_t msm_sensor_fill_ois_subdevid_by_name( + struct msm_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + struct device_node *src_node = NULL; + uint32_t val = 0, ois_name_len; + int32_t *ois_subdev_id; + struct msm_sensor_info_t *sensor_info; + struct device_node *of_node = s_ctrl->of_node; + + if (!s_ctrl->sensordata->ois_name || !of_node) + return -EINVAL; + + ois_name_len = strlen(s_ctrl->sensordata->ois_name); + if (ois_name_len >= MAX_SENSOR_NAME) + return -EINVAL; + + sensor_info = s_ctrl->sensordata->sensor_info; + ois_subdev_id = &sensor_info->subdev_id[SUB_MODULE_OIS]; + /* + * string for ois name is valid, set sudev id to -1 + * and try to found new id + */ + *ois_subdev_id = -1; + + if (0 == ois_name_len) + return 0; + + 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__); + return -EINVAL; + } + *ois_subdev_id = val; + of_node_put(src_node); + src_node = NULL; + } + + return rc; +} + +static int32_t msm_sensor_fill_slave_info_init_params( + struct msm_camera_sensor_slave_info *slave_info, + struct msm_sensor_info_t *sensor_info) +{ + struct msm_sensor_init_params *sensor_init_params; + if (!slave_info || !sensor_info) + return -EINVAL; + + if (!slave_info->is_init_params_valid) + return 0; + + sensor_init_params = &slave_info->sensor_init_params; + if (INVALID_CAMERA_B != sensor_init_params->position) + sensor_info->position = + sensor_init_params->position; + + if (SENSOR_MAX_MOUNTANGLE > sensor_init_params->sensor_mount_angle) { + sensor_info->sensor_mount_angle = + sensor_init_params->sensor_mount_angle; + sensor_info->is_mount_angle_valid = 1; + } + + if (CAMERA_MODE_INVALID != sensor_init_params->modes_supported) + sensor_info->modes_supported = + sensor_init_params->modes_supported; + + return 0; +} + + +static int32_t msm_sensor_validate_slave_info( + struct msm_sensor_info_t *sensor_info) +{ + if (INVALID_CAMERA_B == sensor_info->position) { + sensor_info->position = BACK_CAMERA_B; + CDBG("%s:%d Set default sensor position\n", + __func__, __LINE__); + } + if (CAMERA_MODE_INVALID == sensor_info->modes_supported) { + sensor_info->modes_supported = CAMERA_MODE_2D_B; + CDBG("%s:%d Set default sensor modes_supported\n", + __func__, __LINE__); + } + if (SENSOR_MAX_MOUNTANGLE <= sensor_info->sensor_mount_angle) { + sensor_info->sensor_mount_angle = 0; + CDBG("%s:%d Set default sensor mount angle\n", + __func__, __LINE__); + sensor_info->is_mount_angle_valid = 1; + } + return 0; +} + +#ifdef CONFIG_COMPAT +static int32_t msm_sensor_get_pw_settings_compat( + struct msm_sensor_power_setting *ps, + struct msm_sensor_power_setting *us_ps, uint32_t size) +{ + int32_t rc = 0, i = 0; + struct msm_sensor_power_setting32 *ps32 = + kzalloc(sizeof(*ps32) * size, GFP_KERNEL); + + if (!ps32) { + pr_err("failed: no memory ps32"); + return -ENOMEM; + } + if (copy_from_user(ps32, (void *)us_ps, sizeof(*ps32) * size)) { + pr_err("failed: copy_from_user"); + kfree(ps32); + return -EFAULT; + } + for (i = 0; i < size; i++) { + ps[i].config_val = ps32[i].config_val; + ps[i].delay = ps32[i].delay; + ps[i].seq_type = ps32[i].seq_type; + ps[i].seq_val = ps32[i].seq_val; + } + kfree(ps32); + return rc; +} +#endif + +static int32_t msm_sensor_create_pd_settings(void *setting, + struct msm_sensor_power_setting *pd, uint32_t size_down, + struct msm_sensor_power_setting *pu) +{ + int32_t rc = 0; + int c, end; + struct msm_sensor_power_setting pd_tmp; + + pr_err("Generating power_down_setting"); + +#ifdef CONFIG_COMPAT + if (is_compat_task()) { + int i = 0; + struct msm_sensor_power_setting32 *power_setting_iter = + (struct msm_sensor_power_setting32 *)compat_ptr(( + (struct msm_camera_sensor_slave_info32 *)setting)-> + power_setting_array.power_setting); + + for (i = 0; i < size_down; i++) { + pd[i].config_val = power_setting_iter[i].config_val; + pd[i].delay = power_setting_iter[i].delay; + pd[i].seq_type = power_setting_iter[i].seq_type; + pd[i].seq_val = power_setting_iter[i].seq_val; + } + } else +#endif + { + if (copy_from_user(pd, (void *)pu, sizeof(*pd) * size_down)) { + pr_err("failed: copy_from_user"); + return -EFAULT; + } + } + /* reverse */ + end = size_down - 1; + for (c = 0; c < size_down/2; c++) { + pd_tmp = pd[c]; + pd[c] = pd[end]; + pd[end] = pd_tmp; + end--; + } + return rc; +} + +static int32_t msm_sensor_get_power_down_settings(void *setting, + struct msm_camera_sensor_slave_info *slave_info, + struct msm_camera_power_ctrl_t *power_info) +{ + int32_t rc = 0; + uint16_t size_down = 0; + uint16_t i = 0; + struct msm_sensor_power_setting *pd = NULL; + + /* DOWN */ + size_down = slave_info->power_setting_array.size_down; + if (!size_down || size_down > MAX_POWER_CONFIG) + size_down = slave_info->power_setting_array.size; + /* Validate size_down */ + if (size_down > MAX_POWER_CONFIG) { + pr_err("failed: invalid size_down %d", size_down); + return -EINVAL; + } + /* Allocate memory for power down setting */ + pd = kzalloc(sizeof(*pd) * size_down, GFP_KERNEL); + if (!pd) { + pr_err("failed: no memory power_setting %p", pd); + return -EFAULT; + } + + if (slave_info->power_setting_array.power_down_setting) { +#ifdef CONFIG_COMPAT + if (is_compat_task()) { + rc = msm_sensor_get_pw_settings_compat( + pd, slave_info->power_setting_array. + power_down_setting, size_down); + if (rc < 0) { + pr_err("failed"); + kfree(pd); + return -EFAULT; + } + } else +#endif + if (copy_from_user(pd, (void *)slave_info->power_setting_array. + power_down_setting, sizeof(*pd) * size_down)) { + pr_err("failed: copy_from_user"); + kfree(pd); + return -EFAULT; + } + } else { + + rc = msm_sensor_create_pd_settings(setting, pd, size_down, + slave_info->power_setting_array.power_setting); + if (rc < 0) { + pr_err("failed"); + kfree(pd); + return -EFAULT; + } + } + + /* Fill power down setting and power down setting size */ + power_info->power_down_setting = pd; + power_info->power_down_setting_size = size_down; + + /* Print power setting */ + for (i = 0; i < size_down; i++) { + CDBG("DOWN seq_type %d seq_val %d config_val %ld delay %d", + pd[i].seq_type, pd[i].seq_val, + pd[i].config_val, pd[i].delay); + } + return rc; +} + +static int32_t msm_sensor_get_power_up_settings(void *setting, + struct msm_camera_sensor_slave_info *slave_info, + struct msm_camera_power_ctrl_t *power_info) +{ + int32_t rc = 0; + uint16_t size = 0; + uint16_t i = 0; + struct msm_sensor_power_setting *pu = NULL; + + size = slave_info->power_setting_array.size; + + /* Validate size */ + if ((size == 0) || (size > MAX_POWER_CONFIG)) { + pr_err("failed: invalid power_setting size_up = %d\n", size); + return -EINVAL; + } + + /* Allocate memory for power up setting */ + pu = kzalloc(sizeof(*pu) * size, GFP_KERNEL); + if (!pu) { + pr_err("failed: no memory power_setting %p", pu); + return -ENOMEM; + } + +#ifdef CONFIG_COMPAT + if (is_compat_task()) { + rc = msm_sensor_get_pw_settings_compat(pu, + slave_info->power_setting_array. + power_setting, size); + if (rc < 0) { + pr_err("failed"); + kfree(pu); + return -EFAULT; + } + } else +#endif + { + if (copy_from_user(pu, + (void *)slave_info->power_setting_array.power_setting, + sizeof(*pu) * size)) { + pr_err("failed: copy_from_user"); + kfree(pu); + return -EFAULT; + } + } + + /* Print power setting */ + for (i = 0; i < size; i++) { + CDBG("UP seq_type %d seq_val %d config_val %ld delay %d", + pu[i].seq_type, pu[i].seq_val, + pu[i].config_val, pu[i].delay); + } + + + /* Fill power up setting and power up setting size */ + power_info->power_setting = pu; + power_info->power_setting_size = size; + + return rc; +} + +static int32_t msm_sensor_get_power_settings(void *setting, + struct msm_camera_sensor_slave_info *slave_info, + struct msm_camera_power_ctrl_t *power_info) +{ + int32_t rc = 0; + + rc = msm_sensor_get_power_up_settings(setting, slave_info, power_info); + if (rc < 0) { + pr_err("failed"); + return -EINVAL; + } + + rc = msm_sensor_get_power_down_settings(setting, slave_info, + power_info); + if (rc < 0) { + pr_err("failed"); + return -EINVAL; + } + return rc; +} + +static void msm_sensor_fill_sensor_info(struct msm_sensor_ctrl_t *s_ctrl, + struct msm_sensor_info_t *sensor_info, char *entity_name) +{ + uint32_t i; + + if (!s_ctrl || !sensor_info) { + pr_err("%s:failed\n", __func__); + return; + } + + strlcpy(sensor_info->sensor_name, s_ctrl->sensordata->sensor_name, + MAX_SENSOR_NAME); + + sensor_info->session_id = s_ctrl->sensordata->sensor_info->session_id; + + s_ctrl->sensordata->sensor_info->subdev_id[SUB_MODULE_SENSOR] = + s_ctrl->sensordata->sensor_info->session_id; + for (i = 0; i < SUB_MODULE_MAX; i++) { + sensor_info->subdev_id[i] = + s_ctrl->sensordata->sensor_info->subdev_id[i]; + sensor_info->subdev_intf[i] = + s_ctrl->sensordata->sensor_info->subdev_intf[i]; + } + + sensor_info->is_mount_angle_valid = + s_ctrl->sensordata->sensor_info->is_mount_angle_valid; + sensor_info->sensor_mount_angle = + s_ctrl->sensordata->sensor_info->sensor_mount_angle; + sensor_info->modes_supported = + s_ctrl->sensordata->sensor_info->modes_supported; + sensor_info->position = + s_ctrl->sensordata->sensor_info->position; + + strlcpy(entity_name, s_ctrl->msm_sd.sd.entity.name, MAX_SENSOR_NAME); +} + +/* static function definition */ +int32_t msm_sensor_driver_probe(void *setting, + struct msm_sensor_info_t *probed_info, char *entity_name) +{ + int32_t rc = 0; + struct msm_sensor_ctrl_t *s_ctrl = NULL; + struct msm_camera_cci_client *cci_client = NULL; + struct msm_camera_sensor_slave_info *slave_info = NULL; + struct msm_camera_slave_info *camera_info = NULL; + + unsigned long mount_pos = 0; + uint32_t is_yuv; + + /* Validate input parameters */ + if (!setting) { + pr_err("failed: slave_info %p", setting); + return -EINVAL; + } + + /* Allocate memory for slave info */ + slave_info = kzalloc(sizeof(*slave_info), GFP_KERNEL); + if (!slave_info) { + pr_err("failed: no memory slave_info %p", slave_info); + return -ENOMEM; + } +#ifdef CONFIG_COMPAT + if (is_compat_task()) { + struct msm_camera_sensor_slave_info32 *slave_info32 = + kzalloc(sizeof(*slave_info32), GFP_KERNEL); + if (!slave_info32) { + pr_err("failed: no memory for slave_info32 %p\n", + slave_info32); + rc = -ENOMEM; + goto free_slave_info; + } + if (copy_from_user((void *)slave_info32, setting, + sizeof(*slave_info32))) { + pr_err("failed: copy_from_user"); + rc = -EFAULT; + kfree(slave_info32); + goto free_slave_info; + } + + strlcpy(slave_info->actuator_name, slave_info32->actuator_name, + sizeof(slave_info->actuator_name)); + + strlcpy(slave_info->eeprom_name, slave_info32->eeprom_name, + sizeof(slave_info->eeprom_name)); + + strlcpy(slave_info->sensor_name, slave_info32->sensor_name, + sizeof(slave_info->sensor_name)); + + strlcpy(slave_info->ois_name, slave_info32->ois_name, + sizeof(slave_info->ois_name)); + + strlcpy(slave_info->flash_name, slave_info32->flash_name, + sizeof(slave_info->flash_name)); + + slave_info->addr_type = slave_info32->addr_type; + slave_info->camera_id = slave_info32->camera_id; + + slave_info->i2c_freq_mode = slave_info32->i2c_freq_mode; + slave_info->sensor_id_info = slave_info32->sensor_id_info; + + slave_info->slave_addr = slave_info32->slave_addr; + slave_info->slave_addr2 = slave_info32->slave_addr2; + slave_info->power_setting_array.size = + slave_info32->power_setting_array.size; + slave_info->power_setting_array.size_down = + slave_info32->power_setting_array.size_down; + slave_info->power_setting_array.size_down = + slave_info32->power_setting_array.size_down; + slave_info->power_setting_array.power_setting = + compat_ptr(slave_info32-> + power_setting_array.power_setting); + slave_info->power_setting_array.power_down_setting = + compat_ptr(slave_info32-> + power_setting_array.power_down_setting); + slave_info->is_init_params_valid = + slave_info32->is_init_params_valid; + slave_info->sensor_init_params = + slave_info32->sensor_init_params; + slave_info->output_format = + slave_info32->output_format; + kfree(slave_info32); + } else +#endif + { + if (copy_from_user(slave_info, + (void *)setting, sizeof(*slave_info))) { + pr_err("failed: copy_from_user"); + rc = -EFAULT; + goto free_slave_info; + } + } + + /* Print slave info */ + CDBG("camera id %d Slave addr 0x%X addr_type %d\n", + slave_info->camera_id, slave_info->slave_addr, + slave_info->addr_type); + CDBG("sensor_id_reg_addr 0x%X sensor_id 0x%X sensor id mask %d", + slave_info->sensor_id_info.sensor_id_reg_addr, + slave_info->sensor_id_info.sensor_id, + slave_info->sensor_id_info.sensor_id_mask); + CDBG("power up size %d power down size %d\n", + slave_info->power_setting_array.size, + slave_info->power_setting_array.size_down); + + if (slave_info->is_init_params_valid) { + CDBG("position %d", + slave_info->sensor_init_params.position); + CDBG("mount %d", + slave_info->sensor_init_params.sensor_mount_angle); + } + + /* Validate camera id */ + if (slave_info->camera_id >= MAX_CAMERAS) { + pr_err("failed: invalid camera id %d max %d", + slave_info->camera_id, MAX_CAMERAS); + rc = -EINVAL; + goto free_slave_info; + } + + /* Extract s_ctrl from camera id */ + s_ctrl = g_sctrl[slave_info->camera_id]; + if (!s_ctrl) { + pr_err("failed: s_ctrl %p for camera_id %d", s_ctrl, + slave_info->camera_id); + rc = -EINVAL; + goto free_slave_info; + } + + CDBG("s_ctrl[%d] %p", slave_info->camera_id, s_ctrl); + + if (s_ctrl->is_probe_succeed == 1) { + /* + * Different sensor on this camera slot has been connected + * and probe already succeeded for that sensor. Ignore this + * probe + */ + if (slave_info->sensor_id_info.sensor_id == + s_ctrl->sensordata->cam_slave_info-> + sensor_id_info.sensor_id) { + pr_err("slot%d: sensor id%d already probed\n", + slave_info->camera_id, + s_ctrl->sensordata->cam_slave_info-> + sensor_id_info.sensor_id); + msm_sensor_fill_sensor_info(s_ctrl, + probed_info, entity_name); + } else + pr_err("slot %d has some other sensor\n", + slave_info->camera_id); + + rc = 0; + goto free_slave_info; + } + + if (slave_info->power_setting_array.size == 0 && + slave_info->slave_addr == 0) { + s_ctrl->is_csid_tg_mode = 1; + goto CSID_TG; + } + + rc = msm_sensor_get_power_settings(setting, slave_info, + &s_ctrl->sensordata->power_info); + if (rc < 0) { + pr_err("failed"); + goto free_slave_info; + } + + + camera_info = kzalloc(sizeof(struct msm_camera_slave_info), GFP_KERNEL); + if (!camera_info) { + pr_err("failed: no memory slave_info %p", camera_info); + goto free_slave_info; + + } + + s_ctrl->sensordata->slave_info = camera_info; + + /* Fill sensor slave info */ + camera_info->sensor_slave_addr = slave_info->slave_addr; + camera_info->sensor_id_reg_addr = + slave_info->sensor_id_info.sensor_id_reg_addr; + camera_info->sensor_id = slave_info->sensor_id_info.sensor_id; + camera_info->sensor_id2 = slave_info->sensor_id_info.sensor_id2; + camera_info->sensor_id_mask = slave_info->sensor_id_info.sensor_id_mask; + + /* Fill CCI master, slave address and CCI default params */ + if (!s_ctrl->sensor_i2c_client) { + pr_err("failed: sensor_i2c_client %p", + s_ctrl->sensor_i2c_client); + rc = -EINVAL; + goto free_camera_info; + } + /* Fill sensor address type */ + s_ctrl->sensor_i2c_client->addr_type = slave_info->addr_type; + if (s_ctrl->sensor_i2c_client->client) + s_ctrl->sensor_i2c_client->client->addr = + camera_info->sensor_slave_addr; + + cci_client = s_ctrl->sensor_i2c_client->cci_client; + if (!cci_client) { + pr_err("failed: cci_client %p", cci_client); + goto free_camera_info; + } + cci_client->cci_i2c_master = s_ctrl->cci_i2c_master; + cci_client->sid = slave_info->slave_addr >> 1; + cci_client->retries = 3; + cci_client->id_map = 0; + cci_client->i2c_freq_mode = slave_info->i2c_freq_mode; + + /* Parse and fill vreg params for powerup settings */ + rc = msm_camera_fill_vreg_params( + s_ctrl->sensordata->power_info.cam_vreg, + s_ctrl->sensordata->power_info.num_vreg, + s_ctrl->sensordata->power_info.power_setting, + s_ctrl->sensordata->power_info.power_setting_size); + if (rc < 0) { + pr_err("failed: msm_camera_get_dt_power_setting_data rc %d", + rc); + goto free_camera_info; + } + + /* Parse and fill vreg params for powerdown settings*/ + rc = msm_camera_fill_vreg_params( + s_ctrl->sensordata->power_info.cam_vreg, + s_ctrl->sensordata->power_info.num_vreg, + s_ctrl->sensordata->power_info.power_down_setting, + s_ctrl->sensordata->power_info.power_down_setting_size); + if (rc < 0) { + pr_err("failed: msm_camera_fill_vreg_params for PDOWN rc %d", + rc); + goto free_camera_info; + } + +CSID_TG: + /* Update sensor, actuator and eeprom name in + * sensor control structure */ + s_ctrl->sensordata->sensor_name = slave_info->sensor_name; + s_ctrl->sensordata->eeprom_name = slave_info->eeprom_name; + s_ctrl->sensordata->actuator_name = slave_info->actuator_name; + s_ctrl->sensordata->ois_name = slave_info->ois_name; + /* Save Sensor Info */ + s_ctrl->sensordata->cam_slave_info = slave_info; + /* + * Update eeporm subdevice Id by input eeprom name + */ + rc = msm_sensor_fill_eeprom_subdevid_by_name(s_ctrl); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto free_camera_info; + } + /* + * Update actuator subdevice Id by input actuator name + */ + rc = msm_sensor_fill_actuator_subdevid_by_name(s_ctrl); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto free_camera_info; + } + + rc = msm_sensor_fill_ois_subdevid_by_name(s_ctrl); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto free_camera_info; + } + + /* Power up and probe sensor */ + rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl); + if (rc < 0) { + pr_err("%s power up failed", slave_info->sensor_name); + goto free_camera_info; + } + + pr_err("%s probe succeeded", slave_info->sensor_name); + +#ifdef CONFIG_CAMERA_WT_FACTORY_SUPPORTED + if (slave_info->camera_id == 0) { + hardwareinfo_set_prop(HARDWARE_BACK_CAM,s_ctrl->sensordata->sensor_name); + } else { + hardwareinfo_set_prop(HARDWARE_FRONT_CAM,s_ctrl->sensordata->sensor_name); + } +#endif + + /* + Set probe succeeded flag to 1 so that no other camera shall + * probed on this slot + */ + s_ctrl->is_probe_succeed = 1; + + /* + * Update the subdevice id of flash-src based on availability in kernel. + */ + if (strlen(slave_info->flash_name) == 0) { + s_ctrl->sensordata->sensor_info-> + subdev_id[SUB_MODULE_LED_FLASH] = -1; + } + + /* + * Create /dev/videoX node, comment for now until dummy /dev/videoX + * node is created and used by HAL + */ + + if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE) + rc = msm_sensor_driver_create_v4l_subdev(s_ctrl); + else + rc = msm_sensor_driver_create_i2c_v4l_subdev(s_ctrl); + if (rc < 0) { + pr_err("failed: camera creat v4l2 rc %d", rc); + goto camera_power_down; + } + + /* Power down */ + s_ctrl->func_tbl->sensor_power_down(s_ctrl); + + rc = msm_sensor_fill_slave_info_init_params( + slave_info, + s_ctrl->sensordata->sensor_info); + if (rc < 0) { + pr_err("%s Fill slave info failed", slave_info->sensor_name); + goto free_camera_info; + } + rc = msm_sensor_validate_slave_info(s_ctrl->sensordata->sensor_info); + if (rc < 0) { + pr_err("%s Validate slave info failed", + slave_info->sensor_name); + goto free_camera_info; + } + /* Update sensor mount angle and position in media entity flag */ + is_yuv = (slave_info->output_format == MSM_SENSOR_YCBCR) ? 1 : 0; + mount_pos = is_yuv << 25 | + (s_ctrl->sensordata->sensor_info->position << 16) | + ((s_ctrl->sensordata-> + sensor_info->sensor_mount_angle / 90) << 8); + + s_ctrl->msm_sd.sd.entity.flags = mount_pos | MEDIA_ENT_FL_DEFAULT; + + msm_sensor_fill_sensor_info(s_ctrl, probed_info, entity_name); + + return rc; + +camera_power_down: + s_ctrl->func_tbl->sensor_power_down(s_ctrl); +free_camera_info: + kfree(camera_info); +free_slave_info: + kfree(slave_info); + return rc; +} + +static int32_t msm_sensor_driver_get_dt_data(struct msm_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + struct msm_camera_sensor_board_info *sensordata = NULL; + struct device_node *of_node = s_ctrl->of_node; + uint32_t cell_id; + + s_ctrl->sensordata = kzalloc(sizeof(*sensordata), GFP_KERNEL); + if (!s_ctrl->sensordata) { + pr_err("failed: no memory"); + return -ENOMEM; + } + + sensordata = s_ctrl->sensordata; + + /* + * Read cell index - this cell index will be the camera slot where + * this camera will be mounted + */ + rc = of_property_read_u32(of_node, "cell-index", &cell_id); + if (rc < 0) { + pr_err("failed: cell-index rc %d", rc); + goto FREE_SENSOR_DATA; + } + s_ctrl->id = cell_id; + + /* Validate cell_id */ + if (cell_id >= MAX_CAMERAS) { + pr_err("failed: invalid cell_id %d", cell_id); + rc = -EINVAL; + goto FREE_SENSOR_DATA; + } + + /* Check whether g_sctrl is already filled for this cell_id */ + if (g_sctrl[cell_id]) { + pr_err("failed: sctrl already filled for cell_id %d", cell_id); + rc = -EINVAL; + goto FREE_SENSOR_DATA; + } + + /* Read subdev info */ + rc = msm_sensor_get_sub_module_index(of_node, &sensordata->sensor_info); + if (rc < 0) { + pr_err("failed"); + goto FREE_SENSOR_DATA; + } + + /*Get clocks information*/ + rc = msm_camera_get_clk_info(s_ctrl->pdev, + &s_ctrl->sensordata->power_info.clk_info, + &s_ctrl->sensordata->power_info.clk_ptr, + &s_ctrl->sensordata->power_info.clk_info_size); + if (rc < 0) { + pr_err("failed: msm_camera_get_clk_info rc %d", rc); + goto FREE_SUB_MODULE_DATA; + } + + /* Read vreg information */ + rc = msm_camera_get_dt_vreg_data(of_node, + &sensordata->power_info.cam_vreg, + &sensordata->power_info.num_vreg); + if (rc < 0) { + pr_err("failed: msm_camera_get_dt_vreg_data rc %d", rc); + goto FREE_CLK_DATA; + } + + /* Read gpio information */ + rc = msm_sensor_driver_get_gpio_data + (&(sensordata->power_info.gpio_conf), of_node); + if (rc < 0) { + pr_err("failed: msm_sensor_driver_get_gpio_data rc %d", rc); + goto FREE_VREG_DATA; + } + + /* Get CCI master */ + rc = of_property_read_u32(of_node, "qcom,cci-master", + &s_ctrl->cci_i2c_master); + CDBG("qcom,cci-master %d, rc %d", s_ctrl->cci_i2c_master, rc); + if (rc < 0) { + /* Set default master 0 */ + s_ctrl->cci_i2c_master = MASTER_0; + rc = 0; + } + + /* Get mount angle */ + if (0 > of_property_read_u32(of_node, "qcom,mount-angle", + &sensordata->sensor_info->sensor_mount_angle)) { + /* Invalidate mount angle flag */ + sensordata->sensor_info->is_mount_angle_valid = 0; + sensordata->sensor_info->sensor_mount_angle = 0; + } else { + sensordata->sensor_info->is_mount_angle_valid = 1; + } + CDBG("%s qcom,mount-angle %d\n", __func__, + sensordata->sensor_info->sensor_mount_angle); + if (0 > of_property_read_u32(of_node, "qcom,sensor-position", + &sensordata->sensor_info->position)) { + CDBG("%s:%d Invalid sensor position\n", __func__, __LINE__); + sensordata->sensor_info->position = INVALID_CAMERA_B; + } + if (0 > of_property_read_u32(of_node, "qcom,sensor-mode", + &sensordata->sensor_info->modes_supported)) { + CDBG("%s:%d Invalid sensor mode supported\n", + __func__, __LINE__); + sensordata->sensor_info->modes_supported = CAMERA_MODE_INVALID; + } + /* Get vdd-cx regulator */ + /*Optional property, don't return error if absent */ + of_property_read_string(of_node, "qcom,vdd-cx-name", + &sensordata->misc_regulator); + CDBG("qcom,misc_regulator %s", sensordata->misc_regulator); + + s_ctrl->set_mclk_23880000 = of_property_read_bool(of_node, + "qcom,mclk-23880000"); + + CDBG("%s qcom,mclk-23880000 = %d\n", __func__, + s_ctrl->set_mclk_23880000); + + return rc; + +FREE_VREG_DATA: + kfree(sensordata->power_info.cam_vreg); +FREE_CLK_DATA: + msm_camera_put_clk_info(s_ctrl->pdev, + &s_ctrl->sensordata->power_info.clk_info, + &s_ctrl->sensordata->power_info.clk_ptr, + s_ctrl->sensordata->power_info.clk_info_size); + +FREE_SUB_MODULE_DATA: + kfree(sensordata->sensor_info); +FREE_SENSOR_DATA: + kfree(sensordata); + return rc; +} + +static int32_t msm_sensor_driver_parse(struct msm_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + + CDBG("Enter"); + /* Validate input parameters */ + + + /* Allocate memory for sensor_i2c_client */ + s_ctrl->sensor_i2c_client = kzalloc(sizeof(*s_ctrl->sensor_i2c_client), + GFP_KERNEL); + if (!s_ctrl->sensor_i2c_client) { + pr_err("failed: no memory sensor_i2c_client %p", + s_ctrl->sensor_i2c_client); + return -ENOMEM; + } + + /* Allocate memory for mutex */ + s_ctrl->msm_sensor_mutex = kzalloc(sizeof(*s_ctrl->msm_sensor_mutex), + GFP_KERNEL); + if (!s_ctrl->msm_sensor_mutex) { + pr_err("failed: no memory msm_sensor_mutex %p", + s_ctrl->msm_sensor_mutex); + goto FREE_SENSOR_I2C_CLIENT; + } + + /* Parse dt information and store in sensor control structure */ + rc = msm_sensor_driver_get_dt_data(s_ctrl); + if (rc < 0) { + pr_err("failed: rc %d", rc); + goto FREE_MUTEX; + } + + /* Initialize mutex */ + mutex_init(s_ctrl->msm_sensor_mutex); + + /* Initilize v4l2 subdev info */ + s_ctrl->sensor_v4l2_subdev_info = msm_sensor_driver_subdev_info; + s_ctrl->sensor_v4l2_subdev_info_size = + ARRAY_SIZE(msm_sensor_driver_subdev_info); + + /* Initialize default parameters */ + rc = msm_sensor_init_default_params(s_ctrl); + if (rc < 0) { + pr_err("failed: msm_sensor_init_default_params rc %d", rc); + goto FREE_DT_DATA; + } + + /* Store sensor control structure in static database */ + g_sctrl[s_ctrl->id] = s_ctrl; + CDBG("g_sctrl[%d] %p", s_ctrl->id, g_sctrl[s_ctrl->id]); + + return rc; + +FREE_DT_DATA: + kfree(s_ctrl->sensordata->power_info.gpio_conf->gpio_num_info); + kfree(s_ctrl->sensordata->power_info.gpio_conf->cam_gpio_req_tbl); + kfree(s_ctrl->sensordata->power_info.gpio_conf); + kfree(s_ctrl->sensordata->power_info.cam_vreg); + kfree(s_ctrl->sensordata); +FREE_MUTEX: + kfree(s_ctrl->msm_sensor_mutex); +FREE_SENSOR_I2C_CLIENT: + kfree(s_ctrl->sensor_i2c_client); + return rc; +} + +static int32_t msm_sensor_driver_platform_probe(struct platform_device *pdev) +{ + int32_t rc = 0; + struct msm_sensor_ctrl_t *s_ctrl = NULL; + + /* Create sensor control structure */ + s_ctrl = kzalloc(sizeof(*s_ctrl), GFP_KERNEL); + if (!s_ctrl) { + pr_err("failed: no memory s_ctrl %p", s_ctrl); + return -ENOMEM; + } + + platform_set_drvdata(pdev, s_ctrl); + + /* Initialize sensor device type */ + s_ctrl->sensor_device_type = MSM_CAMERA_PLATFORM_DEVICE; + s_ctrl->of_node = pdev->dev.of_node; + + /*fill in platform device*/ + s_ctrl->pdev = pdev; + + rc = msm_sensor_driver_parse(s_ctrl); + if (rc < 0) { + pr_err("failed: msm_sensor_driver_parse rc %d", rc); + goto FREE_S_CTRL; + } + + /* Fill platform device id*/ + pdev->id = s_ctrl->id; + + /* Fill device in power info */ + s_ctrl->sensordata->power_info.dev = &pdev->dev; + return rc; +FREE_S_CTRL: + kfree(s_ctrl); + return rc; +} + +static int32_t msm_sensor_driver_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int32_t rc = 0; + struct msm_sensor_ctrl_t *s_ctrl; + + CDBG("\n\nEnter: msm_sensor_driver_i2c_probe"); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s %s i2c_check_functionality failed\n", + __func__, client->name); + rc = -EFAULT; + return rc; + } + + /* Create sensor control structure */ + s_ctrl = kzalloc(sizeof(*s_ctrl), GFP_KERNEL); + if (!s_ctrl) { + pr_err("failed: no memory s_ctrl %p", s_ctrl); + return -ENOMEM; + } + + i2c_set_clientdata(client, s_ctrl); + + /* Initialize sensor device type */ + s_ctrl->sensor_device_type = MSM_CAMERA_I2C_DEVICE; + s_ctrl->of_node = client->dev.of_node; + + rc = msm_sensor_driver_parse(s_ctrl); + if (rc < 0) { + pr_err("failed: msm_sensor_driver_parse rc %d", rc); + goto FREE_S_CTRL; + } + + if (s_ctrl->sensor_i2c_client != NULL) { + s_ctrl->sensor_i2c_client->client = client; + s_ctrl->sensordata->power_info.dev = &client->dev; + + } + + return rc; +FREE_S_CTRL: + kfree(s_ctrl); + return rc; +} + +static int msm_sensor_driver_i2c_remove(struct i2c_client *client) +{ + struct msm_sensor_ctrl_t *s_ctrl = i2c_get_clientdata(client); + + pr_err("%s: sensor FREE\n", __func__); + + if (!s_ctrl) { + pr_err("%s: sensor device is NULL\n", __func__); + return 0; + } + + g_sctrl[s_ctrl->id] = NULL; + msm_sensor_free_sensor_data(s_ctrl); + kfree(s_ctrl->msm_sensor_mutex); + kfree(s_ctrl->sensor_i2c_client); + kfree(s_ctrl); + + return 0; +} + +static const struct i2c_device_id i2c_id[] = { + {SENSOR_DRIVER_I2C, (kernel_ulong_t)NULL}, + { } +}; + +static struct i2c_driver msm_sensor_driver_i2c = { + .id_table = i2c_id, + .probe = msm_sensor_driver_i2c_probe, + .remove = msm_sensor_driver_i2c_remove, + .driver = { + .name = SENSOR_DRIVER_I2C, + }, +}; + +static int __init msm_sensor_driver_init(void) +{ + int32_t rc = 0; + + CDBG("%s Enter\n", __func__); + rc = platform_driver_register(&msm_sensor_platform_driver); + if (rc) + pr_err("%s platform_driver_register failed rc = %d", + __func__, rc); + rc = i2c_add_driver(&msm_sensor_driver_i2c); + if (rc) + pr_err("%s i2c_add_driver failed rc = %d", __func__, rc); + + return rc; +} + +static void __exit msm_sensor_driver_exit(void) +{ + CDBG("Enter"); + platform_driver_unregister(&msm_sensor_platform_driver); + i2c_del_driver(&msm_sensor_driver_i2c); + return; +} + +module_init(msm_sensor_driver_init); +module_exit(msm_sensor_driver_exit); +MODULE_DESCRIPTION("msm_sensor_driver"); +MODULE_LICENSE("GPL v2"); diff --git a/camera/sensor/msm_sensor_driver.h b/camera/sensor/msm_sensor_driver.h new file mode 100644 index 00000000..1ab58245 --- /dev/null +++ b/camera/sensor/msm_sensor_driver.h @@ -0,0 +1,21 @@ +/* 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_SENSOR_DRIVER_H +#define MSM_SENSOR_DRIVER_H + +#include "msm_sensor.h" + +int32_t msm_sensor_driver_probe(void *setting, + struct msm_sensor_info_t *probed_info, char *entity_name); + +#endif diff --git a/camera/sensor/msm_sensor_init.c b/camera/sensor/msm_sensor_init.c new file mode 100644 index 00000000..8b6e3d3e --- /dev/null +++ b/camera/sensor/msm_sensor_init.c @@ -0,0 +1,225 @@ +/* Copyright (c) 2013-2015, 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. + */ + +#define pr_fmt(fmt) "MSM-SENSOR-INIT %s:%d " fmt "\n", __func__, __LINE__ + +/* Header files */ +#include "msm_sensor_init.h" +#include "msm_sensor_driver.h" +#include "msm_sensor.h" +#include "msm_sd.h" + +/* Logging macro */ +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +static struct msm_sensor_init_t *s_init; +static struct v4l2_file_operations msm_sensor_init_v4l2_subdev_fops; +/* Static function declaration */ +static long msm_sensor_init_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg); + +/* Static structure declaration */ +static struct v4l2_subdev_core_ops msm_sensor_init_subdev_core_ops = { + .ioctl = msm_sensor_init_subdev_ioctl, +}; + +static struct v4l2_subdev_ops msm_sensor_init_subdev_ops = { + .core = &msm_sensor_init_subdev_core_ops, +}; + +static const struct v4l2_subdev_internal_ops msm_sensor_init_internal_ops; + +static int msm_sensor_wait_for_probe_done(struct msm_sensor_init_t *s_init) +{ + int rc; + int tm = 10000; + if (s_init->module_init_status == 1) { + CDBG("msm_cam_get_module_init_status -2\n"); + return 0; + } + rc = wait_event_timeout(s_init->state_wait, + (s_init->module_init_status == 1), msecs_to_jiffies(tm)); + if (rc == 0) + pr_err("%s:%d wait timeout\n", __func__, __LINE__); + + return rc; +} + +/* Static function definition */ +static int32_t msm_sensor_driver_cmd(struct msm_sensor_init_t *s_init, + void *arg) +{ + int32_t rc = 0; + struct sensor_init_cfg_data *cfg = (struct sensor_init_cfg_data *)arg; + + /* Validate input parameters */ + if (!s_init || !cfg) { + pr_err("failed: s_init %p cfg %p", s_init, cfg); + return -EINVAL; + } + + switch (cfg->cfgtype) { + case CFG_SINIT_PROBE: + mutex_lock(&s_init->imutex); + s_init->module_init_status = 0; + rc = msm_sensor_driver_probe(cfg->cfg.setting, + &cfg->probed_info, + cfg->entity_name); + mutex_unlock(&s_init->imutex); + if (rc < 0) + pr_err("%s failed (non-fatal) rc %d", __func__, rc); + break; + + case CFG_SINIT_PROBE_DONE: + s_init->module_init_status = 1; + wake_up(&s_init->state_wait); + break; + + case CFG_SINIT_PROBE_WAIT_DONE: + msm_sensor_wait_for_probe_done(s_init); + break; + + default: + pr_err("default"); + break; + } + + return rc; +} + +static long msm_sensor_init_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + long rc = 0; + struct msm_sensor_init_t *s_init = v4l2_get_subdevdata(sd); + CDBG("Enter"); + + /* Validate input parameters */ + if (!s_init) { + pr_err("failed: s_init %p", s_init); + return -EINVAL; + } + + switch (cmd) { + case VIDIOC_MSM_SENSOR_INIT_CFG: + rc = msm_sensor_driver_cmd(s_init, arg); + break; + + default: + pr_err_ratelimited("default\n"); + break; + } + + return rc; +} + +#ifdef CONFIG_COMPAT +static long msm_sensor_init_subdev_do_ioctl( + struct file *file, unsigned int cmd, void *arg) +{ + int32_t rc = 0; + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + struct sensor_init_cfg_data32 *u32 = + (struct sensor_init_cfg_data32 *)arg; + struct sensor_init_cfg_data sensor_init_data; + + switch (cmd) { + case VIDIOC_MSM_SENSOR_INIT_CFG32: + memset(&sensor_init_data, 0, sizeof(sensor_init_data)); + sensor_init_data.cfgtype = u32->cfgtype; + sensor_init_data.cfg.setting = compat_ptr(u32->cfg.setting); + cmd = VIDIOC_MSM_SENSOR_INIT_CFG; + rc = msm_sensor_init_subdev_ioctl(sd, cmd, &sensor_init_data); + if (rc < 0) { + pr_err("%s:%d VIDIOC_MSM_SENSOR_INIT_CFG failed (non-fatal)", + __func__, __LINE__); + return rc; + } + u32->probed_info = sensor_init_data.probed_info; + strlcpy(u32->entity_name, sensor_init_data.entity_name, + sizeof(sensor_init_data.entity_name)); + return 0; + default: + return msm_sensor_init_subdev_ioctl(sd, cmd, arg); + } +} + +static long msm_sensor_init_subdev_fops_ioctl( + struct file *file, unsigned int cmd, unsigned long arg) +{ + return video_usercopy(file, cmd, arg, msm_sensor_init_subdev_do_ioctl); +} +#endif + +static int __init msm_sensor_init_module(void) +{ + int ret = 0; + /* Allocate memory for msm_sensor_init control structure */ + s_init = kzalloc(sizeof(struct msm_sensor_init_t), GFP_KERNEL); + if (!s_init) { + pr_err("failed: no memory s_init %p", NULL); + return -ENOMEM; + } + + CDBG("MSM_SENSOR_INIT_MODULE %p", NULL); + + /* Initialize mutex */ + mutex_init(&s_init->imutex); + + /* Create /dev/v4l-subdevX for msm_sensor_init */ + v4l2_subdev_init(&s_init->msm_sd.sd, &msm_sensor_init_subdev_ops); + snprintf(s_init->msm_sd.sd.name, sizeof(s_init->msm_sd.sd.name), "%s", + "msm_sensor_init"); + v4l2_set_subdevdata(&s_init->msm_sd.sd, s_init); + s_init->msm_sd.sd.internal_ops = &msm_sensor_init_internal_ops; + s_init->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + media_entity_init(&s_init->msm_sd.sd.entity, 0, NULL, 0); + s_init->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + s_init->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_SENSOR_INIT; + s_init->msm_sd.sd.entity.name = s_init->msm_sd.sd.name; + s_init->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x6; + ret = msm_sd_register(&s_init->msm_sd); + if (ret) { + CDBG("%s: msm_sd_register error = %d\n", __func__, ret); + goto error; + } + msm_cam_copy_v4l2_subdev_fops(&msm_sensor_init_v4l2_subdev_fops); +#ifdef CONFIG_COMPAT + msm_sensor_init_v4l2_subdev_fops.compat_ioctl32 = + msm_sensor_init_subdev_fops_ioctl; +#endif + s_init->msm_sd.sd.devnode->fops = + &msm_sensor_init_v4l2_subdev_fops; + + init_waitqueue_head(&s_init->state_wait); + + return 0; +error: + mutex_destroy(&s_init->imutex); + kfree(s_init); + return ret; +} + +static void __exit msm_sensor_exit_module(void) +{ + msm_sd_unregister(&s_init->msm_sd); + mutex_destroy(&s_init->imutex); + kfree(s_init); + return; +} + +module_init(msm_sensor_init_module); +module_exit(msm_sensor_exit_module); +MODULE_DESCRIPTION("msm_sensor_init"); +MODULE_LICENSE("GPL v2"); diff --git a/camera/sensor/msm_sensor_init.h b/camera/sensor/msm_sensor_init.h new file mode 100644 index 00000000..256b0a1f --- /dev/null +++ b/camera/sensor/msm_sensor_init.h @@ -0,0 +1,25 @@ +/* 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_SENSOR_INIT_H +#define MSM_SENSOR_INIT_H + +#include "msm_sensor.h" + +struct msm_sensor_init_t { + struct mutex imutex; + struct msm_sd_subdev msm_sd; + int module_init_status; + wait_queue_head_t state_wait; +}; + +#endif diff --git a/camera/sensor/ois/Makefile b/camera/sensor/ois/Makefile new file mode 100644 index 00000000..b6e59d60 --- /dev/null +++ b/camera/sensor/ois/Makefile @@ -0,0 +1,5 @@ +ccflags-y += -Icamera +ccflags-y += -Icamera/common +ccflags-y += -Icamera/sensor/io +ccflags-y += -Icamera/sensor/cci +obj-$(CONFIG_MSMB_CAMERA) += msm_ois.o diff --git a/camera/sensor/ois/msm_ois.c b/camera/sensor/ois/msm_ois.c new file mode 100644 index 00000000..2f6b9d3e --- /dev/null +++ b/camera/sensor/ois/msm_ois.c @@ -0,0 +1,900 @@ +/* Copyright (c) 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. + */ + +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include <linux/module.h> +#include "msm_sd.h" +#include "msm_ois.h" +#include "msm_cci.h" + +DEFINE_MSM_MUTEX(msm_ois_mutex); +/*#define MSM_OIS_DEBUG*/ +#undef CDBG +#ifdef MSM_OIS_DEBUG +#define CDBG(fmt, args...) pr_err(fmt, ##args) +#else +#define CDBG(fmt, args...) pr_debug(fmt, ##args) +#endif + +static struct v4l2_file_operations msm_ois_v4l2_subdev_fops; +static int32_t msm_ois_power_up(struct msm_ois_ctrl_t *o_ctrl); +static int32_t msm_ois_power_down(struct msm_ois_ctrl_t *o_ctrl); + +static struct i2c_driver msm_ois_i2c_driver; + +static int32_t msm_ois_write_settings(struct msm_ois_ctrl_t *o_ctrl, + uint16_t size, struct reg_settings_ois_t *settings) +{ + int32_t rc = -EFAULT; + int32_t i = 0; + struct msm_camera_i2c_seq_reg_array *reg_setting; + CDBG("Enter\n"); + + for (i = 0; i < size; i++) { + switch (settings[i].i2c_operation) { + case MSM_OIS_WRITE: { + switch (settings[i].data_type) { + case MSM_CAMERA_I2C_BYTE_DATA: + case MSM_CAMERA_I2C_WORD_DATA: + rc = o_ctrl->i2c_client.i2c_func_tbl->i2c_write( + &o_ctrl->i2c_client, + settings[i].reg_addr, + settings[i].reg_data, + settings[i].data_type); + break; + case MSM_CAMERA_I2C_DWORD_DATA: + reg_setting = + kzalloc(sizeof(struct msm_camera_i2c_seq_reg_array), + GFP_KERNEL); + if (!reg_setting) + return -ENOMEM; + + reg_setting->reg_addr = settings[i].reg_addr; + reg_setting->reg_data[0] = (uint8_t) + ((settings[i].reg_data & + 0xFF000000) >> 24); + reg_setting->reg_data[1] = (uint8_t) + ((settings[i].reg_data & + 0x00FF0000) >> 16); + reg_setting->reg_data[2] = (uint8_t) + ((settings[i].reg_data & + 0x0000FF00) >> 8); + reg_setting->reg_data[3] = (uint8_t) + (settings[i].reg_data & 0x000000FF); + reg_setting->reg_data_size = 4; + rc = o_ctrl->i2c_client.i2c_func_tbl-> + i2c_write_seq(&o_ctrl->i2c_client, + reg_setting->reg_addr, + reg_setting->reg_data, + reg_setting->reg_data_size); + kfree(reg_setting); + reg_setting = NULL; + if (rc < 0) + return rc; + break; + + default: + pr_err("Unsupport data type: %d\n", + settings[i].data_type); + break; + } + if (settings[i].delay > 20) + msleep(settings[i].delay); + else if (0 != settings[i].delay) + usleep_range(settings[i].delay * 1000, + (settings[i].delay * 1000) + 1000); + } + break; + + case MSM_OIS_POLL: { + switch (settings[i].data_type) { + case MSM_CAMERA_I2C_BYTE_DATA: + case MSM_CAMERA_I2C_WORD_DATA: + + rc = o_ctrl->i2c_client.i2c_func_tbl + ->i2c_poll(&o_ctrl->i2c_client, + settings[i].reg_addr, + settings[i].reg_data, + settings[i].data_type, + settings[i].delay); + break; + + default: + pr_err("Unsupport data type: %d\n", + settings[i].data_type); + break; + } + } + break; + + case MSM_OIS_READ: { + + switch (settings[i].data_type) { + + case MSM_CAMERA_I2C_BYTE_DATA: + case MSM_CAMERA_I2C_WORD_DATA: { + uint16_t data; + rc = o_ctrl->i2c_client.i2c_func_tbl->i2c_read( + &o_ctrl->i2c_client, + settings[i].reg_addr, + &data, + settings[i].data_type); + if (rc < 0) + return rc; + settings[i].reg_data = data; + break; + } + + case MSM_CAMERA_I2C_DWORD_DATA: { + uint8_t data[4]; + + rc = o_ctrl->i2c_client.i2c_func_tbl-> + i2c_read_seq( + &o_ctrl->i2c_client, + settings[i].reg_addr, + data, + 4); + if (rc < 0) + return rc; + settings[i].reg_data = (data[0] << 24) + | (data[1] << 16) + | (data[2] << 8) + | data[3]; + break; + } + + default: + pr_err("Unsupport data type: %d\n", + settings[i].data_type); + break; + } + } + break; + + } + + if (rc < 0) + break; + } + + CDBG("Exit\n"); + return rc; +} + +static int32_t msm_ois_vreg_control(struct msm_ois_ctrl_t *o_ctrl, + int config) +{ + int rc = 0, i, cnt; + struct msm_ois_vreg *vreg_cfg; + + vreg_cfg = &o_ctrl->vreg_cfg; + cnt = vreg_cfg->num_vreg; + if (!cnt) + return 0; + + if (cnt >= MSM_OIS_MAX_VREGS) { + pr_err("%s failed %d cnt %d\n", __func__, __LINE__, cnt); + return -EINVAL; + } + + for (i = 0; i < cnt; i++) { + rc = msm_camera_config_single_vreg(&(o_ctrl->pdev->dev), + &vreg_cfg->cam_vreg[i], + (struct regulator **)&vreg_cfg->data[i], + config); + } + return rc; +} + +static int32_t msm_ois_power_down(struct msm_ois_ctrl_t *o_ctrl) +{ + int32_t rc = 0; + enum msm_sensor_power_seq_gpio_t gpio; + + CDBG("Enter\n"); + if (o_ctrl->ois_state != OIS_DISABLE_STATE) { + + rc = msm_ois_vreg_control(o_ctrl, 0); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + return rc; + } + + for (gpio = SENSOR_GPIO_AF_PWDM; gpio < SENSOR_GPIO_MAX; + gpio++) { + if (o_ctrl->gconf && + o_ctrl->gconf->gpio_num_info && + o_ctrl->gconf-> + gpio_num_info->valid[gpio] == 1) { + gpio_set_value_cansleep( + o_ctrl->gconf->gpio_num_info + ->gpio_num[gpio], + GPIOF_OUT_INIT_LOW); + + if (o_ctrl->cam_pinctrl_status) { + rc = pinctrl_select_state( + o_ctrl->pinctrl_info.pinctrl, + o_ctrl->pinctrl_info. + gpio_state_suspend); + if (rc < 0) + pr_err("ERR:%s:%d cannot set pin to suspend state: %d", + __func__, __LINE__, rc); + devm_pinctrl_put( + o_ctrl->pinctrl_info.pinctrl); + } + o_ctrl->cam_pinctrl_status = 0; + rc = msm_camera_request_gpio_table( + o_ctrl->gconf->cam_gpio_req_tbl, + o_ctrl->gconf->cam_gpio_req_tbl_size, + 0); + if (rc < 0) + pr_err("ERR:%s:Failed in selecting state in ois power down: %d\n", + __func__, rc); + } + } + + o_ctrl->i2c_tbl_index = 0; + o_ctrl->ois_state = OIS_OPS_INACTIVE; + } + CDBG("Exit\n"); + return rc; +} + +static int msm_ois_init(struct msm_ois_ctrl_t *o_ctrl) +{ + int rc = 0; + CDBG("Enter\n"); + + if (!o_ctrl) { + pr_err("failed\n"); + return -EINVAL; + } + + if (o_ctrl->ois_device_type == MSM_CAMERA_PLATFORM_DEVICE) { + rc = o_ctrl->i2c_client.i2c_func_tbl->i2c_util( + &o_ctrl->i2c_client, MSM_CCI_INIT); + if (rc < 0) + pr_err("cci_init failed\n"); + } + o_ctrl->ois_state = OIS_OPS_ACTIVE; + CDBG("Exit\n"); + return rc; +} + +static int32_t msm_ois_control(struct msm_ois_ctrl_t *o_ctrl, + struct msm_ois_set_info_t *set_info) +{ + struct reg_settings_ois_t *settings = NULL; + int32_t rc = 0; + struct msm_camera_cci_client *cci_client = NULL; + CDBG("Enter\n"); + + if (o_ctrl->ois_device_type == MSM_CAMERA_PLATFORM_DEVICE) { + cci_client = o_ctrl->i2c_client.cci_client; + cci_client->sid = + set_info->ois_params.i2c_addr >> 1; + cci_client->retries = 3; + cci_client->id_map = 0; + cci_client->cci_i2c_master = o_ctrl->cci_master; + cci_client->i2c_freq_mode = set_info->ois_params.i2c_freq_mode; + } else { + o_ctrl->i2c_client.client->addr = + set_info->ois_params.i2c_addr; + } + o_ctrl->i2c_client.addr_type = MSM_CAMERA_I2C_WORD_ADDR; + + + if (set_info->ois_params.setting_size > 0 && + set_info->ois_params.setting_size + < MAX_OIS_REG_SETTINGS) { + settings = kmalloc( + sizeof(struct reg_settings_ois_t) * + (set_info->ois_params.setting_size), + GFP_KERNEL); + if (settings == NULL) { + pr_err("Error allocating memory\n"); + return -EFAULT; + } + if (copy_from_user(settings, + (void *)set_info->ois_params.settings, + set_info->ois_params.setting_size * + sizeof(struct reg_settings_ois_t))) { + kfree(settings); + pr_err("Error copying\n"); + return -EFAULT; + } + + rc = msm_ois_write_settings(o_ctrl, + set_info->ois_params.setting_size, + settings); + + if (copy_to_user((void *)set_info->ois_params.settings, + settings, + set_info->ois_params.setting_size * + sizeof(struct reg_settings_ois_t))) { + kfree(settings); + pr_err("Error copying\n"); + return -EFAULT; + } + + kfree(settings); + if (rc < 0) { + pr_err("Error\n"); + return -EFAULT; + } + } + + CDBG("Exit\n"); + + return rc; +} + + +static int32_t msm_ois_config(struct msm_ois_ctrl_t *o_ctrl, + void __user *argp) +{ + struct msm_ois_cfg_data *cdata = + (struct msm_ois_cfg_data *)argp; + int32_t rc = 0; + mutex_lock(o_ctrl->ois_mutex); + CDBG("Enter\n"); + CDBG("%s type %d\n", __func__, cdata->cfgtype); + switch (cdata->cfgtype) { + case CFG_OIS_INIT: + rc = msm_ois_init(o_ctrl); + if (rc < 0) + pr_err("msm_ois_init failed %d\n", rc); + break; + case CFG_OIS_POWERDOWN: + rc = msm_ois_power_down(o_ctrl); + if (rc < 0) + pr_err("msm_ois_power_down failed %d\n", rc); + break; + case CFG_OIS_POWERUP: + rc = msm_ois_power_up(o_ctrl); + if (rc < 0) + pr_err("Failed ois power up%d\n", rc); + break; + case CFG_OIS_CONTROL: + rc = msm_ois_control(o_ctrl, &cdata->cfg.set_info); + if (rc < 0) + pr_err("Failed ois control%d\n", rc); + break; + case CFG_OIS_I2C_WRITE_SEQ_TABLE: { + struct msm_camera_i2c_seq_reg_setting conf_array; + struct msm_camera_i2c_seq_reg_array *reg_setting = NULL; + +#ifdef CONFIG_COMPAT + if (is_compat_task()) { + memcpy(&conf_array, + (void *)cdata->cfg.settings, + sizeof(struct msm_camera_i2c_seq_reg_setting)); + } else +#endif + if (copy_from_user(&conf_array, + (void *)cdata->cfg.settings, + sizeof(struct msm_camera_i2c_seq_reg_setting))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + if (!conf_array.size) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + reg_setting = kzalloc(conf_array.size * + (sizeof(struct msm_camera_i2c_seq_reg_array)), + GFP_KERNEL); + if (!reg_setting) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -ENOMEM; + break; + } + if (copy_from_user(reg_setting, (void *)conf_array.reg_setting, + conf_array.size * + sizeof(struct msm_camera_i2c_seq_reg_array))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + kfree(reg_setting); + rc = -EFAULT; + break; + } + + conf_array.reg_setting = reg_setting; + rc = o_ctrl->i2c_client.i2c_func_tbl-> + i2c_write_seq_table(&o_ctrl->i2c_client, + &conf_array); + kfree(reg_setting); + break; + } + default: + break; + } + mutex_unlock(o_ctrl->ois_mutex); + CDBG("Exit\n"); + return rc; +} + +static int32_t msm_ois_get_subdev_id(struct msm_ois_ctrl_t *o_ctrl, + void *arg) +{ + uint32_t *subdev_id = (uint32_t *)arg; + CDBG("Enter\n"); + if (!subdev_id) { + pr_err("failed\n"); + return -EINVAL; + } + if (o_ctrl->ois_device_type == MSM_CAMERA_PLATFORM_DEVICE) + *subdev_id = o_ctrl->pdev->id; + else + *subdev_id = o_ctrl->subdev_id; + + CDBG("subdev_id %d\n", *subdev_id); + CDBG("Exit\n"); + return 0; +} + +static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = { + .i2c_read = msm_camera_cci_i2c_read, + .i2c_read_seq = msm_camera_cci_i2c_read_seq, + .i2c_write = msm_camera_cci_i2c_write, + .i2c_write_table = msm_camera_cci_i2c_write_table, + .i2c_write_seq = msm_camera_cci_i2c_write_seq, + .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_cci_i2c_write_table_w_microdelay, + .i2c_util = msm_sensor_cci_i2c_util, + .i2c_poll = msm_camera_cci_i2c_poll, +}; + +static struct msm_camera_i2c_fn_t msm_sensor_qup_func_tbl = { + .i2c_read = msm_camera_qup_i2c_read, + .i2c_read_seq = msm_camera_qup_i2c_read_seq, + .i2c_write = msm_camera_qup_i2c_write, + .i2c_write_table = msm_camera_qup_i2c_write_table, + .i2c_write_seq = msm_camera_qup_i2c_write_seq, + .i2c_write_seq_table = msm_camera_qup_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_qup_i2c_write_table_w_microdelay, + .i2c_poll = msm_camera_qup_i2c_poll, +}; + +static int msm_ois_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) { + int rc = 0; + struct msm_ois_ctrl_t *o_ctrl = v4l2_get_subdevdata(sd); + CDBG("Enter\n"); + if (!o_ctrl) { + pr_err("failed\n"); + return -EINVAL; + } + mutex_lock(o_ctrl->ois_mutex); + if (o_ctrl->ois_device_type == MSM_CAMERA_PLATFORM_DEVICE && + o_ctrl->ois_state != OIS_DISABLE_STATE) { + rc = o_ctrl->i2c_client.i2c_func_tbl->i2c_util( + &o_ctrl->i2c_client, MSM_CCI_RELEASE); + if (rc < 0) + pr_err("cci_init failed\n"); + } + o_ctrl->ois_state = OIS_DISABLE_STATE; + mutex_unlock(o_ctrl->ois_mutex); + CDBG("Exit\n"); + return rc; +} + +static const struct v4l2_subdev_internal_ops msm_ois_internal_ops = { + .close = msm_ois_close, +}; + +static long msm_ois_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int rc; + struct msm_ois_ctrl_t *o_ctrl = v4l2_get_subdevdata(sd); + void __user *argp = (void __user *)arg; + CDBG("Enter\n"); + CDBG("%s:%d o_ctrl %p argp %p\n", __func__, __LINE__, o_ctrl, argp); + switch (cmd) { + case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID: + return msm_ois_get_subdev_id(o_ctrl, argp); + case VIDIOC_MSM_OIS_CFG: + return msm_ois_config(o_ctrl, argp); + case MSM_SD_SHUTDOWN: + if (!o_ctrl->i2c_client.i2c_func_tbl) { + pr_err("o_ctrl->i2c_client.i2c_func_tbl NULL\n"); + return -EINVAL; + } + rc = msm_ois_power_down(o_ctrl); + if (rc < 0) { + pr_err("%s:%d OIS Power down failed\n", + __func__, __LINE__); + } + return msm_ois_close(sd, NULL); + default: + return -ENOIOCTLCMD; + } +} + +static int32_t msm_ois_power_up(struct msm_ois_ctrl_t *o_ctrl) +{ + int rc = 0; + enum msm_sensor_power_seq_gpio_t gpio; + + CDBG("%s called\n", __func__); + + rc = msm_ois_vreg_control(o_ctrl, 1); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + return rc; + } + + for (gpio = SENSOR_GPIO_AF_PWDM; + gpio < SENSOR_GPIO_MAX; gpio++) { + if (o_ctrl->gconf && o_ctrl->gconf->gpio_num_info && + o_ctrl->gconf->gpio_num_info->valid[gpio] == 1) { + rc = msm_camera_request_gpio_table( + o_ctrl->gconf->cam_gpio_req_tbl, + o_ctrl->gconf->cam_gpio_req_tbl_size, 1); + if (rc < 0) { + pr_err("ERR:%s:Failed in selecting state for ois: %d\n", + __func__, rc); + return rc; + } + if (o_ctrl->cam_pinctrl_status) { + rc = pinctrl_select_state( + o_ctrl->pinctrl_info.pinctrl, + o_ctrl->pinctrl_info.gpio_state_active); + if (rc < 0) + pr_err("ERR:%s:%d cannot set pin to active state: %d", + __func__, __LINE__, rc); + } + + gpio_set_value_cansleep( + o_ctrl->gconf->gpio_num_info->gpio_num[gpio], + 1); + } + } + + o_ctrl->ois_state = OIS_ENABLE_STATE; + CDBG("Exit\n"); + return rc; +} + +static struct v4l2_subdev_core_ops msm_ois_subdev_core_ops = { + .ioctl = msm_ois_subdev_ioctl, +}; + +static struct v4l2_subdev_ops msm_ois_subdev_ops = { + .core = &msm_ois_subdev_core_ops, +}; + +static const struct i2c_device_id msm_ois_i2c_id[] = { + {"qcom,ois", (kernel_ulong_t)NULL}, + { } +}; + +static int32_t msm_ois_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + struct msm_ois_ctrl_t *ois_ctrl_t = NULL; + CDBG("Enter\n"); + + if (client == NULL) { + pr_err("msm_ois_i2c_probe: client is null\n"); + return -EINVAL; + } + + ois_ctrl_t = kzalloc(sizeof(struct msm_ois_ctrl_t), + GFP_KERNEL); + if (!ois_ctrl_t) { + pr_err("%s:%d failed no memory\n", __func__, __LINE__); + return -ENOMEM; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("i2c_check_functionality failed\n"); + rc = -EINVAL; + goto probe_failure; + } + + CDBG("client = 0x%p\n", client); + + rc = of_property_read_u32(client->dev.of_node, "cell-index", + &ois_ctrl_t->subdev_id); + CDBG("cell-index %d, rc %d\n", ois_ctrl_t->subdev_id, rc); + if (rc < 0) { + pr_err("failed rc %d\n", rc); + goto probe_failure; + } + + ois_ctrl_t->i2c_driver = &msm_ois_i2c_driver; + ois_ctrl_t->i2c_client.client = client; + /* Set device type as I2C */ + ois_ctrl_t->ois_device_type = MSM_CAMERA_I2C_DEVICE; + ois_ctrl_t->i2c_client.i2c_func_tbl = &msm_sensor_qup_func_tbl; + ois_ctrl_t->ois_v4l2_subdev_ops = &msm_ois_subdev_ops; + ois_ctrl_t->ois_mutex = &msm_ois_mutex; + + /* Assign name for sub device */ + snprintf(ois_ctrl_t->msm_sd.sd.name, sizeof(ois_ctrl_t->msm_sd.sd.name), + "%s", ois_ctrl_t->i2c_driver->driver.name); + + /* Initialize sub device */ + v4l2_i2c_subdev_init(&ois_ctrl_t->msm_sd.sd, + ois_ctrl_t->i2c_client.client, + ois_ctrl_t->ois_v4l2_subdev_ops); + v4l2_set_subdevdata(&ois_ctrl_t->msm_sd.sd, ois_ctrl_t); + ois_ctrl_t->msm_sd.sd.internal_ops = &msm_ois_internal_ops; + ois_ctrl_t->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + media_entity_init(&ois_ctrl_t->msm_sd.sd.entity, 0, NULL, 0); + ois_ctrl_t->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + ois_ctrl_t->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_OIS; + ois_ctrl_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2; + msm_sd_register(&ois_ctrl_t->msm_sd); + ois_ctrl_t->ois_state = OIS_DISABLE_STATE; + pr_info("msm_ois_i2c_probe: succeeded\n"); + CDBG("Exit\n"); + +probe_failure: + kfree(ois_ctrl_t); + return rc; +} + +#ifdef CONFIG_COMPAT +static long msm_ois_subdev_do_ioctl( + struct file *file, unsigned int cmd, void *arg) +{ + long rc = 0; + struct video_device *vdev; + struct v4l2_subdev *sd; + struct msm_ois_cfg_data32 *u32; + struct msm_ois_cfg_data ois_data; + void *parg; + struct msm_camera_i2c_seq_reg_setting settings; + struct msm_camera_i2c_seq_reg_setting32 settings32; + + if (!file || !arg) { + pr_err("%s:failed NULL parameter\n", __func__); + return -EINVAL; + } + vdev = video_devdata(file); + sd = vdev_to_v4l2_subdev(vdev); + u32 = (struct msm_ois_cfg_data32 *)arg; + parg = arg; + + ois_data.cfgtype = u32->cfgtype; + + switch (cmd) { + case VIDIOC_MSM_OIS_CFG32: + cmd = VIDIOC_MSM_OIS_CFG; + + switch (u32->cfgtype) { + case CFG_OIS_CONTROL: + ois_data.cfg.set_info.ois_params.setting_size = + u32->cfg.set_info.ois_params.setting_size; + ois_data.cfg.set_info.ois_params.i2c_addr = + u32->cfg.set_info.ois_params.i2c_addr; + ois_data.cfg.set_info.ois_params.i2c_freq_mode = + u32->cfg.set_info.ois_params.i2c_freq_mode; + ois_data.cfg.set_info.ois_params.i2c_addr_type = + u32->cfg.set_info.ois_params.i2c_addr_type; + ois_data.cfg.set_info.ois_params.i2c_data_type = + u32->cfg.set_info.ois_params.i2c_data_type; + ois_data.cfg.set_info.ois_params.settings = + compat_ptr(u32->cfg.set_info.ois_params. + settings); + parg = &ois_data; + break; + case CFG_OIS_I2C_WRITE_SEQ_TABLE: + if (copy_from_user(&settings32, + (void *)compat_ptr(u32->cfg.settings), + sizeof( + struct msm_camera_i2c_seq_reg_setting32))) { + pr_err("copy_from_user failed\n"); + return -EFAULT; + } + + settings.addr_type = settings32.addr_type; + settings.delay = settings32.delay; + settings.size = settings32.size; + settings.reg_setting = + compat_ptr(settings32.reg_setting); + + ois_data.cfgtype = u32->cfgtype; + ois_data.cfg.settings = &settings; + parg = &ois_data; + break; + default: + parg = &ois_data; + break; + } + } + rc = msm_ois_subdev_ioctl(sd, cmd, parg); + + return rc; +} + +static long msm_ois_subdev_fops_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return video_usercopy(file, cmd, arg, msm_ois_subdev_do_ioctl); +} +#endif + +static int32_t msm_ois_platform_probe(struct platform_device *pdev) +{ + int32_t rc = 0; + struct msm_camera_cci_client *cci_client = NULL; + struct msm_ois_ctrl_t *msm_ois_t = NULL; + struct msm_ois_vreg *vreg_cfg; + CDBG("Enter\n"); + + if (!pdev->dev.of_node) { + pr_err("of_node NULL\n"); + return -EINVAL; + } + + msm_ois_t = kzalloc(sizeof(struct msm_ois_ctrl_t), + GFP_KERNEL); + if (!msm_ois_t) { + pr_err("%s:%d failed no memory\n", __func__, __LINE__); + return -ENOMEM; + } + rc = of_property_read_u32((&pdev->dev)->of_node, "cell-index", + &pdev->id); + CDBG("cell-index %d, rc %d\n", pdev->id, rc); + if (rc < 0) { + kfree(msm_ois_t); + pr_err("failed rc %d\n", rc); + return rc; + } + + rc = of_property_read_u32((&pdev->dev)->of_node, "qcom,cci-master", + &msm_ois_t->cci_master); + CDBG("qcom,cci-master %d, rc %d\n", msm_ois_t->cci_master, rc); + if (rc < 0 || msm_ois_t->cci_master >= MASTER_MAX) { + kfree(msm_ois_t); + pr_err("failed rc %d\n", rc); + return rc; + } + + if (of_find_property((&pdev->dev)->of_node, + "qcom,cam-vreg-name", NULL)) { + vreg_cfg = &msm_ois_t->vreg_cfg; + rc = msm_camera_get_dt_vreg_data((&pdev->dev)->of_node, + &vreg_cfg->cam_vreg, &vreg_cfg->num_vreg); + if (rc < 0) { + kfree(msm_ois_t); + pr_err("failed rc %d\n", rc); + return rc; + } + } + + rc = msm_sensor_driver_get_gpio_data(&(msm_ois_t->gconf), + (&pdev->dev)->of_node); + if (rc < 0) { + pr_err("%s: No/Error OIS GPIO\n", __func__); + } else { + msm_ois_t->cam_pinctrl_status = 1; + rc = msm_camera_pinctrl_init( + &(msm_ois_t->pinctrl_info), &(pdev->dev)); + if (rc < 0) { + pr_err("ERR:%s: Error in reading OIS pinctrl\n", + __func__); + msm_ois_t->cam_pinctrl_status = 0; + } + } + + msm_ois_t->ois_v4l2_subdev_ops = &msm_ois_subdev_ops; + msm_ois_t->ois_mutex = &msm_ois_mutex; + + /* Set platform device handle */ + msm_ois_t->pdev = pdev; + /* Set device type as platform device */ + msm_ois_t->ois_device_type = MSM_CAMERA_PLATFORM_DEVICE; + msm_ois_t->i2c_client.i2c_func_tbl = &msm_sensor_cci_func_tbl; + msm_ois_t->i2c_client.cci_client = kzalloc(sizeof( + struct msm_camera_cci_client), GFP_KERNEL); + if (!msm_ois_t->i2c_client.cci_client) { + kfree(msm_ois_t->vreg_cfg.cam_vreg); + kfree(msm_ois_t); + pr_err("failed no memory\n"); + return -ENOMEM; + } + + cci_client = msm_ois_t->i2c_client.cci_client; + cci_client->cci_subdev = msm_cci_get_subdev(); + cci_client->cci_i2c_master = msm_ois_t->cci_master; + v4l2_subdev_init(&msm_ois_t->msm_sd.sd, + msm_ois_t->ois_v4l2_subdev_ops); + v4l2_set_subdevdata(&msm_ois_t->msm_sd.sd, msm_ois_t); + msm_ois_t->msm_sd.sd.internal_ops = &msm_ois_internal_ops; + msm_ois_t->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(msm_ois_t->msm_sd.sd.name, + ARRAY_SIZE(msm_ois_t->msm_sd.sd.name), "msm_ois"); + media_entity_init(&msm_ois_t->msm_sd.sd.entity, 0, NULL, 0); + msm_ois_t->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + msm_ois_t->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_OIS; + msm_ois_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2; + msm_sd_register(&msm_ois_t->msm_sd); + msm_ois_t->ois_state = OIS_DISABLE_STATE; + msm_cam_copy_v4l2_subdev_fops(&msm_ois_v4l2_subdev_fops); +#ifdef CONFIG_COMPAT + msm_ois_v4l2_subdev_fops.compat_ioctl32 = + msm_ois_subdev_fops_ioctl; +#endif + msm_ois_t->msm_sd.sd.devnode->fops = + &msm_ois_v4l2_subdev_fops; + + CDBG("Exit\n"); + return rc; +} + +static const struct of_device_id msm_ois_i2c_dt_match[] = { + {.compatible = "qcom,ois"}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_ois_i2c_dt_match); + +static struct i2c_driver msm_ois_i2c_driver = { + .id_table = msm_ois_i2c_id, + .probe = msm_ois_i2c_probe, + .remove = __exit_p(msm_ois_i2c_remove), + .driver = { + .name = "qcom,ois", + .owner = THIS_MODULE, + .of_match_table = msm_ois_i2c_dt_match, + }, +}; + +static const struct of_device_id msm_ois_dt_match[] = { + {.compatible = "qcom,ois", .data = NULL}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_ois_dt_match); + +static struct platform_driver msm_ois_platform_driver = { + .probe = msm_ois_platform_probe, + .driver = { + .name = "qcom,ois", + .owner = THIS_MODULE, + .of_match_table = msm_ois_dt_match, + }, +}; + +static int __init msm_ois_init_module(void) +{ + int32_t rc = 0; + CDBG("Enter\n"); + rc = platform_driver_register(&msm_ois_platform_driver); + if (!rc) + return rc; + CDBG("%s:%d rc %d\n", __func__, __LINE__, rc); + return i2c_add_driver(&msm_ois_i2c_driver); +} + +static void __exit msm_ois_exit_module(void) +{ + platform_driver_unregister(&msm_ois_platform_driver); + i2c_del_driver(&msm_ois_i2c_driver); + return; +} + +module_init(msm_ois_init_module); +module_exit(msm_ois_exit_module); +MODULE_DESCRIPTION("MSM OIS"); +MODULE_LICENSE("GPL v2"); diff --git a/camera/sensor/ois/msm_ois.h b/camera/sensor/ois/msm_ois.h new file mode 100644 index 00000000..62c61b93 --- /dev/null +++ b/camera/sensor/ois/msm_ois.h @@ -0,0 +1,66 @@ +/* Copyright (c) 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_OIS_H +#define MSM_OIS_H + +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <lenovo_soc/qcom/camera2.h> +#include <media/v4l2-subdev.h> +#include <lenovo_media/msmb_camera.h> +#include "msm_camera_i2c.h" +#include "msm_camera_dt_util.h" +#include "msm_camera_io_util.h" + +#define DEFINE_MSM_MUTEX(mutexname) \ + static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) + +#define MSM_OIS_MAX_VREGS (10) + +struct msm_ois_ctrl_t; + +enum msm_ois_state_t { + OIS_ENABLE_STATE, + OIS_OPS_ACTIVE, + OIS_OPS_INACTIVE, + OIS_DISABLE_STATE, +}; + +struct msm_ois_vreg { + struct camera_vreg_t *cam_vreg; + void *data[MSM_OIS_MAX_VREGS]; + int num_vreg; +}; + +struct msm_ois_ctrl_t { + struct i2c_driver *i2c_driver; + struct platform_driver *pdriver; + struct platform_device *pdev; + struct msm_camera_i2c_client i2c_client; + enum msm_camera_device_type_t ois_device_type; + struct msm_sd_subdev msm_sd; + struct mutex *ois_mutex; + enum msm_camera_i2c_data_type i2c_data_type; + struct v4l2_subdev sdev; + struct v4l2_subdev_ops *ois_v4l2_subdev_ops; + void *user_data; + uint16_t i2c_tbl_index; + enum cci_i2c_master_t cci_master; + uint32_t subdev_id; + enum msm_ois_state_t ois_state; + struct msm_ois_vreg vreg_cfg; + struct msm_camera_gpio_conf *gconf; + struct msm_pinctrl_info pinctrl_info; + uint8_t cam_pinctrl_status; +}; + +#endif |
