From e3f55646ed98e96b23f4af387e95245d4b071140 Mon Sep 17 00:00:00 2001
From: Mikhail Vanyulin <mikhail.vanyulin@rtsoft.de>
Date: Mon, 4 Dec 2023 17:44:06 +0100
Subject: [PATCH] logo: add mem logo

This is a fix for issue [746-000092] logo: Implement logo driver to show
logo from RAM

If CONFIG_LOGO_SECO_MEMLOGO is set, logo driver will get logo memory address
and size from dtb. And if this points to a correct data, logo, loaded into
memory will be displayed.
If some part of dtb node is missing or data in the memory is incorrect,
a logo, which was selected by the driver before will be used.

It is also necessary to reserve memory to which logo is loaded.

Exampled dtb nodes:
       seco-memlogo {
               compatible = "seco,memlogo";
               address = <0x14000000>;
               size = <74501>;
       };

       reserved-memory {
               #address-cells = <1>;
               #size-cells = <1>;
               ranges;

               logo_reserved: logo@14000000 {
                       reusable;
                       reg = <0x14000000 74501>;
               };
       };

Example script, which will generate logo from logo.png:
    WIDTH=492
    HEIGHT=150
    pngtopam logo.png > logo.ppm
    pnmpad -width $WIDTH -height $HEIGHT logo.ppm > logo_pad.ppm
    pnmcolormap 224 logo_pad.ppm > colormap
    pnmremap -floyd -plain -mapfile=colormap logo_pad.ppm > logo_plain.ppm
    seco-pnmtologo -t clut224 -o logo.dat -f bin logo_plain.ppm

Example command to load logo into memory from U-Boot:
    load mmc 1:3 0x14000000 logo.dat

Signed-off-by: Mikhail Vanyulin <mikhail.vanyulin@rtsoft.de>
(cherry picked from commit e30bc118a5d2b4fd003c71006980c5b2c06429ca)
---
 drivers/video/logo/Kconfig | 12 ++++++
 drivers/video/logo/logo.c  | 75 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 87 insertions(+)

diff --git a/drivers/video/logo/Kconfig b/drivers/video/logo/Kconfig
index c5ef942ad73a7..685c46e628993 100644
--- a/drivers/video/logo/Kconfig
+++ b/drivers/video/logo/Kconfig
@@ -43,6 +43,18 @@ config LOGO_PARISC_CLUT224
 	depends on PARISC
 	default y
 
+config LOGO_SECO_MEMLOGO
+	bool "Memory based dynamic logo"
+	depends on LOGO
+	depends on OF
+	default y
+	help
+	  Get logo from memory. Memory address and logo size are taken from
+	  a device tree node. Compatible should be "seco,memlogo".
+	  Address is set by "address", size - by "size".
+	  It is also necessary to reserve memory for logo via
+	  "reserved-memory" node in dtb.
+
 config LOGO_SGI_CLUT224
 	bool "224-color SGI Linux logo"
 	depends on SGI_IP22 || SGI_IP27 || SGI_IP32
diff --git a/drivers/video/logo/logo.c b/drivers/video/logo/logo.c
index 59b0bc8f4f334..40623314604d3 100644
--- a/drivers/video/logo/logo.c
+++ b/drivers/video/logo/logo.c
@@ -18,6 +18,10 @@
 #include <asm/setup.h>
 #endif
 
+#ifdef CONFIG_LOGO_SECO_MEMLOGO
+#include <linux/of.h>
+#endif
+
 static bool nologo;
 module_param(nologo, bool, 0);
 MODULE_PARM_DESC(nologo, "Disables startup logo");
@@ -44,6 +48,19 @@ late_initcall_sync(fb_logo_late_init);
 const struct linux_logo * __ref fb_find_logo(int depth)
 {
 	const struct linux_logo *logo = NULL;
+#ifdef CONFIG_LOGO_SECO_MEMLOGO
+	u32 logophys = 0;
+	u32 logosize = 0;
+
+	const char *compatible = "seco,memlogo";
+	struct device_node *np;
+	int ret;
+
+	struct linux_logo *validate_logo = NULL;
+	unsigned int logo_bpc;
+	unsigned int logo_size_validate;
+	unsigned int logo_data_size;
+#endif
 
 	if (nologo || logos_freed)
 		return NULL;
@@ -112,6 +129,64 @@ const struct linux_logo * __ref fb_find_logo(int depth)
 		/* EDGEHOG OS logo */
 		logo = &logo_edgehog_clut224;
 #endif
+#ifdef CONFIG_LOGO_SECO_MEMLOGO
+		np = of_find_compatible_node(NULL, NULL, compatible);
+		if (!np)
+			pr_warn("memlogo: Couldn't find node: %s\n", compatible);
+		else {
+			ret = of_property_read_u32(np, "address", &logophys);
+			if (ret)
+				pr_warn("memlogo: address is not found in device tree\n");
+			ret = of_property_read_u32(np, "size", &logosize);
+			if (ret)
+				pr_warn("memlogo: size is not found in device tree\n");
+		}
+		of_node_put(np);
+
+		if (logophys && logosize) {
+			validate_logo = phys_to_virt(logophys);
+
+			if (validate_logo->type < LINUX_LOGO_MONO ||
+			    validate_logo->type > LINUX_LOGO_GRAY256)
+				return logo;
+
+			switch (validate_logo->type) {
+			case LINUX_LOGO_MONO:
+				logo_bpc = 1;
+				break;
+			case LINUX_LOGO_VGA16:
+				logo_bpc = 4;
+				break;
+			case LINUX_LOGO_CLUT224:
+				fallthrough;
+			case LINUX_LOGO_GRAY256:
+				logo_bpc = 8;
+				break;
+			}
+
+			logo_data_size = DIV_ROUND_UP(validate_logo->width *
+						      validate_logo->height *
+						      logo_bpc, 8);
+
+			logo_size_validate = sizeof(struct linux_logo);
+			logo_size_validate += logo_data_size;
+			logo_size_validate += validate_logo->clutsize;
+			if (logosize < logo_size_validate) {
+				pr_err("memlogo: logo size is too big. Need to reserve more memory for logo in DT\n");
+				return logo;
+			}
+
+			if (logosize < logo_data_size + (uint32_t)validate_logo->data)
+				return logo;
+			if (validate_logo->type == LINUX_LOGO_CLUT224 &&
+			    logosize < validate_logo->clutsize + (uint32_t)validate_logo->clut)
+				return logo;
+
+			validate_logo->data += (size_t)validate_logo;
+			validate_logo->clut += (size_t)validate_logo;
+			logo = validate_logo;
+		}
+#endif
 
 	}
 	return logo;
-- 
GitLab