Re: [PATCHv2] drm: adv7511/33: Fix adv7511_cec_init() failure handling

From: Hans Verkuil
Date: Tue Nov 21 2017 - 03:18:40 EST


On 11/21/2017 07:48 AM, Laurent Pinchart wrote:
> Hi Hans,
>
> Thank you for the patch.
>
> On Monday, 20 November 2017 22:57:34 EET Hans Verkuil wrote:
>> If the device tree for a board did not specify a cec clock, then
>> adv7511_cec_init would return an error, which would cause adv7511_probe()
>> to fail and thus there is no HDMI output.
>>
>> There is no need to have adv7511_probe() fail if the CEC initialization
>> fails, so just change adv7511_cec_init() to a void function. In addition,
>> adv7511_cec_init() should just return silently if the cec clock isn't
>> found and show a message for any other errors.
>
> I don't think that's correct. You at least need to defer probing if the clock
> is specified in DT but has no provider available yet. For other errors I agree
> that we can still initialize the ADV7511 in a degraded mode without CEC
> support, although I would prefer to also error out in case of invalid DT to
> ensure that DT errors are caught as early as possible.

Ah yes, probe deferring, I forgot about that. I'll make a v3.
The only other possible error is ENOENT for when no CEC clock is defined,
which just means it should continue without CEC (i.e. this is not an error).

I'll make a v3, also incorporating your other comments below.

Regards,

Hans

>
>> An otherwise correct cleanup patch from Dan Carpenter turned this broken
>> failure handling into a kernel Oops, so bisection points to commit
>> 7af35b0addbc ("drm/kirin: Checking for IS_ERR() instead of NULL") rather
>> than 3b1b975003e4 ("drm: adv7511/33: add HDMI CEC support").
>>
>> Based on earlier patches from Arnd and John.
>>
>> Reported-by: Naresh Kamboju <naresh.kamboju@xxxxxxxxxx>
>> Cc: Xinliang Liu <xinliang.liu@xxxxxxxxxx>
>> Cc: Dan Carpenter <dan.carpenter@xxxxxxxxxx>
>> Cc: Sean Paul <seanpaul@xxxxxxxxxxxx>
>> Cc: Archit Taneja <architt@xxxxxxxxxxxxxx>
>> Cc: John Stultz <john.stultz@xxxxxxxxxx>
>> Link: https://bugs.linaro.org/show_bug.cgi?id=3345
>> Link: https://lkft.validation.linaro.org/scheduler/job/48017#L3551
>> Fixes: 7af35b0addbc ("drm/kirin: Checking for IS_ERR() instead of NULL")
>> Fixes: 3b1b975003e4 ("drm: adv7511/33: add HDMI CEC support")
>> Signed-off-by: Hans Verkuil <hans.verkuil@xxxxxxxxx>
>> Tested-by: Hans Verkuil <hans.verkuil@xxxxxxxxx>
>> ---
>> This rework of Arnd and John's patches goes a bit further and makes
>> cec_init a void function and just silently exits if there is no cec clock
>> defined in the dts. I'm sure that's the reason why the kirin board failed
>> on this. BTW: if the kirin board DOES support cec, then it would be nice
>> if it can be hooked up in the dts!
>>
>> Tested with my Dragonboard and Renesas Koelsch board.
>>
>> Change since my previous RFC PATCH:
>>
>> - added static inline adv7511_cec_init to avoid #ifdef in the probe function
>> as suggested by John Stultz.
>>
>> Regards,
>>
>> Hans
>> ---
>> diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h
>> b/drivers/gpu/drm/bridge/adv7511/adv7511.h index 543a5eb91624..16051bfa5578
>> 100644
>> --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h
>> +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h
>> @@ -374,9 +374,17 @@ struct adv7511 {
>> };
>>
>> #ifdef CONFIG_DRM_I2C_ADV7511_CEC
>> -int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511,
>> - unsigned int offset);
>> +void adv7511_cec_init(struct device *dev, struct adv7511 *adv7511,
>> + unsigned int offset);
>> void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1);
>> +#else
>> +static inline void adv7511_cec_init(struct device *dev,
>> + struct adv7511 *adv7511,
>> + unsigned int offset)
>> +{
>> + regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset,
>> + ADV7511_CEC_CTRL_POWER_DOWN);
>> +}
>> #endif
>>
>> #ifdef CONFIG_DRM_I2C_ADV7533
>> diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c
>> b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c index
>> b33d730e4d73..c1cd471d31fa 100644
>> --- a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c
>> +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c
>> @@ -300,18 +300,20 @@ static int adv7511_cec_parse_dt(struct device *dev,
>> struct adv7511 *adv7511) return 0;
>> }
>>
>> -int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511,
>> - unsigned int offset)
>> +void adv7511_cec_init(struct device *dev, struct adv7511 *adv7511,
>> + unsigned int offset)
>> {
>> int ret = adv7511_cec_parse_dt(dev, adv7511);
>>
>> if (ret)
>> - return ret;
>> + goto disable_cec;
>>
>> adv7511->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,
>> adv7511, dev_name(dev), CEC_CAP_DEFAULTS, ADV7511_MAX_ADDRS);
>> - if (IS_ERR(adv7511->cec_adap))
>> - return PTR_ERR(adv7511->cec_adap);
>> + if (IS_ERR(adv7511->cec_adap)) {
>> + ret = PTR_ERR(adv7511->cec_adap);
>> + goto fail;
>> + }
>>
>> regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset, 0);
>> /* cec soft reset */
>> @@ -329,9 +331,15 @@ int adv7511_cec_init(struct device *dev, struct adv7511
>> *adv7511, ((adv7511->cec_clk_freq / 750000) - 1) << 2);
>>
>> ret = cec_register_adapter(adv7511->cec_adap, dev);
>> - if (ret) {
>> - cec_delete_adapter(adv7511->cec_adap);
>> - adv7511->cec_adap = NULL;
>> - }
>> - return ret;
>> + if (!ret)
>> + return;
>
> This confused me for an instant, I think a goto error_cec_delete would be
> clearer, but that's up to you (maybe I just wasn't awake enough).
>
>> + cec_delete_adapter(adv7511->cec_adap);
>> + adv7511->cec_adap = NULL;
>> +
>> +fail:
>
> Nitpicking, I'd name this error to match the labels in the probe function.
>
>> + dev_info(dev, "Initializing CEC failed with error %d, disabling CEC\n",
>> + ret);
>> +disable_cec:
>> + regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset,
>> + ADV7511_CEC_CTRL_POWER_DOWN);
>> }
>> diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
>> b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index
>> 3a33075dbb22..2eb465827bcd 100644
>> --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
>> +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
>> @@ -1200,15 +1200,7 @@ static int adv7511_probe(struct i2c_client *i2c,
>> const struct i2c_device_id *id) adv7511_audio_init(dev, adv7511);
>>
>> offset = adv7511->type == ADV7533 ? ADV7533_REG_CEC_OFFSET : 0;
>> -
>> -#ifdef CONFIG_DRM_I2C_ADV7511_CEC
>> - ret = adv7511_cec_init(dev, adv7511, offset);
>> - if (ret)
>> - goto err_unregister_cec;
>> -#else
>> - regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset,
>> - ADV7511_CEC_CTRL_POWER_DOWN);
>> -#endif
>> + adv7511_cec_init(dev, adv7511, offset);
>
> Now that the offset is only passed to this function, how about computing it in
> adv7511_cec_init() to store as much CEC code as possible in drivers/gpu/drm/
> bridge/adv7511/adv7511_cec.c ?
>
>>
>> return 0;
>