Merge tag 'v2022.04-rc5' into next
[platform/kernel/u-boot.git] / drivers / video / video-uclass.c
index b19bfb4..9ae032e 100644 (file)
@@ -3,13 +3,22 @@
  * Copyright (c) 2015 Google, Inc
  */
 
+#define LOG_CATEGORY UCLASS_VIDEO
+
 #include <common.h>
+#include <console.h>
+#include <cpu_func.h>
 #include <dm.h>
+#include <log.h>
+#include <malloc.h>
 #include <mapmem.h>
 #include <stdio_dev.h>
 #include <video.h>
 #include <video_console.h>
+#include <asm/cache.h>
+#include <asm/global_data.h>
 #include <dm/lists.h>
+#include <dm/device_compat.h>
 #include <dm/device-internal.h>
 #include <dm/uclass-internal.h>
 #ifdef CONFIG_SANDBOX
  * Theory of operation:
  *
  * Before relocation each device is bound. The driver for each device must
- * set the @align and @size values in struct video_uc_platdata. This
+ * set the @align and @size values in struct video_uc_plat. This
  * information represents the requires size and alignment of the frame buffer
  * for the device. The values can be an over-estimate but cannot be too
  * small. The actual values will be suppled (in the same manner) by the bind()
- * method after relocation.
+ * method after relocation. Additionally driver can allocate frame buffer
+ * itself by setting plat->base.
  *
  * This information is then picked up by video_reserve() which works out how
  * much memory is needed for all devices. This is allocated between
  */
 DECLARE_GLOBAL_DATA_PTR;
 
+/**
+ * struct video_uc_priv - Information for the video uclass
+ *
+ * @video_ptr: Current allocation position of the video framebuffer pointer.
+ *     While binding devices after relocation, this points to the next
+ *     available address to use for a device's framebuffer. It starts at
+ *     gd->video_top and works downwards, running out of space when it hits
+ *     gd->video_bottom.
+ */
+struct video_uc_priv {
+       ulong video_ptr;
+};
+
 void video_set_flush_dcache(struct udevice *dev, bool flush)
 {
        struct video_priv *priv = dev_get_uclass_priv(dev);
@@ -50,12 +73,16 @@ void video_set_flush_dcache(struct udevice *dev, bool flush)
 
 static ulong alloc_fb(struct udevice *dev, ulong *addrp)
 {
-       struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
+       struct video_uc_plat *plat = dev_get_uclass_plat(dev);
        ulong base, align, size;
 
        if (!plat->size)
                return 0;
 
+       /* Allow drivers to allocate the frame buffer themselves */
+       if (plat->base)
+               return 0;
+
        align = plat->align ? plat->align : 1 << 20;
        base = *addrp - plat->size;
        base &= ~(align - 1);
@@ -79,7 +106,13 @@ int video_reserve(ulong *addrp)
                debug("%s: Reserving %lx bytes at %lx for video device '%s'\n",
                      __func__, size, *addrp, dev->name);
        }
+
+       /* Allocate space for PCI video devices in case there were not bound */
+       if (*addrp == gd->video_top)
+               *addrp -= CONFIG_VIDEO_PCI_DEFAULT_FB_SIZE;
+
        gd->video_bottom = *addrp;
+       gd->fb_base = *addrp;
        debug("Video frame buffers from %lx to %lx\n", gd->video_bottom,
              gd->video_top);
 
@@ -89,30 +122,36 @@ int video_reserve(ulong *addrp)
 int video_clear(struct udevice *dev)
 {
        struct video_priv *priv = dev_get_uclass_priv(dev);
+       int ret;
 
        switch (priv->bpix) {
-       case VIDEO_BPP16: {
-               u16 *ppix = priv->fb;
-               u16 *end = priv->fb + priv->fb_size;
-
-               while (ppix < end)
-                       *ppix++ = priv->colour_bg;
-               break;
-       }
-       case VIDEO_BPP32: {
-               u32 *ppix = priv->fb;
-               u32 *end = priv->fb + priv->fb_size;
-
-               while (ppix < end)
-                       *ppix++ = priv->colour_bg;
-               break;
-       }
+       case VIDEO_BPP16:
+               if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
+                       u16 *ppix = priv->fb;
+                       u16 *end = priv->fb + priv->fb_size;
+
+                       while (ppix < end)
+                               *ppix++ = priv->colour_bg;
+                       break;
+               }
+       case VIDEO_BPP32:
+               if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
+                       u32 *ppix = priv->fb;
+                       u32 *end = priv->fb + priv->fb_size;
+
+                       while (ppix < end)
+                               *ppix++ = priv->colour_bg;
+                       break;
+               }
        default:
                memset(priv->fb, priv->colour_bg, priv->fb_size);
                break;
        }
+       ret = video_sync_copy(dev, priv->fb, priv->fb + priv->fb_size);
+       if (ret)
+               return ret;
 
-       return 0;
+       return video_sync(dev, false);
 }
 
 void video_set_default_colors(struct udevice *dev, bool invert)
@@ -120,14 +159,14 @@ void video_set_default_colors(struct udevice *dev, bool invert)
        struct video_priv *priv = dev_get_uclass_priv(dev);
        int fore, back;
 
-#ifdef CONFIG_SYS_WHITE_ON_BLACK
-       /* White is used when switching to bold, use light gray here */
-       fore = VID_LIGHT_GRAY;
-       back = VID_BLACK;
-#else
-       fore = VID_BLACK;
-       back = VID_WHITE;
-#endif
+       if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
+               /* White is used when switching to bold, use light gray here */
+               fore = VID_LIGHT_GRAY;
+               back = VID_BLACK;
+       } else {
+               fore = VID_BLACK;
+               back = VID_WHITE;
+       }
        if (invert) {
                int temp;
 
@@ -142,8 +181,17 @@ void video_set_default_colors(struct udevice *dev, bool invert)
 }
 
 /* Flush video activity to the caches */
-void video_sync(struct udevice *vid, bool force)
+int video_sync(struct udevice *vid, bool force)
 {
+       struct video_ops *ops = video_get_ops(vid);
+       int ret;
+
+       if (ops && ops->video_sync) {
+               ret = ops->video_sync(vid);
+               if (ret)
+                       return ret;
+       }
+
        /*
         * flush_dcache_range() is declared in common.h but it seems that some
         * architectures do not actually implement it. Is there a way to find
@@ -161,23 +209,42 @@ void video_sync(struct udevice *vid, bool force)
        struct video_priv *priv = dev_get_uclass_priv(vid);
        static ulong last_sync;
 
-       if (force || get_timer(last_sync) > 10) {
+       if (force || get_timer(last_sync) > 100) {
                sandbox_sdl_sync(priv->fb);
                last_sync = get_timer(0);
        }
 #endif
+       return 0;
 }
 
 void video_sync_all(void)
 {
        struct udevice *dev;
+       int ret;
+
+       for (uclass_find_first_device(UCLASS_VIDEO, &dev);
+            dev;
+            uclass_find_next_device(&dev)) {
+               if (device_active(dev)) {
+                       ret = video_sync(dev, true);
+                       if (ret)
+                               dev_dbg(dev, "Video sync failed\n");
+               }
+       }
+}
+
+bool video_is_active(void)
+{
+       struct udevice *dev;
 
        for (uclass_find_first_device(UCLASS_VIDEO, &dev);
             dev;
             uclass_find_next_device(&dev)) {
                if (device_active(dev))
-                       video_sync(dev, true);
+                       return true;
        }
+
+       return false;
 }
 
 int video_get_xsize(struct udevice *dev)
@@ -194,23 +261,83 @@ int video_get_ysize(struct udevice *dev)
        return priv->ysize;
 }
 
-/* Set up the colour map */
-static int video_pre_probe(struct udevice *dev)
+#ifdef CONFIG_VIDEO_COPY
+int video_sync_copy(struct udevice *dev, void *from, void *to)
 {
        struct video_priv *priv = dev_get_uclass_priv(dev);
 
-       priv->cmap = calloc(256, sizeof(ushort));
-       if (!priv->cmap)
-               return -ENOMEM;
+       if (priv->copy_fb) {
+               long offset, size;
+
+               /* Find the offset of the first byte to copy */
+               if ((ulong)to > (ulong)from) {
+                       size = to - from;
+                       offset = from - priv->fb;
+               } else {
+                       size = from - to;
+                       offset = to - priv->fb;
+               }
+
+               /*
+                * Allow a bit of leeway for valid requests somewhere near the
+                * frame buffer
+                */
+               if (offset < -priv->fb_size || offset > 2 * priv->fb_size) {
+#ifdef DEBUG
+                       char str[120];
+
+                       snprintf(str, sizeof(str),
+                                "[** FAULT sync_copy fb=%p, from=%p, to=%p, offset=%lx]",
+                                priv->fb, from, to, offset);
+                       console_puts_select_stderr(true, str);
+#endif
+                       return -EFAULT;
+               }
+
+               /*
+                * Silently crop the memcpy. This allows callers to avoid doing
+                * this themselves. It is common for the end pointer to go a
+                * few lines after the end of the frame buffer, since most of
+                * the update algorithms terminate a line after their last write
+                */
+               if (offset + size > priv->fb_size) {
+                       size = priv->fb_size - offset;
+               } else if (offset < 0) {
+                       size += offset;
+                       offset = 0;
+               }
+
+               memcpy(priv->copy_fb + offset, priv->fb + offset, size);
+       }
 
        return 0;
 }
 
-static int video_pre_remove(struct udevice *dev)
+int video_sync_copy_all(struct udevice *dev)
 {
        struct video_priv *priv = dev_get_uclass_priv(dev);
 
-       free(priv->cmap);
+       video_sync_copy(dev, priv->fb, priv->fb + priv->fb_size);
+
+       return 0;
+}
+
+#endif
+
+#define SPLASH_DECL(_name) \
+       extern u8 __splash_ ## _name ## _begin[]; \
+       extern u8 __splash_ ## _name ## _end[]
+
+#define SPLASH_START(_name)    __splash_ ## _name ## _begin
+
+SPLASH_DECL(u_boot_logo);
+
+static int show_splash(struct udevice *dev)
+{
+       u8 *data = SPLASH_START(u_boot_logo);
+       int ret;
+
+       ret = video_bmp_display(dev, map_to_sysmem(data), -4, 4, true);
 
        return 0;
 }
@@ -218,7 +345,7 @@ static int video_pre_remove(struct udevice *dev)
 /* Set up the display ready for use */
 static int video_post_probe(struct udevice *dev)
 {
-       struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
+       struct video_uc_plat *plat = dev_get_uclass_plat(dev);
        struct video_priv *priv = dev_get_uclass_priv(dev);
        char name[30], drv[15], *str;
        const char *drv_name = drv;
@@ -232,6 +359,9 @@ static int video_post_probe(struct udevice *dev)
 
        priv->fb_size = priv->line_length * priv->ysize;
 
+       if (IS_ENABLED(CONFIG_VIDEO_COPY) && plat->copy_base)
+               priv->copy_fb = map_sysmem(plat->copy_base, plat->size);
+
        /* Set up colors  */
        video_set_default_colors(dev, false);
 
@@ -277,28 +407,47 @@ static int video_post_probe(struct udevice *dev)
                return ret;
        }
 
+       if (IS_ENABLED(CONFIG_VIDEO_LOGO) && !plat->hide_logo) {
+               ret = show_splash(dev);
+               if (ret) {
+                       log_debug("Cannot show splash screen\n");
+                       return ret;
+               }
+       }
+
        return 0;
 };
 
 /* Post-relocation, allocate memory for the frame buffer */
 static int video_post_bind(struct udevice *dev)
 {
-       ulong addr = gd->video_top;
+       struct video_uc_priv *uc_priv;
+       ulong addr;
        ulong size;
 
        /* Before relocation there is nothing to do here */
        if (!(gd->flags & GD_FLG_RELOC))
                return 0;
+
+       /* Set up the video pointer, if this is the first device */
+       uc_priv = uclass_get_priv(dev->uclass);
+       if (!uc_priv->video_ptr)
+               uc_priv->video_ptr = gd->video_top;
+
+       /* Allocate framebuffer space for this device */
+       addr = uc_priv->video_ptr;
        size = alloc_fb(dev, &addr);
        if (addr < gd->video_bottom) {
-               /* Device tree node may need the 'u-boot,dm-pre-reloc' tag */
+               /* Device tree node may need the 'u-boot,dm-pre-reloc' or
+                * 'u-boot,dm-pre-proper' tag
+                */
                printf("Video device '%s' cannot allocate frame buffer memory -ensure the device is set up before relocation\n",
                       dev->name);
                return -ENOSPC;
        }
        debug("%s: Claiming %lx bytes at %lx for video device '%s'\n",
              __func__, size, addr, dev->name);
-       gd->video_bottom = addr;
+       uc_priv->video_ptr = addr;
 
        return 0;
 }
@@ -308,9 +457,8 @@ UCLASS_DRIVER(video) = {
        .name           = "video",
        .flags          = DM_UC_FLAG_SEQ_ALIAS,
        .post_bind      = video_post_bind,
-       .pre_probe      = video_pre_probe,
        .post_probe     = video_post_probe,
-       .pre_remove     = video_pre_remove,
-       .per_device_auto_alloc_size     = sizeof(struct video_priv),
-       .per_device_platdata_auto_alloc_size = sizeof(struct video_uc_platdata),
+       .priv_auto      = sizeof(struct video_uc_priv),
+       .per_device_auto        = sizeof(struct video_priv),
+       .per_device_plat_auto   = sizeof(struct video_uc_plat),
 };