drm/vc4: add HDMI CEC support
authorHans Verkuil <hans.verkuil@cisco.com>
Sun, 16 Jul 2017 10:48:04 +0000 (12:48 +0200)
committerEric Anholt <eric@anholt.net>
Wed, 26 Jul 2017 19:15:12 +0000 (12:15 -0700)
This patch adds support to VC4 for CEC.  It is under a separate
Kconfig option to keep everyone using VC4 from needing to pull in the
CEC core.

Thanks to Eric Anholt for providing me with the CEC register information.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Eric Anholt <eric@anholt.net>
Link: https://patchwork.freedesktop.org/patch/msgid/20170716104804.48308-4-hverkuil@xs4all.nl
drivers/gpu/drm/vc4/Kconfig
drivers/gpu/drm/vc4/vc4_hdmi.c
drivers/gpu/drm/vc4/vc4_regs.h

index 4361bdcfd28a2177b762922ebe62ee15d1435fcb..fdae18aeab4fe582d99e576d5600bed3b5afc443 100644 (file)
@@ -19,3 +19,11 @@ config DRM_VC4
          This driver requires that "avoid_warnings=2" be present in
          the config.txt for the firmware, to keep it from smashing
          our display setup.
+
+config DRM_VC4_HDMI_CEC
+       bool "Broadcom VC4 HDMI CEC Support"
+       depends on DRM_VC4
+       select CEC_CORE
+       help
+         Choose this option if you have a Broadcom VC4 GPU
+         and want to use CEC.
index 92556588545ff32f61fe77bb8471e1e22478ba76..da5ee8047e51f5a9fe77f2f1de8500736807a260 100644 (file)
 #include <sound/pcm_drm_eld.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include "media/cec.h"
 #include "vc4_drv.h"
 #include "vc4_regs.h"
 
+#define HSM_CLOCK_FREQ 163682864
+#define CEC_CLOCK_FREQ 40000
+#define CEC_CLOCK_DIV  (HSM_CLOCK_FREQ / CEC_CLOCK_FREQ)
+
 /* HDMI audio information */
 struct vc4_hdmi_audio {
        struct snd_soc_card card;
@@ -85,6 +90,11 @@ struct vc4_hdmi {
        int hpd_gpio;
        bool hpd_active_low;
 
+       struct cec_adapter *cec_adap;
+       struct cec_msg cec_rx_msg;
+       bool cec_tx_ok;
+       bool cec_irq_was_rx;
+
        struct clk *pixel_clock;
        struct clk *hsm_clock;
 };
@@ -149,6 +159,23 @@ static const struct {
        HDMI_REG(VC4_HDMI_VERTB1),
        HDMI_REG(VC4_HDMI_TX_PHY_RESET_CTL),
        HDMI_REG(VC4_HDMI_TX_PHY_CTL0),
+
+       HDMI_REG(VC4_HDMI_CEC_CNTRL_1),
+       HDMI_REG(VC4_HDMI_CEC_CNTRL_2),
+       HDMI_REG(VC4_HDMI_CEC_CNTRL_3),
+       HDMI_REG(VC4_HDMI_CEC_CNTRL_4),
+       HDMI_REG(VC4_HDMI_CEC_CNTRL_5),
+       HDMI_REG(VC4_HDMI_CPU_STATUS),
+       HDMI_REG(VC4_HDMI_CPU_MASK_STATUS),
+
+       HDMI_REG(VC4_HDMI_CEC_RX_DATA_1),
+       HDMI_REG(VC4_HDMI_CEC_RX_DATA_2),
+       HDMI_REG(VC4_HDMI_CEC_RX_DATA_3),
+       HDMI_REG(VC4_HDMI_CEC_RX_DATA_4),
+       HDMI_REG(VC4_HDMI_CEC_TX_DATA_1),
+       HDMI_REG(VC4_HDMI_CEC_TX_DATA_2),
+       HDMI_REG(VC4_HDMI_CEC_TX_DATA_3),
+       HDMI_REG(VC4_HDMI_CEC_TX_DATA_4),
 };
 
 static const struct {
@@ -216,8 +243,8 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
                if (gpio_get_value_cansleep(vc4->hdmi->hpd_gpio) ^
                    vc4->hdmi->hpd_active_low)
                        return connector_status_connected;
-               else
-                       return connector_status_disconnected;
+               cec_phys_addr_invalidate(vc4->hdmi->cec_adap);
+               return connector_status_disconnected;
        }
 
        if (drm_probe_ddc(vc4->hdmi->ddc))
@@ -225,8 +252,8 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
 
        if (HDMI_READ(VC4_HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED)
                return connector_status_connected;
-       else
-               return connector_status_disconnected;
+       cec_phys_addr_invalidate(vc4->hdmi->cec_adap);
+       return connector_status_disconnected;
 }
 
 static void vc4_hdmi_connector_destroy(struct drm_connector *connector)
@@ -247,6 +274,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
        struct edid *edid;
 
        edid = drm_get_edid(connector, vc4->hdmi->ddc);
+       cec_s_phys_addr_from_edid(vc4->hdmi->cec_adap, edid);
        if (!edid)
                return -ENODEV;
 
@@ -1121,6 +1149,159 @@ static void vc4_hdmi_audio_cleanup(struct vc4_hdmi *hdmi)
                snd_soc_unregister_codec(dev);
 }
 
+#ifdef CONFIG_DRM_VC4_HDMI_CEC
+static irqreturn_t vc4_cec_irq_handler_thread(int irq, void *priv)
+{
+       struct vc4_dev *vc4 = priv;
+       struct vc4_hdmi *hdmi = vc4->hdmi;
+
+       if (hdmi->cec_irq_was_rx) {
+               if (hdmi->cec_rx_msg.len)
+                       cec_received_msg(hdmi->cec_adap, &hdmi->cec_rx_msg);
+       } else if (hdmi->cec_tx_ok) {
+               cec_transmit_done(hdmi->cec_adap, CEC_TX_STATUS_OK,
+                                 0, 0, 0, 0);
+       } else {
+               /*
+                * This CEC implementation makes 1 retry, so if we
+                * get a NACK, then that means it made 2 attempts.
+                */
+               cec_transmit_done(hdmi->cec_adap, CEC_TX_STATUS_NACK,
+                                 0, 2, 0, 0);
+       }
+       return IRQ_HANDLED;
+}
+
+static void vc4_cec_read_msg(struct vc4_dev *vc4, u32 cntrl1)
+{
+       struct cec_msg *msg = &vc4->hdmi->cec_rx_msg;
+       unsigned int i;
+
+       msg->len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK) >>
+                                       VC4_HDMI_CEC_REC_WRD_CNT_SHIFT);
+       for (i = 0; i < msg->len; i += 4) {
+               u32 val = HDMI_READ(VC4_HDMI_CEC_RX_DATA_1 + i);
+
+               msg->msg[i] = val & 0xff;
+               msg->msg[i + 1] = (val >> 8) & 0xff;
+               msg->msg[i + 2] = (val >> 16) & 0xff;
+               msg->msg[i + 3] = (val >> 24) & 0xff;
+       }
+}
+
+static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
+{
+       struct vc4_dev *vc4 = priv;
+       struct vc4_hdmi *hdmi = vc4->hdmi;
+       u32 stat = HDMI_READ(VC4_HDMI_CPU_STATUS);
+       u32 cntrl1, cntrl5;
+
+       if (!(stat & VC4_HDMI_CPU_CEC))
+               return IRQ_NONE;
+       hdmi->cec_rx_msg.len = 0;
+       cntrl1 = HDMI_READ(VC4_HDMI_CEC_CNTRL_1);
+       cntrl5 = HDMI_READ(VC4_HDMI_CEC_CNTRL_5);
+       hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT;
+       if (hdmi->cec_irq_was_rx) {
+               vc4_cec_read_msg(vc4, cntrl1);
+               cntrl1 |= VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
+               HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1);
+               cntrl1 &= ~VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
+       } else {
+               hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD;
+               cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
+       }
+       HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1);
+       HDMI_WRITE(VC4_HDMI_CPU_CLEAR, VC4_HDMI_CPU_CEC);
+
+       return IRQ_WAKE_THREAD;
+}
+
+static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+       struct vc4_dev *vc4 = cec_get_drvdata(adap);
+       /* clock period in microseconds */
+       const u32 usecs = 1000000 / CEC_CLOCK_FREQ;
+       u32 val = HDMI_READ(VC4_HDMI_CEC_CNTRL_5);
+
+       val &= ~(VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET |
+                VC4_HDMI_CEC_CNT_TO_4700_US_MASK |
+                VC4_HDMI_CEC_CNT_TO_4500_US_MASK);
+       val |= ((4700 / usecs) << VC4_HDMI_CEC_CNT_TO_4700_US_SHIFT) |
+              ((4500 / usecs) << VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT);
+
+       if (enable) {
+               HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val |
+                          VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
+               HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val);
+               HDMI_WRITE(VC4_HDMI_CEC_CNTRL_2,
+                        ((1500 / usecs) << VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT) |
+                        ((1300 / usecs) << VC4_HDMI_CEC_CNT_TO_1300_US_SHIFT) |
+                        ((800 / usecs) << VC4_HDMI_CEC_CNT_TO_800_US_SHIFT) |
+                        ((600 / usecs) << VC4_HDMI_CEC_CNT_TO_600_US_SHIFT) |
+                        ((400 / usecs) << VC4_HDMI_CEC_CNT_TO_400_US_SHIFT));
+               HDMI_WRITE(VC4_HDMI_CEC_CNTRL_3,
+                        ((2750 / usecs) << VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT) |
+                        ((2400 / usecs) << VC4_HDMI_CEC_CNT_TO_2400_US_SHIFT) |
+                        ((2050 / usecs) << VC4_HDMI_CEC_CNT_TO_2050_US_SHIFT) |
+                        ((1700 / usecs) << VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT));
+               HDMI_WRITE(VC4_HDMI_CEC_CNTRL_4,
+                        ((4300 / usecs) << VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT) |
+                        ((3900 / usecs) << VC4_HDMI_CEC_CNT_TO_3900_US_SHIFT) |
+                        ((3600 / usecs) << VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT) |
+                        ((3500 / usecs) << VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT));
+
+               HDMI_WRITE(VC4_HDMI_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC);
+       } else {
+               HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, VC4_HDMI_CPU_CEC);
+               HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val |
+                          VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
+       }
+       return 0;
+}
+
+static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+{
+       struct vc4_dev *vc4 = cec_get_drvdata(adap);
+
+       HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1,
+                  (HDMI_READ(VC4_HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) |
+                  (log_addr & 0xf) << VC4_HDMI_CEC_ADDR_SHIFT);
+       return 0;
+}
+
+static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+                                     u32 signal_free_time, struct cec_msg *msg)
+{
+       struct vc4_dev *vc4 = cec_get_drvdata(adap);
+       u32 val;
+       unsigned int i;
+
+       for (i = 0; i < msg->len; i += 4)
+               HDMI_WRITE(VC4_HDMI_CEC_TX_DATA_1 + i,
+                          (msg->msg[i]) |
+                          (msg->msg[i + 1] << 8) |
+                          (msg->msg[i + 2] << 16) |
+                          (msg->msg[i + 3] << 24));
+
+       val = HDMI_READ(VC4_HDMI_CEC_CNTRL_1);
+       val &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
+       HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, val);
+       val &= ~VC4_HDMI_CEC_MESSAGE_LENGTH_MASK;
+       val |= (msg->len - 1) << VC4_HDMI_CEC_MESSAGE_LENGTH_SHIFT;
+       val |= VC4_HDMI_CEC_START_XMIT_BEGIN;
+
+       HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, val);
+       return 0;
+}
+
+static const struct cec_adap_ops vc4_hdmi_cec_adap_ops = {
+       .adap_enable = vc4_hdmi_cec_adap_enable,
+       .adap_log_addr = vc4_hdmi_cec_adap_log_addr,
+       .adap_transmit = vc4_hdmi_cec_adap_transmit,
+};
+#endif
+
 static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
        struct platform_device *pdev = to_platform_device(dev);
@@ -1180,7 +1361,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
         * needs to be a bit higher than the pixel clock rate
         * (generally 148.5Mhz).
         */
-       ret = clk_set_rate(hdmi->hsm_clock, 163682864);
+       ret = clk_set_rate(hdmi->hsm_clock, HSM_CLOCK_FREQ);
        if (ret) {
                DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
                goto err_put_i2c;
@@ -1231,6 +1412,37 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
                ret = PTR_ERR(hdmi->connector);
                goto err_destroy_encoder;
        }
+#ifdef CONFIG_DRM_VC4_HDMI_CEC
+       hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
+                                             vc4, "vc4",
+                                             CEC_CAP_TRANSMIT |
+                                             CEC_CAP_LOG_ADDRS |
+                                             CEC_CAP_PASSTHROUGH |
+                                             CEC_CAP_RC, 1);
+       ret = PTR_ERR_OR_ZERO(hdmi->cec_adap);
+       if (ret < 0)
+               goto err_destroy_conn;
+       HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, 0xffffffff);
+       value = HDMI_READ(VC4_HDMI_CEC_CNTRL_1);
+       value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK;
+       /*
+        * Set the logical address to Unregistered and set the clock
+        * divider: the hsm_clock rate and this divider setting will
+        * give a 40 kHz CEC clock.
+        */
+       value |= VC4_HDMI_CEC_ADDR_MASK |
+                (4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT);
+       HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, value);
+       ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
+                                       vc4_cec_irq_handler,
+                                       vc4_cec_irq_handler_thread, 0,
+                                       "vc4 hdmi cec", vc4);
+       if (ret)
+               goto err_delete_cec_adap;
+       ret = cec_register_adapter(hdmi->cec_adap, dev);
+       if (ret < 0)
+               goto err_delete_cec_adap;
+#endif
 
        ret = vc4_hdmi_audio_init(hdmi);
        if (ret)
@@ -1238,6 +1450,12 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
        return 0;
 
+#ifdef CONFIG_DRM_VC4_HDMI_CEC
+err_delete_cec_adap:
+       cec_delete_adapter(hdmi->cec_adap);
+err_destroy_conn:
+       vc4_hdmi_connector_destroy(hdmi->connector);
+#endif
 err_destroy_encoder:
        vc4_hdmi_encoder_destroy(hdmi->encoder);
 err_unprepare_hsm:
@@ -1257,7 +1475,7 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
        struct vc4_hdmi *hdmi = vc4->hdmi;
 
        vc4_hdmi_audio_cleanup(hdmi);
-
+       cec_unregister_adapter(hdmi->cec_adap);
        vc4_hdmi_connector_destroy(hdmi->connector);
        vc4_hdmi_encoder_destroy(hdmi->encoder);
 
index d382c34c1b9e0c6d5b949ca09bf1e8401d44f91a..55677bd50f6610795e3d04bfdeaa245cf9aad0cb 100644 (file)
 # define VC4_HDMI_VERTB_VBP_MASK               VC4_MASK(8, 0)
 # define VC4_HDMI_VERTB_VBP_SHIFT              0
 
+#define VC4_HDMI_CEC_CNTRL_1                   0x0e8
+/* Set when the transmission has ended. */
+# define VC4_HDMI_CEC_TX_EOM                   BIT(31)
+/* If set, transmission was acked on the 1st or 2nd attempt (only one
+ * retry is attempted).  If in continuous mode, this means TX needs to
+ * be filled if !TX_EOM.
+ */
+# define VC4_HDMI_CEC_TX_STATUS_GOOD           BIT(30)
+# define VC4_HDMI_CEC_RX_EOM                   BIT(29)
+# define VC4_HDMI_CEC_RX_STATUS_GOOD           BIT(28)
+/* Number of bytes received for the message. */
+# define VC4_HDMI_CEC_REC_WRD_CNT_MASK         VC4_MASK(27, 24)
+# define VC4_HDMI_CEC_REC_WRD_CNT_SHIFT                24
+/* Sets continuous receive mode.  Generates interrupt after each 8
+ * bytes to signal that RX_DATA should be consumed, and at RX_EOM.
+ *
+ * If disabled, maximum 16 bytes will be received (including header),
+ * and interrupt at RX_EOM.  Later bytes will be acked but not put
+ * into the RX_DATA.
+ */
+# define VC4_HDMI_CEC_RX_CONTINUE              BIT(23)
+# define VC4_HDMI_CEC_TX_CONTINUE              BIT(22)
+/* Set this after a CEC interrupt. */
+# define VC4_HDMI_CEC_CLEAR_RECEIVE_OFF                BIT(21)
+/* Starts a TX.  Will wait for appropriate idel time before CEC
+ * activity. Must be cleared in between transmits.
+ */
+# define VC4_HDMI_CEC_START_XMIT_BEGIN         BIT(20)
+# define VC4_HDMI_CEC_MESSAGE_LENGTH_MASK      VC4_MASK(19, 16)
+# define VC4_HDMI_CEC_MESSAGE_LENGTH_SHIFT     16
+/* Device's CEC address */
+# define VC4_HDMI_CEC_ADDR_MASK                        VC4_MASK(15, 12)
+# define VC4_HDMI_CEC_ADDR_SHIFT               12
+/* Divides off of HSM clock to generate CEC bit clock. */
+/* With the current defaults the CEC bit clock is 40 kHz = 25 usec */
+# define VC4_HDMI_CEC_DIV_CLK_CNT_MASK         VC4_MASK(11, 0)
+# define VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT                0
+
+/* Set these fields to how many bit clock cycles get to that many
+ * microseconds.
+ */
+#define VC4_HDMI_CEC_CNTRL_2                   0x0ec
+# define VC4_HDMI_CEC_CNT_TO_1500_US_MASK      VC4_MASK(30, 24)
+# define VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT     24
+# define VC4_HDMI_CEC_CNT_TO_1300_US_MASK      VC4_MASK(23, 17)
+# define VC4_HDMI_CEC_CNT_TO_1300_US_SHIFT     17
+# define VC4_HDMI_CEC_CNT_TO_800_US_MASK       VC4_MASK(16, 11)
+# define VC4_HDMI_CEC_CNT_TO_800_US_SHIFT      11
+# define VC4_HDMI_CEC_CNT_TO_600_US_MASK       VC4_MASK(10, 5)
+# define VC4_HDMI_CEC_CNT_TO_600_US_SHIFT      5
+# define VC4_HDMI_CEC_CNT_TO_400_US_MASK       VC4_MASK(4, 0)
+# define VC4_HDMI_CEC_CNT_TO_400_US_SHIFT      0
+
+#define VC4_HDMI_CEC_CNTRL_3                   0x0f0
+# define VC4_HDMI_CEC_CNT_TO_2750_US_MASK      VC4_MASK(31, 24)
+# define VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT     24
+# define VC4_HDMI_CEC_CNT_TO_2400_US_MASK      VC4_MASK(23, 16)
+# define VC4_HDMI_CEC_CNT_TO_2400_US_SHIFT     16
+# define VC4_HDMI_CEC_CNT_TO_2050_US_MASK      VC4_MASK(15, 8)
+# define VC4_HDMI_CEC_CNT_TO_2050_US_SHIFT     8
+# define VC4_HDMI_CEC_CNT_TO_1700_US_MASK      VC4_MASK(7, 0)
+# define VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT     0
+
+#define VC4_HDMI_CEC_CNTRL_4                   0x0f4
+# define VC4_HDMI_CEC_CNT_TO_4300_US_MASK      VC4_MASK(31, 24)
+# define VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT     24
+# define VC4_HDMI_CEC_CNT_TO_3900_US_MASK      VC4_MASK(23, 16)
+# define VC4_HDMI_CEC_CNT_TO_3900_US_SHIFT     16
+# define VC4_HDMI_CEC_CNT_TO_3600_US_MASK      VC4_MASK(15, 8)
+# define VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT     8
+# define VC4_HDMI_CEC_CNT_TO_3500_US_MASK      VC4_MASK(7, 0)
+# define VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT     0
+
+#define VC4_HDMI_CEC_CNTRL_5                   0x0f8
+# define VC4_HDMI_CEC_TX_SW_RESET              BIT(27)
+# define VC4_HDMI_CEC_RX_SW_RESET              BIT(26)
+# define VC4_HDMI_CEC_PAD_SW_RESET             BIT(25)
+# define VC4_HDMI_CEC_MUX_TP_OUT_CEC           BIT(24)
+# define VC4_HDMI_CEC_RX_CEC_INT               BIT(23)
+# define VC4_HDMI_CEC_CLK_PRELOAD_MASK         VC4_MASK(22, 16)
+# define VC4_HDMI_CEC_CLK_PRELOAD_SHIFT                16
+# define VC4_HDMI_CEC_CNT_TO_4700_US_MASK      VC4_MASK(15, 8)
+# define VC4_HDMI_CEC_CNT_TO_4700_US_SHIFT     8
+# define VC4_HDMI_CEC_CNT_TO_4500_US_MASK      VC4_MASK(7, 0)
+# define VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT     0
+
+/* Transmit data, first byte is low byte of the 32-bit reg.  MSB of
+ * each byte transmitted first.
+ */
+#define VC4_HDMI_CEC_TX_DATA_1                 0x0fc
+#define VC4_HDMI_CEC_TX_DATA_2                 0x100
+#define VC4_HDMI_CEC_TX_DATA_3                 0x104
+#define VC4_HDMI_CEC_TX_DATA_4                 0x108
+#define VC4_HDMI_CEC_RX_DATA_1                 0x10c
+#define VC4_HDMI_CEC_RX_DATA_2                 0x110
+#define VC4_HDMI_CEC_RX_DATA_3                 0x114
+#define VC4_HDMI_CEC_RX_DATA_4                 0x118
+
 #define VC4_HDMI_TX_PHY_RESET_CTL              0x2c0
 
 #define VC4_HDMI_TX_PHY_CTL0                   0x2c4
 # define VC4_HDMI_TX_PHY_RNG_PWRDN             BIT(25)
 
+/* Interrupt status bits */
+#define VC4_HDMI_CPU_STATUS                    0x340
+#define VC4_HDMI_CPU_SET                       0x344
+#define VC4_HDMI_CPU_CLEAR                     0x348
+# define VC4_HDMI_CPU_CEC                      BIT(6)
+# define VC4_HDMI_CPU_HOTPLUG                  BIT(0)
+
+#define VC4_HDMI_CPU_MASK_STATUS               0x34c
+#define VC4_HDMI_CPU_MASK_SET                  0x350
+#define VC4_HDMI_CPU_MASK_CLEAR                        0x354
+
 #define VC4_HDMI_GCP(x)                                (0x400 + ((x) * 0x4))
 #define VC4_HDMI_RAM_PACKET(x)                 (0x400 + ((x) * 0x24))
 #define VC4_HDMI_PACKET_STRIDE                 0x24
 
 #define VC4_HD_M_CTL                           0x00c
+/* Debug: Current receive value on the CEC pad. */
+# define VC4_HD_CECRXD                         BIT(9)
+/* Debug: Override CEC output to 0. */
+# define VC4_HD_CECOVR                         BIT(8)
 # define VC4_HD_M_REGISTER_FILE_STANDBY                (3 << 6)
 # define VC4_HD_M_RAM_STANDBY                  (3 << 4)
 # define VC4_HD_M_SW_RST                       BIT(2)