[PATCH] drm/mediatek: use HDMI state notifier support

Philipp Zabel p.zabel at pengutronix.de
Tue Nov 15 10:39:51 PST 2016


Issue hot-plug detection, EDID update, and ELD update notifications
from the CEC and HDMI drivers using the HDMI state notifier support.

Signed-off-by: Philipp Zabel <p.zabel at pengutronix.de>
---
This patch depends on the "video: add HDMI state notifier support" patch [1] by
Hans Verkuil, based on Russell King's earlier version. With this we can replace
the custom callback interface between HDMI and CEC drivers with a common
mechanism. It will also allow other drivers such as hdmi-codec to react to the
emitted events.

[1] https://patchwork.linuxtv.org/patch/38109/
---
 drivers/gpu/drm/mediatek/mtk_cec.c  | 56 +++++++++++--------------------------
 drivers/gpu/drm/mediatek/mtk_cec.h  | 26 -----------------
 drivers/gpu/drm/mediatek/mtk_hdmi.c | 46 ++++++++++++++++++------------
 3 files changed, 44 insertions(+), 84 deletions(-)
 delete mode 100644 drivers/gpu/drm/mediatek/mtk_cec.h

diff --git a/drivers/gpu/drm/mediatek/mtk_cec.c b/drivers/gpu/drm/mediatek/mtk_cec.c
index 7a3eb8c..9a1807b 100644
--- a/drivers/gpu/drm/mediatek/mtk_cec.c
+++ b/drivers/gpu/drm/mediatek/mtk_cec.c
@@ -13,12 +13,11 @@
  */
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/hdmi-notifier.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 
-#include "mtk_cec.h"
-
 #define TR_CONFIG		0x00
 #define CLEAR_CEC_IRQ			BIT(15)
 
@@ -55,12 +54,9 @@
 
 struct mtk_cec {
 	void __iomem *regs;
+	struct hdmi_notifier *notifier;
 	struct clk *clk;
 	int irq;
-	bool hpd;
-	void (*hpd_event)(bool hpd, struct device *dev);
-	struct device *hdmi_dev;
-	spinlock_t lock;
 };
 
 static void mtk_cec_clear_bits(struct mtk_cec *cec, unsigned int offset,
@@ -94,20 +90,7 @@ static void mtk_cec_mask(struct mtk_cec *cec, unsigned int offset,
 	writel(val, cec->regs + offset);
 }
 
-void mtk_cec_set_hpd_event(struct device *dev,
-			   void (*hpd_event)(bool hpd, struct device *dev),
-			   struct device *hdmi_dev)
-{
-	struct mtk_cec *cec = dev_get_drvdata(dev);
-	unsigned long flags;
-
-	spin_lock_irqsave(&cec->lock, flags);
-	cec->hdmi_dev = hdmi_dev;
-	cec->hpd_event = hpd_event;
-	spin_unlock_irqrestore(&cec->lock, flags);
-}
-
-bool mtk_cec_hpd_high(struct device *dev)
+static bool mtk_cec_hpd_high(struct device *dev)
 {
 	struct mtk_cec *cec = dev_get_drvdata(dev);
 	unsigned int status;
@@ -152,21 +135,6 @@ static void mtk_cec_clear_htplg_irq(struct mtk_cec *cec)
 			   RX_INT_32K_CLR | HDMI_HTPLG_INT_32K_CLR);
 }
 
-static void mtk_cec_hpd_event(struct mtk_cec *cec, bool hpd)
-{
-	void (*hpd_event)(bool hpd, struct device *dev);
-	struct device *hdmi_dev;
-	unsigned long flags;
-
-	spin_lock_irqsave(&cec->lock, flags);
-	hpd_event = cec->hpd_event;
-	hdmi_dev = cec->hdmi_dev;
-	spin_unlock_irqrestore(&cec->lock, flags);
-
-	if (hpd_event)
-		hpd_event(hpd, hdmi_dev);
-}
-
 static irqreturn_t mtk_cec_htplg_isr_thread(int irq, void *arg)
 {
 	struct device *dev = arg;
@@ -176,11 +144,13 @@ static irqreturn_t mtk_cec_htplg_isr_thread(int irq, void *arg)
 	mtk_cec_clear_htplg_irq(cec);
 	hpd = mtk_cec_hpd_high(dev);
 
-	if (cec->hpd != hpd) {
+	if (cec->notifier->connected != hpd) {
 		dev_dbg(dev, "hotplug event! cur hpd = %d, hpd = %d\n",
-			cec->hpd, hpd);
-		cec->hpd = hpd;
-		mtk_cec_hpd_event(cec, hpd);
+			cec->notifier->connected, hpd);
+		if (hpd)
+			hdmi_event_connect(cec->notifier);
+		else
+			hdmi_event_disconnect(cec->notifier);
 	}
 	return IRQ_HANDLED;
 }
@@ -197,7 +167,6 @@ static int mtk_cec_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	platform_set_drvdata(pdev, cec);
-	spin_lock_init(&cec->lock);
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	cec->regs = devm_ioremap_resource(dev, res);
@@ -220,6 +189,12 @@ static int mtk_cec_probe(struct platform_device *pdev)
 		return cec->irq;
 	}
 
+	cec->notifier = hdmi_notifier_get(dev);
+	if (!cec->notifier) {
+		clk_disable_unprepare(cec->clk);
+		return -ENOMEM;
+	}
+
 	ret = devm_request_threaded_irq(dev, cec->irq, NULL,
 					mtk_cec_htplg_isr_thread,
 					IRQF_SHARED | IRQF_TRIGGER_LOW |
@@ -245,6 +220,7 @@ static int mtk_cec_remove(struct platform_device *pdev)
 {
 	struct mtk_cec *cec = platform_get_drvdata(pdev);
 
+	hdmi_notifier_put(cec->notifier);
 	mtk_cec_htplg_irq_disable(cec);
 	clk_disable_unprepare(cec->clk);
 	return 0;
diff --git a/drivers/gpu/drm/mediatek/mtk_cec.h b/drivers/gpu/drm/mediatek/mtk_cec.h
deleted file mode 100644
index 10057b7..0000000
--- a/drivers/gpu/drm/mediatek/mtk_cec.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (c) 2014 MediaTek Inc.
- * Author: Jie Qiu <jie.qiu at mediatek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-#ifndef _MTK_CEC_H
-#define _MTK_CEC_H
-
-#include <linux/types.h>
-
-struct device;
-
-void mtk_cec_set_hpd_event(struct device *dev,
-			   void (*hotplug_event)(bool hpd, struct device *dev),
-			   struct device *hdmi_dev);
-bool mtk_cec_hpd_high(struct device *dev);
-
-#endif /* _MTK_CEC_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index 71227de..c04a71a 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
@@ -20,6 +20,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/hdmi.h>
+#include <linux/hdmi-notifier.h>
 #include <linux/i2c.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -32,7 +33,6 @@
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <sound/hdmi-codec.h>
-#include "mtk_cec.h"
 #include "mtk_hdmi.h"
 #include "mtk_hdmi_regs.h"
 
@@ -153,6 +153,8 @@ struct mtk_hdmi {
 	struct device *dev;
 	struct phy *phy;
 	struct device *cec_dev;
+	struct hdmi_notifier *notifier;
+	struct notifier_block nb;
 	struct i2c_adapter *ddc_adpt;
 	struct clk *clk[MTK_HDMI_CLK_COUNT];
 	struct drm_display_mode mode;
@@ -1196,19 +1198,10 @@ static enum drm_connector_status hdmi_conn_detect(struct drm_connector *conn,
 {
 	struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
 
-	return mtk_cec_hpd_high(hdmi->cec_dev) ?
+	return hdmi->notifier->connected ?
 	       connector_status_connected : connector_status_disconnected;
 }
 
-static void hdmi_conn_destroy(struct drm_connector *conn)
-{
-	struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
-
-	mtk_cec_set_hpd_event(hdmi->cec_dev, NULL, NULL);
-
-	drm_connector_cleanup(conn);
-}
-
 static int mtk_hdmi_conn_get_modes(struct drm_connector *conn)
 {
 	struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
@@ -1225,9 +1218,11 @@ static int mtk_hdmi_conn_get_modes(struct drm_connector *conn)
 	hdmi->dvi_mode = !drm_detect_monitor_audio(edid);
 
 	drm_mode_connector_update_edid_property(conn, edid);
+	hdmi_event_new_edid(hdmi->notifier, edid, sizeof(*edid));
 
 	ret = drm_add_edid_modes(conn, edid);
 	drm_edid_to_eld(conn, edid);
+	hdmi_event_new_eld(hdmi->notifier, conn->eld);
 	kfree(edid);
 	return ret;
 }
@@ -1269,7 +1264,7 @@ static const struct drm_connector_funcs mtk_hdmi_connector_funcs = {
 	.dpms = drm_atomic_helper_connector_dpms,
 	.detect = hdmi_conn_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = hdmi_conn_destroy,
+	.destroy = drm_connector_cleanup,
 	.reset = drm_atomic_helper_connector_reset,
 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -1282,12 +1277,16 @@ static const struct drm_connector_helper_funcs
 	.best_encoder = mtk_hdmi_conn_best_enc,
 };
 
-static void mtk_hdmi_hpd_event(bool hpd, struct device *dev)
+static int mtk_hdmi_notify(struct notifier_block *nb, unsigned long event,
+			    void *data)
 {
-	struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+	struct mtk_hdmi *hdmi = container_of(nb, struct mtk_hdmi, nb);
 
-	if (hdmi && hdmi->bridge.encoder && hdmi->bridge.encoder->dev)
+	if ((event == HDMI_CONNECTED || event == HDMI_DISCONNECTED) &&
+	    (hdmi->bridge.encoder && hdmi->bridge.encoder->dev))
 		drm_helper_hpd_irq_event(hdmi->bridge.encoder->dev);
+
+	return NOTIFY_OK;
 }
 
 /*
@@ -1330,8 +1329,6 @@ static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge)
 		}
 	}
 
-	mtk_cec_set_hpd_event(hdmi->cec_dev, mtk_hdmi_hpd_event, hdmi->dev);
-
 	return 0;
 }
 
@@ -1707,6 +1704,15 @@ static int mtk_drm_hdmi_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	hdmi->notifier = hdmi_notifier_get(hdmi->cec_dev);
+	if (!hdmi->notifier)
+		return -ENOMEM;
+
+	hdmi->nb.notifier_call = mtk_hdmi_notify;
+	ret = hdmi_notifier_register(hdmi->notifier, &hdmi->nb);
+	if (ret)
+		goto err_notifier_put;
+
 	mtk_hdmi_register_audio_driver(dev);
 
 	hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs;
@@ -1714,7 +1720,7 @@ static int mtk_drm_hdmi_probe(struct platform_device *pdev)
 	ret = drm_bridge_add(&hdmi->bridge);
 	if (ret) {
 		dev_err(dev, "failed to add bridge, ret = %d\n", ret);
-		return ret;
+		goto err_notifier_unregister;
 	}
 
 	ret = mtk_hdmi_clk_enable_audio(hdmi);
@@ -1728,6 +1734,10 @@ static int mtk_drm_hdmi_probe(struct platform_device *pdev)
 
 err_bridge_remove:
 	drm_bridge_remove(&hdmi->bridge);
+err_notifier_unregister:
+	hdmi_notifier_unregister(hdmi->notifier, &hdmi->nb);
+err_notifier_put:
+	hdmi_notifier_put(hdmi->notifier);
 	return ret;
 }
 
-- 
2.10.2




More information about the Linux-mediatek mailing list