diff --git a/drivers/gpu/drm/nouveau/include/nvif/driverif.h b/drivers/gpu/drm/nouveau/include/nvif/driverif.h
index e1d3ccc2128c..49db239f1156 100644
--- a/drivers/gpu/drm/nouveau/include/nvif/driverif.h
+++ b/drivers/gpu/drm/nouveau/include/nvif/driverif.h
@@ -2,6 +2,7 @@
 #ifndef __NVIF_DRIVERIF_H__
 #define __NVIF_DRIVERIF_H__
 #include <drm/display/drm_dp.h>
+#include <linux/vga_switcheroo.h>
 
 struct nvif_event_priv;
 struct nvif_client_priv;
@@ -36,6 +37,12 @@ struct nvif_event_impl {
 
 struct nvif_driver_func {
 	enum nvif_event_stat (*event)(u64 token, void *repv, u32 repc);
+
+	struct nvif_driver_func_switcheroo {
+		bool (*can_switch)(const struct nvif_driver_func *);
+		void (*set_state)(const struct nvif_driver_func *, enum vga_switcheroo_state);
+		void (*reprobe)(const struct nvif_driver_func *);
+	} switcheroo;
 };
 
 struct nvif_driver {
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/pci.h b/drivers/gpu/drm/nouveau/include/nvkm/core/pci.h
index 7444c4d59e09..a1e9ee6da44e 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/core/pci.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/core/pci.h
@@ -7,6 +7,8 @@ struct nvkm_device_pci {
 	struct nvkm_device device;
 	struct pci_dev *pdev;
 	bool suspend;
+
+	struct dev_pm_domain vga_pm_domain;
 };
 
 int nvkm_device_pci_new(struct pci_dev *, const char *cfg, const char *dbg,
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index b9c43fe552c0..da3120258527 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -8,14 +8,6 @@
 #include "nouveau_drv.h"
 #include "nouveau_acpi.h"
 
-bool nouveau_is_optimus(void) {
-	return nouveau_dsm_priv.optimus_detected;
-}
-
-bool nouveau_is_v1_dsm(void) {
-	return nouveau_dsm_priv.dsm_detected;
-}
-
 void *
 nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
 {
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.h b/drivers/gpu/drm/nouveau/nouveau_acpi.h
index a7fac1f7a73d..be1b218cb921 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.h
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.h
@@ -5,16 +5,12 @@
 #define ROM_BIOS_PAGE 4096
 
 #if defined(CONFIG_ACPI) && defined(CONFIG_X86)
-bool nouveau_is_optimus(void);
-bool nouveau_is_v1_dsm(void);
 #include <device/acpi.h>
 static inline void nouveau_switcheroo_optimus_dsm(void) { nvkm_acpi_switcheroo_set_powerdown(); }
 void *nouveau_acpi_edid(struct drm_device *, struct drm_connector *);
 bool nouveau_acpi_video_backlight_use_native(void);
 void nouveau_acpi_video_register_backlight(void);
 #else
-static inline bool nouveau_is_optimus(void) { return false; };
-static inline bool nouveau_is_v1_dsm(void) { return false; };
 static inline void nouveau_switcheroo_optimus_dsm(void) {}
 static inline void *nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) { return NULL; }
 static inline bool nouveau_acpi_video_backlight_use_native(void) { return true; }
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 52c4c3f58799..580849b78231 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -505,7 +505,6 @@ nouveau_drm_device_fini(struct nouveau_drm *drm)
 	nouveau_bios_takedown(dev);
 
 	nouveau_ttm_fini(drm);
-	nouveau_vga_fini(drm);
 
 	/*
 	 * There may be existing clients from as-yet unclosed files. For now,
@@ -556,8 +555,6 @@ nouveau_drm_device_init(struct nouveau_drm *drm)
 	if (drm->device.impl->chipset == 0xc1)
 		nvif_mask(&drm->device, 0x00088080, 0x00000800, 0x00000000);
 
-	nouveau_vga_init(drm);
-
 	ret = nouveau_ttm_init(drm);
 	if (ret)
 		goto fail_ttm;
@@ -608,7 +605,6 @@ nouveau_drm_device_init(struct nouveau_drm *drm)
 fail_bios:
 	nouveau_ttm_fini(drm);
 fail_ttm:
-	nouveau_vga_fini(drm);
 	nouveau_cli_fini(&drm->cli);
 fail_wq:
 	destroy_workqueue(drm->sched_wq);
@@ -650,6 +646,7 @@ nouveau_drm_device_new(const struct drm_driver *drm_driver, struct device *paren
 
 	drm->nvkm = device;
 	drm->driver = nouveau_driver;
+	drm->driver.switcheroo = nouveau_switcheroo;
 
 	device->cfgopt = nouveau_config;
 	device->dbgopt = nouveau_debug;
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index c2014a29891c..b44f0d408ccc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -295,8 +295,6 @@ struct nouveau_drm {
 	/* led management */
 	struct nouveau_led *led;
 
-	struct dev_pm_domain vga_pm_domain;
-
 	struct nouveau_svm *svm;
 
 	struct nouveau_dmem *dmem;
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c
index 1f5ccec025d7..cb5cd4b93fdf 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vga.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vga.c
@@ -8,41 +8,35 @@
 #include "nouveau_vga.h"
 
 static void
-nouveau_switcheroo_set_state(struct pci_dev *pdev,
-			     enum vga_switcheroo_state state)
+nouveau_switcheroo_set_state(const struct nvif_driver_func *driver, enum vga_switcheroo_state state)
 {
-	struct nouveau_drm *drm = pci_get_drvdata(pdev);
-	struct drm_device *dev = drm->dev;
-
-	if ((nouveau_is_optimus() || nouveau_is_v1_dsm()) && state == VGA_SWITCHEROO_OFF)
-		return;
+	struct drm_device *dev = container_of(driver, struct nouveau_drm, driver)->dev;
 
 	if (state == VGA_SWITCHEROO_ON) {
 		pr_err("VGA switcheroo: switched nouveau on\n");
 		dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
-		nouveau_pmops_resume(&pdev->dev);
+		nouveau_pmops_resume(dev->dev);
 		dev->switch_power_state = DRM_SWITCH_POWER_ON;
 	} else {
 		pr_err("VGA switcheroo: switched nouveau off\n");
 		dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
-		nouveau_switcheroo_optimus_dsm();
-		nouveau_pmops_suspend(&pdev->dev);
+		nouveau_pmops_suspend(dev->dev);
 		dev->switch_power_state = DRM_SWITCH_POWER_OFF;
 	}
 }
 
 static void
-nouveau_switcheroo_reprobe(struct pci_dev *pdev)
+nouveau_switcheroo_reprobe(const struct nvif_driver_func *driver)
 {
-	struct nouveau_drm *drm = pci_get_drvdata(pdev);
+	struct nouveau_drm *drm = container_of(driver, struct nouveau_drm, driver);
 
 	drm_fb_helper_output_poll_changed(drm->dev);
 }
 
 static bool
-nouveau_switcheroo_can_switch(struct pci_dev *pdev)
+nouveau_switcheroo_can_switch(const struct nvif_driver_func *driver)
 {
-	struct nouveau_drm *drm = pci_get_drvdata(pdev);
+	struct nouveau_drm *drm = container_of(driver, struct nouveau_drm, driver);
 
 	/*
 	 * FIXME: open_count is protected by drm_global_mutex but that would lead to
@@ -52,56 +46,13 @@ nouveau_switcheroo_can_switch(struct pci_dev *pdev)
 	return atomic_read(&drm->dev->open_count) == 0;
 }
 
-static const struct vga_switcheroo_client_ops
-nouveau_switcheroo_ops = {
-	.set_gpu_state = nouveau_switcheroo_set_state,
+const struct nvif_driver_func_switcheroo
+nouveau_switcheroo = {
+	.set_state = nouveau_switcheroo_set_state,
 	.reprobe = nouveau_switcheroo_reprobe,
 	.can_switch = nouveau_switcheroo_can_switch,
 };
 
-void
-nouveau_vga_init(struct nouveau_drm *drm)
-{
-	struct drm_device *dev = drm->dev;
-	bool runtime = nouveau_pmops_runtime(dev->dev);
-	struct pci_dev *pdev;
-
-	/* only relevant for PCI devices */
-	if (!dev_is_pci(dev->dev))
-		return;
-	pdev = to_pci_dev(dev->dev);
-
-	/* don't register Thunderbolt eGPU with vga_switcheroo */
-	if (pci_is_thunderbolt_attached(pdev))
-		return;
-
-	vga_switcheroo_register_client(pdev, &nouveau_switcheroo_ops, runtime);
-
-	if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus())
-		vga_switcheroo_init_domain_pm_ops(drm->dev->dev, &drm->vga_pm_domain);
-}
-
-void
-nouveau_vga_fini(struct nouveau_drm *drm)
-{
-	struct drm_device *dev = drm->dev;
-	bool runtime = nouveau_pmops_runtime(dev->dev);
-	struct pci_dev *pdev;
-
-	/* only relevant for PCI devices */
-	if (!dev_is_pci(dev->dev))
-		return;
-	pdev = to_pci_dev(dev->dev);
-
-	if (pci_is_thunderbolt_attached(pdev))
-		return;
-
-	vga_switcheroo_unregister_client(pdev);
-	if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus())
-		vga_switcheroo_fini_domain_pm_ops(drm->dev->dev);
-}
-
-
 void
 nouveau_vga_lastclose(struct drm_device *dev)
 {
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.h b/drivers/gpu/drm/nouveau/nouveau_vga.h
index 951a83f984dd..119b39cfbbcc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vga.h
+++ b/drivers/gpu/drm/nouveau/nouveau_vga.h
@@ -2,8 +2,7 @@
 #ifndef __NOUVEAU_VGA_H__
 #define __NOUVEAU_VGA_H__
 
-void nouveau_vga_init(struct nouveau_drm *);
-void nouveau_vga_fini(struct nouveau_drm *);
+extern const struct nvif_driver_func_switcheroo nouveau_switcheroo;
 void nouveau_vga_lastclose(struct drm_device *dev);
 
 #endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/device/acpi.c b/drivers/gpu/drm/nouveau/nvkm/device/acpi.c
index 2bead7e879a5..cbaad3ea10eb 100644
--- a/drivers/gpu/drm/nouveau/nvkm/device/acpi.c
+++ b/drivers/gpu/drm/nouveau/nvkm/device/acpi.c
@@ -23,9 +23,11 @@
  */
 #include "acpi.h"
 
-#include <core/device.h>
+#include <core/pci.h>
 #include <subdev/clk.h>
 
+#include <nvif/driverif.h>
+
 #include <linux/mxm-wmi.h>
 #include <linux/vga_switcheroo.h>
 
@@ -189,6 +191,48 @@ static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switchero
 	return 0;
 }
 
+#include "nouveau_drv.h"
+#include "nouveau_acpi.h"
+
+static void
+nvkm_acpi_switcheroo_reprobe(struct pci_dev *pdev)
+{
+	struct nvkm_device *device = ((struct nouveau_drm *)pci_get_drvdata(pdev))->nvkm;
+
+	device->driver->switcheroo.reprobe(device->driver);
+}
+
+static void
+nvkm_acpi_switcheroo_set_state(struct pci_dev *pdev,
+			     enum vga_switcheroo_state state)
+{
+	struct nvkm_device *device = ((struct nouveau_drm *)pci_get_drvdata(pdev))->nvkm;
+
+	if (state == VGA_SWITCHEROO_OFF) {
+		if (nouveau_dsm_priv.dsm_detected || nouveau_dsm_priv.optimus_detected)
+			return;
+
+		nvkm_acpi_switcheroo_set_powerdown();
+	}
+
+	device->driver->switcheroo.set_state(device->driver, state);
+}
+
+static bool
+nvkm_acpi_switcheroo_can_switch(struct pci_dev *pdev)
+{
+	struct nvkm_device *device = ((struct nouveau_drm *)pci_get_drvdata(pdev))->nvkm;
+
+	return device->driver->switcheroo.can_switch(device->driver);
+}
+
+static const struct vga_switcheroo_client_ops
+nvkm_acpi_switcheroo_ops = {
+	.can_switch = nvkm_acpi_switcheroo_can_switch,
+	.set_gpu_state = nvkm_acpi_switcheroo_set_state,
+	.reprobe = nvkm_acpi_switcheroo_reprobe,
+};
+
 static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id)
 {
 	if (!nouveau_dsm_priv.dsm_detected)
@@ -380,16 +424,53 @@ nvkm_acpi_ntfy(struct notifier_block *nb, unsigned long val, void *data)
 void
 nvkm_acpi_fini(struct nvkm_device *device)
 {
+	struct nvkm_device_pci *pdev;
+
+	if (!device->func->pci)
+		return;
+
+	pdev = device->func->pci(device);
+	(void)pdev;
+
 #ifdef CONFIG_ACPI
 	unregister_acpi_notifier(&device->acpi.nb);
 #endif
+
+#ifdef CONFIG_VGA_SWITCHEROO
+	if (pci_is_thunderbolt_attached(pdev->pdev))
+		return;
+
+	vga_switcheroo_unregister_client(pdev->pdev);
+	if (device->runpm == NVKM_DEVICE_RUNPM_V1)
+		vga_switcheroo_fini_domain_pm_ops(device->dev);
+#endif
 }
 
 void
 nvkm_acpi_init(struct nvkm_device *device)
 {
+	struct nvkm_device_pci *pdev;
+
+	if (!device->func->pci)
+		return;
+
+	pdev = device->func->pci(device);
+	(void)pdev;
+
 #ifdef CONFIG_ACPI
 	device->acpi.nb.notifier_call = nvkm_acpi_ntfy;
 	register_acpi_notifier(&device->acpi.nb);
 #endif
+
+#ifdef CONFIG_VGA_SWITCHEROO
+	/* don't register Thunderbolt eGPU with vga_switcheroo */
+	if (pci_is_thunderbolt_attached(pdev->pdev))
+		return;
+
+	vga_switcheroo_register_client(pdev->pdev, &nvkm_acpi_switcheroo_ops,
+				       device->runpm != NVKM_DEVICE_RUNPM_NONE);
+
+	if (device->runpm == NVKM_DEVICE_RUNPM_V1)
+		vga_switcheroo_init_domain_pm_ops(device->dev, &pdev->vga_pm_domain);
+#endif
 }
