[PATCH 2/2] pwm: mtk-disp: Configure double buffering before reading in .get_state()

AngeloGioacchino Del Regno angelogioacchino.delregno at collabora.com
Thu Jan 26 08:16:10 PST 2023


Il 26/01/23 17:09, Nícolas F. R. A. Prado ha scritto:
> On Thu, Jan 26, 2023 at 04:24:29PM +0100, AngeloGioacchino Del Regno wrote:
>> Il 26/01/23 16:19, Nícolas F. R. A. Prado ha scritto:
>>> On Mon, Jan 23, 2023 at 05:06:15PM +0100, AngeloGioacchino Del Regno wrote:
>>>> The DISP_PWM controller's default behavior is to always use register
>>>> double buffering: all reads/writes are then performed on shadow
>>>> registers instead of working registers and this becomes an issue
>>>> in case our chosen configuration in Linux is different from the
>>>> default (or from the one that was pre-applied by the bootloader).
>>>>
>>>> An example of broken behavior is when the controller is configured
>>>> to use shadow registers, but this driver wants to configure it
>>>> otherwise: what happens is that the .get_state() callback is called
>>>> right after registering the pwmchip and checks whether the PWM is
>>>> enabled by reading the DISP_PWM_EN register;
>>>> At this point, if shadow registers are enabled but their content
>>>> was not committed before booting Linux, we are *not* reading the
>>>> current PWM enablement status, leading to the kernel knowing that
>>>> the hardware is actually enabled when, in reality, it's not.
>>>>
>>>> The aforementioned issue emerged since this driver was fixed with
>>>> commit 0b5ef3429d8f ("pwm: mtk-disp: Fix the parameters calculated
>>>> by the enabled flag of disp_pwm") making it to read the enablement
>>>> status from the right register.
>>>>
>>>> Configure the controller in the .get_state() callback to avoid
>>>> this desync issue and get the backlight properly working again.
>>>>
>>>> Fixes: 3f2b16734914 ("pwm: mtk-disp: Implement atomic API .get_state()")
>>>> Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno at collabora.com>
>>>> ---
>>>>    drivers/pwm/pwm-mtk-disp.c | 10 ++++++++++
>>>>    1 file changed, 10 insertions(+)
>>>>
>>>> diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c
>>>> index 82b430d881a2..fe9593f968ee 100644
>>>> --- a/drivers/pwm/pwm-mtk-disp.c
>>>> +++ b/drivers/pwm/pwm-mtk-disp.c
>>>> @@ -196,6 +196,16 @@ static int mtk_disp_pwm_get_state(struct pwm_chip *chip,
>>>>    		return err;
>>>>    	}
>>>> +	/*
>>>> +	 * Apply DISP_PWM_DEBUG settings to choose whether to enable or disable
>>>> +	 * registers double buffer and manual commit to working register before
>>>> +	 * performing any read/write operation
>>>> +	 */
>>>> +	if (mdp->data->bls_debug)
>>>
>>> I feel like this condition should be the same as in the apply() callback, since
>>> they're doing the same write operation, so also have '&& !has_commit'.
>>>
>>
>> The bls_debug register is used to both enable and/or disable various features,
>> including the one that I'm targeting in this commit, which is disabling shadow
>> registers.
>>
>> As I explained in the commit message, we don't want to - and cannot - assume that
>> the bootloader doesn't *reset* the backlight controller before booting Linux: a
>> reset would re-enable the shadow registers, and this function being called as
>> first to check the backlight EN status may fail to do so.
>>
>> This is as well true in the opposite situation where, in the future, we may want
>> to set shadow registers ON, while the bootloader sets them OFF before booting:
>> adding a (x && !has_commit) check in this branch would defeat that purpose and
>> make this commit... well.. partially broken! :-)
> 
> Makes sense, but in that case shouldn't we drop the (&& !has_commit) in the
> check of the previous commit too? I get that in the pwm's core current logic,

No. The previous commit checks !has_commit to select a register write strategy
between "shadow -> commit" and "working registers - no commit necessary".

If you drop that check from the previous commit, how can you choose the write
strategy to use?! :-)

You can't rely on reading the bls_debug register, either, because it may be
holding different values compared to what we want due to, for example, a reset
and you can't rely on checking bls_debug_mask because in the future this kind
of selector may be residing in an entirely different register.

> get_state() is run before apply(), but given that we also write the debug
> register in apply(), we're not relying on that. So as it currently stands, if in
> the future the bootloader sets shadow registers OFF, and we want to set them ON,
> and we call apply() before having called get_state(), we'd be back to the broken
> behavior.

No, because the default BLS_DEBUG register value at IP reset is 0 - where 0 means
"do not disable commit, do not disable shadow register read, do not disable shadow
register write" :-)

Cheers,
Angelo

> 
> Thanks,
> Nícolas
> 
>>
>> Cheers!
>> Angelo
>>
>>> Reviewed-by: Nícolas F. R. A. Prado <nfraprado at collabora.com>
>>> Tested-by: Nícolas F. R. A. Prado <nfraprado at collabora.com>
>>>
>>> On MT8192 Asurada Spherion.
>>>
>>> Thanks,
>>> Nícolas
>>>
>>>> +		mtk_disp_pwm_update_bits(mdp, mdp->data->bls_debug,
>>>> +					 mdp->data->bls_debug_mask,
>>>> +					 mdp->data->bls_debug_mask);
>>>> +
>>>>    	rate = clk_get_rate(mdp->clk_main);
>>>>    	con0 = readl(mdp->base + mdp->data->con0);
>>>>    	con1 = readl(mdp->base + mdp->data->con1);
>>>> -- 
>>>> 2.39.0
>>>>
>>>>
>>
>>





More information about the linux-arm-kernel mailing list