[PATCH v2 2/6] video: logo: allow the boot logo to come from the device tree
Max Pedraza <[email protected]> Wed, 5 Aug 2026 00:56:13 +0200
| Newsgroups | gmane.linux.kernel,gmane.comp.video.dri.devel,gmane.linux.drivers.devicetree |
|---|---|
| Message-ID | <[email protected]> |
Add CONFIG_LOGO_DT_CLUT224, which makes fb_find_logo() look for a node compatible with "linux,boot-logo-clut224" under /chosen before falling back to the logos built into the kernel image. The image is validated before it is used: the palette must have at most 224 entries, the pixel data length must match the geometry, and every pixel must reference an entry that exists. A malformed node is reported and ignored rather than drawn, so a bad device tree cannot take the display down with it. The image is copied out of the device tree so that the 32 entry offset the frame buffer layer reserves for the console can be applied to the pixels, and the copy is released from fb_logo_late_init() alongside the built-in logos. The node lives under /chosen because a logo is configuration handed over by firmware rather than a description of the hardware, which is also where simple-framebuffer nodes live for the same reason. Signed-off-by: Max Pedraza <[email protected]> --- drivers/video/logo/Kconfig | 12 +++ drivers/video/logo/logo.c | 160 +++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) diff --git a/drivers/video/logo/Kconfig b/drivers/video/logo/Kconfig index cda15b958..215afa7ef 100644 --- a/drivers/video/logo/Kconfig +++ b/drivers/video/logo/Kconfig @@ -76,4 +76,16 @@ config LOGO_LINUX_CLUT224_FILE magick source_image -compress none -colors 224 destination.ppm +config LOGO_DT_CLUT224 + bool "224-color logo supplied by the device tree" + depends on OF + help + Look for a boot logo in the device tree, in a node compatible with + "linux,boot-logo-clut224" under /chosen, instead of using one of + the logos built into the kernel image. This allows a single kernel + image to be used by several products that only differ in branding. + + If no such node is present, or it is disabled, the built-in logo + selected above is used, so saying Y here is safe. + endif # LOGO diff --git a/drivers/video/logo/logo.c b/drivers/video/logo/logo.c index 91535f884..7f8b04ecf 100644 --- a/drivers/video/logo/logo.c +++ b/drivers/video/logo/logo.c @@ -11,6 +11,9 @@ */ #include <linux/linux_logo.h> +#include <linux/of.h> +#include <linux/sizes.h> +#include <linux/slab.h> #include <linux/stddef.h> #include <linux/module.h> @@ -22,6 +25,155 @@ static bool nologo; module_param(nologo, bool, 0); MODULE_PARM_DESC(nologo, "Disables startup logo"); +#ifdef CONFIG_LOGO_DT_CLUT224 + +#define LOGO_DT_COMPATIBLE "linux,boot-logo-clut224" +#define LOGO_DT_MAX_CLUT 224 +/* + * The first 32 palette entries are reserved for the console, so the logo + * colours start at index 32. That is an implementation detail of the frame + * buffer layer rather than a property of the image, so the device tree stores + * plain indices and the offset is applied here. + */ +#define LOGO_DT_CLUT_OFFSET 32 +/* Sanity limit on the image size, a device tree is not a good place for more */ +#define LOGO_DT_MAX_PIXELS SZ_32M + +static struct linux_logo logo_dt_clut224 = { + .type = LINUX_LOGO_CLUT224, +}; + +static unsigned char *logo_dt_clut; +static unsigned char *logo_dt_data; + +static int logo_dt_parse(struct device_node *np) +{ + unsigned int clutsize, npixels, i; + unsigned char *clut, *data; + u32 width, height; + int len, ret; + + ret = of_property_read_u32(np, "width", &width); + if (ret) + return ret; + + ret = of_property_read_u32(np, "height", &height); + if (ret) + return ret; + + if (!width || !height || (u64)width * height > LOGO_DT_MAX_PIXELS) + return -EINVAL; + + npixels = width * height; + + len = of_property_count_u8_elems(np, "clut"); + if (len < 3 || len % 3) + return -EINVAL; + + clutsize = len / 3; + if (clutsize > LOGO_DT_MAX_CLUT) + return -EINVAL; + + ret = of_property_count_u8_elems(np, "data"); + if (ret < 0) + return ret; + if ((unsigned int)ret != npixels) + return -EINVAL; + + clut = kmalloc(len, GFP_KERNEL); + if (!clut) + return -ENOMEM; + + data = kmalloc(npixels, GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto err_free_clut; + } + + ret = of_property_read_u8_array(np, "clut", clut, len); + if (ret) + goto err_free_data; + + ret = of_property_read_u8_array(np, "data", data, npixels); + if (ret) + goto err_free_data; + + for (i = 0; i < npixels; i++) { + if (data[i] >= clutsize) { + ret = -ERANGE; + goto err_free_data; + } + data[i] += LOGO_DT_CLUT_OFFSET; + } + + logo_dt_clut = clut; + logo_dt_data = data; + + logo_dt_clut224.width = width; + logo_dt_clut224.height = height; + logo_dt_clut224.clutsize = clutsize; + logo_dt_clut224.clut = clut; + logo_dt_clut224.data = data; + + return 0; + +err_free_data: + kfree(data); +err_free_clut: + kfree(clut); + return ret; +} + +static const struct linux_logo *logo_dt_find(void) +{ + static bool probed; + struct device_node *np; + int ret; + + if (probed) + return logo_dt_data ? &logo_dt_clut224 : NULL; + + probed = true; + + np = of_get_compatible_child(of_chosen, LOGO_DT_COMPATIBLE); + if (!np) + return NULL; + + if (of_device_is_available(np)) { + ret = logo_dt_parse(np); + if (ret) + pr_warn("logo: ignoring malformed %pOF node (%d)\n", + np, ret); + } + + of_node_put(np); + + return logo_dt_data ? &logo_dt_clut224 : NULL; +} + +static void logo_dt_free(void) +{ + logo_dt_clut224.clut = NULL; + logo_dt_clut224.data = NULL; + + kfree(logo_dt_clut); + logo_dt_clut = NULL; + + kfree(logo_dt_data); + logo_dt_data = NULL; +} + +#else /* !CONFIG_LOGO_DT_CLUT224 */ + +static inline const struct linux_logo *logo_dt_find(void) +{ + return NULL; +} + +static inline void logo_dt_free(void) { } + +#endif /* CONFIG_LOGO_DT_CLUT224 */ + /* * Logos are located in the initdata, and will be freed in kernel_init. * Use late_init to mark the logos as freed to prevent any further use. @@ -32,6 +184,7 @@ static bool logos_freed; static int __init fb_logo_late_init(void) { logos_freed = true; + logo_dt_free(); return 0; } @@ -48,6 +201,13 @@ const struct linux_logo * __ref fb_find_logo(int depth) if (nologo || logos_freed) return NULL; + /* A logo supplied by the device tree wins over the built-in ones */ + if (depth >= 8) { + logo = logo_dt_find(); + if (logo) + return logo; + } + #ifdef CONFIG_LOGO_LINUX_MONO if (depth >= 1) logo = &logo_linux_mono; -- 2.39.5