[Intel-gfx] [RFC 06/15] drm/i915: power-related changes non-HDAudio HDMI interface

Pierre-Louis Bossart pierre-louis.bossart at linux.intel.com
Sat Mar 5 02:50:43 UTC 2016


PM and RPM changes for interface available on Baytrail and CherryTrail

This driver was downloaded from https://github.com/01org/baytrailaudio/

...and had the changes to .config stripped and the revert on sound/init.c

Clean-up, port to 4.4 and intel-drm by Pierre Bossart

Signed-off-by: David Henningsson <david.henningsson at canonical.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>
---
 drivers/gpu/drm/i915/i915_rpm.c | 476 ++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_pm.c |  53 +++++
 2 files changed, 529 insertions(+)
 create mode 100644 drivers/gpu/drm/i915/i915_rpm.c

diff --git a/drivers/gpu/drm/i915/i915_rpm.c b/drivers/gpu/drm/i915/i915_rpm.c
new file mode 100644
index 0000000..511311c
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_rpm.c
@@ -0,0 +1,476 @@
+/*
+ * Copyright 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author:
+ * Naresh Kumar Kachhi <naresh.kumar.kachhi at intel.com>
+ */
+
+#include "i915_drv.h"
+#include "i915_reg.h"
+#include "intel_drv.h"
+#include <linux/console.h>
+#ifdef CONFIG_PM_RUNTIME
+#include <linux/pm_runtime.h>
+#endif
+#include <linux/proc_fs.h>  /* Needed for procfs access */
+#include <linux/fs.h>	    /* For the basic file system */
+#include <linux/kernel.h>
+
+#define RPM_AUTOSUSPEND_DELAY 500
+
+#ifdef CONFIG_PM_RUNTIME
+
+/**
+ * - Where should we use get/put?
+ *   Get/put should be used very carefully as we might end up in weird states
+ *   if not used properly (see the Note below). We want to cover all the
+ *   acesses that might result in accessing rings/display/registers/gtt etc
+ *   Mostly covering ioctls and drm callbacks should be enough. You can
+ *   avoid those which does not access any HW.
+ *
+ * - When should we avoid get/put?
+ *   WQ and interrupts should be taken care in suspend path. We should
+ *   disable all the interrupts and cancel any pending WQs. Never try to
+ *   cover interrupt/WQ with get/put unless you are sure about it.
+ *
+ * Note:Following scenarios should be strictly avoided while using get_sync
+ * 1. Calling get_sync with struct_mutex or mode_config.mutex locked
+ *    - we acquire these locks in runtime_resume, so any call to get_sync
+ *    with these mutex locked might end up in a dead lock.
+ *    check_mutex_current function can be used to debug this scenario.
+ *    - Or let's say thread1 has done get_sync and is currently executing
+ *    runtime_resume function. Before thread1 is able to acquire these
+ *    mutex, thread2 acquires the mutex and does a get_sync. Now thread1
+ *    is waiting for mutex and thread2 is waiting for dev->power.lock
+ *    resulting in a deadlock. Use check_mutex to debug this.
+ * 2. Calling get_sync from runtime_resume path
+ *    runtime_resume is called with dev->power.lock held. doing get_sync
+ *    in same path will end up in deadlock
+ */
+
+#define RPM_PROC_ENTRY_FILENAME		"i915_rpm_op"
+#define RPM_PROC_ENTRY_DIRECTORY	"driver/i915rpm"
+
+int i915_rpm_get_procfs(struct inode *inode, struct file *file);
+int i915_rpm_put_procfs(struct inode *inode, struct file *file);
+/* proc file operations supported */
+static const struct file_operations rpm_file_ops = {
+	.owner		= THIS_MODULE,
+	.open		= i915_rpm_get_procfs,
+	.release	= i915_rpm_put_procfs,
+};
+
+bool i915_pm_runtime_enabled(struct device *dev)
+{
+	return pm_runtime_enabled(dev);
+}
+
+void i915_rpm_enable(struct device *dev)
+{
+	int cur_status = pm_runtime_enabled(dev);
+
+	if (!cur_status) {
+		pm_runtime_enable(dev);
+		pm_runtime_allow(dev);
+	}
+}
+
+void i915_rpm_disable(struct drm_device *drm_dev)
+{
+	struct device *dev = drm_dev->dev;
+	int cur_status = pm_runtime_enabled(dev);
+
+	if (cur_status) {
+		pm_runtime_forbid(dev);
+		pm_runtime_disable(dev);
+	}
+}
+
+static int i915_rpm_procfs_init(struct drm_device *drm_dev)
+{
+	struct drm_i915_private *dev_priv = drm_dev->dev_private;
+
+	dev_priv->rpm.i915_proc_dir = NULL;
+	dev_priv->rpm.i915_proc_file = NULL;
+
+	/**
+	 * Create directory for rpm file(s)
+	 */
+	dev_priv->rpm.i915_proc_dir = proc_mkdir(RPM_PROC_ENTRY_DIRECTORY,
+						 NULL);
+	if (dev_priv->rpm.i915_proc_dir == NULL) {
+		DRM_ERROR("Could not initialize %s\n",
+				RPM_PROC_ENTRY_DIRECTORY);
+		return -ENOMEM;
+	}
+	/**
+	 * Create the /proc file
+	 */
+	dev_priv->rpm.i915_proc_file = proc_create_data(
+						RPM_PROC_ENTRY_FILENAME,
+						S_IRUGO | S_IWUSR,
+						dev_priv->rpm.i915_proc_dir,
+						&rpm_file_ops,
+						drm_dev);
+	/* check if file is created successfuly */
+	if (dev_priv->rpm.i915_proc_file == NULL) {
+		DRM_ERROR("Could not initialize %s/%s\n",
+			RPM_PROC_ENTRY_DIRECTORY, RPM_PROC_ENTRY_FILENAME);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static int i915_rpm_procfs_deinit(struct drm_device *drm_dev)
+{
+	struct drm_i915_private *dev_priv = drm_dev->dev_private;
+	/* Clean up proc file */
+	if (dev_priv->rpm.i915_proc_file) {
+		remove_proc_entry(RPM_PROC_ENTRY_FILENAME,
+				 dev_priv->rpm.i915_proc_dir);
+		dev_priv->rpm.i915_proc_file = NULL;
+	}
+	if (dev_priv->rpm.i915_proc_dir) {
+		remove_proc_entry(RPM_PROC_ENTRY_DIRECTORY, NULL);
+		dev_priv->rpm.i915_proc_dir = NULL;
+	}
+	return 0;
+}
+
+/* RPM init */
+int i915_rpm_init(struct drm_device *drm_dev)
+{
+	int ret = 0;
+	struct device *dev = drm_dev->dev;
+	struct drm_i915_private *dev_priv = drm_dev->dev_private;
+
+	ret = i915_rpm_procfs_init(drm_dev);
+	if (ret)
+		DRM_ERROR("unable to initialize procfs entry");
+	ret = pm_runtime_set_active(dev);
+	dev_priv->rpm.ring_active = false;
+	atomic_set(&dev_priv->rpm.procfs_count, 0);
+	pm_runtime_allow(dev);
+	/* enable Auto Suspend */
+	pm_runtime_set_autosuspend_delay(dev, RPM_AUTOSUSPEND_DELAY);
+	pm_runtime_use_autosuspend(dev);
+	if (dev->power.runtime_error)
+		DRM_ERROR("rpm init: error = %d\n", dev->power.runtime_error);
+
+	return ret;
+}
+
+int i915_rpm_deinit(struct drm_device *drm_dev)
+{
+	struct device *dev = drm_dev->dev;
+
+	pm_runtime_forbid(dev);
+	pm_runtime_set_suspended(dev);
+	pm_runtime_get_noresume(dev);
+	if (dev->power.runtime_error)
+		DRM_ERROR("rpm init: error = %d\n", dev->power.runtime_error);
+
+	i915_rpm_procfs_deinit(drm_dev);
+	return 0;
+}
+
+/**
+ * We have different flavour of get/put based on access type (ring/disp/
+ * vxd etc). this is done based on different requirements and to make
+ * debugging a little easier. Debugfs introduces separate counter for
+ * each type.
+ */
+
+/**
+ * Once we have scheduled commands on GPU, it might take a while GPU
+ * to execute them. Following is done to make sure Gfx is in D0i0 while
+ * GPU is executing the commands.
+ * 1. For IOCTLS make sure we are in D0i0 by calling "get_ioctl".
+ * 2. if IOCTL scheudles GPU commands using rings do the following
+ *  a. For all ring accesses make sure we add a request in the request
+ *     list and schedule a work item to track the "seq no". This
+ *     is done by using "i915_add_request" or "i915_add_request_no_flush"
+ *     functions.
+ *  b. If request list was empty, we do a "get_ring". This will increment
+ *     ref count to make sure GPU will be in D0 state.
+ *  c. Once the list becomes empty call put_ring
+ *
+ * Note: All the ring accesses are covered with struct_mutex. So we
+ * don't need any synchronization to protect ring_active.
+ */
+int i915_rpm_get_ring(struct drm_device *drm_dev)
+{
+	struct intel_engine_cs *ring;
+	struct drm_i915_private *dev_priv = drm_dev->dev_private;
+	int i;
+	bool idle = true;
+
+	for_each_ring(ring, dev_priv, i)
+		idle &= list_empty(&ring->request_list);
+
+	if (idle) {
+		if (!dev_priv->rpm.ring_active) {
+			dev_priv->rpm.ring_active = true;
+			pm_runtime_get_noresume(drm_dev->dev);
+		}
+	}
+
+	return 0;
+}
+
+int i915_rpm_put_ring(struct drm_device *drm_dev)
+{
+	struct drm_i915_private *dev_priv = drm_dev->dev_private;
+
+	if (dev_priv->rpm.ring_active) {
+		/* Mark last time it was busy and schedule a autosuspend */
+		pm_runtime_mark_last_busy(drm_dev->dev);
+		pm_runtime_put_autosuspend(drm_dev->dev);
+		dev_priv->rpm.ring_active = false;
+	}
+	return 0;
+}
+
+/**
+ * To cover the function pointers that are assigned to drm structures
+ * and can be called from drm
+ */
+int i915_rpm_get_callback(struct drm_device *drm_dev)
+{
+	return pm_runtime_get_sync(drm_dev->dev);
+}
+
+int i915_rpm_put_callback(struct drm_device *drm_dev)
+{
+	pm_runtime_mark_last_busy(drm_dev->dev);
+	return pm_runtime_put_autosuspend(drm_dev->dev);
+}
+
+/**
+ * early_suspend/DSR should call this function to notify PM Core about
+ * display idleness
+ */
+int i915_rpm_get_disp(struct drm_device *drm_dev)
+{
+	return pm_runtime_get_sync(drm_dev->dev);
+}
+
+int i915_rpm_put_disp(struct drm_device *drm_dev)
+{
+	pm_runtime_mark_last_busy(drm_dev->dev);
+	return pm_runtime_put_autosuspend(drm_dev->dev);
+}
+
+/** to cover the ioctls with get/put*/
+int i915_rpm_get_ioctl(struct drm_device *drm_dev)
+{
+	/* Don't do anything if device is not ready */
+	if (drm_device_is_unplugged(drm_dev))
+		return 0;
+
+	return pm_runtime_get_sync(drm_dev->dev);
+}
+
+int i915_rpm_put_ioctl(struct drm_device *drm_dev)
+{
+	/* Don't do anything if device is not ready */
+	if (drm_device_is_unplugged(drm_dev))
+		return 0;
+
+	pm_runtime_mark_last_busy(drm_dev->dev);
+	return pm_runtime_put_autosuspend(drm_dev->dev);
+}
+
+/* these operations are caled from user mode (CoreU) to make sure
+ * Gfx is up before register accesses from user mode
+ */
+int i915_rpm_get_procfs(struct inode *inode, struct file *file)
+{
+	struct drm_device *dev = PDE_DATA(inode);
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	atomic_inc(&dev_priv->rpm.procfs_count);
+	pm_runtime_get_sync(dev->dev);
+	return 0;
+}
+
+int i915_rpm_put_procfs(struct inode *inode, struct file *file)
+{
+	struct drm_device *dev = PDE_DATA(inode);
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	pm_runtime_mark_last_busy(dev->dev);
+	pm_runtime_put_autosuspend(dev->dev);
+	atomic_dec(&dev_priv->rpm.procfs_count);
+	return 0;
+}
+
+/**
+ * VXD driver need to call this to make sure Gfx is in D0i0
+ * while VXD is on
+ */
+#ifdef CONFIG_DRM_VXD_BYT
+int i915_rpm_get_vxd(struct drm_device *drm_dev)
+{
+	return pm_runtime_get_sync(drm_dev->dev);
+}
+EXPORT_SYMBOL(i915_rpm_get_vxd);
+
+/**
+ * VXD driver need to call this to notify Gfx that it is
+ * done with HW accesses
+ */
+int i915_rpm_put_vxd(struct drm_device *drm_dev)
+{
+	pm_runtime_mark_last_busy(drm_dev->dev);
+	return pm_runtime_put_autosuspend(drm_dev->dev);
+}
+EXPORT_SYMBOL(i915_rpm_put_vxd);
+#endif
+
+/* mainly for debug purpose, check if the access is valid */
+bool i915_rpm_access_check(struct drm_device *dev)
+{
+	if (dev->dev->power.runtime_status == RPM_SUSPENDED) {
+		DRM_ERROR("invalid access, will cause Hard Hang\n");
+		dump_stack();
+		return false;
+	}
+	return true;
+}
+
+/* mainly for debug purpose, check if mutex is locked by
+ * current thread
+ */
+int check_mutex_current(struct drm_device *drm_dev)
+{
+	int ret = 0;
+
+	if ((mutex_is_locked(&drm_dev->mode_config.mutex)) &&
+			(drm_dev->mode_config.mutex.owner == current)) {
+		DRM_ERROR("config mutex locked by current thread\n");
+		dump_stack();
+		ret = -1;
+	}
+	if ((mutex_is_locked(&drm_dev->struct_mutex)) &&
+			(drm_dev->struct_mutex.owner == current)) {
+		DRM_ERROR("struct mutex locked by current thread\n");
+		dump_stack();
+		ret = -2;
+	}
+	return ret;
+}
+
+int check_mutex(struct drm_device *drm_dev)
+{
+	int ret = 0;
+
+	if (mutex_is_locked(&drm_dev->mode_config.mutex)) {
+		DRM_ERROR("config mutex locked\n");
+		dump_stack();
+		ret = -1;
+	}
+	if (mutex_is_locked(&drm_dev->struct_mutex)) {
+		DRM_ERROR("struct mutex locked\n");
+		dump_stack();
+		ret = -2;
+	}
+	return ret;
+}
+
+/* Check for current runtime state */
+bool i915_is_device_active(struct drm_device *dev)
+{
+	return (dev->dev->power.runtime_status == RPM_ACTIVE);
+}
+
+bool i915_is_device_resuming(struct drm_device *dev)
+{
+	return (dev->dev->power.runtime_status == RPM_RESUMING);
+}
+
+bool i915_is_device_suspended(struct drm_device *dev)
+{
+	return (dev->dev->power.runtime_status == RPM_SUSPENDED);
+}
+
+bool i915_is_device_suspending(struct drm_device *dev)
+{
+	return (dev->dev->power.runtime_status == RPM_SUSPENDING);
+}
+
+#else /*CONFIG_PM_RUNTIME*/
+int i915_rpm_init(struct drm_device *dev) {return 0; }
+int i915_rpm_deinit(struct drm_device *dev) {return 0; }
+int i915_rpm_get(struct drm_device *dev, u32 flags) {return 0; }
+int i915_rpm_put(struct drm_device *dev, u32 flags) {return 0; }
+int i915_rpm_get_ring(struct drm_device *dev) {return 0; }
+int i915_rpm_put_ring(struct drm_device *dev) {return 0; }
+int i915_rpm_get_callback(struct drm_device *dev) {return 0; }
+int i915_rpm_put_callback(struct drm_device *dev) {return 0; }
+int i915_rpm_get_ioctl(struct drm_device *dev) {return 0; }
+int i915_rpm_put_ioctl(struct drm_device *dev) {return 0; }
+int i915_rpm_get_disp(struct drm_device *dev) {return 0; }
+int i915_rpm_put_disp(struct drm_device *dev) {return 0; }
+int i915_rpm_get_procfs(struct inode *inode,
+			      struct file *file) {return 0; }
+int i915_rpm_put_procfs(struct inode *inode,
+			      struct file *file) {return 0; }
+#ifdef CONFIG_DRM_VXD_BYT
+int i915_rpm_get_vxd(struct drm_device *dev) {return 0; }
+int i915_rpm_put_vxd(struct drm_device *dev) {return 0; }
+#endif
+
+bool i915_is_device_active(struct drm_device *dev)
+{
+	return true;
+}
+
+bool i915_is_device_resuming(struct drm_device *dev)
+{
+	return false;
+}
+
+bool i915_is_device_suspended(struct drm_device *dev)
+{
+	return false;
+}
+
+bool i915_is_device_suspending(struct drm_device *dev)
+{
+	return false;
+}
+
+bool i915_rpm_access_check(struct drm_device *dev)
+{
+	return true;
+}
+bool i915_pm_runtime_enabled(struct device *dev)
+{
+	return false;
+}
+
+void i915_rpm_enable(struct device *dev) {}
+
+void i915_rpm_disable(struct drm_device *drm_dev) {}
+
+#endif /*CONFIG_PM_RUNTIME*/
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index d33de95..dae77d1 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -31,6 +31,17 @@
 #include "../../../platform/x86/intel_ips.h"
 #include <linux/module.h>
 
+typedef enum _UHBUsage {
+	OSPM_UHB_ONLY_IF_ON = 0,
+	OSPM_UHB_FORCE_POWER_ON,
+} UHBUsage;
+
+static struct drm_device *gdev;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	#include <linux/earlysuspend.h>
+#endif
+
 /**
  * DOC: RC6
  *
@@ -7098,6 +7109,7 @@ void intel_suspend_hw(struct drm_device *dev)
 void intel_init_pm(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
+	gdev = dev;
 
 	intel_fbc_init(dev_priv);
 
@@ -7398,3 +7410,44 @@ void intel_pm_setup(struct drm_device *dev)
 	atomic_set(&dev_priv->pm.wakeref_count, 0);
 	atomic_set(&dev_priv->pm.atomic_seq, 0);
 }
+
+bool ospm_power_is_hw_on(int hw_islands)
+{
+#if 0
+	struct drm_device *drm_dev = gdev;
+	unsigned long flags;
+	bool ret = false;
+	struct drm_i915_private *dev_priv = drm_dev->dev_private;
+	u32 data = vlv_punit_read(dev_priv, VLV_IOSFSB_PWRGT_STATUS);
+
+	if ((VLV_POWER_GATE_DISPLAY_MASK & data)
+			== VLV_POWER_GATE_DISPLAY_MASK) {
+		DRM_ERROR("Display Island not ON\n");
+		return false;
+	} else {
+		return true;
+	}
+#endif
+	return true;
+}
+EXPORT_SYMBOL(ospm_power_is_hw_on);
+
+/* Dummy Function for HDMI Audio Power management.
+ * Will be updated once S0iX code is integrated
+ */
+bool ospm_power_using_hw_begin(int hw_island, UHBUsage usage)
+{
+	struct drm_device *drm_dev = gdev;
+
+	i915_rpm_get_disp(drm_dev);
+	return i915_is_device_active(drm_dev);
+}
+EXPORT_SYMBOL(ospm_power_using_hw_begin);
+
+void ospm_power_using_hw_end(int hw_island)
+{
+	struct drm_device *drm_dev = gdev;
+
+	i915_rpm_put_disp(drm_dev);
+}
+EXPORT_SYMBOL(ospm_power_using_hw_end);
-- 
1.9.1



More information about the Intel-gfx mailing list