From 8c2f9f153942c6640a983565416b7be7ac74151b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= Date: Tue, 26 Oct 2021 01:27:34 +0200 Subject: [PATCH 213/391] drm: bridge: dw-hdmi: Allow to accept HPD status from other drivers This change allows other drivers to provide HPD status. Signed-off-by: Ondrej Jirman --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 63 +++++++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index aa51c61a7..a503a8333 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -211,6 +212,8 @@ struct dw_hdmi { hdmi_codec_plugged_cb plugged_cb; struct device *codec_dev; enum drm_connector_status last_connector_result; + struct extcon_dev *extcon; + struct notifier_block extcon_nb; }; #define HDMI_IH_PHY_STAT0_RX_SENSE \ @@ -1703,6 +1706,12 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, void *data) { + if (hdmi->extcon) { + return extcon_get_state(hdmi->extcon, EXTCON_DISP_HDMI) > 0 + ? connector_status_connected + : connector_status_disconnected; + } + return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? connector_status_connected : connector_status_disconnected; } @@ -1713,7 +1722,7 @@ void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data, { u8 old_mask = hdmi->phy_mask; - if (force || disabled || !rxsense) + if (force || disabled || !rxsense || hdmi->extcon) hdmi->phy_mask |= HDMI_PHY_RX_SENSE; else hdmi->phy_mask &= ~HDMI_PHY_RX_SENSE; @@ -3143,7 +3152,7 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) status = connector_status_disconnected; } - if (status != connector_status_unknown) { + if (status != connector_status_unknown && !hdmi->extcon) { dev_dbg(hdmi->dev, "EVENT=%s\n", status == connector_status_connected ? "plugin" : "plugout"); @@ -3346,6 +3355,25 @@ static int dw_hdmi_parse_dt(struct dw_hdmi *hdmi) return 0; } +static int dw_hdmi_extcon_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct dw_hdmi *hdmi = container_of(nb, struct dw_hdmi, extcon_nb); + + enum drm_connector_status status = dw_hdmi_phy_read_hpd(hdmi, NULL); + + dev_info(hdmi->dev, "EVENT=%s\n", + status == connector_status_connected ? "plugin" : "plugout"); + + if (hdmi->bridge.dev) { + //XXX: ??? + drm_helper_hpd_irq_event(hdmi->bridge.dev); + drm_bridge_hpd_notify(&hdmi->bridge, status); + } + + return NOTIFY_DONE; +} + struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, const struct dw_hdmi_plat_data *plat_data) { @@ -3387,15 +3415,38 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, if (ret < 0) return ERR_PTR(ret); + hdmi->extcon = extcon_get_edev_by_phandle(dev, 0); + if (IS_ERR(hdmi->extcon)) { + if (PTR_ERR(hdmi->extcon) == -ENODEV) { + hdmi->extcon = NULL; + } else { + if (PTR_ERR(hdmi->extcon) != -EPROBE_DEFER) + dev_err(dev, "Invalid or missing extcon\n"); + return ERR_CAST(hdmi->extcon); + } + } + + if (hdmi->extcon) { + /* don't register IRQ for native HPD */ + hdmi->phy_mask = (u8)~(HDMI_PHY_RX_SENSE); + + hdmi->extcon_nb.notifier_call = dw_hdmi_extcon_notifier; + ret = extcon_register_notifier_all(hdmi->extcon, &hdmi->extcon_nb); + if (ret < 0) { + dev_err(dev, "failed to register extcon notifier\n"); + return ERR_PTR(ret); + } + } + ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); if (ddc_node) { hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node); of_node_put(ddc_node); if (!hdmi->ddc) { dev_dbg(hdmi->dev, "failed to read ddc node\n"); - return ERR_PTR(-EPROBE_DEFER); + ret = -EPROBE_DEFER; + goto err_extcon; } - } else { dev_dbg(hdmi->dev, "no ddc property found\n"); } @@ -3639,6 +3690,8 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, clk_disable_unprepare(hdmi->isfr_clk); err_res: i2c_put_adapter(hdmi->ddc); +err_extcon: + extcon_unregister_notifier_all(hdmi->extcon, &hdmi->extcon_nb); return ERR_PTR(ret); } @@ -3646,6 +3699,8 @@ EXPORT_SYMBOL_GPL(dw_hdmi_probe); void dw_hdmi_remove(struct dw_hdmi *hdmi) { + extcon_unregister_notifier_all(hdmi->extcon, &hdmi->extcon_nb); + drm_bridge_remove(&hdmi->bridge); if (hdmi->audio && !IS_ERR(hdmi->audio)) -- 2.35.3