/* 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. * */ #define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ #include #include #include "msm_flash.h" #include "msm_camera_dt_util.h" #include "msm_cci.h" #undef CDBG #define CDBG(fmt, args...) pr_debug(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_i2c_dt_match[] = { {.compatible = "qcom,camera-flash"}, {} }; static const struct i2c_device_id msm_flash_i2c_id[] = { {"qcom,camera-flash", (kernel_ulong_t)NULL}, { } }; 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_flash_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_flash_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; } else { flash_ctrl->flash_i2c_client.client->addr = flash_init_info->slave_addr; } 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; if ((flash_ctrl->power_info.power_setting_size > MAX_POWER_CONFIG) || (flash_ctrl->power_info.power_down_setting_size > MAX_POWER_CONFIG)) { pr_err("%s:%d invalid power setting size=%d size_down=%d\n", __func__, __LINE__, flash_ctrl->power_info.power_setting_size, flash_ctrl->power_info.power_down_setting_size); rc = -EINVAL; goto msm_flash_i2c_init_fail; } 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: %pK %pK\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; } 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_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); } 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; } #ifdef CONFIG_COMPAT static int32_t msm_flash_init_prepare( struct msm_flash_ctrl_t *flash_ctrl, struct msm_flash_cfg_data_t *flash_data) { return msm_flash_init(flash_ctrl, flash_data); } #else static int32_t msm_flash_init_prepare( struct msm_flash_ctrl_t *flash_ctrl, struct msm_flash_cfg_data_t *flash_data) { struct msm_flash_cfg_data_t flash_data_k; struct msm_flash_init_info_t flash_init_info; int32_t i = 0; flash_data_k.cfg_type = flash_data->cfg_type; for (i = 0; i < MAX_LED_TRIGGERS; i++) { flash_data_k.flash_current[i] = flash_data->flash_current[i]; flash_data_k.flash_duration[i] = flash_data->flash_duration[i]; } flash_data_k.cfg.flash_init_info = &flash_init_info; if (copy_from_user(&flash_init_info, (void *)(flash_data->cfg.flash_init_info), sizeof(struct msm_flash_init_info_t))) { pr_err("%s copy_from_user failed %d\n", __func__, __LINE__); return -EFAULT; } return msm_flash_init(flash_ctrl, &flash_data_k); } #endif 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]; pr_debug("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]; pr_debug("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_prepare(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; 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_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_gpio_dt_data(struct device_node *of_node, struct msm_flash_ctrl_t *fctrl) { int32_t rc = 0, i = 0; uint16_t *gpio_array = NULL; int16_t gpio_array_size = 0; struct msm_camera_gpio_conf *gconf = NULL; gpio_array_size = of_gpio_count(of_node); CDBG("%s gpio count %d\n", __func__, gpio_array_size); if (gpio_array_size > 0) { fctrl->power_info.gpio_conf = kzalloc(sizeof(struct msm_camera_gpio_conf), GFP_KERNEL); if (!fctrl->power_info.gpio_conf) { pr_err("%s failed %d\n", __func__, __LINE__); rc = -ENOMEM; return rc; } gconf = fctrl->power_info.gpio_conf; gpio_array = kzalloc(sizeof(uint16_t) * gpio_array_size, GFP_KERNEL); if (!gpio_array) { pr_err("%s failed %d\n", __func__, __LINE__); rc = -ENOMEM; goto free_gpio_conf; } for (i = 0; i < gpio_array_size; i++) { gpio_array[i] = of_get_gpio(of_node, i); if (((int16_t)gpio_array[i]) < 0) { pr_err("%s failed %d\n", __func__, __LINE__); rc = -EINVAL; goto free_gpio_array; } 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 free_gpio_array; } rc = msm_camera_get_dt_gpio_set_tbl(of_node, gconf, gpio_array, gpio_array_size); if (rc < 0) { pr_err("%s failed %d\n", __func__, __LINE__); goto free_cam_gpio_req_tbl; } 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 free_cam_gpio_set_tbl; } if (fctrl->flash_driver_type == FLASH_DRIVER_DEFAULT) fctrl->flash_driver_type = FLASH_DRIVER_GPIO; CDBG("%s:%d fctrl->flash_driver_type = %d", __func__, __LINE__, fctrl->flash_driver_type); } return 0; free_cam_gpio_set_tbl: kfree(gconf->cam_gpio_set_tbl); free_cam_gpio_req_tbl: kfree(gconf->cam_gpio_req_tbl); free_gpio_array: kfree(gpio_array); free_gpio_conf: kfree(fctrl->power_info.gpio_conf); return rc; } 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); /* 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; 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_flash_get_gpio_dt_data(of_node, fctrl); if (rc < 0) { pr_err("%s:%d msm_flash_get_gpio_dt_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 = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); struct msm_flash_cfg_data_t32 *u32 = (struct msm_flash_cfg_data_t32 *)arg; 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"); 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: 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 int msm_camera_flash_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int32_t rc = 0; struct msm_flash_ctrl_t *flash_ctrl = NULL; CDBG("Enter\n"); if (client == NULL) { pr_err("msm_flash_i2c_probe: client is 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)); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { pr_err("i2c_check_functionality failed\n"); kfree(flash_ctrl); return -EINVAL; } rc = msm_flash_get_dt_data(client->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 = &client->dev; flash_ctrl->flash_device_type = MSM_CAMERA_I2C_DEVICE; flash_ctrl->flash_mutex = &msm_flash_mutex; flash_ctrl->flash_i2c_client.i2c_func_tbl = &msm_flash_qup_func_tbl; flash_ctrl->flash_i2c_client.client = client; /* 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_flash_v4l2_subdev_fops = 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; CDBG("probe success\n"); return rc; } 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_flash_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_flash_v4l2_subdev_fops = 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_i2c_dt_match); static struct i2c_driver msm_flash_i2c_driver = { .id_table = msm_flash_i2c_id, .probe = msm_camera_flash_i2c_probe, .remove = __exit_p(msm_camera_flash_i2c_remove), .driver = { .name = "qcom,camera-flash", .owner = THIS_MODULE, .of_match_table = msm_flash_i2c_dt_match, }, }; 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) return rc; pr_err("platform probe for flash failed"); /* Perform i2c probe if platform probe fails. */ rc = i2c_add_driver(&msm_flash_i2c_driver); if (rc) pr_err("i2c probe for flash failed"); return rc; } static void __exit msm_flash_exit_module(void) { platform_driver_unregister(&msm_flash_platform_driver); i2c_del_driver(&msm_flash_i2c_driver); } static struct msm_flash_table msm_pmic_flash_table = { .flash_driver_type = FLASH_DRIVER_PMIC, .func_tbl = { .camera_flash_init = msm_flash_off, .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, }, }; module_init(msm_flash_init_module); module_exit(msm_flash_exit_module); MODULE_DESCRIPTION("MSM FLASH"); MODULE_LICENSE("GPL v2");