From patchwork Tue Sep 9 06:28:14 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Airlie X-Patchwork-Id: 4866351 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 976B5C0338 for ; Tue, 9 Sep 2014 06:28:37 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 99D832016C for ; Tue, 9 Sep 2014 06:28:36 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id B7421200E7 for ; Tue, 9 Sep 2014 06:28:35 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id A91536E3A8; Mon, 8 Sep 2014 23:28:34 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by gabe.freedesktop.org (Postfix) with ESMTP id 7F0B66E3A5; Mon, 8 Sep 2014 23:28:33 -0700 (PDT) Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s896SXY2014135 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 9 Sep 2014 02:28:33 -0400 Received: from tyrion-bne-redhat-com.bne.redhat.com (dhcp-41-68.bne.redhat.com [10.64.41.68]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s896SIP1023257; Tue, 9 Sep 2014 02:28:32 -0400 From: Dave Airlie To: dri-devel@lists.freedesktop.org, intel-gfx@lists.freedesktop.org Date: Tue, 9 Sep 2014 16:28:14 +1000 Message-Id: <1410244096-9854-10-git-send-email-airlied@gmail.com> In-Reply-To: <1410244096-9854-1-git-send-email-airlied@gmail.com> References: <1410244096-9854-1-git-send-email-airlied@gmail.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.27 Subject: [Intel-gfx] [PATCH 09/11] drm/crtc: workaround userspace trying to derail crtc stealing X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Spam-Status: No, score=-6.7 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, FREEMAIL_FROM,RCVD_IN_DNSWL_MED,RP_MATCHES_RCVD,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Dave Airlie This is probably not the greatest idea in the world, but if userspace does a modesetting sequences initial state : crtc 0 -> eDP-1 modeset : crtc 1 -> DP-4 (dual crtc) we have to steal crtc 2 for DP-3 modeset : crtc 2 -> eDP-1 we are kind off stuck, so when we see this, we back up the crtc configuration, proceed with the userspace modeset, then do the second modeset on the released crtc 0. Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 107 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 102 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 628f3af..e30518b 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2509,6 +2509,91 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc, } EXPORT_SYMBOL(drm_crtc_check_viewport); +static int drm_mode_get_crtc_set(struct drm_crtc *crtc, struct drm_mode_set *backup_set) +{ + struct drm_device *dev = crtc->dev; + struct drm_display_mode *mode; + struct drm_connector *connector; + int num_connectors = 0; + int i; + + backup_set->crtc = crtc; + backup_set->x = crtc->x; + backup_set->y = crtc->y; + backup_set->fb = crtc->primary->fb; + + mode = drm_mode_create(dev); + if (!mode) { + return -ENOMEM; + } + + *mode = crtc->mode; + backup_set->mode = mode; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (!connector->encoder) + continue; + if (!connector->encoder->crtc) + continue; + + if (connector->encoder->crtc == crtc) + num_connectors++; + } + + backup_set->connectors = kmalloc(num_connectors * sizeof(struct drm_connector *), GFP_KERNEL); + if (!backup_set->connectors) { + drm_mode_destroy(dev, mode); + return -ENOMEM; + } + + i = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (!connector->encoder) + continue; + if (!connector->encoder->crtc) + continue; + + if (connector->encoder->crtc == crtc) + backup_set->connectors[i++] = connector; + } + + backup_set->num_connectors = i; + return 0; +} + +static int drm_mode_reset_tiled_crtc(struct drm_mode_set *backup_set, + struct drm_crtc *tile_master) +{ + struct drm_crtc *crtc2, *pick_crtc = NULL; + struct drm_device *dev = backup_set->crtc->dev; + int ret; + /* first up we need to find another crtc to use */ + list_for_each_entry(crtc2, &dev->mode_config.crtc_list, head) { + if (crtc2 == backup_set->crtc) + continue; + if (crtc2->enabled && !crtc2->tile_master) + continue; + pick_crtc = crtc2; + break; + } + + if (!pick_crtc) { + DRM_DEBUG_KMS("unable to find backup crtc\n"); + goto out; + + } + + backup_set->crtc = pick_crtc; + + pick_crtc->tile_master = tile_master; + list_add_tail(&pick_crtc->tile, &tile_master->tile_crtc_list); + + ret = drm_mode_set_config_internal(backup_set); +out: + kfree(backup_set->connectors); + return 0; +} + /* tiled variants */ static int drm_mode_setcrtc_tiled(struct drm_mode_set *orig_set) { @@ -2631,6 +2716,9 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, struct drm_framebuffer *fb = NULL; struct drm_display_mode *mode = NULL; struct drm_mode_set set; + struct drm_mode_set tile_backup_set; + struct drm_crtc *backup_tile_master = NULL; + bool rework_backup = false; uint32_t __user *set_connectors_ptr; int ret; int i; @@ -2653,12 +2741,17 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); if (crtc->tile_master) { - if (crtc_req->mode_valid) - ret = -EBUSY; - else + if (!crtc_req->mode_valid) { ret = 0; - DRM_DEBUG_KMS("[CRTC:%d] refused due to tile %d\n", crtc->base.id, ret); - goto out; + goto out; + } + + drm_mode_get_crtc_set(crtc, &tile_backup_set); + DRM_DEBUG_KMS("[CRTC:%d] backing up tiling\n", crtc->base.id); + rework_backup = true; + backup_tile_master = crtc->tile_master; + crtc->tile_master = false; + list_del(&crtc->tile); } if (crtc_req->mode_valid) { @@ -2791,6 +2884,10 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, } ret = drm_mode_set_config_internal(&set); } + + if (rework_backup) { + drm_mode_reset_tiled_crtc(&tile_backup_set, backup_tile_master); + } out: if (fb) drm_framebuffer_unreference(fb);