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

From: Hans Verkuil
Date: Mon Nov 20 2017 - 15:57:45 EST

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.

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.


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 {

-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);
+static inline void adv7511_cec_init(struct device *dev,
+ struct adv7511 *adv7511,
+ unsigned int offset)
+ regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset,

#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;
+ cec_delete_adapter(adv7511->cec_adap);
+ adv7511->cec_adap = NULL;
+ dev_info(dev, "Initializing CEC failed with error %d, disabling CEC\n",
+ ret);
+ regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset,
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;
- regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset,
+ adv7511_cec_init(dev, adv7511, offset);

return 0;