From 9f1c584215db165872cdf60129fc20843477a508 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Tue, 21 Mar 2017 20:49:54 -0700 Subject: [PATCH] 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 --- .../bindings/leds/leds-qpnp-flash-v2.txt | 11 +- drivers/leds/leds-qpnp-flash-v2.c | 110 ++++++++++++++---- 2 files changed, 97 insertions(+), 24 deletions(-) diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt index da54fb11ffd4..176f9e115b42 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 @@ Optional properties: 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 1a6cad946a25..a89a92d253ac 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 @@ enum { LED3, }; +enum strobe_type { + SW_STROBE = 0, + HW_STROBE, + LPG_STROBE, +}; + /* * Configurations for each individual LED */ @@ -194,7 +205,8 @@ struct flash_node_data { u8 ires; u8 hdrm_val; u8 current_reg_val; - u8 trigger; + u8 strobe_ctrl; + u8 strobe_sel; bool led_on; }; @@ -232,6 +244,7 @@ struct flash_led_platform_data { 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 @@ struct flash_led_platform_data { 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 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) 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; } @@ -980,7 +1014,7 @@ static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode) 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) { @@ -1034,13 +1068,6 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) 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 || @@ -1048,13 +1075,13 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) 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; @@ -1072,7 +1099,7 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) 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) { @@ -1364,7 +1391,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, 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; @@ -1483,14 +1510,52 @@ 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) { + 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) { @@ -1506,7 +1571,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, 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); @@ -1886,9 +1951,10 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, 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;