| /* Copyright (c) 2017, 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/firmware.h> |
| #include <cam_sensor_cmn_header.h> |
| #include "cam_ois_core.h" |
| #include "cam_ois_soc.h" |
| #include "cam_sensor_util.h" |
| #include "cam_debug_util.h" |
| |
| /** |
| * cam_ois_get_dev_handle - get device handle |
| * @o_ctrl: ctrl structure |
| * @arg: Camera control command argument |
| * |
| * Returns success or failure |
| */ |
| static int cam_ois_get_dev_handle(struct cam_ois_ctrl_t *o_ctrl, |
| void *arg) |
| { |
| struct cam_sensor_acquire_dev ois_acq_dev; |
| struct cam_create_dev_hdl bridge_params; |
| struct cam_control *cmd = (struct cam_control *)arg; |
| |
| if (o_ctrl->bridge_intf.device_hdl != -1) { |
| CAM_ERR(CAM_OIS, "Device is already acquired"); |
| return -EFAULT; |
| } |
| if (copy_from_user(&ois_acq_dev, (void __user *) cmd->handle, |
| sizeof(ois_acq_dev))) |
| return -EFAULT; |
| |
| bridge_params.session_hdl = ois_acq_dev.session_handle; |
| bridge_params.ops = &o_ctrl->bridge_intf.ops; |
| bridge_params.v4l2_sub_dev_flag = 0; |
| bridge_params.media_entity_flag = 0; |
| bridge_params.priv = o_ctrl; |
| |
| ois_acq_dev.device_handle = |
| cam_create_device_hdl(&bridge_params); |
| o_ctrl->bridge_intf.device_hdl = ois_acq_dev.device_handle; |
| o_ctrl->bridge_intf.session_hdl = ois_acq_dev.session_handle; |
| |
| CAM_DBG(CAM_OIS, "Device Handle: %d", ois_acq_dev.device_handle); |
| if (copy_to_user((void __user *) cmd->handle, &ois_acq_dev, |
| sizeof(struct cam_sensor_acquire_dev))) { |
| CAM_ERR(CAM_OIS, "ACQUIRE_DEV: copy to user failed"); |
| return -EFAULT; |
| } |
| return 0; |
| } |
| |
| static int cam_ois_vreg_control(struct cam_ois_ctrl_t *o_ctrl, |
| int config) |
| { |
| int rc = 0, cnt; |
| struct cam_hw_soc_info *soc_info; |
| |
| soc_info = &o_ctrl->soc_info; |
| cnt = soc_info->num_rgltr; |
| |
| if (!cnt) |
| return 0; |
| |
| if (cnt >= CAM_SOC_MAX_REGULATOR) { |
| CAM_ERR(CAM_OIS, "Regulators more than supported %d", cnt); |
| return -EINVAL; |
| } |
| |
| if (config) |
| rc = cam_soc_util_enable_platform_resource(soc_info, false, 0, |
| false); |
| else |
| rc = cam_soc_util_disable_platform_resource(soc_info, false, |
| false); |
| |
| return rc; |
| } |
| |
| static int cam_ois_power_up(struct cam_ois_ctrl_t *o_ctrl) |
| { |
| int rc = 0; |
| struct cam_hw_soc_info *soc_info = |
| &o_ctrl->soc_info; |
| struct msm_camera_gpio_num_info *gpio_num_info = NULL; |
| |
| rc = cam_ois_vreg_control(o_ctrl, 1); |
| if (rc < 0) { |
| CAM_ERR(CAM_OIS, "OIS Reg Failed %d", rc); |
| return rc; |
| } |
| |
| gpio_num_info = o_ctrl->gpio_num_info; |
| |
| if (soc_info->gpio_data && |
| gpio_num_info && |
| gpio_num_info->valid[SENSOR_VAF] == 1) { |
| rc = cam_soc_util_request_platform_resource(&o_ctrl->soc_info, |
| NULL, NULL); |
| rc = cam_soc_util_enable_platform_resource(&o_ctrl->soc_info, |
| false, 0, false); |
| if (rc < 0) { |
| CAM_ERR(CAM_OIS, "Failed in req gpio: %d", rc); |
| return rc; |
| } |
| |
| gpio_set_value_cansleep( |
| gpio_num_info->gpio_num[SENSOR_VAF], |
| 1); |
| } |
| |
| /* VREG needs some delay to power up */ |
| usleep_range(2000, 2050); |
| |
| return rc; |
| } |
| |
| static int cam_ois_power_down(struct cam_ois_ctrl_t *o_ctrl) |
| { |
| int32_t rc = 0; |
| struct cam_hw_soc_info *soc_info = |
| &o_ctrl->soc_info; |
| struct msm_camera_gpio_num_info *gpio_num_info = NULL; |
| |
| rc = cam_ois_vreg_control(o_ctrl, 0); |
| if (rc < 0) { |
| CAM_ERR(CAM_OIS, "Failed %d"); |
| return rc; |
| } |
| |
| gpio_num_info = o_ctrl->gpio_num_info; |
| |
| if (soc_info->gpio_data && |
| gpio_num_info && |
| gpio_num_info->valid[SENSOR_VAF] == 1) { |
| |
| gpio_set_value_cansleep( |
| gpio_num_info->gpio_num[SENSOR_VAF], |
| GPIOF_OUT_INIT_LOW); |
| |
| rc = cam_soc_util_release_platform_resource(&o_ctrl->soc_info); |
| rc |= cam_soc_util_disable_platform_resource(&o_ctrl->soc_info, |
| 0, 0); |
| if (rc < 0) |
| CAM_ERR(CAM_OIS, |
| "Failed to disable platform resources: %d", rc); |
| } |
| |
| return rc; |
| } |
| |
| static int cam_ois_apply_settings(struct cam_ois_ctrl_t *o_ctrl, |
| struct i2c_settings_array *i2c_set) |
| { |
| struct i2c_settings_list *i2c_list; |
| int32_t rc = 0; |
| uint32_t i, size; |
| |
| if (o_ctrl == NULL || i2c_set == NULL) { |
| CAM_ERR(CAM_OIS, "Invalid Args"); |
| return -EINVAL; |
| } |
| |
| if (i2c_set->is_settings_valid != 1) { |
| CAM_ERR(CAM_OIS, " Invalid settings"); |
| return -EINVAL; |
| } |
| |
| list_for_each_entry(i2c_list, |
| &(i2c_set->list_head), list) { |
| if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_RANDOM) { |
| rc = camera_io_dev_write(&(o_ctrl->io_master_info), |
| &(i2c_list->i2c_settings)); |
| if (rc < 0) { |
| CAM_ERR(CAM_OIS, |
| "Failed in Applying i2c wrt settings"); |
| return rc; |
| } |
| } else if (i2c_list->op_code == CAM_SENSOR_I2C_POLL) { |
| size = i2c_list->i2c_settings.size; |
| for (i = 0; i < size; i++) { |
| rc = camera_io_dev_poll( |
| &(o_ctrl->io_master_info), |
| i2c_list->i2c_settings. |
| reg_setting[i].reg_addr, |
| i2c_list->i2c_settings. |
| reg_setting[i].reg_data, |
| i2c_list->i2c_settings. |
| reg_setting[i].data_mask, |
| i2c_list->i2c_settings.addr_type, |
| i2c_list->i2c_settings.data_type, |
| i2c_list->i2c_settings. |
| reg_setting[i].delay); |
| if (rc < 0) { |
| CAM_ERR(CAM_OIS, |
| "i2c poll apply setting Fail"); |
| return rc; |
| } |
| } |
| } |
| } |
| |
| return rc; |
| } |
| |
| static int cam_ois_slaveInfo_pkt_parser(struct cam_ois_ctrl_t *o_ctrl, |
| uint32_t *cmd_buf) |
| { |
| int32_t rc = 0; |
| struct cam_cmd_ois_info *ois_info; |
| |
| if (!o_ctrl || !cmd_buf) { |
| CAM_ERR(CAM_OIS, "Invalid Args"); |
| return -EINVAL; |
| } |
| |
| ois_info = (struct cam_cmd_ois_info *)cmd_buf; |
| if (o_ctrl->io_master_info.master_type == CCI_MASTER) { |
| o_ctrl->io_master_info.cci_client->i2c_freq_mode = |
| ois_info->i2c_freq_mode; |
| o_ctrl->io_master_info.cci_client->sid = |
| ois_info->slave_addr >> 1; |
| o_ctrl->ois_fw_flag = ois_info->ois_fw_flag; |
| o_ctrl->is_ois_calib = ois_info->is_ois_calib; |
| memcpy(o_ctrl->ois_name, ois_info->ois_name, 32); |
| o_ctrl->io_master_info.cci_client->retries = 3; |
| o_ctrl->io_master_info.cci_client->id_map = 0; |
| memcpy(&(o_ctrl->opcode), &(ois_info->opcode), |
| sizeof(struct cam_ois_opcode)); |
| CAM_DBG(CAM_OIS, "Slave addr: 0x%x Freq Mode: %d", |
| ois_info->slave_addr, ois_info->i2c_freq_mode); |
| } else if (o_ctrl->io_master_info.master_type == I2C_MASTER) { |
| o_ctrl->io_master_info.client->addr = ois_info->slave_addr; |
| CAM_DBG(CAM_OIS, "Slave addr: 0x%x", ois_info->slave_addr); |
| } else { |
| CAM_ERR(CAM_OIS, "Invalid Master type : %d", |
| o_ctrl->io_master_info.master_type); |
| rc = -EINVAL; |
| } |
| |
| return rc; |
| } |
| |
| static int cam_ois_fw_download(struct cam_ois_ctrl_t *o_ctrl) |
| { |
| uint16_t total_bytes = 0; |
| uint8_t *ptr = NULL; |
| int32_t rc = 0, cnt; |
| const struct firmware *fw = NULL; |
| const char *fw_name_prog = NULL; |
| const char *fw_name_coeff = NULL; |
| char name_prog[32] = {0}; |
| char name_coeff[32] = {0}; |
| struct device *dev = &(o_ctrl->pdev->dev); |
| struct cam_sensor_i2c_reg_setting i2c_reg_setting; |
| |
| if (!o_ctrl) { |
| CAM_ERR(CAM_OIS, "Invalid Args"); |
| return -EINVAL; |
| } |
| |
| snprintf(name_coeff, 32, "%s.coeff", o_ctrl->ois_name); |
| |
| snprintf(name_prog, 32, "%s.prog", o_ctrl->ois_name); |
| |
| /* cast pointer as const pointer*/ |
| fw_name_prog = name_prog; |
| fw_name_coeff = name_coeff; |
| |
| /* Load FW */ |
| rc = request_firmware(&fw, fw_name_prog, dev); |
| if (rc) { |
| CAM_ERR(CAM_OIS, "Failed to locate %s", fw_name_prog); |
| return rc; |
| } |
| |
| total_bytes = fw->size; |
| i2c_reg_setting.addr_type = CAMERA_SENSOR_I2C_TYPE_BYTE; |
| i2c_reg_setting.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE; |
| i2c_reg_setting.size = total_bytes; |
| i2c_reg_setting.reg_setting = (struct cam_sensor_i2c_reg_array *) |
| kzalloc(sizeof(struct cam_sensor_i2c_reg_array) * total_bytes, |
| GFP_KERNEL); |
| if (!i2c_reg_setting.reg_setting) { |
| CAM_ERR(CAM_OIS, "Failed in allocating i2c_array"); |
| release_firmware(fw); |
| return -ENOMEM; |
| } |
| |
| for (cnt = 0, ptr = (uint8_t *)fw->data; cnt < total_bytes; |
| cnt++, ptr++) { |
| i2c_reg_setting.reg_setting[cnt].reg_addr = |
| o_ctrl->opcode.prog; |
| i2c_reg_setting.reg_setting[cnt].reg_data = *ptr; |
| i2c_reg_setting.reg_setting[cnt].delay = 0; |
| i2c_reg_setting.reg_setting[cnt].data_mask = 0; |
| } |
| |
| rc = camera_io_dev_write_continuous(&(o_ctrl->io_master_info), |
| &i2c_reg_setting, 1); |
| if (rc < 0) { |
| CAM_ERR(CAM_OIS, "OIS FW download failed %d", rc); |
| goto release_firmware; |
| } |
| kfree(i2c_reg_setting.reg_setting); |
| release_firmware(fw); |
| |
| rc = request_firmware(&fw, fw_name_coeff, dev); |
| if (rc) { |
| CAM_ERR(CAM_OIS, "Failed to locate %s", fw_name_coeff); |
| return rc; |
| } |
| |
| total_bytes = fw->size; |
| i2c_reg_setting.addr_type = CAMERA_SENSOR_I2C_TYPE_BYTE; |
| i2c_reg_setting.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE; |
| i2c_reg_setting.size = total_bytes; |
| i2c_reg_setting.reg_setting = (struct cam_sensor_i2c_reg_array *) |
| kzalloc(sizeof(struct cam_sensor_i2c_reg_array) * total_bytes, |
| GFP_KERNEL); |
| if (!i2c_reg_setting.reg_setting) { |
| CAM_ERR(CAM_OIS, "Failed in allocating i2c_array"); |
| release_firmware(fw); |
| return -ENOMEM; |
| } |
| |
| for (cnt = 0, ptr = (uint8_t *)fw->data; cnt < total_bytes; |
| cnt++, ptr++) { |
| i2c_reg_setting.reg_setting[cnt].reg_addr = |
| o_ctrl->opcode.coeff; |
| i2c_reg_setting.reg_setting[cnt].reg_data = *ptr; |
| i2c_reg_setting.reg_setting[cnt].delay = 0; |
| i2c_reg_setting.reg_setting[cnt].data_mask = 0; |
| } |
| |
| rc = camera_io_dev_write_continuous(&(o_ctrl->io_master_info), |
| &i2c_reg_setting, 1); |
| if (rc < 0) |
| CAM_ERR(CAM_OIS, "OIS FW download failed %d", rc); |
| |
| release_firmware: |
| kfree(i2c_reg_setting.reg_setting); |
| release_firmware(fw); |
| |
| return rc; |
| } |
| |
| /** |
| * cam_ois_pkt_parse - Parse csl packet |
| * @o_ctrl: ctrl structure |
| * @arg: Camera control command argument |
| * |
| * Returns success or failure |
| */ |
| static int cam_ois_pkt_parse(struct cam_ois_ctrl_t *o_ctrl, void *arg) |
| { |
| int32_t rc = 0; |
| uint64_t generic_ptr; |
| struct cam_control *ioctl_ctrl = NULL; |
| struct cam_config_dev_cmd dev_config; |
| struct i2c_settings_array *i2c_reg_settings = NULL; |
| struct cam_cmd_buf_desc *cmd_desc = NULL; |
| uint64_t generic_pkt_addr; |
| size_t pkt_len; |
| struct cam_packet *csl_packet = NULL; |
| size_t len_of_buff = 0; |
| uint32_t *offset = NULL, *cmd_buf; |
| |
| ioctl_ctrl = (struct cam_control *)arg; |
| if (copy_from_user(&dev_config, (void __user *) ioctl_ctrl->handle, |
| sizeof(dev_config))) |
| return -EFAULT; |
| rc = cam_mem_get_cpu_buf(dev_config.packet_handle, |
| (uint64_t *)&generic_pkt_addr, &pkt_len); |
| if (rc) { |
| CAM_ERR(CAM_OIS, |
| "error in converting command Handle Error: %d", rc); |
| return rc; |
| } |
| |
| if (dev_config.offset > pkt_len) { |
| CAM_ERR(CAM_OIS, |
| "offset is out of bound: off: %lld len: %zu", |
| dev_config.offset, pkt_len); |
| return -EINVAL; |
| } |
| |
| csl_packet = (struct cam_packet *) |
| (generic_pkt_addr + dev_config.offset); |
| switch (csl_packet->header.op_code & 0xFFFFFF) { |
| case CAM_OIS_PACKET_OPCODE_INIT: |
| offset = (uint32_t *)&csl_packet->payload; |
| offset += (csl_packet->cmd_buf_offset / sizeof(uint32_t)); |
| cmd_desc = (struct cam_cmd_buf_desc *)(offset); |
| |
| if ((csl_packet->num_cmd_buf < 2) && |
| (csl_packet->num_cmd_buf > 3)) { |
| CAM_ERR(CAM_OIS, "wrong cmd Buffer count: %d", |
| csl_packet->num_cmd_buf); |
| return -EINVAL; |
| } |
| |
| rc = cam_mem_get_cpu_buf(cmd_desc[0].mem_handle, |
| (uint64_t *)&generic_ptr, &len_of_buff); |
| if (rc < 0) { |
| CAM_ERR(CAM_OIS, "Failed to get cpu buf"); |
| return rc; |
| } |
| |
| cmd_buf = (uint32_t *)generic_ptr; |
| cmd_buf += cmd_desc->offset / sizeof(uint32_t); |
| rc = cam_ois_slaveInfo_pkt_parser(o_ctrl, cmd_buf); |
| if (rc < 0) { |
| CAM_ERR(CAM_OIS, "Failed in parsing the pkt"); |
| return rc; |
| } |
| |
| cmd_buf += (sizeof(struct cam_cmd_i2c_info)/sizeof(uint32_t)); |
| |
| i2c_reg_settings = &(o_ctrl->i2c_init_data); |
| i2c_reg_settings->is_settings_valid = 1; |
| i2c_reg_settings->request_id = 0; |
| rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings, |
| &cmd_desc[1], 1); |
| if (rc < 0) { |
| CAM_ERR(CAM_OIS, "OIS pkt parsing failed: %d", |
| rc); |
| return rc; |
| } |
| |
| if (o_ctrl->is_ois_calib) { |
| i2c_reg_settings = &(o_ctrl->i2c_calib_data); |
| i2c_reg_settings->is_settings_valid = 1; |
| i2c_reg_settings->request_id = 0; |
| rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings, |
| &cmd_desc[2], 1); |
| if (rc < 0) { |
| CAM_ERR(CAM_OIS, |
| "OIS pkt parsing failed: %d", rc); |
| return rc; |
| } |
| } |
| break; |
| case CAM_OIS_PACKET_OPCODE_OIS_CONTROL: |
| offset = (uint32_t *)&csl_packet->payload; |
| offset += (csl_packet->cmd_buf_offset / sizeof(uint32_t)); |
| cmd_desc = (struct cam_cmd_buf_desc *)(offset); |
| i2c_reg_settings = &(o_ctrl->i2c_mode_data); |
| i2c_reg_settings->is_settings_valid = 1; |
| i2c_reg_settings->request_id = 0; |
| rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings, |
| cmd_desc, 1); |
| if (rc < 0) { |
| CAM_ERR(CAM_OIS, "OIS pkt parsing failed: %d", rc); |
| return rc; |
| } |
| |
| rc = cam_ois_apply_settings(o_ctrl, i2c_reg_settings); |
| if (rc < 0) |
| CAM_ERR(CAM_OIS, "Cannot apply mode settings"); |
| break; |
| default: |
| break; |
| } |
| return rc; |
| } |
| |
| /** |
| * cam_ois_driver_cmd - Handle ois cmds |
| * @e_ctrl: ctrl structure |
| * @arg: Camera control command argument |
| * |
| * Returns success or failure |
| */ |
| int cam_ois_driver_cmd(struct cam_ois_ctrl_t *o_ctrl, void *arg) |
| { |
| int rc = 0; |
| struct cam_ois_query_cap_t ois_cap = {0}; |
| struct cam_control *cmd = (struct cam_control *)arg; |
| |
| if (!o_ctrl) { |
| CAM_ERR(CAM_OIS, "e_ctrl is NULL"); |
| return -EINVAL; |
| } |
| |
| mutex_lock(&(o_ctrl->ois_mutex)); |
| switch (cmd->op_code) { |
| case CAM_QUERY_CAP: |
| ois_cap.slot_info = o_ctrl->soc_info.index; |
| |
| if (copy_to_user((void __user *) cmd->handle, |
| &ois_cap, |
| sizeof(struct cam_ois_query_cap_t))) { |
| CAM_ERR(CAM_OIS, "Failed Copy to User"); |
| return -EFAULT; |
| goto release_mutex; |
| } |
| CAM_DBG(CAM_OIS, "ois_cap: ID: %d", ois_cap.slot_info); |
| break; |
| case CAM_ACQUIRE_DEV: |
| rc = cam_ois_get_dev_handle(o_ctrl, arg); |
| if (rc) { |
| CAM_ERR(CAM_OIS, "Failed to acquire dev"); |
| goto release_mutex; |
| } |
| break; |
| case CAM_START_DEV: |
| rc = cam_ois_power_up(o_ctrl); |
| if (rc) { |
| CAM_ERR(CAM_OIS, " OIS Power up failed"); |
| goto release_mutex; |
| } |
| rc = camera_io_init(&o_ctrl->io_master_info); |
| if (rc) { |
| CAM_ERR(CAM_OIS, "cci_init failed"); |
| goto pwr_dwn; |
| } |
| |
| if (o_ctrl->ois_fw_flag) { |
| rc = cam_ois_fw_download(o_ctrl); |
| if (rc) { |
| CAM_ERR(CAM_OIS, "Failed OIS FW Download"); |
| goto pwr_dwn; |
| } |
| } |
| |
| rc = cam_ois_apply_settings(o_ctrl, &o_ctrl->i2c_init_data); |
| if (rc < 0) { |
| CAM_ERR(CAM_OIS, "Cannot apply Init settings"); |
| goto pwr_dwn; |
| } |
| |
| if (o_ctrl->is_ois_calib) { |
| rc = cam_ois_apply_settings(o_ctrl, |
| &o_ctrl->i2c_calib_data); |
| if (rc) { |
| CAM_ERR(CAM_OIS, "Cannot apply calib data"); |
| goto pwr_dwn; |
| } |
| } |
| break; |
| case CAM_CONFIG_DEV: |
| rc = cam_ois_pkt_parse(o_ctrl, arg); |
| if (rc) { |
| CAM_ERR(CAM_OIS, "Failed in ois pkt Parsing"); |
| goto release_mutex; |
| } |
| break; |
| case CAM_RELEASE_DEV: |
| if (o_ctrl->bridge_intf.device_hdl == -1) { |
| CAM_ERR(CAM_OIS, "link hdl: %d device hdl: %d", |
| o_ctrl->bridge_intf.device_hdl, |
| o_ctrl->bridge_intf.link_hdl); |
| rc = -EINVAL; |
| goto release_mutex; |
| } |
| rc = cam_destroy_device_hdl(o_ctrl->bridge_intf.device_hdl); |
| if (rc < 0) |
| CAM_ERR(CAM_OIS, "destroying the device hdl"); |
| o_ctrl->bridge_intf.device_hdl = -1; |
| o_ctrl->bridge_intf.link_hdl = -1; |
| o_ctrl->bridge_intf.session_hdl = -1; |
| break; |
| case CAM_STOP_DEV: |
| rc = camera_io_release(&o_ctrl->io_master_info); |
| if (rc < 0) |
| CAM_ERR(CAM_OIS, "Failed in releasing CCI"); |
| rc = cam_ois_power_down(o_ctrl); |
| if (rc < 0) { |
| CAM_ERR(CAM_OIS, "OIS Power down failed"); |
| goto release_mutex; |
| } |
| break; |
| default: |
| CAM_ERR(CAM_OIS, "invalid opcode"); |
| goto release_mutex; |
| } |
| pwr_dwn: |
| cam_ois_power_down(o_ctrl); |
| release_mutex: |
| mutex_unlock(&(o_ctrl->ois_mutex)); |
| |
| return rc; |
| } |