leds: qti-flash: Add support for on_time and off_time parameters

Add support to enable switch after a delay of on_time milliseconds
and to disable switch after a delay of off_time milliseconds. Adding
delays to turn ON and OFF the switch is required by flash client
(camera) for fine precision flash control.

Add sysfs support to configure on_time and off_time for switch devices.

E.g. To configure ON time of 1 second and OFF time of 0.5 seconds on
switch 0 and strobing flash LED 0,
 echo 300 > /sys/class/leds/leds:flash_0/flash_brightness
 echo 1000 > /sys/class/leds/led:switch_0/on_time
 echo 500 > /sys/class/leds/led_switch_0/off_time
 echo 1 > /sys/class/leds/led_switch_0/brightness

Change-Id: I343eddf097bc7bb9960cd3fd4934c9b0a40cf450
Signed-off-by: Shyam Kumar Thella <sthella@codeaurora.org>
This commit is contained in:
Shyam Kumar Thella 2020-02-12 12:17:48 +05:30 committed by David Collins
parent 7bcf81b20e
commit ec59d570b6
2 changed files with 178 additions and 6 deletions

View File

@ -6,6 +6,7 @@
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/led-class-flash.h>
@ -134,6 +135,10 @@ struct flash_node_data {
struct flash_switch_data {
struct qti_flash_led *led;
struct led_classdev cdev;
struct hrtimer on_timer;
struct hrtimer off_timer;
u64 on_time_ms;
u64 off_time_ms;
u32 led_mask;
bool enabled;
bool symmetry_en;
@ -312,6 +317,7 @@ static int qti_flash_led_module_control(struct qti_flash_led *led,
}
static int qti_flash_led_strobe(struct qti_flash_led *led,
struct flash_switch_data *snode,
u8 mask, u8 value)
{
int rc;
@ -324,6 +330,14 @@ static int qti_flash_led_strobe(struct qti_flash_led *led,
if (rc < 0)
goto error;
if (snode && snode->off_time_ms) {
pr_debug("Off timer started with delay %d ms\n",
snode->off_time_ms);
hrtimer_start(&snode->off_timer,
ms_to_ktime(snode->off_time_ms),
HRTIMER_MODE_REL);
}
rc = qti_flash_led_masked_write(led, FLASH_EN_LED_CTRL,
mask, value);
if (rc < 0)
@ -563,7 +577,7 @@ static int qti_flash_switch_enable(struct flash_switch_data *snode)
led_en |= (1 << led->fnode[i].id);
}
return qti_flash_led_strobe(led, snode->led_mask, led_en);
return qti_flash_led_strobe(led, snode, snode->led_mask, led_en);
}
static int qti_flash_switch_disable(struct flash_switch_data *snode)
@ -593,7 +607,10 @@ static int qti_flash_switch_disable(struct flash_switch_data *snode)
led->fnode[i].configured = false;
}
return qti_flash_led_strobe(led, led_dis, ~led_dis);
snode->on_time_ms = 0;
snode->off_time_ms = 0;
return qti_flash_led_strobe(led, NULL, led_dis, ~led_dis);
}
static void qti_flash_led_switch_brightness_set(
@ -611,10 +628,21 @@ static void qti_flash_led_switch_brightness_set(
return;
}
if (state)
if (state) {
if (snode->on_time_ms) {
pr_debug("On timer started with delay %d ms\n",
snode->on_time_ms);
hrtimer_start(&snode->on_timer,
ms_to_ktime(snode->on_time_ms),
HRTIMER_MODE_REL);
snode->enabled = state;
return;
}
rc = qti_flash_switch_enable(snode);
else
} else {
rc = qti_flash_switch_disable(snode);
}
if (rc < 0)
pr_err("Failed to %s switch, rc=%d\n",
@ -639,6 +667,60 @@ static struct led_classdev *trigger_to_lcdev(struct led_trigger *trig)
return NULL;
}
static enum hrtimer_restart off_timer_function(struct hrtimer *timer)
{
struct flash_switch_data *snode = container_of(timer,
struct flash_switch_data, off_timer);
int rc = 0;
rc = qti_flash_switch_disable(snode);
if (rc < 0)
pr_err("Failed to disable flash LED switch %s, rc=%d\n",
snode->cdev.name, rc);
return HRTIMER_NORESTART;
}
static enum hrtimer_restart on_timer_function(struct hrtimer *timer)
{
struct flash_switch_data *snode = container_of(timer,
struct flash_switch_data, on_timer);
int rc = 0;
rc = qti_flash_switch_enable(snode);
if (rc < 0) {
snode->enabled = false;
pr_err("Failed to enable flash LED switch %s, rc=%d\n",
snode->cdev.name, rc);
}
return HRTIMER_NORESTART;
}
int qti_flash_led_set_param(struct led_trigger *trig,
struct flash_led_param param)
{
struct led_classdev *led_cdev = trigger_to_lcdev(trig);
struct flash_switch_data *snode;
if (!led_cdev) {
pr_err("Invalid led_cdev in trigger %s\n", trig->name);
return -EINVAL;
}
if (!param.on_time_ms && !param.off_time_ms) {
pr_err("Invalid param, on_time/off_time cannot be 0\n");
return -EINVAL;
}
snode = container_of(led_cdev, struct flash_switch_data, cdev);
snode->on_time_ms = param.on_time_ms;
snode->off_time_ms = param.off_time_ms;
return 0;
}
EXPORT_SYMBOL(qti_flash_led_set_param);
#define UCONV 1000000LL
#define MCONV 1000LL
#define VIN_FLASH_MIN_UV 3300000LL
@ -901,8 +983,73 @@ static ssize_t qti_flash_led_max_current_show(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "%d\n", snode->led->max_current);
}
static ssize_t qti_flash_on_time_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int rc;
struct flash_switch_data *snode;
struct led_classdev *led_cdev = dev_get_drvdata(dev);
u64 val;
rc = kstrtou64(buf, 0, &val);
if (rc < 0)
return rc;
if (!val)
return -EINVAL;
snode = container_of(led_cdev, struct flash_switch_data, cdev);
snode->on_time_ms = val;
return count;
}
static ssize_t qti_flash_on_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct flash_switch_data *snode;
struct led_classdev *led_cdev = dev_get_drvdata(dev);
snode = container_of(led_cdev, struct flash_switch_data, cdev);
return scnprintf(buf, PAGE_SIZE, "%lu\n", snode->on_time_ms);
}
static ssize_t qti_flash_off_time_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int rc;
struct flash_switch_data *snode;
struct led_classdev *led_cdev = dev_get_drvdata(dev);
u64 val;
rc = kstrtou64(buf, 0, &val);
if (rc < 0)
return rc;
snode = container_of(led_cdev, struct flash_switch_data, cdev);
snode->off_time_ms = val;
return count;
}
static ssize_t qti_flash_off_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct flash_switch_data *snode;
struct led_classdev *led_cdev = dev_get_drvdata(dev);
snode = container_of(led_cdev, struct flash_switch_data, cdev);
return scnprintf(buf, PAGE_SIZE, "%lu\n", snode->off_time_ms);
}
static struct device_attribute qti_flash_led_attrs[] = {
__ATTR(max_current, 0664, qti_flash_led_max_current_show, NULL),
__ATTR(max_current, 0400, qti_flash_led_max_current_show, NULL),
__ATTR(on_time, 0600, qti_flash_on_time_show,
qti_flash_on_time_store),
__ATTR(off_time, 0600, qti_flash_off_time_show,
qti_flash_off_time_store),
};
static int qti_flash_brightness_set_blocking(
@ -944,7 +1091,7 @@ static int qti_flash_strobe_set(struct led_classdev_flash *fdev,
mask = FLASH_LED_ENABLE(fnode->id);
value = state ? FLASH_LED_ENABLE(fnode->id) : 0;
rc = qti_flash_led_strobe(fnode->led, mask, value);
rc = qti_flash_led_strobe(fnode->led, NULL, mask, value);
if (!rc) {
fnode->enabled = state;
if (!state)
@ -1152,6 +1299,13 @@ static int register_switch_device(struct qti_flash_led *led,
snode->symmetry_en = of_property_read_bool(node, "qcom,symmetry-en");
snode->on_time_ms = 0;
snode->off_time_ms = 0;
hrtimer_init(&snode->on_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
hrtimer_init(&snode->off_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
snode->on_timer.function = on_timer_function;
snode->off_timer.function = off_timer_function;
snode->led = led;
snode->cdev.brightness_set = qti_flash_led_switch_brightness_set;
snode->cdev.brightness_get = qti_flash_led_brightness_get;

View File

@ -10,14 +10,32 @@
#define QUERY_MAX_AVAIL_CURRENT BIT(0)
/**
* struct flash_led_param: QTI flash LED parameter data
* @on_time_ms : Time to wait before strobing the switch
* @off_time_ms : Time to wait to turn off LED after strobing switch
*/
struct flash_led_param {
u64 on_time_ms;
u64 off_time_ms;
};
#if IS_ENABLED(CONFIG_LEDS_QTI_FLASH)
int qti_flash_led_prepare(struct led_trigger *trig,
int options, int *max_current);
int qti_flash_led_set_param(struct led_trigger *trig,
struct flash_led_param param);
#else
static inline int qti_flash_led_prepare(struct led_trigger *trig,
int options, int *max_current)
{
return -EINVAL;
}
static inline int qti_flash_led_set_param(struct led_trigger *trig,
struct flash_led_param param);
{
return -EINVAL;
}
#endif
#endif