From nobody Fri Oct 2 05:28:19 2026 Received: from mail-wm2-f13.google.com (mail-wm2-f13.google.com [74.125.225.141]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9051857ED8C for ; Wed, 23 Sep 2026 20:11:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.225.141 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194271; cv=none; b=lRAGdwmtyFQXIUPY8RKH+GcaHEDU6TI/aJSdMBEr26y87WIuzgexheNg9QEKYSlxB1Bh/DvlALZhjudZb8gWRv3muFp4EaEpQrm24GcwG052nXj8ykROktohNRbaX1+q7lZyvyxXJgrB7PJ3934UySwjsCryWWJMDzpbgm1ESx0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194271; c=relaxed/simple; bh=MGegY49PyrX5dI2WXMEBmc92kvSEAfIxTrxpSA/jTWo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=YXdUITZZsWNfCdAqFFiVwQa1H61cFRlRaCd0auW8zqTjrk8ll+BZF//aKg1jMD/g/UpQZs9OWU6pRFETx15xPNIv0Zyy12hfL2Syfh9sU4vKewLHuYpFezHpV3xiymV5bUZ52MTAshxv0f16twTSJ1iQHX2SbrCZNt4n0qxgm8Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=sJV3KT2J; arc=none smtp.client-ip=74.125.225.141 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="sJV3KT2J" Received: by mail-wm2-f13.google.com with SMTP id 5b1f17b1804b1-49b912e2406so6611265e9.1 for ; Wed, 23 Sep 2026 13:10:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790194254; x=1790799054; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=KzWnvXvBcRYh23VzcFk9fIrKKbovPKtd+dDK4CYg1CA=; b=sJV3KT2JPwcsLZnEEveDSmtWGeXz57ilHAD+b+Wet1VQ4DqqSxWM+d9Y5RyRr/6Ycz vPaN7To38SaSL5d5TKit7/AWP664F62EALQjtRpyt42TNgc8suM2LIVWwFuwuW4SWBWk BP4Z6b2CcLF+vMKFhe2rH2OUCH49n4C4ttIJOQ8jHNLHEZCuQhlny0B1OmUWlBEeT3nL rTFheIzbz406WgABzHlrMA/6aMC4Ob4sUcfWrEo7tW8JNmW5AerlgpIm/pWduIm7eq9Q w+xD4opCXuZX3WhVxMaWKWsy7eZNvKu2vJ5jGwrvSmFaIjJm9EpJeKblSCQ3qxtj5pFn jQfA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790194254; x=1790799054; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=KzWnvXvBcRYh23VzcFk9fIrKKbovPKtd+dDK4CYg1CA=; b=VKW+v+L4SFvTqiCOilGqvxEzayLqj/ZIllL801nyDC0Sv05oZucJGoSZL+aTINgPBW 4rBdyV2XWpptfQUagvh3ivfF9Mu8kR/pDXBybD0fZ2wXqcF+s3UydjrF8lppSv7O2Qne UVJdVWQtf59na6opgyoH/6NI5Utm+vPRxSiTdRK24dAaPgnvtUG4LRHFgvb7jPZNjnd/ ulaPev/fdmruQLgzVaRvJXP7R6lOv0l3VKJeVw0ttQgu67gB02SXHRNu2r6LpS2S/VPy zvh+KvkoecuiJMVXTknw7ciV/zV0EsKCe/OOm68EwWhCSnktVn0QoWrzbEiMGTXYAYVd KXAw== X-Forwarded-Encrypted: i=1; AKwUvBz4KjpaDuMZ1apbWUray8WpTt2eeVgo9Zyt+f395wC8ZK7TiABtdSJMFG/chFKx4WMGlBtk+8MHKo/C8kI=@vger.kernel.org X-Gm-Message-State: AFuF++kH723QoTEHl8PGnyBEEMM3AJsTZ5k6/ubVAyw7WPLqFuD71XvX g1XYwxAOMSFAXQQk9rVYgP1sdEPuYWNQQXKjzCVDZgCjeKfOst8z94VP X-Gm-Gg: AYBFou16r4IjdFWjyQRzQe/03O1TYaHsPf8HWHZcjrCe8QRUpKu+KIqbb5B43VOh3d8 PW6RRHcYqo50XW/GPPJEOLBelg9tMK7EoyBO38o7ESIAui2fPdFBbSm1O2X/sBCQXozjj37z/Rv xJRKfqOLGClOMTl2VuoofM6994tWjr7vNyvujMHju8C9/PA4Yj2hZf2VvVZxCoksGPgsF1ogNhC GHzm4k0hrDVPSDkwsXDRs4Dn9OPoeY4+t0PtyfgsXFzE7DKKUI5PfjawkpArpltCBRkJh10xhQk L1TVNjV8giGtTR6Dq0s0BbwCq2UNOUR/zVn3KlwmvHd013ZkAKTQwBu2KYkm5cmIBKRs15nAg2p pldbceHPHl+y+g70TA8wWkfP/L1nXm7NnZOa+o6p5/Cl+T9T6YpoOdJRIBcsf1AZoyejaWVnnI/ bdHzyFwJBjeuOE9yGPZ4agqTg7cKAsWXazoONPOoubqgPhx8X3Btvcop1ub4UjfnVX5DA2W0tTA EQTl8+MkwQ7UPFQrsz7aaTlOwWQ2h2Jazk= X-Received: by 2002:a05:600c:4705:b0:49e:65f2:db64 with SMTP id 5b1f17b1804b1-49fe66d03b6mr4620805e9.5.1790194253946; Wed, 23 Sep 2026 13:10:53 -0700 (PDT) Received: from ingenieria31.oficinasStQ.local ([79.112.15.218]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49fe5cd4c8dsm13559295e9.3.2026.09.23.13.10.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Sep 2026 13:10:53 -0700 (PDT) From: Max Pedraza To: Helge Deller , Thomas Zimmermann , Simona Vetter , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Maxime Ripard Cc: linux-fbdev@vger.kernel.org, devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Max Pedraza Subject: [PATCH v3 1/7] fbdev: describe where the boot logo goes in one place Date: Wed, 23 Sep 2026 22:10:29 +0200 Message-Id: <20260923201035.51007-2-maximpedraza@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260923201035.51007-1-maximpedraza@gmail.com> References: <20260923201035.51007-1-maximpedraza@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" fb_prepare_logo() works out how many rows to keep clear for the logo and fb_show_logo_line() works out where to draw it, and both of them open code the same decision: centred if fb_center_logo, top left otherwise. The two calculations have to agree, because fbcon erases whatever falls outside the rows that were reserved, and nothing makes them. Give the position a small structure of its own, with -1 on an axis meaning centre on that axis, and have both callers ask for it. fb_center_logo becomes {-1, -1} rather than a second path, and the arithmetic lives in one function that both use, so they cannot end up disagreeing. The structure and logo_place_axis() go in linux_logo.h rather than in the frame buffer code, since where a logo goes is a property of the logo, not of the one thing that draws it today. logo_place_axis() also clamps, which the open coded version did not: the result is now always a position at which the logo lies entirely on screen. That is not reachable today, since the only positions are the two the console offers, but it stops being something the next caller has to remember. No functional change intended. Signed-off-by: Max Pedraza --- drivers/video/fbdev/core/fb_logo.c | 58 +++++++++++++++++++----------- include/linux/linux_logo.h | 29 +++++++++++++++ 2 files changed, 67 insertions(+), 20 deletions(-) diff --git a/drivers/video/fbdev/core/fb_logo.c b/drivers/video/fbdev/core/= fb_logo.c index 0bab8352b6..5ec9f9554f 100644 --- a/drivers/video/fbdev/core/fb_logo.c +++ b/drivers/video/fbdev/core/fb_logo.c @@ -8,6 +8,17 @@ bool fb_center_logo __read_mostly; int fb_logo_count __read_mostly =3D -1; =20 +/* + * The placement in effect, as asked for on the console command line. + */ +static const struct logo_placement *fb_logo_placement(void) +{ + static const struct logo_placement centred =3D { .x =3D -1, .y =3D -1 }; + static const struct logo_placement top_left =3D { }; + + return fb_center_logo ? ¢red : &top_left; +} + static inline unsigned int safe_shift(unsigned int d, int n) { return n < 0 ? d >> -n : d << n; @@ -281,7 +292,11 @@ static int fb_show_logo_line(struct fb_info *info, int= rotate, { u32 *palette =3D NULL, *saved_pseudo_palette =3D NULL; unsigned char *logo_new =3D NULL, *logo_rotate =3D NULL; + const struct logo_placement *p; + unsigned int xres =3D info->var.xres; + unsigned int yres =3D info->var.yres; struct fb_image image; + unsigned int block; =20 /* Return if the frame buffer is not mapped or suspended */ if (logo =3D=3D NULL || info->state !=3D FBINFO_STATE_RUNNING || @@ -322,26 +337,22 @@ static int fb_show_logo_line(struct fb_info *info, in= t rotate, fb_set_logo(info, logo, logo_new, fb_logo.depth); } =20 - if (fb_center_logo) { - int xres =3D info->var.xres; - int yres =3D info->var.yres; + image.width =3D logo->width; + image.height =3D logo->height; =20 - if (rotate =3D=3D FB_ROTATE_CW || rotate =3D=3D FB_ROTATE_CCW) { - xres =3D info->var.yres; - yres =3D info->var.xres; - } + if (rotate =3D=3D FB_ROTATE_CW || rotate =3D=3D FB_ROTATE_CCW) + swap(xres, yres); =20 - while (n && (n * (logo->width + 8) - 8 > xres)) - --n; - image.dx =3D (xres - (n * (logo->width + 8) - 8)) / 2; - image.dy =3D y ?: (yres - logo->height) / 2; - } else { - image.dx =3D 0; - image.dy =3D y; - } + while (n && (n * (logo->width + 8) - 8 > xres)) + --n; =20 - image.width =3D logo->width; - image.height =3D logo->height; + /* The copies are drawn in a row, so they are centred as one block */ + block =3D n ? n * (logo->width + 8) - 8 : logo->width; + + p =3D fb_logo_placement(); + image.dx =3D logo_place_axis(p->x, xres, block); + /* A stacked logo goes where the caller put it */ + image.dy =3D y ? y : logo_place_axis(p->y, yres, image.height); =20 if (rotate) { logo_rotate =3D kmalloc_array(logo->width, logo->height, @@ -418,6 +429,7 @@ static int fb_show_extra_logos(struct fb_info *info, in= t y, int rotate) int fb_prepare_logo(struct fb_info *info, int rotate) { int depth =3D fb_get_color_depth(&info->var, &info->fix); + const struct logo_placement *p; unsigned int yres; int height; =20 @@ -480,9 +492,15 @@ int fb_prepare_logo(struct fb_info *info, int rotate) } } =20 - height =3D fb_logo.logo->height; - if (fb_center_logo) - height +=3D (yres - fb_logo.logo->height) / 2; + /* + * fbcon only leaves the first @height rows of the screen alone, so a + * logo placed further down would be drawn and then immediately + * cleared. Ask the same placement fb_show_logo_line() will use, so + * that the two cannot disagree. + */ + p =3D fb_logo_placement(); + height =3D logo_place_axis(p->y, yres, fb_logo.logo->height) + + fb_logo.logo->height; #ifdef CONFIG_FB_LOGO_EXTRA height =3D fb_prepare_extra_logos(info, height, yres); #endif diff --git a/include/linux/linux_logo.h b/include/linux/linux_logo.h index 1e727a2cb4..b3b15d3800 100644 --- a/include/linux/linux_logo.h +++ b/include/linux/linux_logo.h @@ -13,6 +13,8 @@ */ =20 #include +#include +#include =20 =20 #define LINUX_LOGO_MONO 1 /* monochrome black/white */ @@ -36,6 +38,33 @@ extern const struct linux_logo logo_linux_clut224; extern const struct linux_logo logo_spe_clut224; =20 extern const struct linux_logo *fb_find_logo(int depth); + +/* + * Where a boot logo goes. A coordinate of -1 centres the logo on that axi= s. + * Whatever draws the logo describes its placement this way and computes it + * with logo_place_axis(), so that no two places can end up disagreeing ab= out + * where the logo is. + */ +struct logo_placement { + s32 x, y; +}; + +/* + * Place a logo along one axis. @pos is the coordinate asked for, or -1 to + * centre on that axis. The result is clamped so that the logo always lies + * entirely within the screen: the drawing code does not clip, so asking f= or + * more than that would otherwise scribble past the end of the frame buffe= r. + */ +static inline int logo_place_axis(s32 pos, unsigned int span, unsigned int= size) +{ + int last =3D (int)span - (int)size; + + if (size > span) + return 0; + + return pos =3D=3D -1 ? last / 2 : clamp(pos, 0, last); +} + #ifdef CONFIG_FB_LOGO_EXTRA extern void fb_append_extra_logo(const struct linux_logo *logo, unsigned int n); --=20 2.39.5 From nobody Fri Oct 2 05:28:19 2026 Received: from mail-wm2-f12.google.com (mail-wm2-f12.google.com [74.125.225.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DA29E580389 for ; Wed, 23 Sep 2026 20:11:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.225.140 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194274; cv=none; b=syeaX4jPUnfAJdK4Y/4791E97QotCXJvtI2Ggd7Cb97MEncWpTSF5Peml7UbTRfwsOmSBR6D+RVQOxBVvvIE1agb9tEINcd2ntCaU1xJH+qidX6qU0dwPBkWKDbFg494RkT2iQH6EObGQJi81PHQXlcfVaAIJOETpqvVidjNiMc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194274; c=relaxed/simple; bh=zt6hSECodB5WF8/06/9r9WG9bP75OayoZGseUD1mFAM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=pfwh7ZK/Xwmidp3f3jHsnf9deLGrg8/pry/esLXFvmqHgXZQ0LEDvdbqHNFswsiqiS5rtji901g2Ysy1QJEIiZgFMGvkvC8ZABGi/9/mgrLY07yHNj0D0TGij4rizbCUBF0si6V0evYj8tgOddwC55vq+reuzpYTQe3UCJxX/xI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=FP/hOT9t; arc=none smtp.client-ip=74.125.225.140 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="FP/hOT9t" Received: by mail-wm2-f12.google.com with SMTP id 5b1f17b1804b1-49ccfd61ecaso10694885e9.3 for ; Wed, 23 Sep 2026 13:11:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790194255; x=1790799055; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Z5ud7C6Q7hzdhSrMZ0UnmI/lSmzd22+sOaMu+2pIMXc=; b=FP/hOT9t6AENCmlVY+cwKC4aAhbgY1oTXmFtvmbMalrBfTFIwSYrWTjC3mmyp5fMDG sfd152MN9mHGHdxNrUcry6+dxCZGE2qOjXtYzt1dRQFIoguCn2BvHTvccqGHHOERy3WL Qx1OWyrN74/exQ73vnY2PFK7qKDsB2o/NChi/Bpgseiub/O/V1v7eDgY8p8NJc9hktFW 8JSl5ZsG8oRUTWGwSU6cWNj3FAqkJsgARvHL82vtzYp/cD8acdnKA2B49UngI3XAEr95 QYxgwLnl1+IKPzu5yD5t7Y5gwxbXhaWjz6kobA1+DDv9SnVdnd1FUHpoHysY/3r8hKEH nyVA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790194255; x=1790799055; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=Z5ud7C6Q7hzdhSrMZ0UnmI/lSmzd22+sOaMu+2pIMXc=; b=Plj6nYzWFc7THubZPdw4/542rihoCMaBRkaTmkYXdZZI0jVgbAw8NOoDtQ8K/wDEIs iPLBvyEo9s+oaNdliio6FCEvVi0u8jr3EcJWzf0YZG/t8BH1uR9zEXJ87Ht+/Bl30XvK Qxpgc2v7N+JXxxM5ijSPwoRn7Ov/ldhFz/6JTR3bMTk7iL/umSbsMWeRMiLikqlyDSDk 83OUC4fIzXsDwpgKESZp6wvjosd9b4lnUb1Pq+iWd9IhmMANBwhUaMrXoIrL59RWSutz 1p1E71UWblPN7QbYAnVj0ajfP6ODlm+tMAmpQLY88drKJhSQJg8iSxyqhQZNOKt0xlXp R++A== X-Forwarded-Encrypted: i=1; AKwUvBxLQ8BHlBk6bnMnnTTezPyOmotpdAXJ27goMurJKEno9JM61NvlRY7mexCR09bpLAKBYs/0f1E6hougvKs=@vger.kernel.org X-Gm-Message-State: AFuF++l8KeBamiSTTLjbOKN4xa59ZqJ8419W1ifytK90oQrcQUhy1OPM g2sKwo5gMcgvis8BxcNMy1+FnlZgIYwLhBVKuwNyE5cd0H9wjS4a4voN X-Gm-Gg: AYBFou3yUImyzv9cmekN6yLPctjxu0cdckxhrC7PSM0lDefyHoSHuTO3h8lqFlx+fw1 FwtmldUZa60ezHXjUSumsdQfRIBmJwfR+uo1yhBJWTTw/GEj905G0xzU4Zy1vyvRQood6Y1jkJm ts3xbXqg+UpG/F88LR6Kio8UEBoONwCRv0Dq1rLapNLl2PVxnBKDLwx7cCQ9b8PuH3bgNxg9bmd NkAXFM3aSek0uIwxVkdHbRTHuyn1wjMcKJGA5xTAa6i6SxQo1NS3h+vWM6+QmSr0/007afsZKhO +zkhPBzWb9CPRcTJBtvkeceHwWYc66fjcitFWcp0EN7VW+Dq+lJ/wbo2/dV6RDYLmYS5VNbxd4z M7q+/Oq0CMEOfRbe/NO0AQuQeQ9dBVfeN3Q/q2Gi+aou7oOkgkugyKAhlqEPw/OVXhOvrYojTH0 hiOX3fUDeccVzOm/BVVdgwMOCy2EoJMEh2SaECGVbA42xB5WcxHjt6YkqjgyG1eovr6molcc8KQ 4/NqymP8ce93GA62akR/hYt X-Received: by 2002:a05:600c:4fc7:b0:49e:799e:c1c3 with SMTP id 5b1f17b1804b1-49fe66f1444mr4525325e9.17.1790194255204; Wed, 23 Sep 2026 13:10:55 -0700 (PDT) Received: from ingenieria31.oficinasStQ.local ([79.112.15.218]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49fe5cd4c8dsm13559295e9.3.2026.09.23.13.10.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Sep 2026 13:10:54 -0700 (PDT) From: Max Pedraza To: Helge Deller , Thomas Zimmermann , Simona Vetter , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Maxime Ripard Cc: linux-fbdev@vger.kernel.org, devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Max Pedraza Subject: [PATCH v3 2/7] dt-bindings: display: add a device tree supplied boot logo Date: Wed, 23 Sep 2026 22:10:30 +0200 Message-Id: <20260923201035.51007-3-maximpedraza@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260923201035.51007-1-maximpedraza@gmail.com> References: <20260923201035.51007-1-maximpedraza@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Embedded products frequently need a product specific boot logo. Today that means pointing CONFIG_LOGO_LINUX_CLUT224_FILE at a different image, which bakes it into the kernel image: a change of branding, or a second product sharing the same board support package, requires a separate kernel build, and requalifying that kernel for what is a cosmetic change. Add a binding for a "boot-logo-clut224" node, which carries the logo in the same paletted format the built-in CLUT224 logos already use, plus a few optional properties describing where on the screen it is drawn. The node lives under /chosen because a logo is configuration handed over by firmware rather than a description of the hardware. Open Firmware, which the device tree descends from, carried a boot logo in the same spirit as the oem-logo variable under /options, and simple-framebuffer nodes live under /chosen today for the same reason. The palette size is derived from the length of the "clut" property rather than spelled out separately, and "data" holds plain palette indices; the 32 entry offset the frame buffer layer reserves for the console is an implementation detail and is applied by the kernel, not by the binding. "logo-position" takes -1 on an axis to mean centre on that axis rather than carrying a separate boolean for it. A boolean can only centre both axes or neither, and next to explicit coordinates it would have to override them silently when a device tree gave both. A rotation turns the logo, not the screen. "logo-position" and "logo-offset" are screen pixels whatever "logo-rotation" says, and a quarter turn only changes how much room the logo takes up. Placing the logo in a frame that turns with it would make the same pair of coordinates mean different places on the same panel, decided by a property that is meant to describe the image. Every coordinate is bounded. The kernel clamps them anyway, since it cannot trust a device tree, but a value that cannot possibly be meant is worth catching in dtbs_check rather than on the panel. Signed-off-by: Max Pedraza --- .../bindings/display/boot-logo-clut224.yaml | 135 ++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 136 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/boot-logo-clu= t224.yaml diff --git a/Documentation/devicetree/bindings/display/boot-logo-clut224.ya= ml b/Documentation/devicetree/bindings/display/boot-logo-clut224.yaml new file mode 100644 index 0000000000..9dc3471763 --- /dev/null +++ b/Documentation/devicetree/bindings/display/boot-logo-clut224.yaml @@ -0,0 +1,135 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/boot-logo-clut224.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Boot logo supplied by the device tree + +maintainers: + - Max Pedraza + +description: | + Embedded systems commonly need a product specific boot logo. Today that + means pointing CONFIG_LOGO_LINUX_CLUT224_FILE at a different image, which + bakes it into the kernel image: changing the logo means building and + deploying a new kernel. + + This node lets the boot logo be described by the device tree instead, so= that + a single kernel image can serve several products, or several revisions o= f the + same product, that only differ in branding. + + Since a logo is configuration rather than a description of the hardware,= the + node lives under /chosen, next to the other things firmware hands to the + operating system. Open Firmware, which the device tree descends from, ca= rried + a boot logo in the same spirit as the oem-logo variable under /options. + + The image is stored in the same paletted format the in-kernel CLUT224 lo= gos + use: a palette of at most 224 RGB entries plus one palette index per pix= el. + +properties: + $nodename: + const: logo + + compatible: + const: boot-logo-clut224 + + width: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Logo width in pixels. + minimum: 1 + maximum: 65535 + + height: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Logo height in pixels. + minimum: 1 + maximum: 65535 + + clut: + $ref: /schemas/types.yaml#/definitions/uint8-array + description: + Colour lookup table, as consecutive red, green and blue bytes per en= try. + The number of entries is derived from the property length and must n= ot + exceed 224. + minItems: 3 + maxItems: 672 + + data: + $ref: /schemas/types.yaml#/definitions/uint8-array + description: + One byte per pixel, left to right and top to bottom, each byte being= an + index into the colour lookup table. The property length must be equa= l to + width multiplied by height. + + logo-position: + $ref: /schemas/types.yaml#/definitions/int32-array + description: + X and Y coordinates, in pixels, of the top left corner of the logo. + A value of -1 on an axis centres the logo on that axis instead. + Defaults to the top left corner of the screen. + items: + - description: X coordinate, or -1 to centre horizontally + minimum: -1 + maximum: 65535 + - description: Y coordinate, or -1 to centre vertically + minimum: -1 + maximum: 65535 + + logo-offset: + $ref: /schemas/types.yaml#/definitions/int32-array + description: + X and Y displacement, in screen pixels, applied after the logo has b= een + placed. + Mostly useful together with a centred axis, to land the logo somewhe= re + other than the middle of a panel whose usable area is not its centre. + items: + - description: X displacement + minimum: -65535 + maximum: 65535 + - description: Y displacement + minimum: -65535 + maximum: 65535 + + logo-rotation: + $ref: /schemas/types.yaml#/definitions/string + description: + Rotation applied to the logo before it is drawn. It turns the logo a= nd + not the screen, so a quarter turn swaps how much room the logo takes + up, but logo-position and logo-offset stay in screen pixels either + way. + enum: [none, cw, ccw, ud] + default: none + +required: + - compatible + - width + - height + - clut + - data + +additionalProperties: false + +examples: + - | + // A 4x2 logo using three colours, centred on both axes. + / { + compatible =3D "foo"; + model =3D "foo"; + #address-cells =3D <1>; + #size-cells =3D <1>; + + chosen { + logo { + compatible =3D "boot-logo-clut224"; + width =3D <4>; + height =3D <2>; + clut =3D /bits/ 8 <0xff 0x00 0x00 + 0x00 0xff 0x00 + 0x00 0x00 0xff>; + data =3D /bits/ 8 <0x00 0x01 0x01 0x00 + 0x02 0x00 0x00 0x02>; + logo-position =3D <(-1) (-1)>; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index c241444789..804a48e9bc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10339,6 +10339,7 @@ L: dri-devel@lists.freedesktop.org S: Maintained Q: http://patchwork.kernel.org/project/linux-fbdev/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/deller/linux-fbdev.git +F: Documentation/devicetree/bindings/display/boot-logo-clut224.yaml F: Documentation/fb/ F: drivers/video/ F: include/linux/fb.h --=20 2.39.5 From nobody Fri Oct 2 05:28:19 2026 Received: from mail-wm2-f12.google.com (mail-wm2-f12.google.com [74.125.225.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D4BD7580387 for ; Wed, 23 Sep 2026 20:11:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.225.140 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194293; cv=none; b=Ls0q6qYyJro0sASB4mZSi0qFtqT1yDgKgBKsgAap/eW3IHaSHUF45wIGJnj4ww3z3fCgYRGeYnLbMlEQNEhg7fwck8pp76hKE1aQRXB6K6nOam+MGmymQwyC6yJPUiZKPEJPfMtFiXIbS46xpiHv+2WmSophmXsA1ZSUirVaDi0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194293; c=relaxed/simple; bh=7oqKquhakJe90oiy+DxuOhrOlnIl+OIGd9ser/F9AeM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=HHtHoxgGVZBUZyCVEFOVSDGMJv+w2TyYsMyqTA7GPqQibz2JZohxPUobPg6nWOgQVuxnmlOMpt7wgh3hmcCzvw2A+s39muNKb1Zfn5fultoLWA/7ZVVgQHB67iuRbNboP8eQnBRJhCbWnqHquJ/UCF+Foknf0ULFo+Q2HEfHGTk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=U4Jd6et6; arc=none smtp.client-ip=74.125.225.140 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="U4Jd6et6" Received: by mail-wm2-f12.google.com with SMTP id 5b1f17b1804b1-49b912e4ad9so7772575e9.2 for ; Wed, 23 Sep 2026 13:11:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790194256; x=1790799056; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=5EUHnFPULkkfl9EPKbDpfgSFJiqys4NK1JcsO8IiCyo=; b=U4Jd6et6NatnLFrAhJCTQsC5cuINZRmg111cCCHHYlyjG7Gj1bodZTXOsG4puSQJqr IR2k2QW3Znqdz78D5WIjSnepRFhtW1+oCGTsz+24DY1kg1ZFmdwjY7iev8LlOspxMQMN 3eSDqNa8slwWu3y+3Kfk2qM+zD3s4KjFTAsapUSXDUCk64G0dfYqFLQinMscYxGr7YUN Z++T+qGGcjIKSljR8rqZMBzB/3cbfK3DTfvqyVDvRF6yiMg9v7GbjjC6JmZb4WO9O67V e0UhrjDRSZOw1DxVxlztXdk0nFCf12gZBFAT0nUp5t2r3LESdAlUUV1K/Lt2vcS6+FO5 s+kA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790194256; x=1790799056; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=5EUHnFPULkkfl9EPKbDpfgSFJiqys4NK1JcsO8IiCyo=; b=u2uRWJE3FLYntcgEEof/CLPaI/Dg6UoedS0gb/RM3JxYicyrGKHhPx92s4+Wlmhe2m HMxiVgTHslapOfh/53yAggHIZdrLk6iGSD6NVyYOBi6Gl8IP+lZoGbph47U7GhIA7pgv UTvs3Q1CL+50h9hEV+44KIGR5HSTPH+T7Y3+jlleCfkXKZPZJK+5BGiw/uBkTWkpZ2Yq 0u3lc1uHKCh1/lxiuBi4shHFURr+bEn8dhjDYK+KZ+KM0sk4/THFHA4uS6YwtsRUsD7U 21ATJnEdrRu2Y/p3/1PpgHiQhyq3gLqaGp5LtSZbfj2ej4ElumwgBCLYOtxeDQWxF5P1 8u+g== X-Forwarded-Encrypted: i=1; AKwUvBz03urh3heEWnTthM1kSRQ4LiZuYlZ+NigQbUmerQxLJ7NkzQCXMqli09uB2o0OnFtM5SKTiq/jniYWGd8=@vger.kernel.org X-Gm-Message-State: AFuF++leLZR/EerD9qrqVjdJG1UtxJlQ2TTfRg1jMmTRxBhPMOf3Y+qK ikxtPz9kk7yuR/3y0lt7N16ie5imrRBEsQ2UZ+8vaVR2OKjsD0ymuWB7 X-Gm-Gg: AYBFou3f5rgf1xJUD6TVSG5pfxySCfwK2x2z8VfIAM3KKNlyyRUA+pYqWozR8MjQYqX VGXYXnLABbBAAiTf3KZv2B1TM0XLU4C+VHCZvmbT2GtbtuP0EX8wdm/Y9ja/flv1RThW+kn0Dbc /S53GNnSMILtVnA+z5s/Yl2R/5xWdvfpg5V8wzm+uvdG/53RJxU6pl4LzwsG+309zZvSU2JvEXa M2tRSszqy5snMFmvLIqKdS7FZHSMaE2bagoL4UOhnEmdICD1PfkxqA5SZ/QXzlhB+BMyBZLuVGf rWJSP6IDt4c4Ho4YPkLjLsSxiffy424biGqNIRzIQzsac0NXcnBYepbhW5y47fIZfE0rk71EiyY J6ST4Gc00AHxc4JdDIQta1RGAqan/boyZyOXuGOgivS4C/mQDiPkSdYuKoJwSKGv3d+59j+Kqli OSNOVgT6ztCyFFaUMb9Qe7rowBEvbmByBtIWO9OHPbUpv4pjWqRXOT7zzHIjZwfV4NO5jQC/AFh gldIov/2ooBMceC6xznEODm X-Received: by 2002:a05:600c:c8f:b0:49c:dc14:d681 with SMTP id 5b1f17b1804b1-49fe66bdf3bmr4527355e9.3.1790194256374; Wed, 23 Sep 2026 13:10:56 -0700 (PDT) Received: from ingenieria31.oficinasStQ.local ([79.112.15.218]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49fe5cd4c8dsm13559295e9.3.2026.09.23.13.10.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Sep 2026 13:10:56 -0700 (PDT) From: Max Pedraza To: Helge Deller , Thomas Zimmermann , Simona Vetter , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Maxime Ripard Cc: linux-fbdev@vger.kernel.org, devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Max Pedraza Subject: [PATCH v3 3/7] video: logo: allow the boot logo to come from the device tree Date: Wed, 23 Sep 2026 22:10:31 +0200 Message-Id: <20260923201035.51007-4-maximpedraza@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260923201035.51007-1-maximpedraza@gmail.com> References: <20260923201035.51007-1-maximpedraza@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add CONFIG_LOGO_DT_CLUT224, which makes fb_find_logo() look for a node compatible with "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. The pixels are allocated with kvmalloc(), since a full screen image is larger than kmalloc() will comfortably serve. Unlike the built-in logos the copy is never freed. Those are initdata and go away in free_initmem(), which runs after async_synchronize_full(); anything this code could hook into runs before that, so releasing the image here would pull it out from under a display driver whose probe is still in flight. It is a modest allocation and it lives as long as the device tree it came from, which also means fb_find_logo() can still hand it out after the built-in logos are gone. 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. The lookup is guarded with IS_ENABLED() rather than wrapped in an #ifdef, so that the code is compile checked whatever the configuration and the compiler drops it when the option is off. Built for x86_64 with CONFIG_OF= =3Dn the object is left with no unresolved of_* symbol and none of the data. Signed-off-by: Max Pedraza --- drivers/video/logo/Kconfig | 12 +++ drivers/video/logo/logo.c | 154 ++++++++++++++++++++++++++++++++++++- include/linux/linux_logo.h | 3 + 3 files changed, 168 insertions(+), 1 deletion(-) diff --git a/drivers/video/logo/Kconfig b/drivers/video/logo/Kconfig index cda15b9589..cce89948f1 100644 --- a/drivers/video/logo/Kconfig +++ b/drivers/video/logo/Kconfig @@ -76,4 +76,16 @@ config LOGO_LINUX_CLUT224_FILE =20 magick source_image -compress none -colors 224 destination.ppm =20 +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 + "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 91535f8848..84afd5b337 100644 --- a/drivers/video/logo/logo.c +++ b/drivers/video/logo/logo.c @@ -11,6 +11,9 @@ */ =20 #include +#include +#include +#include #include #include =20 @@ -22,6 +25,139 @@ static bool nologo; module_param(nologo, bool, 0); MODULE_PARM_DESC(nologo, "Disables startup logo"); =20 +/* Boot logo supplied by the device tree */ + +#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 st= ores + * 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 m= ore: + * a 4K screen is 8.3M pixels, and the copy is kept for the life of the ke= rnel. + * One byte per pixel, so this is a byte count as well. + */ +#define LOGO_DT_MAX_PIXELS SZ_16M + +static struct linux_logo logo_dt_clut224 =3D { + .type =3D 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 =3D of_property_read_u32(np, "width", &width); + if (ret) + return ret; + + ret =3D of_property_read_u32(np, "height", &height); + if (ret) + return ret; + + if (!width || !height || (u64)width * height > LOGO_DT_MAX_PIXELS) + return -EINVAL; + + npixels =3D width * height; + + len =3D of_property_count_u8_elems(np, "clut"); + if (len < 3 || len % 3) + return -EINVAL; + + clutsize =3D len / 3; + if (clutsize > LOGO_DT_MAX_CLUT) + return -EINVAL; + + ret =3D of_property_count_u8_elems(np, "data"); + if (ret < 0) + return ret; + if ((unsigned int)ret !=3D npixels) + return -EINVAL; + + clut =3D kmalloc(len, GFP_KERNEL); + if (!clut) + return -ENOMEM; + + /* The palette is at most 672 bytes, the pixels can be megabytes */ + data =3D kvmalloc(npixels, GFP_KERNEL); + if (!data) { + ret =3D -ENOMEM; + goto err_free_clut; + } + + ret =3D of_property_read_u8_array(np, "clut", clut, len); + if (ret) + goto err_free_data; + + ret =3D of_property_read_u8_array(np, "data", data, npixels); + if (ret) + goto err_free_data; + + for (i =3D 0; i < npixels; i++) { + if (data[i] >=3D clutsize) { + ret =3D -ERANGE; + goto err_free_data; + } + data[i] +=3D LOGO_DT_CLUT_OFFSET; + } + + logo_dt_clut =3D clut; + logo_dt_data =3D data; + + logo_dt_clut224.width =3D width; + logo_dt_clut224.height =3D height; + logo_dt_clut224.clutsize =3D clutsize; + logo_dt_clut224.clut =3D clut; + logo_dt_clut224.data =3D data; + + return 0; + +err_free_data: + kvfree(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 (!IS_ENABLED(CONFIG_LOGO_DT_CLUT224)) + return NULL; + + if (probed) + return logo_dt_data ? &logo_dt_clut224 : NULL; + + probed =3D true; + + np =3D of_get_compatible_child(of_chosen, LOGO_DT_COMPATIBLE); + if (!np) + return NULL; + + if (of_device_is_available(np)) { + ret =3D 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; +} + /* * 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. @@ -45,7 +181,23 @@ const struct linux_logo * __ref fb_find_logo(int depth) { const struct linux_logo *logo =3D NULL; =20 - if (nologo || logos_freed) + if (nologo) + return NULL; + + /* + * A logo supplied by the device tree wins over the built-in ones. It + * is an ordinary allocation rather than initdata, so unlike them it + * stays valid for the life of the kernel, and is still there for a + * display driver whose probe finishes after the built-in logos have + * gone. + */ + if (depth >=3D 8) { + logo =3D logo_dt_find(); + if (logo) + return logo; + } + + if (logos_freed) return NULL; =20 #ifdef CONFIG_LOGO_LINUX_MONO diff --git a/include/linux/linux_logo.h b/include/linux/linux_logo.h index b3b15d3800..1e7e9db6dd 100644 --- a/include/linux/linux_logo.h +++ b/include/linux/linux_logo.h @@ -22,6 +22,9 @@ #define LINUX_LOGO_CLUT224 3 /* 224 colors */ #define LINUX_LOGO_GRAY256 4 /* 256 levels grayscale */ =20 +/* Compatible of the /chosen child describing a device tree supplied logo = */ +#define LOGO_DT_COMPATIBLE "boot-logo-clut224" + =20 struct linux_logo { int type; /* one of LINUX_LOGO_* */ --=20 2.39.5 From nobody Fri Oct 2 05:28:19 2026 Received: from mail-wm2-f12.google.com (mail-wm2-f12.google.com [74.125.225.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A6C8947D92E for ; Wed, 23 Sep 2026 20:11:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.225.140 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194276; cv=none; b=bNZXs4RLkYuiSD8V2TsPP8Bqxpo0bc4/gGIyoqvNa6NGV9himUKOxIG7L97WjfAO6rJJBxAn+fsaDzy9uGaA7R/vdifhXTI525qoEnUk3RA2pBQYv+7yh/7SsT+eaOIp//eC1BsIl7vRHgvcEeefXXZj0z+FSCUAL2TD+UKZrus= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194276; c=relaxed/simple; bh=1SsJzad2rfGBp3q0IiHU6zhsQrc+jYuLW4SsxI3epfA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=mGG594EfDcTlJZXsJJquVixBjfFXWAoHY1mmrD7AQkdYV1iUosdRJLPRcP6ruhQW/sF+cMG1snBSzxuiunt5ouUUZGyUmDLQVrc2k/jwOIC60JZCpa7ts6u/DVQGBBvOGO5v1DJ6wOrI6RDmrFyCV/Y8kp2635T1YIRs9C4m8EI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=jHHVz6Bb; arc=none smtp.client-ip=74.125.225.140 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="jHHVz6Bb" Received: by mail-wm2-f12.google.com with SMTP id 5b1f17b1804b1-49d1fb0cf5eso9814495e9.3 for ; Wed, 23 Sep 2026 13:11:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790194258; x=1790799058; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=OC847st04FyQ0eid/r6wSfnOUb+hyMcWCkXMSMi0LQk=; b=jHHVz6BbQzL+A67ABd/rhim+I8Peaa8JW40+GqpdlzVx8Br+3iKRqXh3B9NMTqqU7+ pYRABGP4ptyjfQwu3iZ0WNSkdHPJ4YnJly4Dkc8t5v+GBta4a6bJOnZ5UMYutz0zgG39 IKxlMt4NhfJ8qvFH86w0l/vMQFi+O1zcy6mQvB/JUasJHjtIno838/GWDPzdf0ZIie0m kLxcqgtSpzyxEEiA5U2JhtTSea2XAzEhjPaw7YJuqlACsH19QJXmvA55EitebqypBFGx qf9uZRmaCNN+lOXAUwduBuyOFztz0rzB5x8OJvWPq3bNcv29eOeBmoiHDX5KW+Rl70V1 L7uQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790194258; x=1790799058; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=OC847st04FyQ0eid/r6wSfnOUb+hyMcWCkXMSMi0LQk=; b=rscUEIj/t/zc1obiQR561QrtSun7h3arFudVS8xpoo7h3uGsIBtZfhtMjYA4pke7oY /X+0iPULCcQp6udMmiYVv85XhSJ8Kcc+bms01vbthDOuwpIzEo3c1eW9R9K4YJW5oVNj /S19G/+D/B68kgf04Ofp7PegrrBJbCY+iMiuJiZ1mTHNDYxlhBsAWqNv+oXJi4Y3GBVU NiTqDM7IpU/bCUiG+/9V6+tZpOLDc8FczrjF7ijXnR3og7PoGYKN7P62LjLpOoMUDB+D kMdGLwV/lvsjob0Gk46KCWsk3hYyq61geGIIpemJmqiUKptCCaVhYDvHE+2sSazwXVwC Kytw== X-Forwarded-Encrypted: i=1; AKwUvByjsJfOlBL3f2Jwt+Mnt2IB98R44ThuV2edCU7e8gyL0eFAzQdBLJEXvnG06e2oPBxAfacq9uo++e7mBUs=@vger.kernel.org X-Gm-Message-State: AFuF++mJuoWgAGdS45hOvJ4xzGv4SP+qaDLlBjc05JVMRrvLYOsMeJ3G dwlwTEvWSJG/xrHFSwUKf214f5jf8ZS21pMfUHELVW+xg9YAWWnMjLkK X-Gm-Gg: AYBFou1nzprqnpFZQ+0DY/jkLa+M3a1296fWjOVb2xRZnIPCaWl5z6EmO7bufuRiGk1 BVI9lyUo/PKbiGz6tE71N1+D7H/x2GN8Edx8ORhfgd/6kE5Gaa7j2cZ/m7AXwxZhkaw6rVtSyJP 6jj0Mn8SwhaTDl7oZ1BUg8rM2G1RaylesDQIda1tFXKHyJImMwnCAq0BogdMZNQou1uUjh6v5bb cTvBgRiyLibkSJXlZqhxpY0jsduCL8cQtEnO9VY5K67rm+5RE1M/w183lHnXkc5YTS7wOGKIlMO llukryG5UpPCoc4DjDpU5u+2qhBJfljXQ+ltoNsy9VBtpcnQJcVWu/XVj4ovzTj08LcF5c1fMD9 rDq/8PW4ipxSsJl4+LLy065N/T5nQ9n+h5pCdVZIW9NdwgdMxTCPixlvEVwkG4aPe5RGe+SWNvW mXF1xF9QzOIt0BsDIMuLRfoQYXeNlY6GQpmI/5POXIuIKnnTiSgq4IHkEkA9CSTiLCxQkAJYysB QpB9Ic8BZF0yBxwN49KiZlg X-Received: by 2002:a05:600c:3b02:b0:49f:bd3c:bc26 with SMTP id 5b1f17b1804b1-49fe6711fd2mr4734555e9.33.1790194257582; Wed, 23 Sep 2026 13:10:57 -0700 (PDT) Received: from ingenieria31.oficinasStQ.local ([79.112.15.218]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49fe5cd4c8dsm13559295e9.3.2026.09.23.13.10.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Sep 2026 13:10:57 -0700 (PDT) From: Max Pedraza To: Helge Deller , Thomas Zimmermann , Simona Vetter , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Maxime Ripard Cc: linux-fbdev@vger.kernel.org, devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Max Pedraza Subject: [PATCH v3 4/7] fbdev: honour the device tree boot logo placement properties Date: Wed, 23 Sep 2026 22:10:32 +0200 Message-Id: <20260923201035.51007-5-maximpedraza@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260923201035.51007-1-maximpedraza@gmail.com> References: <20260923201035.51007-1-maximpedraza@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" A logo supplied by the device tree describes a specific product, so where it lands on the panel matters in a way it does not for the built-in Tux. Fill in the placement added earlier from the optional "logo-position", "logo-offset" and "logo-rotation" properties of the "boot-logo-clut224" node, so that it takes the place of what the console command line would otherwise have asked for. "logo-position" gives the top left corner, with -1 on an axis meaning centre on that axis, which is what fb_center_logo already meant. "logo-offset" is added afterwards, and is the reason the offset joins the structure here. Centring and then displacing is what panels with a partially visible area need, where the usable region is not the centre of the mode, and it is the only way to express it when the image comes from a reserved memory region and the device tree therefore does not know the logo size. Both come straight from the device tree, so the sum is done in 64 bits and clamped, and a silly pair of values lands the logo against an edge rather than off the screen. The properties are read by logo_dt_placement(), in logo.c next to the parser of the image that comes from the same node, so that everything that knows the binding lives in one place. The frame buffer code only asks for the result. The rotation values match FB_ROTATE_*, but they do not mean what a rotated console means. fbcon turns the screen, so it looks at the panel sideways, places the logo in the console's own frame and maps the result back. A rotation asked for by the device tree turns the logo alone, on a screen that stays where it is, so the placement stays in screen pixels and only the room the logo takes up changes. fb_rotate_logo() is split into the part that turns the image and the part that moves the placement, and a device tree rotation uses only the former. A logo placed by the device tree is drawn once rather than once per CPU: the device tree names one position, and repeating the image from there would either overlap the copies or walk them off the reserved region. For the same reason the extra logos appended by fb_append_extra_logo() are not drawn when the device tree supplied the logo. They stack up from wherever the previous logo ended, which is an arbitrary point once the device tree has placed the first one, so the two do not compose. Only PowerPC Cell appends any, and a message is printed if there was anything to drop. The space fb_prepare_extra_logos() reserves for them is left alone, so that combination keeps a gap it no longer fills. The node is read once, from fb_prepare_logo(), since fb_show_logo() is only ever reached after that has run. The read is guarded with IS_ENABLED() rather than an #ifdef so that the code is compile checked whatever the configuration, and optimised away when the option is off. A node whose image is rejected still has its placement honoured, so a malformed node yields the built-in logo drawn where the device tree asked for it. Signed-off-by: Max Pedraza --- drivers/video/fbdev/core/fb_logo.c | 189 ++++++++++++++++++++++++----- drivers/video/logo/logo.c | 78 ++++++++++++ include/linux/linux_logo.h | 45 +++++-- 3 files changed, 274 insertions(+), 38 deletions(-) diff --git a/drivers/video/fbdev/core/fb_logo.c b/drivers/video/fbdev/core/= fb_logo.c index 5ec9f9554f..ff7d0b409e 100644 --- a/drivers/video/fbdev/core/fb_logo.c +++ b/drivers/video/fbdev/core/fb_logo.c @@ -9,13 +9,72 @@ bool fb_center_logo __read_mostly; int fb_logo_count __read_mostly =3D -1; =20 /* - * The placement in effect, as asked for on the console command line. + * Placement of a logo supplied by the device tree. Both the image and its + * placement are parsed by drivers/video/logo/logo.c, here we only keep a = copy + * of where it goes. + */ +static struct { + struct logo_placement pos; + bool valid; + int rotation; /* LOGO_ROTATE_* (=3D=3D FB_ROTATE_*), or -1 */ +} fb_logo_dt; + +/* + * Read the placement out of the device tree. Called once, from + * fb_prepare_logo(): fb_show_logo() is only ever reached after that has r= un, + * so everything below can just look at fb_logo_dt. + */ +static void fb_logo_dt_read(void) +{ + static bool read_done; + + /* + * logo_dt_placement() checks the option too, but it lives in another + * translation unit, so without the check here the compiler would have + * to keep fb_logo_dt around for a call that can only fail. + */ + if (!IS_ENABLED(CONFIG_LOGO_DT_CLUT224) || read_done) + return; + + fb_logo_dt.valid =3D !logo_dt_placement(&fb_logo_dt.pos, + &fb_logo_dt.rotation); + /* Last, so that nothing can observe a half filled in placement */ + read_done =3D true; +} + +/* + * A rotation asked for by the device tree turns the logo, not the screen,= so + * it is placed in screen pixels. A rotated console turns the screen, and = the + * logo is placed in the console's own frame. Telling the two apart is what + * this is for. + */ +static bool fb_logo_dt_rotated(void) +{ + return IS_ENABLED(CONFIG_LOGO_DT_CLUT224) && fb_logo_dt.valid && + fb_logo_dt.rotation >=3D 0; +} + +static int fb_logo_dt_rotation(int rotate) +{ + if (fb_logo_dt_rotated()) + return fb_logo_dt.rotation; + + return rotate; +} + +/* + * The placement in effect: what the device tree asked for if it supplied a + * logo, otherwise what the console command line asked for. fb_prepare_log= o() + * has already called fb_logo_dt_read() by the time anything gets here. */ static const struct logo_placement *fb_logo_placement(void) { static const struct logo_placement centred =3D { .x =3D -1, .y =3D -1 }; static const struct logo_placement top_left =3D { }; =20 + if (IS_ENABLED(CONFIG_LOGO_DT_CLUT224) && fb_logo_dt.valid) + return &fb_logo_dt.pos; + return fb_center_logo ? ¢red : &top_left; } =20 @@ -218,35 +277,45 @@ static void fb_rotate_logo_ccw(const u8 *in, u8 *out,= u32 width, u32 height) out[height * (w - j) + i] =3D *in++; } =20 -static void fb_rotate_logo(struct fb_info *info, u8 *dst, - struct fb_image *image, int rotate) +/* Turn the image itself, leaving @image->dx and @image->dy alone */ +static void fb_rotate_logo_image(u8 *dst, struct fb_image *image, int rota= te) { - u32 tmp; - if (rotate =3D=3D FB_ROTATE_UD) { fb_rotate_logo_ud(image->data, dst, image->width, image->height); - image->dx =3D info->var.xres - image->width - image->dx; - image->dy =3D info->var.yres - image->height - image->dy; } else if (rotate =3D=3D FB_ROTATE_CW) { fb_rotate_logo_cw(image->data, dst, image->width, image->height); swap(image->width, image->height); - tmp =3D image->dy; - image->dy =3D image->dx; - image->dx =3D info->var.xres - image->width - tmp; } else if (rotate =3D=3D FB_ROTATE_CCW) { fb_rotate_logo_ccw(image->data, dst, image->width, image->height); swap(image->width, image->height); - tmp =3D image->dx; - image->dx =3D image->dy; - image->dy =3D info->var.yres - image->height - tmp; } =20 image->data =3D dst; } =20 +/* As above, and map the placement from the console's frame to the screen = */ +static void fb_rotate_logo(struct fb_info *info, u8 *dst, + struct fb_image *image, int rotate) +{ + u32 tmp =3D rotate =3D=3D FB_ROTATE_CW ? image->dy : image->dx; + + fb_rotate_logo_image(dst, image, rotate); + + if (rotate =3D=3D FB_ROTATE_UD) { + image->dx =3D info->var.xres - image->width - image->dx; + image->dy =3D info->var.yres - image->height - image->dy; + } else if (rotate =3D=3D FB_ROTATE_CW) { + image->dy =3D image->dx; + image->dx =3D info->var.xres - image->width - tmp; + } else if (rotate =3D=3D FB_ROTATE_CCW) { + image->dx =3D image->dy; + image->dy =3D info->var.yres - image->height - tmp; + } +} + static void fb_do_show_logo(struct fb_info *info, struct fb_image *image, int rotate, unsigned int num) { @@ -295,6 +364,8 @@ static int fb_show_logo_line(struct fb_info *info, int = rotate, const struct logo_placement *p; unsigned int xres =3D info->var.xres; unsigned int yres =3D info->var.yres; + bool dt_rotated =3D fb_logo_dt_rotated(); + unsigned int fw, fh; struct fb_image image; unsigned int block; =20 @@ -340,28 +411,60 @@ static int fb_show_logo_line(struct fb_info *info, in= t rotate, image.width =3D logo->width; image.height =3D logo->height; =20 - if (rotate =3D=3D FB_ROTATE_CW || rotate =3D=3D FB_ROTATE_CCW) - swap(xres, yres); + /* How much of the screen the logo covers once it has been turned */ + fw =3D image.width; + fh =3D image.height; + if (rotate =3D=3D FB_ROTATE_CW || rotate =3D=3D FB_ROTATE_CCW) { + swap(fw, fh); + /* A turned console is looked at sideways, a turned logo is not */ + if (!dt_rotated) + swap(xres, yres); + } =20 - while (n && (n * (logo->width + 8) - 8 > xres)) - --n; + if (IS_ENABLED(CONFIG_LOGO_DT_CLUT224) && fb_logo_dt.valid) { + /* + * The device tree asks for the logo at one place, so draw it + * once. Repeating it per CPU would either overlap the copies + * or walk them out of the region reserved for the logo. + */ + n =3D 1; + } else { + while (n && (n * (logo->width + 8) - 8 > xres)) + --n; + } =20 /* The copies are drawn in a row, so they are centred as one block */ block =3D n ? n * (logo->width + 8) - 8 : logo->width; =20 p =3D fb_logo_placement(); - image.dx =3D logo_place_axis(p->x, xres, block); - /* A stacked logo goes where the caller put it */ - image.dy =3D y ? y : logo_place_axis(p->y, yres, image.height); + if (dt_rotated) { + /* + * The device tree names a place on the screen, so the logo is + * placed by the room it takes up there and drawn as it lies. + */ + image.dx =3D logo_place_axis(p->x, p->offset_x, xres, fw); + image.dy =3D logo_place_axis(p->y, p->offset_y, yres, fh); + } else { + image.dx =3D logo_place_axis(p->x, p->offset_x, xres, block); + /* A stacked logo goes where the caller put it */ + image.dy =3D y ? y : logo_place_axis(p->y, p->offset_y, yres, + image.height); + } =20 if (rotate) { logo_rotate =3D kmalloc_array(logo->width, logo->height, GFP_KERNEL); - if (logo_rotate) - fb_rotate_logo(info, logo_rotate, &image, rotate); + if (logo_rotate) { + if (dt_rotated) + fb_rotate_logo_image(logo_rotate, &image, + rotate); + else + fb_rotate_logo(info, logo_rotate, &image, + rotate); + } } =20 - fb_do_show_logo(info, &image, rotate, n); + fb_do_show_logo(info, &image, dt_rotated ? FB_ROTATE_UR : rotate, n); =20 kfree(palette); if (saved_pseudo_palette !=3D NULL) @@ -414,10 +517,23 @@ static int fb_prepare_extra_logos(struct fb_info *inf= o, unsigned int height, return height; } =20 +/* + * A logo supplied by the device tree is placed where the device tree asks, + * and the extra logos stack up from wherever the previous one ended. Those + * two do not compose: the stack would start at an arbitrary point and walk + * off the screen. The device tree wins, and says so if anything is droppe= d. + */ static int fb_show_extra_logos(struct fb_info *info, int y, int rotate) { unsigned int i; =20 + if (IS_ENABLED(CONFIG_LOGO_DT_CLUT224) && fb_logo_dt.valid) { + if (fb_logo_ex_num) + pr_info("fb: device tree logo in use, %u extra logo(s) not drawn\n", + fb_logo_ex_num); + return y; + } + for (i =3D 0; i < fb_logo_ex_num; i++) y =3D fb_show_logo_line(info, rotate, fb_logo_ex[i].logo, y, fb_logo_ex[i].n); @@ -430,11 +546,14 @@ int fb_prepare_logo(struct fb_info *info, int rotate) { int depth =3D fb_get_color_depth(&info->var, &info->fix); const struct logo_placement *p; - unsigned int yres; + unsigned int yres, fh; int height; =20 memset(&fb_logo, 0, sizeof(struct logo_data)); =20 + fb_logo_dt_read(); + rotate =3D fb_logo_dt_rotation(rotate); + if (info->flags & FBINFO_MISC_TILEBLITTING || info->fbops->owner || !fb_logo_count) return 0; @@ -458,12 +577,23 @@ int fb_prepare_logo(struct fb_info *info, int rotate) if (!fb_logo.logo) return 0; =20 - if (rotate =3D=3D FB_ROTATE_UR || rotate =3D=3D FB_ROTATE_UD) + /* + * A turned console is looked at sideways, so the logo is measured + * against the console's own frame. A logo turned by the device tree + * leaves the screen alone, and only covers a different part of it. + */ + fh =3D fb_logo.logo->height; + if (fb_logo_dt_rotated()) { yres =3D info->var.yres; - else + if (rotate =3D=3D FB_ROTATE_CW || rotate =3D=3D FB_ROTATE_CCW) + fh =3D fb_logo.logo->width; + } else if (rotate =3D=3D FB_ROTATE_UR || rotate =3D=3D FB_ROTATE_UD) { + yres =3D info->var.yres; + } else { yres =3D info->var.xres; + } =20 - if (fb_logo.logo->height > yres) { + if (fh > yres) { fb_logo.logo =3D NULL; return 0; } @@ -499,8 +629,7 @@ int fb_prepare_logo(struct fb_info *info, int rotate) * that the two cannot disagree. */ p =3D fb_logo_placement(); - height =3D logo_place_axis(p->y, yres, fb_logo.logo->height) + - fb_logo.logo->height; + height =3D logo_place_axis(p->y, p->offset_y, yres, fh) + fh; #ifdef CONFIG_FB_LOGO_EXTRA height =3D fb_prepare_extra_logos(info, height, yres); #endif @@ -516,6 +645,8 @@ int fb_show_logo(struct fb_info *info, int rotate) if (!fb_logo_count) return 0; =20 + rotate =3D fb_logo_dt_rotation(rotate); + count =3D fb_logo_count < 0 ? num_online_cpus() : fb_logo_count; y =3D fb_show_logo_line(info, rotate, fb_logo.logo, 0, count); #ifdef CONFIG_FB_LOGO_EXTRA diff --git a/drivers/video/logo/logo.c b/drivers/video/logo/logo.c index 84afd5b337..b600fc3aab 100644 --- a/drivers/video/logo/logo.c +++ b/drivers/video/logo/logo.c @@ -158,6 +158,84 @@ static const struct linux_logo *logo_dt_find(void) return logo_dt_data ? &logo_dt_clut224 : NULL; } =20 +static int logo_dt_parse_rotation(const char *rotation) +{ + if (!strcmp(rotation, "none")) + return LOGO_ROTATE_NONE; + if (!strcmp(rotation, "cw")) + return LOGO_ROTATE_CW; + if (!strcmp(rotation, "ud")) + return LOGO_ROTATE_UD; + if (!strcmp(rotation, "ccw")) + return LOGO_ROTATE_CCW; + + return -EINVAL; +} + +/** + * logo_dt_placement - where a logo supplied by the device tree asks to go + * @pos: filled in with the requested position and offset + * @rotation: filled in with one of LOGO_ROTATE_*, or -1 when the node does + * not ask for any + * + * Only the placement is read here, the image itself is fb_find_logo()'s + * business. It lives next to the image parser rather than in any one user= so + * that everything that draws the logo reads the same properties the same = way. + * + * Return: 0 if the device tree supplies a logo node, -ENODEV otherwise, in + * which case @pos and @rotation are left untouched. + */ +int logo_dt_placement(struct logo_placement *pos, int *rotation) +{ + struct device_node *np; + const char *str; + u32 val[2]; + int rot; + + if (!IS_ENABLED(CONFIG_LOGO_DT_CLUT224)) + return -ENODEV; + + /* + * Same node the image itself comes from, looked up the same way: a + * child of /chosen, not just anything compatible anywhere in the tree. + */ + np =3D of_get_compatible_child(of_chosen, LOGO_DT_COMPATIBLE); + if (!np) + return -ENODEV; + + if (!of_device_is_available(np)) { + of_node_put(np); + return -ENODEV; + } + + *pos =3D (struct logo_placement){ }; + *rotation =3D -1; + + if (!of_property_read_u32_array(np, "logo-position", val, 2)) { + pos->x =3D (s32)val[0]; + pos->y =3D (s32)val[1]; + } + + if (!of_property_read_u32_array(np, "logo-offset", val, 2)) { + pos->offset_x =3D (s32)val[0]; + pos->offset_y =3D (s32)val[1]; + } + + if (!of_property_read_string(np, "logo-rotation", &str)) { + rot =3D logo_dt_parse_rotation(str); + if (rot < 0) + pr_warn("logo: %pOF: unknown logo-rotation \"%s\"\n", + np, str); + else + *rotation =3D rot; + } + + of_node_put(np); + + return 0; +} +EXPORT_SYMBOL_GPL(logo_dt_placement); + /* * 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. diff --git a/include/linux/linux_logo.h b/include/linux/linux_logo.h index 1e7e9db6dd..a8b7bde8d4 100644 --- a/include/linux/linux_logo.h +++ b/include/linux/linux_logo.h @@ -12,6 +12,7 @@ * Copyright (C) 2003 Geert Uytterhoeven */ =20 +#include #include #include #include @@ -43,29 +44,55 @@ extern const struct linux_logo logo_spe_clut224; extern const struct linux_logo *fb_find_logo(int depth); =20 /* - * Where a boot logo goes. A coordinate of -1 centres the logo on that axi= s. - * Whatever draws the logo describes its placement this way and computes it - * with logo_place_axis(), so that no two places can end up disagreeing ab= out - * where the logo is. + * Where a boot logo goes. A coordinate of -1 centres the logo on that axi= s, + * and the offset is added afterwards. Whatever draws the logo describes i= ts + * placement this way and computes it with logo_place_axis(), so that no t= wo + * places can end up disagreeing about where the logo is. */ struct logo_placement { s32 x, y; + s32 offset_x, offset_y; }; =20 +/* Rotation a device tree supplied logo can ask for, same values as FB_ROT= ATE_* */ +#define LOGO_ROTATE_NONE 0 +#define LOGO_ROTATE_CW 1 +#define LOGO_ROTATE_UD 2 +#define LOGO_ROTATE_CCW 3 + +#ifdef CONFIG_LOGO +int logo_dt_placement(struct logo_placement *pos, int *rotation); +#else +static inline int logo_dt_placement(struct logo_placement *pos, int *rotat= ion) +{ + return -ENODEV; +} +#endif + /* * Place a logo along one axis. @pos is the coordinate asked for, or -1 to - * centre on that axis. The result is clamped so that the logo always lies - * entirely within the screen: the drawing code does not clip, so asking f= or - * more than that would otherwise scribble past the end of the frame buffe= r. + * centre on that axis, and @off is applied afterwards. The result is clam= ped + * so that the logo always lies entirely within the screen: the drawing co= de + * does not clip, so a device tree asking for more than that would otherwi= se + * scribble past the end of the frame buffer. */ -static inline int logo_place_axis(s32 pos, unsigned int span, unsigned int= size) +static inline int logo_place_axis(s32 pos, s32 off, unsigned int span, + unsigned int size) { int last =3D (int)span - (int)size; + s64 coord; =20 if (size > span) return 0; =20 - return pos =3D=3D -1 ? last / 2 : clamp(pos, 0, last); + coord =3D pos =3D=3D -1 ? last / 2 : pos; + + /* + * Both come straight from the device tree, so the sum is done wide + * enough that a silly pair of values lands the logo against an edge + * instead of wrapping round to the other one. + */ + return clamp_t(s64, coord + off, 0, last); } =20 #ifdef CONFIG_FB_LOGO_EXTRA --=20 2.39.5 From nobody Fri Oct 2 05:28:19 2026 Received: from mail-wm2-f13.google.com (mail-wm2-f13.google.com [74.125.225.141]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BE23351DAED for ; Wed, 23 Sep 2026 20:11:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.225.141 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194271; cv=none; b=I4XEmgwQfzvio8BULvI1Evx9IAtXeQH2u/K6RGLFrCDM6x7HFA5I6vqAMcIYm5VR15X2cDQ+9eXWnj1KVEJL3C7mxDYpoT0gKvCGEUAs5T2PvYCQQwN1RUHXeWerpJeRMpzuWN0zUeX+cgur5cDsxLH5TCKXP3Hl8f48d3lDPtg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194271; c=relaxed/simple; bh=0bOxnq+Qjeh+sxAlHlASgbdrTP5Xke/gEBsr1dGPWwc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=E+9PnDTCY++4/5/Li9OMrfcan2r5hiScCadTHCmliOiuRZzLhf9641gy+Y6myRddMkbC5eJz/Eozve55Z/1McDeP9TuCC1+JVO6HzxTZnXnxtcvFjOHzGj6mcqxzfH1cw8eUPux/3ZgHPTtsCh4n51oz2f+0WnXjWNvgqHC5hBs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=cxa86pxk; arc=none smtp.client-ip=74.125.225.141 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="cxa86pxk" Received: by mail-wm2-f13.google.com with SMTP id 5b1f17b1804b1-49b912e4ad9so7773005e9.2 for ; Wed, 23 Sep 2026 13:11:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790194259; x=1790799059; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=OHFYvczNQQ8zbB57FCazhQfFZ5fguvDLVHqTQCN5Cj0=; b=cxa86pxks2OvJmhjl2gKHFKGCbvSm269fshF+eV+GcZMI7KpHBFDJqDjXJpFxqxXGa AtowXnFpYspunZOSHqIrDcTnVkncgQsyXm25ODPh3MFB548CuLtZOxxijFcZFwy778/T 7xdDnWLp0eaITiv6wF/REXpQYtmKjcXdybIOBhbTo3a7vmon/LGMB6LT2bzbFVgi8YDk vi5FidlCoXEKwW4saaViCVIMpeafWRVrWDgdQj1WxhcE1lezbF355e46YbtLHHEUYqTn vo0fzCYx/RD5kFZ6C8FvZ12iDNFibFBU2zfDgovkD9pUBOm/zu9+LKvI6WmDiFLfzaYv DyZg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790194259; x=1790799059; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=OHFYvczNQQ8zbB57FCazhQfFZ5fguvDLVHqTQCN5Cj0=; b=pEHSpnAKeLxoGre9yRXlLqwhXJvqCsBK4IEpECHg7nHTDEFUIZi/DUO8aGRtM9F9O0 vDzztupeajjEdNNxbfZJR8BoA+WGtZ+dkqf09Z6+oyw1vHIhaOYTKf5XArr7tR7tPcUF /d9qbjCa0heUvZxPsnPWwC0pkcOr2sqG5KIfzGKmnJs+o2v5pI1MIZUaMALYFLdeRLQw 35fKgcHBKmGCpN3ek5rot6p2fcelymIjTWKwd7Mxrnye99LsiuULupFiDpn1MPzPwVY3 5EXZXaZqLoheyp8V4BtGDkyEyZggWiQh81IyuvTDNSCpSOyCgbgpcqTVFGIUpshSgmJr g3GQ== X-Forwarded-Encrypted: i=1; AKwUvByWPhLW2f4NTUEIGz2eWO9PDyErlKvRBxbx9h+ocn+3EIhxutepn/HtQzBlXBiS7e9tT7yGp6eX2Ndhu3E=@vger.kernel.org X-Gm-Message-State: AFuF++lHvzQ618Upgud9BCPfv4v2P++1Rc1p1VOqBiFIFasfMmiaXPMj MmXcKbG0nnUlT1VNAmsyysqPqjMG/1Xqhzk3JNbuOY/Mb9usSx1IKRli X-Gm-Gg: AYBFou1FYpyq6xxChtgXx7I7OO+/aBpmP1aWrkQc1VoRVBX6Y6jugp59+yuNkpqVzMt Riqq4LlK0YnGzbglC4AuNBqT3McEiur5wdSc/igvxREM9+dH6qosGTR28bo87j3vM0w11Ln1PVP p6d4QNU7OVpkoRIH+Onely1pvBbqgELZxIpIxHz7inVGCzwGJoOQMb6djEVChcEuaUUkeXyUo7V Xprkbt/XP70fiRs7tbNOwio9FxokjzO7rNFHB6uAgSs40a3REoJYdCWiaNQAUExET5CiY/C9NoO majGytj+QjuZYmSilGWWzY6UlZ47cdHnqmwzmkKfDs8MZk7yCDilnT7NRv9vBeG53MZTLUxmEcz dXrSTjW7FI/2VCBVFotmobJY2wKAowCAVdOMJClLpVc4DhjuXyeMFNy6I2PifMaXNoUJOkR2H2s CTfAnZQggxB3vH4bhIVndZot3/q0VRVaHa8J2SQivSGdlOmAOZqYZMU7yjGmtKyd/SN5tX3WLkb PRaDovbWyts5TJMg4jFMC04 X-Received: by 2002:a05:600c:c16d:b0:49f:cac3:c68a with SMTP id 5b1f17b1804b1-49fe66f9ff1mr4161775e9.31.1790194258869; Wed, 23 Sep 2026 13:10:58 -0700 (PDT) Received: from ingenieria31.oficinasStQ.local ([79.112.15.218]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49fe5cd4c8dsm13559295e9.3.2026.09.23.13.10.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Sep 2026 13:10:58 -0700 (PDT) From: Max Pedraza To: Helge Deller , Thomas Zimmermann , Simona Vetter , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Maxime Ripard Cc: linux-fbdev@vger.kernel.org, devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Max Pedraza Subject: [PATCH v3 5/7] dt-bindings: display: allow the boot logo in a reserved memory region Date: Wed, 23 Sep 2026 22:10:33 +0200 Message-Id: <20260923201035.51007-6-maximpedraza@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260923201035.51007-1-maximpedraza@gmail.com> References: <20260923201035.51007-1-maximpedraza@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Carrying the image in the device tree ties it to the device tree, but the image and where it goes on screen are independent axes of variation. One board sold to several customers wants several device trees that differ in the logo; one customer with several products built on that board wants the same logo placed differently on each panel. The second case would otherwise mean duplicating the same image into every device tree. Let the node point at a reserved memory region filled in by the bootloader instead, so one image can be shared by device trees that differ only in placement. The region starts with a small header carrying a magic number and the geometry, so the kernel can tell a logo from an empty or stale region and bounds check everything against the reservation. A phandle to a declared region is used rather than a bare address: the reservation is what makes the memory safe to read and what gives the kernel a size to validate against. The two ways of supplying the image are mutually exclusive, which is what the oneOf introduced here says. Until now the image properties were simply required, since they were the only way to supply one. Signed-off-by: Max Pedraza --- .../bindings/display/boot-logo-clut224.yaml | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/display/boot-logo-clut224.ya= ml b/Documentation/devicetree/bindings/display/boot-logo-clut224.yaml index 9dc3471763..a71fc09a7c 100644 --- a/Documentation/devicetree/bindings/display/boot-logo-clut224.yaml +++ b/Documentation/devicetree/bindings/display/boot-logo-clut224.yaml @@ -62,12 +62,31 @@ properties: index into the colour lookup table. The property length must be equa= l to width multiplied by height. =20 + memory-region: + maxItems: 1 + description: | + Reserved memory region holding the logo, as an alternative to carryi= ng + it in the width, height, clut and data properties. The bootloader is + expected to have placed the image there before starting the kernel. + + This lets one image be shared by several device trees that differ on= ly + in where the logo goes, which is what a family of products built on = the + same board but with different panels needs. + + The region starts with a header of four little endian 32 bit words: + the magic number 0x4f474f4c ("LOGO"), the width, the height and the + number of palette entries. The palette follows, as consecutive red, + green and blue bytes per entry, and then one byte per pixel, each an + index into that palette. + logo-position: $ref: /schemas/types.yaml#/definitions/int32-array description: - X and Y coordinates, in pixels, of the top left corner of the logo. - A value of -1 on an axis centres the logo on that axis instead. - Defaults to the top left corner of the screen. + X and Y coordinates, in screen pixels, of the top left corner of the + logo once it has been rotated. A value of -1 on an axis centres the + logo on that axis instead, which is the only way to say it when the + logo size is not known to the device tree, as is the case for the + memory-region form. Defaults to the top left corner of the screen. items: - description: X coordinate, or -1 to centre horizontally minimum: -1 @@ -103,10 +122,17 @@ properties: =20 required: - compatible - - width - - height - - clut - - data + +# The image either lives in the device tree or in a reserved memory region, +# never both. +oneOf: + - required: + - width + - height + - clut + - data + - required: + - memory-region =20 additionalProperties: false =20 --=20 2.39.5 From nobody Fri Oct 2 05:28:19 2026 Received: from mail-wm2-f13.google.com (mail-wm2-f13.google.com [74.125.225.141]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6D1EF57D236 for ; Wed, 23 Sep 2026 20:11:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.225.141 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194269; cv=none; b=b3Mp7kFZ4agB5OpRrZPFQkr8Wkf1fpXnqdT5sNqCr2XuS6jau8E+liG7B0DMHMvPSDo8OYD/1KrreTTSuqPSeZvWWaJzm2XJ3uilB9OU2dVnCldxEYvIi6c/GnBTm5n25bxM2GMvgOuiQP6XbbgQbLZAUM5vxty+wnhHBvVWv1g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194269; c=relaxed/simple; bh=mFQyvIj6reChEL67BBSeB3Wkx8Rn724plptNKQo7mR4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=c+Ri38ysoRKHRiVOrkNJuBr+5D+N3yC71yHUBpfwPI8+MfS0gNPxhmXDxIZdK3uhC0lzeaf8lLP8LEwFar/8Pnta/D7evcLKYFN8UhhH/s2ME3HPa/Bywl585Hjujj3Hd9aWyZxkRYlCKgogYDM4QCXGtvJDKZp1BAcNqwZWwzk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=kegrsdY2; arc=none smtp.client-ip=74.125.225.141 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="kegrsdY2" Received: by mail-wm2-f13.google.com with SMTP id 5b1f17b1804b1-49b912d8239so10040595e9.0 for ; Wed, 23 Sep 2026 13:11:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790194260; x=1790799060; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=i2OOLJKEASfUrtwVJ4InIXktjShhe/Ob36b0n3ix6zY=; b=kegrsdY29H3k5aBzxWO5TR8bqhrk0Ehn5vBhXEH3OFigqwq4fKZuPpBC+J5nrVrjXd WhOBriRfHAVO12QYu00mqbIyFT16BJzBOEF0zd9fEiyKcjmoBpVZJOMWCsYFE/r+RDoD 7NLIKXRnBNN/Btp3CDSYDh3zrbMJaihfD0OXXwiFJZzLx8dNbnHMlYpsJ7ElBvw2Q7mC S7WLKLqHHawbb+DuyeVgaDGlc8vLU77ffI/T4VID1A9BrE4UDKv8FuG20lLyO2oUyJc/ Zbyuqy2zXgQBH28JEVGy/OjlA+W45LcTLjQMDSsD1F1sOEKMcC9+E3sA9LBD74w2Nahl ddMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790194260; x=1790799060; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=i2OOLJKEASfUrtwVJ4InIXktjShhe/Ob36b0n3ix6zY=; b=bs/ku1wORBQ7LFfe2JUxmpPyDfXTWXZP2yUsJnHgHEMQ8KVBqvR7atm5UycaQFm/zf ebFTJyw1y5PVAD3GBMMUhH5aVu/sXoSWEbNkmqOLPRc7OdKjpvLgkr4S3dM1BOIbSDW6 Y5oySRtWlAcbZhPFjXYAbsItxXQoKaJHEJzUEVJSBYf63UsvxQgpWfb45UKC0TVUxqDb ZzzhmDjWGpiSTfUZH8jjMtGbpfVK3g+M7jIAwHE/dZkyTaTwwwOJ6eVfUKbDKZ7Nm5c+ 6475rkTJ28Dq+EkZC/XO+M8dE6nPtgXAQqb9M4D4HP9IpVe1ncLD69b8pBnm2sAb/7p8 TTIw== X-Forwarded-Encrypted: i=1; AKwUvBwwCsNZSxQdeTn93+SfyzeKVFzUw3vHDleeyR8vzriK1XqEFG/Nl+ThI78vianxiPiHYcn5o5zWAliZnwI=@vger.kernel.org X-Gm-Message-State: AFuF++mQC/LGLNZ7AxmsLav3dgcm9vkeI+IJnNqFGLxjBglK2pM3dLEc J8HIdfze58o+mbuqto/8ihZO59m7lXj19Ev+NqjqyX7a2yxAfBPSlOmB X-Gm-Gg: AYBFou23fnCAmYlgdGMKnLvHJpvaZOipteitnJiGErf1Ei+g/ry/CS1sXXzkAeoiawH cI+M7Mf/M5fSVdJmmQVwyKLGB1S22cx3OsnyjhNybAvtp4UcEnp6+aEsvpIjhy8PQ61MXhm1B+E 82/Bp7j5EXJr5BgDUxLJFECg02hoa+Dkd2BlRPS0g90J9OxWdQYxIH29QQLPEU9GZobk7aFeAI5 UlTRscxR+p8QIdsDZDTTL9Hxe8QcZAMSULl+NeDMF2Pbvjdz7bYYlR0vx2LNjQuPZHjVFDbx3Oh Ns+QmKD6aK9j+TWEWtQqxA92Eo7PKG5sa52XTaTt+sDaheoAwEeGB3ksw046+MePtFEMTPAt9Xx rFlnNu/Wb1PfHdhZcEeyzNPv7bVmyDfDYAqI0sJHAVyGjJBKchEZ0yhCtgxeCpwsWzi+cTdX/ZR FjF/sMMWNCU0dg4FPM1JCS8P+4KGMDYOmbrS3xSY0odhx5TXXSbwnFSfimJTqp4bGw7VLl2UIJI DiHQM0guu6/0llot5sQSbrs0owvKG4X0UA= X-Received: by 2002:a05:600c:310e:b0:49e:8377:c880 with SMTP id 5b1f17b1804b1-49fe6716468mr4217415e9.33.1790194260255; Wed, 23 Sep 2026 13:11:00 -0700 (PDT) Received: from ingenieria31.oficinasStQ.local ([79.112.15.218]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49fe5cd4c8dsm13559295e9.3.2026.09.23.13.10.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Sep 2026 13:10:59 -0700 (PDT) From: Max Pedraza To: Helge Deller , Thomas Zimmermann , Simona Vetter , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Maxime Ripard Cc: linux-fbdev@vger.kernel.org, devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Max Pedraza Subject: [PATCH v3 6/7] video: logo: allow the boot logo to come from a reserved memory region Date: Wed, 23 Sep 2026 22:10:34 +0200 Message-Id: <20260923201035.51007-7-maximpedraza@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260923201035.51007-1-maximpedraza@gmail.com> References: <20260923201035.51007-1-maximpedraza@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Take the image from the region a "memory-region" phandle points at, when the node has one, instead of from the width, height, clut and data properties. Only a declared region is accepted, never a bare physical address. That matters for more than tidiness: the region is reserved before the allocator starts, so the logo code can never be pointed at memory the kernel is using for something else, and a size is known, so every field can be bounds checked. The region is mapped with memremap(), which copes with a no-map reservation and fails cleanly instead of handing back a bogus pointer the way phys_to_virt() on an arbitrary address would. The image is validated and copied out rather than used in place: the magic number has to match, the geometry has to be sane, the palette and pixels have to fit inside the reserved region, and every pixel has to reference an existing palette entry. Anything else is reported and ignored, falling back to the built-in logo. Copying also means nothing that happens to the region afterwards can affect what has already been validated, and it keeps the pixel data in the same shape both paths produce. Signed-off-by: Max Pedraza --- drivers/video/logo/logo.c | 176 ++++++++++++++++++++++++++++++-------- 1 file changed, 138 insertions(+), 38 deletions(-) diff --git a/drivers/video/logo/logo.c b/drivers/video/logo/logo.c index b600fc3aab..6f0adcd34e 100644 --- a/drivers/video/logo/logo.c +++ b/drivers/video/logo/logo.c @@ -10,8 +10,10 @@ * Copyright (C) 2003 Geert Uytterhoeven */ =20 +#include #include #include +#include #include #include #include @@ -38,9 +40,19 @@ MODULE_PARM_DESC(nologo, "Disables startup logo"); /* * Sanity limit on the image size, a device tree is not a good place for m= ore: * a 4K screen is 8.3M pixels, and the copy is kept for the life of the ke= rnel. - * One byte per pixel, so this is a byte count as well. + * One byte per pixel, so this is a byte count as well. Keep in step with + * MAX_PIXELS in ppmtodtlogo. */ #define LOGO_DT_MAX_PIXELS SZ_16M +/* "LOGO", little endian, at the start of a handed over memory region */ +#define LOGO_DT_MAGIC 0x4f474f4c + +struct logo_dt_header { + __le32 magic; + __le32 width; + __le32 height; + __le32 clutsize; +}; =20 static struct linux_logo logo_dt_clut224 =3D { .type =3D LINUX_LOGO_CLUT224, @@ -49,59 +61,42 @@ static struct linux_logo logo_dt_clut224 =3D { static unsigned char *logo_dt_clut; static unsigned char *logo_dt_data; =20 -static int logo_dt_parse(struct device_node *np) +/* Reject geometries that cannot describe a sane image before using them */ +static int logo_dt_check_geometry(u32 width, u32 height, u32 clutsize) { - unsigned int clutsize, npixels, i; - unsigned char *clut, *data; - u32 width, height; - int len, ret; - - ret =3D of_property_read_u32(np, "width", &width); - if (ret) - return ret; - - ret =3D of_property_read_u32(np, "height", &height); - if (ret) - return ret; - if (!width || !height || (u64)width * height > LOGO_DT_MAX_PIXELS) return -EINVAL; =20 - npixels =3D width * height; - - len =3D of_property_count_u8_elems(np, "clut"); - if (len < 3 || len % 3) + if (!clutsize || clutsize > LOGO_DT_MAX_CLUT) return -EINVAL; =20 - clutsize =3D len / 3; - if (clutsize > LOGO_DT_MAX_CLUT) - return -EINVAL; + return 0; +} =20 - ret =3D of_property_count_u8_elems(np, "data"); - if (ret < 0) - return ret; - if ((unsigned int)ret !=3D npixels) - return -EINVAL; +/* + * Take a private copy of an image that has already been range checked, so + * that nothing else can change it under us, and shift the pixels into the + * palette slots the frame buffer layer leaves to the logo. + */ +static int logo_dt_store(u32 width, u32 height, u32 clutsize, + const u8 *clut_src, const u8 *data_src) +{ + unsigned int npixels =3D width * height; + unsigned char *clut, *data; + unsigned int i; + int ret; =20 - clut =3D kmalloc(len, GFP_KERNEL); + /* The palette is at most 672 bytes, the pixels can be megabytes */ + clut =3D kmemdup(clut_src, clutsize * 3, GFP_KERNEL); if (!clut) return -ENOMEM; =20 - /* The palette is at most 672 bytes, the pixels can be megabytes */ - data =3D kvmalloc(npixels, GFP_KERNEL); + data =3D kvmemdup(data_src, npixels, GFP_KERNEL); if (!data) { ret =3D -ENOMEM; goto err_free_clut; } =20 - ret =3D of_property_read_u8_array(np, "clut", clut, len); - if (ret) - goto err_free_data; - - ret =3D of_property_read_u8_array(np, "data", data, npixels); - if (ret) - goto err_free_data; - for (i =3D 0; i < npixels; i++) { if (data[i] >=3D clutsize) { ret =3D -ERANGE; @@ -128,6 +123,111 @@ static int logo_dt_parse(struct device_node *np) return ret; } =20 +static int logo_dt_parse_properties(struct device_node *np) +{ + const u8 *clut, *data; + u32 width, height; + int len, ret; + + ret =3D of_property_read_u32(np, "width", &width); + if (ret) + return ret; + + ret =3D of_property_read_u32(np, "height", &height); + if (ret) + return ret; + + len =3D of_property_count_u8_elems(np, "clut"); + if (len < 3 || len % 3) + return -EINVAL; + + ret =3D logo_dt_check_geometry(width, height, len / 3); + if (ret) + return ret; + + if (of_property_count_u8_elems(np, "data") !=3D width * height) + return -EINVAL; + + clut =3D of_get_property(np, "clut", NULL); + data =3D of_get_property(np, "data", NULL); + if (!clut || !data) + return -EINVAL; + + return logo_dt_store(width, height, len / 3, clut, data); +} + +/* + * Image handed over by the bootloader in a reserved memory region. Only a + * region the device tree declared is accepted, never a bare address, so t= he + * kernel can never be pointed at memory it is using for something else, a= nd + * so that a size is known and every access can be bounds checked. + */ +static int logo_dt_parse_memory_region(struct device_node *np) +{ + u32 width, height, clutsize; + const struct logo_dt_header *hdr; + struct device_node *mem_np; + struct reserved_mem *rmem; + size_t clutlen, datalen; + const u8 *payload; + void *mem; + int ret; + + mem_np =3D of_parse_phandle(np, "memory-region", 0); + if (!mem_np) + return -ENOENT; + + rmem =3D of_reserved_mem_lookup(mem_np); + of_node_put(mem_np); + if (!rmem) + return -EINVAL; + + if (rmem->size < sizeof(*hdr)) + return -EINVAL; + + mem =3D memremap(rmem->base, rmem->size, MEMREMAP_WB); + if (!mem) + return -ENOMEM; + + hdr =3D mem; + if (le32_to_cpu(hdr->magic) !=3D LOGO_DT_MAGIC) { + ret =3D -EINVAL; + goto out_unmap; + } + + width =3D le32_to_cpu(hdr->width); + height =3D le32_to_cpu(hdr->height); + clutsize =3D le32_to_cpu(hdr->clutsize); + + ret =3D logo_dt_check_geometry(width, height, clutsize); + if (ret) + goto out_unmap; + + clutlen =3D (size_t)clutsize * 3; + datalen =3D (size_t)width * height; + + /* Everything the header promises has to fit inside the region */ + if (sizeof(*hdr) + clutlen + datalen > rmem->size) { + ret =3D -EINVAL; + goto out_unmap; + } + + payload =3D (const u8 *)(hdr + 1); + ret =3D logo_dt_store(width, height, clutsize, payload, payload + clutlen= ); + +out_unmap: + memunmap(mem); + return ret; +} + +static int logo_dt_parse(struct device_node *np) +{ + if (of_property_present(np, "memory-region")) + return logo_dt_parse_memory_region(np); + + return logo_dt_parse_properties(np); +} + static const struct linux_logo *logo_dt_find(void) { static bool probed; --=20 2.39.5 From nobody Fri Oct 2 05:28:19 2026 Received: from mail-wm2-f12.google.com (mail-wm2-f12.google.com [74.125.225.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 700A557F750 for ; Wed, 23 Sep 2026 20:11:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.225.140 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194279; cv=none; b=IQoPnielxO0D4AzX4I5EAtTCoMveg3RJZLyjVTniOkUDxjWbEfkOeIYUS7syiULAJQK4PPU0w693im9wAombKTfz+B7WQGiyiaYPgc+Wb37GVM3ke/VnIGqjhSZUmEzK4c3LF+lxyul+UFzaZS+LppgCIRplKlejCYCQ3H5C+mc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790194279; c=relaxed/simple; bh=hcf2y0wvjX9aWK11lKMkQMcHjeBkgkPDVNiobkNIk3U=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=XqNk+aXgfSwyuKMpwbBLFjwsxXCw7cn3vhFVY9p0rx6cS0r4R/6sh6csqQ9Ho/+FvMHt2zTvHklpUGXgDmvATVM98nhFH7TkI64djEo09thXLwQvIHMbZGaECmw4Xm2+sxXgzU45GrWId9g6ISV7rkkA82l0WMESV/eDxG2zSlE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ex7OhGz7; arc=none smtp.client-ip=74.125.225.140 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ex7OhGz7" Received: by mail-wm2-f12.google.com with SMTP id 5b1f17b1804b1-49ce364488dso4429875e9.0 for ; Wed, 23 Sep 2026 13:11:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790194261; x=1790799061; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Tv2gyMGASSEUQ5phQtVihq/4jYMJzkV2HdzigXP4G4U=; b=ex7OhGz7TJFdtHawzTd2p25tvcp5IA3W4sdFiuc+sISadTn+fkSwRNeNNNU49TWT+a D1sU3tx3D73UbZPLJSvWRQUv493SIuED82H8+Zd5evoUVGiVbtaWR1S0+C2V31M8fcgf FBMmuDNoyBjHFG+r9o54l51Bwwe2BDwCEI1H16O1k8iye6AI0SbWTNiLNWTlFgiS6E72 4Kodz7rl6HuVseieMPJVmX3Fs/9+KfK5wYGS91lsb+m9DVB5vjvb7Khg90h797HARfNC D7TVp8zANUH5cz6XxGrK8Mz+j3/FBpOpJpRmSRxs7xoIMbuYeg29HJYVzcS3kx36fRYk bKUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790194261; x=1790799061; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=Tv2gyMGASSEUQ5phQtVihq/4jYMJzkV2HdzigXP4G4U=; b=MqzAFZsrsQHwAqP387g0TZuE7Lbl12gllRy9+vL4Cm9hCsIMMXWjFleuRRI05Cu+6u Z5lau8nfNJTrXtI+aqrnD9dPjqd/IXlHD15Tr/3zjovIA3n94ouf5q3xh2mK0YRdN5Lo +qr/eENSaiJjTvbk4i0k2WNk/KSVHzI1iayyq3IV0sJLon1uzLt2zaUcNqlJhoXBJILj LZNVwG3oM0LSW/uZIGtugk5oap03v0yCCdPRitj3onruud7uZJJGRDVJj7lFCCbiQJ7e DpmvVCbe89EdJERjLwAI0M1ULVRoJDaVw/7mYNhBsW6pboky9mjG4T1mXwtkAsvmXSfN 6b9A== X-Forwarded-Encrypted: i=1; AKwUvByG3LC8WdhowAdoN5e5C3QQdgtFpKgPPql1SmADr2uJzL5p10EVzU9itpJnrUdQfgqfxU1yL+ssTvYkTn4=@vger.kernel.org X-Gm-Message-State: AFuF++nU7/3y8BcNr5DYft61zTOcDTH+L5xJdsw5UTY7k5tg0ogCYlgf 4y3sF9mqcSVWov53G/YH0c/MT6Vely5LkQdXtKM2b5Z+tuF1vzmyv9Vk X-Gm-Gg: AYBFou3HPERSOaec6/ilEdKzpn1RSErx/ONTFmhIM/2uLU37TkNTXp27BxE3euz98eb K0zalIWaqyuANFFUyUX0PdOB+RLOc7jdl6mAkoJ8OWhRXGdr+rikb+n54ru4DpwnpytymQSAMPf PpsbOFP6ANATG05rvIms8AEXyDUmzXK8Yg1d1lk9JTGuazq+hfd9LngsaJXr8NflEQTBGg3yxhM JNEwPCOX8ouBXXjC3zHB46d7ynWwF897yEoyh2MWQshfTy0sMKCiXD3nkzLoYtRXxfWWZ2TG3c0 f5FFKQ3Z9k3ZIvo1YSH0NROCGd1jx9CZoaHMxVoz05VBZ2jPGEKSxlwZiOaGAIzUa5z4Q1FXpXF tzxiP5rhU/kXm+K3I2+2tpwn68sHwu8rVYv/EJhU6MD8Ywcf5jmNJ2PGYyIEoiJUG8x17UzoGZx dECNnWUhiTvUnKVcstr8w2RfFDQCj85BXB2ZskO5OJsJPXBb6u6Tb9wyiJZKuXzD7yZ+k2SoGqt 7buG4iba3jubGRYZaVjzGOWamphFYxdnis= X-Received: by 2002:a05:600c:4e53:b0:49c:e37e:4389 with SMTP id 5b1f17b1804b1-49fe66c8288mr4894915e9.4.1790194261482; Wed, 23 Sep 2026 13:11:01 -0700 (PDT) Received: from ingenieria31.oficinasStQ.local ([79.112.15.218]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49fe5cd4c8dsm13559295e9.3.2026.09.23.13.11.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Sep 2026 13:11:01 -0700 (PDT) From: Max Pedraza To: Helge Deller , Thomas Zimmermann , Simona Vetter , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Maxime Ripard Cc: linux-fbdev@vger.kernel.org, devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Max Pedraza Subject: [PATCH v3 7/7] video: logo: add ppmtodtlogo host tool Date: Wed, 23 Sep 2026 22:10:35 +0200 Message-Id: <20260923201035.51007-8-maximpedraza@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260923201035.51007-1-maximpedraza@gmail.com> References: <20260923201035.51007-1-maximpedraza@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The byte arrays a "boot-logo-clut224" node carries are not meant to be written by hand. Add a host tool that converts a PPM image into the node, along the lines of the existing pnmtologo: plain C, no dependencies, and no quantization of its own -- the image must already use at most 224 distinct colours, and the tool points at ImageMagick when it does not. Three output flavours: ppmtodtlogo logo.ppm a complete overlay ppmtodtlogo -t dtsi logo.ppm a bare node, for inclusion ppmtodtlogo -t bin -o logo.bin ... the blob for a reserved memory region, header included The devicetree outputs also carry the optional placement properties, commented out unless requested on the command line, so the generated file documents what can be tuned without regenerating the image. The tool is built when CONFIG_LOGO_DT_CLUT224 is enabled but is not used by the kernel build itself. Signed-off-by: Max Pedraza --- drivers/video/logo/Makefile | 6 +- drivers/video/logo/ppmtodtlogo.c | 416 +++++++++++++++++++++++++++++++ 2 files changed, 421 insertions(+), 1 deletion(-) create mode 100644 drivers/video/logo/ppmtodtlogo.c diff --git a/drivers/video/logo/Makefile b/drivers/video/logo/Makefile index 937b37d3b6..6dc205d24e 100644 --- a/drivers/video/logo/Makefile +++ b/drivers/video/logo/Makefile @@ -10,7 +10,11 @@ obj-$(CONFIG_SPU_BASE) +=3D logo_spe_clut224.o =20 # How to generate logo's =20 -hostprogs :=3D pnmtologo +hostprogs :=3D pnmtologo ppmtodtlogo + +# Not used by the build itself: converts a user's image into the devicetree +# node or memory blob the "boot-logo-clut224" binding consumes. +always-$(CONFIG_LOGO_DT_CLUT224) +=3D ppmtodtlogo =20 # Create commands like "pnmtologo -t mono -n logo_linux_mono -o ..." quiet_cmd_logo =3D LOGO $@ diff --git a/drivers/video/logo/ppmtodtlogo.c b/drivers/video/logo/ppmtodtl= ogo.c new file mode 100644 index 0000000000..7fded9ce9b --- /dev/null +++ b/drivers/video/logo/ppmtodtlogo.c @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Convert a PPM image into a devicetree boot logo node, or into the binary + * blob the "boot-logo-clut224" binding reads from a reserved memory + * region. + * + * Like pnmtologo, this tool does not quantize: the image must already use + * at most 224 distinct colours. Reduce it first if needed, for instance: + * + * magick logo.png -colors 224 logo.ppm + * + * The node is emitted under /chosen, where the binding expects it: a logo= is + * configuration handed over by firmware rather than a description of the + * hardware. + * + * The devicetree output stores plain palette indices; the 32 entry offset + * the frame buffer layer reserves for the console is applied by the kerne= l. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CLUT_COLORS 224 +/* Keep in step with LOGO_DT_MAX_PIXELS in drivers/video/logo/logo.c */ +#define MAX_PIXELS (16U * 1024 * 1024) +#define BLOB_MAGIC 0x4f474f4cU /* "LOGO", little endian */ +#define BYTES_PER_LINE 12 + +static const char *programname; +static const char *filename; +static const char *outputname; +static FILE *out; + +enum output_type { + OUTPUT_DTS, /* complete overlay */ + OUTPUT_DTSI, /* bare node, for inclusion */ + OUTPUT_BIN, /* blob for a reserved memory region */ +}; + +static enum output_type output_type =3D OUTPUT_DTS; + +/* Placement options, emitted into the node */ +static int opt_centered; +static char *opt_position; +static char *opt_offset; +static const char *opt_rotation; + +struct color { + unsigned char red; + unsigned char green; + unsigned char blue; +}; + +static unsigned int logo_width; +static unsigned int logo_height; +static unsigned char *logo_data; +static struct color logo_clut[MAX_CLUT_COLORS]; +static unsigned int logo_clutsize; + +static void die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + exit(1); +} + +static void usage(void) +{ + die("Usage: %s [options] \n" + "\n" + "Convert a PPM image into a \"boot-logo-clut224\" node.\n" + "The image must use at most %d distinct colours.\n" + "\n" + " -o write to file instead of stdout\n" + " -t dts (default), dtsi or bin\n" + " -c centre the logo (logo-position =3D <(-1) (-1)>)\= n" + " -p , logo-position, -1 on an axis centres it\n" + " -f , logo-offset\n" + " -r logo-rotation: cw, ccw, ud or none\n" + " -h this help\n", + programname, MAX_CLUT_COLORS); +} + +static unsigned int get_number(FILE *fp) +{ + int c; + unsigned int val; + + /* Skip leading whitespace */ + do { + c =3D fgetc(fp); + if (c =3D=3D EOF) + die("%s: end of file\n", filename); + if (c =3D=3D '#') { + /* Ignore comments 'till end of line */ + do { + c =3D fgetc(fp); + if (c =3D=3D EOF) + die("%s: end of file\n", filename); + } while (c !=3D '\n'); + } + } while (isspace(c)); + + if (!isdigit(c)) + die("%s: expected a number\n", filename); + + /* Parse decimal number */ + val =3D 0; + while (isdigit(c)) { + val =3D 10 * val + c - '0'; + c =3D fgetc(fp); + if (c =3D=3D EOF) + break; + } + return val; +} + +static unsigned char get_byte(FILE *fp) +{ + int c =3D fgetc(fp); + + if (c =3D=3D EOF) + die("%s: end of file\n", filename); + return c; +} + +static unsigned int find_clut_entry(struct color color) +{ + unsigned int i; + + for (i =3D 0; i < logo_clutsize; i++) + if (logo_clut[i].red =3D=3D color.red && + logo_clut[i].green =3D=3D color.green && + logo_clut[i].blue =3D=3D color.blue) + return i; + + if (logo_clutsize =3D=3D MAX_CLUT_COLORS) + die("%s: more than %d colors, reduce the image first, e.g.\n" + " magick %s -colors %d out.ppm\n", + filename, MAX_CLUT_COLORS, filename, MAX_CLUT_COLORS); + + logo_clut[logo_clutsize] =3D color; + return logo_clutsize++; +} + +static void read_image(void) +{ + unsigned int i, npixels, maxval; + int magic, raw; + FILE *fp; + + fp =3D fopen(filename, "rb"); + if (!fp) + die("Cannot open file %s: %s\n", filename, strerror(errno)); + + if (fgetc(fp) !=3D 'P') + die("%s is not a PPM file\n", filename); + + magic =3D fgetc(fp); + switch (magic) { + case '3': + raw =3D 0; + break; + case '6': + raw =3D 1; + break; + default: + die("%s is not a PPM file (only P3 and P6 are supported)\n", + filename); + } + + logo_width =3D get_number(fp); + logo_height =3D get_number(fp); + maxval =3D get_number(fp); + if (maxval !=3D 255) + die("%s: maximum color value must be 255\n", filename); + + if (!logo_width || !logo_height) + die("%s: zero sized image\n", filename); + if ((unsigned long long)logo_width * logo_height > MAX_PIXELS) + die("%s: image too large\n", filename); + + npixels =3D logo_width * logo_height; + logo_data =3D malloc(npixels); + if (!logo_data) + die("%s\n", strerror(errno)); + + for (i =3D 0; i < npixels; i++) { + struct color color; + + if (raw) { + color.red =3D get_byte(fp); + color.green =3D get_byte(fp); + color.blue =3D get_byte(fp); + } else { + color.red =3D get_number(fp); + color.green =3D get_number(fp); + color.blue =3D get_number(fp); + } + logo_data[i] =3D find_clut_entry(color); + } + + fclose(fp); +} + +static void write_bytes(const unsigned char *data, unsigned int len, + const char *indent) +{ + unsigned int i; + + for (i =3D 0; i < len; i++) { + if (i % BYTES_PER_LINE =3D=3D 0) + fprintf(out, "%s%s", i ? "\n" : "", indent); + else + fputc(' ', out); + fprintf(out, "0x%02x", data[i]); + } +} + +static void write_placement(const char *indent) +{ + const char *pos =3D "0 0"; + + fprintf(out, "%s/* logo-position gives the top left corner; -1 on an axis= \n", + indent); + fprintf(out, "%s * centres it there. logo-offset is added afterwards. */\= n", + indent); + + /* -c is a shorthand for centring on both axes; -p wins over it */ + if (opt_position) + pos =3D opt_position; + else if (opt_centered) + pos =3D "(-1) (-1)"; + + fprintf(out, "%s%slogo-position =3D <%s>;\n", indent, + opt_position || opt_centered ? "" : "// ", pos); + fprintf(out, "%s%slogo-offset =3D <%s>;\n", indent, + opt_offset ? "" : "// ", opt_offset ? opt_offset : "0 0"); + fprintf(out, "%s%slogo-rotation =3D \"%s\";\t/* cw, ccw, ud, none */\n", + indent, opt_rotation ? "" : "// ", + opt_rotation ? opt_rotation : "ccw"); +} + +static void write_node(const char *indent) +{ + char subindent[16]; + + snprintf(subindent, sizeof(subindent), "%s\t\t", indent); + + fprintf(out, "%scompatible =3D \"boot-logo-clut224\";\n", indent); + fprintf(out, "\n"); + write_placement(indent); + fprintf(out, "\n"); + fprintf(out, "%swidth =3D <%u>;\n", indent, logo_width); + fprintf(out, "%sheight =3D <%u>;\n", indent, logo_height); + fprintf(out, "\n"); + fprintf(out, "%sclut =3D /bits/ 8 <", indent); + write_bytes((const unsigned char *)logo_clut, logo_clutsize * 3, + subindent); + fprintf(out, ">;\n"); + fprintf(out, "\n"); + fprintf(out, "%sdata =3D /bits/ 8 <", indent); + write_bytes(logo_data, logo_width * logo_height, subindent); + fprintf(out, ">;\n"); +} + +static void write_header_comment(void) +{ + fprintf(out, "/*\n"); + fprintf(out, " * Boot logo generated by ppmtodtlogo from %s\n", + filename); + fprintf(out, " * %ux%u pixels, %u colours.\n", logo_width, logo_height, + logo_clutsize); + fprintf(out, " */\n"); +} + +static void write_dts(void) +{ + fprintf(out, "/dts-v1/;\n/plugin/;\n\n"); + write_header_comment(); + fprintf(out, "\n"); + fprintf(out, "/ {\n"); + fprintf(out, "\tfragment@101 {\n"); + fprintf(out, "\t\ttarget-path =3D \"/chosen\";\n"); + fprintf(out, "\n"); + fprintf(out, "\t\t__overlay__ {\n"); + fprintf(out, "\t\t\tlogo {\n"); + write_node("\t\t\t\t"); + fprintf(out, "\t\t\t};\n"); + fprintf(out, "\t\t};\n"); + fprintf(out, "\t};\n"); + fprintf(out, "};\n"); +} + +static void write_dtsi(void) +{ + write_header_comment(); + fprintf(out, "\n"); + fprintf(out, "chosen {\n"); + fprintf(out, "\tlogo {\n"); + write_node("\t\t"); + fprintf(out, "\t};\n"); + fprintf(out, "};\n"); +} + +static void put_le32(unsigned int val) +{ + fputc(val & 0xff, out); + fputc((val >> 8) & 0xff, out); + fputc((val >> 16) & 0xff, out); + fputc((val >> 24) & 0xff, out); +} + +static void write_bin(void) +{ + put_le32(BLOB_MAGIC); + put_le32(logo_width); + put_le32(logo_height); + put_le32(logo_clutsize); + fwrite(logo_clut, 3, logo_clutsize, out); + fwrite(logo_data, 1, logo_width * logo_height, out); +} + +int main(int argc, char *argv[]) +{ + int opt; + char *p; + + programname =3D argv[0]; + + while ((opt =3D getopt(argc, argv, "o:t:cp:f:r:h")) !=3D -1) { + switch (opt) { + case 'o': + outputname =3D optarg; + break; + case 't': + if (!strcmp(optarg, "dts")) + output_type =3D OUTPUT_DTS; + else if (!strcmp(optarg, "dtsi")) + output_type =3D OUTPUT_DTSI; + else if (!strcmp(optarg, "bin")) + output_type =3D OUTPUT_BIN; + else + usage(); + break; + case 'c': + opt_centered =3D 1; + break; + case 'p': + opt_position =3D optarg; + break; + case 'f': + opt_offset =3D optarg; + break; + case 'r': + if (strcmp(optarg, "cw") && strcmp(optarg, "ccw") && + strcmp(optarg, "ud") && strcmp(optarg, "none")) + usage(); + opt_rotation =3D optarg; + break; + default: + usage(); + } + } + if (optind !=3D argc - 1) + usage(); + filename =3D argv[optind]; + + /* "10,20" and "10 20" are both accepted for -p and -f */ + for (p =3D opt_position; p && *p; p++) + if (*p =3D=3D ',') + *p =3D ' '; + for (p =3D opt_offset; p && *p; p++) + if (*p =3D=3D ',') + *p =3D ' '; + + read_image(); + + if (outputname) { + out =3D fopen(outputname, + output_type =3D=3D OUTPUT_BIN ? "wb" : "w"); + if (!out) + die("Cannot create file %s: %s\n", outputname, + strerror(errno)); + } else { + out =3D stdout; + } + + switch (output_type) { + case OUTPUT_DTS: + write_dts(); + break; + case OUTPUT_DTSI: + write_dtsi(); + break; + case OUTPUT_BIN: + write_bin(); + break; + } + + if (outputname) + fclose(out); + + fprintf(stderr, "%s: %ux%u pixels, %u colours\n", filename, logo_width, + logo_height, logo_clutsize); + + return 0; +} --=20 2.39.5