From ad3ae9b12f39429f5752bde06ccd294ffb643f7e Mon Sep 17 00:00:00 2001 From: zador-blood-stained Date: Thu, 28 Sep 2017 22:24:43 +0300 Subject: [PATCH] Add H3/H5 CEC support --- ...-00-dw-hdmi-add-cec-notifier-support.patch | 108 ++++ .../20-01-dw-hdmi-better-clock-control.patch | 96 ++++ ...dw-hdmi-add-missing-cec-notifier-put.patch | 32 ++ .../20-04-dw-hdmi-add-cec-driver.patch | 522 ++++++++++++++++++ .../sunxi-next/20-05-dw-hdmi-cleanup.patch | 76 +++ 5 files changed, 834 insertions(+) create mode 100644 patch/kernel/sunxi-next/20-00-dw-hdmi-add-cec-notifier-support.patch create mode 100644 patch/kernel/sunxi-next/20-01-dw-hdmi-better-clock-control.patch create mode 100644 patch/kernel/sunxi-next/20-02-dw-hdmi-add-missing-cec-notifier-put.patch create mode 100644 patch/kernel/sunxi-next/20-04-dw-hdmi-add-cec-driver.patch create mode 100644 patch/kernel/sunxi-next/20-05-dw-hdmi-cleanup.patch diff --git a/patch/kernel/sunxi-next/20-00-dw-hdmi-add-cec-notifier-support.patch b/patch/kernel/sunxi-next/20-00-dw-hdmi-add-cec-notifier-support.patch new file mode 100644 index 000000000..72fb11944 --- /dev/null +++ b/patch/kernel/sunxi-next/20-00-dw-hdmi-add-cec-notifier-support.patch @@ -0,0 +1,108 @@ +From e84b8d75ac116e67ac3bdb23cafbd03430aa50b9 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Mon, 31 Jul 2017 15:29:41 +0100 +Subject: drm/bridge: dw-hdmi: add cec notifier support + +Add CEC notifier support to the HDMI bridge driver, so that the CEC +part of the IP can receive its physical address. + +Signed-off-by: Russell King +Tested-by: Neil Armstrong +Reviewed-by: Neil Armstrong +Tested-by: Laurent Pinchart +Acked-by: Hans Verkuil +Signed-off-by: Archit Taneja +Link: https://patchwork.freedesktop.org/patch/msgid/E1dcBhV-00088e-8x@rmk-PC.armlinux.org.uk +--- + drivers/gpu/drm/bridge/synopsys/Kconfig | 1 + + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 22 +++++++++++++++++++++- + 2 files changed, 22 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/bridge/synopsys/Kconfig b/drivers/gpu/drm/bridge/synopsys/Kconfig +index a2fb939..7281681 100644 +--- a/drivers/gpu/drm/bridge/synopsys/Kconfig ++++ b/drivers/gpu/drm/bridge/synopsys/Kconfig +@@ -2,6 +2,7 @@ config DRM_DW_HDMI + tristate + select DRM_KMS_HELPER + select REGMAP_MMIO ++ select CEC_CORE if CEC_NOTIFIER + + config DRM_DW_HDMI_AHB_AUDIO + tristate "Synopsys Designware AHB Audio interface" +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +index 60faf2d..67b4af0 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +@@ -36,7 +36,10 @@ + #include "dw-hdmi.h" + #include "dw-hdmi-audio.h" + ++#include ++ + #define DDC_SEGMENT_ADDR 0x30 ++ + #define HDMI_EDID_LEN 512 + + enum hdmi_datamap { +@@ -175,6 +178,8 @@ struct dw_hdmi { + struct regmap *regm; + void (*enable_audio)(struct dw_hdmi *hdmi); + void (*disable_audio)(struct dw_hdmi *hdmi); ++ ++ struct cec_notifier *cec_notifier; + }; + + #define HDMI_IH_PHY_STAT0_RX_SENSE \ +@@ -1896,6 +1901,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector) + hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid); + hdmi->sink_has_audio = drm_detect_monitor_audio(edid); + drm_mode_connector_update_edid_property(connector, edid); ++ cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid); + ret = drm_add_edid_modes(connector, edid); + /* Store the ELD */ + drm_edid_to_eld(connector, edid); +@@ -2119,11 +2125,16 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) + * ask the source to re-read the EDID. + */ + if (intr_stat & +- (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) ++ (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) { + __dw_hdmi_setup_rx_sense(hdmi, + phy_stat & HDMI_PHY_HPD, + phy_stat & HDMI_PHY_RX_SENSE); + ++ if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0) ++ cec_notifier_set_phys_addr(hdmi->cec_notifier, ++ CEC_PHYS_ADDR_INVALID); ++ } ++ + if (intr_stat & HDMI_IH_PHY_STAT0_HPD) { + dev_dbg(hdmi->dev, "EVENT=%s\n", + phy_int_pol & HDMI_PHY_HPD ? "plugin" : "plugout"); +@@ -2376,6 +2387,12 @@ __dw_hdmi_probe(struct platform_device *pdev, + if (ret) + goto err_iahb; + ++ hdmi->cec_notifier = cec_notifier_get(dev); ++ if (!hdmi->cec_notifier) { ++ ret = -ENOMEM; ++ goto err_iahb; ++ } ++ + /* + * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator + * N and cts values before enabling phy +@@ -2452,6 +2469,9 @@ err_iahb: + hdmi->ddc = NULL; + } + ++ if (hdmi->cec_notifier) ++ cec_notifier_put(hdmi->cec_notifier); ++ + clk_disable_unprepare(hdmi->iahb_clk); + err_isfr: + clk_disable_unprepare(hdmi->isfr_clk); +-- +cgit v1.1 + diff --git a/patch/kernel/sunxi-next/20-01-dw-hdmi-better-clock-control.patch b/patch/kernel/sunxi-next/20-01-dw-hdmi-better-clock-control.patch new file mode 100644 index 000000000..a60f43c06 --- /dev/null +++ b/patch/kernel/sunxi-next/20-01-dw-hdmi-better-clock-control.patch @@ -0,0 +1,96 @@ +From 7cc4ab225ae30bd9ec9239e7dba5b2937f526ccc Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Mon, 31 Jul 2017 15:29:46 +0100 +Subject: drm/bridge: dw-hdmi: add better clock disable control + +The video setup path aways sets the clock disable register to a specific +value, which has the effect of disabling the CEC engine. When we add the +CEC driver, this becomes a problem. + +Fix this by only setting/clearing the bits that the video path needs to. + +Reviewed-by: Jose Abreu +Signed-off-by: Russell King +Tested-by: Laurent Pinchart +Acked-by: Hans Verkuil +Signed-off-by: Archit Taneja +Link: https://patchwork.freedesktop.org/patch/msgid/E1dcBha-00088l-DE@rmk-PC.armlinux.org.uk +--- + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 29 ++++++++++++++++++----------- + 1 file changed, 18 insertions(+), 11 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +index 67b4af0..f8171cd 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +@@ -166,6 +166,7 @@ struct dw_hdmi { + bool bridge_is_on; /* indicates the bridge is on */ + bool rxsense; /* rxsense state */ + u8 phy_mask; /* desired phy int mask settings */ ++ u8 mc_clkdis; /* clock disable register */ + + spinlock_t audio_lock; + struct mutex audio_mutex; +@@ -551,8 +552,11 @@ EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate); + + static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable) + { +- hdmi_modb(hdmi, enable ? 0 : HDMI_MC_CLKDIS_AUDCLK_DISABLE, +- HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); ++ if (enable) ++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE; ++ else ++ hdmi->mc_clkdis |= HDMI_MC_CLKDIS_AUDCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + } + + static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi) +@@ -1574,8 +1578,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, + /* HDMI Initialization Step B.4 */ + static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) + { +- u8 clkdis; +- + /* control period minimum duration */ + hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR); + hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR); +@@ -1587,17 +1589,21 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) + hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM); + + /* Enable pixel clock and tmds data path */ +- clkdis = 0x7F; +- clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; +- hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); ++ hdmi->mc_clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE | ++ HDMI_MC_CLKDIS_CSCCLK_DISABLE | ++ HDMI_MC_CLKDIS_AUDCLK_DISABLE | ++ HDMI_MC_CLKDIS_PREPCLK_DISABLE | ++ HDMI_MC_CLKDIS_TMDSCLK_DISABLE; ++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + +- clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; +- hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); ++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + + /* Enable csc path */ + if (is_color_space_conversion(hdmi)) { +- clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; +- hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); ++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + } + + /* Enable color space conversion if needed */ +@@ -2272,6 +2278,7 @@ __dw_hdmi_probe(struct platform_device *pdev, + hdmi->disabled = true; + hdmi->rxsense = true; + hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE); ++ hdmi->mc_clkdis = 0x7f; + + mutex_init(&hdmi->mutex); + mutex_init(&hdmi->audio_mutex); +-- +cgit v1.1 + diff --git a/patch/kernel/sunxi-next/20-02-dw-hdmi-add-missing-cec-notifier-put.patch b/patch/kernel/sunxi-next/20-02-dw-hdmi-add-missing-cec-notifier-put.patch new file mode 100644 index 000000000..2e3a618b8 --- /dev/null +++ b/patch/kernel/sunxi-next/20-02-dw-hdmi-add-missing-cec-notifier-put.patch @@ -0,0 +1,32 @@ +From e383bf85d32b5953f34fa0e3d619886ec49413b1 Mon Sep 17 00:00:00 2001 +From: Hans Verkuil +Date: Mon, 7 Aug 2017 09:20:35 +0200 +Subject: drm/bridge: dw-hdmi: add missing cec_notifier_put + +The __dw_hdmi_remove() function was missing a call to cec_notifier_put +to balance the cec_notifier_get in the probe function. + +Signed-off-by: Hans Verkuil +Signed-off-by: Archit Taneja +Link: https://patchwork.freedesktop.org/patch/msgid/a7688d13-2d61-ed16-f2df-28cbb5007f38@xs4all.nl +--- + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +index f8171cd..a24ec4a 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +@@ -2496,6 +2496,9 @@ static void __dw_hdmi_remove(struct dw_hdmi *hdmi) + /* Disable all interrupts */ + hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); + ++ if (hdmi->cec_notifier) ++ cec_notifier_put(hdmi->cec_notifier); ++ + clk_disable_unprepare(hdmi->iahb_clk); + clk_disable_unprepare(hdmi->isfr_clk); + +-- +cgit v1.1 + diff --git a/patch/kernel/sunxi-next/20-04-dw-hdmi-add-cec-driver.patch b/patch/kernel/sunxi-next/20-04-dw-hdmi-add-cec-driver.patch new file mode 100644 index 000000000..a6f41b1d1 --- /dev/null +++ b/patch/kernel/sunxi-next/20-04-dw-hdmi-add-cec-driver.patch @@ -0,0 +1,522 @@ +From a616e63c56ef35365b781f08c7d437840f56940f Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Wed, 2 Aug 2017 20:41:07 +0200 +Subject: drm/bridge: dw-hdmi: add cec driver + +Add a CEC driver for the dw-hdmi hardware. + +Reviewed-by: Neil Armstrong +Signed-off-by: Russell King +[hans.verkuil: unsigned -> unsigned int] +[hans.verkuil: cec_transmit_done -> cec_transmit_attempt_done] +[hans.verkuil: add missing CEC_CAP_PASSTHROUGH] +Acked-by: Hans Verkuil +Tested-by: Hans Verkuil +Tested-by: Laurent Pinchart +Signed-off-by: Archit Taneja +Link: https://patchwork.freedesktop.org/patch/msgid/20170802184108.7913-4-hverkuil@xs4all.nl +--- + drivers/gpu/drm/bridge/synopsys/Kconfig | 9 + + drivers/gpu/drm/bridge/synopsys/Makefile | 1 + + drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c | 327 ++++++++++++++++++++++++++ + drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.h | 19 ++ + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 42 +++- + drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 1 + + 6 files changed, 398 insertions(+), 1 deletion(-) + create mode 100644 drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c + create mode 100644 drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.h + +diff --git a/drivers/gpu/drm/bridge/synopsys/Kconfig b/drivers/gpu/drm/bridge/synopsys/Kconfig +index 7281681..3cc53b4 100644 +--- a/drivers/gpu/drm/bridge/synopsys/Kconfig ++++ b/drivers/gpu/drm/bridge/synopsys/Kconfig +@@ -22,3 +22,12 @@ config DRM_DW_HDMI_I2S_AUDIO + help + Support the I2S Audio interface which is part of the Synopsys + Designware HDMI block. ++ ++config DRM_DW_HDMI_CEC ++ tristate "Synopsis Designware CEC interface" ++ depends on DRM_DW_HDMI ++ select CEC_CORE ++ select CEC_NOTIFIER ++ help ++ Support the CE interface which is part of the Synopsys ++ Designware HDMI block. +diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile +index 5f57d36..5dad97d 100644 +--- a/drivers/gpu/drm/bridge/synopsys/Makefile ++++ b/drivers/gpu/drm/bridge/synopsys/Makefile +@@ -3,3 +3,4 @@ + obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o + obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o + obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o ++obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c +new file mode 100644 +index 0000000..6c32351 +--- /dev/null ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c +@@ -0,0 +1,327 @@ ++/* ++ * Designware HDMI CEC driver ++ * ++ * Copyright (C) 2015-2017 Russell King. ++ * ++ * 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. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#include "dw-hdmi-cec.h" ++ ++enum { ++ HDMI_IH_CEC_STAT0 = 0x0106, ++ HDMI_IH_MUTE_CEC_STAT0 = 0x0186, ++ ++ HDMI_CEC_CTRL = 0x7d00, ++ CEC_CTRL_START = BIT(0), ++ CEC_CTRL_FRAME_TYP = 3 << 1, ++ CEC_CTRL_RETRY = 0 << 1, ++ CEC_CTRL_NORMAL = 1 << 1, ++ CEC_CTRL_IMMED = 2 << 1, ++ ++ HDMI_CEC_STAT = 0x7d01, ++ CEC_STAT_DONE = BIT(0), ++ CEC_STAT_EOM = BIT(1), ++ CEC_STAT_NACK = BIT(2), ++ CEC_STAT_ARBLOST = BIT(3), ++ CEC_STAT_ERROR_INIT = BIT(4), ++ CEC_STAT_ERROR_FOLL = BIT(5), ++ CEC_STAT_WAKEUP = BIT(6), ++ ++ HDMI_CEC_MASK = 0x7d02, ++ HDMI_CEC_POLARITY = 0x7d03, ++ HDMI_CEC_INT = 0x7d04, ++ HDMI_CEC_ADDR_L = 0x7d05, ++ HDMI_CEC_ADDR_H = 0x7d06, ++ HDMI_CEC_TX_CNT = 0x7d07, ++ HDMI_CEC_RX_CNT = 0x7d08, ++ HDMI_CEC_TX_DATA0 = 0x7d10, ++ HDMI_CEC_RX_DATA0 = 0x7d20, ++ HDMI_CEC_LOCK = 0x7d30, ++ HDMI_CEC_WKUPCTRL = 0x7d31, ++}; ++ ++struct dw_hdmi_cec { ++ struct dw_hdmi *hdmi; ++ const struct dw_hdmi_cec_ops *ops; ++ u32 addresses; ++ struct cec_adapter *adap; ++ struct cec_msg rx_msg; ++ unsigned int tx_status; ++ bool tx_done; ++ bool rx_done; ++ struct cec_notifier *notify; ++ int irq; ++}; ++ ++static void dw_hdmi_write(struct dw_hdmi_cec *cec, u8 val, int offset) ++{ ++ cec->ops->write(cec->hdmi, val, offset); ++} ++ ++static u8 dw_hdmi_read(struct dw_hdmi_cec *cec, int offset) ++{ ++ return cec->ops->read(cec->hdmi, offset); ++} ++ ++static int dw_hdmi_cec_log_addr(struct cec_adapter *adap, u8 logical_addr) ++{ ++ struct dw_hdmi_cec *cec = cec_get_drvdata(adap); ++ ++ if (logical_addr == CEC_LOG_ADDR_INVALID) ++ cec->addresses = 0; ++ else ++ cec->addresses |= BIT(logical_addr) | BIT(15); ++ ++ dw_hdmi_write(cec, cec->addresses & 255, HDMI_CEC_ADDR_L); ++ dw_hdmi_write(cec, cec->addresses >> 8, HDMI_CEC_ADDR_H); ++ ++ return 0; ++} ++ ++static int dw_hdmi_cec_transmit(struct cec_adapter *adap, u8 attempts, ++ u32 signal_free_time, struct cec_msg *msg) ++{ ++ struct dw_hdmi_cec *cec = cec_get_drvdata(adap); ++ unsigned int i, ctrl; ++ ++ switch (signal_free_time) { ++ case CEC_SIGNAL_FREE_TIME_RETRY: ++ ctrl = CEC_CTRL_RETRY; ++ break; ++ case CEC_SIGNAL_FREE_TIME_NEW_INITIATOR: ++ default: ++ ctrl = CEC_CTRL_NORMAL; ++ break; ++ case CEC_SIGNAL_FREE_TIME_NEXT_XFER: ++ ctrl = CEC_CTRL_IMMED; ++ break; ++ } ++ ++ for (i = 0; i < msg->len; i++) ++ dw_hdmi_write(cec, msg->msg[i], HDMI_CEC_TX_DATA0 + i); ++ ++ dw_hdmi_write(cec, msg->len, HDMI_CEC_TX_CNT); ++ dw_hdmi_write(cec, ctrl | CEC_CTRL_START, HDMI_CEC_CTRL); ++ ++ return 0; ++} ++ ++static irqreturn_t dw_hdmi_cec_hardirq(int irq, void *data) ++{ ++ struct cec_adapter *adap = data; ++ struct dw_hdmi_cec *cec = cec_get_drvdata(adap); ++ unsigned int stat = dw_hdmi_read(cec, HDMI_IH_CEC_STAT0); ++ irqreturn_t ret = IRQ_HANDLED; ++ ++ if (stat == 0) ++ return IRQ_NONE; ++ ++ dw_hdmi_write(cec, stat, HDMI_IH_CEC_STAT0); ++ ++ if (stat & CEC_STAT_ERROR_INIT) { ++ cec->tx_status = CEC_TX_STATUS_ERROR; ++ cec->tx_done = true; ++ ret = IRQ_WAKE_THREAD; ++ } else if (stat & CEC_STAT_DONE) { ++ cec->tx_status = CEC_TX_STATUS_OK; ++ cec->tx_done = true; ++ ret = IRQ_WAKE_THREAD; ++ } else if (stat & CEC_STAT_NACK) { ++ cec->tx_status = CEC_TX_STATUS_NACK; ++ cec->tx_done = true; ++ ret = IRQ_WAKE_THREAD; ++ } ++ ++ if (stat & CEC_STAT_EOM) { ++ unsigned int len, i; ++ ++ len = dw_hdmi_read(cec, HDMI_CEC_RX_CNT); ++ if (len > sizeof(cec->rx_msg.msg)) ++ len = sizeof(cec->rx_msg.msg); ++ ++ for (i = 0; i < len; i++) ++ cec->rx_msg.msg[i] = ++ dw_hdmi_read(cec, HDMI_CEC_RX_DATA0 + i); ++ ++ dw_hdmi_write(cec, 0, HDMI_CEC_LOCK); ++ ++ cec->rx_msg.len = len; ++ smp_wmb(); ++ cec->rx_done = true; ++ ++ ret = IRQ_WAKE_THREAD; ++ } ++ ++ return ret; ++} ++ ++static irqreturn_t dw_hdmi_cec_thread(int irq, void *data) ++{ ++ struct cec_adapter *adap = data; ++ struct dw_hdmi_cec *cec = cec_get_drvdata(adap); ++ ++ if (cec->tx_done) { ++ cec->tx_done = false; ++ cec_transmit_attempt_done(adap, cec->tx_status); ++ } ++ if (cec->rx_done) { ++ cec->rx_done = false; ++ smp_rmb(); ++ cec_received_msg(adap, &cec->rx_msg); ++ } ++ return IRQ_HANDLED; ++} ++ ++static int dw_hdmi_cec_enable(struct cec_adapter *adap, bool enable) ++{ ++ struct dw_hdmi_cec *cec = cec_get_drvdata(adap); ++ ++ if (!enable) { ++ dw_hdmi_write(cec, ~0, HDMI_CEC_MASK); ++ dw_hdmi_write(cec, ~0, HDMI_IH_MUTE_CEC_STAT0); ++ dw_hdmi_write(cec, 0, HDMI_CEC_POLARITY); ++ ++ cec->ops->disable(cec->hdmi); ++ } else { ++ unsigned int irqs; ++ ++ dw_hdmi_write(cec, 0, HDMI_CEC_CTRL); ++ dw_hdmi_write(cec, ~0, HDMI_IH_CEC_STAT0); ++ dw_hdmi_write(cec, 0, HDMI_CEC_LOCK); ++ ++ dw_hdmi_cec_log_addr(cec->adap, CEC_LOG_ADDR_INVALID); ++ ++ cec->ops->enable(cec->hdmi); ++ ++ irqs = CEC_STAT_ERROR_INIT | CEC_STAT_NACK | CEC_STAT_EOM | ++ CEC_STAT_DONE; ++ dw_hdmi_write(cec, irqs, HDMI_CEC_POLARITY); ++ dw_hdmi_write(cec, ~irqs, HDMI_CEC_MASK); ++ dw_hdmi_write(cec, ~irqs, HDMI_IH_MUTE_CEC_STAT0); ++ } ++ return 0; ++} ++ ++static const struct cec_adap_ops dw_hdmi_cec_ops = { ++ .adap_enable = dw_hdmi_cec_enable, ++ .adap_log_addr = dw_hdmi_cec_log_addr, ++ .adap_transmit = dw_hdmi_cec_transmit, ++}; ++ ++static void dw_hdmi_cec_del(void *data) ++{ ++ struct dw_hdmi_cec *cec = data; ++ ++ cec_delete_adapter(cec->adap); ++} ++ ++static int dw_hdmi_cec_probe(struct platform_device *pdev) ++{ ++ struct dw_hdmi_cec_data *data = dev_get_platdata(&pdev->dev); ++ struct dw_hdmi_cec *cec; ++ int ret; ++ ++ if (!data) ++ return -ENXIO; ++ ++ /* ++ * Our device is just a convenience - we want to link to the real ++ * hardware device here, so that userspace can see the association ++ * between the HDMI hardware and its associated CEC chardev. ++ */ ++ cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL); ++ if (!cec) ++ return -ENOMEM; ++ ++ cec->irq = data->irq; ++ cec->ops = data->ops; ++ cec->hdmi = data->hdmi; ++ ++ platform_set_drvdata(pdev, cec); ++ ++ dw_hdmi_write(cec, 0, HDMI_CEC_TX_CNT); ++ dw_hdmi_write(cec, ~0, HDMI_CEC_MASK); ++ dw_hdmi_write(cec, ~0, HDMI_IH_MUTE_CEC_STAT0); ++ dw_hdmi_write(cec, 0, HDMI_CEC_POLARITY); ++ ++ cec->adap = cec_allocate_adapter(&dw_hdmi_cec_ops, cec, "dw_hdmi", ++ CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | ++ CEC_CAP_RC | CEC_CAP_PASSTHROUGH, ++ CEC_MAX_LOG_ADDRS); ++ if (IS_ERR(cec->adap)) ++ return PTR_ERR(cec->adap); ++ ++ /* override the module pointer */ ++ cec->adap->owner = THIS_MODULE; ++ ++ ret = devm_add_action(&pdev->dev, dw_hdmi_cec_del, cec); ++ if (ret) { ++ cec_delete_adapter(cec->adap); ++ return ret; ++ } ++ ++ ret = devm_request_threaded_irq(&pdev->dev, cec->irq, ++ dw_hdmi_cec_hardirq, ++ dw_hdmi_cec_thread, IRQF_SHARED, ++ "dw-hdmi-cec", cec->adap); ++ if (ret < 0) ++ return ret; ++ ++ cec->notify = cec_notifier_get(pdev->dev.parent); ++ if (!cec->notify) ++ return -ENOMEM; ++ ++ ret = cec_register_adapter(cec->adap, pdev->dev.parent); ++ if (ret < 0) { ++ cec_notifier_put(cec->notify); ++ return ret; ++ } ++ ++ /* ++ * CEC documentation says we must not call cec_delete_adapter ++ * after a successful call to cec_register_adapter(). ++ */ ++ devm_remove_action(&pdev->dev, dw_hdmi_cec_del, cec); ++ ++ cec_register_cec_notifier(cec->adap, cec->notify); ++ ++ return 0; ++} ++ ++static int dw_hdmi_cec_remove(struct platform_device *pdev) ++{ ++ struct dw_hdmi_cec *cec = platform_get_drvdata(pdev); ++ ++ cec_unregister_adapter(cec->adap); ++ cec_notifier_put(cec->notify); ++ ++ return 0; ++} ++ ++static struct platform_driver dw_hdmi_cec_driver = { ++ .probe = dw_hdmi_cec_probe, ++ .remove = dw_hdmi_cec_remove, ++ .driver = { ++ .name = "dw-hdmi-cec", ++ }, ++}; ++module_platform_driver(dw_hdmi_cec_driver); ++ ++MODULE_AUTHOR("Russell King "); ++MODULE_DESCRIPTION("Synopsys Designware HDMI CEC driver for i.MX"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS(PLATFORM_MODULE_PREFIX "dw-hdmi-cec"); +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.h +new file mode 100644 +index 0000000..cf4dc12 +--- /dev/null ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.h +@@ -0,0 +1,19 @@ ++#ifndef DW_HDMI_CEC_H ++#define DW_HDMI_CEC_H ++ ++struct dw_hdmi; ++ ++struct dw_hdmi_cec_ops { ++ void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); ++ u8 (*read)(struct dw_hdmi *hdmi, int offset); ++ void (*enable)(struct dw_hdmi *hdmi); ++ void (*disable)(struct dw_hdmi *hdmi); ++}; ++ ++struct dw_hdmi_cec_data { ++ struct dw_hdmi *hdmi; ++ const struct dw_hdmi_cec_ops *ops; ++ int irq; ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +index a24ec4a..6aae581 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +@@ -35,6 +35,7 @@ + + #include "dw-hdmi.h" + #include "dw-hdmi-audio.h" ++#include "dw-hdmi-cec.h" + + #include + +@@ -133,6 +134,7 @@ struct dw_hdmi { + unsigned int version; + + struct platform_device *audio; ++ struct platform_device *cec; + struct device *dev; + struct clk *isfr_clk; + struct clk *iahb_clk; +@@ -1794,7 +1796,6 @@ static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi) + hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK); + hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK); + hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK); +- hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK); + hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT); + hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT); + +@@ -2236,6 +2237,29 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi) + return -ENODEV; + } + ++static void dw_hdmi_cec_enable(struct dw_hdmi *hdmi) ++{ ++ mutex_lock(&hdmi->mutex); ++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); ++ mutex_unlock(&hdmi->mutex); ++} ++ ++static void dw_hdmi_cec_disable(struct dw_hdmi *hdmi) ++{ ++ mutex_lock(&hdmi->mutex); ++ hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CECCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); ++ mutex_unlock(&hdmi->mutex); ++} ++ ++static const struct dw_hdmi_cec_ops dw_hdmi_cec_ops = { ++ .write = hdmi_writeb, ++ .read = hdmi_readb, ++ .enable = dw_hdmi_cec_enable, ++ .disable = dw_hdmi_cec_disable, ++}; ++ + static const struct regmap_config hdmi_regmap_8bit_config = { + .reg_bits = 32, + .val_bits = 8, +@@ -2258,6 +2282,7 @@ __dw_hdmi_probe(struct platform_device *pdev, + struct device_node *np = dev->of_node; + struct platform_device_info pdevinfo; + struct device_node *ddc_node; ++ struct dw_hdmi_cec_data cec; + struct dw_hdmi *hdmi; + struct resource *iores = NULL; + int irq; +@@ -2462,6 +2487,19 @@ __dw_hdmi_probe(struct platform_device *pdev, + hdmi->audio = platform_device_register_full(&pdevinfo); + } + ++ if (config0 & HDMI_CONFIG0_CEC) { ++ cec.hdmi = hdmi; ++ cec.ops = &dw_hdmi_cec_ops; ++ cec.irq = irq; ++ ++ pdevinfo.name = "dw-hdmi-cec"; ++ pdevinfo.data = &cec; ++ pdevinfo.size_data = sizeof(cec); ++ pdevinfo.dma_mask = 0; ++ ++ hdmi->cec = platform_device_register_full(&pdevinfo); ++ } ++ + /* Reset HDMI DDC I2C master controller and mute I2CM interrupts */ + if (hdmi->i2c) + dw_hdmi_i2c_init(hdmi); +@@ -2492,6 +2530,8 @@ static void __dw_hdmi_remove(struct dw_hdmi *hdmi) + { + if (hdmi->audio && !IS_ERR(hdmi->audio)) + platform_device_unregister(hdmi->audio); ++ if (!IS_ERR(hdmi->cec)) ++ platform_device_unregister(hdmi->cec); + + /* Disable all interrupts */ + hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +index c59f87e..69644c8 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +@@ -555,6 +555,7 @@ enum { + + /* CONFIG0_ID field values */ + HDMI_CONFIG0_I2S = 0x10, ++ HDMI_CONFIG0_CEC = 0x02, + + /* CONFIG1_ID field values */ + HDMI_CONFIG1_AHB = 0x01, +-- +cgit v1.1 + diff --git a/patch/kernel/sunxi-next/20-05-dw-hdmi-cleanup.patch b/patch/kernel/sunxi-next/20-05-dw-hdmi-cleanup.patch new file mode 100644 index 000000000..1666bf0ae --- /dev/null +++ b/patch/kernel/sunxi-next/20-05-dw-hdmi-cleanup.patch @@ -0,0 +1,76 @@ +From 2356c573ba0eca736ec156b82ca6a6b37b8fe609 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Wed, 2 Aug 2017 20:41:08 +0200 +Subject: drm/bridge: dw-hdmi: remove CEC engine register definitions + +We don't need the CEC engine register definitions, so let's remove them. + +Signed-off-by: Russell King +Acked-by: Hans Verkuil +Tested-by: Hans Verkuil +Tested-by: Laurent Pinchart +Signed-off-by: Archit Taneja +Link: https://patchwork.freedesktop.org/patch/msgid/20170802184108.7913-5-hverkuil@xs4all.nl +--- + drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 45 ------------------------------- + 1 file changed, 45 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +index 69644c8..9d90eb9 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +@@ -478,51 +478,6 @@ + #define HDMI_A_PRESETUP 0x501A + #define HDMI_A_SRM_BASE 0x5020 + +-/* CEC Engine Registers */ +-#define HDMI_CEC_CTRL 0x7D00 +-#define HDMI_CEC_STAT 0x7D01 +-#define HDMI_CEC_MASK 0x7D02 +-#define HDMI_CEC_POLARITY 0x7D03 +-#define HDMI_CEC_INT 0x7D04 +-#define HDMI_CEC_ADDR_L 0x7D05 +-#define HDMI_CEC_ADDR_H 0x7D06 +-#define HDMI_CEC_TX_CNT 0x7D07 +-#define HDMI_CEC_RX_CNT 0x7D08 +-#define HDMI_CEC_TX_DATA0 0x7D10 +-#define HDMI_CEC_TX_DATA1 0x7D11 +-#define HDMI_CEC_TX_DATA2 0x7D12 +-#define HDMI_CEC_TX_DATA3 0x7D13 +-#define HDMI_CEC_TX_DATA4 0x7D14 +-#define HDMI_CEC_TX_DATA5 0x7D15 +-#define HDMI_CEC_TX_DATA6 0x7D16 +-#define HDMI_CEC_TX_DATA7 0x7D17 +-#define HDMI_CEC_TX_DATA8 0x7D18 +-#define HDMI_CEC_TX_DATA9 0x7D19 +-#define HDMI_CEC_TX_DATA10 0x7D1a +-#define HDMI_CEC_TX_DATA11 0x7D1b +-#define HDMI_CEC_TX_DATA12 0x7D1c +-#define HDMI_CEC_TX_DATA13 0x7D1d +-#define HDMI_CEC_TX_DATA14 0x7D1e +-#define HDMI_CEC_TX_DATA15 0x7D1f +-#define HDMI_CEC_RX_DATA0 0x7D20 +-#define HDMI_CEC_RX_DATA1 0x7D21 +-#define HDMI_CEC_RX_DATA2 0x7D22 +-#define HDMI_CEC_RX_DATA3 0x7D23 +-#define HDMI_CEC_RX_DATA4 0x7D24 +-#define HDMI_CEC_RX_DATA5 0x7D25 +-#define HDMI_CEC_RX_DATA6 0x7D26 +-#define HDMI_CEC_RX_DATA7 0x7D27 +-#define HDMI_CEC_RX_DATA8 0x7D28 +-#define HDMI_CEC_RX_DATA9 0x7D29 +-#define HDMI_CEC_RX_DATA10 0x7D2a +-#define HDMI_CEC_RX_DATA11 0x7D2b +-#define HDMI_CEC_RX_DATA12 0x7D2c +-#define HDMI_CEC_RX_DATA13 0x7D2d +-#define HDMI_CEC_RX_DATA14 0x7D2e +-#define HDMI_CEC_RX_DATA15 0x7D2f +-#define HDMI_CEC_LOCK 0x7D30 +-#define HDMI_CEC_WKUPCTRL 0x7D31 +- + /* I2C Master Registers (E-DDC) */ + #define HDMI_I2CM_SLAVE 0x7E00 + #define HDMI_I2CM_ADDRESS 0x7E01 +-- +cgit v1.1 +