public inbox for linux-arm-kernel@lists.infradead.org
 help / color / mirror / Atom feed
From: Thomas Zimmermann <tzimmermann@suse.de>
To: javierm@redhat.com, arnd@arndb.de, ardb@kernel.org,
	ilias.apalodimas@linaro.org, chenhuacai@kernel.org,
	kernel@xen0n.name, maarten.lankhorst@linux.intel.com,
	mripard@kernel.org, airlied@gmail.com, simona@ffwll.ch,
	kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
	decui@microsoft.com, longli@microsoft.com, deller@gmx.de
Cc: linux-arm-kernel@lists.infradead.org, loongarch@lists.linux.dev,
	linux-efi@vger.kernel.org, linux-riscv@lists.infradead.org,
	dri-devel@lists.freedesktop.org, linux-hyperv@vger.kernel.org,
	linux-fbdev@vger.kernel.org,
	Thomas Zimmermann <tzimmermann@suse.de>
Subject: [PATCH 5/8] firmware: sysfb: Implement screen_info relocation for primary display
Date: Thu,  2 Apr 2026 11:09:19 +0200	[thread overview]
Message-ID: <20260402092305.208728-6-tzimmermann@suse.de> (raw)
In-Reply-To: <20260402092305.208728-1-tzimmermann@suse.de>

Move the relocation tracking for screen_info from the screen_info
helpers to sysfb. The relocation code operates on sysfb_primary_display,
which belongs to sysfb. The remaining screen_info helpers are now free
from global state.

Adapt some symbol names. Now prefer early returns in the helper
sysfb_apply_screen_info_fixup() over nested branching. Also return an
errno code from sysfb_apply_screen_info_fixup() if the relocation
failed. In this case, do not create a device for the framebuffer.
The original code advertised this behavior in a comment but never
implemented it.

Framebuffer aperture relocation can happen during boot if the PCI
graphics device is located behind a PCI bridge. If the bridge's sub-
bus gets relocated, the framebuffer aperture moves accordingly. The
helper for tracking these relocations fixes up the values stored in
sysfb_primary_display so that they refer to the correct address range
again. Generic system-framebuffer drivers would not work otherwise.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/firmware/sysfb.h         |   5 ++
 drivers/firmware/sysfb_pci.c     | 111 +++++++++++++++++++++++++++++++
 drivers/firmware/sysfb_primary.c |   9 ++-
 drivers/video/screen_info_pci.c  | 110 ------------------------------
 include/linux/screen_info.h      |   3 -
 5 files changed, 123 insertions(+), 115 deletions(-)

diff --git a/drivers/firmware/sysfb.h b/drivers/firmware/sysfb.h
index 9f7fe2e03f68..1eaa3b0fa364 100644
--- a/drivers/firmware/sysfb.h
+++ b/drivers/firmware/sysfb.h
@@ -8,8 +8,13 @@
 struct pci_dev;
 
 #ifdef CONFIG_PCI
+int sysfb_apply_screen_info_fixups(void);
 bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev);
 #else
+static inline int sysfb_apply_screen_info_fixups(void)
+{
+	return 0;
+}
 static inline bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev)
 {
 	return false;
diff --git a/drivers/firmware/sysfb_pci.c b/drivers/firmware/sysfb_pci.c
index 8f3adeef4fb1..d972750c6bc6 100644
--- a/drivers/firmware/sysfb_pci.c
+++ b/drivers/firmware/sysfb_pci.c
@@ -1,9 +1,120 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 #include <linux/pci.h>
+#include <linux/printk.h>
+#include <linux/screen_info.h>
+#include <linux/sysfb.h>
 
 #include "sysfb.h"
 
+static struct pci_dev *sysfb_lfb_pdev;
+static size_t sysfb_lfb_bar;
+static resource_size_t sysfb_lfb_res_start; // original start of resource
+static resource_size_t sysfb_lfb_offset; // framebuffer offset within resource
+
+static bool __sysfb_relocation_is_valid(const struct screen_info *si, struct resource *pr)
+{
+	u64 size = __screen_info_lfb_size(si, screen_info_video_type(si));
+
+	if (sysfb_lfb_offset > resource_size(pr))
+		return false;
+	if (size > resource_size(pr))
+		return false;
+	if (resource_size(pr) - size < sysfb_lfb_offset)
+		return false;
+
+	return true;
+}
+
+int sysfb_apply_screen_info_fixups(void)
+{
+	struct screen_info *si = &sysfb_primary_display.screen;
+	struct resource *pr;
+
+	if (!sysfb_lfb_pdev)
+		return 0; /* primary display is not on a PCI device */
+
+	pr = &sysfb_lfb_pdev->resource[sysfb_lfb_bar];
+
+	if (pr->start == sysfb_lfb_res_start)
+		return 0; /* no relocation took place */
+
+	if (!__sysfb_relocation_is_valid(si, pr))
+		return -ENXIO;
+
+	/*
+	 * Only update base if we have an actual relocation to a valid I/O range.
+	 */
+	__screen_info_set_lfb_base(si, pr->start + sysfb_lfb_offset);
+	pr_info("Relocating firmware framebuffer to offset %pa[d] within %pr\n",
+		&sysfb_lfb_offset, pr);
+
+	return 0;
+}
+
+static int __screen_info_lfb_pci_bus_region(const struct screen_info *si, unsigned int type,
+					    struct pci_bus_region *r)
+{
+	u64 base, size;
+
+	base = __screen_info_lfb_base(si);
+	if (!base)
+		return -EINVAL;
+
+	size = __screen_info_lfb_size(si, type);
+	if (!size)
+		return -EINVAL;
+
+	r->start = base;
+	r->end = base + size - 1;
+
+	return 0;
+}
+
+static void sysfb_fixup_lfb(struct pci_dev *pdev)
+{
+	unsigned int type;
+	struct pci_bus_region bus_region;
+	int ret;
+	struct resource r = {
+		.flags = IORESOURCE_MEM,
+	};
+	const struct resource *pr;
+	const struct screen_info *si = &sysfb_primary_display.screen;
+
+	if (sysfb_lfb_pdev)
+		return; // already found
+
+	type = screen_info_video_type(si);
+	if (!__screen_info_has_lfb(type))
+		return; // only applies to EFI; maybe VESA
+
+	ret = __screen_info_lfb_pci_bus_region(si, type, &bus_region);
+	if (ret < 0)
+		return;
+
+	/*
+	 * Translate the PCI bus address to resource. Account for an offset if
+	 * the framebuffer is behind a PCI host bridge.
+	 */
+	pcibios_bus_to_resource(pdev->bus, &r, &bus_region);
+
+	pr = pci_find_resource(pdev, &r);
+	if (!pr)
+		return;
+
+	/*
+	 * We've found a PCI device with the framebuffer resource. Store away
+	 * the parameters to track relocation of the framebuffer aperture.
+	 */
+	sysfb_lfb_pdev = pdev;
+	sysfb_lfb_bar = pr - pdev->resource;
+	sysfb_lfb_offset = r.start - pr->start;
+	sysfb_lfb_res_start = bus_region.start;
+}
+DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY, 16,
+			       sysfb_fixup_lfb);
+
 bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev)
 {
 	/*
diff --git a/drivers/firmware/sysfb_primary.c b/drivers/firmware/sysfb_primary.c
index ab8d7fc468bb..298f87a43a7e 100644
--- a/drivers/firmware/sysfb_primary.c
+++ b/drivers/firmware/sysfb_primary.c
@@ -32,6 +32,7 @@
 #include <linux/pci.h>
 #include <linux/platform_data/simplefb.h>
 #include <linux/platform_device.h>
+#include <linux/printk.h>
 #include <linux/screen_info.h>
 #include <linux/sysfb.h>
 
@@ -127,11 +128,15 @@ static __init int sysfb_init(void)
 	struct simplefb_platform_data mode;
 	const char *name;
 	bool compatible;
-	int ret = 0;
+	int ret;
 
-	screen_info_apply_fixups();
+	ret = sysfb_apply_screen_info_fixups();
 
 	mutex_lock(&disable_lock);
+	if (ret) {
+		pr_warn("Invalid relocation, disabling system framebuffer\n");
+		disabled = true; /* screen_info relocation failed */
+	}
 	if (disabled)
 		goto unlock_mutex;
 
diff --git a/drivers/video/screen_info_pci.c b/drivers/video/screen_info_pci.c
index 8f34d8a74f09..d8985a54ce71 100644
--- a/drivers/video/screen_info_pci.c
+++ b/drivers/video/screen_info_pci.c
@@ -1,117 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 
 #include <linux/pci.h>
-#include <linux/printk.h>
 #include <linux/screen_info.h>
-#include <linux/string.h>
-#include <linux/sysfb.h>
-
-static struct pci_dev *screen_info_lfb_pdev;
-static size_t screen_info_lfb_bar;
-static resource_size_t screen_info_lfb_res_start; // original start of resource
-static resource_size_t screen_info_lfb_offset; // framebuffer offset within resource
-
-static bool __screen_info_relocation_is_valid(const struct screen_info *si, struct resource *pr)
-{
-	u64 size = __screen_info_lfb_size(si, screen_info_video_type(si));
-
-	if (screen_info_lfb_offset > resource_size(pr))
-		return false;
-	if (size > resource_size(pr))
-		return false;
-	if (resource_size(pr) - size < screen_info_lfb_offset)
-		return false;
-
-	return true;
-}
-
-void screen_info_apply_fixups(void)
-{
-	struct screen_info *si = &sysfb_primary_display.screen;
-
-	if (screen_info_lfb_pdev) {
-		struct resource *pr = &screen_info_lfb_pdev->resource[screen_info_lfb_bar];
-
-		if (pr->start != screen_info_lfb_res_start) {
-			if (__screen_info_relocation_is_valid(si, pr)) {
-				/*
-				 * Only update base if we have an actual
-				 * relocation to a valid I/O range.
-				 */
-				__screen_info_set_lfb_base(si, pr->start + screen_info_lfb_offset);
-				pr_info("Relocating firmware framebuffer to offset %pa[d] within %pr\n",
-					&screen_info_lfb_offset, pr);
-			} else {
-				pr_warn("Invalid relocating, disabling firmware framebuffer\n");
-			}
-		}
-	}
-}
-
-static int __screen_info_lfb_pci_bus_region(const struct screen_info *si, unsigned int type,
-					    struct pci_bus_region *r)
-{
-	u64 base, size;
-
-	base = __screen_info_lfb_base(si);
-	if (!base)
-		return -EINVAL;
-
-	size = __screen_info_lfb_size(si, type);
-	if (!size)
-		return -EINVAL;
-
-	r->start = base;
-	r->end = base + size - 1;
-
-	return 0;
-}
-
-static void screen_info_fixup_lfb(struct pci_dev *pdev)
-{
-	unsigned int type;
-	struct pci_bus_region bus_region;
-	int ret;
-	struct resource r = {
-		.flags = IORESOURCE_MEM,
-	};
-	const struct resource *pr;
-	const struct screen_info *si = &sysfb_primary_display.screen;
-
-	if (screen_info_lfb_pdev)
-		return; // already found
-
-	type = screen_info_video_type(si);
-	if (!__screen_info_has_lfb(type))
-		return; // only applies to EFI; maybe VESA
-
-	ret = __screen_info_lfb_pci_bus_region(si, type, &bus_region);
-	if (ret < 0)
-		return;
-
-	/*
-	 * Translate the PCI bus address to resource. Account
-	 * for an offset if the framebuffer is behind a PCI host
-	 * bridge.
-	 */
-	pcibios_bus_to_resource(pdev->bus, &r, &bus_region);
-
-	pr = pci_find_resource(pdev, &r);
-	if (!pr)
-		return;
-
-	/*
-	 * We've found a PCI device with the framebuffer
-	 * resource. Store away the parameters to track
-	 * relocation of the framebuffer aperture.
-	 */
-	screen_info_lfb_pdev = pdev;
-	screen_info_lfb_bar = pr - pdev->resource;
-	screen_info_lfb_offset = r.start - pr->start;
-	screen_info_lfb_res_start = bus_region.start;
-}
-DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY, 16,
-			       screen_info_fixup_lfb);
 
 static struct pci_dev *__screen_info_pci_dev(struct resource *res)
 {
diff --git a/include/linux/screen_info.h b/include/linux/screen_info.h
index c022403c599a..2adbe25b88d8 100644
--- a/include/linux/screen_info.h
+++ b/include/linux/screen_info.h
@@ -140,11 +140,8 @@ u32 __screen_info_lfb_bits_per_pixel(const struct screen_info *si);
 int screen_info_pixel_format(const struct screen_info *si, struct pixel_format *f);
 
 #if defined(CONFIG_PCI)
-void screen_info_apply_fixups(void);
 struct pci_dev *screen_info_pci_dev(const struct screen_info *si);
 #else
-static inline void screen_info_apply_fixups(void)
-{ }
 static inline struct pci_dev *screen_info_pci_dev(const struct screen_info *si)
 {
 	return NULL;
-- 
2.53.0



  parent reply	other threads:[~2026-04-02  9:23 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-02  9:09 [PATCH 0/8] firmware: sysfb: Consolidate config/code wrt. sysfb_primary_screen Thomas Zimmermann
2026-04-02  9:09 ` [PATCH 1/8] hv: Select CONFIG_SYSFB only for CONFIG_HYPERV_VMBUS Thomas Zimmermann
2026-04-02 10:50   ` Saurabh Singh Sengar
2026-04-02  9:09 ` [PATCH 2/8] firmware: efi: Never declare sysfb_primary_display on x86 Thomas Zimmermann
2026-04-02  9:09 ` [PATCH 3/8] firmware: sysfb: Make CONFIG_SYSFB a user-selectable option Thomas Zimmermann
2026-04-02 13:08   ` Arnd Bergmann
2026-04-02 14:10     ` Thomas Zimmermann
2026-04-02 14:59       ` Arnd Bergmann
2026-04-02 15:27         ` Thomas Zimmermann
2026-04-02 16:47           ` Arnd Bergmann
2026-04-02  9:09 ` [PATCH 4/8] firmware: sysfb: Split sysfb.c into sysfb_primary.c and sysfb_pci.c Thomas Zimmermann
2026-04-02  9:09 ` Thomas Zimmermann [this message]
2026-04-02  9:09 ` [PATCH 6/8] firmware: sysfb: Avoid forward-declaring sysfb_parent_dev() Thomas Zimmermann
2026-04-02  9:09 ` [PATCH 7/8] firmware: efi: Make CONFIG_EFI_EARLYCON depend on CONFIG_SYSFB; clean up Thomas Zimmermann
2026-04-02  9:09 ` [PATCH 8/8] firmware: sysfb: Move CONFIG_FIRMWARE_EDID to firmware options Thomas Zimmermann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260402092305.208728-6-tzimmermann@suse.de \
    --to=tzimmermann@suse.de \
    --cc=airlied@gmail.com \
    --cc=ardb@kernel.org \
    --cc=arnd@arndb.de \
    --cc=chenhuacai@kernel.org \
    --cc=decui@microsoft.com \
    --cc=deller@gmx.de \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=haiyangz@microsoft.com \
    --cc=ilias.apalodimas@linaro.org \
    --cc=javierm@redhat.com \
    --cc=kernel@xen0n.name \
    --cc=kys@microsoft.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-efi@vger.kernel.org \
    --cc=linux-fbdev@vger.kernel.org \
    --cc=linux-hyperv@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=longli@microsoft.com \
    --cc=loongarch@lists.linux.dev \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=mripard@kernel.org \
    --cc=simona@ffwll.ch \
    --cc=wei.liu@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox