drm/nouveau/kms: prepare to support suspend/resume of display state with atomic
This is different from the equivilant functions in the atomic helpers in that we fully disable the pipe instead of just setting it to inactive. We do this (primarily) to ensure the framebuffer cleanup paths are hit, allowing buffers to be un-pinned from memory so they can be evicted to system memory and not lose their contents while suspended. Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
parent
986edb91b2
commit
b167db0e68
|
@ -25,6 +25,8 @@
|
|||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
#include <nvif/class.h>
|
||||
|
@ -573,11 +575,137 @@ nouveau_display_destroy(struct drm_device *dev)
|
|||
kfree(disp);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_atomic_disable_connector(struct drm_atomic_state *state,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct drm_connector_state *connector_state;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_plane_state *plane_state;
|
||||
struct drm_plane *plane;
|
||||
int ret;
|
||||
|
||||
if (!(crtc = connector->state->crtc))
|
||||
return 0;
|
||||
|
||||
connector_state = drm_atomic_get_connector_state(state, connector);
|
||||
if (IS_ERR(connector_state))
|
||||
return PTR_ERR(connector_state);
|
||||
|
||||
ret = drm_atomic_set_crtc_for_connector(connector_state, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
crtc_state = drm_atomic_get_crtc_state(state, crtc);
|
||||
if (IS_ERR(crtc_state))
|
||||
return PTR_ERR(crtc_state);
|
||||
|
||||
ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
crtc_state->active = false;
|
||||
|
||||
drm_for_each_plane_mask(plane, connector->dev, crtc_state->plane_mask) {
|
||||
plane_state = drm_atomic_get_plane_state(state, plane);
|
||||
if (IS_ERR(plane_state))
|
||||
return PTR_ERR(plane_state);
|
||||
|
||||
ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_atomic_set_fb_for_plane(plane_state, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_atomic_disable(struct drm_device *dev,
|
||||
struct drm_modeset_acquire_ctx *ctx)
|
||||
{
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_connector *connector;
|
||||
int ret;
|
||||
|
||||
state = drm_atomic_state_alloc(dev);
|
||||
if (!state)
|
||||
return -ENOMEM;
|
||||
|
||||
state->acquire_ctx = ctx;
|
||||
|
||||
drm_for_each_connector(connector, dev) {
|
||||
ret = nouveau_atomic_disable_connector(state, connector);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
ret = drm_atomic_commit(state);
|
||||
drm_atomic_state_put(state);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct drm_atomic_state *
|
||||
nouveau_atomic_suspend(struct drm_device *dev)
|
||||
{
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_atomic_state *state;
|
||||
int ret;
|
||||
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
retry:
|
||||
ret = drm_modeset_lock_all_ctx(dev, &ctx);
|
||||
if (ret < 0) {
|
||||
state = ERR_PTR(ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
state = drm_atomic_helper_duplicate_state(dev, &ctx);
|
||||
if (IS_ERR(state))
|
||||
goto unlock;
|
||||
|
||||
ret = nouveau_atomic_disable(dev, &ctx);
|
||||
if (ret < 0) {
|
||||
drm_atomic_state_put(state);
|
||||
state = ERR_PTR(ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
unlock:
|
||||
if (PTR_ERR(state) == -EDEADLK) {
|
||||
drm_modeset_backoff(&ctx);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
return state;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_display_suspend(struct drm_device *dev, bool runtime)
|
||||
{
|
||||
struct nouveau_display *disp = nouveau_display(dev);
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
if (dev->mode_config.funcs->atomic_commit) {
|
||||
if (!runtime) {
|
||||
disp->suspend = nouveau_atomic_suspend(dev);
|
||||
if (IS_ERR(disp->suspend)) {
|
||||
int ret = PTR_ERR(disp->suspend);
|
||||
disp->suspend = NULL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
nouveau_display_fini(dev, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
nouveau_display_fini(dev, true);
|
||||
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
|
@ -605,10 +733,20 @@ nouveau_display_suspend(struct drm_device *dev, bool runtime)
|
|||
void
|
||||
nouveau_display_resume(struct drm_device *dev, bool runtime)
|
||||
{
|
||||
struct nouveau_display *disp = nouveau_display(dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct drm_crtc *crtc;
|
||||
int ret, head;
|
||||
|
||||
if (dev->mode_config.funcs->atomic_commit) {
|
||||
nouveau_display_init(dev);
|
||||
if (disp->suspend) {
|
||||
drm_atomic_helper_resume(dev, disp->suspend);
|
||||
disp->suspend = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* re-pin fb/cursors */
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_framebuffer *nouveau_fb;
|
||||
|
|
|
@ -53,6 +53,8 @@ struct nouveau_display {
|
|||
/* not really hue and saturation: */
|
||||
struct drm_property *vibrant_hue_property;
|
||||
struct drm_property *color_vibrance_property;
|
||||
|
||||
struct drm_atomic_state *suspend;
|
||||
};
|
||||
|
||||
static inline struct nouveau_display *
|
||||
|
|
Loading…
Reference in New Issue