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

Reply via email to