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;