From nobody Mon Feb 9 08:29:10 2026 Received: from mail-pf1-f226.google.com (mail-pf1-f226.google.com [209.85.210.226]) (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 8B7A630FC1E for ; Mon, 29 Dec 2025 21:59:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.226 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767045562; cv=none; b=QqKN/DhQQhV+X8CxkKlhL/bJvlUGHzFqoTA2f3iD1XCPAZK9hq81K8zBENjNLJw1PZkEPS27G9NZ+Idk8UYOfQ0TmeeTaqVt2/oUdrBm+UMEVO+KGyYL8ErjhVsPwgmgTccaqYXyiyql6Y1Oh0DIcLzFqEm8HI7QDofNPyw7lMc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767045562; c=relaxed/simple; bh=BcBsQxJIQJJlqTuvgYwuQeVrGVdbVN2Bv4pHjjNssq4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=qlMDwPN0nPN1OA/KoNyHRFQtart0ATNst8DqXNjkLjtag1b0RfAR0SPqP9YbHW162JT2kEMJIfZoexcXDugivh2ubzSCh7DwOcbUsFgM8iyvH+K+a4jezU2w2rIq3OhoKb42NQ/DT3VSCegZnTEqMtYf3bmYDTpRf0TbFwu0eCI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=broadcom.com; spf=fail smtp.mailfrom=broadcom.com; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b=BBAiwTaN; arc=none smtp.client-ip=209.85.210.226 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=broadcom.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=broadcom.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="BBAiwTaN" Received: by mail-pf1-f226.google.com with SMTP id d2e1a72fcca58-7b9215e55e6so5795269b3a.2 for ; Mon, 29 Dec 2025 13:59:19 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767045559; x=1767650359; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=pJWRKQHrZ65aEzUoFIqsgm/0iuuS/awy21jyOivXLQY=; b=NUnk+jBqqE8sOZhkiAZOdJZ5iz9Ojzx57vKIbY7jXfWjkDig3s3KZ5y9MFIG1+3Coy rJ5cZ+U3kKn4j+OR47YDJUblb5es6TQWrjwQiBqFo0bIcpW0EJOJxZvplMK3Nwol+uMR Cw7q5joiHGV9DYxxo3h0eS1JUq2VomcJAlQDRBQ6PwX0SKasXodAvMqr4uFB1cKGKkHL JJNIzhiTJcuvA4GRpSJzAcdA02nTFC5okxotgy6k2Hm6P/dNdIZc2JbHok5wBntpBDKt S1twcTsipLXepYKdSwLn2OQCd5lASMUOh1fnhs5bE4jhHkkZeia+eyk9vR3bmKnIB+yg 26+A== X-Forwarded-Encrypted: i=1; AJvYcCXBC2mR9XSd4JOB2UbtFEa8wKEmBzEM+hYtKSA6Tbvuv6CS+6ptJp33RV1yiyN/aA4EwL4heaXzGeLewHY=@vger.kernel.org X-Gm-Message-State: AOJu0YxyZXVJFyjtXpPYxGxMz1uYDbsToAVrEkTXqi8O33gK2gV1E5qt praVGYbX8jspehry0ZzbFWCcgNSVTDAXpbRi6dslX7e8r4ZUNeKVwYLaWkMOsb6MxkqnG8SHuA0 bWL/CDJya419+a10/XnH36ody4gy8J8MbVHO0DVNaJMDK1QjoW9R7GII6JrmDCFKIt/WKyDt4Cg jkxDUK3hyPiWj20LtemdoGLZLxVeZJd6alifYWveNTkzyIbaefQLGQHd26oW7YOwM74aZEFuhjp ZDXkVfUsZHMb8qf X-Gm-Gg: AY/fxX5eQta4JJ9JSzV3IKLqd+M0CqWiL/GH0xVZT5WfFUF6frpciD9H4ZJoBtAMvqQ kZaF6xS6DzwPBIOKv4YIPBODbLwZzr/vDrZ73CpNvIi2jlHtvfhOCnlmk+agBbg3oVt+j9TatOn mZ+RgvxQIlYX/dz6eq0wkNuH3WSvZCjK12oTjOo2g8wQMLe633JOKQUdI8C5oxhHSCxEgtVF271 +lgFYq9BWmZr6Cyv5tIGy6GAabP1Hgri6kMK0iQSUDG51g4QrW6GxyfVxw6P3L7KhK0eUmruzBJ 1oqmuUn+VxZ4yK/Mxkb2kKAIao9cr5fslATfeShF3YhvlSdhMEwp+5vGCX6Oxqbsw6ALeeyLDxC 7QX+wkZblPjm8w81WJUwk27ZGRtOauKodLsQ6r8Nbl4U+/GAdPHbNl3jH+fD5fcZcQ1P9tGV4u3 f+z60WuYQ34j31BqvNabzvQQj5i2cwBkAYZlexLF/liIjW X-Google-Smtp-Source: AGHT+IEUfu6KQAxkN9UYASuPYwKpT8Kn7c1fQogQlBic7A7+dGAPbyH6PSD/tODEqyGZUjliYbcCO5/gcJ/n X-Received: by 2002:a05:7022:150d:b0:11c:e661:2590 with SMTP id a92af1059eb24-121722ba459mr25017799c88.20.1767045558616; Mon, 29 Dec 2025 13:59:18 -0800 (PST) Received: from smtp-us-east1-p01-i01-si01.dlp.protect.broadcom.com (address-144-49-247-101.dlp.protect.broadcom.com. [144.49.247.101]) by smtp-relay.gmail.com with ESMTPS id a92af1059eb24-1217252ca1esm6693605c88.3.2025.12.29.13.59.15 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 29 Dec 2025 13:59:18 -0800 (PST) X-Relaying-Domain: broadcom.com X-CFilter-Loop: Reflected Received: by mail-qv1-f71.google.com with SMTP id 6a1803df08f44-88a360b8096so245141706d6.0 for ; Mon, 29 Dec 2025 13:59:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; t=1767045555; x=1767650355; 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; bh=pJWRKQHrZ65aEzUoFIqsgm/0iuuS/awy21jyOivXLQY=; b=BBAiwTaN/whI3+DNFkzYsUacChl2+p5hTcPuFnKVeSvgXR9ZjMgTBBohj4DR8UlqEe GA+I8+WpV8Aqq/aDm7HPVowmAwyKS952ABr9S84hc8U8fmUpfMxO/ENJ9D3R+e2wn/xx mUd/ApIMoGaJgt/5aEVC57sKpCCMaj5bz4eQM= X-Forwarded-Encrypted: i=1; AJvYcCU04vmW12QPk0Qyix43plPED7JNDdwjQt/2W+9BrtxYoRlC3TWj+86JReJJc44nnqt9UjFRsVv8NpB77Bs=@vger.kernel.org X-Received: by 2002:a05:6214:419b:b0:88f:fbcf:e7cb with SMTP id 6a1803df08f44-88ffbcfeeabmr183064896d6.51.1767045554301; Mon, 29 Dec 2025 13:59:14 -0800 (PST) X-Received: by 2002:a05:6214:419b:b0:88f:fbcf:e7cb with SMTP id 6a1803df08f44-88ffbcfeeabmr183064626d6.51.1767045553722; Mon, 29 Dec 2025 13:59:13 -0800 (PST) Received: from localhost.localdomain (pool-173-49-113-140.phlapa.fios.verizon.net. [173.49.113.140]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-88d9759f164sm231530026d6.24.2025.12.29.13.59.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 29 Dec 2025 13:59:11 -0800 (PST) From: Zack Rusin To: dri-devel@lists.freedesktop.org Cc: Ard Biesheuvel , Thomas Zimmermann , Javier Martinez Canillas , Helge Deller , linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fbdev@vger.kernel.org Subject: [PATCH 01/12] video/aperture: Add sysfb restore on DRM probe failure Date: Mon, 29 Dec 2025 16:58:07 -0500 Message-ID: <20251229215906.3688205-2-zack.rusin@broadcom.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20251229215906.3688205-1-zack.rusin@broadcom.com> References: <20251229215906.3688205-1-zack.rusin@broadcom.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 X-DetectorID-Processed: b00c1d49-9d2e-4205-b15f-d015386d3d5e Content-Type: text/plain; charset="utf-8" When a DRM driver calls aperture_remove_conflicting_pci_devices(), the firmware framebuffer (EFI, VESA, etc.) is disabled and its platform device is unregistered. If the DRM driver's probe subsequently fails, the user is left with no display output. Add sysfb_restore() to re-enable the Generic System Framebuffers support and re-create the platform device that was previously unregistered by sysfb_disable(). Add devm_aperture_remove_conflicting_pci_devices() which wraps the existing function and registers a devm action to automatically call sysfb_restore() if the driver's probe fails or the driver is unloaded. Drivers can call devm_aperture_remove_conflicting_pci_devices_done() after successful probe to cancel the automatic restore. Refactor sysfb_init() to use a shared __sysfb_create_device() helper that can be called from both sysfb_init() and sysfb_restore(). Add a quirks_applied flag to handle the edge case where a driver calls sysfb_disable() before sysfb_init() runs, in this case sysfb_restore() defers device creation to sysfb_init() since the __init quirk functions cannot be called after init memory is freed. Signed-off-by: Zack Rusin Cc: Ard Biesheuvel Cc: Thomas Zimmermann Cc: Javier Martinez Canillas Cc: Helge Deller Cc: linux-efi@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linux-fbdev@vger.kernel.org --- drivers/firmware/efi/sysfb_efi.c | 2 +- drivers/firmware/sysfb.c | 191 +++++++++++++++++++----------- drivers/firmware/sysfb_simplefb.c | 10 +- drivers/video/aperture.c | 54 +++++++++ include/linux/aperture.h | 14 +++ include/linux/sysfb.h | 6 + 6 files changed, 201 insertions(+), 76 deletions(-) diff --git a/drivers/firmware/efi/sysfb_efi.c b/drivers/firmware/efi/sysfb_= efi.c index 1e509595ac03..3fe7c57ad849 100644 --- a/drivers/firmware/efi/sysfb_efi.c +++ b/drivers/firmware/efi/sysfb_efi.c @@ -365,7 +365,7 @@ __init void sysfb_apply_efi_quirks(void) } } =20 -__init void sysfb_set_efifb_fwnode(struct platform_device *pd) +void sysfb_set_efifb_fwnode(struct platform_device *pd) { if (screen_info.orig_video_isVGA =3D=3D VIDEO_TYPE_EFI && IS_ENABLED(CONF= IG_PCI)) { fwnode_init(&efifb_fwnode, &efifb_fwnode_ops); diff --git a/drivers/firmware/sysfb.c b/drivers/firmware/sysfb.c index 889e5b05c739..c45b6f487103 100644 --- a/drivers/firmware/sysfb.c +++ b/drivers/firmware/sysfb.c @@ -38,6 +38,7 @@ static struct platform_device *pd; static DEFINE_MUTEX(disable_lock); static bool disabled; +static bool quirks_applied; =20 static struct device *sysfb_parent_dev(const struct screen_info *si); =20 @@ -79,6 +80,121 @@ void sysfb_disable(struct device *dev) } EXPORT_SYMBOL_GPL(sysfb_disable); =20 +/* Caller must hold disable_lock */ +static int __sysfb_create_device(bool restore) +{ + struct screen_info *si =3D &screen_info; + struct device *parent; + unsigned int type; + struct simplefb_platform_data mode; + const char *name; + bool compatible; + int ret =3D 0; + + if (!IS_ERR_OR_NULL(pd)) + return 0; + + /* + * If quirks haven't been applied yet, sysfb_init() hasn't run. + * Don't create the device now - let sysfb_init() do it after + * applying the necessary fixups and quirks. We can't call + * sysfb_apply_efi_quirks() here because it's __init. + */ + if (!quirks_applied) + return 0; + + parent =3D sysfb_parent_dev(si); + if (IS_ERR(parent)) + return PTR_ERR(parent); + + type =3D screen_info_video_type(si); + + /* try to create a simple-framebuffer device */ + compatible =3D sysfb_parse_mode(si, &mode); + if (compatible) { + pd =3D sysfb_create_simplefb(si, &mode, parent); + if (!IS_ERR(pd)) { + if (restore) + pr_info("sysfb: restored simple-framebuffer device\n"); + goto put_device; + } + } + + /* if the FB is incompatible, create a legacy framebuffer device */ + switch (type) { + case VIDEO_TYPE_EGAC: + name =3D "ega-framebuffer"; + break; + case VIDEO_TYPE_VGAC: + name =3D "vga-framebuffer"; + break; + case VIDEO_TYPE_VLFB: + name =3D "vesa-framebuffer"; + break; + case VIDEO_TYPE_EFI: + name =3D "efi-framebuffer"; + break; + default: + name =3D "platform-framebuffer"; + break; + } + + pd =3D platform_device_alloc(name, 0); + if (!pd) { + ret =3D -ENOMEM; + goto put_device; + } + + pd->dev.parent =3D parent; + + sysfb_set_efifb_fwnode(pd); + + ret =3D platform_device_add_data(pd, si, sizeof(*si)); + if (ret) + goto err; + + ret =3D platform_device_add(pd); + if (ret) + goto err; + + if (restore) + pr_info("sysfb: restored %s device\n", name); + goto put_device; +err: + platform_device_put(pd); + pd =3D NULL; +put_device: + put_device(parent); + return ret; +} + +/** + * sysfb_restore() - restore the Generic System Framebuffer + * + * This function re-enables the Generic System Framebuffers support and + * re-creates the platform device that was previously unregistered by + * sysfb_disable(). This is intended for use by DRM drivers that need to + * restore the fallback framebuffer when their probe fails after having + * called aperture_remove_conflicting_devices() or similar. + * + * Context: The function can sleep. A @disable_lock mutex is acquired. + * + * Returns: + * 0 on success, or a negative errno value otherwise. + */ +int sysfb_restore(void) +{ + int ret; + + mutex_lock(&disable_lock); + disabled =3D false; + ret =3D __sysfb_create_device(true); + mutex_unlock(&disable_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(sysfb_restore); + /** * sysfb_handles_screen_info() - reports if sysfb handles the global scree= n_info * @@ -141,82 +257,17 @@ static struct device *sysfb_parent_dev(const struct s= creen_info *si) =20 static __init int sysfb_init(void) { - struct screen_info *si =3D &screen_info; - struct device *parent; - unsigned int type; - struct simplefb_platform_data mode; - const char *name; - bool compatible; int ret =3D 0; =20 screen_info_apply_fixups(); - - mutex_lock(&disable_lock); - if (disabled) - goto unlock_mutex; - sysfb_apply_efi_quirks(); =20 - parent =3D sysfb_parent_dev(si); - if (IS_ERR(parent)) { - ret =3D PTR_ERR(parent); - goto unlock_mutex; - } - - /* try to create a simple-framebuffer device */ - compatible =3D sysfb_parse_mode(si, &mode); - if (compatible) { - pd =3D sysfb_create_simplefb(si, &mode, parent); - if (!IS_ERR(pd)) - goto put_device; - } - - type =3D screen_info_video_type(si); - - /* if the FB is incompatible, create a legacy framebuffer device */ - switch (type) { - case VIDEO_TYPE_EGAC: - name =3D "ega-framebuffer"; - break; - case VIDEO_TYPE_VGAC: - name =3D "vga-framebuffer"; - break; - case VIDEO_TYPE_VLFB: - name =3D "vesa-framebuffer"; - break; - case VIDEO_TYPE_EFI: - name =3D "efi-framebuffer"; - break; - default: - name =3D "platform-framebuffer"; - break; - } - - pd =3D platform_device_alloc(name, 0); - if (!pd) { - ret =3D -ENOMEM; - goto put_device; - } - - pd->dev.parent =3D parent; - - sysfb_set_efifb_fwnode(pd); - - ret =3D platform_device_add_data(pd, si, sizeof(*si)); - if (ret) - goto err; - - ret =3D platform_device_add(pd); - if (ret) - goto err; - - goto put_device; -err: - platform_device_put(pd); -put_device: - put_device(parent); -unlock_mutex: + mutex_lock(&disable_lock); + quirks_applied =3D true; + if (!disabled) + ret =3D __sysfb_create_device(false); mutex_unlock(&disable_lock); + return ret; } =20 diff --git a/drivers/firmware/sysfb_simplefb.c b/drivers/firmware/sysfb_sim= plefb.c index 592d8a644619..6fcbc3ae17d5 100644 --- a/drivers/firmware/sysfb_simplefb.c +++ b/drivers/firmware/sysfb_simplefb.c @@ -24,8 +24,8 @@ static const char simplefb_resname[] =3D "BOOTFB"; static const struct simplefb_format formats[] =3D SIMPLEFB_FORMATS; =20 /* try parsing screen_info into a simple-framebuffer mode struct */ -__init bool sysfb_parse_mode(const struct screen_info *si, - struct simplefb_platform_data *mode) +bool sysfb_parse_mode(const struct screen_info *si, + struct simplefb_platform_data *mode) { __u8 type; u32 bits_per_pixel; @@ -61,9 +61,9 @@ __init bool sysfb_parse_mode(const struct screen_info *si, return false; } =20 -__init struct platform_device *sysfb_create_simplefb(const struct screen_i= nfo *si, - const struct simplefb_platform_data *mode, - struct device *parent) +struct platform_device *sysfb_create_simplefb(const struct screen_info *si, + const struct simplefb_platform_data *mode, + struct device *parent) { struct platform_device *pd; struct resource res; diff --git a/drivers/video/aperture.c b/drivers/video/aperture.c index 2b5a1e666e9b..4de6dc04a3fd 100644 --- a/drivers/video/aperture.c +++ b/drivers/video/aperture.c @@ -372,3 +372,57 @@ int aperture_remove_conflicting_pci_devices(struct pci= _dev *pdev, const char *na =20 } EXPORT_SYMBOL(aperture_remove_conflicting_pci_devices); + +static void devm_aperture_restore_sysfb(void *unused) +{ + sysfb_restore(); +} + +/** + * devm_aperture_remove_conflicting_pci_devices - remove existing framebuf= fers + * with sysfb restore on fa= ilure + * @pdev: PCI device + * @name: a descriptive name of the requesting driver + * + * This function removes devices that own apertures within any of @pdev's + * memory bars, similar to aperture_remove_conflicting_pci_devices(). + * + * Additionally, it registers a devm action that will restore the system + * framebuffer if the driver's probe fails or the driver is unloaded. This + * ensures the user doesn't lose display output if the DRM driver probe fa= ils + * after removing the firmware framebuffer. + * + * This function should be called early in the driver's probe function. The + * driver must call devm_aperture_remove_conflicting_pci_devices_done() af= ter + * successfully completing probe to cancel the automatic restore. + * + * Returns: + * 0 on success, or a negative errno code otherwise + */ +int devm_aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, + const char *name) +{ + int ret; + + ret =3D aperture_remove_conflicting_pci_devices(pdev, name); + if (ret) + return ret; + + return devm_add_action_or_reset(&pdev->dev, devm_aperture_restore_sysfb, + NULL); +} +EXPORT_SYMBOL(devm_aperture_remove_conflicting_pci_devices); + +/** + * devm_aperture_remove_conflicting_pci_devices_done - cancel sysfb restore + * @pdev: PCI device + * + * Cancels the automatic sysfb restore action registered by + * devm_aperture_remove_conflicting_pci_devices(). Call this after the + * driver has successfully completed probe and registered its display. + */ +void devm_aperture_remove_conflicting_pci_devices_done(struct pci_dev *pde= v) +{ + devm_remove_action(&pdev->dev, devm_aperture_restore_sysfb, NULL); +} +EXPORT_SYMBOL(devm_aperture_remove_conflicting_pci_devices_done); diff --git a/include/linux/aperture.h b/include/linux/aperture.h index 1a9a88b11584..ea0ece7f777e 100644 --- a/include/linux/aperture.h +++ b/include/linux/aperture.h @@ -19,6 +19,10 @@ int aperture_remove_conflicting_devices(resource_size_t = base, resource_size_t si int __aperture_remove_legacy_vga_devices(struct pci_dev *pdev); =20 int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const ch= ar *name); + +int devm_aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, + const char *name); +void devm_aperture_remove_conflicting_pci_devices_done(struct pci_dev *pde= v); #else static inline int devm_aperture_acquire_for_platform_device(struct platfor= m_device *pdev, resource_size_t base, @@ -42,6 +46,16 @@ static inline int aperture_remove_conflicting_pci_device= s(struct pci_dev *pdev, { return 0; } + +static inline int devm_aperture_remove_conflicting_pci_devices(struct pci_= dev *pdev, + const char *name) +{ + return 0; +} + +static inline void devm_aperture_remove_conflicting_pci_devices_done(struc= t pci_dev *pdev) +{ +} #endif =20 /** diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h index b449665c686a..c0ade38bcf99 100644 --- a/include/linux/sysfb.h +++ b/include/linux/sysfb.h @@ -63,6 +63,7 @@ struct efifb_dmi_info { #ifdef CONFIG_SYSFB =20 void sysfb_disable(struct device *dev); +int sysfb_restore(void); =20 bool sysfb_handles_screen_info(void); =20 @@ -72,6 +73,11 @@ static inline void sysfb_disable(struct device *dev) { } =20 +static inline int sysfb_restore(void) +{ + return -ENODEV; +} + static inline bool sysfb_handles_screen_info(void) { return false; --=20 2.48.1