blob: 4ae4b59fadb173832c42d97bbffdfad6f1bcc8b1 [file] [log] [blame]
/* 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");