summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_fb.c')
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fb.c85
1 files changed, 75 insertions, 10 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index e9005b9a5e8c..6f4cb4678cbc 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -418,6 +418,60 @@ static int vmw_fb_compute_depth(struct fb_var_screeninfo *var,
return 0;
}
+static int vmwgfx_set_config_internal(struct drm_mode_set *set)
+{
+ struct drm_crtc *crtc = set->crtc;
+ struct drm_framebuffer *fb;
+ struct drm_crtc *tmp;
+ struct drm_modeset_acquire_ctx *ctx;
+ struct drm_device *dev = set->crtc->dev;
+ int ret;
+
+ ctx = dev->mode_config.acquire_ctx;
+
+restart:
+ /*
+ * NOTE: ->set_config can also disable other crtcs (if we steal all
+ * connectors from it), hence we need to refcount the fbs across all
+ * crtcs. Atomic modeset will have saner semantics ...
+ */
+ drm_for_each_crtc(tmp, dev)
+ tmp->primary->old_fb = tmp->primary->fb;
+
+ fb = set->fb;
+
+ ret = crtc->funcs->set_config(set, ctx);
+ if (ret == 0) {
+ crtc->primary->crtc = crtc;
+ crtc->primary->fb = fb;
+ }
+
+ drm_for_each_crtc(tmp, dev) {
+ if (tmp->primary->fb)
+ drm_framebuffer_get(tmp->primary->fb);
+ if (tmp->primary->old_fb)
+ drm_framebuffer_put(tmp->primary->old_fb);
+ tmp->primary->old_fb = NULL;
+ }
+
+ if (ret == -EDEADLK) {
+ dev->mode_config.acquire_ctx = NULL;
+
+retry_locking:
+ drm_modeset_backoff(ctx);
+
+ ret = drm_modeset_lock_all_ctx(dev, ctx);
+ if (ret)
+ goto retry_locking;
+
+ dev->mode_config.acquire_ctx = ctx;
+
+ goto restart;
+ }
+
+ return ret;
+}
+
static int vmw_fb_kms_detach(struct vmw_fb_par *par,
bool detach_bo,
bool unref_bo)
@@ -434,9 +488,9 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par,
set.y = 0;
set.mode = NULL;
set.fb = NULL;
- set.num_connectors = 1;
+ set.num_connectors = 0;
set.connectors = &par->con;
- ret = drm_mode_set_config_internal(&set);
+ ret = vmwgfx_set_config_internal(&set);
if (ret) {
DRM_ERROR("Could not unset a mode.\n");
return ret;
@@ -451,13 +505,15 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par,
}
if (par->vmw_bo && detach_bo) {
+ struct vmw_private *vmw_priv = par->vmw_priv;
+
if (par->bo_ptr) {
ttm_bo_kunmap(&par->map);
par->bo_ptr = NULL;
}
if (unref_bo)
vmw_dmabuf_unreference(&par->vmw_bo);
- else
+ else if (vmw_priv->active_display_unit != vmw_du_legacy)
vmw_dmabuf_unpin(par->vmw_priv, par->vmw_bo, false);
}
@@ -576,7 +632,7 @@ static int vmw_fb_set_par(struct fb_info *info)
set.num_connectors = 1;
set.connectors = &par->con;
- ret = drm_mode_set_config_internal(&set);
+ ret = vmwgfx_set_config_internal(&set);
if (ret)
goto out_unlock;
@@ -585,18 +641,25 @@ static int vmw_fb_set_par(struct fb_info *info)
/*
* Pin before mapping. Since we don't know in what placement
- * to pin, call into KMS to do it for us.
+ * to pin, call into KMS to do it for us. LDU doesn't require
+ * additional pinning because set_config() would've pinned
+ * it already
*/
- ret = vfb->pin(vfb);
- if (ret) {
- DRM_ERROR("Could not pin the fbdev framebuffer.\n");
- goto out_unlock;
+ if (vmw_priv->active_display_unit != vmw_du_legacy) {
+ ret = vfb->pin(vfb);
+ if (ret) {
+ DRM_ERROR("Could not pin the fbdev "
+ "framebuffer.\n");
+ goto out_unlock;
+ }
}
ret = ttm_bo_kmap(&par->vmw_bo->base, 0,
par->vmw_bo->base.num_pages, &par->map);
if (ret) {
- vfb->unpin(vfb);
+ if (vmw_priv->active_display_unit != vmw_du_legacy)
+ vfb->unpin(vfb);
+
DRM_ERROR("Could not map the fbdev framebuffer.\n");
goto out_unlock;
}
@@ -822,7 +885,9 @@ int vmw_fb_off(struct vmw_private *vmw_priv)
flush_delayed_work(&par->local_work);
mutex_lock(&par->bo_mutex);
+ drm_modeset_lock_all(vmw_priv->dev);
(void) vmw_fb_kms_detach(par, true, false);
+ drm_modeset_unlock_all(vmw_priv->dev);
mutex_unlock(&par->bo_mutex);
return 0;