techpack: display: 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: 139655049
Bug: 126304228
Change-Id: I21e3c7b0830a9695db9f65526c111ce5153d1764
Signed-off-by: Adrian Salido <salidoa@google.com>
Signed-off-by: Robb Glasser <rglasser@google.com>
(cherry picked from commit 11a2193b434cb3130743fbff89a161062883132e)
Signed-off-by: Ken Huang <kenbshuang@google.com>
This commit is contained in:
Adrian Salido 2017-12-04 16:52:17 -08:00 committed by Michael Bestas
parent 566f5b44eb
commit 61c42b756e
No known key found for this signature in database
GPG key ID: CC95044519BE6669
3 changed files with 176 additions and 0 deletions

View file

@ -39,6 +39,7 @@
#include <linux/of_address.h>
#include <linux/kthread.h>
#include <linux/workqueue.h>
#include <uapi/linux/sched/types.h>
#include <drm/drm_of.h>
@ -61,6 +62,9 @@
#define MSM_VERSION_MINOR 3
#define MSM_VERSION_PATCHLEVEL 0
#define IDLE_ENCODER_MASK_DEFAULT 1
#define IDLE_TIMEOUT_MS_DEFAULT 100
static DEFINE_MUTEX(msm_release_lock);
static void msm_fb_output_poll_changed(struct drm_device *dev)
@ -697,6 +701,160 @@ static struct msm_kms *_msm_drm_init_helper(struct msm_drm_private *priv,
return kms;
}
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, 0, &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");
idle->active_mask = 0;
idle->encoder_mask = IDLE_ENCODER_MASK_DEFAULT;
idle->timeout_ms = IDLE_TIMEOUT_MS_DEFAULT;
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);
@ -747,6 +905,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)

View file

@ -591,6 +591,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;
@ -701,6 +710,8 @@ struct msm_drm_private {
/* update the flag when msm driver receives shutdown notification */
bool shutdown_in_progress;
struct msm_idle idle;
};
/* get struct msm_kms * from drm_device * */
@ -967,6 +978,7 @@ static inline void __exit msm_mdp_unregister(void)
}
#endif
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);

View file

@ -2383,6 +2383,8 @@ static int _sde_encoder_rc_kickoff(struct drm_encoder *drm_enc,
/* cancel delayed off work, if any */
_sde_encoder_rc_cancel_delayed(sde_enc, 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 */
@ -2492,6 +2494,8 @@ static int _sde_encoder_rc_frame_done(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,