1261 lines
39 KiB
C
1261 lines
39 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/*
|
||
|
* LED driver for Samsung S2MPB02
|
||
|
*
|
||
|
* Copyright (C) 2014 Samsung Electronics
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
* This driver is based on leds-max77804.c
|
||
|
*/
|
||
|
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/init.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/leds.h>
|
||
|
#include <linux/workqueue.h>
|
||
|
#include <linux/spinlock.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/mfd/samsung/s2mpb02.h>
|
||
|
#include <linux/mfd/samsung/s2mpb02-regulator.h>
|
||
|
#include <linux/leds-s2mpb02.h>
|
||
|
#include <linux/ctype.h>
|
||
|
#include <linux/of_gpio.h>
|
||
|
|
||
|
struct device *camera_flash_dev;
|
||
|
struct s2mpb02_led_data *global_led_datas[S2MPB02_LED_MAX];
|
||
|
|
||
|
struct s2mpb02_led_data {
|
||
|
struct led_classdev led;
|
||
|
struct s2mpb02_dev *s2mpb02;
|
||
|
struct s2mpb02_led *data;
|
||
|
struct i2c_client *i2c;
|
||
|
struct work_struct work;
|
||
|
struct mutex lock;
|
||
|
spinlock_t value_lock;
|
||
|
int brightness;
|
||
|
int test_brightness;
|
||
|
};
|
||
|
|
||
|
static u8 leds_mask[S2MPB02_LED_MAX] = {
|
||
|
S2MPB02_FLASH_MASK,
|
||
|
S2MPB02_TORCH_MASK,
|
||
|
S2MPB02_TORCH_MASK,
|
||
|
};
|
||
|
|
||
|
static u8 leds_shift[S2MPB02_LED_MAX] = {
|
||
|
4,
|
||
|
0,
|
||
|
0,
|
||
|
};
|
||
|
|
||
|
u32 flash1_gpio;
|
||
|
u32 torch1_gpio;
|
||
|
#ifdef CONFIG_LEDS_S2MPB02_MULTI_TORCH_REAR2
|
||
|
u32 flash2_gpio;
|
||
|
u32 torch2_gpio;
|
||
|
#endif
|
||
|
static enum s2mpb02_torch_current torch_recording;
|
||
|
bool flash_config_factory;
|
||
|
u32 original_brightness[S2MPB02_LED_MAX];
|
||
|
|
||
|
static int s2mpb02_set_bits(struct i2c_client *client, const u8 reg,
|
||
|
const u8 mask, const u8 inval)
|
||
|
{
|
||
|
int ret;
|
||
|
u8 value;
|
||
|
|
||
|
ret = s2mpb02_read_reg(client, reg, &value);
|
||
|
if (unlikely(ret < 0))
|
||
|
return ret;
|
||
|
|
||
|
value = (value & ~mask) | (inval & mask);
|
||
|
|
||
|
ret = s2mpb02_write_reg(client, reg, value);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int s2mpb02_led_get_en_value(struct s2mpb02_led_data *led_data, int on)
|
||
|
{
|
||
|
if (on) {
|
||
|
if (led_data->data->id == S2MPB02_FLASH_LED_1) {
|
||
|
return ((S2MPB02_FLED_ENABLE << S2MPB02_FLED_ENABLE_SHIFT) |
|
||
|
(S2MPB02_FLED_FLASH_MODE << S2MPB02_FLED_MODE_SHIFT));
|
||
|
/* Turn on FLASH by I2C */
|
||
|
} else if (led_data->data->id == S2MPB02_TORCH_LED_2) {
|
||
|
return S2MPB02_FLED2_TORCH_ON;
|
||
|
/* Turn on TORCH by I2C */
|
||
|
} else {
|
||
|
return ((S2MPB02_FLED_ENABLE << S2MPB02_FLED_ENABLE_SHIFT) |
|
||
|
(S2MPB02_FLED_TORCH_MODE << S2MPB02_FLED_MODE_SHIFT));
|
||
|
/* Turn on TORCH by I2C */
|
||
|
}
|
||
|
} else {
|
||
|
return (S2MPB02_FLED_DISABLE << S2MPB02_FLED_ENABLE_SHIFT);
|
||
|
/* controlled by GPIO */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void s2mpb02_led_set(struct led_classdev *led_cdev,
|
||
|
enum led_brightness value)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static void s2mpb02_gpio_set(unsigned int gpio, int onoff)
|
||
|
{
|
||
|
if (!gpio_is_valid(gpio)) {
|
||
|
pr_info("%s: not valid gpio");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
gpio_request(gpio, NULL);
|
||
|
if (onoff)
|
||
|
gpio_direction_output(gpio, 1);
|
||
|
else
|
||
|
gpio_direction_output(gpio, 0);
|
||
|
gpio_free(gpio);
|
||
|
}
|
||
|
|
||
|
static void led_set(struct s2mpb02_led_data *led_data, int turn_way)
|
||
|
{
|
||
|
int ret;
|
||
|
struct s2mpb02_led *data = led_data->data;
|
||
|
int id = data->id;
|
||
|
int value;
|
||
|
u8 reg;
|
||
|
u8 mask;
|
||
|
|
||
|
if (turn_way == S2MPB02_LED_TURN_WAY_GPIO) {
|
||
|
/* Turn way LED by GPIO */
|
||
|
value = s2mpb02_led_get_en_value(led_data, 0);
|
||
|
if ((id == S2MPB02_FLASH_LED_1) || (id == S2MPB02_TORCH_LED_1)) {
|
||
|
reg = S2MPB02_REG_FLED_CTRL1;
|
||
|
mask = S2MPB02_FLED_ENABLE_MODE_MASK;
|
||
|
} else {
|
||
|
reg = S2MPB02_REG_FLED_CTRL2;
|
||
|
mask = S2MPB02_FLED2_ENABLE_MODE_MASK;
|
||
|
}
|
||
|
ret = s2mpb02_set_bits(led_data->i2c, id, mask, value);
|
||
|
if (unlikely(ret))
|
||
|
goto error_set_bits;
|
||
|
|
||
|
if (led_data->data->brightness == LED_OFF) {
|
||
|
if ((id == S2MPB02_FLASH_LED_1) || (id == S2MPB02_TORCH_LED_1))
|
||
|
reg = S2MPB02_REG_FLED_CUR1;
|
||
|
else
|
||
|
reg = S2MPB02_REG_FLED_CUR2;
|
||
|
|
||
|
/* set current */
|
||
|
ret = s2mpb02_set_bits(led_data->i2c, reg,
|
||
|
leds_mask[id], data->brightness << leds_shift[id]);
|
||
|
if (unlikely(ret))
|
||
|
goto error_set_bits;
|
||
|
|
||
|
s2mpb02_gpio_set(flash1_gpio, 0);
|
||
|
s2mpb02_gpio_set(torch1_gpio, 0);
|
||
|
#ifdef CONFIG_LEDS_S2MPB02_MULTI_TORCH_REAR2
|
||
|
s2mpb02_gpio_set(flash2_gpio, 0);
|
||
|
s2mpb02_gpio_set(torch2_gpio, 0);
|
||
|
#endif
|
||
|
} else {
|
||
|
if ((id == S2MPB02_FLASH_LED_1) || (id == S2MPB02_TORCH_LED_1))
|
||
|
reg = S2MPB02_REG_FLED_CUR1;
|
||
|
else
|
||
|
reg = S2MPB02_REG_FLED_CUR2;
|
||
|
|
||
|
/* set current */
|
||
|
ret = s2mpb02_set_bits(led_data->i2c, reg,
|
||
|
leds_mask[id], data->brightness << leds_shift[id]);
|
||
|
if (unlikely(ret))
|
||
|
goto error_set_bits;
|
||
|
|
||
|
if (id == S2MPB02_FLASH_LED_1) {
|
||
|
s2mpb02_gpio_set(flash1_gpio, 1);
|
||
|
s2mpb02_gpio_set(torch1_gpio, 0);
|
||
|
#ifdef CONFIG_LEDS_S2MPB02_MULTI_TORCH_REAR2
|
||
|
s2mpb02_gpio_set(flash2_gpio, 0);
|
||
|
s2mpb02_gpio_set(torch2_gpio, 0);
|
||
|
#endif
|
||
|
#ifdef CONFIG_LEDS_S2MPB02_MULTI_TORCH_REAR2
|
||
|
} else if (id == S2MPB02_TORCH_LED_2) {
|
||
|
s2mpb02_gpio_set(flash1_gpio, 0);
|
||
|
s2mpb02_gpio_set(torch1_gpio, 0);
|
||
|
s2mpb02_gpio_set(flash2_gpio, 0);
|
||
|
s2mpb02_gpio_set(torch2_gpio, 1);
|
||
|
#endif
|
||
|
} else { /* S2MPB02_TORCH_LED_1 */
|
||
|
s2mpb02_gpio_set(flash1_gpio, 0);
|
||
|
s2mpb02_gpio_set(torch1_gpio, 1);
|
||
|
#ifdef CONFIG_LEDS_S2MPB02_MULTI_TORCH_REAR2
|
||
|
s2mpb02_gpio_set(flash2_gpio, 0);
|
||
|
s2mpb02_gpio_set(torch2_gpio, 0);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
} else { /* (turn_way == S2MPB02_LED_TURN_WAY_I2C) */
|
||
|
if (led_data->data->brightness == LED_OFF) {
|
||
|
value = s2mpb02_led_get_en_value(led_data, 0);
|
||
|
if ((id == S2MPB02_FLASH_LED_1) || (id == S2MPB02_TORCH_LED_1)) {
|
||
|
reg = S2MPB02_REG_FLED_CTRL1;
|
||
|
mask = S2MPB02_FLED_ENABLE_MODE_MASK;
|
||
|
} else {
|
||
|
reg = S2MPB02_REG_FLED_CTRL2;
|
||
|
mask = S2MPB02_FLED2_ENABLE_MODE_MASK;
|
||
|
}
|
||
|
ret = s2mpb02_set_bits(led_data->i2c, reg, mask, value);
|
||
|
if (unlikely(ret))
|
||
|
goto error_set_bits;
|
||
|
|
||
|
if ((id == S2MPB02_FLASH_LED_1) || (id == S2MPB02_TORCH_LED_1))
|
||
|
reg = S2MPB02_REG_FLED_CUR1;
|
||
|
else
|
||
|
reg = S2MPB02_REG_FLED_CUR2;
|
||
|
|
||
|
/* set current */
|
||
|
ret = s2mpb02_set_bits(led_data->i2c, reg, leds_mask[id],
|
||
|
data->brightness << leds_shift[id]);
|
||
|
if (unlikely(ret))
|
||
|
goto error_set_bits;
|
||
|
} else {
|
||
|
if ((id == S2MPB02_FLASH_LED_1) || (id == S2MPB02_TORCH_LED_1))
|
||
|
reg = S2MPB02_REG_FLED_CUR1;
|
||
|
else
|
||
|
reg = S2MPB02_REG_FLED_CUR2;
|
||
|
|
||
|
/* set current */
|
||
|
ret = s2mpb02_set_bits(led_data->i2c, reg, leds_mask[id],
|
||
|
data->brightness << leds_shift[id]);
|
||
|
if (unlikely(ret))
|
||
|
goto error_set_bits;
|
||
|
|
||
|
/* Turn on LED by I2C */
|
||
|
value = s2mpb02_led_get_en_value(led_data, 1);
|
||
|
if ((id == S2MPB02_FLASH_LED_1) || (id == S2MPB02_TORCH_LED_1)) {
|
||
|
reg = S2MPB02_REG_FLED_CTRL1;
|
||
|
mask = S2MPB02_FLED_ENABLE_MODE_MASK;
|
||
|
} else {
|
||
|
reg = S2MPB02_REG_FLED_CTRL2;
|
||
|
mask = S2MPB02_FLED2_ENABLE_MODE_MASK;
|
||
|
}
|
||
|
ret = s2mpb02_set_bits(led_data->i2c, reg, mask, value);
|
||
|
if (unlikely(ret))
|
||
|
goto error_set_bits;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
error_set_bits:
|
||
|
pr_err("%s: can't set led level %d\n", __func__, ret);
|
||
|
}
|
||
|
|
||
|
static void s2mpb02_led_work(struct work_struct *work)
|
||
|
{
|
||
|
struct s2mpb02_led_data *led_data
|
||
|
= container_of(work, struct s2mpb02_led_data, work);
|
||
|
|
||
|
pr_debug("[LED] %s\n", __func__);
|
||
|
|
||
|
mutex_lock(&led_data->lock);
|
||
|
led_set(led_data, S2MPB02_LED_TURN_WAY_I2C);
|
||
|
mutex_unlock(&led_data->lock);
|
||
|
}
|
||
|
|
||
|
static int s2mpb02_led_setup(struct s2mpb02_led_data *led_data)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct s2mpb02_led *data = led_data->data;
|
||
|
int id, value;
|
||
|
|
||
|
if (data == NULL) {
|
||
|
pr_err("%s : data is null\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
id = data->id;
|
||
|
|
||
|
/* set Low Voltage operating mode disable */
|
||
|
ret |= s2mpb02_update_reg(led_data->i2c, S2MPB02_REG_FLED_CTRL1,
|
||
|
S2MPB02_FLED_CTRL1_LV_DISABLE, S2MPB02_FLED_CTRL1_LV_EN_MASK);
|
||
|
|
||
|
/* set current & timeout */
|
||
|
ret |= s2mpb02_update_reg(led_data->i2c, S2MPB02_REG_FLED_CUR1,
|
||
|
data->brightness << leds_shift[id], leds_mask[id]);
|
||
|
ret |= s2mpb02_update_reg(led_data->i2c, S2MPB02_REG_FLED_TIME1,
|
||
|
data->timeout << leds_shift[id], leds_mask[id]);
|
||
|
|
||
|
value = s2mpb02_led_get_en_value(led_data, 0);
|
||
|
ret |= s2mpb02_update_reg(led_data->i2c,
|
||
|
S2MPB02_REG_FLED_CTRL1, value, S2MPB02_FLED_ENABLE_MODE_MASK);
|
||
|
|
||
|
#ifdef CONFIG_LEDS_S2MPB02_MULTI_TORCH_REAR2
|
||
|
/* set current & timeout */
|
||
|
ret |= s2mpb02_update_reg(led_data->i2c, S2MPB02_REG_FLED_CUR2,
|
||
|
data->brightness << leds_shift[id], leds_mask[id]);
|
||
|
ret |= s2mpb02_update_reg(led_data->i2c, S2MPB02_REG_FLED_TIME2,
|
||
|
0x00, leds_mask[id]);
|
||
|
|
||
|
value = s2mpb02_led_get_en_value(led_data, 0);
|
||
|
ret = s2mpb02_update_reg(led_data->i2c,
|
||
|
S2MPB02_REG_FLED_CTRL2, value, S2MPB02_FLED2_ENABLE_MODE_MASK);
|
||
|
#endif
|
||
|
|
||
|
ret |= s2mpb02_update_reg(led_data->i2c,
|
||
|
S2MPB02_REG_FLED_CTRL2, 0x04, S2MPB02_TORCH_MASK);
|
||
|
|
||
|
#ifdef CONFIG_LEDS_IRIS_IRLED_SUPPORT
|
||
|
/* Remote-mode off, Power-LED Mode on, GPIO Polarity */
|
||
|
ret |= s2mpb02_write_reg(led_data->i2c, S2MPB02_REG_FLED_CTRL2, 0x38);
|
||
|
|
||
|
/* set current - 1050mA */
|
||
|
ret |= s2mpb02_write_reg(led_data->i2c, S2MPB02_REG_FLED_CUR2, 0xAF);
|
||
|
|
||
|
/* set 0x18, 0x19, 0x1A, 0x1B */
|
||
|
ret |= s2mpb02_write_reg(led_data->i2c, S2MPB02_REG_FLED_IRON1, 0x19);
|
||
|
ret |= s2mpb02_write_reg(led_data->i2c, S2MPB02_REG_FLED_IRON2, 0x0B);
|
||
|
ret |= s2mpb02_write_reg(led_data->i2c, S2MPB02_REG_FLED_IRD1, 0x00);
|
||
|
ret |= s2mpb02_write_reg(led_data->i2c, S2MPB02_REG_FLED_IRD2, 0x2C);
|
||
|
#endif
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void s2mpb02_led_get_status(struct s2mpb02_led_data *led_data)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
u8 value[11] = {0, };
|
||
|
|
||
|
ret = s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_INT1, &value[0]); /*INT1*/
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_CTRL1, &value[1]); /*FLED_CTRL1*/
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_CTRL2, &value[2]); /*FLED_CTRL2*/
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_CUR1, &value[3]); /*FLED_CUR1*/
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_TIME1, &value[4]); /*FLED_TIME1*/
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_CUR2, &value[5]); /*FLED_CUR2*/
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_TIME2, &value[6]); /*FLED_TIME2*/
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_IRON1, &value[7]); /*FLED_IRON1*/
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_IRON2, &value[8]); /*FLED_IRON2*/
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_IRD1, &value[9]); /*FLED_IRD1*/
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_IRD2, &value[10]); /*FLED_IRD2*/
|
||
|
if (unlikely(ret < 0))
|
||
|
pr_info("%s : i2c fail\n", __func__);
|
||
|
|
||
|
pr_info("%s: INT1[0x%02x] FLED CTRL1[0x%02x] CTRL2[0x%02x] CUR1[0x%02x] TIME1[0x%02x]\n",
|
||
|
__func__, value[0], value[1], value[2], value[3], value[4]);
|
||
|
pr_info("%s: CUR2[0x%02x] TIME2[0x%02x] IRON1[0x%02x] IRON2[0x%02x] IRD1[0x%02x] IRD2[0x%02x]\n",
|
||
|
__func__, value[5], value[6], value[7], value[8], value[9], value[10]);
|
||
|
}
|
||
|
|
||
|
/*++ Only use in HealthHW R&D Group to check the ALS sensor performance ++*/
|
||
|
int s2mpb02_led_en(int mode, int onoff, enum s2mpb02_led_turn_way turn_way)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
if (global_led_datas[S2MPB02_TORCH_LED_1] == NULL) {
|
||
|
pr_err("<%s> global_led_datas[S2MPB02_TORCH_LED_1] is NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&global_led_datas[mode]->lock);
|
||
|
|
||
|
if (onoff > 0) {/* enable */
|
||
|
pr_info("<%s> enable %d, %d\n", __func__, onoff, mode);
|
||
|
if (mode == S2MPB02_TORCH_LED_1) {
|
||
|
if (onoff >= S2MPB02_TORCH_OUT_I_MAX)
|
||
|
onoff = S2MPB02_TORCH_OUT_I_MAX-1;
|
||
|
} else if (mode == S2MPB02_FLASH_LED_1) {
|
||
|
if (onoff >= S2MPB02_FLASH_OUT_I_MAX)
|
||
|
onoff = S2MPB02_FLASH_OUT_I_MAX-1;
|
||
|
} else {
|
||
|
pr_err("<%s> mode %d is invalid\n", __func__, mode);
|
||
|
ret = -1;
|
||
|
goto p_unlock;
|
||
|
}
|
||
|
global_led_datas[mode]->data->brightness = onoff;
|
||
|
} else {/* disable */
|
||
|
pr_info("<%s> disable %d, %d\n", __func__, onoff, mode);
|
||
|
global_led_datas[mode]->data->brightness = LED_OFF;
|
||
|
}
|
||
|
|
||
|
led_set(global_led_datas[mode], turn_way);
|
||
|
|
||
|
p_unlock:
|
||
|
mutex_unlock(&global_led_datas[mode]->lock);
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(s2mpb02_led_en);
|
||
|
/*-- Request from the HealthHW R&D Group to check the ALS sensor performance --*/
|
||
|
|
||
|
void s2mpb02_led_dump_register(u8 s_reg, u8 e_reg)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
u8 value = 0;
|
||
|
u8 i = 0;
|
||
|
|
||
|
struct s2mpb02_led_data *led_data = global_led_datas[S2MPB02_TORCH_LED_1];
|
||
|
|
||
|
for (i = s_reg; i <= e_reg; i++) {
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, i, &value); /*Fled_ctrl2*/
|
||
|
if (unlikely(ret < 0))
|
||
|
pr_info("%s : error to read register\n", __func__);
|
||
|
else
|
||
|
pr_info("%s : reg. 0x%2X = 0x%2X\n", __func__, i, value);
|
||
|
}
|
||
|
}
|
||
|
EXPORT_SYMBOL(s2mpb02_led_dump_register);
|
||
|
|
||
|
int s2mpb02_set_torch_current(enum s2mpb02_torch_mode torch_mode, unsigned int intensity)
|
||
|
{
|
||
|
struct s2mpb02_led_data *led_data = global_led_datas[S2MPB02_TORCH_LED_1];
|
||
|
struct s2mpb02_led *data = led_data->data;
|
||
|
int ret = 0;
|
||
|
u8 cur_brightness;
|
||
|
|
||
|
switch (torch_mode) {
|
||
|
case S2MPB02_TORCH_RECORDING:
|
||
|
if (intensity)
|
||
|
cur_brightness = (intensity / 20);
|
||
|
else
|
||
|
cur_brightness = torch_recording;
|
||
|
break;
|
||
|
case S2MPB02_TORCH_PREFLASH:
|
||
|
case S2MPB02_TORCH_NORMAL:
|
||
|
default:
|
||
|
if (intensity)
|
||
|
cur_brightness = (intensity / 20);
|
||
|
else
|
||
|
cur_brightness = original_brightness[data->id];
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pr_info("%s: torch_mode[%s] intensity[%d] cur_brightness[%d]\n",
|
||
|
__func__,
|
||
|
torch_mode ? (torch_mode == S2MPB02_TORCH_PREFLASH ? "PRE-FLASH" : "NORMAL") : "RECORDING",
|
||
|
intensity, cur_brightness);
|
||
|
mutex_lock(&led_data->lock);
|
||
|
|
||
|
data->brightness = cur_brightness;
|
||
|
|
||
|
/* set current */
|
||
|
ret = s2mpb02_set_bits(led_data->i2c, S2MPB02_REG_FLED_CUR1,
|
||
|
leds_mask[data->id], data->brightness << leds_shift[data->id]);
|
||
|
if (unlikely(ret))
|
||
|
pr_err("%s: failed to set FLED_CUR1, %d\n", __func__, ret);
|
||
|
|
||
|
mutex_unlock(&led_data->lock);
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(s2mpb02_set_torch_current);
|
||
|
|
||
|
int s2mpb02_set_flash_current(unsigned int intensity)
|
||
|
{
|
||
|
struct s2mpb02_led_data *led_data = global_led_datas[S2MPB02_FLASH_LED_1];
|
||
|
struct s2mpb02_led *data = led_data->data;
|
||
|
int ret = 0;
|
||
|
u8 cur_brightness = (intensity > 150) ? ((intensity - 50) / 100) : 1;
|
||
|
|
||
|
pr_info("%s: flash intensity %d\n", __func__, intensity);
|
||
|
|
||
|
if (cur_brightness < S2MPB02_FLASH_OUT_I_150MA)
|
||
|
cur_brightness = S2MPB02_FLASH_OUT_I_150MA;
|
||
|
else if (cur_brightness >= S2MPB02_FLASH_OUT_I_MAX)
|
||
|
cur_brightness = S2MPB02_FLASH_OUT_I_1550MA;
|
||
|
mutex_lock(&led_data->lock);
|
||
|
|
||
|
/* set current */
|
||
|
s2mpb02_set_bits(led_data->i2c, S2MPB02_REG_FLED_CUR1,
|
||
|
leds_mask[data->id], cur_brightness << leds_shift[data->id]);
|
||
|
led_data->brightness = cur_brightness;
|
||
|
if (unlikely(ret))
|
||
|
pr_err("%s: failed to set FLED_CUR1, %d\n", __func__, ret);
|
||
|
|
||
|
mutex_unlock(&led_data->lock);
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(s2mpb02_set_flash_current);
|
||
|
|
||
|
ssize_t s2mpb02_store(struct device *dev,
|
||
|
struct device_attribute *attr, const char *buf,
|
||
|
size_t count)
|
||
|
{
|
||
|
int value = 0;
|
||
|
int ret = 0;
|
||
|
|
||
|
if ((buf == NULL) || kstrtouint(buf, 10, &value))
|
||
|
return -1;
|
||
|
|
||
|
if (global_led_datas[S2MPB02_TORCH_LED_1] == NULL) {
|
||
|
pr_err("<%s> global_led_datas[S2MPB02_TORCH_LED_1] is NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
pr_info("[LED]%s , value:%d\n", __func__, value);
|
||
|
mutex_lock(&global_led_datas[S2MPB02_TORCH_LED_1]->lock);
|
||
|
|
||
|
if (value == 0) {
|
||
|
/* Turn off Torch */
|
||
|
if (!flash_config_factory) {
|
||
|
global_led_datas[S2MPB02_TORCH_LED_1]->data->brightness = LED_OFF;
|
||
|
led_set(global_led_datas[S2MPB02_TORCH_LED_1], S2MPB02_LED_TURN_WAY_GPIO);
|
||
|
} else {
|
||
|
global_led_datas[S2MPB02_FLASH_LED_1]->data->brightness = LED_OFF;
|
||
|
if (!gpio_is_valid(flash1_gpio))
|
||
|
led_set(global_led_datas[S2MPB02_FLASH_LED_1], S2MPB02_LED_TURN_WAY_I2C);
|
||
|
else
|
||
|
led_set(global_led_datas[S2MPB02_FLASH_LED_1], S2MPB02_LED_TURN_WAY_GPIO);
|
||
|
}
|
||
|
} else if (value == 1) {
|
||
|
/* Turn on Torch */
|
||
|
global_led_datas[S2MPB02_TORCH_LED_1]->data->brightness = S2MPB02_TORCH_OUT_I_60MA;
|
||
|
led_set(global_led_datas[S2MPB02_TORCH_LED_1], S2MPB02_LED_TURN_WAY_GPIO);
|
||
|
} else if (value == 100) {
|
||
|
/* Factory mode Turn on Torch */
|
||
|
global_led_datas[S2MPB02_TORCH_LED_1]->data->brightness = S2MPB02_TORCH_OUT_I_240MA;
|
||
|
led_set(global_led_datas[S2MPB02_TORCH_LED_1], S2MPB02_LED_TURN_WAY_GPIO);
|
||
|
} else if (value == 200) {
|
||
|
/* Factory mode Turn on Flash */
|
||
|
/* set reserved reg. 0x63 for continuous flash on */
|
||
|
flash_config_factory = true;
|
||
|
ret = s2mpb02_write_reg(global_led_datas[S2MPB02_FLASH_LED_1]->i2c, 0x63, 0x5F);
|
||
|
if (ret < 0)
|
||
|
pr_info("[LED]%s , failed set flash register setting\n", __func__);
|
||
|
|
||
|
global_led_datas[S2MPB02_FLASH_LED_1]->data->brightness = S2MPB02_FLASH_OUT_I_350MA;
|
||
|
if (!gpio_is_valid(flash1_gpio))
|
||
|
led_set(global_led_datas[S2MPB02_FLASH_LED_1], S2MPB02_LED_TURN_WAY_I2C);
|
||
|
else
|
||
|
led_set(global_led_datas[S2MPB02_FLASH_LED_1], S2MPB02_LED_TURN_WAY_GPIO);
|
||
|
} else if (value >= 1001 && value <= 1010) {
|
||
|
/* Torch mode, step 1~5 value : 1001, 1002, 1004, 1006, 1009 */
|
||
|
int brightness_value = value - 1001;
|
||
|
int torch_intensity = -1;
|
||
|
|
||
|
if (global_led_datas[S2MPB02_TORCH_LED_1]->data->torch_table[brightness_value] != 0)
|
||
|
torch_intensity = global_led_datas[S2MPB02_TORCH_LED_1]->data->torch_table[brightness_value];
|
||
|
|
||
|
if (torch_intensity < 0) {
|
||
|
pr_info("[LED]%s , Invalid torch_intensity(%d), reset as default: %d\n", __func__,
|
||
|
torch_intensity,
|
||
|
global_led_datas[S2MPB02_TORCH_LED_1]->data->brightness);
|
||
|
torch_intensity = global_led_datas[S2MPB02_TORCH_LED_1]->data->brightness;
|
||
|
}
|
||
|
/* Turn on Torch Step 40mA ~ 240mA */
|
||
|
pr_info("[LED]%s , %d->%d(%dmA)\n", __func__, brightness_value, torch_intensity, (torch_intensity)*20);
|
||
|
global_led_datas[S2MPB02_TORCH_LED_1]->data->brightness = torch_intensity;
|
||
|
led_set(global_led_datas[S2MPB02_TORCH_LED_1], S2MPB02_LED_TURN_WAY_GPIO);
|
||
|
} else if (value >= 2000 && value <= 2015) {
|
||
|
int torch_intensity = value - 2000;
|
||
|
|
||
|
/* Turn on Torch 20mA ~ 300mA of each step 20mA*/
|
||
|
pr_info("[LED]%s , torch brightness set (%dmA)\n", __func__, (torch_intensity)*20);
|
||
|
global_led_datas[S2MPB02_TORCH_LED_1]->data->brightness = torch_intensity;
|
||
|
led_set(global_led_datas[S2MPB02_TORCH_LED_1], S2MPB02_LED_TURN_WAY_GPIO);
|
||
|
} else {
|
||
|
pr_info("[LED]%s , Invalid value:%d\n", __func__, value);
|
||
|
}
|
||
|
|
||
|
if (value <= 0) {
|
||
|
s2mpb02_set_bits(global_led_datas[S2MPB02_TORCH_LED_1]->i2c, S2MPB02_REG_FLED_CUR1,
|
||
|
leds_mask[global_led_datas[S2MPB02_TORCH_LED_1]->data->id],
|
||
|
original_brightness[S2MPB02_TORCH_LED_1] <<
|
||
|
leds_shift[global_led_datas[S2MPB02_TORCH_LED_1]->data->id]);
|
||
|
global_led_datas[S2MPB02_TORCH_LED_1]->data->brightness =
|
||
|
original_brightness[S2MPB02_TORCH_LED_1];
|
||
|
|
||
|
if (flash_config_factory) {
|
||
|
s2mpb02_write_reg(global_led_datas[S2MPB02_FLASH_LED_1]->i2c, 0x63, 0x7F);
|
||
|
s2mpb02_set_bits(global_led_datas[S2MPB02_FLASH_LED_1]->i2c, S2MPB02_REG_FLED_CUR1,
|
||
|
leds_mask[global_led_datas[S2MPB02_FLASH_LED_1]->data->id],
|
||
|
original_brightness[S2MPB02_FLASH_LED_1] <<
|
||
|
leds_shift[global_led_datas[S2MPB02_FLASH_LED_1]->data->id]);
|
||
|
global_led_datas[S2MPB02_FLASH_LED_1]->data->brightness =
|
||
|
original_brightness[S2MPB02_FLASH_LED_1];
|
||
|
flash_config_factory = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&global_led_datas[S2MPB02_TORCH_LED_1]->lock);
|
||
|
pr_info("[LED]%s, gpio state flash:%d, torch:%d", __func__, gpio_get_value(flash1_gpio),
|
||
|
gpio_get_value(torch1_gpio));
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_LEDS_S2MPB02_MULTI_TORCH_REAR2
|
||
|
ssize_t s2mpb02_store_2nd(struct device *dev,
|
||
|
struct device_attribute *attr, const char *buf,
|
||
|
size_t count)
|
||
|
{
|
||
|
int value = 0;
|
||
|
int ret = 0;
|
||
|
|
||
|
if ((buf == NULL) || kstrtouint(buf, 10, &value))
|
||
|
return -1;
|
||
|
|
||
|
if (global_led_datas[S2MPB02_TORCH_LED_2] == NULL) {
|
||
|
pr_err("<%s> global_led_datas[S2MPB02_TORCH_LED_2] is NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
pr_info("[LED]%s , value:%d\n", __func__, value);
|
||
|
mutex_lock(&global_led_datas[S2MPB02_TORCH_LED_2]->lock);
|
||
|
|
||
|
if (value == 0) {
|
||
|
if (!flash_config_factory) {
|
||
|
global_led_datas[S2MPB02_TORCH_LED_2]->data->brightness = LED_OFF;
|
||
|
led_set(global_led_datas[S2MPB02_TORCH_LED_2], S2MPB02_LED_TURN_WAY_I2C);
|
||
|
} else {
|
||
|
global_led_datas[S2MPB02_FLASH_LED_1]->data->brightness = LED_OFF;
|
||
|
led_set(global_led_datas[S2MPB02_FLASH_LED_1], S2MPB02_LED_TURN_WAY_I2C);
|
||
|
}
|
||
|
} else if (value == 1) {
|
||
|
/* Turn on Torch */
|
||
|
global_led_datas[S2MPB02_TORCH_LED_2]->data->brightness = S2MPB02_TORCH_OUT_I_60MA;
|
||
|
led_set(global_led_datas[S2MPB02_TORCH_LED_2], S2MPB02_LED_TURN_WAY_I2C);
|
||
|
} else if (value == 100) {
|
||
|
/* Factory mode Turn on Torch */
|
||
|
global_led_datas[S2MPB02_TORCH_LED_2]->data->brightness = S2MPB02_TORCH_OUT_I_240MA;
|
||
|
led_set(global_led_datas[S2MPB02_TORCH_LED_2], S2MPB02_LED_TURN_WAY_I2C);
|
||
|
} else if (value == 200) {
|
||
|
/* Factory mode Turn on Flash */
|
||
|
/* set reserved reg. 0x63 for continuous flash on */
|
||
|
flash_config_factory = true;
|
||
|
ret = s2mpb02_write_reg(global_led_datas[S2MPB02_FLASH_LED_1]->i2c, 0x63, 0x5F);
|
||
|
if (ret < 0)
|
||
|
pr_info("[LED]%s , failed set flash register setting\n", __func__);
|
||
|
|
||
|
global_led_datas[S2MPB02_FLASH_LED_1]->data->brightness = S2MPB02_FLASH_OUT_I_350MA;
|
||
|
led_set(global_led_datas[S2MPB02_FLASH_LED_1], S2MPB02_LED_TURN_WAY_I2C);
|
||
|
} else if (value >= 1001 && value <= 1010) {
|
||
|
/* Torch mode, step 1~5 value : 1001, 1002, 1004, 1006, 1009 */
|
||
|
int brightness_value = value - 1001;
|
||
|
int torch_intensity = -1;
|
||
|
|
||
|
if (global_led_datas[S2MPB02_TORCH_LED_2]->data->torch_table[brightness_value] != 0)
|
||
|
torch_intensity = global_led_datas[S2MPB02_TORCH_LED_2]->data->torch_table[brightness_value];
|
||
|
|
||
|
if (torch_intensity < 0) {
|
||
|
pr_info("[LED]%s , Invalid torch_intensity(%d), reset as default: %d\n",
|
||
|
__func__, torch_intensity,
|
||
|
global_led_datas[S2MPB02_TORCH_LED_2]->data->brightness);
|
||
|
torch_intensity = global_led_datas[S2MPB02_TORCH_LED_2]->data->brightness;
|
||
|
}
|
||
|
/* Turn on Torch Step 40mA ~ 240mA */
|
||
|
pr_info("[LED]%s , %d->%d(%dmA)\n", __func__, brightness_value, torch_intensity, (torch_intensity)*20);
|
||
|
global_led_datas[S2MPB02_TORCH_LED_2]->data->brightness = torch_intensity;
|
||
|
led_set(global_led_datas[S2MPB02_TORCH_LED_2], S2MPB02_LED_TURN_WAY_I2C);
|
||
|
} else if (value >= 2000 && value <= 2015) {
|
||
|
int torch_intensity = value - 2000;
|
||
|
|
||
|
/* Turn on Torch 20mA ~ 300mA of each step 20mA*/
|
||
|
pr_info("[LED]%s , torch brightness set (%dmA)\n", __func__, (torch_intensity)*20);
|
||
|
global_led_datas[S2MPB02_TORCH_LED_2]->data->brightness = torch_intensity;
|
||
|
led_set(global_led_datas[S2MPB02_TORCH_LED_2], S2MPB02_LED_TURN_WAY_I2C);
|
||
|
} else {
|
||
|
pr_info("[LED]%s , Invalid value:%d\n", __func__, value);
|
||
|
}
|
||
|
|
||
|
if (value <= 0) {
|
||
|
s2mpb02_set_bits(global_led_datas[S2MPB02_TORCH_LED_2]->i2c, S2MPB02_REG_FLED_CUR1,
|
||
|
leds_mask[global_led_datas[S2MPB02_TORCH_LED_2]->data->id],
|
||
|
original_brightness[S2MPB02_TORCH_LED_2] <<
|
||
|
leds_shift[global_led_datas[S2MPB02_TORCH_LED_2]->data->id]);
|
||
|
global_led_datas[S2MPB02_TORCH_LED_2]->data->brightness =
|
||
|
original_brightness[S2MPB02_TORCH_LED_2];
|
||
|
|
||
|
if (flash_config_factory) {
|
||
|
s2mpb02_write_reg(global_led_datas[S2MPB02_FLASH_LED_1]->i2c, 0x63, 0x7F);
|
||
|
s2mpb02_set_bits(global_led_datas[S2MPB02_FLASH_LED_1]->i2c, S2MPB02_REG_FLED_CUR2,
|
||
|
leds_mask[global_led_datas[S2MPB02_FLASH_LED_1]->data->id],
|
||
|
original_brightness[S2MPB02_FLASH_LED_1] <<
|
||
|
leds_shift[global_led_datas[S2MPB02_FLASH_LED_1]->data->id]);
|
||
|
global_led_datas[S2MPB02_FLASH_LED_1]->data->brightness =
|
||
|
original_brightness[S2MPB02_FLASH_LED_1];
|
||
|
flash_config_factory = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&global_led_datas[S2MPB02_TORCH_LED_2]->lock);
|
||
|
return count;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
ssize_t s2mpb02_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
if (global_led_datas[S2MPB02_TORCH_LED_1] == NULL) {
|
||
|
pr_err("<%s> global_led_datas[S2MPB02_TORCH_LED_1] is NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
pr_info("[LED] %s , MAX STEP TORCH_LED:%d\n", __func__, S2MPB02_TORCH_OUT_I_MAX - 1);
|
||
|
return sprintf(buf, "%d\n", S2MPB02_TORCH_OUT_I_MAX - 1);
|
||
|
}
|
||
|
|
||
|
ssize_t s2mpb02_state_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
return sprintf(buf, "%d\n", gpio_get_value(torch1_gpio));
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_LEDS_IRIS_IRLED_CERTIFICATE_SUPPORT
|
||
|
ssize_t s2mpb02_irled_torch_store(struct device *dev,
|
||
|
struct device_attribute *attr, const char *buf,
|
||
|
size_t count)
|
||
|
{
|
||
|
int value = 0;
|
||
|
u8 ledvalue = 0;
|
||
|
int ret = 0;
|
||
|
|
||
|
if ((buf == NULL) || kstrtouint(buf, 10, &value))
|
||
|
return -1;
|
||
|
|
||
|
if (global_led_datas[0] == NULL) {
|
||
|
pr_err("<%s> global_led_datas[S2MPB02_TORCH_LED_1] is NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
pr_info("%s , value:%d\n", __func__, value);
|
||
|
if (value == 1) {
|
||
|
pr_info("%s IRLED TORCH ON\n", __func__);
|
||
|
/* FLED_TIME2.IR_MAX_TIMER_EN Disable */
|
||
|
ret |= s2mpb02_update_reg(global_led_datas[0]->i2c,
|
||
|
S2MPB02_REG_FLED_TIME2,
|
||
|
S2MPB02_FLED_TIME2_IRMAX_TIMER_DISABLE,
|
||
|
S2MPB02_FLED_TIME2_IRMAX_TIMER_EN_MASK);
|
||
|
|
||
|
/* Remote-mode off, TORCH MODE */
|
||
|
ret |= s2mpb02_update_reg(global_led_datas[0]->i2c,
|
||
|
S2MPB02_REG_FLED_CTRL2,
|
||
|
S2MPB02_FLED_CTRL2_TORCH_ON,
|
||
|
S2MPB02_FLED_CTRL2_TORCH_MASK);
|
||
|
ret |= s2mpb02_update_reg(global_led_datas[0]->i2c,
|
||
|
S2MPB02_REG_FLED_CUR2, 0x0E, S2MPB02_FLED_CUR2_TORCH_CUR2_MASK);
|
||
|
} else if (value == 0) {
|
||
|
pr_info("%s IRLED TORCH OFF\n", __func__);
|
||
|
/* Remote-mode off, Power-LED Mode on, GPIO Polarity */
|
||
|
ret |= s2mpb02_update_reg(global_led_datas[0]->i2c,
|
||
|
S2MPB02_REG_FLED_CTRL2,
|
||
|
S2MPB02_FLED_CTRL2_FLASH_ON,
|
||
|
S2MPB02_FLED_CTRL2_TORCH_MASK);
|
||
|
} else {
|
||
|
pr_err("%s value(%d) is invalid!!\n", __func__, value);
|
||
|
return -1;
|
||
|
}
|
||
|
ret |= s2mpb02_read_reg(global_led_datas[0]->i2c, S2MPB02_REG_FLED_CTRL2, &ledvalue);
|
||
|
pr_info("[LED]%s , ledvalue:%x\n", __func__, ledvalue);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
ssize_t s2mpb02_irled_torch_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct s2mpb02_led_data *led_data;
|
||
|
int ret = 0;
|
||
|
u8 value[10] = {0, };
|
||
|
|
||
|
if (global_led_datas[0] == NULL) {
|
||
|
pr_err("<%s> global_led_datas[S2MPB02_TORCH_LED_1] is NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
led_data = global_led_datas[0];
|
||
|
|
||
|
ret = s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_CTRL1, &value[0]); //Fled_ctrl1
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_CTRL2, &value[1]); //Fled_ctrl2
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_CUR1, &value[2]); //Fled_cur1
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_TIME1, &value[3]); //Fled_time1
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_CUR2, &value[4]); //Fled_cur2
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_TIME2, &value[5]); //Fled_time2
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_IRON1, &value[6]); //Fled_iron1
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_IRON2, &value[7]); //Fled_iron2
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_IRD1, &value[8]); //Fled_ird1
|
||
|
ret |= s2mpb02_read_reg(led_data->i2c, S2MPB02_REG_FLED_IRD2, &value[9]); //Fled_ird2
|
||
|
if (unlikely(ret < 0))
|
||
|
pr_err("%s : error to get dt node\n", __func__);
|
||
|
|
||
|
pr_info("%s : Fled_ctrl1 = 0x%12x, Fled_ctrl2 = 0x%13x, Fled_cur1 = 0x%14x, Fled_time1 = 0x%15x\n",
|
||
|
__func__, value[0], value[1], value[2], value[3]);
|
||
|
|
||
|
pr_info("%s : Fled_cur2 = 0x%16x, Fled_time2 = 0x%17x, Fled_iron1 = 0x%x\n",
|
||
|
__func__, value[4], value[5], value[6]);
|
||
|
|
||
|
pr_info("%s : Fled_iron2 = 0x%x, Fled_ird1 = 0x%x, Fled_ird2 = 0x%x\n",
|
||
|
__func__, value[7], value[8], value[9]);
|
||
|
|
||
|
return sprintf(buf, "0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n",
|
||
|
value[0], value[1], value[2], value[3], value[4], value[5],
|
||
|
value[6], value[7], value[8], value[9]);
|
||
|
}
|
||
|
|
||
|
static DEVICE_ATTR(irled_torch, 00644,
|
||
|
s2mpb02_irled_torch_show, s2mpb02_irled_torch_store);
|
||
|
#endif
|
||
|
|
||
|
static DEVICE_ATTR(rear_flash, 00644,
|
||
|
s2mpb02_show, s2mpb02_store);
|
||
|
|
||
|
#ifdef CONFIG_LEDS_S2MPB02_MULTI_TORCH_REAR2
|
||
|
static DEVICE_ATTR(rear_flash2, 00644,
|
||
|
s2mpb02_show, s2mpb02_store_2nd);
|
||
|
#endif
|
||
|
|
||
|
static DEVICE_ATTR(flash_state, 00444, s2mpb02_state_show, NULL);
|
||
|
|
||
|
#ifdef CONFIG_LEDS_IRIS_IRLED_SUPPORT
|
||
|
int s2mpb02_ir_led_current(uint32_t current_value)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
unsigned int value = current_value - 1;
|
||
|
unsigned char data = 0;
|
||
|
|
||
|
pr_info("[%s] led current value : %u\n", __func__, value);
|
||
|
|
||
|
data = ((value & 0x0F) << 4) | 0x0F;
|
||
|
|
||
|
ret = s2mpb02_write_reg(global_led_datas[0]->i2c, S2MPB02_REG_FLED_CUR2, data);
|
||
|
if (ret < 0)
|
||
|
pr_err("[%s] i2c write error", __func__);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(s2mpb02_ir_led_current);
|
||
|
|
||
|
int s2mpb02_ir_led_pulse_width(uint32_t width)
|
||
|
{
|
||
|
unsigned int value = width;
|
||
|
unsigned char iron1 = 0;
|
||
|
unsigned char iron2 = 0;
|
||
|
int ret = 0;
|
||
|
|
||
|
pr_info("[%s] led pulse_width value : %u\n", __func__, value);
|
||
|
|
||
|
iron1 = (value >> 2) & 0xFF;
|
||
|
iron2 = (value & 0x03) << 6;
|
||
|
|
||
|
pr_info("[%s] IRON1(0x%02x), IRON2(0x%02x)\n", __func__, iron1, iron2);
|
||
|
|
||
|
/* set 0x18, 0x19 */
|
||
|
ret |= s2mpb02_write_reg(global_led_datas[0]->i2c, S2MPB02_REG_FLED_IRON1, iron1);
|
||
|
ret |= s2mpb02_set_bits(global_led_datas[0]->i2c, S2MPB02_REG_FLED_IRON2,
|
||
|
S2MPB02_FLED2_IRON2_MASK, iron2);
|
||
|
if (ret < 0)
|
||
|
pr_err("[%s] i2c write error", __func__);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(s2mpb02_ir_led_pulse_width);
|
||
|
|
||
|
int s2mpb02_ir_led_pulse_delay(uint32_t delay)
|
||
|
{
|
||
|
unsigned int value = delay;
|
||
|
unsigned char ird1 = 0;
|
||
|
unsigned char ird2 = 0;
|
||
|
int ret = 0;
|
||
|
|
||
|
pr_info("[%s] led pulse_delay value : %u\n", __func__, value);
|
||
|
|
||
|
ird1 = (value >> 2) & 0xFF;
|
||
|
ird2 = ((value & 0x03) << 6) | 0x2C; /* value 0x2C means RSVD[5:0] Reserved */
|
||
|
|
||
|
pr_info("[%s] IRD1(0x%02x), IRD2(0x%02x)\n", __func__, ird1, ird2);
|
||
|
|
||
|
/* set 0x1A, 0x1B */
|
||
|
ret |= s2mpb02_write_reg(global_led_datas[0]->i2c, S2MPB02_REG_FLED_IRD1, ird1);
|
||
|
ret |= s2mpb02_write_reg(global_led_datas[0]->i2c, S2MPB02_REG_FLED_IRD2, ird2);
|
||
|
if (ret < 0)
|
||
|
pr_err("[%s] i2c write error", __func__);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(s2mpb02_ir_led_pulse_delay);
|
||
|
|
||
|
int s2mpb02_ir_led_max_time(uint32_t max_time)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
pr_info("[%s] led max_time value : %u\n", __func__, max_time);
|
||
|
|
||
|
/* set IRLED max timer interrupt clear and enabled */
|
||
|
ret |= s2mpb02_set_bits(global_led_datas[0]->i2c, S2MPB02_REG_FLED_CTRL2,
|
||
|
S2MPB02_FLED2_MAX_TIME_CLEAR_MASK, 0x00);
|
||
|
ret |= s2mpb02_set_bits(global_led_datas[0]->i2c, S2MPB02_REG_FLED_TIME2,
|
||
|
S2MPB02_FLED2_MAX_TIME_EN_MASK, 0x00);
|
||
|
if (max_time > 0) {
|
||
|
ret |= s2mpb02_set_bits(global_led_datas[0]->i2c, S2MPB02_REG_FLED_TIME2,
|
||
|
S2MPB02_FLED2_MAX_TIME_EN_MASK, 0x01);
|
||
|
|
||
|
ret |= s2mpb02_set_bits(global_led_datas[0]->i2c, S2MPB02_REG_FLED_IRON2,
|
||
|
S2MPB02_FLED2_MAX_TIME_MASK, (u8) max_time - 1);
|
||
|
}
|
||
|
|
||
|
s2mpb02_led_get_status(global_led_datas[0]);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(s2mpb02_ir_led_max_time);
|
||
|
#endif
|
||
|
|
||
|
#if defined(CONFIG_OF)
|
||
|
static int of_s2mpb02_torch_dt(struct s2mpb02_dev *iodev,
|
||
|
struct s2mpb02_led_platform_data *pdata)
|
||
|
{
|
||
|
struct device_node *led_np, *np, *c_np;
|
||
|
int ret;
|
||
|
u32 temp;
|
||
|
u32 irda_off = 0;
|
||
|
const char *temp_str;
|
||
|
int index;
|
||
|
u32 torch_table_enable = 0;
|
||
|
|
||
|
led_np = iodev->dev->of_node;
|
||
|
if (!led_np) {
|
||
|
pr_info("<%s> could not find led sub-node\n", __func__);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
np = of_find_node_by_name(led_np, "torch");
|
||
|
if (!np) {
|
||
|
pr_info("<%s> could not find led sub-node\n",
|
||
|
__func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
torch1_gpio = of_get_named_gpio(np, "torch1-gpio", 0);
|
||
|
if (!gpio_is_valid(torch1_gpio))
|
||
|
pr_info("%s failed to get a torch1_gpio\n", __func__);
|
||
|
else
|
||
|
s2mpb02_gpio_set(torch1_gpio, 0);
|
||
|
|
||
|
flash1_gpio = of_get_named_gpio(np, "flash1-gpio", 0);
|
||
|
if (!gpio_is_valid(flash1_gpio))
|
||
|
pr_info("%s failed to get a flash1_gpio\n", __func__);
|
||
|
else
|
||
|
s2mpb02_gpio_set(flash1_gpio, 0);
|
||
|
|
||
|
#ifdef CONFIG_LEDS_S2MPB02_MULTI_TORCH_REAR2
|
||
|
torch2_gpio = of_get_named_gpio(np, "torch2-gpio", 0);
|
||
|
if (!gpio_is_valid(torch2_gpio))
|
||
|
pr_info("%s failed to get a torch2_gpio\n", __func__);
|
||
|
else
|
||
|
s2mpb02_gpio_set(torch2_gpio, 0);
|
||
|
|
||
|
flash2_gpio = of_get_named_gpio(np, "flash2-gpio", 0);
|
||
|
if (!gpio_is_valid(flash2_gpio))
|
||
|
pr_info("%s failed to get a flash2_gpio\n", __func__);
|
||
|
else
|
||
|
s2mpb02_gpio_set(flash2_gpio, 0);
|
||
|
#endif
|
||
|
|
||
|
ret = of_property_read_u32(np, "torch_recording", &temp);
|
||
|
if (ret) {
|
||
|
torch_recording = S2MPB02_TORCH_OUT_I_220MA;
|
||
|
pr_info("%s failed to get a torch_current. default 220mA\n", __func__);
|
||
|
} else
|
||
|
torch_recording = (enum s2mpb02_torch_current)temp;
|
||
|
|
||
|
pdata->num_leds = of_get_child_count(np);
|
||
|
|
||
|
for_each_child_of_node(np, c_np) {
|
||
|
ret = of_property_read_u32(c_np, "id", &temp);
|
||
|
if (ret)
|
||
|
pr_info("%s failed to get a id\n", __func__);
|
||
|
|
||
|
index = temp;
|
||
|
pdata->leds[index].id = temp;
|
||
|
|
||
|
ret = of_property_read_string(c_np, "ledname", &temp_str);
|
||
|
if (ret)
|
||
|
pr_info("%s failed to get a ledname\n", __func__);
|
||
|
|
||
|
pdata->leds[index].name = temp_str;
|
||
|
ret = of_property_read_u32(c_np, "brightness", &temp);
|
||
|
if (ret)
|
||
|
pr_info("%s failed to get a brightness\n", __func__);
|
||
|
|
||
|
if (temp > S2MPB02_FLASH_TORCH_CURRENT_MAX)
|
||
|
pr_info("%s out of range : brightness\n", __func__);
|
||
|
|
||
|
pdata->leds[index].brightness = temp;
|
||
|
original_brightness[index] = temp;
|
||
|
|
||
|
ret = of_property_read_u32(c_np, "timeout", &temp);
|
||
|
if (ret)
|
||
|
pr_info("%s failed to get a timeout\n", __func__);
|
||
|
|
||
|
if (temp > S2MPB02_TIMEOUT_MAX)
|
||
|
pr_info("%s out of range : timeout\n", __func__);
|
||
|
|
||
|
pdata->leds[index].timeout = temp;
|
||
|
|
||
|
if (index == 1) {
|
||
|
ret = of_property_read_u32(c_np, "irda_off", &irda_off);
|
||
|
if (ret)
|
||
|
pr_info("%s failed to get a irda_off\n", __func__);
|
||
|
|
||
|
pdata->leds[index].irda_off = irda_off;
|
||
|
}
|
||
|
|
||
|
ret = of_property_read_u32(c_np, "torch_table_enable", &torch_table_enable);
|
||
|
if (ret)
|
||
|
pr_info("%s failed to get a torch_table_enable\n", __func__);
|
||
|
|
||
|
if (torch_table_enable == 1) {
|
||
|
pdata->leds[index].torch_table_enable = torch_table_enable;
|
||
|
ret = of_property_read_u32_array(c_np, "torch_table",
|
||
|
pdata->leds[index].torch_table, TORCH_STEP);
|
||
|
} else {
|
||
|
pdata->leds[index].torch_table_enable = 0;
|
||
|
}
|
||
|
}
|
||
|
of_node_put(led_np);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
#endif /* CONFIG_OF */
|
||
|
|
||
|
int s2mpb02_create_sysfs(struct class *class)
|
||
|
{
|
||
|
if (class == NULL) {
|
||
|
class = class_create(THIS_MODULE, "camera");
|
||
|
if (IS_ERR(class)) {
|
||
|
pr_err("Failed to create class(camera)!\n");
|
||
|
return PTR_ERR(class);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
camera_flash_dev = device_create(class, NULL, 3, NULL, "flash");
|
||
|
|
||
|
if (IS_ERR(camera_flash_dev)) {
|
||
|
pr_err("<%s> Failed to create device(flash)!\n", __func__);
|
||
|
} else {
|
||
|
if (device_create_file(camera_flash_dev, &dev_attr_rear_flash) < 0) {
|
||
|
pr_err("<%s> failed to create device file, %s\n",
|
||
|
__func__, dev_attr_rear_flash.attr.name);
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_LEDS_IRIS_IRLED_CERTIFICATE_SUPPORT
|
||
|
if (device_create_file(camera_flash_dev,
|
||
|
&dev_attr_irled_torch) < 0) {
|
||
|
pr_err("<%s> failed to create device file, %s\n",
|
||
|
__func__, dev_attr_irled_torch.attr.name);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef CONFIG_LEDS_S2MPB02_MULTI_TORCH_REAR2
|
||
|
if (device_create_file(camera_flash_dev, &dev_attr_rear_flash2) < 0) {
|
||
|
pr_err("<%s> failed to create device file, %s\n",
|
||
|
__func__, dev_attr_rear_flash2.attr.name);
|
||
|
}
|
||
|
#endif
|
||
|
if (device_create_file(camera_flash_dev, &dev_attr_flash_state) < 0) {
|
||
|
pr_err("<%s> failed to create device file, %s\n",
|
||
|
__func__, dev_attr_flash_state.attr.name);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(s2mpb02_create_sysfs);
|
||
|
|
||
|
int s2mpb02_destroy_sysfs(struct class *class)
|
||
|
{
|
||
|
if (camera_flash_dev) {
|
||
|
device_remove_file(camera_flash_dev, &dev_attr_rear_flash);
|
||
|
#ifdef CONFIG_LEDS_S2MPB02_MULTI_TORCH_REAR2
|
||
|
device_remove_file(camera_flash_dev, &dev_attr_rear_flash2);
|
||
|
#endif
|
||
|
#ifdef CONFIG_LEDS_IRIS_IRLED_CERTIFICATE_SUPPORT
|
||
|
device_remove_file(camera_flash_dev, &dev_attr_irled_torch);
|
||
|
#endif
|
||
|
device_remove_file(camera_flash_dev, &dev_attr_flash_state);
|
||
|
}
|
||
|
|
||
|
if (class && camera_flash_dev)
|
||
|
device_destroy(class, camera_flash_dev->devt);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(s2mpb02_destroy_sysfs);
|
||
|
|
||
|
static int s2mpb02_led_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int i;
|
||
|
struct s2mpb02_dev *s2mpb02 = dev_get_drvdata(pdev->dev.parent);
|
||
|
#ifndef CONFIG_OF
|
||
|
struct s2mpb02_platform_data *s2mpb02_pdata
|
||
|
= dev_get_platdata(s2mpb02->dev);
|
||
|
#endif
|
||
|
struct s2mpb02_led_platform_data *pdata;
|
||
|
struct s2mpb02_led_data *led_data;
|
||
|
struct s2mpb02_led *data;
|
||
|
struct s2mpb02_led_data **led_datas;
|
||
|
|
||
|
#ifdef CONFIG_OF
|
||
|
pdata = kzalloc(sizeof(struct s2mpb02_led_platform_data), GFP_KERNEL);
|
||
|
if (!pdata)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
ret = of_s2mpb02_torch_dt(s2mpb02, pdata);
|
||
|
if (ret < 0) {
|
||
|
pr_err("s2mpb02-torch : %s not found torch dt! ret[%d]\n",
|
||
|
__func__, ret);
|
||
|
kfree(pdata);
|
||
|
return -1;
|
||
|
}
|
||
|
#else
|
||
|
pdata = s2mpb02_pdata->led_data;
|
||
|
if (pdata == NULL) {
|
||
|
pr_err("[LED] no platform data for this led is found\n");
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
#endif
|
||
|
led_datas = kzalloc(sizeof(struct s2mpb02_led_data *)
|
||
|
* S2MPB02_LED_MAX, GFP_KERNEL);
|
||
|
if (unlikely(!led_datas)) {
|
||
|
pr_err("[LED] memory allocation error %s", __func__);
|
||
|
kfree(pdata);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
platform_set_drvdata(pdev, led_datas);
|
||
|
|
||
|
pr_info("[LED] %s %d leds\n", __func__, pdata->num_leds);
|
||
|
|
||
|
for (i = 0; i != pdata->num_leds; ++i) {
|
||
|
pr_info("%s led%d setup ...\n", __func__, i);
|
||
|
|
||
|
data = kzalloc(sizeof(struct s2mpb02_led), GFP_KERNEL);
|
||
|
if (unlikely(!data)) {
|
||
|
pr_err("[LED] memory allocation error %s\n", __func__);
|
||
|
ret = -ENOMEM;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
memcpy(data, &(pdata->leds[i]), sizeof(struct s2mpb02_led));
|
||
|
|
||
|
led_data = kzalloc(sizeof(struct s2mpb02_led_data),
|
||
|
GFP_KERNEL);
|
||
|
|
||
|
global_led_datas[i] = led_data;
|
||
|
led_datas[i] = led_data;
|
||
|
if (unlikely(!led_data)) {
|
||
|
pr_err("[LED] memory allocation error %s\n", __func__);
|
||
|
ret = -ENOMEM;
|
||
|
kfree(data);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
led_data->s2mpb02 = s2mpb02;
|
||
|
led_data->i2c = s2mpb02->i2c;
|
||
|
led_data->data = data;
|
||
|
led_data->led.name = data->name;
|
||
|
led_data->led.brightness_set = s2mpb02_led_set;
|
||
|
led_data->led.brightness = LED_OFF;
|
||
|
led_data->brightness = data->brightness;
|
||
|
led_data->led.flags = 0;
|
||
|
led_data->led.max_brightness = S2MPB02_FLASH_TORCH_CURRENT_MAX;
|
||
|
|
||
|
mutex_init(&led_data->lock);
|
||
|
spin_lock_init(&led_data->value_lock);
|
||
|
INIT_WORK(&led_data->work, s2mpb02_led_work);
|
||
|
|
||
|
ret = led_classdev_register(&pdev->dev, &led_data->led);
|
||
|
if (unlikely(ret)) {
|
||
|
pr_err("unable to register LED\n");
|
||
|
cancel_work_sync(&led_data->work);
|
||
|
mutex_destroy(&led_data->lock);
|
||
|
kfree(data);
|
||
|
kfree(led_data);
|
||
|
led_datas[i] = NULL;
|
||
|
global_led_datas[i] = NULL;
|
||
|
ret = -EFAULT;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
ret = s2mpb02_led_setup(led_data);
|
||
|
/* To prevent faint LED light, enable active discharge */
|
||
|
ret |= s2mpb02_update_reg(led_data->i2c, S2MPB02_REG_FLED_IRD2,
|
||
|
0x02, 0x02);
|
||
|
if (unlikely(ret)) {
|
||
|
pr_err("unable to register LED\n");
|
||
|
cancel_work_sync(&led_data->work);
|
||
|
mutex_destroy(&led_data->lock);
|
||
|
led_classdev_unregister(&led_data->led);
|
||
|
kfree(data);
|
||
|
kfree(led_data);
|
||
|
led_datas[i] = NULL;
|
||
|
global_led_datas[i] = NULL;
|
||
|
ret = -EFAULT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_OF
|
||
|
kfree(pdata);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
pr_err("<%s> end\n", __func__);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int s2mpb02_led_remove(struct platform_device *pdev)
|
||
|
{
|
||
|
struct s2mpb02_led_data **led_datas = platform_get_drvdata(pdev);
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i != S2MPB02_LED_MAX; ++i) {
|
||
|
if (led_datas[i] == NULL)
|
||
|
continue;
|
||
|
|
||
|
cancel_work_sync(&led_datas[i]->work);
|
||
|
mutex_destroy(&led_datas[i]->lock);
|
||
|
led_classdev_unregister(&led_datas[i]->led);
|
||
|
kfree(led_datas[i]->data);
|
||
|
kfree(led_datas[i]);
|
||
|
led_datas[i] = NULL;
|
||
|
global_led_datas[i] = NULL;
|
||
|
}
|
||
|
kfree(led_datas);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void s2mpb02_led_shutdown(struct device *dev)
|
||
|
{
|
||
|
global_led_datas[S2MPB02_TORCH_LED_1]->data->brightness = LED_OFF;
|
||
|
led_set(global_led_datas[S2MPB02_TORCH_LED_1], S2MPB02_LED_TURN_WAY_I2C);
|
||
|
}
|
||
|
|
||
|
static struct platform_driver s2mpb02_led_driver = {
|
||
|
.probe = s2mpb02_led_probe,
|
||
|
.remove = s2mpb02_led_remove,
|
||
|
.driver = {
|
||
|
.name = "s2mpb02-led",
|
||
|
.owner = THIS_MODULE,
|
||
|
.shutdown = s2mpb02_led_shutdown,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
static int s2mpb02_led_init(void)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
ret = platform_driver_register(&s2mpb02_led_driver);
|
||
|
if (ret)
|
||
|
pr_err("s2mpb02_led_driver register failed(%d)", ret);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
module_init(s2mpb02_led_init);
|
||
|
|
||
|
static void s2mpb02_led_exit(void)
|
||
|
{
|
||
|
platform_driver_unregister(&s2mpb02_led_driver);
|
||
|
}
|
||
|
module_exit(s2mpb02_led_exit);
|
||
|
|
||
|
MODULE_DESCRIPTION("S2MPB02 LED driver");
|
||
|
MODULE_LICENSE("GPL");
|
||
|
|