Commit b167db0e authored by Ben Skeggs's avatar Ben Skeggs

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: default avatarBen Skeggs <bskeggs@redhat.com>
parent 986edb91
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
*/ */
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <nvif/class.h> #include <nvif/class.h>
...@@ -573,11 +575,137 @@ nouveau_display_destroy(struct drm_device *dev) ...@@ -573,11 +575,137 @@ nouveau_display_destroy(struct drm_device *dev)
kfree(disp); 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 int
nouveau_display_suspend(struct drm_device *dev, bool runtime) nouveau_display_suspend(struct drm_device *dev, bool runtime)
{ {
struct nouveau_display *disp = nouveau_display(dev);
struct drm_crtc *crtc; 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); nouveau_display_fini(dev, true);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
...@@ -605,10 +733,20 @@ nouveau_display_suspend(struct drm_device *dev, bool runtime) ...@@ -605,10 +733,20 @@ nouveau_display_suspend(struct drm_device *dev, bool runtime)
void void
nouveau_display_resume(struct drm_device *dev, bool runtime) nouveau_display_resume(struct drm_device *dev, bool runtime)
{ {
struct nouveau_display *disp = nouveau_display(dev);
struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_drm *drm = nouveau_drm(dev);
struct drm_crtc *crtc; struct drm_crtc *crtc;
int ret, head; 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 */ /* re-pin fb/cursors */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_framebuffer *nouveau_fb; struct nouveau_framebuffer *nouveau_fb;
......
...@@ -53,6 +53,8 @@ struct nouveau_display { ...@@ -53,6 +53,8 @@ struct nouveau_display {
/* not really hue and saturation: */ /* not really hue and saturation: */
struct drm_property *vibrant_hue_property; struct drm_property *vibrant_hue_property;
struct drm_property *color_vibrance_property; struct drm_property *color_vibrance_property;
struct drm_atomic_state *suspend;
}; };
static inline struct nouveau_display * static inline struct nouveau_display *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment