154 lines
4.6 KiB
Diff
154 lines
4.6 KiB
Diff
|
From 8c2f9f153942c6640a983565416b7be7ac74151b Mon Sep 17 00:00:00 2001
|
||
|
From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= <megi@xff.cz>
|
||
|
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 <megi@xff.cz>
|
||
|
---
|
||
|
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 <linux/clk.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/err.h>
|
||
|
+#include <linux/extcon.h>
|
||
|
#include <linux/hdmi.h>
|
||
|
#include <linux/i2c.h>
|
||
|
#include <linux/irq.h>
|
||
|
@@ -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
|
||
|
|