Loading Documentation/devicetree/bindings/leds/backlight/qcom-spmi-wled.txt +22 −0 Original line number Diff line number Diff line Loading @@ -74,6 +74,26 @@ platforms. The PMIC is connected to the host processor via SPMI bus. Definition: Specify if cabc (content adaptive backlight control) is needed. - qcom,ext-pfet-sc-pro-en Usage: optional Value type: <bool> Definition: Specify if external PFET control for short circuit protection is needed. - interrupts Usage: optional Value type: <prop encoded array> Definition: Interrupts associated with WLED. Interrupts can be specified as per the encoding listed under Documentation/devicetree/bindings/spmi/ qcom,spmi-pmic-arb.txt. - interrupt-names Usage: optional Value type: <string> Definition: Interrupt names associated with the interrupts. Must be "sc-irq". Example: qcom-wled@d800 { Loading @@ -82,6 +102,8 @@ qcom-wled@d800 { reg-names = "wled-ctrl-base", "wled-sink-base"; label = "backlight"; interrupts = <0x3 0xd8 0x2 IRQ_TYPE_EDGE_RISING>; interrupt-names = "sc-irq"; qcom,fs-current-limit = <25000>; qcom,current-boost-limit = <970>; qcom,switching-freq = <800>; Loading drivers/video/backlight/qcom-spmi-wled.c +126 −14 Original line number Diff line number Diff line Loading @@ -14,6 +14,9 @@ #define pr_fmt(fmt) "WLED: %s: " fmt, __func__ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/ktime.h> #include <linux/kernel.h> #include <linux/backlight.h> #include <linux/module.h> Loading @@ -27,6 +30,8 @@ #define WLED_MAX_BRIGHTNESS 4095 /* WLED control registers */ #define WLED_CTRL_FAULT_STATUS 0x08 #define WLED_CTRL_MOD_ENABLE 0x46 #define WLED_CTRL_MOD_EN_MASK BIT(7) #define WLED_CTRL_MODULE_EN_SHIFT 7 Loading @@ -40,6 +45,15 @@ #define WLED_CTRL_ILIM 0x4e #define WLED_CTRL_ILIM_MASK GENMASK(2, 0) #define WLED_CTRL_SHORT_PROTECT 0x5e #define WLED_CTRL_SHORT_EN_MASK BIT(7) #define WLED_CTRL_SEC_ACCESS 0xd0 #define WLED_CTRL_SEC_UNLOCK 0xa5 #define WLED_CTRL_TEST1 0xe2 #define WLED_EXT_FET_DTEST2 0x09 /* WLED sink registers */ #define WLED_SINK_CURR_SINK_EN 0x46 #define WLED_SINK_CURR_SINK_MASK GENMASK(7, 4) Loading Loading @@ -74,19 +88,23 @@ struct wled_config { u32 switch_freq; u32 fs_current; u32 string_cfg; int sc_irq; bool en_cabc; bool ext_pfet_sc_pro_en; }; struct wled { const char *name; struct platform_device *pdev; struct regmap *regmap; struct mutex lock; struct wled_config cfg; ktime_t last_sc_event_time; u16 sink_addr; u16 ctrl_addr; u32 brightness; u32 sc_count; bool prev_state; struct wled_config cfg; }; static int wled_module_enable(struct wled *wled, int val) Loading Loading @@ -160,25 +178,26 @@ static int wled_update_status(struct backlight_device *bl) bl->props.state & BL_CORE_FBBLANK) brightness = 0; mutex_lock(&wled->lock); if (brightness) { rc = wled_set_brightness(wled, brightness); if (rc < 0) { pr_err("wled failed to set brightness rc:%d\n", rc); return rc; goto unlock_mutex; } if (!!brightness != wled->prev_state) { rc = wled_module_enable(wled, !!brightness); if (rc < 0) { pr_err("wled enable failed rc:%d\n", rc); return rc; goto unlock_mutex; } } } else { rc = wled_module_enable(wled, brightness); if (rc < 0) { pr_err("wled disable failed rc:%d\n", rc); return rc; goto unlock_mutex; } } Loading @@ -187,19 +206,72 @@ static int wled_update_status(struct backlight_device *bl) rc = wled_sync_toggle(wled); if (rc < 0) { pr_err("wled sync failed rc:%d\n", rc); return rc; goto unlock_mutex; } wled->brightness = brightness; unlock_mutex: mutex_unlock(&wled->lock); return rc; } #define WLED_SC_DLY_MS 20 #define WLED_SC_CNT_MAX 5 #define WLED_SC_RESET_CNT_DLY_US 1000000 static irqreturn_t wled_sc_irq_handler(int irq, void *_wled) { struct wled *wled = _wled; int rc; u32 val; s64 elapsed_time; rc = regmap_read(wled->regmap, wled->ctrl_addr + WLED_CTRL_FAULT_STATUS, &val); if (rc < 0) { pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc); return IRQ_HANDLED; } wled->sc_count++; pr_err("WLED short circuit detected %d times fault_status=%x\n", wled->sc_count, val); mutex_lock(&wled->lock); rc = wled_module_enable(wled, false); if (rc < 0) { pr_err("wled disable failed rc:%d\n", rc); goto unlock_mutex; } elapsed_time = ktime_us_delta(ktime_get(), wled->last_sc_event_time); if (elapsed_time > WLED_SC_RESET_CNT_DLY_US) { wled->sc_count = 0; } else if (wled->sc_count > WLED_SC_CNT_MAX) { pr_err("SC trigged %d times, disabling WLED forever!\n", wled->sc_count); goto unlock_mutex; } wled->last_sc_event_time = ktime_get(); msleep(WLED_SC_DLY_MS); rc = wled_module_enable(wled, true); if (rc < 0) pr_err("wled enable failed rc:%d\n", rc); unlock_mutex: mutex_unlock(&wled->lock); return IRQ_HANDLED; } static int wled_setup(struct wled *wled) { int rc, temp, i; u8 sink_en = 0; u8 string_cfg = wled->cfg.string_cfg; int sc_irq = wled->cfg.sc_irq; rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + WLED_CTRL_OVP, Loading Loading @@ -264,6 +336,39 @@ static int wled_setup(struct wled *wled) return rc; } if (sc_irq >= 0) { rc = devm_request_threaded_irq(&wled->pdev->dev, sc_irq, NULL, wled_sc_irq_handler, IRQF_ONESHOT, "wled_sc_irq", wled); if (rc < 0) { pr_err("Unable to request sc(%d) IRQ(err:%d)\n", sc_irq, rc); return rc; } rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + WLED_CTRL_SHORT_PROTECT, WLED_CTRL_SHORT_EN_MASK, WLED_CTRL_SHORT_EN_MASK); if (rc < 0) return rc; } if (wled->cfg.ext_pfet_sc_pro_en) { /* unlock the secure access register */ rc = regmap_write(wled->regmap, wled->ctrl_addr + WLED_CTRL_SEC_ACCESS, WLED_CTRL_SEC_UNLOCK); if (rc < 0) return rc; rc = regmap_write(wled->regmap, wled->ctrl_addr + WLED_CTRL_TEST1, WLED_EXT_FET_DTEST2); if (rc < 0) return rc; } return 0; } Loading @@ -274,6 +379,7 @@ static const struct wled_config wled_config_defaults = { .switch_freq = 11, .string_cfg = 0xf, .en_cabc = 0, .ext_pfet_sc_pro_en = 1, }; struct wled_var_cfg { Loading Loading @@ -379,6 +485,7 @@ static int wled_configure(struct wled *wled, struct device *dev) bool *val_ptr; } bool_opts[] = { { "qcom,en-cabc", &cfg->en_cabc, }, { "qcom,ext-pfet-sc-pro", &cfg->ext_pfet_sc_pro_en, }, }; prop_addr = of_get_address(dev->of_node, 0, NULL, NULL); Loading Loading @@ -430,6 +537,10 @@ static int wled_configure(struct wled *wled, struct device *dev) *bool_opts[i].val_ptr = true; } wled->cfg.sc_irq = platform_get_irq_byname(wled->pdev, "sc-irq"); if (wled->cfg.sc_irq < 0) dev_dbg(&wled->pdev->dev, "sc irq is not used\n"); return 0; } Loading Loading @@ -472,6 +583,7 @@ static int wled_probe(struct platform_device *pdev) return rc; } mutex_init(&wled->lock); val = WLED_DEFAULT_BRIGHTNESS; of_property_read_u32(pdev->dev.of_node, "default-brightness", &val); wled->brightness = val; Loading Loading
Documentation/devicetree/bindings/leds/backlight/qcom-spmi-wled.txt +22 −0 Original line number Diff line number Diff line Loading @@ -74,6 +74,26 @@ platforms. The PMIC is connected to the host processor via SPMI bus. Definition: Specify if cabc (content adaptive backlight control) is needed. - qcom,ext-pfet-sc-pro-en Usage: optional Value type: <bool> Definition: Specify if external PFET control for short circuit protection is needed. - interrupts Usage: optional Value type: <prop encoded array> Definition: Interrupts associated with WLED. Interrupts can be specified as per the encoding listed under Documentation/devicetree/bindings/spmi/ qcom,spmi-pmic-arb.txt. - interrupt-names Usage: optional Value type: <string> Definition: Interrupt names associated with the interrupts. Must be "sc-irq". Example: qcom-wled@d800 { Loading @@ -82,6 +102,8 @@ qcom-wled@d800 { reg-names = "wled-ctrl-base", "wled-sink-base"; label = "backlight"; interrupts = <0x3 0xd8 0x2 IRQ_TYPE_EDGE_RISING>; interrupt-names = "sc-irq"; qcom,fs-current-limit = <25000>; qcom,current-boost-limit = <970>; qcom,switching-freq = <800>; Loading
drivers/video/backlight/qcom-spmi-wled.c +126 −14 Original line number Diff line number Diff line Loading @@ -14,6 +14,9 @@ #define pr_fmt(fmt) "WLED: %s: " fmt, __func__ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/ktime.h> #include <linux/kernel.h> #include <linux/backlight.h> #include <linux/module.h> Loading @@ -27,6 +30,8 @@ #define WLED_MAX_BRIGHTNESS 4095 /* WLED control registers */ #define WLED_CTRL_FAULT_STATUS 0x08 #define WLED_CTRL_MOD_ENABLE 0x46 #define WLED_CTRL_MOD_EN_MASK BIT(7) #define WLED_CTRL_MODULE_EN_SHIFT 7 Loading @@ -40,6 +45,15 @@ #define WLED_CTRL_ILIM 0x4e #define WLED_CTRL_ILIM_MASK GENMASK(2, 0) #define WLED_CTRL_SHORT_PROTECT 0x5e #define WLED_CTRL_SHORT_EN_MASK BIT(7) #define WLED_CTRL_SEC_ACCESS 0xd0 #define WLED_CTRL_SEC_UNLOCK 0xa5 #define WLED_CTRL_TEST1 0xe2 #define WLED_EXT_FET_DTEST2 0x09 /* WLED sink registers */ #define WLED_SINK_CURR_SINK_EN 0x46 #define WLED_SINK_CURR_SINK_MASK GENMASK(7, 4) Loading Loading @@ -74,19 +88,23 @@ struct wled_config { u32 switch_freq; u32 fs_current; u32 string_cfg; int sc_irq; bool en_cabc; bool ext_pfet_sc_pro_en; }; struct wled { const char *name; struct platform_device *pdev; struct regmap *regmap; struct mutex lock; struct wled_config cfg; ktime_t last_sc_event_time; u16 sink_addr; u16 ctrl_addr; u32 brightness; u32 sc_count; bool prev_state; struct wled_config cfg; }; static int wled_module_enable(struct wled *wled, int val) Loading Loading @@ -160,25 +178,26 @@ static int wled_update_status(struct backlight_device *bl) bl->props.state & BL_CORE_FBBLANK) brightness = 0; mutex_lock(&wled->lock); if (brightness) { rc = wled_set_brightness(wled, brightness); if (rc < 0) { pr_err("wled failed to set brightness rc:%d\n", rc); return rc; goto unlock_mutex; } if (!!brightness != wled->prev_state) { rc = wled_module_enable(wled, !!brightness); if (rc < 0) { pr_err("wled enable failed rc:%d\n", rc); return rc; goto unlock_mutex; } } } else { rc = wled_module_enable(wled, brightness); if (rc < 0) { pr_err("wled disable failed rc:%d\n", rc); return rc; goto unlock_mutex; } } Loading @@ -187,19 +206,72 @@ static int wled_update_status(struct backlight_device *bl) rc = wled_sync_toggle(wled); if (rc < 0) { pr_err("wled sync failed rc:%d\n", rc); return rc; goto unlock_mutex; } wled->brightness = brightness; unlock_mutex: mutex_unlock(&wled->lock); return rc; } #define WLED_SC_DLY_MS 20 #define WLED_SC_CNT_MAX 5 #define WLED_SC_RESET_CNT_DLY_US 1000000 static irqreturn_t wled_sc_irq_handler(int irq, void *_wled) { struct wled *wled = _wled; int rc; u32 val; s64 elapsed_time; rc = regmap_read(wled->regmap, wled->ctrl_addr + WLED_CTRL_FAULT_STATUS, &val); if (rc < 0) { pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc); return IRQ_HANDLED; } wled->sc_count++; pr_err("WLED short circuit detected %d times fault_status=%x\n", wled->sc_count, val); mutex_lock(&wled->lock); rc = wled_module_enable(wled, false); if (rc < 0) { pr_err("wled disable failed rc:%d\n", rc); goto unlock_mutex; } elapsed_time = ktime_us_delta(ktime_get(), wled->last_sc_event_time); if (elapsed_time > WLED_SC_RESET_CNT_DLY_US) { wled->sc_count = 0; } else if (wled->sc_count > WLED_SC_CNT_MAX) { pr_err("SC trigged %d times, disabling WLED forever!\n", wled->sc_count); goto unlock_mutex; } wled->last_sc_event_time = ktime_get(); msleep(WLED_SC_DLY_MS); rc = wled_module_enable(wled, true); if (rc < 0) pr_err("wled enable failed rc:%d\n", rc); unlock_mutex: mutex_unlock(&wled->lock); return IRQ_HANDLED; } static int wled_setup(struct wled *wled) { int rc, temp, i; u8 sink_en = 0; u8 string_cfg = wled->cfg.string_cfg; int sc_irq = wled->cfg.sc_irq; rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + WLED_CTRL_OVP, Loading Loading @@ -264,6 +336,39 @@ static int wled_setup(struct wled *wled) return rc; } if (sc_irq >= 0) { rc = devm_request_threaded_irq(&wled->pdev->dev, sc_irq, NULL, wled_sc_irq_handler, IRQF_ONESHOT, "wled_sc_irq", wled); if (rc < 0) { pr_err("Unable to request sc(%d) IRQ(err:%d)\n", sc_irq, rc); return rc; } rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + WLED_CTRL_SHORT_PROTECT, WLED_CTRL_SHORT_EN_MASK, WLED_CTRL_SHORT_EN_MASK); if (rc < 0) return rc; } if (wled->cfg.ext_pfet_sc_pro_en) { /* unlock the secure access register */ rc = regmap_write(wled->regmap, wled->ctrl_addr + WLED_CTRL_SEC_ACCESS, WLED_CTRL_SEC_UNLOCK); if (rc < 0) return rc; rc = regmap_write(wled->regmap, wled->ctrl_addr + WLED_CTRL_TEST1, WLED_EXT_FET_DTEST2); if (rc < 0) return rc; } return 0; } Loading @@ -274,6 +379,7 @@ static const struct wled_config wled_config_defaults = { .switch_freq = 11, .string_cfg = 0xf, .en_cabc = 0, .ext_pfet_sc_pro_en = 1, }; struct wled_var_cfg { Loading Loading @@ -379,6 +485,7 @@ static int wled_configure(struct wled *wled, struct device *dev) bool *val_ptr; } bool_opts[] = { { "qcom,en-cabc", &cfg->en_cabc, }, { "qcom,ext-pfet-sc-pro", &cfg->ext_pfet_sc_pro_en, }, }; prop_addr = of_get_address(dev->of_node, 0, NULL, NULL); Loading Loading @@ -430,6 +537,10 @@ static int wled_configure(struct wled *wled, struct device *dev) *bool_opts[i].val_ptr = true; } wled->cfg.sc_irq = platform_get_irq_byname(wled->pdev, "sc-irq"); if (wled->cfg.sc_irq < 0) dev_dbg(&wled->pdev->dev, "sc irq is not used\n"); return 0; } Loading Loading @@ -472,6 +583,7 @@ static int wled_probe(struct platform_device *pdev) return rc; } mutex_init(&wled->lock); val = WLED_DEFAULT_BRIGHTNESS; of_property_read_u32(pdev->dev.of_node, "default-brightness", &val); wled->brightness = val; Loading