[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