[PATCH 1/5] drm: Add helpers to kick off PSR enable/disable

Daniel Vetter daniel at ffwll.ch
Mon Mar 11 12:34:08 UTC 2019


On Fri, Mar 01, 2019 at 11:00:02AM -0500, Sean Paul wrote:
> On Fri, Mar 01, 2019 at 09:18:13AM +0100, Daniel Vetter wrote:
> > On Thu, Feb 28, 2019 at 04:09:30PM -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.
> > > 
> > > 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>
> 
> /snip to the good part
> 
> > 
> > So for the high-level design, before looking at the implementation
> > details:
> > - I agree with Jose that a per-crtc state sounds more like what we want,
> >   instead of global. Just from a conceptual point.
> 
> Agreed this would be better. I was mostly worried about resource sharing
> between crtcs. ie: If 2 crtcs share a clock, 1 goes to PSR and the other
> changes the rate to something incompatible. However I suppose the driver would
> be responsible for sussing this all out, so sure.
> 
> It'll be nice to avoid the global lock for psr.

Yeah atomic check must fail in this case. But that's not new, if
ACTVE=false then the clock will also be disabled (or should, for a good
driver), and atomic_check still must fail.

I don't think there's going to be a semantic difference between psr
self-refresh and ACTIVE=false. It might turn up more pre-existing driver
bugs, but then we just need to fix those (good igt coverage would be good,
maybe we can build on top of kms_frontbuffer_rendering a bit here, without
the intel-ism).

> > - We can imo flat-out require that your driver is using the fancy new
> >   dirtyfb helpers to remap the dirtyfb call to an atomic update. It's the
> >   clean design for sw controlled manual update displays anyway.
> > - That means your single entry points (instead of trying to catch all of
> >   them like above, and missing some like you do) are ->atomic_check and
> >   ->atomic_commit. That also gives you the clean hook into helpers.
> 
> The reason I did this was because new state was being calculated off the
> disabled state. So the psr disable commit needed to precede the atomic
> check. However, with your suggestions below I think we can avoid that :)
> 
> > - The duplicated state only works when no one else is looking, it's very
> >   fragile. single-threaded (from a driver pov) suspend/resume is ok, I
> >   don't think it's a good idea to expand this.
> 
> Yeah, fair. I was trying to stay out of the way of the existing code, but I'll
> integrate more tightly.
> 
> > 
> > Here's what I'd do:
> > - Stuff a new self_refresh_active state into drm_crtc_state (not sure we
> >   should call this psr or self_refresh - latter is imo better name since
> >   not associated with DP, but longer).
> 
> I struggled with this too, naming is hard.
> 
> > 
> > - Use crtc_state->self_refresh_active and crtc_state->active to compute
> >   the reported active state to userspace in the get_prop functions. This
> >   because doing a mass search&replace over all drivers is too much.
> >   Something like
> > 
> > 	real_active == active || self_refresh
> > 
> > - In duplicate_state always reset the state
> > 	active = real_active();
> > 	self_refresh = false;
> >   to disable self refresh and set active to what userspace the "ACTIVE"
> >   property should be.
> 
> We'll also have to alter the flags when they don't include
> DRM_MODE_ATOMIC_ALLOW_MODESET, and async commits would need to go through the
> synchronous path. Conversely, non-blocking commits will get back to userspace
> more quickly with this design.
> 
> This is the primary reason I kept the psr disable in a separate commit, so the
> original request could go through as intended (albeit a bit delayed, but there's
> no getting around that).

Hm. In a way we're lying to userspace here. But the lie is intentional I'd
say, so what about we make it official in the atomic ioctl? Something like
the below:

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index aa0c9af78b71..322bee5ab28c 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -368,6 +368,18 @@ static int drm_atomic_crtc_check(const struct drm_crtc_state *old_crtc_state,
 		return -EINVAL;
 	}
 
+	/* 
+	 * Transparent self-refresh is a bit a lie to userspace, keep up the
+	 * illusion. We must allow a modeset if one of the crtc is currently in
+	 * self-refresh mode and should be enabled.
+	 */
+	if (old_crtc_state->self_refresh && new_crtc_state->active) {
+		DRM_DEBUG_ATOMIC("[CRTC:%d:%s] self-refresh, transparently allowing modesets\n",
+				 crtc->base.id, crtc->name);
+
+		state->allow_modeset = true;
+	}
+
 	return 0;
 }
 
Note how this is in the core atomic ioctl code, not helpers, so mandatory
lying for all drivers :-)

igt coverage for this would be really nice, or maybe some in-kernel self-tests ....

> > - self_refresh state in connector_state sounds like a good idea
> > 
> > - Your idle worker would do a normal atomic commit, no evil state
> >   duplicate, and then set:
> >   	self_fresh = active;
> > 	active = false;
> >   Handling races left as an exercise :-)
> > 
> > - Somewhere in the commit helpers put a function to arm your idle work
> >   (per-crtc imo simplest).
> > 
> > This should give you a clean lasagne going from core ioctl -> core atomic
> > -> helpers <-> drivers. And not the spaghetti sprawl of finely sprinkling
> > the self refresh helpers all over.
> 
> Alright, I'll mock this up and see if it floats. Thanks for the detailed
> feedback!

Still catching up on mails, hopefully not too much confusion ...

Cheers, Daniel

> 
> Sean
> 
> > 
> > Aside: Going snowboarding next week, so if this is a bonghits idea I'm
> > afraid will take 1 week until I can spin something new :-)
> > 
> > Cheers, Daniel
> > 
> > > 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
> > > -- 
> > > Sean Paul, Software Engineer, Google / Chromium OS
> > > 
> > 
> > -- 
> > Daniel Vetter
> > Software Engineer, Intel Corporation
> > http://blog.ffwll.ch
> 
> -- 
> Sean Paul, Software Engineer, Google / Chromium OS

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


More information about the dri-devel mailing list