Merge tag 'u-boot-atmel-fixes-2021.01-b' of https://gitlab.denx.de/u-boot/custodians...
[platform/kernel/u-boot.git] / drivers / video / meson / meson_dw_hdmi.c
index 9831d97..5d74f7f 100644 (file)
@@ -8,10 +8,12 @@
 #include <display.h>
 #include <dm.h>
 #include <edid.h>
+#include <log.h>
 #include <asm/io.h>
 #include <dw_hdmi.h>
 #include <dm/device-internal.h>
 #include <dm/uclass-internal.h>
+#include <linux/bitops.h>
 #include <power/regulator.h>
 #include <clk.h>
 #include <linux/delay.h>
@@ -24,6 +26,7 @@
 #define HDMITX_TOP_ADDR_REG    0x0
 #define HDMITX_TOP_DATA_REG    0x4
 #define HDMITX_TOP_CTRL_REG    0x8
+#define HDMITX_TOP_G12A_OFFSET 0x8000
 
 /* Controller Communication Channel */
 #define HDMITX_DWC_ADDR_REG    0x10
@@ -37,6 +40,8 @@
 #define HHI_HDMI_PHY_CNTL1     0x3a4 /* 0xe9 */
 #define HHI_HDMI_PHY_CNTL2     0x3a8 /* 0xea */
 #define HHI_HDMI_PHY_CNTL3     0x3ac /* 0xeb */
+#define HHI_HDMI_PHY_CNTL4     0x3b0 /* 0xec */
+#define HHI_HDMI_PHY_CNTL5     0x3b4 /* 0xed */
 
 struct meson_dw_hdmi {
        struct udevice *dev;
@@ -48,6 +53,7 @@ enum hdmi_compatible {
        HDMI_COMPATIBLE_GXBB = 0,
        HDMI_COMPATIBLE_GXL = 1,
        HDMI_COMPATIBLE_GXM = 2,
+       HDMI_COMPATIBLE_G12A = 3,
 };
 
 static inline bool meson_hdmi_is_compatible(struct meson_dw_hdmi *priv,
@@ -60,8 +66,14 @@ static inline bool meson_hdmi_is_compatible(struct meson_dw_hdmi *priv,
 
 static unsigned int dw_hdmi_top_read(struct dw_hdmi *hdmi, unsigned int addr)
 {
+       struct meson_dw_hdmi *priv = container_of(hdmi, struct meson_dw_hdmi,
+                                                 hdmi);
        unsigned int data;
 
+       if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_G12A))
+               return readl(hdmi->ioaddr +
+                            HDMITX_TOP_G12A_OFFSET + (addr << 2));
+
        /* ADDR must be written twice */
        writel(addr & 0xffff, hdmi->ioaddr + HDMITX_TOP_ADDR_REG);
        writel(addr & 0xffff, hdmi->ioaddr + HDMITX_TOP_ADDR_REG);
@@ -76,6 +88,15 @@ static unsigned int dw_hdmi_top_read(struct dw_hdmi *hdmi, unsigned int addr)
 static inline void dw_hdmi_top_write(struct dw_hdmi *hdmi,
                                     unsigned int addr, unsigned int data)
 {
+       struct meson_dw_hdmi *priv = container_of(hdmi, struct meson_dw_hdmi,
+                                                 hdmi);
+
+       if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_G12A)) {
+               writel(data, hdmi->ioaddr +
+                      HDMITX_TOP_G12A_OFFSET + (addr << 2));
+               return;
+       }
+
        /* ADDR must be written twice */
        writel(addr & 0xffff, hdmi->ioaddr + HDMITX_TOP_ADDR_REG);
        writel(addr & 0xffff, hdmi->ioaddr + HDMITX_TOP_ADDR_REG);
@@ -237,7 +258,7 @@ static void meson_dw_hdmi_phy_setup_mode(struct meson_dw_hdmi *priv,
                        hhi_write(HHI_HDMI_PHY_CNTL0, 0x33604142);
                        hhi_write(HHI_HDMI_PHY_CNTL3, 0x0016315b);
                }
-       } else {
+       } else if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_GXBB)) {
                if (pixel_clock >= 371250) {
                        /* 5.94Gbps, 3.7125Gbps */
                        hhi_write(HHI_HDMI_PHY_CNTL0, 0x33353245);
@@ -251,6 +272,23 @@ static void meson_dw_hdmi_phy_setup_mode(struct meson_dw_hdmi *priv,
                        hhi_write(HHI_HDMI_PHY_CNTL0, 0x33632122);
                        hhi_write(HHI_HDMI_PHY_CNTL3, 0x2000115b);
                }
+       } else if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_G12A)) {
+               if (pixel_clock >= 371250) {
+                       /* 5.94Gbps, 3.7125Gbps */
+                       hhi_write(HHI_HDMI_PHY_CNTL0, 0x37eb65c4);
+                       hhi_write(HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
+                       hhi_write(HHI_HDMI_PHY_CNTL5, 0x0000080b);
+               } else if (pixel_clock >= 297000) {
+                       /* 2.97Gbps */
+                       hhi_write(HHI_HDMI_PHY_CNTL0, 0x33eb6262);
+                       hhi_write(HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
+                       hhi_write(HHI_HDMI_PHY_CNTL5, 0x00000003);
+               } else {
+                       /* 1.485Gbps, and below */
+                       hhi_write(HHI_HDMI_PHY_CNTL0, 0x33eb4242);
+                       hhi_write(HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
+                       hhi_write(HHI_HDMI_PHY_CNTL5, 0x00000003);
+               }
        }
 }
 
@@ -292,7 +330,8 @@ static int meson_dw_hdmi_phy_init(struct dw_hdmi *hdmi, uint pixel_clock)
 
        /* BIT_INVERT */
        if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_GXL) ||
-           meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_GXM))
+           meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_GXM) ||
+           meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_G12A))
                dw_hdmi_hhi_update_bits(priv, HHI_HDMI_PHY_CNTL1, BIT(17), 0);
        else
                dw_hdmi_hhi_update_bits(priv, HHI_HDMI_PHY_CNTL1,
@@ -340,7 +379,9 @@ static int meson_dw_hdmi_probe(struct udevice *dev)
        struct meson_dw_hdmi *priv = dev_get_priv(dev);
        struct reset_ctl_bulk resets;
        struct clk_bulk clocks;
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
        struct udevice *supply;
+#endif
        int ret;
 
        priv->dev = dev;
@@ -356,8 +397,12 @@ static int meson_dw_hdmi_probe(struct udevice *dev)
        priv->hdmi.hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
        priv->hdmi.hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_YUV8_1X24;
        priv->hdmi.phy_set = meson_dw_hdmi_phy_init;
-       priv->hdmi.write_reg = dw_hdmi_dwc_write;
-       priv->hdmi.read_reg = dw_hdmi_dwc_read;
+       if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_G12A))
+               priv->hdmi.reg_io_width = 1;
+       else {
+               priv->hdmi.write_reg = dw_hdmi_dwc_write;
+               priv->hdmi.read_reg = dw_hdmi_dwc_read;
+       }
        priv->hdmi.i2c_clk_high = 0x67;
        priv->hdmi.i2c_clk_low = 0x78;
 
@@ -409,9 +454,13 @@ static int meson_dw_hdmi_probe(struct udevice *dev)
        if (ret)
                return ret;
 
-       /* Enable APB3 fail on error */
-       writel_bits(BIT(15), BIT(15), priv->hdmi.ioaddr + HDMITX_TOP_CTRL_REG);
-       writel_bits(BIT(15), BIT(15), priv->hdmi.ioaddr + HDMITX_DWC_CTRL_REG);
+       if (!meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_G12A)) {
+               /* Enable APB3 fail on error */
+               writel_bits(BIT(15), BIT(15),
+                           priv->hdmi.ioaddr + HDMITX_TOP_CTRL_REG);
+               writel_bits(BIT(15), BIT(15),
+                           priv->hdmi.ioaddr + HDMITX_DWC_CTRL_REG);
+       }
 
        /* Bring out of reset */
        dw_hdmi_top_write(&priv->hdmi, HDMITX_TOP_SW_RESET,  0);
@@ -448,6 +497,8 @@ static const struct udevice_id meson_dw_hdmi_ids[] = {
                .data = HDMI_COMPATIBLE_GXL },
        { .compatible = "amlogic,meson-gxm-dw-hdmi",
                .data = HDMI_COMPATIBLE_GXM },
+       { .compatible = "amlogic,meson-g12a-dw-hdmi",
+               .data = HDMI_COMPATIBLE_G12A },
        { }
 };