aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Salido <salidoa@google.com>2017-12-04 16:52:17 -0800
committerdragonGR <alex@dragongr.dev>2022-07-05 23:29:03 +0300
commit551f71b1dc0380a0fd2d2d78c43bd183c32e955b (patch)
tree33675e20cead7de8db92686d44d7483392e52ca7
parent38dcb5d235042513ce6313380accaa72bd47d601 (diff)
drm/msm: add idle state sysfs node
Add a sysfs mechanism to track the idle state of display subsystem. This allows user space to poll on the idle state node to detect when display goes idle for longer than the time set. Bug: 69915886 Change-Id: I21e3c7b0830a9695db9f65526c111ce5153d1764 Signed-off-by: Adrian Salido <salidoa@google.com> Signed-off-by: Robb Glasser <rglasser@google.com>
-rwxr-xr-xdrivers/gpu/drm/msm/msm_drv.c153
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h12
-rw-r--r--drivers/gpu/drm/msm/sde/sde_encoder.c4
3 files changed, 169 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 2dd43dd3722a..508a8b0a5f6a 100755
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -39,6 +39,7 @@
#include <linux/of_address.h>
#include <linux/kthread.h>
+#include <linux/workqueue.h>
#include "msm_drv.h"
#include "msm_debugfs.h"
#include "msm_fence.h"
@@ -459,6 +460,156 @@ static int msm_power_enable_wrapper(void *handle, void *client, bool enable)
return sde_power_resource_enable(handle, client, enable);
}
+static ssize_t idle_encoder_mask_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct drm_device *ddev = dev_get_drvdata(device);
+ struct msm_drm_private *priv = ddev->dev_private;
+ struct msm_idle *idle = &priv->idle;
+ u32 encoder_mask = 0;
+ int rc;
+ unsigned long flags;
+
+ rc = kstrtouint(buf, 10, &encoder_mask);
+ if (rc)
+ return rc;
+
+ spin_lock_irqsave(&idle->lock, flags);
+ idle->encoder_mask = encoder_mask;
+ idle->active_mask &= encoder_mask;
+ spin_unlock_irqrestore(&idle->lock, flags);
+
+ return count;
+}
+
+static ssize_t idle_encoder_mask_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_device *ddev = dev_get_drvdata(device);
+ struct msm_drm_private *priv = ddev->dev_private;
+ struct msm_idle *idle = &priv->idle;
+
+ return snprintf(buf, PAGE_SIZE, "0x%x\n", idle->encoder_mask);
+}
+
+static ssize_t idle_timeout_ms_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct drm_device *ddev = dev_get_drvdata(device);
+ struct msm_drm_private *priv = ddev->dev_private;
+ struct msm_idle *idle = &priv->idle;
+ u32 timeout_ms = 0;
+ int rc;
+ unsigned long flags;
+
+ rc = kstrtouint(buf, 10, &timeout_ms);
+ if (rc)
+ return rc;
+
+ spin_lock_irqsave(&idle->lock, flags);
+ idle->timeout_ms = timeout_ms;
+ spin_unlock_irqrestore(&idle->lock, flags);
+
+ return count;
+}
+
+static ssize_t idle_timeout_ms_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_device *ddev = dev_get_drvdata(device);
+ struct msm_drm_private *priv = ddev->dev_private;
+ struct msm_idle *idle = &priv->idle;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", idle->timeout_ms);
+}
+
+static ssize_t idle_state_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_device *ddev = dev_get_drvdata(device);
+ struct msm_drm_private *priv = ddev->dev_private;
+ struct msm_idle *idle = &priv->idle;
+ const char *state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&idle->lock, flags);
+ if (idle->active_mask) {
+ state = "active";
+ spin_unlock_irqrestore(&idle->lock, flags);
+ return scnprintf(buf, PAGE_SIZE, "%s (0x%x)\n",
+ state, idle->active_mask);
+ } else if (delayed_work_pending(&idle->work))
+ state = "pending";
+ else
+ state = "idle";
+ spin_unlock_irqrestore(&idle->lock, flags);
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", state);
+}
+
+static DEVICE_ATTR_RW(idle_encoder_mask);
+static DEVICE_ATTR_RW(idle_timeout_ms);
+static DEVICE_ATTR_RO(idle_state);
+
+static const struct attribute *msm_idle_attrs[] = {
+ &dev_attr_idle_encoder_mask.attr,
+ &dev_attr_idle_timeout_ms.attr,
+ &dev_attr_idle_state.attr,
+ NULL
+};
+
+static void msm_idle_work(struct work_struct *work)
+{
+ struct delayed_work *dw = to_delayed_work(work);
+ struct msm_idle *idle = container_of(dw, struct msm_idle, work);
+ struct msm_drm_private *priv = container_of(idle,
+ struct msm_drm_private, idle);
+
+ if (!idle->active_mask)
+ sysfs_notify(&priv->dev->dev->kobj, NULL, "idle_state");
+}
+
+void msm_idle_set_state(struct drm_encoder *encoder, bool active)
+{
+ struct drm_device *ddev = encoder->dev;
+ struct msm_drm_private *priv = ddev->dev_private;
+ struct msm_idle *idle = &priv->idle;
+ unsigned int mask = 1 << drm_encoder_index(encoder);
+ unsigned long flags;
+
+ spin_lock_irqsave(&idle->lock, flags);
+ if (mask & idle->encoder_mask) {
+ if (active)
+ idle->active_mask |= mask;
+ else
+ idle->active_mask &= ~mask;
+
+ if (idle->timeout_ms && !idle->active_mask)
+ mod_delayed_work(system_wq, &idle->work,
+ msecs_to_jiffies(idle->timeout_ms));
+ else
+ cancel_delayed_work(&idle->work);
+ }
+ spin_unlock_irqrestore(&idle->lock, flags);
+}
+
+static void msm_idle_init(struct drm_device *ddev)
+{
+ struct msm_drm_private *priv = ddev->dev_private;
+ struct msm_idle *idle = &priv->idle;
+
+ if (sysfs_create_files(&ddev->dev->kobj, msm_idle_attrs) < 0)
+ pr_warn("failed to create idle state file");
+
+ INIT_DELAYED_WORK(&idle->work, msm_idle_work);
+ spin_lock_init(&idle->lock);
+}
+
static int msm_drm_init(struct device *dev, struct drm_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -520,6 +671,8 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
goto dbg_init_fail;
}
+ msm_idle_init(ddev);
+
/* Bind all our sub-components: */
ret = msm_component_bind_all(dev, ddev);
if (ret)
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 6a7a84b6d226..67ddbbcca659 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -518,6 +518,15 @@ struct msm_drm_thread {
struct kthread_worker worker;
};
+struct msm_idle {
+ u32 timeout_ms;
+ u32 encoder_mask;
+ u32 active_mask;
+
+ spinlock_t lock;
+ struct delayed_work work;
+};
+
struct msm_drm_private {
struct drm_device *dev;
@@ -635,6 +644,8 @@ struct msm_drm_private {
/* commit end time */
ktime_t commit_end_time;
+
+ struct msm_idle idle;
};
/* get struct msm_kms * from drm_device * */
@@ -874,6 +885,7 @@ static inline int msm_dsi_modeset_init(struct msm_dsi *msm_dsi,
void __init msm_mdp_register(void);
void __exit msm_mdp_unregister(void);
+void msm_idle_set_state(struct drm_encoder *encoder, bool active);
#ifdef CONFIG_DEBUG_FS
void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
void msm_gem_describe_objects(struct list_head *list, struct seq_file *m);
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 24a649243856..0ddb66906385 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -2208,6 +2208,8 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
SDE_DEBUG_ENC(sde_enc, "sw_event:%d, work cancelled\n",
sw_event);
+ msm_idle_set_state(drm_enc, true);
+
mutex_lock(&sde_enc->rc_lock);
/* return if the resource control is already in ON state */
@@ -2312,6 +2314,8 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
else
idle_pc_duration = IDLE_POWERCOLLAPSE_DURATION;
+ msm_idle_set_state(drm_enc, false);
+
if (!autorefresh_enabled)
kthread_mod_delayed_work(
&disp_thread->worker,