diff --git a/msm/msm_drv.c b/msm/msm_drv.c index 9eb1590540..c5fa4cf9fa 100644 --- a/msm/msm_drv.c +++ b/msm/msm_drv.c @@ -1626,6 +1626,57 @@ int msm_ioctl_power_ctrl(struct drm_device *dev, void *data, return rc; } +/** + * msm_ioctl_display_early_wakeup - early wakeup display. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + */ +int msm_ioctl_display_hint_ops(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_msm_display_hint *display_hint = data; + struct drm_msm_early_wakeup early_wakeup; + void __user *early_wakeup_usr; + struct msm_drm_private *priv; + struct msm_kms *kms; + + priv = dev->dev_private; + kms = priv->kms; + + if (unlikely(!display_hint)) { + DRM_ERROR("invalid ioctl data\n"); + return -EINVAL; + } + + SDE_EVT32(display_hint->hint_flags); + + if (display_hint->hint_flags == DRM_MSM_DISPLAY_EARLY_WAKEUP_HINT) { + if (!display_hint->data) { + DRM_ERROR("early_wakeup: wrong parameter\n"); + return -EINVAL; + } + + early_wakeup_usr = + (void __user *)((uintptr_t)display_hint->data); + + if (copy_from_user(&early_wakeup, early_wakeup_usr, + sizeof(early_wakeup))) { + DRM_ERROR("early_wakeup: copy from user failed\n"); + return -EINVAL; + } + + SDE_EVT32(early_wakeup.wakeup_hint); + if (kms && kms->funcs && kms->funcs->display_early_wakeup + && early_wakeup.wakeup_hint) + kms->funcs->display_early_wakeup(dev, + early_wakeup.connector_id); + } + + return 0; +} + static const struct drm_ioctl_desc msm_ioctls[] = { DRM_IOCTL_DEF_DRV(MSM_GEM_NEW, msm_ioctl_gem_new, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_AUTH|DRM_RENDER_ALLOW), @@ -1639,6 +1690,8 @@ static const struct drm_ioctl_desc msm_ioctls[] = { DRM_IOCTL_DEF_DRV(MSM_RMFB2, msm_ioctl_rmfb2, DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(MSM_POWER_CTRL, msm_ioctl_power_ctrl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_DISPLAY_HINT, msm_ioctl_display_hint_ops, + DRM_UNLOCKED), }; static const struct vm_operations_struct vm_ops = { diff --git a/msm/msm_kms.h b/msm/msm_kms.h index b4b8a0d2c5..957b07fd1d 100644 --- a/msm/msm_kms.h +++ b/msm/msm_kms.h @@ -100,6 +100,8 @@ struct msm_kms_funcs { void (*set_encoder_mode)(struct msm_kms *kms, struct drm_encoder *encoder, bool cmd_mode); + void (*display_early_wakeup)(struct drm_device *dev, + const int32_t connector_id); /* pm suspend/resume hooks */ int (*pm_suspend)(struct device *dev); int (*pm_resume)(struct device *dev); diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 0fbe17cf41..a68d0daa22 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -3690,6 +3690,53 @@ static void sde_encoder_input_event_work_handler(struct kthread_work *work) SDE_ENC_RC_EVENT_EARLY_WAKEUP); } +static void sde_encoder_early_wakeup_work_handler(struct kthread_work *work) +{ + struct sde_encoder_virt *sde_enc = container_of(work, + struct sde_encoder_virt, early_wakeup_work); + + if (!sde_enc) { + SDE_ERROR("invalid sde encoder\n"); + return; + } + + SDE_ATRACE_BEGIN("encoder_early_wakeup"); + sde_encoder_resource_control(&sde_enc->base, + SDE_ENC_RC_EVENT_EARLY_WAKEUP); + SDE_ATRACE_END("encoder_early_wakeup"); +} + +void sde_encoder_early_wakeup(struct drm_encoder *drm_enc) +{ + struct sde_encoder_virt *sde_enc = NULL; + struct msm_drm_thread *disp_thread = NULL; + struct msm_drm_private *priv = NULL; + + priv = drm_enc->dev->dev_private; + sde_enc = to_sde_encoder_virt(drm_enc); + + if (!sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE)) { + SDE_DEBUG_ENC(sde_enc, + "should only early wake up command mode display\n"); + return; + } + + if (!sde_enc->crtc || (sde_enc->crtc->index + >= ARRAY_SIZE(priv->event_thread))) { + SDE_ERROR("invalid CRTC: %d or crtc index: %d\n", + sde_enc->crtc == NULL, + sde_enc->crtc ? sde_enc->crtc->index : -EINVAL); + return; + } + + disp_thread = &priv->disp_thread[sde_enc->crtc->index]; + + SDE_ATRACE_BEGIN("queue_early_wakeup_work"); + kthread_queue_work(&disp_thread->worker, + &sde_enc->early_wakeup_work); + SDE_ATRACE_END("queue_early_wakeup_work"); +} + int sde_encoder_poll_line_counts(struct drm_encoder *drm_enc) { static const uint64_t timeout_us = 50000; @@ -4812,6 +4859,9 @@ struct drm_encoder *sde_encoder_init_with_ops( kthread_init_work(&sde_enc->input_event_work, sde_encoder_input_event_work_handler); + kthread_init_work(&sde_enc->early_wakeup_work, + sde_encoder_early_wakeup_work_handler); + kthread_init_work(&sde_enc->esd_trigger_work, sde_encoder_esd_trigger_work_handler); diff --git a/msm/sde/sde_encoder.h b/msm/sde/sde_encoder.h index c87c7a71c7..adbc63b742 100644 --- a/msm/sde/sde_encoder.h +++ b/msm/sde/sde_encoder.h @@ -168,6 +168,7 @@ struct sde_encoder_ops { * @rc_state: resource controller state * @delayed_off_work: delayed worker to schedule disabling of * clks and resources after IDLE_TIMEOUT time. + * @early_wakeup_work: worker to handle early wakeup event * @input_event_work: worker to handle input device touch events * @esd_trigger_work: worker to handle esd trigger events * @input_handler: handler for input device events @@ -234,6 +235,7 @@ struct sde_encoder_virt { struct mutex rc_lock; enum sde_enc_rc_states rc_state; struct kthread_delayed_work delayed_off_work; + struct kthread_work early_wakeup_work; struct kthread_work input_event_work; struct kthread_work esd_trigger_work; struct input_handler *input_handler; @@ -267,6 +269,12 @@ void sde_encoder_get_hw_resources(struct drm_encoder *encoder, struct sde_encoder_hw_resources *hw_res, struct drm_connector_state *conn_state); +/** + * sde_encoder_early_wakeup - early wake up display + * @encoder: encoder pointer + */ +void sde_encoder_early_wakeup(struct drm_encoder *drm_enc); + /** * sde_encoder_register_vblank_callback - provide callback to encoder that * will be called on the next vblank. diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 34f7b72c27..072190e167 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -3134,6 +3134,32 @@ end: drm_modeset_acquire_fini(&ctx); } + +void sde_kms_display_early_wakeup(struct drm_device *dev, + const int32_t connector_id) +{ + struct drm_connector_list_iter conn_iter; + struct drm_connector *conn; + struct drm_encoder *drm_enc; + + drm_connector_list_iter_begin(dev, &conn_iter); + + drm_for_each_connector_iter(conn, &conn_iter) { + if (connector_id != DRM_MSM_WAKE_UP_ALL_DISPLAYS && + connector_id != conn->base.id) + continue; + + if (conn->state && conn->state->best_encoder) + drm_enc = conn->state->best_encoder; + else + drm_enc = conn->encoder; + + sde_encoder_early_wakeup(drm_enc); + } + + drm_connector_list_iter_end(&conn_iter); +} + static void _sde_kms_pm_suspend_idle_helper(struct sde_kms *sde_kms, struct device *dev) { @@ -3408,6 +3434,7 @@ static const struct msm_kms_funcs kms_funcs = { .atomic_check = sde_kms_atomic_check, .get_format = sde_get_msm_format, .round_pixclk = sde_kms_round_pixclk, + .display_early_wakeup = sde_kms_display_early_wakeup, .pm_suspend = sde_kms_pm_suspend, .pm_resume = sde_kms_pm_resume, .destroy = sde_kms_destroy,