Received: by 10.192.165.148 with SMTP id m20csp3307054imm; Mon, 23 Apr 2018 04:27:36 -0700 (PDT) X-Google-Smtp-Source: AIpwx49tG0LLFSXFONQUVk4cSGa7laDLfS16uaQsOZWs6b9j07/1cSWpg+D0rVdltiy6XpvacIOw X-Received: by 10.99.117.22 with SMTP id q22mr16702929pgc.68.1524482856676; Mon, 23 Apr 2018 04:27:36 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1524482856; cv=none; d=google.com; s=arc-20160816; b=y6yMcfZTtJqd1RJrKlBsTvzXzXE4SiMT2qfpLCXOXcYMHicBGXHrlEVy1w+FEt9D6m SnJjyY/CRUbsutzSuerFnk2eBtSssZOBhewZCbOGEZzxI8DvnqIs0DGuFdOATLuUVflU g0owYQ2HT7gpJKcpAkduWLxwMPDJTBZt0zKik59Wifng1YaeOATkQCIynGQ833ciyFz3 +UV1lUzj6m1gbk4eFvIDo790fOxClGGYN1wmFQ+bBAYpW38MiEVTLTaqb97CiHyq/ZZE qmaxJONkXrmL+FAdlGgHkUmUABzQERGr/m7WOFrAzlZB/PThso+NKgtjm1U7j2/vZDHH JdEg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:user-agent:message-id:references :in-reply-to:subject:cc:to:from:date:content-transfer-encoding :mime-version:dkim-signature:dkim-signature :arc-authentication-results; bh=2mpf09LZu1wNrWh53ejuP4NxiikLEFkSEFrtcj0qkds=; b=DKrzSLpqQJ7AlxS9SgERrbSlZ2ITx/x2UtR4Y5/dozU3DQ4pcwtw0ROuJwRUKuCEMt enow9eX0Ft0/yYtdSa/Dj4hmYDiu2JS/1qTawsVlgcmL0ui1HZ8CtIX9eclwIFBoXQcE moVdjp8v9jmj31KcMldIUYE8ZATMk71xMEXPh6PjBcyEyMe0oxuBO3dZsCeECzJT50Ed pLYC/NBEWBJP8nJwtIOxtVtG6atvLJjgcBHSRy2h+1dhfshVOnOrrckr6gq4AC9qfMvd zk3dM81BXV4oxExw+TluNIlkrEwq8CWyB8hqTInr3itxUl3Zj97h8hHAEIKARnj2KA4+ K0rg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@codeaurora.org header.s=default header.b=G7mc977w; dkim=pass header.i=@codeaurora.org header.s=default header.b=lKg/C/9+; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 127si7434589pff.224.2018.04.23.04.27.22; Mon, 23 Apr 2018 04:27:36 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@codeaurora.org header.s=default header.b=G7mc977w; dkim=pass header.i=@codeaurora.org header.s=default header.b=lKg/C/9+; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754818AbeDWL0M (ORCPT + 99 others); Mon, 23 Apr 2018 07:26:12 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:60900 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754692AbeDWL0H (ORCPT ); Mon, 23 Apr 2018 07:26:07 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id D196C60AE0; Mon, 23 Apr 2018 11:26:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1524482766; bh=UVWIGNeq9ONbrgH5WXUXzV4JwWrp8u6A12wDm9V60BQ=; h=Date:From:To:Cc:Subject:In-Reply-To:References:From; b=G7mc977wGRcYeGuJypdcp4ThDQIdadSW4Hn2YwGrEvNmLe/5vC92zbIGfnXOL6SMO bIoCr7icxvNX+j5YvFL+H8O3/RnRRPzncPKHRX2/7R1zQBF3E/9aawAw9mWFLFGqLS 6z8ngoTfkBM5/6+/RJWsHPBt9+Yc071x1iXETYYM= X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on pdx-caf-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.8 required=2.0 tests=ALL_TRUSTED,BAYES_00, DKIM_SIGNED,T_DKIM_INVALID autolearn=no autolearn_force=no version=3.4.0 Received: from mail.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.codeaurora.org (Postfix) with ESMTP id 6CEF160314; Mon, 23 Apr 2018 11:26:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1524482764; bh=UVWIGNeq9ONbrgH5WXUXzV4JwWrp8u6A12wDm9V60BQ=; h=Date:From:To:Cc:Subject:In-Reply-To:References:From; b=lKg/C/9+RRRQQSkj6dsX3SY28+kdTXk3wkiMUYXGaEnZvtcX0xBCTB0eYkz/PBZN7 PpEL3vZnhGVhUN3uSkyyUBIYMBF7Stp8B2stWg61UVZEovIwBSOlTvemX1QyHafE1R 6OoI86UPPc505Ig0iAHSE9afd2UPP8x3Ix49i7L4= MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII; format=flowed Content-Transfer-Encoding: 7bit Date: Mon, 23 Apr 2018 16:56:04 +0530 From: kgunda@codeaurora.org To: Bjorn Andersson Cc: linux-arm-msm@vger.kernel.org, Lee Jones , Daniel Thompson , Jingoo Han , Richard Purdie , Jacek Anaszewski , Pavel Machek , Rob Herring , Mark Rutland , Bartlomiej Zolnierkiewicz , linux-leds@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fbdev@vger.kernel.org, linux-arm-msm-owner@vger.kernel.org Subject: Re: [PATCH V1 4/4] qcom: spmi-wled: Add auto-calibration logic support In-Reply-To: <20180420160309.GB5615@tuxbook-pro> References: <1510834717-21765-1-git-send-email-kgunda@codeaurora.org> <1510834717-21765-5-git-send-email-kgunda@codeaurora.org> <20171205054004.GG28761@minitux> <20180419155854.GW18510@minitux> <1bf21293555e5f03a9806cc4d605ac60@codeaurora.org> <20180420160309.GB5615@tuxbook-pro> Message-ID: <52729175fcdee4d9267dcbcff8dd51ca@codeaurora.org> X-Sender: kgunda@codeaurora.org User-Agent: Roundcube Webmail/1.2.5 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 2018-04-20 21:33, Bjorn Andersson wrote: > On Thu 19 Apr 22:43 PDT 2018, kgunda@codeaurora.org wrote: > >> On 2018-04-19 21:28, Bjorn Andersson wrote: >> > On Thu 19 Apr 03:45 PDT 2018, kgunda@codeaurora.org wrote: >> > > On 2017-12-05 11:10, Bjorn Andersson wrote: > [..] >> > > > When is this feature needed? >> > > > >> > > This feature is needed if the string configuration is given wrong in >> > > the DT node by the user. >> > >> > DT describes the hardware and for all other nodes it must do so >> > accurately. >> > >> But the user may not be aware of the strings present on the display >> panel or >> may be using the same software on different devices which have >> different >> strings present. > > Swapping display board would still require an update to the DTS, to > support the new panel. But I think that if you're implementing auto > string detection, it should be used as the mechanism to detect the > strings, not something that is run at some later point in time when we > detect a failure. > Ok. got your point. Yes, the detection is done during the boot-up itself, not later point. >> > For cases where the hardware supports auto detection of functionality we >> > remove information from DT and rely on that logic to figure out the >> > hardware. We do not use it to reconfigure the hardware once we detect an >> > error. So when auto-detection is enabled it should always be used to >> > probe the hardware. >> > >> The auto string detection is not supported in any qcom hardware and i >> don't >> think there is a plan to introduce in new hardware also. >> > > Sorry, I don't follow. Who is using auto string detection then? > I mean to say. The HW doesn't handle the auto-detection completely in the HW without having the S/W intervention, which we are using now. > Regards, > Bjorn > >> > Regards, >> > Bjorn >> > >> > > > > Signed-off-by: Kiran Gunda >> > > > > --- >> > > > > .../bindings/leds/backlight/qcom-spmi-wled.txt | 5 + >> > > > > drivers/video/backlight/qcom-spmi-wled.c | 304 >> > > > > ++++++++++++++++++++- >> > > > > 2 files changed, 306 insertions(+), 3 deletions(-) >> > > > > >> > > > > diff --git >> > > > > a/Documentation/devicetree/bindings/leds/backlight/qcom-spmi-wled.txt >> > > > > b/Documentation/devicetree/bindings/leds/backlight/qcom-spmi-wled.txt >> > > > > index d39ee93..f06c0cd 100644 >> > > > > --- >> > > > > a/Documentation/devicetree/bindings/leds/backlight/qcom-spmi-wled.txt >> > > > > +++ >> > > > > b/Documentation/devicetree/bindings/leds/backlight/qcom-spmi-wled.txt >> > > > > @@ -94,6 +94,11 @@ The PMIC is connected to the host processor via >> > > > > SPMI bus. >> > > > > Definition: Interrupt names associated with the interrupts. >> > > > > Currently supported interrupts are "sc-irq" and "ovp-irq". >> > > > > >> > > > > +- qcom,auto-calibration >> > > > >> > > > qcom,auto-string-detect? >> > > > >> > > ok. Will address in the next patch. >> > > > > + Usage: optional >> > > > > + Value type: >> > > > > + Definition: Enables auto-calibration of the WLED sink configuration. >> > > > > + >> > > > > Example: >> > > > > >> > > > > qcom-wled@d800 { >> > > > > diff --git a/drivers/video/backlight/qcom-spmi-wled.c >> > > > > b/drivers/video/backlight/qcom-spmi-wled.c >> > > > > index 8b2a77a..aee5c56 100644 >> > > > > --- a/drivers/video/backlight/qcom-spmi-wled.c >> > > > > +++ b/drivers/video/backlight/qcom-spmi-wled.c >> > > > > @@ -38,11 +38,14 @@ >> > > > > #define QCOM_WLED_CTRL_SC_FAULT_BIT BIT(2) >> > > > > >> > > > > #define QCOM_WLED_CTRL_INT_RT_STS 0x10 >> > > > > +#define QCOM_WLED_CTRL_OVP_FLT_RT_STS_BIT BIT(1) >> > > > >> > > > The use of BIT() makes this a mask and not a bit number, so if you just >> > > > drop that you can afford to spell out the "FAULT" like the data sheet >> > > > does. Perhaps even making it QCOM_WLED_CTRL_OVP_FAULT_STATUS ? >> > > > >> > > ok. Will change it in the next series. >> > > > > >> > > > > #define QCOM_WLED_CTRL_MOD_ENABLE 0x46 >> > > > > #define QCOM_WLED_CTRL_MOD_EN_MASK BIT(7) >> > > > > #define QCOM_WLED_CTRL_MODULE_EN_SHIFT 7 >> > > > > >> > > > > +#define QCOM_WLED_CTRL_FDBK_OP 0x48 >> > > > >> > > > This is called WLED_CTRL_FEEDBACK_CONTROL, why the need to make it >> > > > unreadable? >> > > > >> > > Ok. Will address it in next series. >> > > > > + >> > > > > #define QCOM_WLED_CTRL_SWITCH_FREQ 0x4c >> > > > > #define QCOM_WLED_CTRL_SWITCH_FREQ_MASK GENMASK(3, 0) >> > > > > >> > > > > @@ -99,6 +102,7 @@ struct qcom_wled_config { >> > > > > int ovp_irq; >> > > > > bool en_cabc; >> > > > > bool ext_pfet_sc_pro_en; >> > > > > + bool auto_calib_enabled; >> > > > > }; >> > > > > >> > > > > struct qcom_wled { >> > > > > @@ -108,18 +112,25 @@ struct qcom_wled { >> > > > > struct mutex lock; >> > > > > struct qcom_wled_config cfg; >> > > > > ktime_t last_sc_event_time; >> > > > > + ktime_t start_ovp_fault_time; >> > > > > u16 sink_addr; >> > > > > u16 ctrl_addr; >> > > > > + u16 auto_calibration_ovp_count; >> > > > > u32 brightness; >> > > > > u32 sc_count; >> > > > > bool prev_state; >> > > > > bool ovp_irq_disabled; >> > > > > + bool auto_calib_done; >> > > > > + bool force_mod_disable; >> > > > > }; >> > > > > >> > > > > static int qcom_wled_module_enable(struct qcom_wled *wled, int val) >> > > > > { >> > > > > int rc; >> > > > > >> > > > > + if (wled->force_mod_disable) >> > > > > + return 0; >> > > > > + >> > > > > rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + >> > > > > QCOM_WLED_CTRL_MOD_ENABLE, QCOM_WLED_CTRL_MOD_EN_MASK, >> > > > > val << QCOM_WLED_CTRL_MODULE_EN_SHIFT); >> > > > > @@ -187,12 +198,10 @@ static int qcom_wled_set_brightness(struct >> > > > > qcom_wled *wled, u16 brightness) >> > > > > v[1] = (brightness >> 8) & 0xf; >> > > > > >> > > > > for (i = 0; (string_cfg >> i) != 0; i++) { >> > > > > - if (string_cfg & BIT(i)) { >> > > > >> > > > Why was this check here in the first place, if it's now fine to >> > > > configure the brightness of all strings? >> > > > >> > > > Also, a single-string config of 0b0001 will only set brightness on the >> > > > first string, while 0b1000 will set brightness on all strings. >> > > > >> > > I will correct/remove it next series. >> > > > > rc = regmap_bulk_write(wled->regmap, wled->sink_addr + >> > > > > QCOM_WLED_SINK_BRIGHT_LSB_REG(i), v, 2); >> > > > > if (rc < 0) >> > > > > return rc; >> > > > > - } >> > > > > } >> > > > > >> > > > > return 0; >> > > > > @@ -294,6 +303,262 @@ static irqreturn_t >> > > > > qcom_wled_sc_irq_handler(int irq, void *_wled) >> > > > > return IRQ_HANDLED; >> > > > > } >> > > > > >> > > > > +#define AUTO_CALIB_BRIGHTNESS 200 >> > > > > +static int qcom_wled_auto_calibrate(struct qcom_wled *wled) >> > > > > +{ >> > > > > + int rc = 0, i; >> > > > > + u32 sink_config = 0, int_sts; >> > > > > + u8 reg = 0, sink_test = 0, sink_valid = 0; >> > > > > + u8 string_cfg = wled->cfg.string_cfg; >> > > > > + >> > > > > + /* read configured sink configuration */ >> > > > > + rc = regmap_read(wled->regmap, wled->sink_addr + >> > > > > + QCOM_WLED_SINK_CURR_SINK_EN, &sink_config); >> > > > > + if (rc < 0) { >> > > > > + pr_err("Failed to read SINK configuration rc=%d\n", rc); >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + >> > > > > + /* disable the module before starting calibration */ >> > > > > + rc = regmap_update_bits(wled->regmap, >> > > > > + wled->ctrl_addr + QCOM_WLED_CTRL_MOD_ENABLE, >> > > > > + QCOM_WLED_CTRL_MOD_EN_MASK, 0); >> > > > > + if (rc < 0) { >> > > > > + pr_err("Failed to disable WLED module rc=%d\n", rc); >> > > > > + goto failed_calib; >> > > > > + } >> > > > >> > > > Any error handling beyond this point seems to leave the backlight off >> > > > (indefinitely?), this does seem like potentially bad user experience... >> > > Ok. will address in next series. >> > > > >> > > > In particular I wonder about the case when this would happen at some >> > > > random time, minutes, hours, days, months after the device was booted. >> > > > >> > > This will happen for every reboot. >> > > > > + >> > > > > + /* set low brightness across all sinks */ >> > > > > + rc = qcom_wled_set_brightness(wled, AUTO_CALIB_BRIGHTNESS); >> > > > > + if (rc < 0) { >> > > > > + pr_err("Failed to set brightness for calibration rc=%d\n", rc); >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + >> > > > > + if (wled->cfg.en_cabc) { >> > > > > + for (i = 0; (string_cfg >> i) != 0; i++) { >> > > > > + reg = 0; >> > > > > + rc = regmap_update_bits(wled->regmap, wled->sink_addr + >> > > > > + QCOM_WLED_SINK_CABC_REG(i), >> > > > > + QCOM_WLED_SINK_CABC_MASK, reg); >> > > > >> > > > Just replace "reg" with 0. >> > > Ok. will address in next series. >> > > > >> > > > > + if (rc < 0) >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + } >> > > > > + >> > > > > + /* disable all sinks */ >> > > > > + rc = regmap_write(wled->regmap, >> > > > > + wled->sink_addr + QCOM_WLED_SINK_CURR_SINK_EN, 0); >> > > > > + if (rc < 0) { >> > > > > + pr_err("Failed to disable all sinks rc=%d\n", rc); >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + >> > > > > + /* iterate through the strings one by one */ >> > > > > + for (i = 0; (string_cfg >> i) != 0; i++) { >> > > > > + sink_test = 1 << (QCOM_WLED_SINK_CURR_SINK_SHFT + i); >> > > > >> > > > BIT(QCOM_WLED_SINK_CURR_SINK_SHFT + i); >> > > > >> > > Will address in next series. >> > > > > + >> > > > > + /* Enable feedback control */ >> > > > > + rc = regmap_write(wled->regmap, wled->ctrl_addr + >> > > > > + QCOM_WLED_CTRL_FDBK_OP, i + 1); >> > > > > + if (rc < 0) { >> > > > > + pr_err("Failed to enable feedback for SINK %d rc = %d\n", >> > > > > + i + 1, rc); >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + >> > > > > + /* enable the sink */ >> > > > > + rc = regmap_write(wled->regmap, wled->sink_addr + >> > > > > + QCOM_WLED_SINK_CURR_SINK_EN, sink_test); >> > > > > + if (rc < 0) { >> > > > > + pr_err("Failed to configure SINK %d rc=%d\n", >> > > > > + i + 1, rc); >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + >> > > > > + /* Enable the module */ >> > > > > + rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + >> > > > > + QCOM_WLED_CTRL_MOD_ENABLE, >> > > > > + QCOM_WLED_CTRL_MOD_EN_MASK, >> > > > > + QCOM_WLED_CTRL_MOD_EN_MASK); >> > > > >> > > > I like the use of regmap_update_bits(..., MASK, MASK) it's clean, but >> > > > makes me wonder why it's done differently in qcom_wled_module_enable(). >> > > > >> > > will address it in the next series. >> > > > > + if (rc < 0) { >> > > > > + pr_err("Failed to enable WLED module rc=%d\n", rc); >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + >> > > > > + usleep_range(QCOM_WLED_SOFT_START_DLY_US, >> > > > > + QCOM_WLED_SOFT_START_DLY_US + 1000); >> > > > > + >> > > > > + rc = regmap_read(wled->regmap, wled->ctrl_addr + >> > > > > + QCOM_WLED_CTRL_INT_RT_STS, &int_sts); >> > > > > + if (rc < 0) { >> > > > > + pr_err("Error in reading WLED_INT_RT_STS rc=%d\n", rc); >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + >> > > > > + if (int_sts & QCOM_WLED_CTRL_OVP_FAULT_BIT) >> > > > > + 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 + QCOM_WLED_CTRL_MOD_ENABLE, >> > > > > + QCOM_WLED_CTRL_MOD_EN_MASK, 0); >> > > > > + if (rc < 0) { >> > > > > + pr_err("Failed to disable WLED module rc=%d\n", rc); >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + } >> > > > > + >> > > > > + if (sink_valid == sink_config) { >> > > > > + pr_debug("WLED auto-calibration 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_warn("No valid WLED sinks found\n"); >> > > > > + wled->force_mod_disable = true; >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + >> > > > > + /* write the new sink configuration */ >> > > > > + rc = regmap_write(wled->regmap, >> > > > > + wled->sink_addr + QCOM_WLED_SINK_CURR_SINK_EN, >> > > > > + sink_config); >> > > > > + if (rc < 0) { >> > > > > + pr_err("Failed to reconfigure the default sink rc=%d\n", rc); >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + >> > > > > + /* MODULATOR_EN setting for valid sinks */ >> > > > >> > > > "Enable valid sinks" >> > > > >> > > Will address it in the next series. >> > > > > + for (i = 0; (string_cfg >> i) != 0; i++) { >> > > > > + if (wled->cfg.en_cabc) { >> > > > > + reg = QCOM_WLED_SINK_CABC_EN; >> > > > >> > > > "reg" is a bad name of a variable holding the "value" to be written to a >> > > > register. >> > > > >> > > Will address it in the next series. >> > > > > + rc = regmap_update_bits(wled->regmap, wled->sink_addr + >> > > > > + QCOM_WLED_SINK_CABC_REG(i), >> > > > > + QCOM_WLED_SINK_CABC_MASK, reg); >> > > > >> > > > Again, just inline the value in the function call. >> > > > >> > > Will address it in the next series. >> > > > > + if (rc < 0) >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + >> > > > > + if (sink_config & (1 << (QCOM_WLED_SINK_CURR_SINK_SHFT + i))) >> > > > >> > > > BIT(QCOM_WLED_SINK_CURR_SINK_SHFT + i) >> > > > >> > > Will address it in the next series. >> > > > > + reg = QCOM_WLED_SINK_REG_STR_MOD_EN; >> > > > > + else >> > > > > + reg = 0x0; /* disable modulator_en for unused sink */ >> > > > > + >> > > > > + rc = regmap_write(wled->regmap, wled->sink_addr + >> > > > > + QCOM_WLED_SINK_MOD_EN_REG(i), reg); >> > > > > + if (rc < 0) { >> > > > > + pr_err("Failed to configure MODULATOR_EN rc=%d\n", rc); >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + } >> > > > > + >> > > > > + /* restore the feedback setting */ >> > > > > + rc = regmap_write(wled->regmap, >> > > > > + wled->ctrl_addr + QCOM_WLED_CTRL_FDBK_OP, 0); >> > > > > + if (rc < 0) { >> > > > > + pr_err("Failed to restore feedback setting rc=%d\n", rc); >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + >> > > > > + /* restore brightness */ >> > > > > + rc = qcom_wled_set_brightness(wled, wled->brightness); >> > > > > + if (rc < 0) { >> > > > > + pr_err("Failed to set brightness after calibration rc=%d\n", >> > > > > + rc); >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + >> > > > > + rc = regmap_update_bits(wled->regmap, >> > > > > + wled->ctrl_addr + QCOM_WLED_CTRL_MOD_ENABLE, >> > > > > + QCOM_WLED_CTRL_MOD_EN_MASK, >> > > > > + QCOM_WLED_CTRL_MOD_EN_MASK); >> > > > > + if (rc < 0) { >> > > > > + pr_err("Failed to enable WLED module rc=%d\n", rc); >> > > > > + goto failed_calib; >> > > > > + } >> > > > > + >> > > > > + /* delay for WLED soft-start */ >> > > > >> > > > What comes after this that you want to delay? >> > > > >> > > > This delay is used to make the OVP IRQ not fire immediately, but as >> > > > we've now successfully executed the string auto detection run we're >> > > > never going to do anything in the OVP handler. >> > > > >> > > Will correct it in the next series. >> > > > > + usleep_range(QCOM_WLED_SOFT_START_DLY_US, >> > > > > + QCOM_WLED_SOFT_START_DLY_US + 1000); >> > > > > + >> > > > > +failed_calib: >> > > > > + return rc; >> > > > > +} >> > > > > + >> > > > > +#define WLED_AUTO_CAL_OVP_COUNT 5 >> > > > > +#define WLED_AUTO_CAL_CNT_DLY_US 1000000 /* 1 second */ >> > > > > +static bool qcom_wled_auto_cal_required(struct qcom_wled *wled) >> > > > > +{ >> > > > > + s64 elapsed_time_us; >> > > > > + >> > > > > + /* >> > > > > + * Check if the OVP fault was an occasional one >> > > > > + * or if its firing continuously, the latter qualifies >> > > > > + * for an auto-calibration check. >> > > > > + */ >> > > > > + if (!wled->auto_calibration_ovp_count) { >> > > > > + wled->start_ovp_fault_time = ktime_get(); >> > > > > + wled->auto_calibration_ovp_count++; >> > > > > + } else { >> > > > > + elapsed_time_us = ktime_us_delta(ktime_get(), >> > > > > + wled->start_ovp_fault_time); >> > > > > + if (elapsed_time_us > WLED_AUTO_CAL_CNT_DLY_US) >> > > > > + wled->auto_calibration_ovp_count = 0; >> > > > > + else >> > > > > + wled->auto_calibration_ovp_count++; >> > > > > + >> > > > > + if (wled->auto_calibration_ovp_count >= >> > > > > + WLED_AUTO_CAL_OVP_COUNT) { >> > > > > + wled->auto_calibration_ovp_count = 0; >> > > > > + return true; >> > > > > + } >> > > > > + } >> > > > > + >> > > > > + return false; >> > > > > +} >> > > > > + >> > > > > +static int qcom_wled_auto_calibrate_at_init(struct qcom_wled *wled) >> > > > >> > > > I presume this function is expected to detect if there is a invalid >> > > > configuration at boot and try to figure out which strings are actually >> > > > wired. >> > > > >> > > Correct. >> > > > > +{ >> > > > > + int rc; >> > > > > + u32 fault_status = 0, rt_status = 0; >> > > > > + >> > > > > + if (!wled->cfg.auto_calib_enabled) >> > > > > + return 0; >> > > > > + >> > > > > + rc = regmap_read(wled->regmap, >> > > > > + wled->ctrl_addr + QCOM_WLED_CTRL_INT_RT_STS, >> > > > > + &rt_status); >> > > > > + if (rc < 0) >> > > > > + pr_err("Failed to read RT status rc=%d\n", rc); >> > > > > + >> > > > > + rc = regmap_read(wled->regmap, >> > > > > + wled->ctrl_addr + QCOM_WLED_CTRL_FAULT_STATUS, >> > > > > + &fault_status); >> > > > > + if (rc < 0) >> > > > > + pr_err("Failed to read fault status rc=%d\n", rc); >> > > > > + >> > > > > + if ((rt_status & QCOM_WLED_CTRL_OVP_FLT_RT_STS_BIT) || >> > > > > + (fault_status & QCOM_WLED_CTRL_OVP_FAULT_BIT)) { >> > > > >> > > > You should be able to drop the extra () around these. >> > > > >> > > Ok. Will remove it in the next series. >> > > > > + mutex_lock(&wled->lock); >> > > > > + rc = qcom_wled_auto_calibrate(wled); >> > > > > + if (rc < 0) >> > > > > + pr_err("Failed auto-calibration rc=%d\n", rc); >> > > > >> > > > qcom_wled_auto_calibrate() did already print, no need to repeat this. >> > > > >> > > Ok. Will remove this in the next series. >> > > > > + else >> > > > > + wled->auto_calib_done = true; >> > > > > + mutex_unlock(&wled->lock); >> > > > > + } >> > > > > + >> > > > > + return rc; >> > > > > +} >> > > > > + >> > > > > static irqreturn_t qcom_wled_ovp_irq_handler(int irq, void *_wled) >> > > > > { >> > > > > struct qcom_wled *wled = _wled; >> > > > > @@ -319,6 +584,33 @@ static irqreturn_t >> > > > > qcom_wled_ovp_irq_handler(int irq, void *_wled) >> > > > > pr_err("WLED OVP fault detected, int_sts=%x fault_sts= %x\n", >> > > > > int_sts, fault_sts); >> > > > > >> > > > > + if (fault_sts & QCOM_WLED_CTRL_OVP_FAULT_BIT) { >> > > > > + if (wled->cfg.auto_calib_enabled && !wled->auto_calib_done) { >> > > > > + if (qcom_wled_auto_cal_required(wled)) { >> > > > >> > > > So this will be invoked only once, iff we didn't boot with a faulty >> > > > configuration in which case the qcom_wled_auto_calibrate_at_init() has >> > > > already done this step and set auto_calib_done. >> > > > >> > > > >> > > > Which also would mean that all logic in this handler, beyond the >> > > > printouts, are only ever going to be executed zero or one times. >> > > > >> > > > Why don't you just do auto-detection during probe (iff the flag is set >> > > > in DT) and you can remove all this extra logic? >> > > > >> > > I think we have seen a issue, where the OVP interrupt is not getting >> > > set >> > > some times during the execution of this function at boot. In that >> > > case the >> > > auto calibration is >> > > done bit later. That's why this code is added. >> > > > > + mutex_lock(&wled->lock); >> > > > > + if (wled->cfg.ovp_irq > 0 && >> > > > > + !wled->ovp_irq_disabled) { >> > > > > + disable_irq_nosync(wled->cfg.ovp_irq); >> > > > > + wled->ovp_irq_disabled = true; >> > > > > + } >> > > > > + >> > > > > + rc = qcom_wled_auto_calibrate(wled); >> > > > > + if (rc < 0) >> > > > > + pr_err("Failed auto-calibration rc=%d\n", >> > > > > + rc); >> > > > >> > > > qcom_wled_auto_calibrate() did already print. >> > > > >> > > Ok. I will remove it in the next series. >> > > > > + else >> > > > > + wled->auto_calib_done = true; >> > > > > + >> > > > > + if (wled->cfg.ovp_irq > 0 && >> > > > > + wled->ovp_irq_disabled) { >> > > > > + enable_irq(wled->cfg.ovp_irq); >> > > > > + wled->ovp_irq_disabled = false; >> > > > > + } >> > > > > + mutex_unlock(&wled->lock); >> > > > > + } >> > > > > + } >> > > > > + } >> > > > > + >> > > > >> > > > 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-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-arm-msm" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html