* [PATCH v2 1/7] drm/edid: Include <linux/fb.h>
2026-07-09 9:15 [PATCH v2 0/7] vga_switcheroo, drm: Push fbcon handling into DRM clients Thomas Zimmermann
@ 2026-07-09 9:15 ` Thomas Zimmermann
2026-07-09 9:15 ` [PATCH v2 2/7] drm/client: Add acquire_outputs callback; implement for fbdev emulation Thomas Zimmermann
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Thomas Zimmermann @ 2026-07-09 9:15 UTC (permalink / raw)
To: lukas, jfalempe, alexander.deucher, christian.koenig, airlied,
simona, maarten.lankhorst, mripard, jani.nikula, joonas.lahtinen,
rodrigo.vivi, tursulin, lyude, dakr, deller
Cc: dri-devel, amd-gfx, intel-gfx, nouveau, linux-fbdev,
sashiko-reviews, Thomas Zimmermann
We currently get <linux/fb.h> via <linux/vga_switcheroo.h>. Include it
explicitly.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/drm/drm_edid.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 07970e5b5f65..86ae2d8d4de2 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -32,6 +32,7 @@
#include <linux/byteorder/generic.h>
#include <linux/cec.h>
#include <linux/export.h>
+#include <linux/fb.h> /* for KHZ2PICOS() */
#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 2/7] drm/client: Add acquire_outputs callback; implement for fbdev emulation
2026-07-09 9:15 [PATCH v2 0/7] vga_switcheroo, drm: Push fbcon handling into DRM clients Thomas Zimmermann
2026-07-09 9:15 ` [PATCH v2 1/7] drm/edid: Include <linux/fb.h> Thomas Zimmermann
@ 2026-07-09 9:15 ` Thomas Zimmermann
2026-07-09 9:15 ` [PATCH v2 3/7] vga_switcheroo: Add pre_switch callback to client ops Thomas Zimmermann
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Thomas Zimmermann @ 2026-07-09 9:15 UTC (permalink / raw)
To: lukas, jfalempe, alexander.deucher, christian.koenig, airlied,
simona, maarten.lankhorst, mripard, jani.nikula, joonas.lahtinen,
rodrigo.vivi, tursulin, lyude, dakr, deller
Cc: dri-devel, amd-gfx, intel-gfx, nouveau, linux-fbdev,
sashiko-reviews, Thomas Zimmermann
Add the callback acquire_outputs to drm_client_funcs to inform an internal
DRM client that vga-switcheroo is about to switch the physical outputs to
the client's device. Allows the client to prepare its internal state for
the upcoming switch.
Wire up the DRM client helpers to invoke the helper for a device's
clients.
Implement acquire_outputs for fbdev emulation. Invoke fb_switch_outputs(),
which remaps framebuffers to virtual terminals in fbcon. Currently this
is still being done by vga-switcheroo. With more DRM clients becoming
available, vga-switcheroo needs to become client agonostic.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/drm/clients/drm_fbdev_client.c | 23 +++++++++++++++-------
drivers/gpu/drm/drm_client_event.c | 18 +++++++++++++++++
include/drm/drm_client.h | 14 +++++++++++++
include/drm/drm_client_event.h | 3 +++
4 files changed, 51 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/clients/drm_fbdev_client.c b/drivers/gpu/drm/clients/drm_fbdev_client.c
index 91d196a397cf..827f32668714 100644
--- a/drivers/gpu/drm/clients/drm_fbdev_client.c
+++ b/drivers/gpu/drm/clients/drm_fbdev_client.c
@@ -47,6 +47,14 @@ static int drm_fbdev_client_restore(struct drm_client_dev *client, bool force)
return 0;
}
+static void drm_fbdev_client_acquire_outputs(struct drm_client_dev *client)
+{
+ struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
+
+ if (fb_helper->info)
+ fb_switch_outputs(fb_helper->info);
+}
+
static int drm_fbdev_client_hotplug(struct drm_client_dev *client)
{
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
@@ -95,13 +103,14 @@ static int drm_fbdev_client_resume(struct drm_client_dev *client)
}
static const struct drm_client_funcs drm_fbdev_client_funcs = {
- .owner = THIS_MODULE,
- .free = drm_fbdev_client_free,
- .unregister = drm_fbdev_client_unregister,
- .restore = drm_fbdev_client_restore,
- .hotplug = drm_fbdev_client_hotplug,
- .suspend = drm_fbdev_client_suspend,
- .resume = drm_fbdev_client_resume,
+ .owner = THIS_MODULE,
+ .free = drm_fbdev_client_free,
+ .unregister = drm_fbdev_client_unregister,
+ .restore = drm_fbdev_client_restore,
+ .acquire_outputs = drm_fbdev_client_acquire_outputs,
+ .hotplug = drm_fbdev_client_hotplug,
+ .suspend = drm_fbdev_client_suspend,
+ .resume = drm_fbdev_client_resume,
};
/**
diff --git a/drivers/gpu/drm/drm_client_event.c b/drivers/gpu/drm/drm_client_event.c
index 7b3e362f7926..f0af584da23c 100644
--- a/drivers/gpu/drm/drm_client_event.c
+++ b/drivers/gpu/drm/drm_client_event.c
@@ -123,6 +123,24 @@ void drm_client_dev_restore(struct drm_device *dev, bool force)
mutex_unlock(&dev->clientlist_mutex);
}
+void drm_client_dev_acquire_outputs(struct drm_device *dev)
+{
+ struct drm_client_dev *client;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(client, &dev->clientlist, list) {
+ if (!client->funcs || !client->funcs->acquire_outputs)
+ continue;
+
+ client->funcs->acquire_outputs(client);
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+}
+EXPORT_SYMBOL(drm_client_dev_acquire_outputs);
+
static int drm_client_suspend(struct drm_client_dev *client)
{
struct drm_device *dev = client->dev;
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index 49a21f3dcb36..10a0cae3e48f 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -66,6 +66,20 @@ struct drm_client_funcs {
*/
int (*restore)(struct drm_client_dev *client, bool force);
+ /**
+ * @acquire_outputs:
+ *
+ * Called by vga-switcheroo. Informs the client that the outputs will
+ * be switched to its device. When @acquire_outputs runs, the outputs
+ * have not been switched yet. The client should only prepare the software
+ * state. After the switch happened, the client might get a hotplug
+ * event to update the hardware state.
+ *
+ * This callback exists for remapping framebuffers to virtual terminals
+ * in fbcon.
+ */
+ void (*acquire_outputs)(struct drm_client_dev *client);
+
/**
* @hotplug:
*
diff --git a/include/drm/drm_client_event.h b/include/drm/drm_client_event.h
index 79369c755bc9..c93f404bae1d 100644
--- a/include/drm/drm_client_event.h
+++ b/include/drm/drm_client_event.h
@@ -11,6 +11,7 @@ struct drm_device;
void drm_client_dev_unregister(struct drm_device *dev);
void drm_client_dev_hotplug(struct drm_device *dev);
void drm_client_dev_restore(struct drm_device *dev, bool force);
+void drm_client_dev_acquire_outputs(struct drm_device *dev);
void drm_client_dev_suspend(struct drm_device *dev);
void drm_client_dev_resume(struct drm_device *dev);
#else
@@ -20,6 +21,8 @@ static inline void drm_client_dev_hotplug(struct drm_device *dev)
{ }
static inline void drm_client_dev_restore(struct drm_device *dev, bool force)
{ }
+static inline void drm_client_dev_acquire_outputs(struct drm_device *dev)
+{ }
static inline void drm_client_dev_suspend(struct drm_device *dev)
{ }
static inline void drm_client_dev_resume(struct drm_device *dev)
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 3/7] vga_switcheroo: Add pre_switch callback to client ops
2026-07-09 9:15 [PATCH v2 0/7] vga_switcheroo, drm: Push fbcon handling into DRM clients Thomas Zimmermann
2026-07-09 9:15 ` [PATCH v2 1/7] drm/edid: Include <linux/fb.h> Thomas Zimmermann
2026-07-09 9:15 ` [PATCH v2 2/7] drm/client: Add acquire_outputs callback; implement for fbdev emulation Thomas Zimmermann
@ 2026-07-09 9:15 ` Thomas Zimmermann
2026-07-09 9:16 ` [PATCH v2 4/7] vga_switcheroo: Add post_switch " Thomas Zimmermann
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Thomas Zimmermann @ 2026-07-09 9:15 UTC (permalink / raw)
To: lukas, jfalempe, alexander.deucher, christian.koenig, airlied,
simona, maarten.lankhorst, mripard, jani.nikula, joonas.lahtinen,
rodrigo.vivi, tursulin, lyude, dakr, deller
Cc: dri-devel, amd-gfx, intel-gfx, nouveau, linux-fbdev,
sashiko-reviews, Thomas Zimmermann
Add pre_switch to struct vga_switcheroo_client_ops to inform the
switcheroo client about upcoming switches of the outputs.
This callback is intended to replace the hard-coded call to fbdev's
fb_switch_outputs(). With DRM supporting more clients than just fbdev
emulation, something more flexible is required.
v2:
- remove non-sensical gpu_bound documentation (Sashiko)
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/vga/vga_switcheroo.c | 4 +++-
include/linux/vga_switcheroo.h | 11 ++++++++---
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index 22cf52b78b75..bdf1e56ae891 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -733,8 +733,10 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client)
if (!active->driver_power_control)
set_audio_state(active->id, VGA_SWITCHEROO_OFF);
+ if (new_client->ops->pre_switch)
+ new_client->ops->pre_switch(new_client->pdev);
#if defined(CONFIG_FB)
- if (new_client->fb_info)
+ else if (new_client->fb_info)
fb_switch_outputs(new_client->fb_info);
#endif
diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h
index 7e6ac0114d55..4422daca9ceb 100644
--- a/include/linux/vga_switcheroo.h
+++ b/include/linux/vga_switcheroo.h
@@ -133,17 +133,22 @@ struct vga_switcheroo_handler {
* @can_switch: check if the device is in a position to switch now.
* Mandatory. The client should return false if a user space process
* has one of its device files open
+ * @pre_switch: prepare switch
+ * Optional. This gets called before switching the outputs to the
+ * GPU. Allows drivers to prepare for the switch.
* @gpu_bound: notify the client id to audio client when the GPU is bound.
*
* Client callbacks. A client can be either a GPU or an audio device on a GPU.
- * The @set_gpu_state and @can_switch methods are mandatory, @reprobe may be
- * set to NULL. For audio clients, the @reprobe member is bogus.
- * OTOH, @gpu_bound is only for audio clients, and not used for GPU clients.
+ * The @set_gpu_state and @can_switch methods are mandatory, @pre_switch and
+ * @reprobe may be set to NULL. For audio clients, the @pre_switch and
+ * @reprobe members are bogus. OTOH, @gpu_bound is only for audio clients,
+ * and not used for GPU clients.
*/
struct vga_switcheroo_client_ops {
void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state);
void (*reprobe)(struct pci_dev *dev);
bool (*can_switch)(struct pci_dev *dev);
+ void (*pre_switch)(struct pci_dev *dev);
void (*gpu_bound)(struct pci_dev *dev, enum vga_switcheroo_client_id);
};
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 4/7] vga_switcheroo: Add post_switch callback to client ops
2026-07-09 9:15 [PATCH v2 0/7] vga_switcheroo, drm: Push fbcon handling into DRM clients Thomas Zimmermann
` (2 preceding siblings ...)
2026-07-09 9:15 ` [PATCH v2 3/7] vga_switcheroo: Add pre_switch callback to client ops Thomas Zimmermann
@ 2026-07-09 9:16 ` Thomas Zimmermann
2026-07-09 9:16 ` [PATCH v2 5/7] drm: Implement struct vga_switcheroo_client_ops.pre_switch Thomas Zimmermann
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Thomas Zimmermann @ 2026-07-09 9:16 UTC (permalink / raw)
To: lukas, jfalempe, alexander.deucher, christian.koenig, airlied,
simona, maarten.lankhorst, mripard, jani.nikula, joonas.lahtinen,
rodrigo.vivi, tursulin, lyude, dakr, deller
Cc: dri-devel, amd-gfx, intel-gfx, nouveau, linux-fbdev,
sashiko-reviews, Thomas Zimmermann
Add post_switch to struct vga_switcheroo_client_ops to inform the
switcheroo client about a completed switch of the output.
This callback is intended to replace the reprobe client op. It is a
rename of reprobe for consistency with pre_switch.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/vga/vga_switcheroo.c | 4 +++-
include/linux/vga_switcheroo.h | 12 +++++++-----
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index bdf1e56ae891..7b0af4a8aa7d 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -746,7 +746,9 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client)
if (ret)
return ret;
- if (new_client->ops->reprobe)
+ if (new_client->ops->post_switch)
+ new_client->ops->post_switch(new_client->pdev);
+ else if (new_client->ops->reprobe)
new_client->ops->reprobe(new_client->pdev);
if (vga_switcheroo_pwr_state(active) == VGA_SWITCHEROO_ON)
diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h
index 4422daca9ceb..51851831d4c1 100644
--- a/include/linux/vga_switcheroo.h
+++ b/include/linux/vga_switcheroo.h
@@ -127,21 +127,22 @@ struct vga_switcheroo_handler {
* @set_gpu_state: do the equivalent of suspend/resume for the card.
* Mandatory. This should not cut power to the discrete GPU,
* which is the job of the handler
- * @reprobe: poll outputs.
- * Optional. This gets called after waking the GPU and switching
- * the outputs to it
+ * @reprobe: deprecated
* @can_switch: check if the device is in a position to switch now.
* Mandatory. The client should return false if a user space process
* has one of its device files open
* @pre_switch: prepare switch
* Optional. This gets called before switching the outputs to the
* GPU. Allows drivers to prepare for the switch.
+ * @post_switch: completes switch
+ * Optional. This gets called after waking the GPU and switching
+ * the outputs to it. Allows drivers to poll the switched outputs.
* @gpu_bound: notify the client id to audio client when the GPU is bound.
*
* Client callbacks. A client can be either a GPU or an audio device on a GPU.
* The @set_gpu_state and @can_switch methods are mandatory, @pre_switch and
- * @reprobe may be set to NULL. For audio clients, the @pre_switch and
- * @reprobe members are bogus. OTOH, @gpu_bound is only for audio clients,
+ * @post_switch may be set to NULL. For audio clients, the @pre_switch and
+ * @post_switch members are bogus. OTOH, @gpu_bound is only for audio clients,
* and not used for GPU clients.
*/
struct vga_switcheroo_client_ops {
@@ -149,6 +150,7 @@ struct vga_switcheroo_client_ops {
void (*reprobe)(struct pci_dev *dev);
bool (*can_switch)(struct pci_dev *dev);
void (*pre_switch)(struct pci_dev *dev);
+ void (*post_switch)(struct pci_dev *dev);
void (*gpu_bound)(struct pci_dev *dev, enum vga_switcheroo_client_id);
};
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 5/7] drm: Implement struct vga_switcheroo_client_ops.pre_switch
2026-07-09 9:15 [PATCH v2 0/7] vga_switcheroo, drm: Push fbcon handling into DRM clients Thomas Zimmermann
` (3 preceding siblings ...)
2026-07-09 9:16 ` [PATCH v2 4/7] vga_switcheroo: Add post_switch " Thomas Zimmermann
@ 2026-07-09 9:16 ` Thomas Zimmermann
2026-07-09 9:16 ` [PATCH v2 6/7] drm: Implement vga_switcheroo_client_ops.post_switch Thomas Zimmermann
2026-07-09 9:16 ` [PATCH v2 7/7] vga-switcheroo: Remove unused interfaces Thomas Zimmermann
6 siblings, 0 replies; 8+ messages in thread
From: Thomas Zimmermann @ 2026-07-09 9:16 UTC (permalink / raw)
To: lukas, jfalempe, alexander.deucher, christian.koenig, airlied,
simona, maarten.lankhorst, mripard, jani.nikula, joonas.lahtinen,
rodrigo.vivi, tursulin, lyude, dakr, deller
Cc: dri-devel, amd-gfx, intel-gfx, nouveau, linux-fbdev,
sashiko-reviews, Thomas Zimmermann
Call drm_client_dev_acquire_outputs() from vga_switcheroo's pre_switch
callback. Pushes fbcon updates from vga_switcheroo into DRM's fbdev
emulation. This affects amdgpu, i915, nouveau and radeon. No other
drivers implement vga_switcheroo.
Also remove the calls to vga_switcheroo_client_fb_set() from fbcon. It
is called from the DRM client's hotplug and sets the fbcon's framebuffer
at vga_switcheroo. Running pre_switch and hotplug concurrently could
result in a deadlock between clientlist_mutex and vgasr_mutex. Hence
clean up fbcon here as well.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Lyude Paul <lyude@redhat.com> # nouveau
---
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 8 ++++++++
drivers/gpu/drm/i915/i915_switcheroo.c | 10 ++++++++++
drivers/gpu/drm/nouveau/nouveau_vga.c | 9 +++++++++
drivers/gpu/drm/radeon/radeon_device.c | 8 ++++++++
drivers/video/fbdev/core/fbcon.c | 8 --------
5 files changed, 35 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 78c96c7102e4..87a59a79a019 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -1699,10 +1699,18 @@ static bool amdgpu_switcheroo_can_switch(struct pci_dev *pdev)
return atomic_read(&dev->open_count) == 0;
}
+static void amdgpu_switcheroo_pre_switch(struct pci_dev *pdev)
+{
+ struct drm_device *dev = pci_get_drvdata(pdev);
+
+ drm_client_dev_acquire_outputs(dev);
+}
+
static const struct vga_switcheroo_client_ops amdgpu_switcheroo_ops = {
.set_gpu_state = amdgpu_switcheroo_set_state,
.reprobe = NULL,
.can_switch = amdgpu_switcheroo_can_switch,
+ .pre_switch = amdgpu_switcheroo_pre_switch,
};
/**
diff --git a/drivers/gpu/drm/i915/i915_switcheroo.c b/drivers/gpu/drm/i915/i915_switcheroo.c
index 7e0791024282..6b306ece0556 100644
--- a/drivers/gpu/drm/i915/i915_switcheroo.c
+++ b/drivers/gpu/drm/i915/i915_switcheroo.c
@@ -5,6 +5,7 @@
#include <linux/vga_switcheroo.h>
+#include <drm/drm_client_event.h>
#include <drm/drm_print.h>
#include "display/intel_display_device.h"
@@ -58,10 +59,19 @@ static bool i915_switcheroo_can_switch(struct pci_dev *pdev)
atomic_read(&i915->drm.open_count) == 0;
}
+static void i915_switcheroo_pre_switch(struct pci_dev *pdev)
+{
+ struct drm_i915_private *i915 = pdev_to_i915(pdev);
+
+ if (i915 && intel_display_device_present(i915->display))
+ drm_client_dev_acquire_outputs(&i915->drm);
+}
+
static const struct vga_switcheroo_client_ops i915_switcheroo_ops = {
.set_gpu_state = i915_switcheroo_set_state,
.reprobe = NULL,
.can_switch = i915_switcheroo_can_switch,
+ .pre_switch = i915_switcheroo_pre_switch,
};
int i915_switcheroo_register(struct drm_i915_private *i915)
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c
index a6c375a24154..2d2d08be8fbe 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vga.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vga.c
@@ -76,11 +76,20 @@ nouveau_switcheroo_can_switch(struct pci_dev *pdev)
return atomic_read(&drm->dev->open_count) == 0;
}
+static void
+nouveau_switcheroo_pre_switch(struct pci_dev *pdev)
+{
+ struct nouveau_drm *drm = pci_get_drvdata(pdev);
+
+ drm_client_dev_acquire_outputs(drm->dev);
+}
+
static const struct vga_switcheroo_client_ops
nouveau_switcheroo_ops = {
.set_gpu_state = nouveau_switcheroo_set_state,
.reprobe = nouveau_switcheroo_reprobe,
.can_switch = nouveau_switcheroo_can_switch,
+ .pre_switch = nouveau_switcheroo_pre_switch,
};
void
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 705c012fcf9e..8697a9eb5d13 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1258,10 +1258,18 @@ static bool radeon_switcheroo_can_switch(struct pci_dev *pdev)
return atomic_read(&dev->open_count) == 0;
}
+static void radeon_switcheroo_pre_switch(struct pci_dev *pdev)
+{
+ struct drm_device *dev = pci_get_drvdata(pdev);
+
+ drm_client_dev_acquire_outputs(dev);
+}
+
static const struct vga_switcheroo_client_ops radeon_switcheroo_ops = {
.set_gpu_state = radeon_switcheroo_set_state,
.reprobe = NULL,
.can_switch = radeon_switcheroo_can_switch,
+ .pre_switch = radeon_switcheroo_pre_switch,
};
/**
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index 9f5c4c101581..974c7dcf5251 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -78,7 +78,6 @@
#include <linux/interrupt.h>
#include <linux/crc32.h> /* For counting font checksums */
#include <linux/uaccess.h>
-#include <linux/vga_switcheroo.h>
#include <asm/irq.h>
#include "fbcon.h"
@@ -2851,9 +2850,6 @@ void fbcon_fb_unregistered(struct fb_info *info)
console_lock();
- if (info->device && dev_is_pci(info->device))
- vga_switcheroo_client_fb_set(to_pci_dev(info->device), NULL);
-
fbcon_registered_fb[info->node] = NULL;
fbcon_num_registered_fb--;
@@ -2987,10 +2983,6 @@ static int do_fb_registered(struct fb_info *info)
}
}
- /* Set the fb info for vga_switcheroo clients. Does nothing otherwise. */
- if (info->device && dev_is_pci(info->device))
- vga_switcheroo_client_fb_set(to_pci_dev(info->device), info);
-
return ret;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 6/7] drm: Implement vga_switcheroo_client_ops.post_switch
2026-07-09 9:15 [PATCH v2 0/7] vga_switcheroo, drm: Push fbcon handling into DRM clients Thomas Zimmermann
` (4 preceding siblings ...)
2026-07-09 9:16 ` [PATCH v2 5/7] drm: Implement struct vga_switcheroo_client_ops.pre_switch Thomas Zimmermann
@ 2026-07-09 9:16 ` Thomas Zimmermann
2026-07-09 9:16 ` [PATCH v2 7/7] vga-switcheroo: Remove unused interfaces Thomas Zimmermann
6 siblings, 0 replies; 8+ messages in thread
From: Thomas Zimmermann @ 2026-07-09 9:16 UTC (permalink / raw)
To: lukas, jfalempe, alexander.deucher, christian.koenig, airlied,
simona, maarten.lankhorst, mripard, jani.nikula, joonas.lahtinen,
rodrigo.vivi, tursulin, lyude, dakr, deller
Cc: dri-devel, amd-gfx, intel-gfx, nouveau, linux-fbdev,
sashiko-reviews, Thomas Zimmermann
An output's attached display might have changed while a DRM client's
device did not have the output switched to it.
The nouveau_switcheroo_reprobe() callback sends a hotplug notice to
DRM's internal clients, so that they can reconfigure their display
output if necessary.
As post_switch callback replaces reprobe in vga_switcheroo, update
nouveau accordingly. In the other drivers, remove the NULL-assignment
to reprobe. No functional changes.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Lyude Paul <lyude@redhat.com> # nouveau
---
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 1 -
drivers/gpu/drm/i915/i915_switcheroo.c | 1 -
drivers/gpu/drm/nouveau/nouveau_vga.c | 19 +++++++++----------
drivers/gpu/drm/radeon/radeon_device.c | 1 -
4 files changed, 9 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 87a59a79a019..5bb1567d512d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -1708,7 +1708,6 @@ static void amdgpu_switcheroo_pre_switch(struct pci_dev *pdev)
static const struct vga_switcheroo_client_ops amdgpu_switcheroo_ops = {
.set_gpu_state = amdgpu_switcheroo_set_state,
- .reprobe = NULL,
.can_switch = amdgpu_switcheroo_can_switch,
.pre_switch = amdgpu_switcheroo_pre_switch,
};
diff --git a/drivers/gpu/drm/i915/i915_switcheroo.c b/drivers/gpu/drm/i915/i915_switcheroo.c
index 6b306ece0556..d97057722fde 100644
--- a/drivers/gpu/drm/i915/i915_switcheroo.c
+++ b/drivers/gpu/drm/i915/i915_switcheroo.c
@@ -69,7 +69,6 @@ static void i915_switcheroo_pre_switch(struct pci_dev *pdev)
static const struct vga_switcheroo_client_ops i915_switcheroo_ops = {
.set_gpu_state = i915_switcheroo_set_state,
- .reprobe = NULL,
.can_switch = i915_switcheroo_can_switch,
.pre_switch = i915_switcheroo_pre_switch,
};
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c
index 2d2d08be8fbe..29a801124e56 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vga.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vga.c
@@ -54,15 +54,6 @@ nouveau_switcheroo_set_state(struct pci_dev *pdev,
}
}
-static void
-nouveau_switcheroo_reprobe(struct pci_dev *pdev)
-{
- struct nouveau_drm *drm = pci_get_drvdata(pdev);
- struct drm_device *dev = drm->dev;
-
- drm_client_dev_hotplug(dev);
-}
-
static bool
nouveau_switcheroo_can_switch(struct pci_dev *pdev)
{
@@ -84,12 +75,20 @@ nouveau_switcheroo_pre_switch(struct pci_dev *pdev)
drm_client_dev_acquire_outputs(drm->dev);
}
+static void
+nouveau_switcheroo_post_switch(struct pci_dev *pdev)
+{
+ struct nouveau_drm *drm = pci_get_drvdata(pdev);
+
+ drm_client_dev_hotplug(drm->dev);
+}
+
static const struct vga_switcheroo_client_ops
nouveau_switcheroo_ops = {
.set_gpu_state = nouveau_switcheroo_set_state,
- .reprobe = nouveau_switcheroo_reprobe,
.can_switch = nouveau_switcheroo_can_switch,
.pre_switch = nouveau_switcheroo_pre_switch,
+ .post_switch = nouveau_switcheroo_post_switch,
};
void
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 8697a9eb5d13..9523240110a6 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1267,7 +1267,6 @@ static void radeon_switcheroo_pre_switch(struct pci_dev *pdev)
static const struct vga_switcheroo_client_ops radeon_switcheroo_ops = {
.set_gpu_state = radeon_switcheroo_set_state,
- .reprobe = NULL,
.can_switch = radeon_switcheroo_can_switch,
.pre_switch = radeon_switcheroo_pre_switch,
};
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 7/7] vga-switcheroo: Remove unused interfaces
2026-07-09 9:15 [PATCH v2 0/7] vga_switcheroo, drm: Push fbcon handling into DRM clients Thomas Zimmermann
` (5 preceding siblings ...)
2026-07-09 9:16 ` [PATCH v2 6/7] drm: Implement vga_switcheroo_client_ops.post_switch Thomas Zimmermann
@ 2026-07-09 9:16 ` Thomas Zimmermann
6 siblings, 0 replies; 8+ messages in thread
From: Thomas Zimmermann @ 2026-07-09 9:16 UTC (permalink / raw)
To: lukas, jfalempe, alexander.deucher, christian.koenig, airlied,
simona, maarten.lankhorst, mripard, jani.nikula, joonas.lahtinen,
rodrigo.vivi, tursulin, lyude, dakr, deller
Cc: dri-devel, amd-gfx, intel-gfx, nouveau, linux-fbdev,
sashiko-reviews, Thomas Zimmermann
Remove all unused interfaces that used to implement the functionality
of pre_switch and post_switch.
For pre_switch, remove vga_switcheroo_client_fb_set(). This further
allows for removing all symbols and data structures that refer to
fbdev; such as fb_info and fb_switch_outputs().
For post_switch, remove the reprobe callback from the client ops.
v2:
- move changes to fbcon and drivers into other patches
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/vga/vga_switcheroo.c | 37 ++++----------------------------
include/linux/vga_switcheroo.h | 12 +++++------
2 files changed, 9 insertions(+), 40 deletions(-)
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index 7b0af4a8aa7d..9431d83b38a9 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -32,9 +32,9 @@
#include <linux/apple-gmux.h>
#include <linux/debugfs.h>
-#include <linux/fb.h>
+#include <linux/export.h>
#include <linux/fs.h>
-#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
@@ -90,7 +90,6 @@
/**
* struct vga_switcheroo_client - registered client
* @pdev: client pci device
- * @fb_info: framebuffer to which console is remapped on switching
* @pwr_state: current power state if manual power control is used.
* For driver power control, call vga_switcheroo_pwr_state().
* @ops: client callbacks
@@ -105,12 +104,11 @@
* @vga_dev: pci device, indicate which GPU is bound to current audio client
*
* Registered client. A client can be either a GPU or an audio device on a GPU.
- * For audio clients, the @fb_info and @active members are bogus. For GPU
- * clients, the @vga_dev is bogus.
+ * For audio clients, the @active member is bogus. For GPU clients, the @vga_dev
+ * is bogus.
*/
struct vga_switcheroo_client {
struct pci_dev *pdev;
- struct fb_info *fb_info;
enum vga_switcheroo_state pwr_state;
const struct vga_switcheroo_client_ops *ops;
enum vga_switcheroo_client_id id;
@@ -514,27 +512,6 @@ void vga_switcheroo_unregister_client(struct pci_dev *pdev)
}
EXPORT_SYMBOL(vga_switcheroo_unregister_client);
-/**
- * vga_switcheroo_client_fb_set() - set framebuffer of a given client
- * @pdev: client pci device
- * @info: framebuffer
- *
- * Set framebuffer of a given client. The console will be remapped to this
- * on switching.
- */
-void vga_switcheroo_client_fb_set(struct pci_dev *pdev,
- struct fb_info *info)
-{
- struct vga_switcheroo_client *client;
-
- mutex_lock(&vgasr_mutex);
- client = find_client_from_pci(&vgasr_priv.clients, pdev);
- if (client)
- client->fb_info = info;
- mutex_unlock(&vgasr_mutex);
-}
-EXPORT_SYMBOL(vga_switcheroo_client_fb_set);
-
/**
* vga_switcheroo_lock_ddc() - temporarily switch DDC lines to a given client
* @pdev: client pci device
@@ -735,10 +712,6 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client)
if (new_client->ops->pre_switch)
new_client->ops->pre_switch(new_client->pdev);
-#if defined(CONFIG_FB)
- else if (new_client->fb_info)
- fb_switch_outputs(new_client->fb_info);
-#endif
mutex_lock(&vgasr_priv.mux_hw_lock);
ret = vgasr_priv.handler->switchto(new_client->id);
@@ -748,8 +721,6 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client)
if (new_client->ops->post_switch)
new_client->ops->post_switch(new_client->pdev);
- else if (new_client->ops->reprobe)
- new_client->ops->reprobe(new_client->pdev);
if (vga_switcheroo_pwr_state(active) == VGA_SWITCHEROO_ON)
vga_switchoff(active);
diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h
index 51851831d4c1..d7eee65664ab 100644
--- a/include/linux/vga_switcheroo.h
+++ b/include/linux/vga_switcheroo.h
@@ -31,8 +31,12 @@
#ifndef _LINUX_VGA_SWITCHEROO_H_
#define _LINUX_VGA_SWITCHEROO_H_
-#include <linux/fb.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+struct device;
+struct dev_pm_domain;
+struct fb_info;
struct pci_dev;
/**
@@ -127,7 +131,6 @@ struct vga_switcheroo_handler {
* @set_gpu_state: do the equivalent of suspend/resume for the card.
* Mandatory. This should not cut power to the discrete GPU,
* which is the job of the handler
- * @reprobe: deprecated
* @can_switch: check if the device is in a position to switch now.
* Mandatory. The client should return false if a user space process
* has one of its device files open
@@ -147,7 +150,6 @@ struct vga_switcheroo_handler {
*/
struct vga_switcheroo_client_ops {
void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state);
- void (*reprobe)(struct pci_dev *dev);
bool (*can_switch)(struct pci_dev *dev);
void (*pre_switch)(struct pci_dev *dev);
void (*post_switch)(struct pci_dev *dev);
@@ -163,9 +165,6 @@ int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
const struct vga_switcheroo_client_ops *ops,
struct pci_dev *vga_dev);
-void vga_switcheroo_client_fb_set(struct pci_dev *dev,
- struct fb_info *info);
-
int vga_switcheroo_register_handler(const struct vga_switcheroo_handler *handler,
enum vga_switcheroo_handler_flags_t handler_flags);
void vga_switcheroo_unregister_handler(void);
@@ -185,7 +184,6 @@ void vga_switcheroo_fini_domain_pm_ops(struct device *dev);
static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {}
static inline int vga_switcheroo_register_client(struct pci_dev *dev,
const struct vga_switcheroo_client_ops *ops, bool driver_power_control) { return 0; }
-static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {}
static inline int vga_switcheroo_register_handler(const struct vga_switcheroo_handler *handler,
enum vga_switcheroo_handler_flags_t handler_flags) { return 0; }
static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread