diff --git a/drivers/video/logo/Kconfig b/drivers/video/logo/Kconfig index c5ef942ad73a77b2af23c4fa74dd8fa4259b5997..685c46e628993cf6548f45af4b5ba77ef75d4b44 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 59b0bc8f4f334fc4a9012dd4c8053ba3b758b969..40623314604d3ff97ee412259b84401461128110 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;