diff mbox series

[19/21] drm/nouveau: wrap pm_runtime_* calls with nouveau_runpm

Message ID 20240613170046.88687-20-bskeggs@nvidia.com (mailing list archive)
State New, archived
Headers show
Series drm/nouveau: insert auxiliary device between nvkm and drm | expand

Commit Message

Ben Skeggs June 13, 2024, 5 p.m. UTC
This wraps direct calls to pm_runtime functions to cleanup some common
usage patterns, and keep the logic in one place.

From the next commit, the pm_runtime calls will need to operate against
the DRM driver's auxiliary device instead of directly on the underlying
PCI device.  This commit will help make that switch cleaner.

Signed-off-by: Ben Skeggs <bskeggs@nvidia.com>
---
 drivers/gpu/drm/nouveau/dispnv50/disp.c     | 31 ++++---
 drivers/gpu/drm/nouveau/nouveau_connector.c | 12 ++-
 drivers/gpu/drm/nouveau/nouveau_connector.h |  1 +
 drivers/gpu/drm/nouveau/nouveau_debugfs.c   | 24 +++---
 drivers/gpu/drm/nouveau/nouveau_display.c   | 19 ++---
 drivers/gpu/drm/nouveau/nouveau_drm.c       | 45 ++++-------
 drivers/gpu/drm/nouveau/nouveau_gem.c       | 30 +++----
 drivers/gpu/drm/nouveau/nouveau_runpm.h     | 89 +++++++++++++++++++++
 8 files changed, 156 insertions(+), 95 deletions(-)
 create mode 100644 drivers/gpu/drm/nouveau/nouveau_runpm.h
diff mbox series

Patch

diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c
index 64f3a1b00173..c0fc5233ebd4 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
@@ -952,17 +952,16 @@  static int
 nv50_mstc_detect(struct drm_connector *connector,
 		 struct drm_modeset_acquire_ctx *ctx, bool force)
 {
+	struct nouveau_drm *drm = nouveau_drm(connector->dev);
 	struct nv50_mstc *mstc = nv50_mstc(connector);
 	int ret;
 
 	if (drm_connector_is_unregistered(connector))
 		return connector_status_disconnected;
 
-	ret = pm_runtime_get_sync(connector->dev->dev);
-	if (ret < 0 && ret != -EACCES) {
-		pm_runtime_put_autosuspend(connector->dev->dev);
+	ret = nouveau_runpm_get(drm);
+	if (ret)
 		return connector_status_disconnected;
-	}
 
 	ret = drm_dp_mst_detect_port(connector, ctx, mstc->port->mgr,
 				     mstc->port);
@@ -970,8 +969,7 @@  nv50_mstc_detect(struct drm_connector *connector,
 		goto out;
 
 out:
-	pm_runtime_mark_last_busy(connector->dev->dev);
-	pm_runtime_put_autosuspend(connector->dev->dev);
+	nouveau_runpm_put(drm);
 	return ret;
 }
 
@@ -1950,7 +1948,7 @@  nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
 			  asyh->clr.mask, asyh->set.mask);
 
 		if (old_crtc_state->active && !new_crtc_state->active) {
-			pm_runtime_put_noidle(dev->dev);
+			nouveau_runpm_put_noidle(drm);
 			drm_crtc_vblank_off(crtc);
 		}
 
@@ -2040,7 +2038,7 @@  nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
 		if (new_crtc_state->active) {
 			if (!old_crtc_state->active) {
 				drm_crtc_vblank_on(crtc);
-				pm_runtime_get_noresume(dev->dev);
+				nouveau_runpm_get_noresume(drm);
 			}
 			if (new_crtc_state->event)
 				drm_crtc_vblank_get(crtc);
@@ -2159,8 +2157,7 @@  nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
 	drm_atomic_state_put(state);
 
 	/* Drop the RPM ref we got from nv50_disp_atomic_commit() */
-	pm_runtime_mark_last_busy(dev->dev);
-	pm_runtime_put_autosuspend(dev->dev);
+	nouveau_runpm_put(drm);
 }
 
 static void
@@ -2175,15 +2172,14 @@  static int
 nv50_disp_atomic_commit(struct drm_device *dev,
 			struct drm_atomic_state *state, bool nonblock)
 {
+	struct nouveau_drm *drm = nouveau_drm(dev);
 	struct drm_plane_state *new_plane_state;
 	struct drm_plane *plane;
 	int ret, i;
 
-	ret = pm_runtime_get_sync(dev->dev);
-	if (ret < 0 && ret != -EACCES) {
-		pm_runtime_put_autosuspend(dev->dev);
+	ret = nouveau_runpm_get(drm);
+	if (ret)
 		return ret;
-	}
 
 	ret = drm_atomic_helper_setup_commit(state, nonblock);
 	if (ret)
@@ -2219,7 +2215,7 @@  nv50_disp_atomic_commit(struct drm_device *dev,
 	 * Grab another RPM ref for the commit tail, which will release the
 	 * ref when it's finished
 	 */
-	pm_runtime_get_noresume(dev->dev);
+	nouveau_runpm_get_noresume(drm);
 
 	if (nonblock)
 		queue_work(system_unbound_wq, &state->commit_work);
@@ -2230,7 +2226,7 @@  nv50_disp_atomic_commit(struct drm_device *dev,
 	if (ret)
 		drm_atomic_helper_unprepare_planes(dev, state);
 done:
-	pm_runtime_put_autosuspend(dev->dev);
+	nouveau_runpm_put(drm);
 	return ret;
 }
 
@@ -2439,6 +2435,7 @@  static inline void
 nv50_display_read_hw_or_state(struct drm_device *dev, struct nv50_disp *disp,
 			      struct nouveau_encoder *outp)
 {
+	struct nouveau_drm *drm = nouveau_drm(dev);
 	struct drm_crtc *crtc;
 	struct drm_connector_list_iter conn_iter;
 	struct drm_connector *conn;
@@ -2501,7 +2498,7 @@  nv50_display_read_hw_or_state(struct drm_device *dev, struct nv50_disp *disp,
 	armh->state.connector_mask = drm_connector_mask(conn);
 	armh->state.active = true;
 	armh->state.enable = true;
-	pm_runtime_get_noresume(dev->dev);
+	nouveau_runpm_get_noresume(drm);
 
 	outp->crtc = crtc;
 	outp->ctrl = NVVAL(NV507D, SOR_SET_CONTROL, PROTOCOL, proto) | BIT(crtc->index);
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index e3071ea845e6..c5386166b15d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -26,7 +26,6 @@ 
 
 #include <acpi/button.h>
 
-#include <linux/pm_runtime.h>
 #include <linux/vga_switcheroo.h>
 
 #include <drm/drm_atomic_helper.h>
@@ -579,11 +578,11 @@  nouveau_connector_detect(struct drm_connector *connector, bool force)
 	 * if possible.
 	 */
 	if (drm_kms_helper_is_poll_worker()) {
-		pm_runtime_get_noresume(dev->dev);
+		nouveau_runpm_get_noresume(drm);
 	} else {
-		ret = pm_runtime_get_sync(dev->dev);
-		if (ret < 0 && ret != -EACCES) {
-			pm_runtime_put_autosuspend(dev->dev);
+		ret = nouveau_runpm_get(drm);
+		if (ret) {
+			nouveau_runpm_put(drm);
 			nouveau_connector_set_edid(nv_connector, NULL);
 			return conn_status;
 		}
@@ -674,8 +673,7 @@  nouveau_connector_detect(struct drm_connector *connector, bool force)
 	if (!nv_connector->edid)
 		drm_dp_cec_unset_edid(&nv_connector->aux);
 
-	pm_runtime_mark_last_busy(dev->dev);
-	pm_runtime_put_autosuspend(dev->dev);
+	nouveau_runpm_put(drm);
 
 	return conn_status;
 }
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
index 0608cabed058..d2035855861d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.h
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
@@ -40,6 +40,7 @@ 
 
 #include "nouveau_crtc.h"
 #include "nouveau_encoder.h"
+#include "nouveau_runpm.h"
 
 struct nvkm_i2c_port;
 struct dcb_output;
diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
index aff6ee476f65..4c0e122ba724 100644
--- a/drivers/gpu/drm/nouveau/nouveau_debugfs.c
+++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
@@ -31,6 +31,7 @@ 
 #include <linux/debugfs.h>
 #include "nouveau_debugfs.h"
 #include "nouveau_drv.h"
+#include "nouveau_runpm.h"
 
 static int
 nouveau_debugfs_vbios_image(struct seq_file *m, void *data)
@@ -51,18 +52,14 @@  nouveau_debugfs_strap_peek(struct seq_file *m, void *data)
 	struct nouveau_drm *drm = nouveau_drm(node->minor->dev);
 	int ret;
 
-	ret = pm_runtime_get_sync(drm->dev->dev);
-	if (ret < 0 && ret != -EACCES) {
-		pm_runtime_put_autosuspend(drm->dev->dev);
+	ret = nouveau_runpm_get(drm);
+	if (ret)
 		return ret;
-	}
 
 	seq_printf(m, "0x%08x\n",
 		   nvif_rd32(&drm->device, 0x101000));
 
-	pm_runtime_mark_last_busy(drm->dev->dev);
-	pm_runtime_put_autosuspend(drm->dev->dev);
-
+	nouveau_runpm_put(drm);
 	return 0;
 }
 
@@ -135,8 +132,9 @@  nouveau_debugfs_pstate_set(struct file *file, const char __user *ubuf,
 			   size_t len, loff_t *offp)
 {
 	struct seq_file *m = file->private_data;
-	struct drm_device *drm = m->private;
-	struct nouveau_debugfs *debugfs = nouveau_debugfs(drm);
+	struct drm_device *dev = m->private;
+	struct nouveau_debugfs *debugfs = nouveau_debugfs(dev);
+	struct nouveau_drm *drm = nouveau_drm(dev);
 	struct nvif_control_pstate_user args = { .pwrsrc = -EINVAL };
 	char buf[32] = {}, *tmp, *cur = buf;
 	long value, ret;
@@ -174,14 +172,12 @@  nouveau_debugfs_pstate_set(struct file *file, const char __user *ubuf,
 		args.ustate = value;
 	}
 
-	ret = pm_runtime_get_sync(drm->dev);
-	if (ret < 0 && ret != -EACCES) {
-		pm_runtime_put_autosuspend(drm->dev);
+	ret = nouveau_runpm_get(drm);
+	if (ret)
 		return ret;
-	}
 
 	ret = debugfs->impl->pstate.user(debugfs->priv, &args);
-	pm_runtime_put_autosuspend(drm->dev);
+	nouveau_runpm_put(drm);
 	if (ret < 0)
 		return ret;
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index e892f5f5ea16..57d31a17ad68 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -466,7 +466,7 @@  nouveau_display_hpd_work(struct work_struct *work)
 	int changed = 0;
 	struct drm_connector *first_changed_connector = NULL;
 
-	pm_runtime_get_sync(dev->dev);
+	nouveau_runpm_try_get(drm);
 
 	spin_lock_irq(&drm->hpd_lock);
 	pending = drm->hpd_pending;
@@ -474,8 +474,10 @@  nouveau_display_hpd_work(struct work_struct *work)
 	spin_unlock_irq(&drm->hpd_lock);
 
 	/* Nothing to do, exit early without updating the last busy counter */
-	if (!pending)
-		goto noop;
+	if (!pending) {
+		nouveau_runpm_put_noop(drm);
+		return;
+	}
 
 	mutex_lock(&dev->mode_config.mutex);
 	drm_connector_list_iter_begin(dev, &conn_iter);
@@ -532,9 +534,7 @@  nouveau_display_hpd_work(struct work_struct *work)
 	if (first_changed_connector)
 		drm_connector_put(first_changed_connector);
 
-	pm_runtime_mark_last_busy(drm->dev->dev);
-noop:
-	pm_runtime_put_autosuspend(dev->dev);
+	nouveau_runpm_put(drm);
 }
 
 #ifdef CONFIG_ACPI
@@ -545,24 +545,25 @@  nouveau_display_acpi_ntfy(struct notifier_block *nb, unsigned long val,
 {
 	struct nouveau_drm *drm = container_of(nb, typeof(*drm), acpi_nb);
 	struct acpi_bus_event *info = data;
+	struct device *dev = drm->dev->dev;
 	int ret;
 
 	if (!strcmp(info->device_class, ACPI_VIDEO_CLASS)) {
 		if (info->type == ACPI_VIDEO_NOTIFY_PROBE) {
-			ret = pm_runtime_get(drm->dev->dev);
+			ret = pm_runtime_get(dev);
 			if (ret == 1 || ret == -EACCES) {
 				/* If the GPU is already awake, or in a state
 				 * where we can't wake it up, it can handle
 				 * it's own hotplug events.
 				 */
-				pm_runtime_put_autosuspend(drm->dev->dev);
+				pm_runtime_put_autosuspend(dev);
 			} else if (ret == 0 || ret == -EINPROGRESS) {
 				/* We've started resuming the GPU already, so
 				 * it will handle scheduling a full reprobe
 				 * itself
 				 */
 				NV_DEBUG(drm, "ACPI requested connector reprobe\n");
-				pm_runtime_put_noidle(drm->dev->dev);
+				pm_runtime_put_noidle(dev);
 			} else {
 				NV_WARN(drm, "Dropped ACPI reprobe event due to RPM error: %d\n",
 					ret);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index aa54aee23814..2a9faf0fc277 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -25,7 +25,6 @@ 
 #include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/pci.h>
-#include <linux/pm_runtime.h>
 #include <linux/vga_switcheroo.h>
 #include <linux/mmu_notifier.h>
 #include <linux/dynamic_debug.h>
@@ -72,6 +71,7 @@ 
 #include "nouveau_exec.h"
 #include "nouveau_uvmm.h"
 #include "nouveau_sched.h"
+#include "nouveau_runpm.h"
 
 DECLARE_DYNDBG_CLASSMAP(drm_debug_classes, DD_CLASS_TYPE_DISJOINT_BITS, 0,
 			"DRM_UT_CORE",
@@ -486,10 +486,8 @@  nouveau_drm_device_fini(struct nouveau_drm *drm)
 	struct drm_device *dev = drm->dev;
 	struct nouveau_cli *cli, *temp_cli;
 
-	if (nouveau_pmops_runtime(dev->dev)) {
-		pm_runtime_get_sync(dev->dev);
-		pm_runtime_forbid(dev->dev);
-	}
+	if (nouveau_pmops_runtime(dev->dev))
+		nouveau_runpm_disable(drm);
 
 	nouveau_led_fini(dev);
 	nouveau_dmem_fini(drm);
@@ -581,14 +579,8 @@  nouveau_drm_device_init(struct nouveau_drm *drm)
 	nouveau_dmem_init(drm);
 	nouveau_led_init(dev);
 
-	if (nouveau_pmops_runtime(dev->dev)) {
-		pm_runtime_use_autosuspend(dev->dev);
-		pm_runtime_set_autosuspend_delay(dev->dev, 5000);
-		pm_runtime_set_active(dev->dev);
-		pm_runtime_allow(dev->dev);
-		pm_runtime_mark_last_busy(dev->dev);
-		pm_runtime_put(dev->dev);
-	}
+	if (nouveau_pmops_runtime(dev->dev))
+		nouveau_runpm_enable(drm);
 
 	ret = drm_dev_register(drm->dev, 0);
 	if (ret) {
@@ -1025,11 +1017,9 @@  nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
 	int ret;
 
 	/* need to bring up power immediately if opening device */
-	ret = pm_runtime_get_sync(dev->dev);
-	if (ret < 0 && ret != -EACCES) {
-		pm_runtime_put_autosuspend(dev->dev);
+	ret = nouveau_runpm_get(drm);
+	if (ret)
 		return ret;
-	}
 
 	get_task_comm(tmpname, current);
 	rcu_read_lock();
@@ -1058,8 +1048,7 @@  nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
 		kfree(cli);
 	}
 
-	pm_runtime_mark_last_busy(dev->dev);
-	pm_runtime_put_autosuspend(dev->dev);
+	nouveau_runpm_put(drm);
 	return ret;
 }
 
@@ -1079,7 +1068,7 @@  nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
 	if (!drm_dev_enter(dev, &dev_index))
 		return;
 
-	pm_runtime_get_sync(dev->dev);
+	nouveau_runpm_try_get(drm);
 
 	mutex_lock(&cli->mutex);
 	if (cli->abi16)
@@ -1092,8 +1081,9 @@  nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
 
 	nouveau_cli_fini(cli);
 	kfree(cli);
-	pm_runtime_mark_last_busy(dev->dev);
-	pm_runtime_put_autosuspend(dev->dev);
+
+	nouveau_runpm_put(drm);
+
 	drm_dev_exit(dev_index);
 }
 
@@ -1122,14 +1112,12 @@  long
 nouveau_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct drm_file *filp = file->private_data;
-	struct drm_device *dev = filp->minor->dev;
+	struct nouveau_drm *drm = nouveau_drm(filp->minor->dev);
 	long ret;
 
-	ret = pm_runtime_get_sync(dev->dev);
-	if (ret < 0 && ret != -EACCES) {
-		pm_runtime_put_autosuspend(dev->dev);
+	ret = nouveau_runpm_get(drm);
+	if (ret)
 		return ret;
-	}
 
 	switch (_IOC_NR(cmd) - DRM_COMMAND_BASE) {
 	case DRM_NOUVEAU_NVIF:
@@ -1140,8 +1128,7 @@  nouveau_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 		break;
 	}
 
-	pm_runtime_mark_last_busy(dev->dev);
-	pm_runtime_put_autosuspend(dev->dev);
+	nouveau_runpm_put(drm);
 	return ret;
 }
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index ede2bf30ebc2..e650cb52e267 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -30,6 +30,7 @@ 
 #include "nouveau_dma.h"
 #include "nouveau_fence.h"
 #include "nouveau_abi16.h"
+#include "nouveau_runpm.h"
 
 #include "nouveau_ttm.h"
 #include "nouveau_gem.h"
@@ -78,22 +79,18 @@  nouveau_gem_object_del(struct drm_gem_object *gem)
 {
 	struct nouveau_bo *nvbo = nouveau_gem_object(gem);
 	struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev);
-	struct device *dev = drm->dev->dev;
 	int ret;
 
-	ret = pm_runtime_get_sync(dev);
-	if (WARN_ON(ret < 0 && ret != -EACCES)) {
-		pm_runtime_put_autosuspend(dev);
+	ret = nouveau_runpm_get(drm);
+	if (WARN_ON(ret))
 		return;
-	}
 
 	if (gem->import_attach)
 		drm_prime_gem_destroy(gem, nvbo->bo.sg);
 
 	ttm_bo_put(&nvbo->bo);
 
-	pm_runtime_mark_last_busy(dev);
-	pm_runtime_put_autosuspend(dev);
+	nouveau_runpm_put(drm);
 }
 
 int
@@ -102,7 +99,6 @@  nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv)
 	struct nouveau_cli *cli = nouveau_cli(file_priv);
 	struct nouveau_bo *nvbo = nouveau_gem_object(gem);
 	struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev);
-	struct device *dev = drm->dev->dev;
 	struct nouveau_uvmm *uvmm = nouveau_cli_uvmm(cli);
 	struct nouveau_vmm *vmm = nouveau_cli_vmm(cli);
 	struct nouveau_vma *vma;
@@ -119,19 +115,17 @@  nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv)
 	if (ret)
 		return ret;
 
-	ret = pm_runtime_get_sync(dev);
-	if (ret < 0 && ret != -EACCES) {
-		pm_runtime_put_autosuspend(dev);
+	ret = nouveau_runpm_get(drm);
+	if (ret)
 		goto out;
-	}
 
 	/* only create a VMA on binding */
 	if (!nouveau_cli_uvmm(cli))
 		ret = nouveau_vma_new(nvbo, vmm, &vma);
 	else
 		ret = 0;
-	pm_runtime_mark_last_busy(dev);
-	pm_runtime_put_autosuspend(dev);
+
+	nouveau_runpm_put(drm);
 out:
 	ttm_bo_unreserve(&nvbo->bo);
 	return ret;
@@ -188,7 +182,6 @@  nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv)
 	struct nouveau_cli *cli = nouveau_cli(file_priv);
 	struct nouveau_bo *nvbo = nouveau_gem_object(gem);
 	struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev);
-	struct device *dev = drm->dev->dev;
 	struct nouveau_vmm *vmm = nouveau_cli_vmm(cli);
 	struct nouveau_vma *vma;
 	int ret;
@@ -206,12 +199,11 @@  nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv)
 	vma = nouveau_vma_find(nvbo, vmm);
 	if (vma) {
 		if (--vma->refs == 0) {
-			ret = pm_runtime_get_sync(dev);
-			if (!WARN_ON(ret < 0 && ret != -EACCES)) {
+			ret = nouveau_runpm_get(drm);
+			if (!WARN_ON(ret)) {
 				nouveau_gem_object_unmap(nvbo, vma);
-				pm_runtime_mark_last_busy(dev);
+				nouveau_runpm_put(drm);
 			}
-			pm_runtime_put_autosuspend(dev);
 		}
 	}
 	ttm_bo_unreserve(&nvbo->bo);
diff --git a/drivers/gpu/drm/nouveau/nouveau_runpm.h b/drivers/gpu/drm/nouveau/nouveau_runpm.h
new file mode 100644
index 000000000000..92d6c518bdad
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_runpm.h
@@ -0,0 +1,89 @@ 
+/* SPDX-License-Identifier: MIT */
+#ifndef __NOUVEAU_RUNPM_H__
+#define __NOUVEAU_RUNPM_H__
+#include <linux/pm_runtime.h>
+
+static inline struct device *
+nouveau_runpm_dev(struct nouveau_drm *drm)
+{
+	return drm->dev->dev;
+}
+
+static inline void
+nouveau_runpm_put_noidle(struct nouveau_drm *drm)
+{
+	struct device *dev = nouveau_runpm_dev(drm);
+
+	pm_runtime_put_noidle(dev);
+}
+
+static inline void
+nouveau_runpm_put_noop(struct nouveau_drm *drm)
+{
+	struct device *dev = nouveau_runpm_dev(drm);
+
+	pm_runtime_put_autosuspend(dev);
+}
+
+static inline void
+nouveau_runpm_put(struct nouveau_drm *drm)
+{
+	struct device *dev = nouveau_runpm_dev(drm);
+
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+}
+
+static inline int
+nouveau_runpm_try_get(struct nouveau_drm *drm)
+{
+	struct device *dev = nouveau_runpm_dev(drm);
+
+	return pm_runtime_get_sync(dev);
+}
+
+static inline __must_check int
+nouveau_runpm_get(struct nouveau_drm *drm)
+{
+	struct device *dev = nouveau_runpm_dev(drm);
+	int ret;
+
+	ret = pm_runtime_get_sync(dev);
+	if (ret < 0 && ret != -EACCES) {
+		pm_runtime_put_autosuspend(dev);
+		return ret;
+	}
+
+	return 0;
+}
+
+static inline void
+nouveau_runpm_get_noresume(struct nouveau_drm *drm)
+{
+	struct device *dev = nouveau_runpm_dev(drm);
+
+	pm_runtime_get_noresume(dev);
+}
+
+static inline void
+nouveau_runpm_disable(struct nouveau_drm *drm)
+{
+	struct device *dev = nouveau_runpm_dev(drm);
+
+	pm_runtime_get_sync(dev);
+	pm_runtime_forbid(dev);
+}
+
+static inline void
+nouveau_runpm_enable(struct nouveau_drm *drm)
+{
+	struct device *dev = nouveau_runpm_dev(drm);
+
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_set_autosuspend_delay(dev, 5000);
+	pm_runtime_set_active(dev);
+	pm_runtime_allow(dev);
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put(dev);
+}
+#endif