| /* |
| * leds-sm5713-fled.c - SM5713 Flash-LEDs device driver |
| * |
| * Copyright (C) 2017 Samsung Electronics Co.Ltd |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| |
| #include <linux/slab.h> |
| #include <linux/delay.h> |
| #include <linux/of.h> |
| #include <linux/of_gpio.h> |
| #include <linux/mfd/sm5713.h> |
| #include <linux/mfd/sm5713-private.h> |
| #include <linux/kthread.h> |
| #include <linux/slab.h> |
| #include <linux/module.h> |
| #include <linux/moduleparam.h> |
| #include <linux/platform_device.h> |
| |
| static struct sm5713_fled_data *g_sm5713_fled; |
| extern struct class *camera_class; /*sys/class/camera*/ |
| |
| /* for MUIC HV-VBUS control */ |
| extern int muic_request_disable_afc_state(void); |
| extern void sm5713_request_default_power_src(void); |
| extern int muic_check_fled_state(bool enable, u8 mode); /* mode:1 = FLED_MODE_TORCH, mode:2 = FLED_MODE_FLASH */ |
| extern int sm5713_usbpd_check_fled_state(bool enable, u8 mode); |
| |
| static void fled_set_mode(struct sm5713_fled_data *fled, u8 fled_index, u8 mode) |
| { |
| switch (fled_index) { |
| case SM5713_FLED_INDEX_1: |
| case SM5713_FLED_INDEX_2: |
| sm5713_update_reg(fled->i2c, SM5713_CHG_REG_FLED1CNTL1 + (fled_index * 2), (mode << 0), (0x3 << 0)); |
| break; |
| case SM5713_FLED_INDEX_3: |
| sm5713_update_reg(fled->i2c, SM5713_CHG_REG_FLED3CNTL, (mode << 4), (0x1 << 4)); |
| break; |
| } |
| } |
| |
| static void fled_set_fled_current(struct sm5713_fled_data *fled, u8 fled_index, u8 offset) |
| { |
| switch (fled_index) { |
| case SM5713_FLED_INDEX_1: |
| case SM5713_FLED_INDEX_2: |
| sm5713_update_reg(fled->i2c, SM5713_CHG_REG_FLED1CNTL2 + (fled_index * 2), (offset << 0), (0xf << 0)); |
| break; |
| case SM5713_FLED_INDEX_3: |
| dev_err(fled->dev, "%s: can't used fled2\n", __func__); |
| break; |
| } |
| } |
| |
| static void fled_set_mled_current(struct sm5713_fled_data *fled, u8 fled_index, u8 offset) |
| { |
| switch (fled_index) { |
| case SM5713_FLED_INDEX_1: |
| case SM5713_FLED_INDEX_2: |
| sm5713_update_reg(fled->i2c, SM5713_CHG_REG_FLED1CNTL2 + (fled_index * 2), (offset << 4), (0x7 << 4)); |
| break; |
| case SM5713_FLED_INDEX_3: |
| sm5713_update_reg(fled->i2c, SM5713_CHG_REG_FLED3CNTL, (offset << 0), (0xf << 0)); |
| break; |
| } |
| } |
| |
| static int sm5713_fled_check_vbus(struct sm5713_fled_data *fled) |
| { |
| fled->vbus_voltage = sm5713_charger_oper_get_vbus_voltage(); |
| if (fled->vbus_voltage > 5500) { |
| muic_request_disable_afc_state(); |
| sm5713_request_default_power_src(); |
| } |
| return 0; |
| } |
| |
| static int sm5713_fled_control(u8 fled_index, u8 fled_mode) |
| { |
| struct sm5713_fled_data *fled = g_sm5713_fled; |
| int ret = 0; |
| |
| if (g_sm5713_fled == NULL) { |
| pr_err("sm5713-fled: %s: not probe fled yet\n", __func__); |
| return -ENXIO; |
| } |
| |
| if (fled_mode == FLED_MODE_FLASH) { |
| ret = gpio_request(fled->pdata->led[fled_index].fen_pin, "sm5713_fled"); |
| if (ret < 0) { |
| dev_err(fled->dev, "%s: failed request fen-gpio(%d)", __func__, ret); |
| |
| fled_set_mode(fled, fled_index, fled_mode); |
| fled->pdata->led[fled_index].used_gpio_ctrl = false; |
| } else { |
| gpio_direction_output(fled->pdata->led[fled_index].fen_pin, 0); |
| msleep(10); //it need to delay change to boosting mode in sm5713(about 7ms) |
| fled_set_mode(fled, fled_index, FLED_MODE_EXTERNAL); |
| gpio_set_value(fled->pdata->led[fled_index].fen_pin, 1); |
| gpio_set_value(fled->pdata->led[fled_index].men_pin, 0); |
| fled->pdata->led[fled_index].used_gpio_ctrl = true; |
| } |
| pr_info("sm5713-fled: %s: Flash mode & used gpio = %d.\n", __func__, fled->pdata->led[fled_index].used_gpio_ctrl); |
| } else if (fled_mode == FLED_MODE_TORCH) { |
| ret = gpio_request(fled->pdata->led[fled_index].men_pin, "sm5713_fled"); |
| if (ret < 0) { |
| dev_err(fled->dev, "%s: failed request men-gpio(%d)", __func__, ret); |
| fled_set_mode(fled, fled_index, fled_mode); |
| fled->pdata->led[fled_index].used_gpio_ctrl = false; |
| } else { |
| gpio_direction_output(fled->pdata->led[fled_index].men_pin, 0); |
| |
| fled_set_mode(fled, fled_index, FLED_MODE_EXTERNAL); |
| gpio_set_value(fled->pdata->led[fled_index].men_pin, 1); |
| gpio_set_value(fled->pdata->led[fled_index].fen_pin, 0); |
| fled->pdata->led[fled_index].used_gpio_ctrl = true; |
| } |
| pr_info("sm5713-fled: %s: Torch mode & used gpio = %d.\n", __func__, fled->pdata->led[fled_index].used_gpio_ctrl); |
| } else if (fled_mode == FLED_MODE_OFF) { |
| pr_info("sm5713-fled: %s: off mode & used gpio = %d.\n", __func__, fled->pdata->led[fled_index].used_gpio_ctrl); |
| |
| fled_set_mode(fled, fled_index, fled_mode); |
| |
| if (fled->pdata->led[fled_index].used_gpio_ctrl == true) { |
| gpio_set_value(fled->pdata->led[fled_index].fen_pin, 0); |
| gpio_set_value(fled->pdata->led[fled_index].men_pin, 0); |
| gpio_free(fled->pdata->led[fled_index].men_pin); |
| gpio_free(fled->pdata->led[fled_index].fen_pin); |
| fled->pdata->led[fled_index].used_gpio_ctrl = false; |
| } |
| |
| if (fled->pdata->led[fled_index].en_mled == true) { |
| fled->torch_on_cnt--; |
| if (fled->torch_on_cnt == 0) { |
| sm5713_charger_oper_push_event(SM5713_CHARGER_OP_EVENT_TORCH, 0); |
| if (fled->flash_prepare_cnt == 0) { |
| muic_check_fled_state(0, FLED_MODE_TORCH); |
| sm5713_usbpd_check_fled_state(0, FLED_MODE_TORCH); |
| } |
| } |
| fled->pdata->led[fled_index].en_mled = false; |
| } |
| |
| if (fled->pdata->led[fled_index].en_fled == true) { |
| fled->flash_on_cnt--; |
| if (fled->flash_on_cnt == 0) { |
| sm5713_charger_oper_push_event(SM5713_CHARGER_OP_EVENT_FLASH, 0); |
| } |
| fled->pdata->led[fled_index].en_fled = false; |
| } |
| } else { |
| pr_err("sm5713-fled: %s: fen_pin : %d, men_pin : %d, FLED_MODE = %d\n", __func__, fled->pdata->led[fled_index].fen_pin, fled->pdata->led[fled_index].men_pin, fled_mode); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int sm5713_fled_torch_on(u8 fled_index, u8 brightness) |
| { |
| struct sm5713_fled_data *fled = g_sm5713_fled; |
| |
| pr_info("sm5713-fled: %s: start.\n", __func__); |
| |
| if (g_sm5713_fled == NULL) { |
| pr_err("sm5713-fled: %s: not probe fled yet\n", __func__); |
| return -ENXIO; |
| } |
| |
| mutex_lock(&fled->fled_mutex); |
| |
| if (brightness) { |
| fled_set_mled_current(fled, fled_index, brightness); |
| } else { |
| fled_set_mled_current(fled, fled_index, fled->pdata->led[fled_index].torch_brightness); |
| } |
| |
| if (fled->pdata->led[fled_index].en_mled == false) { |
| if (fled->torch_on_cnt == 0) { |
| sm5713_charger_oper_push_event(SM5713_CHARGER_OP_EVENT_TORCH, 1); |
| } |
| fled->pdata->led[fled_index].en_mled = true; |
| fled->torch_on_cnt++; |
| } |
| |
| sm5713_fled_control(fled_index, FLED_MODE_TORCH); |
| |
| mutex_unlock(&fled->fled_mutex); |
| |
| pr_info("sm5713-fled: %s: done.\n", __func__); |
| |
| return 0; |
| } |
| |
| static int sm5713_fled_flash_on(u8 fled_index, u8 brightness) |
| { |
| struct sm5713_fled_data *fled = g_sm5713_fled; |
| |
| pr_info("sm5713-fled: %s: start.\n", __func__); |
| |
| if (g_sm5713_fled == NULL) { |
| pr_err("sm5713-fled: %s: not probe fled yet\n", __func__); |
| return -ENXIO; |
| } |
| |
| mutex_lock(&fled->fled_mutex); |
| |
| if (brightness) { |
| fled_set_fled_current(fled, fled_index, brightness); |
| } else { |
| fled_set_fled_current(fled, fled_index, fled->pdata->led[fled_index].flash_brightness); |
| } |
| |
| if (fled->pdata->led[fled_index].en_fled == false) { |
| if (fled->flash_on_cnt == 0) { |
| sm5713_charger_oper_push_event(SM5713_CHARGER_OP_EVENT_FLASH, 1); |
| } |
| fled->pdata->led[fled_index].en_fled = true; |
| fled->flash_on_cnt++; |
| } |
| sm5713_fled_control(fled_index, FLED_MODE_FLASH); |
| |
| mutex_unlock(&fled->fled_mutex); |
| |
| pr_info("sm5713-fled: %s: done.\n", __func__); |
| |
| return 0; |
| } |
| |
| static int sm5713_fled_prepare_flash(u8 fled_index) |
| { |
| struct sm5713_fled_data *fled = g_sm5713_fled; |
| |
| pr_info("sm5713-fled: %s: start.\n", __func__); |
| |
| if (fled == NULL) { |
| pr_err("sm5713-fled: %s: not probe fled yet\n", __func__); |
| return -ENXIO; |
| } |
| |
| if (fled_index > SM5713_FLED_INDEX_2 || fled->pdata->led[fled_index].flash_brightness == 0) { |
| pr_err("sm5713-fled: %s: don't support flash mode (fled_index=%d)\n", __func__, fled_index); |
| return -EPERM; |
| } |
| |
| if (fled->pdata->led[fled_index].pre_fled == true) { |
| pr_info("sm5713-fled: %s: already prepared\n", __func__); |
| return 0; |
| } |
| |
| mutex_lock(&fled->fled_mutex); |
| |
| if (fled->flash_prepare_cnt == 0) { |
| sm5713_fled_check_vbus(fled); |
| muic_check_fled_state(1, FLED_MODE_FLASH); |
| sm5713_usbpd_check_fled_state(1, FLED_MODE_FLASH); |
| } |
| fled_set_fled_current(fled, fled_index, fled->pdata->led[fled_index].flash_brightness); |
| fled_set_mled_current(fled, fled_index, fled->pdata->led[fled_index].torch_brightness); |
| |
| fled->flash_prepare_cnt++; |
| fled->pdata->led[fled_index].pre_fled = true; |
| |
| mutex_unlock(&fled->fled_mutex); |
| |
| pr_info("sm5713-fled: %s: done.\n", __func__); |
| |
| return 0; |
| } |
| |
| static int sm5713_fled_close_flash(u8 fled_index) |
| { |
| struct sm5713_fled_data *fled = g_sm5713_fled; |
| |
| pr_info("sm5713-fled: %s: start.\n", __func__); |
| |
| if (fled == NULL) { |
| pr_err("sm5713-fled: %s: not probe fled yet\n", __func__); |
| return -ENXIO; |
| } |
| |
| if (fled_index > SM5713_FLED_INDEX_2 || fled->pdata->led[fled_index].flash_brightness == 0) { |
| pr_err("sm5713-fled: %s: don't support flash mode (fled_index=%d)\n", __func__, fled_index); |
| return -EPERM; |
| } |
| |
| if (fled->pdata->led[fled_index].pre_fled == false) { |
| pr_info("sm5713-fled: %s: already closed\n", __func__); |
| return 0; |
| } |
| |
| mutex_lock(&fled->fled_mutex); |
| |
| fled_set_mode(fled, fled_index, FLED_MODE_OFF); |
| fled->flash_prepare_cnt--; |
| |
| if (fled->flash_prepare_cnt == 0) { |
| sm5713_charger_oper_push_event(SM5713_CHARGER_OP_EVENT_TORCH, 0); |
| sm5713_charger_oper_push_event(SM5713_CHARGER_OP_EVENT_FLASH, 0); |
| muic_check_fled_state(0, FLED_MODE_FLASH); |
| sm5713_usbpd_check_fled_state(0, FLED_MODE_FLASH); |
| } |
| fled->pdata->led[fled_index].pre_fled = false; |
| |
| mutex_unlock(&fled->fled_mutex); |
| |
| pr_info("sm5713-fled: %s: done.\n", __func__); |
| |
| return 0; |
| } |
| |
| /** |
| * For export Camera flash control support |
| * |
| * Caution - MUST be called "sm5713_fled_prepare_flash" before |
| * using to FLED. also if finished using FLED, MUST be called |
| * "sm5713_fled_close_flash". |
| */ |
| |
| int sm5713_fled_prepare_flash_thread_func(void* fled_index) |
| { |
| int ret = 0; |
| u8 fled_index_value = *(u8*)fled_index; |
| |
| ret = sm5713_fled_prepare_flash(fled_index_value); |
| if(ret < 0){ |
| pr_err("PREPARE_FLASH failed"); |
| return ret; |
| } |
| kfree(fled_index); |
| |
| return ret; |
| } |
| |
| int sm5713_fled_run_prepare_flash_thread(u8 fled_index) |
| { |
| struct task_struct* prepare_flash_task; |
| u8* index_data = (u8*)kzalloc(sizeof(u8),GFP_KERNEL); |
| |
| *index_data = fled_index; |
| prepare_flash_task = kthread_run(sm5713_fled_prepare_flash_thread_func, index_data, "prepare_flash"); |
| |
| return 0; |
| } |
| |
| int32_t sm5713_fled_mode_ctrl(u8 fled_index, int state) |
| { |
| struct sm5713_fled_data *fled = g_sm5713_fled; |
| int ret = 0; |
| |
| pr_info("sm5713-fled: %s: sm5713_fled_mode_ctrl start\n", __func__); |
| |
| if (g_sm5713_fled == NULL) |
| { |
| pr_err("sm5713_fled: %s: g_sm5713_fled is not initialized.\n", __func__); |
| return -EFAULT; |
| } |
| |
| switch (state) { |
| |
| case SM5713_FLED_MODE_OFF: |
| /* FlashLight Mode OFF */ |
| ret = sm5713_fled_control(fled_index, FLED_MODE_OFF); |
| if (ret < 0) |
| pr_err("sm5713-fled: %s: SM5713_FLED_MODE_OFF(%d) failed\n", __func__, state); |
| else |
| pr_info("sm5713-fled: %s: SM5713_FLED_MODE_OFF(%d) done\n", __func__, state); |
| break; |
| |
| case SM5713_FLED_MODE_MAIN_FLASH: |
| /* FlashLight Mode Flash */ |
| ret = sm5713_fled_flash_on(fled_index, fled->pdata->led[fled_index].flash_brightness); |
| if (ret < 0) |
| pr_err("sm5713-fled: %s: SM5713_FLED_MODE_MAIN_FLASH(%d) failed\n", __func__, state); |
| else |
| pr_info("sm5713-fled: %s: SM5713_FLED_MODE_MAIN_FLASH(%d) done\n", __func__, state); |
| break; |
| |
| case SM5713_FLED_MODE_TORCH_FLASH: /* TORCH FLASH */ |
| /* FlashLight Mode TORCH */ |
| ret = sm5713_fled_torch_on(fled_index, fled->pdata->led[fled_index].torch_brightness); |
| if (ret < 0) |
| pr_err("sm5713-fled: %s: SM5713_FLED_MODE_TORCH_FLASH(%d) failed\n", __func__, state); |
| else |
| pr_info("sm5713-fled: %s: SM5713_FLED_MODE_TORCH_FLASH(%d) done\n", __func__, state); |
| break; |
| |
| case SM5713_FLED_MODE_PRE_FLASH: /* TORCH FLASH */ |
| /* FlashLight Mode TORCH */ |
| ret = sm5713_fled_torch_on(fled_index, fled->pdata->led[fled_index].preflash_brightness); |
| if (ret < 0) |
| pr_err("sm5713-fled: %s: SM5713_FLED_MODE_PRE_FLASH(%d) failed\n", __func__, state); |
| else |
| pr_info("sm5713-fled: %s: SM5713_FLED_MODE_PRE_FLASH(%d) done\n", __func__, state); |
| break; |
| |
| case SM5713_FLED_MODE_PREPARE_FLASH: |
| /* 9V -> 5V VBUS change */ |
| ret = sm5713_fled_run_prepare_flash_thread(fled_index); |
| if (ret < 0) |
| pr_err("sm5713-fled: %s: SM5713_FLED_MODE_PREPARE_FLASH(%d) thread failed to start\n", __func__, state); |
| else |
| pr_info("sm5713-fled: %s: SM5713_FLED_MODE_PREPARE_FLASH(%d) thread started \n", __func__, state); |
| break; |
| |
| case SM5713_FLED_MODE_CLOSE_FLASH: |
| /* 5V -> 9V VBUS change */ |
| ret = sm5713_fled_close_flash(fled_index); |
| if (ret < 0) |
| pr_err("sm5713-fled: %s: SM5713_FLED_MODE_CLOSE_FLASH(%d) failed\n", __func__, state); |
| else |
| pr_info("sm5713-fled: %s: SM5713_FLED_MODE_CLOSE_FLASH(%d) done\n", __func__, state); |
| break; |
| |
| default: |
| /* FlashLight Mode OFF */ |
| ret = sm5713_fled_control(fled_index, FLED_MODE_OFF); |
| if (ret < 0) |
| pr_err("sm5713-fled: %s: FLED_MODE_OFF(%d) failed\n", __func__, state); |
| else |
| pr_info("sm5713-fled: %s: FLED_MODE_OFF(%d) done\n", __func__, state); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| EXPORT_SYMBOL_GPL(sm5713_fled_mode_ctrl); |
| |
| /** |
| * For camera_class device file control (Torch-LED) |
| */ |
| |
| int get_brightness_index(int store_value) |
| { |
| if (store_value <= 1001) { |
| return 0; |
| } else if (store_value <= 1002) { |
| return 1; |
| } else if (store_value <= 1004) { |
| return 2; |
| } else if (store_value <= 1006) { |
| return 3; |
| } else if (store_value <= 1009) { |
| return 4; |
| } else { |
| return 0; |
| } |
| } |
| |
| #if defined (CONFIG_LEDS_SM5713_DUAL_LED) |
| static int get_fled_index(sm5713_fled_platform_data *pdata, struct device_attribute *attr, int store_value, int *fled_index_array) |
| { |
| int fled_index_num = 0; |
| int i = 0; |
| if (strcmp(attr->attr.name, "rear_flash") == 0 || strcmp(attr->attr.name, "rear_torch_flash") == 0) { |
| if (store_value <= 1001) { |
| fled_index_num = pdata->torch_index_cnt[0]; |
| for (i = 0; i < fled_index_num; i++) { |
| fled_index_array[i] = pdata->torch_index[0][i]; |
| } |
| } else if (store_value <= 1002) { |
| fled_index_num = pdata->torch_index_cnt[1]; |
| for (i = 0; i < fled_index_num; i++) { |
| fled_index_array[i] = pdata->torch_index[1][i]; |
| } |
| } else if (store_value <= 1004) { |
| fled_index_num = pdata->torch_index_cnt[2]; |
| for (i = 0; i < fled_index_num; i++) { |
| fled_index_array[i] = pdata->torch_index[2][i]; |
| } |
| } else if (store_value <= 1006) { |
| fled_index_num = pdata->torch_index_cnt[3]; |
| for (i = 0; i < fled_index_num; i++) { |
| fled_index_array[i] = pdata->torch_index[3][i]; |
| } |
| } else if (store_value <= 1009) { |
| fled_index_num = pdata->torch_index_cnt[4]; |
| for (i = 0; i < fled_index_num; i++) { |
| fled_index_array[i] = pdata->torch_index[4][i]; |
| } |
| } else { |
| fled_index_num = 1; |
| fled_index_array[0] = SM5713_FLED_INDEX_1; |
| } |
| |
| } else { |
| pr_err("sm5713-fled: flash index not matched (attr=%s) \n", attr->attr.name); |
| } |
| pr_err("[leds-sm5713-fled] store_value [%d] selected fled_index is [%d]",store_value, fled_index_num); |
| return fled_index_num; |
| } |
| |
| static ssize_t sm5713_rear_flash_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
| { |
| int fled_index = SM5713_FLED_INDEX_3; |
| int fled_index_cnt; |
| int fled_index_array[SM5713_FLED_MAX_NUM]; |
| u32 store_value; |
| int led_iterator; |
| int ret; |
| struct sm5713_fled_data *fled = g_sm5713_fled; |
| |
| if (g_sm5713_fled == NULL) { |
| pr_err("sm5713-fled: %s: g_sm5713_fled NULL \n", __func__); |
| return -ENODEV; |
| } |
| |
| if (IS_ERR_VALUE((unsigned long)fled_index) || (buf == NULL) || kstrtouint(buf, 10, &store_value)) { |
| return -ENXIO; |
| } |
| |
| fled_index_cnt = get_fled_index(fled->pdata, attr, store_value, fled_index_array); |
| |
| dev_info(fled->dev, "%s: value=%d\n", __func__, store_value); |
| |
| mutex_lock(&fled->fled_mutex); |
| |
| for (led_iterator = 0; led_iterator < fled_index_cnt; led_iterator++) { |
| fled_index = fled_index_array[led_iterator]; |
| fled->pdata->led[fled_index].sysfs_input_data = store_value; |
| |
| if (store_value == 0) { |
| if (fled->pdata->led[fled_index].en_mled == false |
| && fled->pdata->led[fled_index].en_fled == false) { |
| goto out_skip; |
| } |
| sm5713_fled_control(fled_index, FLED_MODE_OFF); |
| } else { |
| if (store_value == 200) { /* 200 : Flash ON */ |
| fled_set_fled_current(fled, fled_index, 0x0); /* Set fled = 600mA */ |
| |
| if (fled->pdata->led[fled_index].en_fled == false) { |
| if (fled->flash_on_cnt == 0) { |
| sm5713_charger_oper_push_event( |
| SM5713_CHARGER_OP_EVENT_FLASH, 1); |
| } |
| fled->pdata->led[fled_index].en_fled = true; |
| fled->flash_on_cnt++; |
| } |
| sm5713_fled_control(fled_index, FLED_MODE_FLASH); |
| } else { /* 1, 100, 1001~1010, : Torch ON */ |
| /* Main Torch on */ |
| if (store_value == 1) { |
| fled_set_mled_current(fled, fled_index, 0x1); /* Set mled = 75mA(0x1) */ |
| /* Factory Torch on */ |
| } else if (store_value == 100) { |
| fled_set_mled_current(fled, fled_index, 0x7); /* Set mled=225mA */ |
| } else if (store_value >= 1001 && store_value <= 1010) { |
| /* Torch on (Normal) */ |
| if (fled->pdata->led[fled_index].apply_torch_brightness_control) { |
| int brigthness_index; |
| brigthness_index = get_brightness_index(store_value); |
| fled_set_mled_current(fled, fled_index, |
| fled->pdata->led[fled_index].torch_brightness_levels[brigthness_index]); |
| } else { |
| if (store_value - 1001 > 7) |
| fled_set_mled_current(fled, fled_index, 0x07); /* Max 225mA(0x7) */ |
| else |
| fled_set_mled_current(fled, fled_index, |
| (store_value - 1001)); /* 50mA(0x0) ~ 225mA(0x7) at 25mA step */ |
| } |
| } else { |
| dev_err(fled->dev, "%s: failed store cmd\n", __func__); |
| ret = -EINVAL; |
| goto out_p; |
| } |
| dev_info(fled->dev, "%s: en_mled=%d, torch_on_cnt = %d \n", |
| __func__, fled->pdata->led[fled_index].en_mled, |
| fled->torch_on_cnt); |
| |
| if (fled->pdata->led[fled_index].en_mled == true) { |
| goto out_skip; |
| } |
| |
| if (fled->torch_on_cnt == 0) { |
| sm5713_fled_check_vbus(fled); |
| muic_check_fled_state(1, FLED_MODE_TORCH); |
| sm5713_usbpd_check_fled_state(1, FLED_MODE_TORCH); |
| sm5713_charger_oper_push_event( |
| SM5713_CHARGER_OP_EVENT_TORCH, 1); |
| } |
| sm5713_fled_control(fled_index, FLED_MODE_TORCH); |
| |
| fled->pdata->led[fled_index].en_mled = true; |
| fled->torch_on_cnt++; |
| } |
| } |
| } |
| |
| out_skip: |
| ret = count; |
| |
| out_p: |
| mutex_unlock(&fled->fled_mutex); |
| |
| return count; |
| } |
| |
| static ssize_t sm5713_rear_flash_show(struct device *dev, struct device_attribute *attr, char *buf) |
| { |
| struct sm5713_fled_data *fled = g_sm5713_fled; |
| |
| if (g_sm5713_fled == NULL) { |
| pr_err("sm5713-fled: %s: g_sm5713_fled is NULL\n", __func__); |
| return -ENODEV; |
| } |
| |
| return sprintf(buf, "LED1[%d], LED2[%d], LED3[%d]\n", |
| fled->pdata->led[SM5713_FLED_INDEX_1].sysfs_input_data, |
| fled->pdata->led[SM5713_FLED_INDEX_2].sysfs_input_data, |
| fled->pdata->led[SM5713_FLED_INDEX_3].sysfs_input_data); |
| } |
| |
| static ssize_t sm5713_rear_flash2_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) { |
| int fled_index = SM5713_FLED_INDEX_1; |
| u32 store_value; |
| int ret; |
| struct sm5713_fled_data *fled = g_sm5713_fled; |
| |
| if (g_sm5713_fled == NULL) { |
| pr_err("sm5713-fled: %s: g_sm5713_fled NULL \n", __func__); |
| return -ENODEV; |
| } |
| |
| if (IS_ERR_VALUE((unsigned long) fled_index) || (buf == NULL) |
| || kstrtouint(buf, 10, &store_value)) { |
| return -ENXIO; |
| } |
| |
| fled->pdata->led[fled_index].sysfs_input_data = store_value; |
| |
| dev_info(fled->dev, "%s: value=%d\n", __func__, store_value); |
| |
| mutex_lock(&fled->fled_mutex); |
| |
| if (store_value == 0) { /* 0: Torch OFF */ |
| if (fled->pdata->led[fled_index].en_mled == false |
| && fled->pdata->led[fled_index].en_fled == false) { |
| goto out_skip; |
| } |
| sm5713_fled_control(fled_index, FLED_MODE_OFF); |
| } else if (store_value == 200) { /* 200 : Flash ON */ |
| fled_set_fled_current(fled, fled_index, 0x0); /* Set fled = 600mA */ |
| |
| if (fled->pdata->led[fled_index].en_fled == false) { |
| if (fled->flash_on_cnt == 0) { |
| sm5713_charger_oper_push_event(SM5713_CHARGER_OP_EVENT_FLASH, |
| 1); |
| } |
| fled->pdata->led[fled_index].en_fled = true; |
| fled->flash_on_cnt++; |
| } |
| sm5713_fled_control(fled_index, FLED_MODE_FLASH); |
| } else { /* 1, 100, 1001~1010, : Torch ON */ |
| /* Main Torch on */ |
| if (store_value == 1) { |
| fled_set_mled_current(fled, fled_index, 0x1); /* Set mled = 75mA(0x1) */ |
| /* Factory Torch on */ |
| } else if (store_value == 100) { |
| fled_set_mled_current(fled, fled_index, 0x7); /* Set mled=225mA */ |
| } else if (store_value >= 1001 && store_value <= 1010) { |
| /* Torch on (Normal) */ |
| if (fled->pdata->led[fled_index].apply_torch_brightness_control) { |
| int brigthness_index; |
| brigthness_index = get_brightness_index(store_value); |
| fled_set_mled_current(fled, fled_index, |
| fled->pdata->led[fled_index].torch_brightness_levels[brigthness_index]); |
| } else { |
| if (store_value - 1001 > 7) |
| fled_set_mled_current(fled, fled_index, 0x07); /* Max 225mA(0x7) */ |
| else |
| fled_set_mled_current(fled, fled_index, |
| (store_value - 1001)); /* 50mA(0x0) ~ 225mA(0x7) at 25mA step */ |
| } |
| } else { |
| dev_err(fled->dev, "%s: failed store cmd\n", __func__); |
| ret = -EINVAL; |
| goto out_p; |
| } |
| dev_info(fled->dev, "%s: en_mled=%d, torch_on_cnt = %d \n", __func__, |
| fled->pdata->led[fled_index].en_mled, fled->torch_on_cnt); |
| |
| if (fled->pdata->led[fled_index].en_mled == true) { |
| goto out_skip; |
| } |
| |
| if (fled->torch_on_cnt == 0) { |
| sm5713_fled_check_vbus(fled); |
| muic_check_fled_state(1, FLED_MODE_TORCH); |
| sm5713_usbpd_check_fled_state(1, FLED_MODE_TORCH); |
| sm5713_charger_oper_push_event(SM5713_CHARGER_OP_EVENT_TORCH, 1); |
| } |
| sm5713_fled_control(fled_index, FLED_MODE_TORCH); |
| |
| fled->pdata->led[fled_index].en_mled = true; |
| fled->torch_on_cnt++; |
| } |
| |
| out_skip: |
| ret = count; |
| |
| out_p: |
| mutex_unlock(&fled->fled_mutex); |
| |
| return count; |
| } |
| |
| static ssize_t sm5713_rear_flash2_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int fled_index = SM5713_FLED_INDEX_1; |
| struct sm5713_fled_data *fled = g_sm5713_fled; |
| |
| if (g_sm5713_fled == NULL) { |
| pr_err("sm5713-fled: %s: g_sm5713_fled is NULL\n", __func__); |
| return -ENODEV; |
| } |
| |
| if (IS_ERR_VALUE((unsigned long) fled_index)) { |
| return -ENOENT; |
| } |
| |
| return sprintf(buf, "%d\n", fled->pdata->led[fled_index].sysfs_input_data); |
| } |
| |
| static DEVICE_ATTR(rear_flash2, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH, sm5713_rear_flash2_show, sm5713_rear_flash2_store); |
| #else /* #if defined (CONFIG_LEDS_SM5713_DUAL_LED) */ |
| static int get_fled_index(struct device_attribute *attr) |
| { |
| int fled_index = -EINVAL; |
| |
| if (strcmp(attr->attr.name, "rear_flash") == 0 || strcmp(attr->attr.name, "rear_torch_flash") == 0) { |
| fled_index = SM5713_FLED_INDEX_1; |
| } else { |
| pr_err("sm5713-fled: flash index not matched (attr=%s) \n", attr->attr.name); |
| } |
| |
| return fled_index; |
| } |
| |
| static ssize_t sm5713_rear_flash_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
| { |
| int fled_index = get_fled_index(attr); |
| u32 store_value; |
| int ret; |
| struct sm5713_fled_data *fled = g_sm5713_fled; |
| |
| if (g_sm5713_fled == NULL) { |
| pr_err("sm5713-fled: %s: g_sm5713_fled NULL \n", __func__); |
| return -ENODEV; |
| } |
| |
| if (IS_ERR_VALUE((unsigned long)fled_index) || (buf == NULL) || kstrtouint(buf, 10, &store_value)) { |
| return -ENXIO; |
| } |
| |
| fled->pdata->led[fled_index].sysfs_input_data = store_value; |
| |
| dev_info(fled->dev, "%s: value=%d\n", __func__, store_value); |
| |
| mutex_lock(&fled->fled_mutex); |
| |
| if (store_value == 0) { /* 0: Torch OFF */ |
| if (fled->pdata->led[fled_index].en_mled == false && fled->pdata->led[fled_index].en_fled == false) { |
| goto out_skip; |
| } |
| sm5713_fled_control(fled_index, FLED_MODE_OFF); |
| } else if (store_value == 200) { /* 200 : Flash ON */ |
| fled_set_fled_current(fled, fled_index, 0x0); /* Set fled = 600mA */ |
| |
| if (fled->pdata->led[fled_index].en_fled == false) { |
| if (fled->flash_on_cnt == 0) { |
| sm5713_charger_oper_push_event(SM5713_CHARGER_OP_EVENT_FLASH, 1); |
| } |
| fled->pdata->led[fled_index].en_fled = true; |
| fled->flash_on_cnt++; |
| } |
| sm5713_fled_control(fled_index, FLED_MODE_FLASH); |
| } else { /* 1, 100, 1001~1010, : Torch ON */ |
| /* Main Torch on */ |
| if (store_value == 1) { |
| fled_set_mled_current(fled, fled_index, 0x1); /* Set mled = 75mA(0x1) */ |
| /* Factory Torch on */ |
| } else if (store_value == 100) { |
| fled_set_mled_current(fled, fled_index, 0x7); /* Set mled=225mA */ |
| } else if (store_value >= 1001 && store_value <= 1010) { |
| /* Torch on (Normal) */ |
| if (fled->pdata->led[fled_index].apply_torch_brightness_control) { |
| int brigthness_index; |
| brigthness_index = get_brightness_index(store_value); |
| fled_set_mled_current(fled, fled_index, fled->pdata->led[fled_index].torch_brightness_levels[brigthness_index]); |
| } else { |
| if (store_value-1001 > 7) |
| fled_set_mled_current(fled, fled_index, 0x07); /* Max 225mA(0x7) */ |
| else |
| fled_set_mled_current(fled, fled_index, (store_value-1001)); /* 50mA(0x0) ~ 225mA(0x7) at 25mA step */ |
| } |
| } else { |
| dev_err(fled->dev, "%s: failed store cmd\n", __func__); |
| ret = -EINVAL; |
| goto out_p; |
| } |
| dev_info(fled->dev, "%s: en_mled=%d, torch_on_cnt = %d \n", __func__, fled->pdata->led[fled_index].en_mled, fled->torch_on_cnt); |
| |
| if (fled->pdata->led[fled_index].en_mled == true) { |
| goto out_skip; |
| } |
| |
| if (fled->torch_on_cnt == 0) { |
| sm5713_fled_check_vbus(fled); |
| muic_check_fled_state(1, FLED_MODE_TORCH); |
| sm5713_usbpd_check_fled_state(1, FLED_MODE_TORCH); |
| sm5713_charger_oper_push_event(SM5713_CHARGER_OP_EVENT_TORCH, 1); |
| } |
| sm5713_fled_control(fled_index, FLED_MODE_TORCH); |
| |
| fled->pdata->led[fled_index].en_mled = true; |
| fled->torch_on_cnt++; |
| } |
| |
| out_skip: |
| ret = count; |
| |
| out_p: |
| mutex_unlock(&fled->fled_mutex); |
| |
| return count; |
| } |
| |
| static ssize_t sm5713_rear_flash_show(struct device *dev, struct device_attribute *attr, char *buf) |
| { |
| int fled_index = get_fled_index(attr); |
| struct sm5713_fled_data *fled = g_sm5713_fled; |
| |
| if (g_sm5713_fled == NULL) { |
| pr_err("sm5713-fled: %s: g_sm5713_fled is NULL\n", __func__); |
| return -ENODEV; |
| } |
| |
| if (IS_ERR_VALUE((unsigned long)fled_index)) { |
| return -ENOENT; |
| } |
| |
| return sprintf(buf, "%d\n", fled->pdata->led[fled_index].sysfs_input_data); |
| } |
| |
| #endif /* #if defined (CONFIG_LEDS_SM5713_DUAL_LED) */ |
| |
| |
| static DEVICE_ATTR(rear_flash, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH, sm5713_rear_flash_show, sm5713_rear_flash_store); |
| |
| static int sm5713_fled_parse_dt(struct device *dev, sm5713_fled_platform_data *pdata) |
| { |
| struct device_node *np, *c_np; |
| int fled_index, ret = 0; |
| u32 temp; |
| |
| np = of_find_node_by_name(NULL, "sm5713-fled"); |
| if (!np) { |
| dev_err(dev, "%s: can't find sm5713-fled np\n", __func__); |
| return -EINVAL; |
| } |
| |
| #if defined (CONFIG_LEDS_SM5713_DUAL_LED) |
| ret = of_property_read_u32_array(np, "torch-led-cnt", |
| pdata->torch_index_cnt, MAX_TORCH_LEVELS_5713); |
| |
| for (temp = 0; temp < MAX_TORCH_LEVELS_5713; temp++) { |
| char name[20]; |
| u32 index; |
| sprintf(name, "torch-led-index-%d", temp); |
| dev_info(dev, "num [%d].name [%s]\n", pdata->torch_index_cnt[temp], name); |
| ret = of_property_read_u32_array(np, name, |
| pdata->torch_index[temp], pdata->torch_index_cnt[temp]); |
| for (index = 0; index < pdata->torch_index_cnt[temp]; index++) { |
| dev_info(dev, "value [%d].\t", pdata->torch_index[temp][index]); |
| } |
| } |
| #endif /* #if defined (CONFIG_LEDS_SM5713_DUAL_LED) */ |
| for_each_child_of_node(np, c_np) { |
| ret = of_property_read_u32(c_np, "id", &fled_index); |
| if (ret) { |
| pr_err("sm5713-fled: %s: fail to get a id\n", __func__); |
| return ret; |
| } |
| |
| of_property_read_u32(c_np, "flash-brightness", &temp); |
| pdata->led[fled_index].flash_brightness = (temp & 0xff); |
| of_property_read_u32(c_np, "preflash-brightness", &temp); |
| pdata->led[fled_index].preflash_brightness = (temp & 0xff); |
| of_property_read_u32(c_np, "torch-brightness", &temp); |
| pdata->led[fled_index].torch_brightness = (temp & 0xff); |
| |
| ret = of_property_read_u32_array(c_np, "torch-brightness-levels", |
| pdata->led[fled_index].torch_brightness_levels, |
| MAX_TORCH_LEVELS_5713); |
| if (ret) { |
| pdata->led[fled_index].apply_torch_brightness_control = false; |
| } else { |
| pdata->led[fled_index].apply_torch_brightness_control = true; |
| } |
| |
| of_property_read_u32(c_np, "timeout", &temp); |
| pdata->led[fled_index].timeout = (temp & 0xff); |
| |
| pdata->led[fled_index].fen_pin = of_get_named_gpio(c_np, "flash-en-gpio", 0); |
| pdata->led[fled_index].men_pin = of_get_named_gpio(c_np, "torch-en-gpio", 0); |
| |
| dev_info(dev, "%s: f_cur=0x%x, pre_cur=0x%x, t_cur=0x%x, tout=%d, gpio=%d:%d\n", |
| __func__, pdata->led[fled_index].flash_brightness, pdata->led[fled_index].preflash_brightness, |
| pdata->led[fled_index].torch_brightness, pdata->led[fled_index].timeout, |
| pdata->led[fled_index].fen_pin, pdata->led[fled_index].men_pin); |
| } |
| |
| dev_info(dev, "%s: parse dt done.\n", __func__); |
| return 0; |
| } |
| |
| static void sm5713_fled_init(struct sm5713_fled_data *fled) |
| { |
| int i; |
| |
| for (i = 0; i < SM5713_FLED_MAX_NUM; ++i) { |
| |
| fled_set_mode(fled, i, FLED_MODE_OFF); |
| fled->pdata->led[i].en_mled = 0; |
| fled->pdata->led[i].en_fled = 0; |
| fled->pdata->led[i].pre_fled = 0; |
| fled->pdata->led[i].used_gpio_ctrl = 0; |
| } |
| |
| fled->torch_on_cnt = 0; |
| fled->flash_on_cnt = 0; |
| fled->flash_prepare_cnt = 0; |
| |
| mutex_init(&fled->fled_mutex); |
| } |
| |
| static int sm5713_fled_probe(struct platform_device *pdev) |
| { |
| struct sm5713_dev *sm5713 = dev_get_drvdata(pdev->dev.parent); |
| struct sm5713_fled_data *fled; |
| int ret = 0; |
| |
| dev_info(&pdev->dev, "sm5713 fled probe start (rev=%d)\n", sm5713->pmic_rev); |
| |
| fled = devm_kzalloc(&pdev->dev, sizeof(struct sm5713_fled_data), GFP_KERNEL); |
| if (unlikely(!fled)) { |
| dev_err(&pdev->dev, "%s: fail to alloc_devm\n", __func__); |
| return -ENOMEM; |
| } |
| fled->dev = &pdev->dev; |
| fled->i2c = sm5713->charger; |
| |
| fled->pdata = devm_kzalloc(&pdev->dev, sizeof(sm5713_fled_platform_data), GFP_KERNEL); |
| if (unlikely(!fled->pdata)) { |
| dev_err(fled->dev, "%s: fail to alloc_pdata\n", __func__); |
| ret = -ENOMEM; |
| goto free_dev; |
| } |
| ret = sm5713_fled_parse_dt(fled->dev, fled->pdata); |
| if (ret < 0) { |
| goto free_pdata; |
| } |
| |
| sm5713_fled_init(fled); |
| g_sm5713_fled = fled; |
| |
| if (IS_ERR_OR_NULL(camera_class)) { |
| dev_err(fled->dev, "%s: can't find camera_class sysfs object, didn't used rear_flash attribute\n", __func__); |
| goto free_pdata; |
| } |
| |
| fled->rear_fled_dev = device_create(camera_class, NULL, 0, NULL, "flash"); |
| if (IS_ERR(fled->rear_fled_dev)) { |
| dev_err(fled->dev, "%s failed create device for rear_flash\n", __func__); |
| goto free_pdata; |
| } |
| fled->rear_fled_dev->parent = fled->dev; |
| |
| ret = device_create_file(fled->rear_fled_dev, &dev_attr_rear_flash); |
| if (IS_ERR_VALUE((unsigned long)ret)) { |
| dev_err(fled->dev, "%s failed create device file for rear_flash\n", __func__); |
| goto free_device; |
| } |
| |
| #if defined (CONFIG_LEDS_SM5713_DUAL_LED) |
| ret = device_create_file(fled->rear_fled_dev, &dev_attr_rear_flash2); |
| if (IS_ERR_VALUE((unsigned long) ret)) { |
| dev_err(fled->dev, "%s failed create device file for rear_flash2\n", |
| __func__); |
| goto free_device; |
| } |
| #endif |
| |
| dev_info(&pdev->dev, "sm5713 fled probe done.\n"); |
| |
| return 0; |
| |
| free_device: |
| device_destroy(camera_class, fled->rear_fled_dev->devt); |
| free_pdata: |
| devm_kfree(&pdev->dev, fled->pdata); |
| free_dev: |
| devm_kfree(&pdev->dev, fled); |
| |
| return ret; |
| } |
| |
| static int sm5713_fled_remove(struct platform_device *pdev) |
| { |
| struct sm5713_fled_data *fled = platform_get_drvdata(pdev); |
| int i = 0; |
| |
| #if defined (CONFIG_LEDS_SM5713_DUAL_LED) |
| device_remove_file(fled->rear_fled_dev, &dev_attr_rear_flash2); |
| #endif |
| device_remove_file(fled->rear_fled_dev, &dev_attr_rear_flash); |
| |
| device_destroy(camera_class, fled->rear_fled_dev->devt); |
| |
| for (i = 0; i < SM5713_FLED_MAX_NUM ; i++) |
| fled_set_mode(fled, i, FLED_MODE_OFF); |
| |
| platform_set_drvdata(pdev, NULL); |
| |
| devm_kfree(&pdev->dev, fled->pdata); |
| devm_kfree(&pdev->dev, fled); |
| |
| return 0; |
| } |
| |
| static struct of_device_id sm5713_fled_match_table[] = { |
| { .compatible = "siliconmitus,sm5713-fled",}, |
| {}, |
| }; |
| |
| static const struct platform_device_id sm5713_fled_id[] = { |
| {"sm5713-fled", 0}, |
| {}, |
| }; |
| |
| static struct platform_driver sm5713_led_driver = { |
| .driver = { |
| .name = "sm5713-fled", |
| .owner = THIS_MODULE, |
| .of_match_table = sm5713_fled_match_table, |
| }, |
| .probe = sm5713_fled_probe, |
| .remove = sm5713_fled_remove, |
| .id_table = sm5713_fled_id, |
| }; |
| |
| static int __init sm5713_led_driver_init(void) |
| { |
| return platform_driver_register(&sm5713_led_driver); |
| } |
| module_init(sm5713_led_driver_init); |
| |
| static void __exit sm5713_led_driver_exit(void) |
| { |
| platform_driver_unregister(&sm5713_led_driver); |
| } |
| module_exit(sm5713_led_driver_exit); |
| |
| |
| MODULE_DESCRIPTION("SM5713 LED driver"); |
| MODULE_AUTHOR("Samsung Electronics"); |
| MODULE_LICENSE("GPL"); |