| /* Copyright (c) 2014-2018, 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/firmware.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 data_type_to_num_bytes( |
| enum msm_camera_i2c_data_type data_type) |
| { |
| int32_t ret_val; |
| |
| switch (data_type) { |
| case MSM_CAMERA_I2C_BYTE_DATA: |
| ret_val = 1; |
| break; |
| case MSM_CAMERA_I2C_WORD_DATA: |
| ret_val = 2; |
| break; |
| case MSM_CAMERA_I2C_DWORD_DATA: |
| ret_val = 4; |
| break; |
| default: |
| pr_err("unsupported data type: %d\n", |
| data_type); |
| ret_val = 1; |
| break; |
| } |
| return ret_val; |
| } |
| |
| static int32_t msm_ois_download(struct msm_ois_ctrl_t *o_ctrl) |
| { |
| uint16_t bytes_in_tx = 0; |
| uint16_t total_bytes = 0; |
| uint8_t *ptr = NULL; |
| int32_t rc = 0; |
| const struct firmware *fw = NULL; |
| const char *fw_name_prog = NULL; |
| const char *fw_name_coeff = NULL; |
| char name_prog[MAX_SENSOR_NAME] = {0}; |
| char name_coeff[MAX_SENSOR_NAME] = {0}; |
| struct device *dev = &(o_ctrl->pdev->dev); |
| enum msm_camera_i2c_reg_addr_type save_addr_type; |
| |
| CDBG("Enter\n"); |
| save_addr_type = o_ctrl->i2c_client.addr_type; |
| o_ctrl->i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR; |
| |
| snprintf(name_coeff, MAX_SENSOR_NAME, "%s.coeff", |
| o_ctrl->oboard_info->ois_name); |
| |
| snprintf(name_prog, MAX_SENSOR_NAME, "%s.prog", |
| o_ctrl->oboard_info->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) { |
| dev_err(dev, "Failed to locate %s\n", fw_name_prog); |
| o_ctrl->i2c_client.addr_type = save_addr_type; |
| return rc; |
| } |
| |
| total_bytes = fw->size; |
| for (ptr = (uint8_t *)fw->data; total_bytes; |
| total_bytes -= bytes_in_tx, ptr += bytes_in_tx) { |
| bytes_in_tx = (total_bytes > 10) ? 10 : total_bytes; |
| rc = o_ctrl->i2c_client.i2c_func_tbl->i2c_write_seq( |
| &o_ctrl->i2c_client, o_ctrl->oboard_info->opcode.prog, |
| ptr, bytes_in_tx); |
| if (rc < 0) { |
| pr_err("Failed:remaining bytes to be downloaded:%d\n", |
| bytes_in_tx); |
| /* abort download fw and return error*/ |
| goto release_firmware; |
| } |
| } |
| release_firmware(fw); |
| |
| rc = request_firmware(&fw, fw_name_coeff, dev); |
| if (rc) { |
| dev_err(dev, "Failed to locate %s\n", fw_name_coeff); |
| o_ctrl->i2c_client.addr_type = save_addr_type; |
| return rc; |
| } |
| total_bytes = fw->size; |
| for (ptr = (uint8_t *)fw->data; total_bytes; |
| total_bytes -= bytes_in_tx, ptr += bytes_in_tx) { |
| bytes_in_tx = (total_bytes > 10) ? 10 : total_bytes; |
| rc = o_ctrl->i2c_client.i2c_func_tbl->i2c_write_seq( |
| &o_ctrl->i2c_client, o_ctrl->oboard_info->opcode.coeff, |
| ptr, bytes_in_tx); |
| if (rc < 0) { |
| pr_err("Failed:remaining bytes to be downloaded:%d\n", |
| total_bytes); |
| /* abort download fw*/ |
| break; |
| } |
| } |
| release_firmware: |
| release_firmware(fw); |
| o_ctrl->i2c_client.addr_type = save_addr_type; |
| |
| return rc; |
| } |
| |
| static int32_t msm_ois_data_config(struct msm_ois_ctrl_t *o_ctrl, |
| struct msm_ois_slave_info *slave_info) |
| { |
| int rc = 0; |
| struct msm_camera_cci_client *cci_client = NULL; |
| |
| CDBG("Enter\n"); |
| if (!slave_info) { |
| pr_err("failed : invalid slave_info\n"); |
| return -EINVAL; |
| } |
| /* fill ois slave info*/ |
| if (strlcpy(o_ctrl->oboard_info->ois_name, slave_info->ois_name, |
| sizeof(o_ctrl->oboard_info->ois_name)) == 0) { |
| pr_err("failed: invalid ois_name\n"); |
| return -EFAULT; |
| } |
| memcpy(&(o_ctrl->oboard_info->opcode), &(slave_info->opcode), |
| sizeof(struct msm_ois_opcode)); |
| o_ctrl->oboard_info->i2c_slaveaddr = slave_info->i2c_addr; |
| |
| /* config cci_client*/ |
| if (o_ctrl->ois_device_type == MSM_CAMERA_PLATFORM_DEVICE) { |
| cci_client = o_ctrl->i2c_client.cci_client; |
| cci_client->sid = |
| o_ctrl->oboard_info->i2c_slaveaddr >> 1; |
| cci_client->retries = 3; |
| cci_client->id_map = 0; |
| cci_client->cci_i2c_master = o_ctrl->cci_master; |
| } else { |
| o_ctrl->i2c_client.client->addr = |
| o_ctrl->oboard_info->i2c_slaveaddr; |
| } |
| o_ctrl->i2c_client.addr_type = MSM_CAMERA_I2C_WORD_ADDR; |
| |
| CDBG("Exit\n"); |
| return rc; |
| } |
| |
| 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, num_byte_seq = 0; |
| uint8_t *reg_data_seq; |
| |
| 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 (settings[i].delay != 0) |
| 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: |
| case MSM_CAMERA_I2C_DWORD_DATA: |
| |
| num_byte_seq = |
| data_type_to_num_bytes |
| (settings[i].data_type); |
| reg_data_seq = kzalloc(sizeof(uint32_t), |
| GFP_KERNEL); |
| if (!reg_data_seq) |
| return -ENOMEM; |
| |
| rc = msm_camera_cci_i2c_read_seq |
| (&o_ctrl->i2c_client, |
| settings[i].reg_addr, |
| reg_data_seq, |
| num_byte_seq); |
| |
| memcpy(&settings[i].reg_data, |
| reg_data_seq, sizeof(uint32_t)); |
| |
| CDBG("ois data read 0x%x from address 0x%x", |
| settings[i].reg_addr, |
| settings[i].reg_data); |
| |
| kfree(reg_data_seq); |
| reg_data_seq = NULL; |
| |
| break; |
| default: |
| pr_err("Unsupport data type for MSM_OIS_READ: %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, i = 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 __user *)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); |
| |
| for (i = 0; i < set_info->ois_params.setting_size; i++) { |
| if (settings[i].i2c_operation |
| == MSM_OIS_READ) { |
| if (copy_to_user( |
| (void __user *) |
| (&set_info->ois_params.settings[i]), |
| &settings[i], |
| sizeof(struct reg_settings_ois_t))) { |
| kfree(settings); |
| pr_err("Error copying\n"); |
| return -EFAULT; |
| } |
| CDBG("ois_data at addr 0x%x is 0x%x", |
| settings[i].reg_addr, |
| settings[i].reg_data); |
| } |
| } |
| |
| 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 *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 __user *)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 || |
| 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 __user *)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_config_download(struct msm_ois_ctrl_t *o_ctrl, |
| void *argp) |
| { |
| struct msm_ois_cfg_download_data *cdata = |
| (struct msm_ois_cfg_download_data *)argp; |
| int32_t rc = 0; |
| |
| if (!o_ctrl || !cdata) { |
| pr_err("failed: Invalid data\n"); |
| return -EINVAL; |
| } |
| mutex_lock(o_ctrl->ois_mutex); |
| CDBG("Enter\n"); |
| CDBG("%s type %d\n", __func__, cdata->cfgtype); |
| switch (cdata->cfgtype) { |
| case CFG_OIS_DATA_CONFIG: |
| rc = msm_ois_data_config(o_ctrl, &cdata->slave_info); |
| if (rc < 0) |
| pr_err("Failed ois data config %d\n", rc); |
| break; |
| case CFG_OIS_DOWNLOAD: |
| rc = msm_ois_download(o_ctrl); |
| if (rc < 0) |
| pr_err("Failed ois download %d\n", rc); |
| 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 *argp = (void *)arg; |
| |
| CDBG("Enter\n"); |
| CDBG("%s:%d o_ctrl %pK argp %pK\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 VIDIOC_MSM_OIS_CFG_DOWNLOAD: |
| return msm_ois_config_download(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; |
| } |
| mutex_lock(o_ctrl->ois_mutex); |
| rc = msm_ois_power_down(o_ctrl); |
| if (rc < 0) { |
| pr_err("%s:%d OIS Power down failed\n", |
| __func__, __LINE__); |
| } |
| mutex_unlock(o_ctrl->ois_mutex); |
| 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) |
| 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%pK\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_pads_init(&ois_ctrl_t->msm_sd.sd.entity, 0, NULL); |
| ois_ctrl_t->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L; |
| 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; |
| |
| switch (cmd) { |
| case VIDIOC_MSM_OIS_CFG32: |
| cmd = VIDIOC_MSM_OIS_CFG; |
| ois_data.cfgtype = u32->cfgtype; |
| |
| 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 __user *)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 = |
| kzalloc( |
| sizeof(struct msm_camera_i2c_seq_reg_array), |
| GFP_KERNEL); |
| if (!settings.reg_setting) |
| return -ENOMEM; |
| if (copy_from_user(settings.reg_setting, |
| (void __user *) |
| compat_ptr(settings32.reg_setting), |
| sizeof(struct msm_camera_i2c_seq_reg_array))) { |
| pr_err("%s:%d failed\n", __func__, __LINE__); |
| return -EFAULT; |
| } |
| |
| ois_data.cfg.settings = &settings; |
| parg = &ois_data; |
| break; |
| default: |
| parg = &ois_data; |
| break; |
| } |
| break; |
| case VIDIOC_MSM_OIS_CFG: |
| pr_err("%s: invalid cmd 0x%x received\n", __func__, cmd); |
| return -EINVAL; |
| } |
| 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) |
| return -ENOMEM; |
| |
| msm_ois_t->oboard_info = kzalloc(sizeof( |
| struct msm_ois_board_info), GFP_KERNEL); |
| if (!msm_ois_t->oboard_info) { |
| kfree(msm_ois_t); |
| 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) { |
| pr_err("failed rc %d\n", rc); |
| goto release_memory; |
| } |
| |
| 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) { |
| pr_err("failed rc %d\n", rc); |
| goto release_memory; |
| } |
| |
| 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) { |
| pr_err("failed rc %d\n", rc); |
| goto release_memory; |
| } |
| } |
| |
| 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); |
| rc = -ENOMEM; |
| goto release_memory; |
| } |
| |
| 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_pads_init(&msm_ois_t->msm_sd.sd.entity, 0, NULL); |
| msm_ois_t->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L; |
| 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; |
| release_memory: |
| kfree(msm_ois_t->oboard_info); |
| kfree(msm_ois_t); |
| 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); |
| } |
| |
| module_init(msm_ois_init_module); |
| module_exit(msm_ois_exit_module); |
| MODULE_DESCRIPTION("MSM OIS"); |
| MODULE_LICENSE("GPL v2"); |