android_kernel_samsung_msm8976/drivers/leds/sm5705_fled.c

1039 lines
31 KiB
C

/*
* Flash-LED device driver for SM5705
*
* Copyright (C) 2015 Silicon Mitus
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/workqueue.h>
#include <linux/leds-sm5705.h>
#include <linux/mfd/sm5705/sm5705.h>
#include <linux/muic/muic_afc.h>
#include <linux/battery/charger/sm5705_charger_oper.h>
#if defined(CONFIG_MUIC_UNIVERSAL_SM5705_AFC)
#define DISABLE_AFC
extern int32_t afc_checked_for_camera;
#endif
enum {
SM5705_FLED_OFF_MODE = 0x0,
SM5705_FLED_ON_MOVIE_MODE = 0x1,
SM5705_FLED_ON_FLASH_MODE = 0x2,
SM5705_FLED_ON_EXTERNAL_CONTROL_MODE = 0x3,
};
struct sm5705_fled_info {
struct device *dev;
struct i2c_client *i2c;
struct sm5705_fled_platform_data *pdata;
struct device *rear_fled_dev;
/* for Flash VBUS check */
struct workqueue_struct *wqueue;
struct delayed_work fled0_vbus_check_work;
struct delayed_work fled1_vbus_check_work;
};
extern struct class *camera_class; /*sys/class/camera*/
bool assistive_light = false;
#ifdef DISABLE_AFC
static DEFINE_MUTEX(lock);
#else
static DEFINE_SPINLOCK(fled_lock);
#endif
static struct sm5705_fled_info *g_sm5705_fled;
static inline int __get_revision_number(void)
{
return 2;
}
/**
* SM5705 Flash-LEDs device register control functions
*/
static int sm5705_FLEDx_mode_enable(struct sm5705_fled_info *sm5705_fled, int index, unsigned char FLEDxEN)
{
int ret;
ret = sm5705_update_reg(sm5705_fled->i2c, SM5705_REG_FLED1CNTL1 + (index * 4), (FLEDxEN & 0x3), 0x3);
if (IS_ERR_VALUE(ret)) {
dev_err(sm5705_fled->dev, "%s: fail to update REG:FLED%dEN (value=%d)\n", __func__, index, FLEDxEN);
return ret;
}
dev_info(sm5705_fled->dev, "%s: FLED[%d] set mode = %d\n", __func__, index, FLEDxEN);
return 0;
}
#if 0
static inline unsigned char _calc_oneshot_time_offset_to_ms(unsigned short ms)
{
if (ms < 100) {
return 0;
} else {
return (((ms - 100) / 100) & 0xF);
}
}
static int sm5705_FLEDx_oneshot_config(struct sm5705_fled_info *sm5705_fled, int index, bool enable, unsigned short timer_ms)
{
int ret;
unsigned char reg_val;
reg_val = (((!enable) & 0x1) << 4) | (_calc_oneshot_time_offset_to_ms(timer_ms) & 0xF);
ret = sm5705_write_reg(sm5705_fled->i2c, SM5705_REG_FLED1CNTL2 + (index * 4), reg_val);
if (IS_ERR_VALUE(ret)) {
dev_err(sm5705_fled->dev, "%s: fail to write REG:FLED%dCNTL2 (value=%d)\n", __func__, index, reg_val);
return ret;
}
return 0;
}
#endif
static inline unsigned char _calc_flash_current_offset_to_mA(int index, unsigned short current_mA)
{
if (index)//FLED1
return current_mA < 400 ? (((current_mA - 25) / 25) & 0x1F) : ((((current_mA - 400) / 25) + 0xF) & 0x1F);
else//FLED0
return current_mA < 700 ? (((current_mA - 300) / 25) & 0x1F) : ((((current_mA - 700) / 50) + 0xF) & 0x1F);
}
static int sm5705_FLEDx_set_flash_current(struct sm5705_fled_info *sm5705_fled, int index, unsigned short current_mA)
{
int ret;
unsigned char reg_val;
reg_val = _calc_flash_current_offset_to_mA(index, current_mA);
ret = sm5705_write_reg(sm5705_fled->i2c, SM5705_REG_FLED1CNTL3 + (index * 4), reg_val);
if (IS_ERR_VALUE(ret)) {
dev_err(sm5705_fled->dev, "%s: fail to write REG:FLED%dCNTL3 (value=%d)\n", __func__, index, reg_val);
return ret;
}
return 0;
}
static inline unsigned char _calc_torch_current_offset_to_mA(unsigned short current_mA)
{
return (((current_mA - 10) / 10) & 0x1F);
}
static inline unsigned short _calc_torch_current_mA_to_offset(unsigned char offset)
{
return (((offset & 0x1F) + 1) * 10);
}
static int sm5705_FLEDx_set_torch_current(struct sm5705_fled_info *sm5705_fled, int index, unsigned short current_mA)
{
int ret;
unsigned char reg_val;
reg_val = _calc_torch_current_offset_to_mA(current_mA);
ret = sm5705_write_reg(sm5705_fled->i2c, SM5705_REG_FLED1CNTL4 + (index * 4), reg_val);
if (IS_ERR_VALUE(ret)) {
dev_err(sm5705_fled->dev, "%s: fail to write REG:FLED%dCNTL4 (value=%d)\n", __func__, index, reg_val);
return ret;
}
return 0;
}
//#define MUIC_DEF
/**
* SM5705 Flash-LED to MUIC interface functions
*/
static inline int sm5705_fled_muic_flash_work_on(struct sm5705_fled_info *sm5705_fled)
{
#ifdef MUIC_DEF
/* MUIC 9V -> 5V function */
muic_check_afc_state(1);
//sm5705_muic_DP_RESET();
#endif
return 0;
}
static inline int sm5705_fled_muic_flash_work_off(struct sm5705_fled_info *sm5705_fled)
{
#ifdef MUIC_DEF
/* MUIC 5V -> 9V function */
muic_check_afc_state(0);
//sm5705_muic_AFC_restart();
#endif
return 0;
}
static inline bool sm5705_fled_check_valid_vbus_from_MUIC(void)
{
#ifdef MUIC_DEF
if (muic_check_afc_state(1) == 1) {
return true;
}
return false;
#else
return true;
#endif
}
/**
* SM5705 Flash-LED operation control functions
*/
static int sm5705_fled_initialize(struct sm5705_fled_info *sm5705_fled)
{
struct device *dev = sm5705_fled->dev;
struct sm5705_fled_platform_data *pdata = sm5705_fled->pdata;
int i, ret;
for (i=0; i < SM5705_FLED_MAX; ++i) {
if (pdata->led[i].used_gpio) {
ret = gpio_request(pdata->led[i].flash_en_pin, "sm5705_fled");
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s: fail to request flash gpio pin = %d (ret=%d)\n", __func__, pdata->led[i].flash_en_pin, ret);
return ret;
}
gpio_direction_output(pdata->led[i].flash_en_pin, 0);
ret = gpio_request(pdata->led[i].torch_en_pin, "sm5705_fled");
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s: fail to request torch gpio pin = %d (ret=%d)\n", __func__, pdata->led[i].torch_en_pin, ret);
return ret;
}
gpio_direction_output(pdata->led[i].torch_en_pin, 0);
dev_info(dev, "SM5705 FLED[%d] used External GPIO control Mode (Flash pin=%d, Torch pin=%d)\n",
i, pdata->led[i].flash_en_pin, pdata->led[i].torch_en_pin);
} else {
dev_info(dev, "SM5705 FLED[%d] used I2C control Mode\n", i);
}
ret = sm5705_FLEDx_mode_enable(sm5705_fled, i, SM5705_FLED_OFF_MODE);
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s: fail to set FLED[%d] external control mode\n", __func__, i);
return ret;
}
}
return 0;
}
static void sm5705_fled_deinitialize(struct sm5705_fled_info *sm5705_fled)
{
struct device *dev = sm5705_fled->dev;
struct sm5705_fled_platform_data *pdata = sm5705_fled->pdata;
int i;
for (i=0; i < SM5705_FLED_MAX; ++i) {
if (pdata->led[i].used_gpio) {
gpio_free(pdata->led[i].flash_en_pin);
gpio_free(pdata->led[i].torch_en_pin);
}
sm5705_FLEDx_mode_enable(sm5705_fled, i, SM5705_FLED_OFF_MODE);
}
dev_info(dev, "%s: FLEDs de-initialize done.\n", __func__);
}
static inline int _fled_turn_on_torch(struct sm5705_fled_info *sm5705_fled, int index)
{
struct sm5705_fled_platform_data *pdata = sm5705_fled->pdata;
struct device *dev = sm5705_fled->dev;
int ret;
if (pdata->led[index].used_gpio) {
ret = sm5705_FLEDx_mode_enable(sm5705_fled, index, SM5705_FLED_ON_EXTERNAL_CONTROL_MODE);
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s: fail to set FLED[%d] External control mode\n", __func__, index);
return ret;
}
gpio_set_value(pdata->led[index].flash_en_pin, 0);
gpio_set_value(pdata->led[index].torch_en_pin, 1);
} else {
ret = sm5705_FLEDx_mode_enable(sm5705_fled, index, SM5705_FLED_ON_MOVIE_MODE);
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s: fail to set FLED[%d] Movie mode\n", __func__, index);
return ret;
}
}
sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_TORCH, 1);
dev_info(dev, "%s: FLED[%d] Torch turn-on done.\n", __func__, index);
return 0;
}
static int sm5705_fled_turn_on_torch(struct sm5705_fled_info *sm5705_fled, int index, unsigned short current_mA)
{
struct device *dev = sm5705_fled->dev;
int ret;
sm5705_fled_muic_flash_work_on(sm5705_fled);
ret = sm5705_FLEDx_set_torch_current(sm5705_fled, index, current_mA);
if (IS_ERR_VALUE(ret)) {
sm5705_fled_muic_flash_work_off(sm5705_fled);
dev_err(dev, "%s: fail to set FLED[%d] torch current (current_mA=%d)\n", __func__, index, current_mA);
return ret;
}
if (sm5705_fled_check_valid_vbus_from_MUIC() == true) {
_fled_turn_on_torch(sm5705_fled, index);
} else {
if (index == SM5705_FLED_0) {
queue_delayed_work(sm5705_fled->wqueue, &sm5705_fled->fled0_vbus_check_work, msecs_to_jiffies(10));
} else {
queue_delayed_work(sm5705_fled->wqueue, &sm5705_fled->fled1_vbus_check_work, msecs_to_jiffies(10));
}
}
return 0;
}
static int sm5705_fled_turn_on_flash(struct sm5705_fled_info *sm5705_fled, int index, unsigned short current_mA)
{
struct device *dev = sm5705_fled->dev;
struct sm5705_fled_platform_data *pdata = sm5705_fled->pdata;
int ret;
ret = sm5705_FLEDx_set_flash_current(sm5705_fled, index, current_mA);
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s: fail to set FLED[%d] flash current (current_mA=%d)\n", __func__, index, current_mA);
return ret;
}
/*
Charger event need to push first before gpio enable.
In flash capture mode, when connect some types charger or USB, from pre-flash -> main-flash ,
torch on first and charger has been change as torch mode, if enable flash first, it will work
on torch mode which use flash current , Vbus will pull down and charger disconnect.
*/
sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_FLASH, 1);
if (pdata->led[index].used_gpio) {
ret = sm5705_FLEDx_mode_enable(sm5705_fled, index, SM5705_FLED_ON_EXTERNAL_CONTROL_MODE);
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s: fail to set FLED[%d] External control mode\n", __func__, index);
return ret;
}
gpio_set_value(pdata->led[index].torch_en_pin, 0);
gpio_set_value(pdata->led[index].flash_en_pin, 1);
} else {
ret = sm5705_FLEDx_mode_enable(sm5705_fled, index, SM5705_FLED_ON_FLASH_MODE);
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s: fail to set FLED[%d] Flash mode\n", __func__, index);
return ret;
}
}
dev_info(dev, "%s: FLED[%d] Flash turn-on done.\n", __func__, index);
return 0;
}
static int sm5705_fled_turn_off(struct sm5705_fled_info *sm5705_fled, int index)
{
struct device *dev = sm5705_fled->dev;
struct sm5705_fled_platform_data *pdata = sm5705_fled->pdata;
int ret;
if (pdata->led[index].used_gpio) {
gpio_set_value(pdata->led[index].flash_en_pin, 0);
gpio_set_value(pdata->led[index].torch_en_pin, 0);
}
ret = sm5705_FLEDx_mode_enable(sm5705_fled, index, SM5705_FLED_OFF_MODE);
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s: fail to set FLED[%d] OFF mode\n", __func__, index);
return ret;
}
sm5705_fled_muic_flash_work_off(sm5705_fled);
ret = sm5705_FLEDx_set_flash_current(sm5705_fled, index, 0);
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s: fail to set FLED[%d] flash current\n", __func__, index);
return ret;
}
ret = sm5705_FLEDx_set_torch_current(sm5705_fled, index, 0);
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s: fail to set FLED[%d] torch current\n", __func__, index);
return ret;
}
sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_FLASH, 0);
sm5705_charger_oper_push_event(SM5705_CHARGER_OP_EVENT_TORCH, 0);
dev_info(dev, "%s: FLED[%d] turn-off done.\n", __func__, index);
return 0;
}
/**
* For Export Flash control functions (external GPIO control)
*/
static bool fimc_is_activated = 0;
int sm5705_fled_prepare_flash(unsigned char index)
{
if (fimc_is_activated == 1) {
/* skip to overlapping function calls */
return 0;
}
dev_info(g_sm5705_fled->dev, "%s: check - GPIO used, set - Torch/Flash current\n", __func__);
if (g_sm5705_fled == NULL) {
pr_err("sm5705-fled: %s: invalid g_sm5705_fled, maybe not registed fled device driver\n", __func__);
return -ENXIO;
}
if (g_sm5705_fled->pdata->led[index].used_gpio == 0) {
pr_err("sm5705-fled: %s: can't used external GPIO control, check device tree\n", __func__);
return -ENOENT;
}
sm5705_fled_muic_flash_work_on(g_sm5705_fled);
sm5705_FLEDx_set_torch_current(g_sm5705_fled, index, g_sm5705_fled->pdata->led[index].torch_current_mA);
sm5705_FLEDx_set_flash_current(g_sm5705_fled, index, g_sm5705_fled->pdata->led[index].flash_current_mA);
fimc_is_activated = 1;
return 0;
}
EXPORT_SYMBOL(sm5705_fled_prepare_flash);
#ifdef DISABLE_AFC
static void sm5705_fled_muic_flash_on_prepare(void)
{
muic_check_afc_state(1);
fimc_is_activated = 1;
}
static void sm5705_fled_muic_flash_off_prepare(void)
{
muic_check_afc_state(0);
fimc_is_activated = 0;
}
#endif
int sm5705_fled_torch_on(unsigned char index)
{
if (assistive_light == false) {
#ifdef DISABLE_AFC
if(fimc_is_activated == 0 && afc_checked_for_camera == 0){
/* used only Torch case - Ex> flashlight : Need to 9V -> 5V */
sm5705_fled_muic_flash_on_prepare();
}
#endif
dev_info(g_sm5705_fled->dev, "%s: Torch - ON\n", __func__);
sm5705_fled_turn_on_torch(g_sm5705_fled, index,g_sm5705_fled->pdata->led[index].torch_current_mA);
}
return 0;
}
EXPORT_SYMBOL(sm5705_fled_torch_on);
int sm5705_fled_flash_on(unsigned char index)
{
if (assistive_light == false) {
/* CAUTION: SM5705 Flash-LED can't driving 9V_VBUS, MUST be "VBUS < 6V" */
if (sm5705_fled_check_valid_vbus_from_MUIC() == false) {
dev_err(g_sm5705_fled->dev, "%s: Can't used Flash_dev, now VBUS=9V\n", __func__);
return -EBUSY;
}
dev_info(g_sm5705_fled->dev, "%s: Flash - ON\n", __func__);
sm5705_fled_turn_on_flash(g_sm5705_fled, index, g_sm5705_fled->pdata->led[index].flash_current_mA);
}
return 0;
}
EXPORT_SYMBOL(sm5705_fled_flash_on);
#if defined(CONFIG_DUAL_LEDS_FLASH)
int sm5705_fled_pre_flash_on(unsigned char index, int32_t pre_flash_current_mA)
{
if (assistive_light == false) {
dev_info(g_sm5705_fled->dev, "%s: Preflash - ON\n", __func__);
sm5705_fled_turn_on_torch(g_sm5705_fled, index, (unsigned short)pre_flash_current_mA);
}
return 0;
}
EXPORT_SYMBOL(sm5705_fled_pre_flash_on);
int sm5705_fled_flash_on_set_current(unsigned char index, int32_t flash_current_mA)
{
if (assistive_light == false) {
dev_info(g_sm5705_fled->dev, "%s: Flash setted by user- ON\n", __func__);
sm5705_fled_turn_on_flash(g_sm5705_fled, index, (unsigned short)flash_current_mA);
}
return 0;
}
EXPORT_SYMBOL(sm5705_fled_flash_on_set_current);
#endif
int sm5705_fled_led_off(unsigned char index)
{
/* zero2lte - test temp code */
if(g_sm5705_fled == NULL){
printk("%s:invalid g_sm5705_fled, maybe not registed fled device driver\n",__func__);
return 0;
}
if (assistive_light == false) {
#ifdef DISABLE_AFC
if (fimc_is_activated == 1 && afc_checked_for_camera == 0) {
/* used only Torch case - Ex> flashlight : Need to 5V -> 9V */
sm5705_fled_muic_flash_off_prepare();
}
#endif
dev_info(g_sm5705_fled->dev, "%s: LED - OFF\n", __func__);
sm5705_fled_turn_off(g_sm5705_fled, index);
}
return 0;
}
EXPORT_SYMBOL(sm5705_fled_led_off);
int sm5705_fled_close_flash(unsigned char index)
{
if (fimc_is_activated == 0) {
/* skip to overlapping function calls */
return 0;
}
dev_info(g_sm5705_fled->dev, "%s: Close Process\n", __func__);
if (g_sm5705_fled == NULL) {
pr_err("sm5705-fled: %s: invalid g_sm5705_fled, maybe not registed fled device driver\n", __func__);
return -ENXIO;
}
sm5705_fled_muic_flash_work_off(g_sm5705_fled);
fimc_is_activated = 0;
return 0;
}
EXPORT_SYMBOL(sm5705_fled_close_flash);
/**
* For Camera-class Rear Flash device file support functions
*/
#define REAR_FLASH_INDEX SM5705_FLED_0
static ssize_t sm5705_rear_flash_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct sm5705_fled_info *sm5705_fled = dev_get_drvdata(dev->parent);
#ifdef DISABLE_AFC
int retry;
#else
unsigned long flags;
#endif
int ret, value_u32;
#if defined(CONFIG_DUAL_LEDS_FLASH)
int index;
if(strcmp(attr->attr.name,"rear_flash") == 0){
pr_err("flash index is 0 \n");
index = 0;
}
else if(strcmp(attr->attr.name,"rear_flash_2") == 0){
pr_err("flash index is 1 \n");
index = 1;
}
else{
pr_err("flash index is not match \n");
}
#endif
if ((buf == NULL) || kstrtouint(buf, 10, &value_u32)) {
return -1;
}
/** temp error canceling code */
if (sm5705_fled != g_sm5705_fled) {
dev_info(dev, "%s: sm5705_fled handler mismatched (g_handle:%p , l_handle:%p)\n", __func__, g_sm5705_fled, sm5705_fled);
sm5705_fled = g_sm5705_fled;
}
dev_info(dev, "%s: value=%d\n", __func__, value_u32);
#ifdef DISABLE_AFC
mutex_lock(&lock);
#else
spin_lock_irqsave(&fled_lock, flags);
#endif
switch (value_u32) {
case 0:
#ifdef DISABLE_AFC
/* MUIC 5V -> 9V function */
muic_torch_prepare(0);
#endif
/* Turn off Torch */
#if defined(CONFIG_DUAL_LEDS_FLASH)
ret = sm5705_fled_turn_off(sm5705_fled, index);
#else
ret = sm5705_fled_turn_off(sm5705_fled, REAR_FLASH_INDEX);
#endif
assistive_light = false;
break;
case 1:
/* Turn on Torch */
#ifdef DISABLE_AFC
for (retry = 0; retry < 3; retry++) {
if(muic_torch_prepare(1) == 1)
break;
pr_err("%s:%d ERROR: AFC disable unsuccessfull retrying after 30ms\n", __func__, __LINE__);
msleep(30);
}
if (retry == 3) {
pr_err("%s:%d ERROR: AFC disable failed\n", __func__, __LINE__);
mutex_unlock(&lock);
return -1;
}
/* MUIC 9V -> 5V function */
#endif
#if defined(CONFIG_DUAL_LEDS_FLASH)
ret = sm5705_fled_turn_on_torch(sm5705_fled, index, 60);
#else
ret = sm5705_fled_turn_on_torch(sm5705_fled, REAR_FLASH_INDEX, 60);
#endif
assistive_light = true;
break;
case 100:
/* Factory mode Turn on Torch */
#ifdef DISABLE_AFC
for (retry = 0; retry < 3; retry++) {
if(muic_torch_prepare(1) == 1)
break;
pr_err("%s:%d ERROR: AFC disable unsuccessfull retrying after 30ms\n", __func__, __LINE__);
msleep(30);
}
if (retry == 3) {
pr_err("%s:%d ERROR: AFC disable failed\n", __func__, __LINE__);
mutex_unlock(&lock);
return -1;
}
/* MUIC 9V -> 5V function */
#endif
#if defined(CONFIG_DUAL_LEDS_FLASH)
ret = sm5705_fled_turn_on_torch(sm5705_fled, index, 240);
#else
ret = sm5705_fled_turn_on_torch(sm5705_fled, REAR_FLASH_INDEX, 240);
#endif
assistive_light = true;
break;
default:
if (value_u32 > 1000 && value_u32 < (1000 + 32)) {
/* Turn on Torch : 20mA ~ 320mA */
#if defined(CONFIG_DUAL_LEDS_FLASH)
ret = sm5705_fled_turn_on_torch(sm5705_fled, index, _calc_torch_current_mA_to_offset(value_u32 - 1000));
#else
ret = sm5705_fled_turn_on_torch(sm5705_fled, REAR_FLASH_INDEX, _calc_torch_current_mA_to_offset(value_u32 - 1000));
#endif
assistive_light = true;
} else {
dev_err(dev, "%s: can't process, invalid value=%d\n", __func__, value_u32);
ret = -EINVAL;
}
break;
}
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s: fail to rear flash file operation:store (value=%d, ret=%d)\n", __func__, value_u32, ret);
}
#ifdef DISABLE_AFC
mutex_unlock(&lock);
#else
spin_unlock_irqrestore(&fled_lock, flags);
#endif
return count;
}
static ssize_t sm5705_rear_flash_show(struct device *dev, struct device_attribute *attr, char *buf)
{
unsigned char offset = _calc_torch_current_offset_to_mA(320);
dev_info(dev, "%s: SM5705 Movie mode max current = 320mA(offset:%d)\n", __func__, offset);
return sprintf(buf, "%d\n", offset);
}
static DEVICE_ATTR(rear_flash, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH, sm5705_rear_flash_show, sm5705_rear_flash_store);
#if defined(CONFIG_DUAL_LEDS_FLASH)
static DEVICE_ATTR(rear_flash_2, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH, sm5705_rear_flash_show, sm5705_rear_flash_store);
#endif
/**
* SM5705 Flash-LED device driver management functions
*/
static void sm5705_fled0_wait_valid_vbus_work(struct work_struct *work)
{
if (sm5705_fled_check_valid_vbus_from_MUIC() == true) {
_fled_turn_on_torch(g_sm5705_fled, 0);
} else {
queue_delayed_work(g_sm5705_fled->wqueue, &g_sm5705_fled->fled0_vbus_check_work, msecs_to_jiffies(10));
}
}
static void sm5705_fled1_wait_valid_vbus_work(struct work_struct *work)
{
if (sm5705_fled_check_valid_vbus_from_MUIC() == true) {
_fled_turn_on_torch(g_sm5705_fled, 1);
} else {
queue_delayed_work(g_sm5705_fled->wqueue, &g_sm5705_fled->fled1_vbus_check_work, msecs_to_jiffies(10));
}
}
#ifdef CONFIG_OF
static int sm5705_fled_parse_dt(struct sm5705_dev *sm5705, struct sm5705_fled_platform_data *pdata)
{
struct device_node *nproot = sm5705->dev->of_node;
struct device_node *np;// *c_np;
unsigned int temp;
int ret = 0, index;
#if defined(CONFIG_DUAL_LEDS_FLASH)
struct device_node *c_np;
#endif
if (!nproot) {
pr_err("<%s> could not find led sub-node led_np\n", __func__);
return -ENODEV;
}
np = of_find_node_by_name(nproot, "sm5705_fled");
if (!np) {
pr_err("%s : could not find led sub-node np\n", __func__);
return -EINVAL;
}
#if !defined(CONFIG_DUAL_LEDS_FLASH)
ret = of_property_read_u32(np, "id", &temp);
if (ret) {
pr_err("%s : could not find led id\n", __func__);
return ret;
}
index = temp;
ret = of_property_read_u32(np, "flash-mode-current-mA", &temp);
if (ret) {
pr_err("%s: fail to get dt:flash-mode-current-mA\n", __func__);
return ret;
}
pdata->led[index].flash_current_mA = temp;
ret = of_property_read_u32(np, "torch-mode-current-mA", &temp);
if (ret) {
pr_err("%s: fail to get dt:torch-mode-current-mA\n", __func__);
return ret;
}
pdata->led[index].torch_current_mA = temp;
ret = of_property_read_u32(np, "used-gpio-control", &temp);
if (ret) {
pr_err("%s: fail to get dt:used-gpio-control\n", __func__);
return ret;
}
pdata->led[index].used_gpio = (bool)(temp & 0x1);
if (pdata->led[index].used_gpio) {
ret = of_get_named_gpio(np, "flash-en-gpio", 0);
if (ret < 0) {
pr_err("%s: fail to get dt:flash-en-gpio (ret=%d)\n", __func__, ret);
return ret;
}
pdata->led[index].flash_en_pin = ret;
ret = of_get_named_gpio(np, "torch-en-gpio", 0);
if (ret < 0) {
pr_err("%s: fail to get dt:torch-en-gpio (ret=%d)\n", __func__, ret);
return ret;
}
pdata->led[index].torch_en_pin = ret;
}
#else
for_each_child_of_node(np, c_np) {
ret = of_property_read_u32(c_np, "id", &temp);
if (ret) {
pr_err("%s: fail to get a id\n", __func__);
return ret;
}
index = temp;
ret = of_property_read_u32(c_np, "flash-mode-current-mA", &temp);
if (ret) {
pr_err("%s: fail to get dt:flash-mode-current-mA\n", __func__);
return ret;
}
pdata->led[index].flash_current_mA = temp;
ret = of_property_read_u32(c_np, "torch-mode-current-mA", &temp);
if (ret) {
pr_err("%s: fail to get dt:torch-mode-current-mA\n", __func__);
return ret;
}
pdata->led[index].torch_current_mA = temp;
ret = of_property_read_u32(c_np, "used-gpio-control", &temp);
if (ret) {
pr_err("%s: fail to get dt:used-gpio-control\n", __func__);
return ret;
}
pdata->led[index].used_gpio = (bool)(temp & 0x1);
if (pdata->led[index].used_gpio) {
ret = of_get_named_gpio(c_np, "flash-en-gpio", 0);
if (ret < 0) {
pr_err("%s: fail to get dt:flash-en-gpio (ret=%d)\n", __func__, ret);
return ret;
}
pdata->led[index].flash_en_pin = ret;
ret = of_get_named_gpio(c_np, "torch-en-gpio", 0);
if (ret < 0) {
pr_err("%s: fail to get dt:torch-en-gpio (ret=%d)\n", __func__, ret);
return ret;
}
pdata->led[index].torch_en_pin = ret;
}
}
#endif
return ret;
}
#endif
static inline struct sm5705_fled_platform_data *_get_sm5705_fled_platform_data(struct device *dev, struct sm5705_dev *sm5705)
{
struct sm5705_fled_platform_data *pdata;
int i, ret = 0;
#ifdef CONFIG_OF
pdata = devm_kzalloc(dev, sizeof(struct sm5705_fled_platform_data), GFP_KERNEL);
if (unlikely(!pdata)) {
dev_err(dev, "%s: fail to allocate memory for sm5705_fled_platform_data\n", __func__);
goto out_p;
}
ret = sm5705_fled_parse_dt(sm5705, pdata);
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s: fail to parse dt for sm5705 flash-led (ret=%d)\n", __func__, ret);
goto out_kfree_p;
}
#else
pdata = sm5705->pdata->fled_platform_data;
if (unlikely(!pdata)) {
dev_err(dev, "%s: fail to get sm5705_fled_platform_data\n", __func__);
goto out_p;
}
#endif
dev_info(dev, "sm5705 flash-LED device platform data info, \n");
for (i=0; i < SM5705_FLED_MAX; ++i) {
dev_info(dev, "[FLED-%d] Flash: %dmA, Torch: %dmA, used_gpio=%d, GPIO_PIN(%d, %d)\n", i, pdata->led[i].flash_current_mA, \
pdata->led[i].torch_current_mA, pdata->led[i].used_gpio, pdata->led[i].flash_en_pin, pdata->led[i].torch_en_pin);
}
return pdata;
out_kfree_p:
devm_kfree(dev, pdata);
out_p:
return NULL;
}
static int sm5705_fled_probe(struct platform_device *pdev)
{
struct sm5705_dev *sm5705 = dev_get_drvdata(pdev->dev.parent);
struct sm5705_fled_info *sm5705_fled;
struct sm5705_fled_platform_data *sm5705_fled_pdata;
struct device *dev = &pdev->dev;
int i, ret = 0;
if (IS_ERR_OR_NULL(camera_class)) {
dev_err(dev, "%s: can't find camera_class sysfs object, didn't used rear_flash attribute\n", __func__);
return -ENOENT;
}
sm5705_fled = devm_kzalloc(dev, sizeof(struct sm5705_fled_info), GFP_KERNEL);
if (unlikely(!sm5705_fled)) {
dev_err(dev, "%s: fail to allocate memory for sm5705_fled_info\n", __func__);
return -ENOMEM;
}
dev_info(dev, "SM5705(rev.%d) Flash-LED devic driver Probing..\n", __get_revision_number());
sm5705_fled_pdata = _get_sm5705_fled_platform_data(dev, sm5705);
if (unlikely(!sm5705_fled_pdata)) {
dev_info(dev, "%s: fail to get platform data\n", __func__);
goto fled_platfrom_data_err;
}
sm5705_fled->dev = dev;
sm5705_fled->i2c = sm5705->i2c;
sm5705_fled->pdata = sm5705_fled_pdata;
platform_set_drvdata(pdev, sm5705_fled);
g_sm5705_fled = sm5705_fled;
ret = sm5705_fled_initialize(sm5705_fled);
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s: fail to initialize SM5705 Flash-LED[%d] (ret=%d)\n", __func__, i, ret);
goto fled_init_err;
}
sm5705_fled->wqueue = create_singlethread_workqueue(dev_name(dev));
if (!sm5705_fled->wqueue) {
dev_err(dev, "%s: fail to Create Workqueue\n", __func__);
goto fled_deinit_err;
}
INIT_DELAYED_WORK(&sm5705_fled->fled0_vbus_check_work, sm5705_fled0_wait_valid_vbus_work);
INIT_DELAYED_WORK(&sm5705_fled->fled1_vbus_check_work, sm5705_fled1_wait_valid_vbus_work);
/* create camera_class rear_flash device */
sm5705_fled->rear_fled_dev = device_create(camera_class, NULL, 3, NULL, "flash");
if (IS_ERR(sm5705_fled->rear_fled_dev)) {
dev_err(dev, "%s fail to create device for rear_flash\n", __func__);
goto fled_deinit_err;
}
sm5705_fled->rear_fled_dev->parent = dev;
ret = device_create_file(sm5705_fled->rear_fled_dev, &dev_attr_rear_flash);
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s fail to create device file for rear_flash\n", __func__);
goto fled_rear_device_err;
}
#if defined(CONFIG_DUAL_LEDS_FLASH)
ret = device_create_file(sm5705_fled->rear_fled_dev, &dev_attr_rear_flash_2);
if (IS_ERR_VALUE(ret)) {
dev_err(dev, "%s fail to create device file for rear_flash\n", __func__);
goto fled_rear_device_err2;
}
#endif
dev_info(dev, "%s: Probe done.\n", __func__);
return 0;
#if defined(CONFIG_DUAL_LEDS_FLASH)
fled_rear_device_err2:
device_remove_file(sm5705_fled->rear_fled_dev, &dev_attr_rear_flash);
#endif
fled_rear_device_err:
device_destroy(camera_class, sm5705_fled->rear_fled_dev->devt);
fled_deinit_err:
sm5705_fled_deinitialize(sm5705_fled);
fled_init_err:
platform_set_drvdata(pdev, NULL);
#ifdef CONFIG_OF
devm_kfree(dev, sm5705_fled_pdata);
#endif
fled_platfrom_data_err:
devm_kfree(dev, sm5705_fled);
g_sm5705_fled = NULL;
return ret;
}
static int sm5705_fled_remove(struct platform_device *pdev)
{
struct sm5705_fled_info *sm5705_fled = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
int i;
device_remove_file(sm5705_fled->rear_fled_dev, &dev_attr_rear_flash);
#if defined(CONFIG_DUAL_LEDS_FLASH)
device_remove_file(sm5705_fled->rear_fled_dev, &dev_attr_rear_flash_2);
#endif
device_destroy(camera_class, sm5705_fled->rear_fled_dev->devt);
for (i = 0; i != SM5705_FLED_MAX; ++i) {
sm5705_fled_turn_off(sm5705_fled, i);
}
sm5705_fled_deinitialize(sm5705_fled);
platform_set_drvdata(pdev, NULL);
#ifdef CONFIG_OF
devm_kfree(dev, sm5705_fled->pdata);
#endif
devm_kfree(dev, sm5705_fled);
return 0;
}
static void sm5705_fled_shutdown(struct device *dev)
{
struct sm5705_fled_info *sm5705_fled = dev_get_drvdata(dev);
int i;
for (i=0; i < SM5705_FLED_MAX; ++i) {
sm5705_fled_turn_off(sm5705_fled, i);
}
}
#ifdef CONFIG_OF
static struct of_device_id sm5705_fled_match_table[] = {
{ .compatible = "siliconmitus,sm5705-fled",},
{},
};
#else
#define sm5705_fled_match_table NULL
#endif
static struct platform_driver sm5705_fled_driver = {
.probe = sm5705_fled_probe,
.remove = sm5705_fled_remove,
.driver = {
.name = "sm5705-fled",
.owner = THIS_MODULE,
.shutdown = sm5705_fled_shutdown,
.of_match_table = sm5705_fled_match_table,
},
};
static int __init sm5705_fled_init(void)
{
printk("%s\n",__func__);
return platform_driver_register(&sm5705_fled_driver);
}
module_init(sm5705_fled_init);
static void __exit sm5705_fled_exit(void)
{
platform_driver_unregister(&sm5705_fled_driver);
}
module_exit(sm5705_fled_exit);
MODULE_DESCRIPTION("SM5705 FLASH-LED driver");
MODULE_ALIAS("platform:sm5705-flashLED");
MODULE_LICENSE("GPL");