Message ID | 1525341432-15818-6-git-send-email-kgunda@codeaurora.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Thu 03 May 02:57 PDT 2018, Kiran Gunda wrote: [..] > + > +#define WLED_AUTO_DETECT_OVP_COUNT 5 > +#define WLED_AUTO_DETECT_CNT_DLY_US HZ /* 1 second */ > +static bool wled_auto_detection_required(struct wled *wled) So cfg.auto_detection_enabled is set, but we didn't have a fault during wled_auto_detection_at_init(), which I presume indicates that the boot loader configured the strings appropriately (or didn't enable the BL). Then first time we try to enable the backlight we will hit the ovp irq, which will enter here a few times to figure out that the strings are incorrectly configured and then we will do the same thing that would have been done if we probed with a fault. This is convoluted! If auto-detection is a feature allowing the developer to omit the string configuration then just do the auto detection explicitly in probe when the developer did so and then never do it again. > +{ > + s64 elapsed_time_us; > + > + if (*wled->version == WLED_PM8941) > + return false; > + /* > + * Check if the OVP fault was an occasional one > + * or if it's firing continuously, the latter qualifies > + * for an auto-detection check. > + */ > + if (!wled->auto_detection_ovp_count) { > + wled->start_ovp_fault_time = ktime_get(); > + wled->auto_detection_ovp_count++; > + } else { > + elapsed_time_us = ktime_us_delta(ktime_get(), > + wled->start_ovp_fault_time); > + if (elapsed_time_us > WLED_AUTO_DETECT_CNT_DLY_US) > + wled->auto_detection_ovp_count = 0; > + else > + wled->auto_detection_ovp_count++; > + > + if (wled->auto_detection_ovp_count >= > + WLED_AUTO_DETECT_OVP_COUNT) { > + wled->auto_detection_ovp_count = 0; > + return true; > + } > + } > + > + return false; > +} > + > +static int wled_auto_detection_at_init(struct wled *wled) > +{ > + int rc; > + u32 fault_status = 0, rt_status = 0; > + > + if (*wled->version == WLED_PM8941) > + return 0; cfg.auto_detection_enabled will be false in this case, so there's no need for the extra check. > + > + if (!wled->cfg.auto_detection_enabled) > + return 0; > + > + rc = regmap_read(wled->regmap, > + wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, > + &rt_status); > + if (rc < 0) { > + pr_err("Failed to read RT status rc=%d\n", rc); > + return rc; > + } > + > + rc = regmap_read(wled->regmap, > + wled->ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS, > + &fault_status); > + if (rc < 0) { > + pr_err("Failed to read fault status rc=%d\n", rc); > + return rc; > + } > + > + if ((rt_status & WLED3_CTRL_REG_OVP_FAULT_STATUS) || > + (fault_status & WLED3_CTRL_REG_OVP_FAULT_BIT)) { So this would only happen if the boot loader set an invalid string configuration, as we have yet to enable the module here? > + mutex_lock(&wled->lock); > + rc = wled_auto_string_detection(wled); > + if (!rc) > + wled->auto_detection_done = true; > + mutex_unlock(&wled->lock); > + } > + > + return rc; > +} > + > +static void handle_ovp_fault(struct wled *wled) > +{ > + if (!wled->cfg.auto_detection_enabled) As this is the only reason for requesting the ovp_irq, how about just not requesting it in this case? > + return; > + > + mutex_lock(&wled->lock); > + if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) { The logic here is unnecessary, the only way handle_ovp_fault() is ever executed is if wled_ovp_irq_handler() was called, which is a really good indication that ovp_irq is valid and !ovp_irq_disabled. So this is always going to be entered. > + disable_irq_nosync(wled->ovp_irq); > + wled->ovp_irq_disabled = true; > + } > + > + if (wled_auto_detection_required(wled)) > + wled_auto_string_detection(wled); > + > + if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) { Again, ovp_irq is valid and we entered the function with either ovp_irq_disabled = true due to some bug or it was set to true above. So this check is useless - which renders ovp_irq_disabled unnecessary as well. > + enable_irq(wled->ovp_irq); > + wled->ovp_irq_disabled = false; > + } > + mutex_unlock(&wled->lock); > +} > + > static irqreturn_t wled_ovp_irq_handler(int irq, void *_wled) > { > struct wled *wled = _wled; > @@ -413,6 +706,9 @@ static irqreturn_t wled_ovp_irq_handler(int irq, void *_wled) > dev_dbg(wled->dev, "WLED OVP fault detected, int_sts=%x fault_sts= %x\n", > int_sts, fault_sts); > > + if (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT) > + handle_ovp_fault(wled); Just inline handle_ovp_fault() here and make things less "generic". > + > return IRQ_HANDLED; > } > > @@ -575,6 +871,10 @@ static int wled4_setup(struct wled *wled) > return rc; > } > > + rc = wled_auto_detection_at_init(wled); > + if (rc < 0) > + return rc; > + > if (wled->cfg.external_pfet) { > /* Unlock the secure register access */ > rc = regmap_write(wled->regmap, wled->ctrl_addr + > @@ -602,6 +902,7 @@ static int wled4_setup(struct wled *wled) > .enabled_strings = 0xf, > .cabc = false, > .external_pfet = true, > + .auto_detection_enabled = false, > }; > > static const u32 wled3_boost_i_limit_values[] = { > @@ -785,6 +1086,7 @@ static int wled_configure(struct wled *wled) > { "qcom,ext-gen", &cfg->ext_gen, }, > { "qcom,cabc", &cfg->cabc, }, > { "qcom,external-pfet", &cfg->external_pfet, }, > + { "qcom,auto-string-detection", &cfg->auto_detection_enabled, }, > }; So afaict the auto detect logic is triggered by two things: * Boot loader enabled backlight with an invalid string configuration, which will make wled_auto_detection_at_init() do the detection. * Once we the driver tries to enable the module, ovp faults will start arriving and we will trigger the auto detection. But I think you can integrate this in a much more direct way. If the module is enabled and there are no faults you should be able to just read the config from the hardware (if auto detect is enabled!) and if the module is not enabled you can just call auto detect from probe(). This will give you flicker free "auto detection" in the event that the boot loader did its job and very clean logic in the other cases. Regards, Bjorn -- To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 2018-05-07 23:40, Bjorn Andersson wrote: > On Thu 03 May 02:57 PDT 2018, Kiran Gunda wrote: > > [..] >> + >> +#define WLED_AUTO_DETECT_OVP_COUNT 5 >> +#define WLED_AUTO_DETECT_CNT_DLY_US HZ /* 1 second */ >> +static bool wled_auto_detection_required(struct wled *wled) > > So cfg.auto_detection_enabled is set, but we didn't have a fault during > wled_auto_detection_at_init(), which I presume indicates that the boot > loader configured the strings appropriately (or didn't enable the BL). > Then first time we try to enable the backlight we will hit the ovp irq, > which will enter here a few times to figure out that the strings are > incorrectly configured and then we will do the same thing that would > have been done if we probed with a fault. > > This is convoluted! > > If auto-detection is a feature allowing the developer to omit the > string > configuration then just do the auto detection explicitly in probe when > the developer did so and then never do it again. > As explained in the previous patch, the auto-detection is needed later, because are also cases where one/more of the connected LED string of the display-backlight is malfunctioning (because of damage) and requires the damaged string to be turned off to prevent the complete panel and/or board from being damaged. >> +{ >> + s64 elapsed_time_us; >> + >> + if (*wled->version == WLED_PM8941) >> + return false; >> + /* >> + * Check if the OVP fault was an occasional one >> + * or if it's firing continuously, the latter qualifies >> + * for an auto-detection check. >> + */ >> + if (!wled->auto_detection_ovp_count) { >> + wled->start_ovp_fault_time = ktime_get(); >> + wled->auto_detection_ovp_count++; >> + } else { >> + elapsed_time_us = ktime_us_delta(ktime_get(), >> + wled->start_ovp_fault_time); >> + if (elapsed_time_us > WLED_AUTO_DETECT_CNT_DLY_US) >> + wled->auto_detection_ovp_count = 0; >> + else >> + wled->auto_detection_ovp_count++; >> + >> + if (wled->auto_detection_ovp_count >= >> + WLED_AUTO_DETECT_OVP_COUNT) { >> + wled->auto_detection_ovp_count = 0; >> + return true; >> + } >> + } >> + >> + return false; >> +} >> + >> +static int wled_auto_detection_at_init(struct wled *wled) >> +{ >> + int rc; >> + u32 fault_status = 0, rt_status = 0; >> + >> + if (*wled->version == WLED_PM8941) >> + return 0; > > cfg.auto_detection_enabled will be false in this case, so there's no > need for the extra check. > Ok. I will remove it in the next series. >> + >> + if (!wled->cfg.auto_detection_enabled) >> + return 0; >> + >> + rc = regmap_read(wled->regmap, >> + wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, >> + &rt_status); >> + if (rc < 0) { >> + pr_err("Failed to read RT status rc=%d\n", rc); >> + return rc; >> + } >> + >> + rc = regmap_read(wled->regmap, >> + wled->ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS, >> + &fault_status); >> + if (rc < 0) { >> + pr_err("Failed to read fault status rc=%d\n", rc); >> + return rc; >> + } >> + >> + if ((rt_status & WLED3_CTRL_REG_OVP_FAULT_STATUS) || >> + (fault_status & WLED3_CTRL_REG_OVP_FAULT_BIT)) { > > So this would only happen if the boot loader set an invalid string > configuration, as we have yet to enable the module here? > Yes. >> + mutex_lock(&wled->lock); >> + rc = wled_auto_string_detection(wled); >> + if (!rc) >> + wled->auto_detection_done = true; >> + mutex_unlock(&wled->lock); >> + } >> + >> + return rc; >> +} >> + >> +static void handle_ovp_fault(struct wled *wled) >> +{ >> + if (!wled->cfg.auto_detection_enabled) > > As this is the only reason for requesting the ovp_irq, how about just > not requesting it in this case? > This is also needed for information purpose if there is any other reason and also discussing with HW systems what needs to do if the OVP keep on triggering. >> + return; >> + >> + mutex_lock(&wled->lock); >> + if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) { > > The logic here is unnecessary, the only way handle_ovp_fault() is ever > executed is if wled_ovp_irq_handler() was called, which is a really > good > indication that ovp_irq is valid and !ovp_irq_disabled. So this is > always going to be entered. > Ok. I will remove this logic in the next series. >> + disable_irq_nosync(wled->ovp_irq); >> + wled->ovp_irq_disabled = true; >> + } >> + >> + if (wled_auto_detection_required(wled)) >> + wled_auto_string_detection(wled); >> + >> + if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) { > > Again, ovp_irq is valid and we entered the function with either > ovp_irq_disabled = true due to some bug or it was set to true above. So > this check is useless - which renders ovp_irq_disabled unnecessary as > well. > Ok. I will remove it in the next series. >> + enable_irq(wled->ovp_irq); >> + wled->ovp_irq_disabled = false; >> + } >> + mutex_unlock(&wled->lock); >> +} >> + >> static irqreturn_t wled_ovp_irq_handler(int irq, void *_wled) >> { >> struct wled *wled = _wled; >> @@ -413,6 +706,9 @@ static irqreturn_t wled_ovp_irq_handler(int irq, >> void *_wled) >> dev_dbg(wled->dev, "WLED OVP fault detected, int_sts=%x fault_sts= >> %x\n", >> int_sts, fault_sts); >> >> + if (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT) >> + handle_ovp_fault(wled); > > Just inline handle_ovp_fault() here and make things less "generic". > Sure. Will do it in the next series. >> + >> return IRQ_HANDLED; >> } >> >> @@ -575,6 +871,10 @@ static int wled4_setup(struct wled *wled) >> return rc; >> } >> >> + rc = wled_auto_detection_at_init(wled); >> + if (rc < 0) >> + return rc; >> + >> if (wled->cfg.external_pfet) { >> /* Unlock the secure register access */ >> rc = regmap_write(wled->regmap, wled->ctrl_addr + >> @@ -602,6 +902,7 @@ static int wled4_setup(struct wled *wled) >> .enabled_strings = 0xf, >> .cabc = false, >> .external_pfet = true, >> + .auto_detection_enabled = false, >> }; >> >> static const u32 wled3_boost_i_limit_values[] = { >> @@ -785,6 +1086,7 @@ static int wled_configure(struct wled *wled) >> { "qcom,ext-gen", &cfg->ext_gen, }, >> { "qcom,cabc", &cfg->cabc, }, >> { "qcom,external-pfet", &cfg->external_pfet, }, >> + { "qcom,auto-string-detection", &cfg->auto_detection_enabled, }, >> }; > > So afaict the auto detect logic is triggered by two things: > > * Boot loader enabled backlight with an invalid string configuration, > which will make wled_auto_detection_at_init() do the detection. > > * Once we the driver tries to enable the module, ovp faults will start > arriving and we will trigger the auto detection. > > But I think you can integrate this in a much more direct way. If the > module is enabled and there are no faults you should be able to just > read the config from the hardware (if auto detect is enabled!) and if > the module is not enabled you can just call auto detect from probe(). > > This will give you flicker free "auto detection" in the event that the > boot loader did its job and very clean logic in the other cases. > Sure. I will improve this logic in the next series. > Regards, > Bjorn > -- > To unsubscribe from this list: send the line "unsubscribe > linux-arm-msm" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed 09 May 00:14 PDT 2018, kgunda@codeaurora.org wrote: > On 2018-05-07 23:40, Bjorn Andersson wrote: > > On Thu 03 May 02:57 PDT 2018, Kiran Gunda wrote: > > > > [..] > > > + > > > +#define WLED_AUTO_DETECT_OVP_COUNT 5 > > > +#define WLED_AUTO_DETECT_CNT_DLY_US HZ /* 1 second */ > > > +static bool wled_auto_detection_required(struct wled *wled) > > > > So cfg.auto_detection_enabled is set, but we didn't have a fault during > > wled_auto_detection_at_init(), which I presume indicates that the boot > > loader configured the strings appropriately (or didn't enable the BL). > > Then first time we try to enable the backlight we will hit the ovp irq, > > which will enter here a few times to figure out that the strings are > > incorrectly configured and then we will do the same thing that would > > have been done if we probed with a fault. > > > > This is convoluted! > > > > If auto-detection is a feature allowing the developer to omit the string > > configuration then just do the auto detection explicitly in probe when > > the developer did so and then never do it again. > > > As explained in the previous patch, the auto-detection is needed later, > because are also cases where one/more of the connected LED string of the > display-backlight is malfunctioning (because of damage) and requires the > damaged string to be turned off to prevent the complete panel and/or board > from being damaged. Okay, that sounds very reasonable. Please ensure that it's clearly described in the commit message, so that we have this documented if someone wonders in the future. Regards, Bjorn -- To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 2018-05-14 22:32, Bjorn Andersson wrote: > On Wed 09 May 00:14 PDT 2018, kgunda@codeaurora.org wrote: > >> On 2018-05-07 23:40, Bjorn Andersson wrote: >> > On Thu 03 May 02:57 PDT 2018, Kiran Gunda wrote: >> > >> > [..] >> > > + >> > > +#define WLED_AUTO_DETECT_OVP_COUNT 5 >> > > +#define WLED_AUTO_DETECT_CNT_DLY_US HZ /* 1 second */ >> > > +static bool wled_auto_detection_required(struct wled *wled) >> > >> > So cfg.auto_detection_enabled is set, but we didn't have a fault during >> > wled_auto_detection_at_init(), which I presume indicates that the boot >> > loader configured the strings appropriately (or didn't enable the BL). >> > Then first time we try to enable the backlight we will hit the ovp irq, >> > which will enter here a few times to figure out that the strings are >> > incorrectly configured and then we will do the same thing that would >> > have been done if we probed with a fault. >> > >> > This is convoluted! >> > >> > If auto-detection is a feature allowing the developer to omit the string >> > configuration then just do the auto detection explicitly in probe when >> > the developer did so and then never do it again. >> > >> As explained in the previous patch, the auto-detection is needed >> later, >> because are also cases where one/more of the connected LED string of >> the >> display-backlight is malfunctioning (because of damage) and requires >> the >> damaged string to be turned off to prevent the complete panel and/or >> board >> from being damaged. > > Okay, that sounds very reasonable. Please ensure that it's clearly > described in the commit message, so that we have this documented if > someone wonders in the future. > > Regards, > Bjorn > -- Thanks for that ! Sure I will describe it in the commit message. > To unsubscribe from this list: send the line "unsubscribe > linux-arm-msm" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c index 80ae084..bacdf3f 100644 --- a/drivers/video/backlight/qcom-wled.c +++ b/drivers/video/backlight/qcom-wled.c @@ -33,11 +33,14 @@ #define WLED4_CTRL_REG_SC_FAULT_BIT BIT(2) #define WLED3_CTRL_REG_INT_RT_STS 0x10 +#define WLED3_CTRL_REG_OVP_FAULT_STATUS BIT(1) #define WLED3_CTRL_REG_MOD_EN 0x46 #define WLED3_CTRL_REG_MOD_EN_MASK BIT(7) #define WLED3_CTRL_REG_MOD_EN_BIT BIT(7) +#define WLED3_CTRL_REG_FEEDBACK_CONTROL 0x48 + #define WLED3_CTRL_REG_FREQ 0x4c #define WLED3_CTRL_REG_FREQ_MASK GENMASK(3, 0) @@ -152,6 +155,7 @@ struct wled_config { bool ext_gen; bool cabc; bool external_pfet; + bool auto_detection_enabled; }; struct wled { @@ -160,8 +164,10 @@ struct wled { struct regmap *regmap; struct mutex lock; /* Lock to avoid race from ISR */ ktime_t last_short_event; + ktime_t start_ovp_fault_time; u16 ctrl_addr; u16 sink_addr; + u16 auto_detection_ovp_count; u32 brightness; u32 max_brightness; u32 short_count; @@ -170,6 +176,7 @@ struct wled { int ovp_irq; bool force_mod_disable; bool ovp_irq_disabled; + bool auto_detection_done; struct wled_config cfg; struct delayed_work ovp_work; @@ -386,6 +393,292 @@ static irqreturn_t wled_short_irq_handler(int irq, void *_wled) return IRQ_HANDLED; } +#define AUTO_DETECT_BRIGHTNESS 200 +static int wled_auto_string_detection(struct wled *wled) +{ + int rc = 0, i; + u32 sink_config = 0, int_sts; + u8 sink_test = 0, sink_valid = 0, val; + + if (wled->auto_detection_done) + return 0; + + /* read configured sink configuration */ + rc = regmap_read(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_CURR_SINK, &sink_config); + if (rc < 0) { + pr_err("Failed to read SINK configuration rc=%d\n", rc); + goto failed_detect; + } + + /* disable the module before starting detection */ + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, 0); + if (rc < 0) { + pr_err("Failed to disable WLED module rc=%d\n", rc); + goto failed_detect; + } + + /* set low brightness across all sinks */ + rc = wled4_set_brightness(wled, AUTO_DETECT_BRIGHTNESS); + if (rc < 0) { + pr_err("Failed to set brightness for auto detection rc=%d\n", + rc); + goto failed_detect; + } + + if (wled->cfg.cabc) { + for (i = 0; i < wled->cfg.num_strings; i++) { + rc = regmap_update_bits(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_STR_CABC(i), + WLED4_SINK_REG_STR_CABC_MASK, + 0); + if (rc < 0) + goto failed_detect; + } + } + + /* disable all sinks */ + rc = regmap_write(wled->regmap, + wled->sink_addr + WLED4_SINK_REG_CURR_SINK, 0); + if (rc < 0) { + pr_err("Failed to disable all sinks rc=%d\n", rc); + goto failed_detect; + } + + /* iterate through the strings one by one */ + for (i = 0; i < wled->cfg.num_strings; i++) { + sink_test = BIT((WLED4_SINK_REG_CURR_SINK_SHFT + i)); + + /* Enable feedback control */ + rc = regmap_write(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_FEEDBACK_CONTROL, i + 1); + if (rc < 0) { + pr_err("Failed to enable feedback for SINK %d rc = %d\n", + i + 1, rc); + goto failed_detect; + } + + /* enable the sink */ + rc = regmap_write(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_CURR_SINK, sink_test); + if (rc < 0) { + pr_err("Failed to configure SINK %d rc=%d\n", i + 1, + rc); + goto failed_detect; + } + + /* Enable the module */ + rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, + WLED3_CTRL_REG_MOD_EN_MASK); + if (rc < 0) { + pr_err("Failed to enable WLED module rc=%d\n", rc); + goto failed_detect; + } + + usleep_range(WLED_SOFT_START_DLY_US, + WLED_SOFT_START_DLY_US + 1000); + + rc = regmap_read(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_INT_RT_STS, &int_sts); + if (rc < 0) { + pr_err("Error in reading WLED3_CTRL_INT_RT_STS rc=%d\n", + rc); + goto failed_detect; + } + + if (int_sts & WLED3_CTRL_REG_OVP_FAULT_STATUS) + pr_debug("WLED OVP fault detected with SINK %d\n", + i + 1); + else + sink_valid |= sink_test; + + /* Disable the module */ + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, 0); + if (rc < 0) { + pr_err("Failed to disable WLED module rc=%d\n", rc); + goto failed_detect; + } + } + + if (sink_valid == sink_config) { + pr_debug("WLED auto-detection complete, default sink-config=%x OK!\n", + sink_config); + } else { + pr_warn("Invalid WLED default sink config=%x changing it to=%x\n", + sink_config, sink_valid); + sink_config = sink_valid; + } + + if (!sink_config) { + pr_err("No valid WLED sinks found\n"); + wled->force_mod_disable = true; + goto failed_detect; + } + + /* write the new sink configuration */ + rc = regmap_write(wled->regmap, + wled->sink_addr + WLED4_SINK_REG_CURR_SINK, + sink_config); + if (rc < 0) { + pr_err("Failed to reconfigure the default sink rc=%d\n", rc); + goto failed_detect; + } + + /* Enable valid sinks */ + for (i = 0; i < wled->cfg.num_strings; i++) { + if (wled->cfg.cabc) { + rc = regmap_update_bits(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_STR_CABC(i), + WLED4_SINK_REG_STR_CABC_MASK, + WLED4_SINK_REG_STR_CABC_MASK); + if (rc < 0) + goto failed_detect; + } + + if (sink_config & BIT(WLED4_SINK_REG_CURR_SINK_SHFT + i)) + val = WLED4_SINK_REG_STR_MOD_MASK; + else + val = 0x0; /* disable modulator_en for unused sink */ + + rc = regmap_write(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_STR_MOD_EN(i), val); + if (rc < 0) { + pr_err("Failed to configure MODULATOR_EN rc=%d\n", rc); + goto failed_detect; + } + } + + /* restore the feedback setting */ + rc = regmap_write(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_FEEDBACK_CONTROL, 0); + if (rc < 0) { + pr_err("Failed to restore feedback setting rc=%d\n", rc); + goto failed_detect; + } + + /* restore brightness */ + rc = wled4_set_brightness(wled, wled->brightness); + if (rc < 0) { + pr_err("Failed to set brightness after auto detection rc=%d\n", + rc); + goto failed_detect; + } + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, + WLED3_CTRL_REG_MOD_EN_MASK); + if (rc < 0) { + pr_err("Failed to enable WLED module rc=%d\n", rc); + goto failed_detect; + } + + wled->auto_detection_done = true; + +failed_detect: + return rc; +} + +#define WLED_AUTO_DETECT_OVP_COUNT 5 +#define WLED_AUTO_DETECT_CNT_DLY_US HZ /* 1 second */ +static bool wled_auto_detection_required(struct wled *wled) +{ + s64 elapsed_time_us; + + if (*wled->version == WLED_PM8941) + return false; + /* + * Check if the OVP fault was an occasional one + * or if it's firing continuously, the latter qualifies + * for an auto-detection check. + */ + if (!wled->auto_detection_ovp_count) { + wled->start_ovp_fault_time = ktime_get(); + wled->auto_detection_ovp_count++; + } else { + elapsed_time_us = ktime_us_delta(ktime_get(), + wled->start_ovp_fault_time); + if (elapsed_time_us > WLED_AUTO_DETECT_CNT_DLY_US) + wled->auto_detection_ovp_count = 0; + else + wled->auto_detection_ovp_count++; + + if (wled->auto_detection_ovp_count >= + WLED_AUTO_DETECT_OVP_COUNT) { + wled->auto_detection_ovp_count = 0; + return true; + } + } + + return false; +} + +static int wled_auto_detection_at_init(struct wled *wled) +{ + int rc; + u32 fault_status = 0, rt_status = 0; + + if (*wled->version == WLED_PM8941) + return 0; + + if (!wled->cfg.auto_detection_enabled) + return 0; + + rc = regmap_read(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, + &rt_status); + if (rc < 0) { + pr_err("Failed to read RT status rc=%d\n", rc); + return rc; + } + + rc = regmap_read(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS, + &fault_status); + if (rc < 0) { + pr_err("Failed to read fault status rc=%d\n", rc); + return rc; + } + + if ((rt_status & WLED3_CTRL_REG_OVP_FAULT_STATUS) || + (fault_status & WLED3_CTRL_REG_OVP_FAULT_BIT)) { + mutex_lock(&wled->lock); + rc = wled_auto_string_detection(wled); + if (!rc) + wled->auto_detection_done = true; + mutex_unlock(&wled->lock); + } + + return rc; +} + +static void handle_ovp_fault(struct wled *wled) +{ + if (!wled->cfg.auto_detection_enabled) + return; + + mutex_lock(&wled->lock); + if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) { + disable_irq_nosync(wled->ovp_irq); + wled->ovp_irq_disabled = true; + } + + if (wled_auto_detection_required(wled)) + wled_auto_string_detection(wled); + + if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) { + enable_irq(wled->ovp_irq); + wled->ovp_irq_disabled = false; + } + mutex_unlock(&wled->lock); +} + static irqreturn_t wled_ovp_irq_handler(int irq, void *_wled) { struct wled *wled = _wled; @@ -413,6 +706,9 @@ static irqreturn_t wled_ovp_irq_handler(int irq, void *_wled) dev_dbg(wled->dev, "WLED OVP fault detected, int_sts=%x fault_sts= %x\n", int_sts, fault_sts); + if (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT) + handle_ovp_fault(wled); + return IRQ_HANDLED; } @@ -575,6 +871,10 @@ static int wled4_setup(struct wled *wled) return rc; } + rc = wled_auto_detection_at_init(wled); + if (rc < 0) + return rc; + if (wled->cfg.external_pfet) { /* Unlock the secure register access */ rc = regmap_write(wled->regmap, wled->ctrl_addr + @@ -602,6 +902,7 @@ static int wled4_setup(struct wled *wled) .enabled_strings = 0xf, .cabc = false, .external_pfet = true, + .auto_detection_enabled = false, }; static const u32 wled3_boost_i_limit_values[] = { @@ -785,6 +1086,7 @@ static int wled_configure(struct wled *wled) { "qcom,ext-gen", &cfg->ext_gen, }, { "qcom,cabc", &cfg->cabc, }, { "qcom,external-pfet", &cfg->external_pfet, }, + { "qcom,auto-string-detection", &cfg->auto_detection_enabled, }, }; prop_addr = of_get_address(dev->of_node, 0, NULL, NULL);
The auto string detection algorithm checks if the current WLED sink configuration is valid. It tries enabling every sink and checks if the OVP fault is observed. Based on this information it detects and enables the valid sink configuration. Auto calibration will be triggered when the OVP fault interrupts are seen frequently thereby it tries to fix the sink configuration. Signed-off-by: Kiran Gunda <kgunda@codeaurora.org> --- drivers/video/backlight/qcom-wled.c | 302 ++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+)