From 74a2c9206851362a8108b6b689f9a313f2941bc8 Mon Sep 17 00:00:00 2001 From: Adrian Salido Date: Mon, 4 Dec 2017 16:52:17 -0800 Subject: [PATCH] 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 Signed-off-by: Robb Glasser (cherry picked from commit 11a2193b434cb3130743fbff89a161062883132e) Signed-off-by: Ken Huang Signed-off-by: Adithya R --- techpack/display/msm/msm_drv.c | 161 +++++++++++++++++++++++++ techpack/display/msm/msm_drv.h | 12 ++ techpack/display/msm/sde/sde_encoder.c | 4 + 3 files changed, 177 insertions(+) diff --git a/techpack/display/msm/msm_drv.c b/techpack/display/msm/msm_drv.c index f091be5a252c..34b9a945252c 100644 --- a/techpack/display/msm/msm_drv.c +++ b/techpack/display/msm/msm_drv.c @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -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); diff --git a/techpack/display/msm/msm_drv.h b/techpack/display/msm/msm_drv.h index 364b615a1ba9..f0bee018641d 100644 --- a/techpack/display/msm/msm_drv.h +++ b/techpack/display/msm/msm_drv.h @@ -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); diff --git a/techpack/display/msm/sde/sde_encoder.c b/techpack/display/msm/sde/sde_encoder.c index b67e4d537499..45386b5c78a1 100644 --- a/techpack/display/msm/sde/sde_encoder.c +++ b/techpack/display/msm/sde/sde_encoder.c @@ -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 &&