summaryrefslogtreecommitdiff
path: root/drivers/leds
diff options
context:
space:
mode:
authorChun Zhang <chunz@codeaurora.org>2016-03-16 17:24:17 -0700
committerKyle Yan <kyan@codeaurora.org>2016-07-11 18:56:22 -0700
commit9eb8b67fe9eef65412d630e0ab3d90300a601450 (patch)
tree4f38f960859bd0481dee5e0f920b939db10d5cc4 /drivers/leds
parent8a547c50fe278a12d776e4737e9776c8a1d2df18 (diff)
leds: qpnp-flash-v2: add support for strobe configuration
Flash LED module supports various kinds of strobe support. Strobe type can be hardware or software, active high or low, and level or edge trigger. Add support for configuring strobe type through device tree properties. CRs-Fixed: 1024187 Change-Id: Ief6a610c82b7f645966c596f543a1d30f7a40dcc Signed-off-by: Chun Zhang <chunz@codeaurora.org> Signed-off-by: Devesh Jhunjhunwala <deveshj@codeaurora.org>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/leds-qpnp-flash-v2.c148
1 files changed, 139 insertions, 9 deletions
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index 6942a8a74ea9..2bd60ce13f83 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -16,6 +16,8 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/platform_device.h>
#include <linux/leds-qpnp-flash-v2.h>
@@ -24,8 +26,9 @@
#define FLASH_LED_REG_TGR_CURRENT(base) (base + 0x43)
#define FLASH_LED_REG_MOD_CTRL(base) (base + 0x46)
#define FLASH_LED_REG_IRES(base) (base + 0x47)
+#define FLASH_LED_REG_STROBE_CFG(base) (base + 0x48)
#define FLASH_LED_REG_STROBE_CTRL(base) (base + 0x49)
-#define FLASH_LED_REG_CHANNEL_CTRL(base) (base + 0x4C)
+#define FLASH_LED_EN_LED_CTRL(base) (base + 0x4C)
#define FLASH_LED_REG_HDRM_PRGM(base) (base + 0x4D)
#define FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(base) (base + 0x50)
#define FLASH_LED_REG_ISC_DELAY(base) (base + 0x52)
@@ -33,10 +36,11 @@
#define FLASH_LED_HDRM_MODE_PRGM_MASK GENMASK(7, 0)
#define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4)
#define FLASH_LED_CURRENT_MASK GENMASK(6, 0)
-#define FLASH_LED_STROBE_CTRL_MASK GENMASK(2, 0)
+#define FLASH_LED_ENABLE_MASK GENMASK(2, 0)
#define FLASH_LED_SAFETY_TMR_MASK GENMASK(7, 0)
#define FLASH_LED_ISC_DELAY_MASK GENMASK(1, 0)
#define FLASH_LED_MOD_CTRL_MASK BIT(7)
+#define FLASH_LED_HW_SW_STROBE_SEL_MASK BIT(2)
#define FLASH_LED_HEADROOM_AUTO_MODE_ENABLED true
#define FLASH_LED_ISC_DELAY_SHIFT 6
@@ -54,7 +58,11 @@
#define FLASH_LED_HDRM_VOL_HI_LO_WIN_DEFAULT_MV 0x04
#define FLASH_LED_HDRM_VOL_BASE_MV 125
#define FLASH_LED_HDRM_VOL_STEP_MV 25
-#define FLASH_LED_STROBE_ENABLE BIT(0)
+#define FLASH_LED_STROBE_CFG_DEFAULT 0x00
+#define FLASH_LED_HW_STROBE_OPTION_1 0x00
+#define FLASH_LED_HW_STROBE_OPTION_2 0x01
+#define FLASH_LED_HW_STROBE_OPTION_3 0x02
+#define FLASH_LED_ENABLE BIT(0)
#define FLASH_LED_MOD_ENABLE BIT(7)
#define FLASH_LED_DISABLE 0x00
#define FLASH_LED_SAFETY_TMR_DISABLED 0x13
@@ -65,11 +73,18 @@ enum flash_led_type {
FLASH_LED_TYPE_TORCH,
};
+enum {
+ LED1 = 0,
+ LED2,
+ LED3,
+};
+
/*
* Flash LED configuration read from device tree
*/
struct flash_led_platform_data {
u8 isc_delay_us;
+ u8 hw_strobe_option;
bool hdrm_auto_mode_en;
};
@@ -143,6 +158,41 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
return 0;
}
+static int qpnp_flash_led_hw_strobe_enable(struct flash_node_data *fnode,
+ int hw_strobe_option, bool on)
+{
+ int rc = 0;
+
+ /*
+ * If the LED controlled by this fnode is not GPIO controlled
+ * for the given strobe_option, return.
+ */
+ if (hw_strobe_option == FLASH_LED_HW_STROBE_OPTION_1)
+ return 0;
+ else if (hw_strobe_option == FLASH_LED_HW_STROBE_OPTION_2
+ && fnode->id != LED3)
+ return 0;
+ else if (hw_strobe_option == FLASH_LED_HW_STROBE_OPTION_3
+ && fnode->id == LED1)
+ return 0;
+
+ if (gpio_is_valid(fnode->hw_strobe_gpio)) {
+ gpio_set_value(fnode->hw_strobe_gpio, on ? 1 : 0);
+ } else if (fnode->hw_strobe_state_active &&
+ fnode->hw_strobe_state_suspend) {
+ rc = pinctrl_select_state(fnode->pinctrl,
+ on ? fnode->hw_strobe_state_active :
+ fnode->hw_strobe_state_suspend);
+ if (rc) {
+ dev_err(&fnode->pdev->dev,
+ "failed to change hw strobe pin state\n");
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
{
int prgm_current_ma = value;
@@ -176,6 +226,13 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
if (rc)
return rc;
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_STROBE_CFG(led->base),
+ FLASH_LED_ENABLE_MASK,
+ led->pdata->hw_strobe_option);
+ if (rc)
+ return rc;
+
val = 0;
for (i = 0; i < led->num_avail_leds; i++) {
if (!led->fnode[i].led_on)
@@ -184,7 +241,7 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
addr_offset = led->fnode[i].id;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_STROBE_CTRL(led->base + addr_offset),
- FLASH_LED_STROBE_CTRL_MASK, FLASH_LED_STROBE_ENABLE);
+ FLASH_LED_ENABLE_MASK, led->fnode[i].trigger);
if (rc)
return rc;
@@ -200,7 +257,7 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
if (rc)
return rc;
- val |= FLASH_LED_STROBE_ENABLE << led->fnode[i].id;
+ val |= FLASH_LED_ENABLE << led->fnode[i].id;
if (led->fnode[i].pinctrl) {
rc = pinctrl_select_state(led->fnode[i].pinctrl,
@@ -211,6 +268,16 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
return rc;
}
}
+
+ if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_MASK) {
+ rc = qpnp_flash_led_hw_strobe_enable(&led->fnode[i],
+ led->pdata->hw_strobe_option, true);
+ if (rc) {
+ dev_err(&led->pdev->dev,
+ "Unable to enable hw strobe\n");
+ return rc;
+ }
+ }
}
rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_MOD_CTRL(led->base),
@@ -219,8 +286,8 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
return rc;
rc = qpnp_flash_led_masked_write(led,
- FLASH_LED_REG_CHANNEL_CTRL(led->base),
- FLASH_LED_STROBE_CTRL_MASK, val);
+ FLASH_LED_EN_LED_CTRL(led->base),
+ FLASH_LED_ENABLE_MASK, val);
if (rc)
return rc;
@@ -228,8 +295,8 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
leds_turn_off:
rc = qpnp_flash_led_masked_write(led,
- FLASH_LED_REG_CHANNEL_CTRL(led->base),
- FLASH_LED_STROBE_CTRL_MASK, FLASH_LED_DISABLE);
+ FLASH_LED_EN_LED_CTRL(led->base),
+ FLASH_LED_ENABLE_MASK, FLASH_LED_DISABLE);
if (rc)
return rc;
@@ -260,6 +327,16 @@ leds_turn_off:
return rc;
}
}
+
+ if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_MASK) {
+ rc = qpnp_flash_led_hw_strobe_enable(&led->fnode[i],
+ led->pdata->hw_strobe_option, false);
+ if (rc) {
+ dev_err(&led->pdev->dev,
+ "Unable to disable hw strobe\n");
+ return rc;
+ }
+ }
}
return 0;
@@ -303,6 +380,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
const char *temp_string;
int rc;
u32 val;
+ bool strobe_sel = 0, edge_trigger = 0, active_high = 0;
fnode->pdev = led->pdev;
fnode->cdev.brightness_set = qpnp_flash_led_brightness_set;
@@ -429,6 +507,50 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
return rc;
}
+ strobe_sel = of_property_read_bool(node, "qcom,hw-strobe-sel");
+ if (strobe_sel) {
+ edge_trigger = of_property_read_bool(node,
+ "qcom,hw-strobe-edge-trigger");
+ active_high = !of_property_read_bool(node,
+ "qcom,hw-strobe-active-low");
+ }
+ fnode->trigger = (strobe_sel << 2) | (edge_trigger << 1) | active_high;
+
+ if (fnode->trigger & FLASH_LED_HW_SW_STROBE_SEL_MASK) {
+ if (of_find_property(node, "qcom,hw-strobe-gpio", NULL)) {
+ fnode->hw_strobe_gpio = of_get_named_gpio(node,
+ "qcom,hw-strobe-gpio", 0);
+ if (fnode->hw_strobe_gpio < 0) {
+ dev_err(&led->pdev->dev,
+ "Invalid gpio specified\n");
+ return fnode->hw_strobe_gpio;
+ }
+ gpio_direction_output(fnode->hw_strobe_gpio, 0);
+ } else {
+ fnode->hw_strobe_gpio = -1;
+ fnode->hw_strobe_state_active =
+ pinctrl_lookup_state(fnode->pinctrl,
+ "strobe_enable");
+ if (IS_ERR_OR_NULL(fnode->hw_strobe_state_active)) {
+ dev_err(&led->pdev->dev,
+ "No active pin for hardware strobe, rc=%ld\n",
+ PTR_ERR(fnode->hw_strobe_state_active));
+ fnode->hw_strobe_state_active = NULL;
+ }
+
+ fnode->hw_strobe_state_suspend =
+ pinctrl_lookup_state(fnode->pinctrl,
+ "strobe_disable");
+ if (IS_ERR_OR_NULL(fnode->hw_strobe_state_suspend)) {
+ dev_err(&led->pdev->dev,
+ "No suspend pin for hardware strobe, rc=%ld\n",
+ PTR_ERR(fnode->hw_strobe_state_suspend)
+ );
+ fnode->hw_strobe_state_suspend = NULL;
+ }
+ }
+ }
+
rc = led_classdev_register(&led->pdev->dev, &fnode->cdev);
if (rc) {
dev_err(&led->pdev->dev, "Unable to register led node %d\n",
@@ -517,6 +639,14 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return rc;
}
+ rc = of_property_read_u32(node, "qcom,hw-strobe-option", &val);
+ if (!rc) {
+ led->pdata->hw_strobe_option = (u8)val;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev, "Unable to parse hw strobe option\n");
+ return rc;
+ }
+
return 0;
}