[PATCH 1/5] drm: Add helpers to kick off PSR enable/disable
Souza, Jose
jose.souza at intel.com
Thu Feb 28 22:29:47 UTC 2019
On Thu, 2019-02-28 at 16:09 -0500, Sean Paul wrote:
> From: Sean Paul <seanpaul at chromium.org>
>
> This patch adds a new drm helper library to help drivers implement
> PSR. Drivers choosing to use it will register connectors with
> PSR-capable displays connected and will receive callbacks when it's
> time
> to enter or exit PSR.
This helpers should target connector not the entire driver, a
modification in any sink would cause a PSR exit in all PSR panels.
Also do atomic commit in drm_psr_helper_flush() that is called from
another atomic commit sounds wrong.
During the atomic checks it should probably just set enable and active
states of drm_crtc_state and the CRTC would be active when committing
changes.
>
> In its current form, it has a timer which will trigger after a
> driver-specified amount of inactivity. When the timer triggers, the
> helpers will save the current atomic state and issue a new state
> which
> has the PSR-enabled pipes turned off. On the next update, the drm
> core
> will poke the PSR helpers to restore the saved state to the driver
> before
> servicing said update.
>
> From the driver's perspective, this works like a regular
> disable/enable
> cycle. The driver need only check the 'psr_transition' state in
> connector_state and keep the panel turned on when in .disable(),
> while
> everything else will cycle off as normal. If drivers want more
> control,
> they can use the psr_transition state to enter a low-power state to
> minimize PSR exit time.
>
> While this carries the PSR moniker, it is not specific to the
> DisplayPort technology. This can be used for power savings with other
> types of self refresh, such as MIPI command mode.
>
> Cc: Zain Wang <wzz at rock-chips.com>
> Cc: Tomasz Figa <tfiga at chromium.org>
> Signed-off-by: Sean Paul <seanpaul at chromium.org>
> ---
> Documentation/gpu/drm-kms-helpers.rst | 9 +
> drivers/gpu/drm/Makefile | 2 +-
> drivers/gpu/drm/drm_atomic_helper.c | 34 +++
> drivers/gpu/drm/drm_atomic_uapi.c | 5 +
> drivers/gpu/drm/drm_fb_helper.c | 9 +
> drivers/gpu/drm/drm_framebuffer.c | 18 ++
> drivers/gpu/drm/drm_psr_helper.c | 343
> ++++++++++++++++++++++++++
> include/drm/drm_connector.h | 22 ++
> include/drm/drm_crtc.h | 11 +
> include/drm/drm_mode_config.h | 6 +
> include/drm/drm_psr_helper.h | 24 ++
> 11 files changed, 482 insertions(+), 1 deletion(-)
> create mode 100644 drivers/gpu/drm/drm_psr_helper.c
> create mode 100644 include/drm/drm_psr_helper.h
>
> diff --git a/Documentation/gpu/drm-kms-helpers.rst
> b/Documentation/gpu/drm-kms-helpers.rst
> index 17ca7f8bf3d3..d218a113bd52 100644
> --- a/Documentation/gpu/drm-kms-helpers.rst
> +++ b/Documentation/gpu/drm-kms-helpers.rst
> @@ -107,6 +107,15 @@ fbdev Helper Functions Reference
> .. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c
> :export:
>
> +Panel Self Refresh Helper Reference
> +===================================
> +
> +.. kernel-doc:: drivers/gpu/drm/drm_psr_helper.c
> + :doc: overview
> +
> +.. kernel-doc:: drivers/gpu/drm/drm_psr_helper.c
> + :export:
> +
> Framebuffer CMA Helper Functions Reference
> ==========================================
>
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 1ac55c65eac0..bff80fb946c7 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -19,7 +19,7 @@ drm-y := drm_auth.o drm_bufs.o
> drm_cache.o \
> drm_plane.o drm_color_mgmt.o drm_print.o \
> drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \
> drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o
> \
> - drm_atomic_uapi.o
> + drm_atomic_uapi.o drm_psr_helper.o
>
> drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
> drm-$(CONFIG_DRM_VM) += drm_vm.o
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c
> b/drivers/gpu/drm/drm_atomic_helper.c
> index c53ecbd9abdd..f5284d55f170 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -30,6 +30,7 @@
> #include <drm/drm_atomic_uapi.h>
> #include <drm/drm_plane_helper.h>
> #include <drm/drm_atomic_helper.h>
> +#include <drm/drm_psr_helper.h>
> #include <drm/drm_writeback.h>
> #include <drm/drm_damage_helper.h>
> #include <linux/dma-fence.h>
> @@ -2746,6 +2747,10 @@ int drm_atomic_helper_update_plane(struct
> drm_plane *plane,
> struct drm_plane_state *plane_state;
> int ret = 0;
>
> + ret = drm_psr_helper_flush(plane->dev, ctx);
> + if (ret)
> + return ret;
> +
> state = drm_atomic_state_alloc(plane->dev);
> if (!state)
> return -ENOMEM;
> @@ -2797,6 +2802,10 @@ int drm_atomic_helper_disable_plane(struct
> drm_plane *plane,
> struct drm_plane_state *plane_state;
> int ret = 0;
>
> + ret = drm_psr_helper_flush(plane->dev, ctx);
> + if (ret)
> + return ret;
> +
> state = drm_atomic_state_alloc(plane->dev);
> if (!state)
> return -ENOMEM;
> @@ -2934,11 +2943,16 @@ int drm_atomic_helper_set_config(struct
> drm_mode_set *set,
> struct drm_crtc *crtc = set->crtc;
> int ret = 0;
>
> + ret = drm_psr_helper_flush(crtc->dev, ctx);
> + if (ret)
> + return ret;
> +
> state = drm_atomic_state_alloc(crtc->dev);
> if (!state)
> return -ENOMEM;
>
> state->acquire_ctx = ctx;
> +
> ret = __drm_atomic_helper_set_config(set, state);
> if (ret != 0)
> goto fail;
> @@ -3139,6 +3153,10 @@ void drm_atomic_helper_shutdown(struct
> drm_device *dev)
>
> DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
>
> + ret = drm_psr_helper_flush(dev, &ctx);
> + if (ret)
> + DRM_ERROR("PSR flush during shutdown failed with %i\n",
> ret);
> +
> ret = __drm_atomic_helper_disable_all(dev, &ctx, true);
> if (ret)
> DRM_ERROR("Disabling all crtc's during unload failed
> with %i\n", ret);
> @@ -3271,6 +3289,10 @@ struct drm_atomic_state
> *drm_atomic_helper_suspend(struct drm_device *dev)
>
> DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, err);
>
> + err = drm_psr_helper_flush(dev, &ctx);
> + if (err)
> + goto unlock;
> +
> state = drm_atomic_helper_duplicate_state(dev, &ctx);
> if (IS_ERR(state))
> goto unlock;
> @@ -3436,6 +3458,10 @@ int drm_atomic_helper_page_flip(struct
> drm_crtc *crtc,
> struct drm_atomic_state *state;
> int ret = 0;
>
> + ret = drm_psr_helper_flush(crtc->dev, ctx);
> + if (ret)
> + return ret;
> +
> state = drm_atomic_state_alloc(plane->dev);
> if (!state)
> return -ENOMEM;
> @@ -3481,6 +3507,10 @@ int drm_atomic_helper_page_flip_target(struct
> drm_crtc *crtc,
> struct drm_crtc_state *crtc_state;
> int ret = 0;
>
> + ret = drm_psr_helper_flush(plane->dev, ctx);
> + if (ret)
> + return ret;
> +
> state = drm_atomic_state_alloc(plane->dev);
> if (!state)
> return -ENOMEM;
> @@ -3532,6 +3562,10 @@ int drm_atomic_helper_legacy_gamma_set(struct
> drm_crtc *crtc,
> int i, ret = 0;
> bool replaced;
>
> + ret = drm_psr_helper_flush(dev, ctx);
> + if (ret)
> + return ret;
> +
> state = drm_atomic_state_alloc(crtc->dev);
> if (!state)
> return -ENOMEM;
> diff --git a/drivers/gpu/drm/drm_atomic_uapi.c
> b/drivers/gpu/drm/drm_atomic_uapi.c
> index 4eb81f10bc54..21d7771d6ec7 100644
> --- a/drivers/gpu/drm/drm_atomic_uapi.c
> +++ b/drivers/gpu/drm/drm_atomic_uapi.c
> @@ -29,6 +29,7 @@
> #include <drm/drm_atomic_uapi.h>
> #include <drm/drm_atomic.h>
> #include <drm/drm_print.h>
> +#include <drm/drm_psr_helper.h>
> #include <drm/drm_drv.h>
> #include <drm/drm_writeback.h>
> #include <drm/drm_vblank.h>
> @@ -1314,6 +1315,10 @@ int drm_mode_atomic_ioctl(struct drm_device
> *dev,
> fence_state = NULL;
> num_fences = 0;
>
> + ret = drm_psr_helper_flush(dev, state->acquire_ctx);
> + if (ret)
> + goto out;
> +
> for (i = 0; i < arg->count_objs; i++) {
> uint32_t obj_id, count_props;
> struct drm_mode_object *obj;
> diff --git a/drivers/gpu/drm/drm_fb_helper.c
> b/drivers/gpu/drm/drm_fb_helper.c
> index 04d23cb430bf..a56ae2acda90 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -41,6 +41,7 @@
> #include <drm/drm_crtc_helper.h>
> #include <drm/drm_atomic.h>
> #include <drm/drm_atomic_helper.h>
> +#include <drm/drm_psr_helper.h>
>
> #include "drm_crtc_internal.h"
> #include "drm_crtc_helper_internal.h"
> @@ -406,6 +407,10 @@ static int restore_fbdev_mode_atomic(struct
> drm_fb_helper *fb_helper, bool activ
>
> state->acquire_ctx = &ctx;
> retry:
> + ret = drm_psr_helper_flush(dev, state->acquire_ctx);
> + if (ret)
> + goto out_state;
> +
> drm_for_each_plane(plane, dev) {
> plane_state = drm_atomic_get_plane_state(state, plane);
> if (IS_ERR(plane_state)) {
> @@ -1442,6 +1447,10 @@ static int setcmap_atomic(struct fb_cmap
> *cmap, struct fb_info *info)
>
> state->acquire_ctx = &ctx;
> retry:
> + ret = drm_psr_helper_flush(dev, &ctx);
> + if (ret)
> + goto out_state;
> +
> for (i = 0; i < fb_helper->crtc_count; i++) {
> crtc = fb_helper->crtc_info[i].mode_set.crtc;
>
> diff --git a/drivers/gpu/drm/drm_framebuffer.c
> b/drivers/gpu/drm/drm_framebuffer.c
> index d8d75e25f6fb..43b029a88578 100644
> --- a/drivers/gpu/drm/drm_framebuffer.c
> +++ b/drivers/gpu/drm/drm_framebuffer.c
> @@ -27,6 +27,7 @@
> #include <drm/drm_atomic.h>
> #include <drm/drm_atomic_uapi.h>
> #include <drm/drm_print.h>
> +#include <drm/drm_psr_helper.h>
> #include <drm/drm_util.h>
>
> #include "drm_internal.h"
> @@ -572,6 +573,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device
> *dev,
> struct drm_clip_rect __user *clips_ptr;
> struct drm_clip_rect *clips = NULL;
> struct drm_mode_fb_dirty_cmd *r = data;
> + struct drm_modeset_acquire_ctx ctx;
> struct drm_framebuffer *fb;
> unsigned flags;
> int num_clips;
> @@ -580,10 +582,24 @@ int drm_mode_dirtyfb_ioctl(struct drm_device
> *dev,
> if (!drm_core_check_feature(dev, DRIVER_MODESET))
> return -EOPNOTSUPP;
>
> + drm_modeset_acquire_init(&ctx, 0);
> +
> fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id);
> if (!fb)
> return -ENOENT;
>
> +retry:
> + ret = drm_psr_helper_flush(fb->dev, &ctx);
> + if (ret) {
> + if (ret == -EDEADLK) {
> + ret = drm_modeset_backoff(&ctx);
> + if (!ret)
> + goto retry;
> + } else {
> + goto out_err1;
> + }
> + }
> +
> num_clips = r->num_clips;
> clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r-
> >clips_ptr;
>
> @@ -630,6 +646,8 @@ int drm_mode_dirtyfb_ioctl(struct drm_device
> *dev,
> kfree(clips);
> out_err1:
> drm_framebuffer_put(fb);
> + drm_modeset_drop_locks(&ctx);
> + drm_modeset_acquire_fini(&ctx);
>
> return ret;
> }
> diff --git a/drivers/gpu/drm/drm_psr_helper.c
> b/drivers/gpu/drm/drm_psr_helper.c
> new file mode 100644
> index 000000000000..0b57a2a53075
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_psr_helper.c
> @@ -0,0 +1,343 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright (C) 2019 Google, Inc.
> + *
> + * Authors:
> + * Sean Paul <seanpaul at chromium.org>
> + */
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_connector.h>
> +#include <drm/drm_device.h>
> +#include <drm/drm_mode_config.h>
> +#include <drm/drm_modeset_lock.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_psr_helper.h>
> +#include <linux/bitops.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +
> +/**
> + * DOC: overview
> + *
> + * This helper library provides an easy way for drivers to leverage
> the atomic
> + * framework to implement panel self refresh (PSR) support. Drivers
> are
> + * responsible for intializing and cleaning up the PSR helpers on
> load/unload.
> + * When drivers identify a display that supports self refreshing
> (eDP or MIPI
> + * command mode), it should register the affected connector with the
> PSR
> + * helpers.
> + *
> + * Once a connector is registered, the PSR helpers will monitor
> activity and
> + * call back into the driver to enable/disable PSR as appropriate.
> The best way
> + * to think about this is that it's a DPMS on/off request with a
> flag set in
> + * state that tells you to disable/enable PSR on the panel instead
> of power-
> + * cycling it.
> + *
> + * Drivers may choose to fully disable their crtc/encoder/bridge
> hardware, or
> + * they can use the "psr_transition" flag in crtc and connector
> state if they
> + * want to enter low power mode without full disable (in case full
> + * disable/enable is too slow).
> + *
> + * PSR will be deactivated if there are any atomic updates, even
> updates that do
> + * not affect the connectors which are self refreshing. Supporting
> this is
> + * possible but non-trivial due to sharing of hardware resources.
> Similarly, if
> + * a crtc is driving multiple connectors, PSR will not be initiated
> on any of
> + * those connectors.
> + */
> +
> +struct drm_psr_state {
> + struct drm_device *dev;
> + struct drm_modeset_lock mutex;
> + struct delayed_work entry_work;
> + struct drm_atomic_state *save_state;
> + unsigned int entry_delay_ms;
> +};
> +
> +static void drm_psr_helper_entry_work(struct work_struct *work)
> +{
> + struct drm_psr_state *psr_state =
> container_of(to_delayed_work(work),
> + struct
> drm_psr_state,
> + entry_work);
> + struct drm_atomic_state *save_state;
> + struct drm_device *dev = psr_state->dev;
> + struct drm_modeset_acquire_ctx ctx;
> + struct drm_atomic_state *state;
> + struct drm_connector *conn;
> + struct drm_connector_list_iter conn_iter;
> + struct drm_connector_state *conn_state;
> + struct drm_crtc *crtc;
> + struct drm_crtc_state *crtc_state;
> + bool commit = false;
> + int ret, i;
> +
> + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
> +
> + ret = drm_modeset_lock(&psr_state->mutex, &ctx);
> + if (ret)
> + goto out;
> +
> + /*
> + * The only way this can happen is if we schedule the worker
> while it's
> + * already running and that timeout subsequently elapses. Since
> we hold
> + * the psr_state->mutex when scheduling, we also know where the
> worker
> + * is sitting in its execution (hint: look up). In this case,
> it's
> + * possible for the entry worker to run twice for the same
> commit. Since
> + * the hardware hasn't changed since the last save state, just
> kick out.
> + */
> + if (psr_state->save_state)
> + goto out;
> +
> + state = drm_atomic_state_alloc(dev);
> + if (!state) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + save_state = drm_atomic_helper_duplicate_state(dev, &ctx);
> +
> + /*
> + * Now that we have the current the HW state saved, we have to
> flip the
> + * psr_transition bit so we know what type of enable we're
> dealing with
> + * when coming back on.
> + *
> + * NOTE: We don't check conn->capable here since that could
> change out
> + * from under us. We'll trust the atomic core to only call
> enable if
> + * necessary (ie: only for those connectors/crtcs that
> currently have
> + * psr enabled).
> + */
> + if (IS_ERR(save_state)) {
> + ret = PTR_ERR(save_state);
> + goto out;
> + }
> + for_each_new_connector_in_state(save_state, conn, conn_state,
> i) {
> + if (!conn_state->crtc)
> + continue;
> + conn_state->psr_transition = true;
> + }
> + for_each_new_crtc_in_state(save_state, crtc, crtc_state, i) {
> + if (!crtc_state->active)
> + continue;
> + crtc_state->psr_transition = true;
> + }
> +
> + state->acquire_ctx = &ctx;
> + drm_connector_list_iter_begin(psr_state->dev, &conn_iter);
> + drm_for_each_connector_iter(conn, &conn_iter) {
> + if (!conn->psr_capable)
> + continue;
> +
> + conn_state = drm_atomic_get_connector_state(state,
> conn);
> + if (IS_ERR(conn_state)) {
> + ret = PTR_ERR(conn_state);
> + drm_connector_list_iter_end(&conn_iter);
> + goto out_free_state;
> + }
> +
> + if (!conn_state->crtc)
> + continue;
> +
> + crtc_state = drm_atomic_get_crtc_state(state,
> conn_state->crtc);
> + if (IS_ERR(crtc_state)) {
> + ret = PTR_ERR(crtc_state);
> + drm_connector_list_iter_end(&conn_iter);
> + goto out_free_state;
> + }
> +
> + /* Don't use PSR if the crtc is driving multiple
> connectors */
> + if (hweight_long(crtc_state->connector_mask) > 1)
> + continue;
> +
> + commit = true;
> + crtc_state->active = false;
> + crtc_state->psr_transition = true;
> + conn_state->psr_transition = true;
> + }
> + drm_connector_list_iter_end(&conn_iter);
> +
> + /* Nothing to commit, so just exit */
> + if (!commit)
> + goto out_free_state;
> +
> + ret = drm_atomic_commit(state);
> + if (ret)
> + goto out_free_state;
> +
> + psr_state->save_state = save_state;
> + goto out;
> +
> +out_free_state:
> + drm_atomic_state_put(save_state);
> + drm_atomic_state_put(state);
> +out:
> + DRM_MODESET_LOCK_ALL_END(ctx, ret);
> +}
> +
> +static int
> +drm_psr_helper_acquire_modeset_locks(struct drm_atomic_state *state,
> + struct drm_modeset_acquire_ctx
> *ctx)
> +{
> + struct drm_mode_config *config = &state->dev->mode_config;
> + struct drm_crtc *crtc;
> + struct drm_crtc_state *new_crtc_state;
> + struct drm_plane *plane;
> + struct drm_plane_state *new_plane_state;
> + int ret, i;
> +
> + ret = drm_modeset_lock(&config->connection_mutex, ctx);
> + if (ret)
> + return ret;
> +
> + for_each_new_plane_in_state(state, plane, new_plane_state, i) {
> + ret = drm_modeset_lock(&plane->mutex, ctx);
> + if (ret)
> + return ret;
> + }
> +
> + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
> + ret = drm_modeset_lock(&crtc->mutex, ctx);
> + if (ret)
> + return ret;
> + }
> + return 0;
> +}
> +
> +/**
> + * drm_psr_helper_flush - Restore the hardware to pre-PSR state
> + * @dev: DRM device
> + * @ctx: An acquire context to use for restoring the state
> + *
> + * This function should be called before every drm_atomic_commit to
> ensure any
> + * connectors that are currently self-refreshing revert back to
> being bus
> + * driven. Drivers may call this function outside of the atomic
> hooks if
> + * they wish to disable PSR pre-emptively (such as upon an input
> event or when
> + * GPU becomes active).
> + *
> + * If everything is successful, this function will schedule the PSR
> entry worker
> + * to enable PSR after the driver-specified timeout.
> + *
> + * If the PSR helper is not being used, this is a no-op.
> + */
> +int drm_psr_helper_flush(struct drm_device *dev,
> + struct drm_modeset_acquire_ctx *ctx)
> +{
> + struct drm_mode_config *config = &dev->mode_config;
> + struct drm_psr_state *psr_state = config->psr_state;
> + int ret;
> +
> + if (!psr_state)
> + return 0;
> +
> + ret = drm_modeset_lock(&psr_state->mutex, ctx);
> + if (ret)
> + return ret;
> +
> + if (!psr_state->save_state)
> + goto out;
> +
> + ret = drm_psr_helper_acquire_modeset_locks(psr_state-
> >save_state, ctx);
> + if (ret)
> + goto out;
> +
> + ret = drm_atomic_helper_commit_duplicated_state(psr_state-
> >save_state,
> + ctx);
> +
> +out:
> + psr_state->save_state = NULL;
> + if (!ret) {
> + mod_delayed_work(system_wq, &psr_state->entry_work,
> + msecs_to_jiffies(psr_state-
> >entry_delay_ms));
> + }
> + return ret;
> +}
> +EXPORT_SYMBOL(drm_psr_helper_flush);
> +
> +/**
> + * drm_psr_helper_register - Registers a connector with the PSR
> helpers
> + * @connector: the connector which has a PSR-supported display
> attached
> + *
> + * Note that this can be called once on initialization for fixed
> panels, or
> + * during enable/hotplug.
> + */
> +int drm_psr_helper_register(struct drm_connector *connector)
> +{
> + struct drm_mode_config *config = &connector->dev->mode_config;
> + struct drm_psr_state *psr_state = config->psr_state;
> +
> + /* PSR helpers are uninitialized */
> + if (WARN_ON(!psr_state))
> + return -EINVAL;
> +
> + /* Acquired via psr_helper_flush */
> + if (!drm_modeset_is_locked(&psr_state->mutex))
> + return -EINVAL;
> +
> + connector->psr_capable = true;
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_psr_helper_register);
> +
> +/**
> + * drm_psr_helper_unregister - Unregisters a connector with the PSR
> helpers
> + * @connector: the connector to unregister
> + */
> +int drm_psr_helper_unregister(struct drm_connector *connector)
> +{
> + struct drm_mode_config *config = &connector->dev->mode_config;
> + struct drm_psr_state *psr_state = config->psr_state;
> +
> + /* PSR helpers are uninitialized */
> + if (WARN_ON(!psr_state))
> + return -EINVAL;
> +
> + /* Acquired via psr_helper_flush */
> + if (!drm_modeset_is_locked(&psr_state->mutex))
> + return -EINVAL;
> +
> + connector->psr_capable = false;
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_psr_helper_unregister);
> +
> +/**
> + * drm_psr_helper_init - Initializes the PSR helpers
> + * @dev: DRM device
> + * @entry_delay_ms: The amount of time to wait after an atomic
> commit before
> + * activating PSR
> + *
> + * Drivers using the PSR helpers must call this some time after
> mode_config
> + * is initialized in order to make use of the PSR helpers. Typically
> + * entry_delay_ms is a function of how quickly the hardware can
> enter/exit PSR.
> + */
> +int drm_psr_helper_init(struct drm_device *dev, unsigned int
> entry_delay_ms)
> +{
> + struct drm_mode_config *config = &dev->mode_config;
> + struct drm_psr_state *psr_state;
> +
> + psr_state = kzalloc(sizeof(*psr_state), GFP_KERNEL);
> + if (!psr_state)
> + return -ENOMEM;
> +
> + INIT_DELAYED_WORK(&psr_state->entry_work,
> drm_psr_helper_entry_work);
> + drm_modeset_lock_init(&psr_state->mutex);
> + psr_state->dev = dev;
> + psr_state->entry_delay_ms = entry_delay_ms;
> +
> + config->psr_state = psr_state;
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_psr_helper_init);
> +
> +/**
> + * drm_psr_helper_cleanup - De-initializes the PSR helpers
> + * @dev: DRM device
> + */
> +void drm_psr_helper_cleanup(struct drm_device *dev)
> +{
> + struct drm_mode_config *config = &dev->mode_config;
> +
> + cancel_delayed_work_sync(&config->psr_state->entry_work);
> + kfree(config->psr_state);
> + config->psr_state = NULL;
> +}
> +EXPORT_SYMBOL(drm_psr_helper_cleanup);
> diff --git a/include/drm/drm_connector.h
> b/include/drm/drm_connector.h
> index c8061992d6cb..9612b3d56f30 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -501,6 +501,17 @@ struct drm_connector_state {
> /** @tv: TV connector state */
> struct drm_tv_connector_state tv;
>
> + /**
> + * @psr_transition:
> + *
> + * Used by the PSR helpers to denote when a PSR transition is
> occuring.
> + * If your connector is PSR-capable, register it with the
> helpers and
> + * check this flag in .enable() and .disable(). If it is true,
> instead
> + * of shutting off the panel, put it in or take it out of self
> + * refreshing mode.
> + */
> + bool psr_transition;
> +
> /**
> * @picture_aspect_ratio: Connector property to control the
> * HDMI infoframe aspect ratio setting.
> @@ -993,6 +1004,17 @@ struct drm_connector {
> */
> struct drm_display_info display_info;
>
> + /**
> + * @psr_capable:
> + *
> + * Set by the driver via drm_psr_helper_register(). Signals
> that this
> + * connector (and associated pipe) is PSR capable and should be
> put in
> + * low-power mode when it is inactive.
> + *
> + * Protected by &drm_mode_config.psr_state.mutex
> + */
> + bool psr_capable;
> +
> /** @funcs: connector control functions */
> const struct drm_connector_funcs *funcs;
>
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index f7c3022dbdf4..10acd4fc0991 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -299,6 +299,17 @@ struct drm_crtc_state {
> */
> bool vrr_enabled;
>
> + /**
> + * @psr_transition:
> + *
> + * Used by the PSR helpers to denote when a PSR transition is
> occuring.
> + * This will be set on enable/disable callbacks when PSR is
> being
> + * enabled or disabled. In some cases, it may not be desirable
> to fully
> + * shut off the crtc during PSR. CRTC's can inspect this flag
> and
> + * determine the best course of action.
> + */
> + bool psr_transition;
> +
> /**
> * @event:
> *
> diff --git a/include/drm/drm_mode_config.h
> b/include/drm/drm_mode_config.h
> index 7f60e8eb269a..371b80d090ab 100644
> --- a/include/drm/drm_mode_config.h
> +++ b/include/drm/drm_mode_config.h
> @@ -37,6 +37,7 @@ struct drm_atomic_state;
> struct drm_mode_fb_cmd2;
> struct drm_format_info;
> struct drm_display_mode;
> +struct drm_psr_state;
>
> /**
> * struct drm_mode_config_funcs - basic driver provided mode setting
> functions
> @@ -900,6 +901,11 @@ struct drm_mode_config {
> */
> struct drm_atomic_state *suspend_state;
>
> + /**
> + * @psr_state: Holds the state for the psr helper
> + */
> + struct drm_psr_state *psr_state;
> +
> const struct drm_mode_config_helper_funcs *helper_private;
> };
>
> diff --git a/include/drm/drm_psr_helper.h
> b/include/drm/drm_psr_helper.h
> new file mode 100644
> index 000000000000..972c4ec98d05
> --- /dev/null
> +++ b/include/drm/drm_psr_helper.h
> @@ -0,0 +1,24 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright (C) 2019 Google, Inc.
> + *
> + * Authors:
> + * Sean Paul <seanpaul at chromium.org>
> + */
> +#ifndef DRM_PSR_HELPER_H_
> +#define DRM_PSR_HELPER_H_
> +
> +struct drm_connector;
> +struct drm_device;
> +struct drm_psr_state;
> +struct drm_modeset_acquire_ctx;
> +
> +int drm_psr_helper_flush(struct drm_device *dev,
> + struct drm_modeset_acquire_ctx *ctx);
> +
> +int drm_psr_helper_register(struct drm_connector *connector);
> +int drm_psr_helper_unregister(struct drm_connector *connector);
> +
> +int drm_psr_helper_init(struct drm_device *dev, unsigned int
> entry_delay_ms);
> +void drm_psr_helper_cleanup(struct drm_device *dev);
> +#endif
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: This is a digitally signed message part
URL: <https://lists.freedesktop.org/archives/dri-devel/attachments/20190228/2abdaa3b/attachment.sig>
More information about the dri-devel
mailing list