leds: qpnp-flash-v2: Add support for LPG strobe
Flash LED3 can be configured for LPG strobe in addition to the
existing HW or SW strobe types. LPG strobe works the same way
as HW strobe except the strobing is controlled by LPG output
instead of a GPIO. To accommodate this, device tree property
"qcom,hw-strobe-sel" got renamed to "qcom,strobe-sel".
While at it, configure hardware strobe option only when the
option is specified through device tree.
Change-Id: I9351d7b7cd8c57ff7707a4ada6c5bdfa42772390
Signed-off-by: Subbaraman Narayanamurthy <subbaram@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
index da54fb1..176f9e1 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
@@ -169,8 +169,15 @@
sleep configuration defined for each pin or pin group.
- qcom,hw-strobe-gpio : phandle to specify GPIO for hardware strobing. This is used when there is no
pinctrl support or PMIC GPIOs are used.
-- qcom,hw-strobe-sel : Boolean property to enable hardware strobe. If not defined, software strobe
- will be used.
+- qcom,strobe-sel : Property to select strobe type. If not defined,
+ software strobe will be used. Allowed options are:
+ 0 - SW strobe
+ 1 - HW strobe
+ 2 - LPG strobe
+ LPG strobe is supported only for LED3.
+ If LPG strobe is specified, then strobe control is
+ configured for active high and level triggered. Also
+ qcom,hw-strobe-option should be set to 1 or 2.
- qcom,hw-strobe-edge-trigger : Boolean property to select trigger type. If defined, hw-strobe is set to
be edge triggered. Otherwise, it is level triggered.
- qcom,hw-strobe-active-low : Boolean property to select strobe signal polarity. If defined, hw-strobe
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index 096ad39..2678a00 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -63,11 +63,13 @@
#define FLASH_LED_REG_MITIGATION_SEL(base) (base + 0x6E)
#define FLASH_LED_REG_MITIGATION_SW(base) (base + 0x6F)
#define FLASH_LED_REG_LMH_LEVEL(base) (base + 0x70)
+#define FLASH_LED_REG_MULTI_STROBE_CTRL(base) (base + 0x71)
+#define FLASH_LED_REG_LPG_INPUT_CTRL(base) (base + 0x72)
#define FLASH_LED_REG_CURRENT_DERATE_EN(base) (base + 0x76)
#define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4)
#define FLASH_LED_CURRENT_MASK GENMASK(6, 0)
-#define FLASH_LED_ENABLE_MASK GENMASK(2, 0)
+#define FLASH_LED_STROBE_MASK GENMASK(1, 0)
#define FLASH_HW_STROBE_MASK GENMASK(2, 0)
#define FLASH_LED_ISC_WARMUP_DELAY_MASK GENMASK(1, 0)
#define FLASH_LED_CURRENT_DERATE_EN_MASK GENMASK(2, 0)
@@ -91,6 +93,9 @@
#define THERMAL_DERATE_SLOW_SHIFT 4
#define THERMAL_DERATE_SLOW_MASK GENMASK(6, 4)
#define THERMAL_DERATE_FAST_MASK GENMASK(2, 0)
+#define LED1N2_FLASH_ONCE_ONLY_BIT BIT(0)
+#define LED3_FLASH_ONCE_ONLY_BIT BIT(1)
+#define LPG_INPUT_SEL_BIT BIT(0)
#define VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us) (val_us / 8)
#define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25)
@@ -174,6 +179,12 @@
LED3,
};
+enum strobe_type {
+ SW_STROBE = 0,
+ HW_STROBE,
+ LPG_STROBE,
+};
+
/*
* Configurations for each individual LED
*/
@@ -194,7 +205,8 @@
u8 ires;
u8 hdrm_val;
u8 current_reg_val;
- u8 trigger;
+ u8 strobe_ctrl;
+ u8 strobe_sel;
bool led_on;
};
@@ -232,6 +244,7 @@
int thermal_thrsh1;
int thermal_thrsh2;
int thermal_thrsh3;
+ int hw_strobe_option;
u32 led1n2_iclamp_low_ma;
u32 led1n2_iclamp_mid_ma;
u32 led3_iclamp_low_ma;
@@ -246,7 +259,6 @@
u8 chgr_mitigation_sel;
u8 lmh_level;
u8 iled_thrsh_val;
- u8 hw_strobe_option;
bool hdrm_auto_mode_en;
bool thermal_derate_en;
bool otst_ramp_bkup_en;
@@ -557,6 +569,28 @@
return rc;
}
+ if (led->pdata->hw_strobe_option > 0) {
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_STROBE_CFG(led->base),
+ FLASH_LED_STROBE_MASK,
+ led->pdata->hw_strobe_option);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (led->fnode[LED3].strobe_sel == LPG_STROBE) {
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_MULTI_STROBE_CTRL(led->base),
+ LED3_FLASH_ONCE_ONLY_BIT, 0);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_LPG_INPUT_CTRL(led->base),
+ LPG_INPUT_SEL_BIT, LPG_INPUT_SEL_BIT);
+ if (rc < 0)
+ return rc;
+ }
return 0;
}
@@ -981,7 +1015,7 @@
led->fnode[i].led_on = false;
- if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT) {
+ if (led->fnode[i].strobe_sel == HW_STROBE) {
rc = qpnp_flash_led_hw_strobe_enable(&led->fnode[i],
led->pdata->hw_strobe_option, false);
if (rc < 0) {
@@ -1035,13 +1069,6 @@
if (rc < 0)
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 < 0)
- return rc;
-
val = 0;
for (i = 0; i < led->num_fnodes; i++) {
if (!led->fnode[i].led_on ||
@@ -1049,13 +1076,13 @@
continue;
addr_offset = led->fnode[i].id;
- if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT)
- mask = FLASH_HW_STROBE_MASK;
- else
+ if (led->fnode[i].strobe_sel == SW_STROBE)
mask = FLASH_LED_HW_SW_STROBE_SEL_BIT;
+ else
+ mask = FLASH_HW_STROBE_MASK;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_STROBE_CTRL(led->base + addr_offset),
- mask, led->fnode[i].trigger);
+ mask, led->fnode[i].strobe_ctrl);
if (rc < 0)
return rc;
@@ -1073,7 +1100,7 @@
val |= FLASH_LED_ENABLE << led->fnode[i].id;
- if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT) {
+ if (led->fnode[i].strobe_sel == HW_STROBE) {
rc = qpnp_flash_led_hw_strobe_enable(&led->fnode[i],
led->pdata->hw_strobe_option, true);
if (rc < 0) {
@@ -1365,7 +1392,7 @@
const char *temp_string;
int rc, min_ma;
u32 val;
- bool strobe_sel = 0, edge_trigger = 0, active_high = 0;
+ bool hw_strobe = 0, edge_trigger = 0, active_high = 0;
fnode->pdev = led->pdev;
fnode->cdev.brightness_set = qpnp_flash_led_brightness_set;
@@ -1484,14 +1511,52 @@
return rc;
}
- strobe_sel = of_property_read_bool(node, "qcom,hw-strobe-sel");
- if (strobe_sel) {
+ fnode->strobe_sel = SW_STROBE;
+ rc = of_property_read_u32(node, "qcom,strobe-sel", &val);
+ if (rc < 0) {
+ if (rc != -EINVAL) {
+ pr_err("Unable to read qcom,strobe-sel property\n");
+ return rc;
+ }
+ } else {
+ if (val < SW_STROBE || val > LPG_STROBE) {
+ pr_err("Incorrect strobe selection specified %d\n",
+ val);
+ return -EINVAL;
+ }
+ fnode->strobe_sel = (u8)val;
+ }
+
+ /*
+ * LPG strobe is allowed only for LED3 and HW strobe option should be
+ * option 2 or 3.
+ */
+ if (fnode->strobe_sel == LPG_STROBE) {
+ if (led->pdata->hw_strobe_option ==
+ FLASH_LED_HW_STROBE_OPTION_1) {
+ pr_err("Incorrect strobe option for LPG strobe\n");
+ return -EINVAL;
+ }
+ if (fnode->id != LED3) {
+ pr_err("Incorrect LED chosen for LPG strobe\n");
+ return -EINVAL;
+ }
+ }
+
+ if (fnode->strobe_sel == HW_STROBE) {
edge_trigger = of_property_read_bool(node,
"qcom,hw-strobe-edge-trigger");
active_high = !of_property_read_bool(node,
"qcom,hw-strobe-active-low");
+ hw_strobe = 1;
+ } else if (fnode->strobe_sel == LPG_STROBE) {
+ /* LPG strobe requires level trigger and active high */
+ edge_trigger = 0;
+ active_high = 1;
+ hw_strobe = 1;
}
- fnode->trigger = (strobe_sel << 2) | (edge_trigger << 1) | active_high;
+ fnode->strobe_ctrl = (hw_strobe << 2) | (edge_trigger << 1) |
+ active_high;
rc = led_classdev_register(&led->pdev->dev, &fnode->cdev);
if (rc < 0) {
@@ -1507,7 +1572,7 @@
fnode->strobe_pinctrl = NULL;
}
- if (fnode->trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT) {
+ if (fnode->strobe_sel == HW_STROBE) {
if (of_find_property(node, "qcom,hw-strobe-gpio", NULL)) {
fnode->hw_strobe_gpio = of_get_named_gpio(node,
"qcom,hw-strobe-gpio", 0);
@@ -1887,9 +1952,10 @@
led->pdata->vph_droop_hysteresis <<= FLASH_LED_VPH_DROOP_HYST_SHIFT;
+ led->pdata->hw_strobe_option = -EINVAL;
rc = of_property_read_u32(node, "qcom,hw-strobe-option", &val);
if (!rc) {
- led->pdata->hw_strobe_option = (u8)val;
+ led->pdata->hw_strobe_option = val;
} else if (rc != -EINVAL) {
pr_err("Unable to parse hw strobe option, rc=%d\n", rc);
return rc;