diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 340ca3ee130442b0af1962cc01346dbdf6b7d529..79b5f9d66a82819220c8a005047d25dffdef6a9d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -25,6 +25,8 @@ */ #include +#include +#include #include #include @@ -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; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h index 3760f0e758fdc854079c32d5d9f325f70bb89890..77d6fe18d91c009ff87a3048b99ffd431ad70f1c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.h +++ b/drivers/gpu/drm/nouveau/nouveau_display.h @@ -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 *