@@ -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;
deleted file mode 100644
@@ -1,26 +0,0 @@
-/*
- * Copyright (c) 2014 MediaTek Inc.
- * Author: Jie Qiu <jie.qiu@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 */
@@ -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;
}
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@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