| /* drivers/leds/leds-tps61310.c |
| * |
| * Copyright (C) 2008-2009 HTC Corporation. |
| * |
| * This software is licensed under the terms of the GNU General Public |
| * License version 2, as published by the Free Software Foundation, and |
| * may be copied, distributed, and modified under those terms. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <linux/i2c.h> |
| #include <linux/delay.h> |
| #include <linux/platform_device.h> |
| #include <linux/workqueue.h> |
| #include <linux/leds.h> |
| #include <linux/slab.h> |
| #include <linux/gpio.h> |
| #include <linux/of_gpio.h> |
| #include <linux/module.h> |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| #include <linux/earlysuspend.h> |
| #endif |
| |
| #define FLT_DBG_LOG(fmt, ...) \ |
| printk(KERN_DEBUG "[FLT]TPS " fmt, ##__VA_ARGS__) |
| #define FLT_INFO_LOG(fmt, ...) \ |
| printk(KERN_INFO "[FLT]TPS " fmt, ##__VA_ARGS__) |
| #define FLT_ERR_LOG(fmt, ...) \ |
| printk(KERN_ERR "[FLT][ERR]TPS " fmt, ##__VA_ARGS__) |
| |
| #define FLASHLIGHT_NAME "flashlight" |
| #define TPS61310_RETRY_COUNT 10 |
| |
| enum flashlight_mode_flags { |
| FL_MODE_OFF = 0, |
| FL_MODE_TORCH, |
| FL_MODE_FLASH, |
| FL_MODE_PRE_FLASH, |
| FL_MODE_TORCH_LED_A, |
| FL_MODE_TORCH_LED_B, |
| FL_MODE_TORCH_LEVEL_1, |
| FL_MODE_TORCH_LEVEL_2, |
| FL_MODE_CAMERA_EFFECT_FLASH, |
| FL_MODE_CAMERA_EFFECT_PRE_FLASH, |
| FL_MODE_FLASH_LEVEL1, |
| FL_MODE_FLASH_LEVEL2, |
| FL_MODE_FLASH_LEVEL3, |
| FL_MODE_FLASH_LEVEL4, |
| FL_MODE_FLASH_LEVEL5, |
| FL_MODE_FLASH_LEVEL6, |
| FL_MODE_FLASH_LEVEL7, |
| FL_MODE_VIDEO_TORCH = 30, |
| FL_MODE_VIDEO_TORCH_1, |
| FL_MODE_VIDEO_TORCH_2, |
| FL_MODE_VIDEO_TORCH_3, |
| FL_MODE_VIDEO_TORCH_4, |
| }; |
| |
| struct TPS61310_flashlight_platform_data { |
| void (*gpio_init) (void); |
| uint32_t flash_duration_ms; |
| uint32_t led_count; /* 0: 1 LED, 1: 2 LED */ |
| uint32_t tps61310_strb0; |
| uint32_t tps61310_strb1; |
| uint32_t tps61310_reset; |
| uint8_t mode_pin_suspend_state_low; |
| uint32_t enable_FLT_1500mA; |
| uint32_t disable_tx_mask; |
| uint32_t power_save; |
| uint32_t power_save_2; |
| }; |
| |
| struct tps61310_data { |
| struct led_classdev fl_lcdev; |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| struct early_suspend fl_early_suspend; |
| #endif |
| enum flashlight_mode_flags mode_status; |
| uint32_t flash_sw_timeout; |
| struct mutex tps61310_data_mutex; |
| uint32_t strb0; |
| uint32_t strb1; |
| uint32_t reset; |
| uint8_t led_count; |
| uint8_t mode_pin_suspend_state_low; |
| uint8_t enable_FLT_1500mA; |
| uint8_t disable_tx_mask; |
| uint32_t power_save; |
| uint32_t power_save_2; |
| struct tps61310_led_data *led_array; |
| }; |
| |
| static struct i2c_client *this_client; |
| static struct tps61310_data *this_tps61310; |
| struct delayed_work tps61310_delayed_work; |
| static struct workqueue_struct *tps61310_work_queue; |
| static struct mutex tps61310_mutex; |
| |
| static int switch_state = 1; |
| static int retry = 0; |
| static int reg_init_fail = 0; |
| |
| static int regaddr = 0x00; |
| static int regdata = 0x00; |
| static int reg_buffered[256] = {0x00}; |
| |
| static int tps61310_i2c_command(uint8_t, uint8_t); |
| static int tps61310_flashlight_control(int); |
| static int tps61310_flashlight_mode(int); |
| |
| static ssize_t sw_timeout_show(struct device *dev, struct device_attribute *attr, |
| char *buf) |
| { |
| return sprintf(buf, "%d\n", this_tps61310->flash_sw_timeout); |
| } |
| static ssize_t sw_timeout_store( |
| struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t size) |
| { |
| int input; |
| sscanf(buf, "%d", &input); |
| FLT_INFO_LOG("%s: %d\n",__func__,input); |
| this_tps61310->flash_sw_timeout = input; |
| return size; |
| } |
| static DEVICE_ATTR(sw_timeout, S_IRUGO | S_IWUSR, sw_timeout_show, sw_timeout_store); |
| |
| static ssize_t regaddr_show(struct device *dev, struct device_attribute *attr, |
| char *buf) |
| { |
| return sprintf(buf, "0x%02x\n", regaddr); |
| } |
| static ssize_t regaddr_store( |
| struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t size) |
| { |
| int input; |
| sscanf(buf, "%x", &input); |
| FLT_INFO_LOG("%s: %d\n",__func__,input); |
| regaddr = input; |
| return size; |
| } |
| static DEVICE_ATTR(regaddr, S_IRUGO | S_IWUSR, regaddr_show, regaddr_store); |
| |
| static ssize_t regdata_show(struct device *dev, struct device_attribute *attr, |
| char *buf) |
| { |
| return sprintf(buf, "0x%02x\n", reg_buffered[regaddr]); |
| } |
| static ssize_t regdata_store( |
| struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t size) |
| { |
| int input; |
| sscanf(buf, "%x", &input); |
| FLT_INFO_LOG("%s: %d\n",__func__,input); |
| regdata = input; |
| |
| tps61310_i2c_command(regaddr, regdata); |
| |
| return size; |
| } |
| static DEVICE_ATTR(regdata, S_IRUGO | S_IWUSR, regdata_show, regdata_store); |
| |
| static ssize_t switch_show(struct device *dev, struct device_attribute *attr, |
| char *buf) |
| { |
| return sprintf(buf, "switch status:%d \n", switch_state); |
| } |
| |
| static ssize_t switch_store( |
| struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t size) |
| { |
| int switch_status; |
| switch_status = -1; |
| sscanf(buf, "%d ",&switch_status); |
| FLT_INFO_LOG("%s: %d\n",__func__,switch_status); |
| switch_state = switch_status; |
| return size; |
| } |
| |
| static DEVICE_ATTR(function_switch, S_IRUGO | S_IWUSR, switch_show, switch_store); |
| |
| static ssize_t max_current_show(struct device *dev, struct device_attribute *attr, |
| char *buf) |
| { |
| if (this_tps61310->enable_FLT_1500mA) |
| return sprintf(buf, "1500\n"); |
| else |
| return sprintf(buf, "750\n"); |
| } |
| static DEVICE_ATTR(max_current, S_IRUGO, max_current_show, NULL); |
| static ssize_t flash_store( |
| struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t size) |
| { |
| int val; |
| sscanf(buf, "%d ",&val); |
| FLT_INFO_LOG("%s: %d\n",__func__,val); |
| tps61310_flashlight_mode(val); |
| return size; |
| } |
| static DEVICE_ATTR(flash, S_IWUSR, NULL, flash_store); |
| |
| static int TPS61310_I2C_TxData(char *txData, int length) |
| { |
| uint8_t loop_i; |
| struct i2c_msg msg[] = { |
| { |
| .addr = this_client->addr, |
| .flags = 0, |
| .len = length, |
| .buf = txData, |
| }, |
| }; |
| |
| for (loop_i = 0; loop_i < TPS61310_RETRY_COUNT; loop_i++) { |
| if (i2c_transfer(this_client->adapter, msg, 1) > 0) |
| break; |
| |
| mdelay(10); |
| } |
| |
| if (loop_i >= TPS61310_RETRY_COUNT) { |
| FLT_ERR_LOG("%s retry over %d\n", __func__, |
| TPS61310_RETRY_COUNT); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int tps61310_i2c_command(uint8_t address, uint8_t data) |
| { |
| uint8_t buffer[2]; |
| int ret; |
| int err = 0; |
| |
| reg_buffered[address] = data; |
| |
| buffer[0] = address; |
| buffer[1] = data; |
| ret = TPS61310_I2C_TxData(buffer, 2); |
| if (ret < 0) { |
| FLT_ERR_LOG("%s error\n", __func__); |
| if (this_tps61310->reset) { |
| FLT_INFO_LOG("reset register"); |
| gpio_set_value_cansleep(this_tps61310->reset, 0); |
| mdelay(10); |
| gpio_set_value_cansleep(this_tps61310->reset, 1); |
| if (address!=0x07 && address!=0x04) { |
| if (this_tps61310->enable_FLT_1500mA) { |
| err |= tps61310_i2c_command(0x07, 0x46); |
| err |= tps61310_i2c_command(0x04, 0x10); |
| } else { |
| /* voltage drop monitor*/ |
| err |= tps61310_i2c_command(0x07, 0xF6); |
| } |
| if (err) |
| reg_init_fail++; |
| } else { |
| reg_init_fail++; |
| } |
| } |
| return ret; |
| } |
| return 0; |
| } |
| |
| static int flashlight_turn_off(void) |
| { |
| int status; |
| FLT_INFO_LOG("%s\n", __func__); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x02, 0x08); |
| tps61310_i2c_command(0x01, 0x00); |
| FLT_INFO_LOG("%s %d\n", __func__,this_tps61310->mode_status); |
| /* Avoid current overflow consumption while flash with 1.5A, |
| * enable/disable moden function |
| */ |
| if (this_tps61310->power_save) { |
| status = this_tps61310->mode_status; |
| if (status == 2 || (status >= 10 && status <=16)) { |
| FLT_INFO_LOG("Disable power saving\n"); |
| gpio_set_value_cansleep(this_tps61310->power_save, 0); |
| } else if (status == FL_MODE_PRE_FLASH) { |
| FLT_INFO_LOG("Enable power saving\n"); |
| gpio_set_value_cansleep(this_tps61310->power_save, 1); |
| } |
| } |
| if (this_tps61310->power_save_2) { |
| status = this_tps61310->mode_status; |
| if (status == 2 || (status >= 10 && status <=16)) { |
| FLT_INFO_LOG("Disable power saving\n"); |
| gpio_set_value_cansleep(this_tps61310->power_save_2, 0); |
| } else if (status == FL_MODE_PRE_FLASH) { |
| FLT_INFO_LOG("Enable power saving\n"); |
| gpio_set_value_cansleep(this_tps61310->power_save_2, 1); |
| } |
| } |
| this_tps61310->mode_status = FL_MODE_OFF; |
| return 0; |
| } |
| |
| void retry_flashlight_control(int err, int mode) |
| { |
| if (err && !retry) { |
| FLT_INFO_LOG("%s error once\n", __func__); |
| retry++; |
| mutex_unlock(&tps61310_mutex); |
| tps61310_flashlight_control(mode); |
| mutex_lock(&tps61310_mutex); |
| } else if(err) { |
| FLT_INFO_LOG("%s error twice\n", __func__); |
| retry = 0; |
| } |
| } |
| |
| int tps61310_flashlight_mode(int mode) |
| { |
| int err = 0; |
| uint8_t current_hex = 0x0; |
| FLT_INFO_LOG("camera flash current %d\n", mode); |
| mutex_lock(&tps61310_mutex); |
| if (this_tps61310->reset && reg_init_fail) { |
| reg_init_fail = 0; |
| if (this_tps61310->enable_FLT_1500mA) { |
| err |= tps61310_i2c_command(0x07, 0x46); |
| err |= tps61310_i2c_command(0x04, 0x10); |
| } else { |
| /* voltage drop monitor*/ |
| err |= tps61310_i2c_command(0x07, 0xF6); |
| } |
| } |
| if (err) { |
| FLT_ERR_LOG("%s error init register\n", __func__); |
| reg_init_fail = 0; |
| mutex_unlock(&tps61310_mutex); |
| return -err; |
| } |
| #if defined CONFIG_FLASHLIGHT_1500mA |
| if (mode == 0) |
| flashlight_turn_off(); |
| else if (mode > 0) { |
| FLT_INFO_LOG("flash 1.5A\n"); |
| if (mode >= 750) { |
| current_hex = (mode - 750) / 50; |
| current_hex += 0x80; |
| tps61310_i2c_command(0x05, 0x6F); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x9E); |
| tps61310_i2c_command(0x02, current_hex); |
| } else { |
| current_hex = mode / 25; |
| current_hex += 0x80; |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, current_hex); |
| } |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| } |
| #endif |
| #if !defined CONFIG_FLASHLIGHT_1500mA |
| if (mode == 0) |
| flashlight_turn_off(); |
| else if (mode > 0) { |
| current_hex = mode / 25; |
| current_hex += 0x80; |
| tps61310_i2c_command(0x05, 0x6F); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, current_hex); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| } |
| #endif |
| mutex_unlock(&tps61310_mutex); |
| return 0; |
| } |
| |
| int tps61310_flashlight_control(int mode) |
| { |
| int ret = 0; |
| int err = 0; |
| |
| mutex_lock(&tps61310_mutex); |
| if (this_tps61310->reset && reg_init_fail) { |
| reg_init_fail = 0; |
| if (this_tps61310->enable_FLT_1500mA) { |
| err |= tps61310_i2c_command(0x07, 0x46); |
| err |= tps61310_i2c_command(0x04, 0x10); |
| } else { |
| /* voltage drop monitor*/ |
| err |= tps61310_i2c_command(0x07, 0xF6); |
| } |
| } |
| if (err) { |
| FLT_ERR_LOG("%s error init register\n", __func__); |
| reg_init_fail = 0; |
| mutex_unlock(&tps61310_mutex); |
| return -err; |
| } |
| if (this_tps61310->led_count == 1) { |
| if (this_tps61310->enable_FLT_1500mA) { |
| #if defined CONFIG_FLASHLIGHT_1500mA |
| switch (mode) { |
| case FL_MODE_OFF: |
| flashlight_turn_off(); |
| break; |
| case FL_MODE_FLASH: |
| FLT_INFO_LOG("flash 1.5A\n"); |
| tps61310_i2c_command(0x05, 0x6F); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x9E); |
| tps61310_i2c_command(0x02, 0x8F); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL1: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x86); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL2: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x88); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL3: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x8C); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL4: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x90); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL5: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x94); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL6: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x98); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL7: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x9C); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_PRE_FLASH: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x04); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_VIDEO_TORCH: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x04); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_VIDEO_TORCH_1: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x01); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_VIDEO_TORCH_2: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x02); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_VIDEO_TORCH_3: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x03); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_VIDEO_TORCH_4: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x04); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_TORCH: |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| err |= tps61310_i2c_command(0x05, 0x6A); |
| err |= tps61310_i2c_command(0x00, 0x05); |
| err |= tps61310_i2c_command(0x01, 0x40); |
| if (this_tps61310->reset) |
| retry_flashlight_control(err, mode); |
| break; |
| case FL_MODE_TORCH_LEVEL_1: |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| err |= tps61310_i2c_command(0x05, 0x6A); |
| err |= tps61310_i2c_command(0x00, 0x01); |
| err |= tps61310_i2c_command(0x01, 0x40); |
| if (this_tps61310->reset) |
| retry_flashlight_control(err, mode); |
| break; |
| case FL_MODE_TORCH_LEVEL_2: |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| err |= tps61310_i2c_command(0x05, 0x6A); |
| err |= tps61310_i2c_command(0x00, 0x03); |
| err |= tps61310_i2c_command(0x01, 0x40); |
| if (this_tps61310->reset) |
| retry_flashlight_control(err, mode); |
| break; |
| default: |
| FLT_ERR_LOG("%s: unknown flash_light flags: %d\n", |
| __func__, mode); |
| ret = -EINVAL; |
| break; |
| } |
| #endif |
| } else { |
| #if !defined CONFIG_FLASHLIGHT_1500mA |
| switch (mode) { |
| case FL_MODE_OFF: |
| flashlight_turn_off(); |
| break; |
| case FL_MODE_FLASH: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x9E); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL1: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x86); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL2: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x88); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL3: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x8C); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL4: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x90); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL5: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x94); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL6: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x98); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL7: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, 0x9C); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_PRE_FLASH: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x04); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_VIDEO_TORCH: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x04); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_VIDEO_TORCH_1: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x01); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_VIDEO_TORCH_2: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x02); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_VIDEO_TORCH_3: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x03); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_VIDEO_TORCH_4: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x04); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_TORCH: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x05); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_TORCH_LEVEL_1: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x01); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_TORCH_LEVEL_2: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x03); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| default: |
| FLT_ERR_LOG("%s: unknown flash_light flags: %d\n", |
| __func__, mode); |
| ret = -EINVAL; |
| break; |
| } |
| #endif |
| } |
| } else if (this_tps61310->led_count == 2) { |
| #if defined CONFIG_TWO_FLASHLIGHT |
| switch (mode) { |
| case FL_MODE_OFF: |
| flashlight_turn_off(); |
| break; |
| case FL_MODE_FLASH: |
| tps61310_i2c_command(0x05, 0x6B); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x02, 0x90); |
| tps61310_i2c_command(0x01, 0x90); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL1: |
| tps61310_i2c_command(0x05, 0x6B); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x02, 0x83); |
| tps61310_i2c_command(0x01, 0x83); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL2: |
| tps61310_i2c_command(0x05, 0x6B); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x02, 0x84); |
| tps61310_i2c_command(0x01, 0x84); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL3: |
| tps61310_i2c_command(0x05, 0x6B); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x02, 0x86); |
| tps61310_i2c_command(0x01, 0x86); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL4: |
| tps61310_i2c_command(0x05, 0x6B); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x02, 0x88); |
| tps61310_i2c_command(0x01, 0x88); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL5: |
| tps61310_i2c_command(0x05, 0x6B); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x02, 0x8A); |
| tps61310_i2c_command(0x01, 0x8A); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL6: |
| tps61310_i2c_command(0x05, 0x6B); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x02, 0x8C); |
| tps61310_i2c_command(0x01, 0x8C); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_FLASH_LEVEL7: |
| tps61310_i2c_command(0x05, 0x6B); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x02, 0x8E); |
| tps61310_i2c_command(0x01, 0x8E); |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| break; |
| case FL_MODE_PRE_FLASH: |
| tps61310_i2c_command(0x05, 0x6B); |
| tps61310_i2c_command(0x00, 0x12); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_VIDEO_TORCH: |
| tps61310_i2c_command(0x05, 0x6B); |
| tps61310_i2c_command(0x00, 0x12); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_TORCH: |
| tps61310_i2c_command(0x05, 0x6B); |
| tps61310_i2c_command(0x00, 0x1B); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_TORCH_LEVEL_1: |
| tps61310_i2c_command(0x05, 0x6B); |
| tps61310_i2c_command(0x00, 0x09); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_TORCH_LEVEL_2: |
| tps61310_i2c_command(0x05, 0x6B); |
| tps61310_i2c_command(0x00, 0x12); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_TORCH_LED_A: |
| tps61310_i2c_command(0x05, 0x69); |
| tps61310_i2c_command(0x00, 0x09); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| case FL_MODE_TORCH_LED_B: |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, 0x09); |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| break; |
| |
| default: |
| FLT_ERR_LOG("%s: unknown flash_light flags: %d\n", |
| __func__, mode); |
| ret = -EINVAL; |
| break; |
| } |
| #endif |
| } |
| |
| FLT_INFO_LOG("%s: mode: %d\n", __func__, mode); |
| this_tps61310->mode_status = mode; |
| mutex_unlock(&tps61310_mutex); |
| |
| return ret; |
| } |
| |
| static void fl_lcdev_brightness_set(struct led_classdev *led_cdev, |
| enum led_brightness brightness) |
| { |
| enum flashlight_mode_flags mode; |
| int ret = -1; |
| |
| |
| if (brightness > 0 && brightness <= LED_HALF) { |
| if (brightness == (LED_HALF - 2)) |
| mode = FL_MODE_TORCH_LEVEL_1; |
| else if (brightness == (LED_HALF - 1)) |
| mode = FL_MODE_TORCH_LEVEL_2; |
| else if (brightness == 1 && this_tps61310->led_count ==2) |
| mode = FL_MODE_TORCH_LED_A; |
| else if (brightness == 2 && this_tps61310->led_count ==2) |
| mode = FL_MODE_TORCH_LED_B; |
| else |
| mode = FL_MODE_TORCH; |
| } else if (brightness > LED_HALF && brightness <= LED_FULL) { |
| if (brightness == (LED_HALF + 1)) |
| mode = FL_MODE_PRE_FLASH; /* pre-flash mode */ |
| else if (brightness == (LED_HALF + 3)) |
| mode = FL_MODE_FLASH_LEVEL1; /* Flashlight mode LEVEL1*/ |
| else if (brightness == (LED_HALF + 4)) |
| mode = FL_MODE_FLASH_LEVEL2; /* Flashlight mode LEVEL2*/ |
| else if (brightness == (LED_HALF + 5)) |
| mode = FL_MODE_FLASH_LEVEL3; /* Flashlight mode LEVEL3*/ |
| else if (brightness == (LED_HALF + 6)) |
| mode = FL_MODE_FLASH_LEVEL4; /* Flashlight mode LEVEL4*/ |
| else if (brightness == (LED_HALF + 7)) |
| mode = FL_MODE_FLASH_LEVEL5; /* Flashlight mode LEVEL5*/ |
| else if (brightness == (LED_HALF + 8)) |
| mode = FL_MODE_FLASH_LEVEL6; /* Flashlight mode LEVEL6*/ |
| else if (brightness == (LED_HALF + 9)) |
| mode = FL_MODE_FLASH_LEVEL7; /* Flashlight mode LEVEL7*/ |
| else |
| mode = FL_MODE_FLASH; /* Flashlight mode */ |
| } else |
| /* off and else */ |
| mode = FL_MODE_OFF; |
| |
| if ((mode != FL_MODE_OFF) && switch_state == 0){ |
| FLT_INFO_LOG("%s flashlight is disabled by switch, mode = %d\n",__func__, mode); |
| return; |
| } |
| |
| retry = 0; |
| ret = tps61310_flashlight_control(mode); |
| if (ret) { |
| FLT_ERR_LOG("%s: control failure rc:%d\n", __func__, ret); |
| return; |
| } |
| } |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| static void flashlight_early_suspend(struct early_suspend *handler) |
| { |
| FLT_INFO_LOG("%s\n", __func__); |
| if (this_tps61310->mode_status) |
| flashlight_turn_off(); |
| if (this_tps61310->power_save) |
| gpio_set_value_cansleep(this_tps61310->power_save, 0); |
| if (this_tps61310->power_save_2) |
| gpio_set_value_cansleep(this_tps61310->power_save_2, 0); |
| } |
| |
| static void flashlight_late_resume(struct early_suspend *handler) |
| { |
| } |
| #endif |
| |
| static void flashlight_turn_off_work(struct work_struct *work) |
| { |
| FLT_INFO_LOG("%s\n", __func__); |
| flashlight_turn_off(); |
| } |
| |
| static int tps61310_parse_dt(struct device *dev, struct TPS61310_flashlight_platform_data *pdata) |
| { |
| struct property *prop; |
| struct device_node *dt = dev->of_node; |
| prop = of_find_property(dt, "tps61310,tps61310_strb0", NULL); |
| if (prop) { |
| pdata->tps61310_strb0 = of_get_named_gpio(dt, "tps61310,tps61310_strb0", 0); |
| } |
| prop = of_find_property(dt, "tps61310,tps61310_strb1", NULL); |
| if (prop) { |
| pdata->tps61310_strb1 = of_get_named_gpio(dt, "tps61310,tps61310_strb1", 0); |
| } |
| prop = of_find_property(dt, "tps61310,flash_duration_ms", NULL); |
| if (prop) { |
| of_property_read_u32(dt, "tps61310,flash_duration_ms", &pdata->flash_duration_ms); |
| } |
| prop = of_find_property(dt, "tps61310,enable_FLT_1500mA", NULL); |
| if (prop) { |
| of_property_read_u32(dt, "tps61310,enable_FLT_1500mA", &pdata->enable_FLT_1500mA); |
| } |
| prop = of_find_property(dt, "tps61310,led_count", NULL); |
| if (prop) { |
| of_property_read_u32(dt, "tps61310,led_count", &pdata->led_count); |
| } |
| prop = of_find_property(dt, "tps61310,disable_tx_mask", NULL); |
| if (prop) { |
| of_property_read_u32(dt, "tps61310,disable_tx_mask", &pdata->disable_tx_mask); |
| } |
| |
| return 0; |
| |
| } |
| |
| enum led_status { |
| OFF = 0, |
| ON, |
| BLINK, |
| }; |
| |
| enum led_id { |
| LED_2 = 0, |
| LED_1_3 = 1, |
| }; |
| |
| struct tps61310_led_data { |
| u8 num_leds; |
| struct i2c_client *client_dev; |
| struct tps61310_data *tps61310; |
| int status; |
| struct led_classdev cdev; |
| int max_current; |
| int id; |
| u8 default_state; |
| int torch_mode; |
| struct mutex lock; |
| struct work_struct work; |
| }; |
| |
| static int tps61310_get_common_configs(struct tps61310_led_data *led, |
| struct device_node *node) |
| { |
| int rc; |
| const char *temp_string; |
| |
| led->cdev.default_trigger = "none"; |
| rc = of_property_read_string(node, "linux,default-trigger", |
| &temp_string); |
| if (!rc) |
| led->cdev.default_trigger = temp_string; |
| else if (rc != -EINVAL) |
| return rc; |
| |
| led->default_state = LEDS_GPIO_DEFSTATE_OFF; |
| rc = of_property_read_string(node, "default-state", |
| &temp_string); |
| if (!rc) { |
| if (!strcmp(temp_string, "keep")) |
| led->default_state = LEDS_GPIO_DEFSTATE_KEEP; |
| else if (!strcmp(temp_string, "on")) |
| led->default_state = LEDS_GPIO_DEFSTATE_ON; |
| else |
| led->default_state = LEDS_GPIO_DEFSTATE_OFF; |
| } else if (rc != -EINVAL) |
| return rc; |
| |
| return 0; |
| } |
| |
| static int tps61310_error_recover(void) |
| { |
| int err = 0; |
| if (this_tps61310->reset && reg_init_fail) { |
| reg_init_fail = 0; |
| if (this_tps61310->enable_FLT_1500mA) { |
| err |= tps61310_i2c_command(0x07, 0x46); |
| err |= tps61310_i2c_command(0x04, 0x10); |
| } else { |
| /* voltage drop monitor*/ |
| err |= tps61310_i2c_command(0x07, 0xF6); |
| } |
| } |
| if (err) { |
| FLT_ERR_LOG("%s error init register\n", __func__); |
| reg_init_fail = 0; |
| return -err; |
| } |
| |
| return err; |
| } |
| |
| static void tps61310_flash_strb(void) |
| { |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| gpio_set_value_cansleep(this_tps61310->strb0, 1); |
| queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work, |
| msecs_to_jiffies(this_tps61310->flash_sw_timeout)); |
| } |
| |
| static void tps61310_torch_strb(void) |
| { |
| gpio_set_value_cansleep(this_tps61310->strb0, 0); |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| tps61310_i2c_command(0x01, 0x40); |
| } |
| |
| static int tps61310_flash_set(struct tps61310_led_data *led, |
| enum led_brightness value) |
| { |
| int err = 0; |
| uint8_t current_hex = 0x0; |
| |
| FLT_INFO_LOG("flash set:%d\n", value); |
| |
| mutex_lock(&tps61310_mutex); |
| err = tps61310_error_recover(); |
| if ( err ) { |
| mutex_unlock(&tps61310_mutex); |
| return err; |
| } |
| |
| if ( value == 0 ) |
| flashlight_turn_off(); |
| else if ( value > 0 ) { |
| uint8_t enled = 0x68; |
| switch (led->id) |
| { |
| case LED_2: |
| current_hex = value / 25; |
| current_hex += 0x80; |
| enled |= 0x02; |
| tps61310_i2c_command(0x05, enled); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x01, current_hex); |
| printk(KERN_INFO "[FLT]" |
| "set led2 current to 0x%x.\r\n", current_hex); |
| break; |
| case LED_1_3: |
| current_hex = value / 50; |
| current_hex += 0x80; |
| enled |= 0x05; |
| tps61310_i2c_command(0x05, enled); |
| tps61310_i2c_command(0x00, 0x00); |
| tps61310_i2c_command(0x02, current_hex); |
| printk(KERN_INFO "[FLT]" |
| "set led13 current to 0x%x.\r\n", current_hex); |
| break; |
| } |
| |
| tps61310_flash_strb(); |
| } |
| |
| mutex_unlock(&tps61310_mutex); |
| return err; |
| } |
| |
| static int tps61310_torch_set(struct tps61310_led_data *led, |
| enum led_brightness value) |
| { |
| int err = 0; |
| uint8_t current_hex = 0x0; |
| |
| FLT_INFO_LOG("torch set:%d\n", value); |
| |
| mutex_lock(&tps61310_mutex); |
| err = tps61310_error_recover(); |
| if ( err ) { |
| mutex_unlock(&tps61310_mutex); |
| return err; |
| } |
| |
| if ( value == 0 ) |
| flashlight_turn_off(); |
| else if ( value > 0 ) { |
| switch (led->id) { |
| case LED_2: |
| current_hex = (value / 25) & 0x07; |
| tps61310_i2c_command(0x05, 0x6A); |
| tps61310_i2c_command(0x00, current_hex); |
| break; |
| case LED_1_3: |
| current_hex = ((value / 50) << 3) & 0x38; |
| tps61310_i2c_command(0x05, 0x6B); |
| tps61310_i2c_command(0x00, current_hex); |
| break; |
| }; |
| |
| tps61310_torch_strb(); |
| } |
| |
| mutex_unlock(&tps61310_mutex); |
| return err; |
| } |
| |
| static void __tps61310_led_work(struct tps61310_led_data *led, |
| enum led_brightness value) |
| { |
| mutex_lock(&led->lock); |
| if ( led->torch_mode ) { |
| switch (led->id) { |
| case LED_2: |
| tps61310_torch_set(led, value); |
| break; |
| case LED_1_3: |
| tps61310_torch_set(led, value); |
| break; |
| }; |
| } else { |
| switch (led->id) { |
| case LED_2: |
| tps61310_flash_set(led, value); |
| break; |
| case LED_1_3: |
| tps61310_flash_set(led, value); |
| break; |
| }; |
| } |
| mutex_unlock(&led->lock); |
| } |
| |
| static void tps61310_led_work(struct work_struct *work) |
| { |
| struct tps61310_led_data *led = container_of(work, |
| struct tps61310_led_data, work); |
| |
| __tps61310_led_work(led, led->cdev.brightness); |
| |
| return; |
| } |
| |
| static void tps61310_led_set(struct led_classdev *led_cdev, |
| enum led_brightness value) |
| { |
| struct tps61310_led_data *led; |
| |
| led = container_of(led_cdev, struct tps61310_led_data, cdev); |
| if (value < LED_OFF || value > led->cdev.max_brightness) { |
| printk(KERN_ERR "[FLT]" |
| "Invalid brightness value\n"); |
| return; |
| } |
| |
| led->cdev.brightness = value; |
| schedule_work(&led->work); |
| } |
| |
| static enum led_brightness tps61310_led_get(struct led_classdev *led_cdev) |
| { |
| struct tps61310_led_data *led; |
| |
| led = container_of(led_cdev, struct tps61310_led_data, cdev); |
| |
| return led->cdev.brightness; |
| } |
| |
| static int tps61310_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| struct tps61310_data *tps61310; |
| struct TPS61310_flashlight_platform_data *pdata; |
| int i = 0; |
| |
| struct tps61310_led_data *led, *led_array; |
| struct device_node *node, *temp; |
| int num_leds = 0, parsed_leds = 0; |
| const char *led_label; |
| int rc; |
| |
| tps61310 = NULL; |
| pdata = NULL; |
| |
| FLT_INFO_LOG("%s +\n", __func__); |
| pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); |
| if (pdata == NULL) { |
| FLT_ERR_LOG("%s: kzalloc pdata fail !!!\n", __func__); |
| rc = -ENOMEM; |
| goto fail_allocate_memory; |
| } |
| rc = tps61310_parse_dt(&client->dev, pdata); |
| |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
| FLT_ERR_LOG("%s: i2c check fail !!!\n", __func__); |
| rc = -ENODEV; |
| goto fail_allocate_memory; |
| } |
| |
| tps61310 = kzalloc(sizeof(struct tps61310_data), GFP_KERNEL); |
| if (!tps61310) { |
| FLT_ERR_LOG("%s: kzalloc data fail !!!\n", __func__); |
| rc = -ENOMEM; |
| goto fail_allocate_memory; |
| } |
| |
| i2c_set_clientdata(client, tps61310); |
| this_client = client; |
| |
| INIT_DELAYED_WORK(&tps61310_delayed_work, flashlight_turn_off_work); |
| tps61310_work_queue = create_singlethread_workqueue("tps61310_wq"); |
| if (!tps61310_work_queue) |
| goto err_create_tps61310_work_queue; |
| |
| tps61310->fl_lcdev.name = FLASHLIGHT_NAME; |
| tps61310->fl_lcdev.brightness_set = fl_lcdev_brightness_set; |
| tps61310->strb0 = pdata->tps61310_strb0; |
| tps61310->strb1 = pdata->tps61310_strb1; |
| tps61310->reset = pdata->tps61310_reset; |
| tps61310->flash_sw_timeout = pdata->flash_duration_ms; |
| tps61310->led_count = (pdata->led_count) ? pdata->led_count : 1; |
| tps61310->mode_pin_suspend_state_low = pdata->mode_pin_suspend_state_low; |
| tps61310->enable_FLT_1500mA = pdata->enable_FLT_1500mA; |
| tps61310->disable_tx_mask = pdata->disable_tx_mask; |
| tps61310->power_save = pdata->power_save; |
| tps61310->power_save_2 = pdata->power_save_2; |
| |
| if (tps61310->strb0) { |
| rc = gpio_request(tps61310->strb0, "strb0"); |
| if (rc) { |
| FLT_ERR_LOG("%s: unable to request gpio %d (%d)\n", |
| __func__, tps61310->strb0, rc); |
| goto fail_allocate_resource; |
| } |
| |
| rc = gpio_direction_output(tps61310->strb0, 0); |
| if (rc) { |
| FLT_ERR_LOG("%s: Unable to set direction (%d)\n", __func__, rc); |
| goto fail_allocate_resource; |
| } |
| } |
| if (tps61310->strb1) { |
| rc = gpio_request(tps61310->strb1, "strb1"); |
| if (rc) { |
| FLT_ERR_LOG("%s: unable to request gpio %d (%d)\n", |
| __func__, tps61310->strb1, rc); |
| goto fail_allocate_resource; |
| } |
| |
| rc = gpio_direction_output(tps61310->strb1, 1); |
| if (rc) { |
| FLT_ERR_LOG("%s: Unable to set direction (%d)\n", __func__, rc); |
| goto fail_allocate_resource; |
| } |
| } |
| if (tps61310->flash_sw_timeout <= 0) |
| tps61310->flash_sw_timeout = 600; |
| |
| node = client->dev.of_node; |
| |
| if (node == NULL) { |
| rc = -ENODEV; |
| goto fail_parsing_of_node; |
| } |
| |
| temp = NULL; |
| while ((temp = of_get_next_child(node, temp))) |
| num_leds++; |
| |
| if (!num_leds) { |
| rc = -ECHILD; |
| goto fail_parsing_of_node; |
| } |
| |
| led_array = devm_kzalloc(&client->dev, |
| (sizeof(struct tps61310_led_data) * num_leds), GFP_KERNEL); |
| if (!led_array) { |
| dev_err(&client->dev, "Unable to allocate memory\n"); |
| rc = -ENOMEM; |
| goto fail_parsing_of_node; |
| } |
| |
| tps61310->led_array = led_array; |
| for_each_child_of_node(node, temp) { |
| led = &led_array[parsed_leds]; |
| led->num_leds = num_leds; |
| led->client_dev = client; |
| led->tps61310 = tps61310; |
| led->status = OFF; |
| |
| rc = of_property_read_string(temp, "label", &led_label); |
| if (rc < 0) { |
| printk(KERN_ERR "[FLT] " |
| "Failure reading label, rc = %d\n", rc); |
| goto fail_id_check; |
| } |
| |
| rc = of_property_read_string(temp, "linux,name", &led->cdev.name); |
| if (rc < 0) { |
| printk(KERN_ERR "[FLT] " |
| "Failure reading led name, rc = %d\n", rc); |
| goto fail_id_check; |
| } |
| |
| rc = of_property_read_u32(temp, "max-current", &led->max_current); |
| if (rc < 0) { |
| printk(KERN_ERR "[FLT] " |
| "Failure reading max_current, rc = %d\n", rc); |
| goto fail_id_check; |
| } |
| |
| rc = of_property_read_u32(temp, "id", &led->id); |
| if (rc < 0) { |
| printk(KERN_ERR "[FLT] " |
| "Failure reading led id, rc = %d\n", rc); |
| goto fail_id_check; |
| } |
| |
| rc = tps61310_get_common_configs(led, temp); |
| if (rc) { |
| printk(KERN_ERR "[FLT] " |
| "Failure reading common led configuration," \ |
| " rc = %d\n", rc); |
| goto fail_id_check; |
| } |
| |
| led->cdev.brightness_set = tps61310_led_set; |
| led->cdev.brightness_get = tps61310_led_get; |
| |
| if (strncmp(led_label, "flash", sizeof("flash")) == 0) { |
| led->torch_mode = 0; |
| if (rc < 0) { |
| printk(KERN_ERR "[FLT] " |
| "Unable to read flash config data\n"); |
| goto fail_id_check; |
| } |
| } else if (strncmp(led_label, "torch", sizeof("torch")) == 0) { |
| led->torch_mode = 1; |
| if (rc < 0) { |
| printk(KERN_ERR "[FLT] " |
| "Unable to read torch config data\n"); |
| goto fail_id_check; |
| } |
| } else { |
| printk(KERN_ERR "[FLT] " |
| "No LED matching label\n"); |
| rc = -EINVAL; |
| goto fail_id_check; |
| } |
| |
| mutex_init(&led->lock); |
| INIT_WORK(&led->work, tps61310_led_work); |
| |
| led->cdev.max_brightness = led->max_current; |
| |
| rc = led_classdev_register(&client->dev, &led->cdev); |
| if (rc) { |
| printk(KERN_ERR "[FLT] " |
| "unable to register led %d,rc=%d\n", |
| led->id, rc); |
| goto fail_id_check; |
| } |
| |
| /* configure default state */ |
| switch (led->default_state) { |
| case LEDS_GPIO_DEFSTATE_OFF: |
| led->cdev.brightness = LED_OFF; |
| break; |
| case LEDS_GPIO_DEFSTATE_ON: |
| led->cdev.brightness = led->cdev.max_brightness; |
| __tps61310_led_work(led, led->cdev.brightness); |
| schedule_work(&led->work); |
| break; |
| case LEDS_GPIO_DEFSTATE_KEEP: |
| led->cdev.brightness = led->cdev.max_brightness; |
| break; |
| } |
| |
| parsed_leds++; |
| } |
| |
| mutex_init(&tps61310_mutex); |
| rc = led_classdev_register(&client->dev, &tps61310->fl_lcdev); |
| if (rc < 0) { |
| FLT_ERR_LOG("%s: failed on led_classdev_register (%d)\n", __func__, rc); |
| goto platform_data_null; |
| } |
| |
| this_tps61310 = tps61310; |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| tps61310->fl_early_suspend.suspend = flashlight_early_suspend; |
| tps61310->fl_early_suspend.resume = flashlight_late_resume; |
| register_early_suspend(&tps61310->fl_early_suspend); |
| #endif |
| |
| rc = device_create_file(tps61310->fl_lcdev.dev, &dev_attr_sw_timeout); |
| if (rc < 0) { |
| FLT_ERR_LOG("%s, create sw_timeout sysfs fail\n", __func__); |
| } |
| rc = device_create_file(tps61310->fl_lcdev.dev, &dev_attr_regaddr); |
| if (rc < 0) { |
| FLT_ERR_LOG("%s, create regaddr sysfs fail\n", __func__); |
| } |
| rc = device_create_file(tps61310->fl_lcdev.dev, &dev_attr_regdata); |
| if (rc < 0) { |
| FLT_ERR_LOG("%s, create regdata sysfs fail\n", __func__); |
| } |
| rc = device_create_file(tps61310->fl_lcdev.dev, &dev_attr_function_switch); |
| if (rc < 0) { |
| FLT_ERR_LOG("%s, create function_switch sysfs fail\n", __func__); |
| } |
| rc = device_create_file(tps61310->fl_lcdev.dev, &dev_attr_max_current); |
| if (rc < 0) { |
| FLT_ERR_LOG("%s, create max_current sysfs fail\n", __func__); |
| } |
| rc = device_create_file(tps61310->fl_lcdev.dev, &dev_attr_flash); |
| if (rc < 0) { |
| FLT_ERR_LOG("%s, create max_current sysfs fail\n", __func__); |
| } |
| /* initial register set as shutdown mode */ |
| tps61310_i2c_command(0x01, 0x00); |
| |
| if (this_tps61310->enable_FLT_1500mA) { |
| FLT_INFO_LOG("Flashlight with 1.5A\n"); |
| tps61310_i2c_command(0x07, 0x46); |
| tps61310_i2c_command(0x04, 0x10); |
| } else { |
| /* disable voltage drop monitor */ |
| tps61310_i2c_command(0x07, 0x76); |
| } |
| /* Disable Tx-mask for issue of can flash while using lower band radio */ |
| if (this_tps61310->disable_tx_mask) |
| tps61310_i2c_command(0x03, 0xC0); |
| if (this_tps61310->reset) |
| FLT_INFO_LOG("%s reset pin exist\n", __func__); |
| else |
| FLT_INFO_LOG("%s no reset pin\n", __func__); |
| if (this_tps61310->power_save) { |
| FLT_INFO_LOG("%s power save pin exist\n", __func__); |
| gpio_set_value_cansleep(this_tps61310->power_save, 0); |
| } |
| else |
| FLT_INFO_LOG("%s no power save pin\n", __func__); |
| if (this_tps61310->power_save_2) { |
| FLT_INFO_LOG("%s power save pin_2 exist\n", __func__); |
| gpio_set_value_cansleep(this_tps61310->power_save_2, 0); |
| } |
| else |
| FLT_INFO_LOG("%s no power save pin_2\n", __func__); |
| |
| FLT_INFO_LOG("%s -\n", __func__); |
| |
| if (pdata) kfree(pdata); |
| return 0; |
| |
| platform_data_null: |
| destroy_workqueue(tps61310_work_queue); |
| mutex_destroy(&tps61310_mutex); |
| fail_id_check: |
| for (i = 0; i < parsed_leds; i++) { |
| mutex_destroy(&led_array[i].lock); |
| led_classdev_unregister(&led_array[i].cdev); |
| } |
| fail_parsing_of_node: |
| if (tps61310->strb1) { |
| gpio_free(tps61310->strb1); |
| } |
| if (tps61310->strb0) { |
| gpio_free(tps61310->strb0); |
| } |
| fail_allocate_resource: |
| err_create_tps61310_work_queue: |
| fail_allocate_memory: |
| if (tps61310) kfree(tps61310); |
| |
| if (pdata) kfree(pdata); |
| return rc; |
| } |
| |
| static int tps61310_remove(struct i2c_client *client) |
| { |
| struct tps61310_data *tps61310 = i2c_get_clientdata(client); |
| struct tps61310_led_data *led_array = tps61310->led_array; |
| |
| if (led_array) { |
| int i, parsed_leds = led_array->num_leds; |
| for (i=0; i<parsed_leds; i++) { |
| cancel_work_sync(&led_array[i].work); |
| mutex_destroy(&led_array[i].lock); |
| led_classdev_unregister(&led_array[i].cdev); |
| } |
| } |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| unregister_early_suspend(&tps61310->fl_early_suspend); |
| #endif |
| |
| destroy_workqueue(tps61310_work_queue); |
| mutex_destroy(&tps61310_mutex); |
| led_classdev_unregister(&tps61310->fl_lcdev); |
| |
| if (tps61310->strb1) { |
| gpio_free(tps61310->strb1); |
| } |
| if (tps61310->strb0) { |
| gpio_free(tps61310->strb0); |
| } |
| |
| kfree(tps61310); |
| |
| FLT_INFO_LOG("%s:\n", __func__); |
| return 0; |
| } |
| |
| static const struct i2c_device_id tps61310_id[] = { |
| { "tps61310", 0 }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(i2c, tps61310_id); |
| static int tps61310_resume(struct i2c_client *client) |
| { |
| |
| FLT_INFO_LOG("%s:\n", __func__); |
| if (this_tps61310->mode_pin_suspend_state_low) |
| gpio_set_value_cansleep(this_tps61310->strb1, 1); |
| return 0; |
| } |
| static int tps61310_suspend(struct i2c_client *client, pm_message_t state) |
| { |
| |
| FLT_INFO_LOG("%s:\n", __func__); |
| if (this_tps61310->mode_pin_suspend_state_low) |
| gpio_set_value_cansleep(this_tps61310->strb1, 0); |
| |
| return 0; |
| } |
| |
| static const struct of_device_id tps61310_mttable[] = { |
| { .compatible = "ti,tps61310"}, |
| { }, |
| }; |
| |
| static struct i2c_driver tps61310_driver = { |
| .driver = { |
| .name = "tps61310", |
| .owner = THIS_MODULE, |
| .of_match_table = tps61310_mttable, |
| }, |
| .probe = tps61310_probe, |
| .remove = tps61310_remove, |
| .suspend = tps61310_suspend, |
| .resume = tps61310_resume, |
| .id_table = tps61310_id, |
| }; |
| module_i2c_driver(tps61310_driver); |
| |
| MODULE_DESCRIPTION("TPS61310 Led Flash driver"); |
| MODULE_LICENSE("GPL"); |