From 685aaa4e194af7bf0a35ed10f7ef57434d0ae3e6 Mon Sep 17 00:00:00 2001 From: Shyam Kumar Thella Date: Mon, 10 Feb 2020 22:29:50 +0530 Subject: [PATCH] leds: qti-flash: Add support for qti_flash_led_prepare() qti_flash_led_prepare() API is used by flash clients (E.g. Camera) to query maximum current available for flash. Add support for this API which initially returns maximum current that is supported for flash LED module (2000 mA). Also, add a sysfs parameter under switch devices to show max available flash current. Change-Id: I3aed009a0d7e0f34a6d05a45af34fcd0f37a6fe7 Signed-off-by: Shyam Kumar Thella Signed-off-by: Guru Das Srinagesh --- drivers/leds/leds-qti-flash.c | 90 ++++++++++++++++++++++++++++++++-- include/linux/leds-qti-flash.h | 23 +++++++++ 2 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 include/linux/leds-qti-flash.h diff --git a/drivers/leds/leds-qti-flash.c b/drivers/leds/leds-qti-flash.c index 9c9d8e073b41..612af5be619f 100644 --- a/drivers/leds/leds-qti-flash.c +++ b/drivers/leds/leds-qti-flash.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,7 @@ #define IRES_12P5_UA 12500 #define IRES_5P0_UA 5000 #define IRES_DEFAULT_UA IRES_12P5_UA +#define MAX_FLASH_CURRENT_MA 2000 enum flash_led_type { FLASH_LED_TYPE_UNKNOWN, @@ -133,6 +135,7 @@ struct qti_flash_led { int all_ramp_up_done_irq; int all_ramp_down_done_irq; int led_fault_irq; + int max_current; u16 base; u8 max_channels; u8 ref_count; @@ -567,6 +570,65 @@ static void qti_flash_led_switch_brightness_set( snode->enabled = state; } +static struct led_classdev *trigger_to_lcdev(struct led_trigger *trig) +{ + struct led_classdev *led_cdev; + + read_lock(&trig->leddev_list_lock); + list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) { + if (!strcmp(led_cdev->default_trigger, trig->name)) { + read_unlock(&trig->leddev_list_lock); + return led_cdev; + } + } + + read_unlock(&trig->leddev_list_lock); + return NULL; +} + +static ssize_t qti_flash_led_max_current_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, "%d\n", snode->led->max_current); +} + +int qti_flash_led_prepare(struct led_trigger *trig, int options, + int *max_current) +{ + struct led_classdev *led_cdev; + struct flash_switch_data *snode; + + if (!trig) { + pr_err("Invalid led_trigger\n"); + return -EINVAL; + } + + led_cdev = trigger_to_lcdev(trig); + if (!led_cdev) { + pr_err("Invalid led_cdev in trigger %s\n", trig->name); + return -ENODEV; + } + + snode = container_of(led_cdev, struct flash_switch_data, cdev); + + if (options & QUERY_MAX_AVAIL_CURRENT) { + *max_current = snode->led->max_current; + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL(qti_flash_led_prepare); + +static struct device_attribute qti_flash_led_attrs[] = { + __ATTR(max_current, 0664, qti_flash_led_max_current_show, NULL), +}; + static int qti_flash_brightness_set_blocking( struct led_classdev *led_cdev, enum led_brightness value) { @@ -685,6 +747,8 @@ static int qti_flash_led_setup(struct qti_flash_led *led) return rc; } + led->max_current = MAX_FLASH_CURRENT_MA; + return rc; } @@ -768,7 +832,7 @@ static int qti_flash_led_register_interrupts(struct qti_flash_led *led) static int register_switch_device(struct qti_flash_led *led, struct flash_switch_data *snode, struct device_node *node) { - int rc; + int rc, i; rc = of_property_read_string(node, "qcom,led-name", &snode->cdev.name); @@ -808,7 +872,22 @@ static int register_switch_device(struct qti_flash_led *led, return rc; } + for (i = 0; i < ARRAY_SIZE(qti_flash_led_attrs); i++) { + rc = sysfs_create_file(&snode->cdev.dev->kobj, + &qti_flash_led_attrs[i].attr); + if (rc < 0) { + pr_err("Failed to create sysfs attrs, rc=%d\n", rc); + goto sysfs_fail; + } + } + return 0; + +sysfs_fail: + while (i >= 0) + sysfs_remove_file(&snode->cdev.dev->kobj, + &qti_flash_led_attrs[i--].attr); + return rc; } static int register_flash_device(struct qti_flash_led *led, @@ -1150,10 +1229,15 @@ static int qti_flash_led_probe(struct platform_device *pdev) static int qti_flash_led_remove(struct platform_device *pdev) { struct qti_flash_led *led = dev_get_drvdata(&pdev->dev); - int i; + int i, j; + + for (i = 0; (i < led->num_snodes); i++) { + for (j = 0; j < ARRAY_SIZE(qti_flash_led_attrs); j++) + sysfs_remove_file(&led->snode[i].cdev.dev->kobj, + &qti_flash_led_attrs[j].attr); - for (i = 0; (i < led->num_snodes); i++) led_classdev_unregister(&led->snode[i].cdev); + } for (i = 0; (i < led->num_fnodes); i++) led_classdev_flash_unregister(&led->fnode[i].fdev); diff --git a/include/linux/leds-qti-flash.h b/include/linux/leds-qti-flash.h new file mode 100644 index 000000000000..f313d79de7c1 --- /dev/null +++ b/include/linux/leds-qti-flash.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef __LEDS_QTI_FLASH_H +#define __LEDS_QTI_FLASH_H + +#include + +#define QUERY_MAX_AVAIL_CURRENT BIT(0) + +#if IS_ENABLED(CONFIG_LEDS_QTI_FLASH) +int qti_flash_led_prepare(struct led_trigger *trig, + int options, int *max_current); +#else +static inline int qti_flash_led_prepare(struct led_trigger *trig, + int options, int *max_current) +{ + return -EINVAL; +} +#endif +#endif