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