From patchwork Tue May 24 11:24:11 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Maarten Lankhorst X-Patchwork-Id: 9133437 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id E310A607D3 for ; Tue, 24 May 2016 11:24:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D5D1B28164 for ; Tue, 24 May 2016 11:24:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CA5742824F; Tue, 24 May 2016 11:24:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C663B28164 for ; Tue, 24 May 2016 11:24:44 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id C9CA56E725; Tue, 24 May 2016 11:24:43 +0000 (UTC) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mblankhorst.nl (mblankhorst.nl [IPv6:2a02:2308::216:3eff:fe92:dfa3]) by gabe.freedesktop.org (Postfix) with ESMTPS id 7AACC6E72A for ; Tue, 24 May 2016 11:24:17 +0000 (UTC) From: Maarten Lankhorst To: intel-gfx@lists.freedesktop.org Date: Tue, 24 May 2016 13:24:11 +0200 Message-Id: <1464089051-3995-5-git-send-email-maarten.lankhorst@linux.intel.com> X-Mailer: git-send-email 2.5.5 In-Reply-To: <1464089051-3995-1-git-send-email-maarten.lankhorst@linux.intel.com> References: <1464089051-3995-1-git-send-email-maarten.lankhorst@linux.intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH v2 4/4] Revert "drm/i915: Allow nonblocking update of pageflips." X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Virus-Scanned: ClamAV using ClamSMTP This reverts commit d55dbd06bb5e1399aba9ab5227465339d1bbefff. It lacks a description, removes the flip trace point, doesn't handle -EBUSY if a flip is already queued and needs to be redone. Cc: Ville Syrjälä Signed-off-by: Maarten Lankhorst --- drivers/gpu/drm/i915/intel_display.c | 350 ++++++++++++++++++++++++++--------- 1 file changed, 266 insertions(+), 84 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 876484270cd2..5fd0ef22aabe 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -108,6 +108,8 @@ static void vlv_prepare_pll(struct intel_crtc *crtc, const struct intel_crtc_state *pipe_config); static void chv_prepare_pll(struct intel_crtc *crtc, const struct intel_crtc_state *pipe_config); +static void intel_begin_crtc_commit(struct drm_crtc *, struct drm_crtc_state *); +static void intel_finish_crtc_commit(struct drm_crtc *, struct drm_crtc_state *); static void skl_init_scalers(struct drm_device *dev, struct intel_crtc *intel_crtc, struct intel_crtc_state *crtc_state); static void skylake_pfit_enable(struct intel_crtc *crtc); @@ -10984,7 +10986,7 @@ static void intel_mmio_flip_work_func(struct work_struct *w) struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_request *req; - int i, ret; + int i; if (!needs_modeset(&crtc_state->base) && crtc_state->update_pipe) { work->put_power_domains = @@ -11006,14 +11008,7 @@ static void intel_mmio_flip_work_func(struct work_struct *w) &dev_priv->rps.mmioflips)); } - ret = drm_crtc_vblank_get(crtc); - I915_STATE_WARN(ret < 0, "enabling vblank failed with %i\n", ret); - - if (work->num_planes && - work->old_plane_state[0]->base.plane == crtc->primary) - intel_fbc_enable(intel_crtc, work->new_crtc_state, work->new_plane_state[0]); - - intel_frontbuffer_flip_prepare(dev, work->fb_bits); + intel_frontbuffer_flip_prepare(dev, crtc_state->fb_bits); intel_pipe_update_start(intel_crtc); if (!needs_modeset(&crtc_state->base)) { @@ -11032,15 +11027,206 @@ static void intel_mmio_flip_work_func(struct work_struct *w) struct intel_plane_state *new_plane_state = work->new_plane_state[i]; struct intel_plane *plane = to_intel_plane(new_plane_state->base.plane); - if (new_plane_state->visible) - plane->update_plane(&plane->base, crtc_state, new_plane_state); - else - plane->disable_plane(&plane->base, crtc); + plane->update_plane(&plane->base, crtc_state, new_plane_state); } intel_pipe_update_end(intel_crtc, work); } +static struct fence *intel_get_excl_fence(struct drm_i915_gem_object *obj) +{ + struct reservation_object *resv; + + + if (!obj->base.dma_buf) + return NULL; + + resv = obj->base.dma_buf->resv; + + /* For framebuffer backed by dmabuf, wait for fence */ + while (1) { + struct fence *fence_excl, *ret = NULL; + + rcu_read_lock(); + + fence_excl = rcu_dereference(resv->fence_excl); + if (fence_excl) + ret = fence_get_rcu(fence_excl); + + rcu_read_unlock(); + + if (ret == fence_excl) + return ret; + } +} + +static int intel_crtc_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t page_flip_flags) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_plane_state *old_state, *new_state = NULL; + struct drm_crtc_state *new_crtc_state = NULL; + struct drm_framebuffer *old_fb = crtc->primary->state->fb; + struct drm_i915_gem_object *obj = intel_fb_obj(fb); + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_plane *primary = crtc->primary; + struct intel_flip_work *work; + int ret; + + old_state = crtc->primary->state; + + if (!crtc->state->active) + return -EINVAL; + + /* + * drm_mode_page_flip_ioctl() should already catch this, but double + * check to be safe. In the future we may enable pageflipping from + * a disabled primary plane. + */ + if (WARN_ON(intel_fb_obj(old_fb) == NULL)) + return -EBUSY; + + /* Can't change pixel format via MI display flips. */ + if (fb->pixel_format != old_fb->pixel_format) + return -EINVAL; + + /* + * TILEOFF/LINOFF registers can't be changed via MI display flips. + * Note that pitch changes could also affect these register. + */ + if (INTEL_INFO(dev)->gen > 3 && + (fb->offsets[0] != old_fb->offsets[0] || + fb->pitches[0] != old_fb->pitches[0])) + return -EINVAL; + + work = kzalloc(sizeof(*work), GFP_KERNEL); + new_crtc_state = intel_crtc_duplicate_state(crtc); + new_state = intel_plane_duplicate_state(primary); + + if (!work || !new_crtc_state || !new_state) { + ret = -ENOMEM; + goto cleanup; + } + + drm_framebuffer_unreference(new_state->fb); + drm_framebuffer_reference(fb); + new_state->fb = fb; + + work->event = event; + INIT_WORK(&work->unpin_work, intel_unpin_work_fn); + INIT_WORK(&work->mmio_work, intel_mmio_flip_work_func); + + work->new_crtc_state = to_intel_crtc_state(new_crtc_state); + work->old_crtc_state = intel_crtc->config; + + work->fb_bits = to_intel_plane(primary)->frontbuffer_bit; + work->new_crtc_state->fb_bits = work->fb_bits; + + work->can_async_unpin = true; + work->num_planes = 1; + work->old_plane_state[0] = to_intel_plane_state(old_state); + work->new_plane_state[0] = to_intel_plane_state(new_state); + + /* Step 1: vblank waiting and workqueue throttling, + * similar to intel_atomic_prepare_commit + */ + ret = drm_crtc_vblank_get(crtc); + if (ret) + goto cleanup; + + /* We borrow the event spin lock for protecting flip_work */ + spin_lock_irq(&dev->event_lock); + if (!list_empty(&intel_crtc->flip_work)) { + struct intel_flip_work *old_work; + + old_work = list_last_entry(&intel_crtc->flip_work, + struct intel_flip_work, head); + + /* Before declaring the flip queue wedged, check if + * the hardware completed the operation behind our backs. + */ + if (pageflip_finished(intel_crtc, old_work)) { + DRM_DEBUG_DRIVER("flip queue: previous flip completed, continuing\n"); + page_flip_completed(intel_crtc, old_work); + } else { + DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); + spin_unlock_irq(&dev->event_lock); + + ret = -EBUSY; + goto cleanup_vblank; + } + } + list_add_tail(&work->head, &intel_crtc->flip_work); + spin_unlock_irq(&dev->event_lock); + + if (atomic_read(&intel_crtc->unpin_work_count) >= 2) + flush_workqueue(dev_priv->wq); + + /* step 2, similar to intel_prepare_plane_fb */ + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + goto cleanup_work; + + ret = intel_pin_and_fence_fb_obj(fb, new_state->rotation); + if (ret) + goto cleanup_unlock; + + i915_gem_track_fb(intel_fb_obj(old_fb), obj, + to_intel_plane(primary)->frontbuffer_bit); + + /* point of no return, swap state */ + primary->state = new_state; + crtc->state = new_crtc_state; + intel_crtc->config = to_intel_crtc_state(new_crtc_state); + primary->fb = fb; + + /* scheduling flip work */ + atomic_inc(&intel_crtc->unpin_work_count); + + if (obj->last_write_req && + !i915_gem_request_completed(obj->last_write_req, true)) + i915_gem_request_assign(&work->old_plane_state[0]->wait_req, + obj->last_write_req); + + if (obj->base.dma_buf) + work->old_plane_state[0]->base.fence = intel_get_excl_fence(obj); + + intel_fbc_pre_update(intel_crtc, + to_intel_crtc_state(new_crtc_state), + to_intel_plane_state(new_state)); + + schedule_work(&work->mmio_work); + + mutex_unlock(&dev->struct_mutex); + + trace_i915_flip_request(intel_crtc->plane, obj); + + return 0; + +cleanup_unlock: + mutex_unlock(&dev->struct_mutex); +cleanup_work: + spin_lock_irq(&dev->event_lock); + list_del(&work->head); + spin_unlock_irq(&dev->event_lock); + +cleanup_vblank: + drm_crtc_vblank_put(crtc); +cleanup: + if (new_state) + intel_plane_destroy_state(primary, new_state); + + if (new_crtc_state) + intel_crtc_destroy_state(crtc, new_crtc_state); + + intel_free_flip_work(work); + return ret; +} + + /** * intel_wm_need_update - Check whether watermarks need updating * @plane: drm plane @@ -11317,6 +11503,8 @@ static int intel_crtc_atomic_check(struct drm_crtc *crtc, static const struct drm_crtc_helper_funcs intel_helper_funcs = { .mode_set_base_atomic = intel_pipe_set_base_atomic, + .atomic_begin = intel_begin_crtc_commit, + .atomic_flush = intel_finish_crtc_commit, .atomic_check = intel_crtc_atomic_check, }; @@ -12736,6 +12924,11 @@ static int intel_atomic_prepare_commit(struct drm_device *dev, struct drm_crtc *crtc; int i, ret; + if (nonblock) { + DRM_DEBUG_KMS("i915 does not yet support nonblocking commit\n"); + return -EINVAL; + } + for_each_crtc_in_state(state, crtc, crtc_state, i) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_flip_work *work; @@ -12785,11 +12978,6 @@ static int intel_atomic_prepare_commit(struct drm_device *dev, } } - if (intel_state->modeset && nonblock) { - DRM_DEBUG_ATOMIC("Nonblock modesets are not yet supported!\n"); - return -EINVAL; - } - ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; @@ -12943,41 +13131,13 @@ static void intel_schedule_unpin(struct drm_crtc *crtc, queue_work(dev_priv->wq, &work->unpin_work); } -static void intel_schedule_flip(struct drm_crtc *crtc, - struct intel_atomic_state *state, - struct intel_flip_work *work, - bool nonblock) -{ - struct intel_crtc_state *crtc_state = work->new_crtc_state; - - if (crtc_state->base.planes_changed || - needs_modeset(&crtc_state->base) || - crtc_state->update_pipe) { - if (nonblock) - schedule_work(&work->mmio_work); - else - intel_mmio_flip_work_func(&work->mmio_work); - } else { - int ret; - - ret = drm_crtc_vblank_get(crtc); - I915_STATE_WARN(ret < 0, "enabling vblank failed with %i\n", ret); - - work->flip_queued_vblank = intel_crtc_get_vblank_counter(to_intel_crtc(crtc)); - smp_mb__before_atomic(); - atomic_set(&work->pending, 1); - } -} - static void intel_schedule_update(struct drm_crtc *crtc, struct intel_atomic_state *state, - struct intel_flip_work *work, - bool nonblock) + struct intel_flip_work *work) { struct drm_device *dev = crtc->dev; - struct intel_crtc_state *pipe_config = work->new_crtc_state; - if (!pipe_config->base.active && work->can_async_unpin) { + if (work->can_async_unpin) { INIT_LIST_HEAD(&work->head); intel_schedule_unpin(crtc, state, work); return; @@ -12987,10 +13147,7 @@ static void intel_schedule_update(struct drm_crtc *crtc, list_add_tail(&work->head, &to_intel_crtc(crtc)->flip_work); spin_unlock_irq(&dev->event_lock); - if (!pipe_config->base.active) - intel_schedule_unpin(crtc, state, work); - else - intel_schedule_flip(crtc, state, work, nonblock); + intel_schedule_unpin(crtc, state, work); } /** @@ -13085,9 +13242,11 @@ static int intel_atomic_commit(struct drm_device *dev, /* Now enable the clocks, plane, pipe, and connectors that we set up. */ for_each_crtc_in_state(state, crtc, old_crtc_state, i) { - struct intel_flip_work *work = intel_state->work[i]; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); bool modeset = needs_modeset(crtc->state); + struct intel_crtc_state *pipe_config = + to_intel_crtc_state(crtc->state); + bool update_pipe = !modeset && pipe_config->update_pipe; crtc_mask |= drm_crtc_mask(crtc); @@ -13099,6 +13258,22 @@ static int intel_atomic_commit(struct drm_device *dev, if (!modeset) intel_pre_plane_update(to_intel_crtc_state(old_crtc_state)); + if (crtc->state->active && + drm_atomic_get_existing_plane_state(state, crtc->primary)) + intel_fbc_enable(intel_crtc, pipe_config, to_intel_plane_state(crtc->primary->state)); + + if (crtc->state->active && + (crtc->state->planes_changed || update_pipe)) + drm_atomic_helper_commit_planes_on_crtc(old_crtc_state); + } + + /* FIXME: add subpixel order */ + + for_each_crtc_in_state(state, crtc, old_crtc_state, i) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_flip_work *work = + intel_state->work[i]; + if (!work) { if (!list_empty_careful(&intel_crtc->flip_work)) { spin_lock_irq(&dev->event_lock); @@ -13118,7 +13293,7 @@ static int intel_atomic_commit(struct drm_device *dev, intel_state->work[i] = NULL; intel_prepare_work(crtc, work, state, old_crtc_state); - intel_schedule_update(crtc, intel_state, work, nonblock); + intel_schedule_update(crtc, intel_state, work); } if (!nonblock && !state->legacy_cursor_update) { @@ -13127,8 +13302,6 @@ static int intel_atomic_commit(struct drm_device *dev, intel_crtc_wait_for_pending_flips(crtc, false); } - /* FIXME: add subpixel order */ - drm_atomic_state_free(state); /* As one of the primary mmio accessors, KMS has a high likelihood @@ -13192,38 +13365,11 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .set_property = drm_atomic_helper_crtc_set_property, .destroy = intel_crtc_destroy, - .page_flip = drm_atomic_helper_page_flip, + .page_flip = intel_crtc_page_flip, .atomic_duplicate_state = intel_crtc_duplicate_state, .atomic_destroy_state = intel_crtc_destroy_state, }; -static struct fence *intel_get_excl_fence(struct drm_i915_gem_object *obj) -{ - struct reservation_object *resv; - - - if (!obj->base.dma_buf) - return NULL; - - resv = obj->base.dma_buf->resv; - - /* For framebuffer backed by dmabuf, wait for fence */ - while (1) { - struct fence *fence_excl, *ret = NULL; - - rcu_read_lock(); - - fence_excl = rcu_dereference(resv->fence_excl); - if (fence_excl) - ret = fence_get_rcu(fence_excl); - - rcu_read_unlock(); - - if (ret == fence_excl) - return ret; - } -} - /** * intel_prepare_plane_fb - Prepare fb for usage on plane * @plane: drm plane to prepare for @@ -13410,6 +13556,42 @@ intel_check_primary_plane(struct drm_plane *plane, &state->visible); } +static void intel_begin_crtc_commit(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct drm_device *dev = crtc->dev; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_crtc_state *old_intel_state = + to_intel_crtc_state(old_crtc_state); + bool modeset = needs_modeset(crtc->state); + + intel_frontbuffer_flip_prepare(dev, to_intel_crtc_state(crtc->state)->fb_bits); + + /* Perform vblank evasion around commit operation */ + intel_pipe_update_start(intel_crtc); + + if (modeset) + return; + + if (crtc->state->color_mgmt_changed || to_intel_crtc_state(crtc->state)->update_pipe) { + intel_color_set_csc(crtc->state); + intel_color_load_luts(crtc->state); + } + + if (to_intel_crtc_state(crtc->state)->update_pipe) + intel_update_pipe_config(intel_crtc, old_intel_state); + else if (INTEL_INFO(dev)->gen >= 9) + skl_detach_scalers(intel_crtc); +} + +static void intel_finish_crtc_commit(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + intel_pipe_update_end(intel_crtc, NULL); +} + /** * intel_plane_destroy - destroy a plane * @plane: plane to destroy