techpack/display: 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

DennySPb: adapt to sm8350
   * Change drm_encoder mask to 2

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>
Signed-off-by: Adithya R <gh0strider.2k18.reborn@gmail.com>
This commit is contained in:
Adrian Salido 2017-12-04 16:52:17 -08:00 committed by zaidkhan0997
parent 14a8a76d1c
commit 74a2c92068
Signed by: zaidkhan0997
GPG Key ID: 6C50557AD547C980
3 changed files with 177 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>
#include <drm/drm_auth.h>
@ -68,6 +69,9 @@
#define LASTCLOSE_TIMEOUT_MS 500
#define IDLE_ENCODER_MASK_DEFAULT 2
#define IDLE_TIMEOUT_MS_DEFAULT 100
#define msm_wait_event_timeout(waitq, cond, timeout_ms, ret) \
do { \
ktime_t cur_ktime; \
@ -755,6 +759,161 @@ static struct msm_kms *_msm_drm_component_init_helper(
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_device_init(struct platform_device *pdev,
struct drm_driver *drv)
{
@ -793,6 +952,8 @@ static int msm_drm_device_init(struct platform_device *pdev,
goto dbg_init_fail;
}
msm_idle_init(ddev);
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);

View File

@ -826,6 +826,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;
@ -939,6 +948,8 @@ struct msm_drm_private {
struct mutex vm_client_lock;
struct list_head vm_client_list;
struct msm_idle idle;
};
/* get struct msm_kms * from drm_device * */
@ -1216,6 +1227,7 @@ static inline void __exit msm_mdp_unregister(void)
}
#endif /* CONFIG_DRM_MSM_MDP5 */
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

@ -1757,6 +1757,8 @@ end:
if (!sde_enc->delay_kickoff)
_sde_encoder_rc_kickoff_delayed(sde_enc, sw_event);
msm_idle_set_state(drm_enc, false);
mutex_unlock(&sde_enc->rc_lock);
return ret;
}
@ -1767,6 +1769,8 @@ static int _sde_encoder_rc_pre_stop(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);
if (is_vid_mode &&