The U-Boot port for ST-Ericsson Ux500 is currently only used on the
"stemmy" board, where U-Boot runs after firmware that already sets up
a boot splash screen. This means that the display is already on
and we can just continue using it for U-Boot.

Add a simple driver that simplifies this by reading the display
configuration (e.g. screen size, bpp) from the hardware registers.

It also checks the configured "source synchronization" - for some
displays (usually DSI command mode displays) we need to explicitly
trigger a software sync. This is done through the video_sync()
callback that triggers the sync and wait for completion.

Cc: Linus Walleij <linus.wall...@linaro.org>
Signed-off-by: Stephan Gerhold <step...@gerhold.net>
---

 drivers/video/Kconfig       |  12 +++
 drivers/video/Makefile      |   1 +
 drivers/video/mcde_simple.c | 141 ++++++++++++++++++++++++++++++++++++
 3 files changed, 154 insertions(+)
 create mode 100644 drivers/video/mcde_simple.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index b69ffcae4b..e614a04aab 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -854,6 +854,18 @@ config VIDEO_DT_SIMPLEFB
          The video output is initialized by U-Boot, and kept by the
          kernel.
 
+config VIDEO_MCDE_SIMPLE
+       bool "Simple driver for ST-Ericsson MCDE with preconfigured display"
+       depends on DM_VIDEO
+       help
+         Enables a simple display driver for ST-Ericsson MCDE
+         (Multichannel Display Engine), which reads the configuration from
+         the MCDE registers.
+
+         This driver assumes that the display hardware has been initialized
+         before u-boot starts, and u-boot will simply render to the pre-
+         allocated frame buffer surface.
+
 config OSD
        bool "Enable OSD support"
        depends on DM
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 933f06e9d8..8a70c9deb5 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_VIDEO_LCD_ORISETECH_OTM8009A) += 
orisetech_otm8009a.o
 obj-$(CONFIG_VIDEO_LCD_RAYDIUM_RM68200) += raydium-rm68200.o
 obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o
 obj-$(CONFIG_VIDEO_LCD_TDO_TL070WSH30) += tdo-tl070wsh30.o
+obj-$(CONFIG_VIDEO_MCDE_SIMPLE) += mcde_simple.o
 obj-${CONFIG_VIDEO_MESON} += meson/
 obj-${CONFIG_VIDEO_MIPI_DSI} += mipi_dsi.o
 obj-$(CONFIG_VIDEO_MVEBU) += mvebu_lcd.o
diff --git a/drivers/video/mcde_simple.c b/drivers/video/mcde_simple.c
new file mode 100644
index 0000000000..0924ceee30
--- /dev/null
+++ b/drivers/video/mcde_simple.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2019 Stephan Gerhold */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <video.h>
+#include <asm/io.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+
+#define MCDE_EXTSRC0A0                 0x200
+#define MCDE_EXTSRC0CONF               0x20C
+#define MCDE_EXTSRC0CONF_BPP           GENMASK(11, 8)
+#define MCDE_OVL0CONF                  0x404
+#define MCDE_OVL0CONF_PPL              GENMASK(10, 0)
+#define MCDE_OVL0CONF_LPF              GENMASK(26, 16)
+#define MCDE_CHNL0SYNCHMOD             0x608
+#define MCDE_CHNL0SYNCHMOD_SRC_SYNCH   GENMASK(1, 0)
+#define MCDE_CHNL0SYNCHSW              0x60C
+#define MCDE_CHNL0SYNCHSW_SW_TRIG      BIT(0)
+#define MCDE_CRA0                      0x800
+#define MCDE_CRA0_FLOEN                        BIT(0)
+
+#define MCDE_FLOW_COMPLETION_TIMEOUT   200000  /* us */
+
+enum mcde_bpp {
+       MCDE_EXTSRC0CONF_BPP_1BPP_PAL,
+       MCDE_EXTSRC0CONF_BPP_2BPP_PAL,
+       MCDE_EXTSRC0CONF_BPP_4BPP_PAL,
+       MCDE_EXTSRC0CONF_BPP_8BPP_PAL,
+       MCDE_EXTSRC0CONF_BPP_RGB444,
+       MCDE_EXTSRC0CONF_BPP_ARGB4444,
+       MCDE_EXTSRC0CONF_BPP_IRGB1555,
+       MCDE_EXTSRC0CONF_BPP_RGB565,
+       MCDE_EXTSRC0CONF_BPP_RGB888,
+       MCDE_EXTSRC0CONF_BPP_XRGB8888,
+       MCDE_EXTSRC0CONF_BPP_ARGB8888,
+       MCDE_EXTSRC0CONF_BPP_YCBCR422,
+};
+
+enum mcde_src_synch {
+       MCDE_CHNL0SYNCHMOD_SRC_SYNCH_HARDWARE,
+       MCDE_CHNL0SYNCHMOD_SRC_SYNCH_NO_SYNCH,
+       MCDE_CHNL0SYNCHMOD_SRC_SYNCH_SOFTWARE,
+};
+
+struct mcde_simple_priv {
+       fdt_addr_t base;
+       enum mcde_src_synch src_synch;
+};
+
+static int mcde_simple_probe(struct udevice *dev)
+{
+       struct mcde_simple_priv *priv = dev_get_priv(dev);
+       struct video_uc_plat *plat = dev_get_uclass_plat(dev);
+       struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+       u32 val;
+
+       priv->base = dev_read_addr(dev);
+       if (priv->base == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       plat->base = readl(priv->base + MCDE_EXTSRC0A0);
+       if (!plat->base)
+               return -ENODEV;
+
+       val = readl(priv->base + MCDE_OVL0CONF);
+       uc_priv->xsize = FIELD_GET(MCDE_OVL0CONF_PPL, val);
+       uc_priv->ysize = FIELD_GET(MCDE_OVL0CONF_LPF, val);
+       uc_priv->rot = 0;
+
+       val = readl(priv->base + MCDE_EXTSRC0CONF);
+       switch (FIELD_GET(MCDE_EXTSRC0CONF_BPP, val)) {
+       case MCDE_EXTSRC0CONF_BPP_RGB565:
+               uc_priv->bpix = VIDEO_BPP16;
+               break;
+       case MCDE_EXTSRC0CONF_BPP_XRGB8888:
+       case MCDE_EXTSRC0CONF_BPP_ARGB8888:
+               uc_priv->bpix = VIDEO_BPP32;
+               break;
+       default:
+               printf("unsupported format: %#x\n", val);
+               return -EINVAL;
+       }
+
+       val = readl(priv->base + MCDE_CHNL0SYNCHMOD);
+       priv->src_synch = FIELD_GET(MCDE_CHNL0SYNCHMOD_SRC_SYNCH, val);
+
+       plat->size = uc_priv->xsize * uc_priv->ysize * VNBYTES(uc_priv->bpix);
+       debug("MCDE base: %#lx, xsize: %d, ysize: %d, bpp: %d\n",
+             plat->base, uc_priv->xsize, uc_priv->ysize, 
VNBITS(uc_priv->bpix));
+
+       video_set_flush_dcache(dev, true);
+       return 0;
+}
+
+static int mcde_simple_video_sync(struct udevice *dev)
+{
+       struct mcde_simple_priv *priv = dev_get_priv(dev);
+       unsigned int val;
+
+       if (priv->src_synch != MCDE_CHNL0SYNCHMOD_SRC_SYNCH_SOFTWARE)
+               return 0;
+
+       /* Enable flow */
+       val = readl(priv->base + MCDE_CRA0);
+       val |= MCDE_CRA0_FLOEN;
+       writel(val, priv->base + MCDE_CRA0);
+
+       /* Trigger a software sync */
+       writel(MCDE_CHNL0SYNCHSW_SW_TRIG, priv->base + MCDE_CHNL0SYNCHSW);
+
+       /* Disable flow */
+       val = readl(priv->base + MCDE_CRA0);
+       val &= ~MCDE_CRA0_FLOEN;
+       writel(val, priv->base + MCDE_CRA0);
+
+       /* Wait for completion */
+       return readl_poll_timeout(priv->base + MCDE_CRA0, val,
+                                 !(val & MCDE_CRA0_FLOEN),
+                                 MCDE_FLOW_COMPLETION_TIMEOUT);
+}
+
+static struct video_ops mcde_simple_ops = {
+       .video_sync = mcde_simple_video_sync,
+};
+
+static const struct udevice_id mcde_simple_ids[] = {
+       { .compatible = "ste,mcde" },
+       { }
+};
+
+U_BOOT_DRIVER(mcde_simple) = {
+       .name           = "mcde_simple",
+       .id             = UCLASS_VIDEO,
+       .ops            = &mcde_simple_ops,
+       .of_match       = mcde_simple_ids,
+       .probe          = mcde_simple_probe,
+       .priv_auto      = sizeof(struct mcde_simple_priv),
+};
-- 
2.32.0

Reply via email to