[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