drm/rockchip: dw_hdmi_qp: Add basic RK3576 HDMI output support
authorAndy Yan <andy.yan@rock-chips.com>
Tue, 31 Dec 2024 09:44:19 +0000 (17:44 +0800)
committerHeiko Stuebner <heiko@sntech.de>
Mon, 6 Jan 2025 15:35:48 +0000 (16:35 +0100)
The HDMI on RK3576 shares the same IP block (PHY and Controller)
with rk3588.
However, there are some control bits scattered in different GRF.

Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
Tested-by: Detlev Casanova <detlev.casanova@collabora.com>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20241231094425.253398-4-andyshrk@163.com
drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c

index 4c82b8400b41c2c3681d27bf539ee1fade84defb..9916843a6ff3b34b5407bf7d2a73812f2a1844cb 100644 (file)
 
 #include "rockchip_drm_drv.h"
 
+#define RK3576_IOC_MISC_CON0           0xa400
+#define RK3576_HDMI_HPD_INT_MSK                BIT(2)
+#define RK3576_HDMI_HPD_INT_CLR                BIT(1)
+
+#define RK3576_IOC_HDMI_HPD_STATUS     0xa440
+#define RK3576_HDMI_LEVEL_INT          BIT(3)
+
+#define RK3576_VO0_GRF_SOC_CON1                0x0004
+#define RK3576_HDMI_FRL_MOD            BIT(0)
+#define RK3576_HDMI_HDCP14_MEM_EN      BIT(15)
+
+#define RK3576_VO0_GRF_SOC_CON8                0x0020
+#define RK3576_COLOR_FORMAT_MASK       (0xf << 4)
+#define RK3576_COLOR_DEPTH_MASK                (0xf << 8)
+#define RK3576_RGB                     (0 << 4)
+#define RK3576_YUV422                  (0x1 << 4)
+#define RK3576_YUV444                  (0x2 << 4)
+#define RK3576_YUV420                  (0x3 << 4)
+#define RK3576_8BPC                    (0x0 << 8)
+#define RK3576_10BPC                   (0x6 << 8)
+#define RK3576_CECIN_MASK              BIT(3)
+
+#define RK3576_VO0_GRF_SOC_CON12       0x0030
+#define RK3576_GRF_OSDA_DLYN           (0xf << 12)
+#define RK3576_GRF_OSDA_DIV            (0x7f << 1)
+#define RK3576_GRF_OSDA_DLY_EN         BIT(0)
+
+#define RK3576_VO0_GRF_SOC_CON14       0x0038
+#define RK3576_I2S_SEL_MASK            BIT(0)
+#define RK3576_SPDIF_SEL_MASK          BIT(1)
+#define HDCP0_P1_GPIO_IN               BIT(2)
+#define RK3576_SCLIN_MASK              BIT(4)
+#define RK3576_SDAIN_MASK              BIT(5)
+#define RK3576_HDMI_GRANT_SEL          BIT(6)
+
 #define RK3588_GRF_SOC_CON2            0x0308
 #define RK3588_HDMI0_HPD_INT_MSK       BIT(13)
 #define RK3588_HDMI0_HPD_INT_CLR       BIT(12)
@@ -167,6 +202,37 @@ static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = {
        .setup_hpd      = dw_hdmi_qp_rk3588_setup_hpd,
 };
 
+static enum drm_connector_status
+dw_hdmi_qp_rk3576_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+       struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
+       u32 val;
+
+       regmap_read(hdmi->regmap, RK3576_IOC_HDMI_HPD_STATUS, &val);
+
+       return val & RK3576_HDMI_LEVEL_INT ?
+               connector_status_connected : connector_status_disconnected;
+}
+
+static void dw_hdmi_qp_rk3576_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+       struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
+       u32 val;
+
+       val = HIWORD_UPDATE(RK3576_HDMI_HPD_INT_CLR,
+                           RK3576_HDMI_HPD_INT_CLR | RK3576_HDMI_HPD_INT_MSK);
+
+       regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
+       regmap_write(hdmi->regmap, 0xa404, 0xffff0102);
+}
+
+static const struct dw_hdmi_qp_phy_ops rk3576_hdmi_phy_ops = {
+       .init           = dw_hdmi_qp_rk3588_phy_init,
+       .disable        = dw_hdmi_qp_rk3588_phy_disable,
+       .read_hpd       = dw_hdmi_qp_rk3576_read_hpd,
+       .setup_hpd      = dw_hdmi_qp_rk3576_setup_hpd,
+};
+
 static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work)
 {
        struct rockchip_hdmi_qp *hdmi = container_of(work,
@@ -182,6 +248,43 @@ static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work)
        }
 }
 
+static irqreturn_t dw_hdmi_qp_rk3576_hardirq(int irq, void *dev_id)
+{
+       struct rockchip_hdmi_qp *hdmi = dev_id;
+       u32 intr_stat, val;
+
+       regmap_read(hdmi->regmap, RK3576_IOC_HDMI_HPD_STATUS, &intr_stat);
+       if (intr_stat) {
+               val = HIWORD_UPDATE(RK3576_HDMI_HPD_INT_MSK, RK3576_HDMI_HPD_INT_MSK);
+
+               regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
+               return IRQ_WAKE_THREAD;
+       }
+
+       return IRQ_NONE;
+}
+
+static irqreturn_t dw_hdmi_qp_rk3576_irq(int irq, void *dev_id)
+{
+       struct rockchip_hdmi_qp *hdmi = dev_id;
+       u32 intr_stat, val;
+
+       regmap_read(hdmi->regmap, RK3576_IOC_HDMI_HPD_STATUS, &intr_stat);
+
+       if (!intr_stat)
+               return IRQ_NONE;
+
+       val = HIWORD_UPDATE(RK3576_HDMI_HPD_INT_CLR, RK3576_HDMI_HPD_INT_CLR);
+       regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
+       mod_delayed_work(system_wq, &hdmi->hpd_work,
+                        msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
+
+       val = HIWORD_UPDATE(0, RK3576_HDMI_HPD_INT_MSK);
+       regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
+
+       return IRQ_HANDLED;
+}
+
 static irqreturn_t dw_hdmi_qp_rk3588_hardirq(int irq, void *dev_id)
 {
        struct rockchip_hdmi_qp *hdmi = dev_id;
@@ -232,6 +335,21 @@ static irqreturn_t dw_hdmi_qp_rk3588_irq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static void dw_hdmi_qp_rk3576_io_init(struct rockchip_hdmi_qp *hdmi)
+{
+       u32 val;
+
+       val = HIWORD_UPDATE(RK3576_SCLIN_MASK, RK3576_SCLIN_MASK) |
+             HIWORD_UPDATE(RK3576_SDAIN_MASK, RK3576_SDAIN_MASK) |
+             HIWORD_UPDATE(RK3576_HDMI_GRANT_SEL, RK3576_HDMI_GRANT_SEL) |
+             HIWORD_UPDATE(RK3576_I2S_SEL_MASK, RK3576_I2S_SEL_MASK);
+
+       regmap_write(hdmi->vo_regmap, RK3576_VO0_GRF_SOC_CON14, val);
+
+       val = HIWORD_UPDATE(0, RK3576_HDMI_HPD_INT_MSK);
+       regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
+}
+
 static void dw_hdmi_qp_rk3588_io_init(struct rockchip_hdmi_qp *hdmi)
 {
        u32 val;
@@ -260,6 +378,12 @@ static void dw_hdmi_qp_rk3588_io_init(struct rockchip_hdmi_qp *hdmi)
        regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
 }
 
+static const struct rockchip_hdmi_qp_ctrl_ops rk3576_hdmi_ctrl_ops = {
+       .io_init                = dw_hdmi_qp_rk3576_io_init,
+       .irq_callback           = dw_hdmi_qp_rk3576_irq,
+       .hardirq_callback       = dw_hdmi_qp_rk3576_hardirq,
+};
+
 static const struct rockchip_hdmi_qp_ctrl_ops rk3588_hdmi_ctrl_ops = {
        .io_init                = dw_hdmi_qp_rk3588_io_init,
        .irq_callback           = dw_hdmi_qp_rk3588_irq,
@@ -273,6 +397,15 @@ struct rockchip_hdmi_qp_cfg {
        const struct dw_hdmi_qp_phy_ops *phy_ops;
 };
 
+static const struct rockchip_hdmi_qp_cfg rk3576_hdmi_cfg = {
+       .num_ports = 1,
+       .port_ids = {
+               0x27da0000,
+       },
+       .ctrl_ops = &rk3576_hdmi_ctrl_ops,
+       .phy_ops = &rk3576_hdmi_phy_ops,
+};
+
 static const struct rockchip_hdmi_qp_cfg rk3588_hdmi_cfg = {
        .num_ports = 2,
        .port_ids = {
@@ -284,8 +417,13 @@ static const struct rockchip_hdmi_qp_cfg rk3588_hdmi_cfg = {
 };
 
 static const struct of_device_id dw_hdmi_qp_rockchip_dt_ids[] = {
-       { .compatible = "rockchip,rk3588-dw-hdmi-qp",
-         .data = &rk3588_hdmi_cfg },
+       {
+               .compatible = "rockchip,rk3576-dw-hdmi-qp",
+               .data = &rk3576_hdmi_cfg
+       }, {
+               .compatible = "rockchip,rk3588-dw-hdmi-qp",
+               .data = &rk3588_hdmi_cfg
+       },
        {},
 };
 MODULE_DEVICE_TABLE(of, dw_hdmi_qp_rockchip_dt_ids);