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@xxxxxxxxxxxxx> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@xxxxxxxxxxxxxxx> --- 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@xxxxxxxxx> + */ + +#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 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx