Merge tag 'drm-intel-next-2019-02-07' of git://anongit.freedesktop.org/drm/drm-intel...
authorDave Airlie <airlied@redhat.com>
Mon, 11 Feb 2019 03:41:53 +0000 (13:41 +1000)
committerDave Airlie <airlied@redhat.com>
Mon, 11 Feb 2019 03:41:59 +0000 (13:41 +1000)
UAPI Changes:

- Expose RPCS (SSEU) configuration to userspace for Ice Lake
in order to allow userspace to reconfigure the subslice config
per context basis. (Tvrtko, Lionel)

Driver Changes:

- Execbuf and preemption improvements including selftests (Chris)
- Rename HAS_GMCH_DISPLAY/HAS_GMCH (Rodrigo)
- Debugfs error handling fix for robustness (Greg)
- Improve reg_rw traces (Ville)
- Push clear_intel_crtc_state onto the heap (Chris)
- Watermark fixes for Ice Lake (Ville)
- Fix enable count array size and bounds checking (Tvrtko)
- MST Fixes (Lyude)
- Prevent race and handle error on I915_GEM_MMAP (Joonas)
- Initial rework for an full atomic gamma mode (Ville)

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190208165000.GA30314@intel.com
55 files changed:
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/i915_active.c [new file with mode: 0644]
drivers/gpu/drm/i915/i915_active.h [new file with mode: 0644]
drivers/gpu/drm/i915/i915_active_types.h [new file with mode: 0644]
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_gem_context.c
drivers/gpu/drm/i915/i915_gem_context.h
drivers/gpu/drm/i915/i915_gem_execbuffer.c
drivers/gpu/drm/i915/i915_gem_fence_reg.c
drivers/gpu/drm/i915/i915_gem_gtt.c
drivers/gpu/drm/i915/i915_gem_object.h
drivers/gpu/drm/i915/i915_gpu_error.c
drivers/gpu/drm/i915/i915_pci.c
drivers/gpu/drm/i915/i915_perf.c
drivers/gpu/drm/i915/i915_pmu.c
drivers/gpu/drm/i915/i915_pmu.h
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_request.c
drivers/gpu/drm/i915/i915_request.h
drivers/gpu/drm/i915/i915_reset.c
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/i915_timeline.c
drivers/gpu/drm/i915/i915_timeline.h
drivers/gpu/drm/i915/i915_vma.c
drivers/gpu/drm/i915/i915_vma.h
drivers/gpu/drm/i915/intel_color.c
drivers/gpu/drm/i915/intel_device_info.h
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_engine_cs.c
drivers/gpu/drm/i915/intel_fbdev.c
drivers/gpu/drm/i915/intel_fifo_underrun.c
drivers/gpu/drm/i915/intel_guc_log.c
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_hotplug.c
drivers/gpu/drm/i915/intel_i2c.c
drivers/gpu/drm/i915/intel_lrc.c
drivers/gpu/drm/i915/intel_lrc.h
drivers/gpu/drm/i915/intel_overlay.c
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/i915/intel_ringbuffer.c
drivers/gpu/drm/i915/intel_ringbuffer.h
drivers/gpu/drm/i915/intel_sprite.c
drivers/gpu/drm/i915/intel_uncore.c
drivers/gpu/drm/i915/selftests/i915_active.c [new file with mode: 0644]
drivers/gpu/drm/i915/selftests/i915_gem_context.c
drivers/gpu/drm/i915/selftests/i915_live_selftests.h
drivers/gpu/drm/i915/selftests/intel_lrc.c
drivers/gpu/drm/i915/selftests/mock_timeline.c
drivers/gpu/drm/i915/vlv_dsi.c
include/uapi/drm/i915_drm.h

index 210d0e8777b6f63d71f07b25f00faf29dfc9d3ee..1787e1299b1b2a8ec6647473aa64c4c03efd71f0 100644 (file)
@@ -57,7 +57,9 @@ i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o intel_pipe_crc.o
 i915-$(CONFIG_PERF_EVENTS) += i915_pmu.o
 
 # GEM code
-i915-y += i915_cmd_parser.o \
+i915-y += \
+         i915_active.o \
+         i915_cmd_parser.o \
          i915_gem_batch_pool.o \
          i915_gem_clflush.o \
          i915_gem_context.o \
diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
new file mode 100644 (file)
index 0000000..215b6ff
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2019 Intel Corporation
+ */
+
+#include "i915_drv.h"
+#include "i915_active.h"
+
+#define BKL(ref) (&(ref)->i915->drm.struct_mutex)
+
+/*
+ * Active refs memory management
+ *
+ * To be more economical with memory, we reap all the i915_active trees as
+ * they idle (when we know the active requests are inactive) and allocate the
+ * nodes from a local slab cache to hopefully reduce the fragmentation.
+ */
+static struct i915_global_active {
+       struct kmem_cache *slab_cache;
+} global;
+
+struct active_node {
+       struct i915_active_request base;
+       struct i915_active *ref;
+       struct rb_node node;
+       u64 timeline;
+};
+
+static void
+__active_park(struct i915_active *ref)
+{
+       struct active_node *it, *n;
+
+       rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
+               GEM_BUG_ON(i915_active_request_isset(&it->base));
+               kmem_cache_free(global.slab_cache, it);
+       }
+       ref->tree = RB_ROOT;
+}
+
+static void
+__active_retire(struct i915_active *ref)
+{
+       GEM_BUG_ON(!ref->count);
+       if (--ref->count)
+               return;
+
+       /* return the unused nodes to our slabcache */
+       __active_park(ref);
+
+       ref->retire(ref);
+}
+
+static void
+node_retire(struct i915_active_request *base, struct i915_request *rq)
+{
+       __active_retire(container_of(base, struct active_node, base)->ref);
+}
+
+static void
+last_retire(struct i915_active_request *base, struct i915_request *rq)
+{
+       __active_retire(container_of(base, struct i915_active, last));
+}
+
+static struct i915_active_request *
+active_instance(struct i915_active *ref, u64 idx)
+{
+       struct active_node *node;
+       struct rb_node **p, *parent;
+       struct i915_request *old;
+
+       /*
+        * We track the most recently used timeline to skip a rbtree search
+        * for the common case, under typical loads we never need the rbtree
+        * at all. We can reuse the last slot if it is empty, that is
+        * after the previous activity has been retired, or if it matches the
+        * current timeline.
+        *
+        * Note that we allow the timeline to be active simultaneously in
+        * the rbtree and the last cache. We do this to avoid having
+        * to search and replace the rbtree element for a new timeline, with
+        * the cost being that we must be aware that the ref may be retired
+        * twice for the same timeline (as the older rbtree element will be
+        * retired before the new request added to last).
+        */
+       old = i915_active_request_raw(&ref->last, BKL(ref));
+       if (!old || old->fence.context == idx)
+               goto out;
+
+       /* Move the currently active fence into the rbtree */
+       idx = old->fence.context;
+
+       parent = NULL;
+       p = &ref->tree.rb_node;
+       while (*p) {
+               parent = *p;
+
+               node = rb_entry(parent, struct active_node, node);
+               if (node->timeline == idx)
+                       goto replace;
+
+               if (node->timeline < idx)
+                       p = &parent->rb_right;
+               else
+                       p = &parent->rb_left;
+       }
+
+       node = kmem_cache_alloc(global.slab_cache, GFP_KERNEL);
+
+       /* kmalloc may retire the ref->last (thanks shrinker)! */
+       if (unlikely(!i915_active_request_raw(&ref->last, BKL(ref)))) {
+               kmem_cache_free(global.slab_cache, node);
+               goto out;
+       }
+
+       if (unlikely(!node))
+               return ERR_PTR(-ENOMEM);
+
+       i915_active_request_init(&node->base, NULL, node_retire);
+       node->ref = ref;
+       node->timeline = idx;
+
+       rb_link_node(&node->node, parent, p);
+       rb_insert_color(&node->node, &ref->tree);
+
+replace:
+       /*
+        * Overwrite the previous active slot in the rbtree with last,
+        * leaving last zeroed. If the previous slot is still active,
+        * we must be careful as we now only expect to receive one retire
+        * callback not two, and so much undo the active counting for the
+        * overwritten slot.
+        */
+       if (i915_active_request_isset(&node->base)) {
+               /* Retire ourselves from the old rq->active_list */
+               __list_del_entry(&node->base.link);
+               ref->count--;
+               GEM_BUG_ON(!ref->count);
+       }
+       GEM_BUG_ON(list_empty(&ref->last.link));
+       list_replace_init(&ref->last.link, &node->base.link);
+       node->base.request = fetch_and_zero(&ref->last.request);
+
+out:
+       return &ref->last;
+}
+
+void i915_active_init(struct drm_i915_private *i915,
+                     struct i915_active *ref,
+                     void (*retire)(struct i915_active *ref))
+{
+       ref->i915 = i915;
+       ref->retire = retire;
+       ref->tree = RB_ROOT;
+       i915_active_request_init(&ref->last, NULL, last_retire);
+       ref->count = 0;
+}
+
+int i915_active_ref(struct i915_active *ref,
+                   u64 timeline,
+                   struct i915_request *rq)
+{
+       struct i915_active_request *active;
+
+       active = active_instance(ref, timeline);
+       if (IS_ERR(active))
+               return PTR_ERR(active);
+
+       if (!i915_active_request_isset(active))
+               ref->count++;
+       __i915_active_request_set(active, rq);
+
+       GEM_BUG_ON(!ref->count);
+       return 0;
+}
+
+bool i915_active_acquire(struct i915_active *ref)
+{
+       lockdep_assert_held(BKL(ref));
+       return !ref->count++;
+}
+
+void i915_active_release(struct i915_active *ref)
+{
+       lockdep_assert_held(BKL(ref));
+       __active_retire(ref);
+}
+
+int i915_active_wait(struct i915_active *ref)
+{
+       struct active_node *it, *n;
+       int ret = 0;
+
+       if (i915_active_acquire(ref))
+               goto out_release;
+
+       ret = i915_active_request_retire(&ref->last, BKL(ref));
+       if (ret)
+               goto out_release;
+
+       rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
+               ret = i915_active_request_retire(&it->base, BKL(ref));
+               if (ret)
+                       break;
+       }
+
+out_release:
+       i915_active_release(ref);
+       return ret;
+}
+
+int i915_request_await_active_request(struct i915_request *rq,
+                                     struct i915_active_request *active)
+{
+       struct i915_request *barrier =
+               i915_active_request_raw(active, &rq->i915->drm.struct_mutex);
+
+       return barrier ? i915_request_await_dma_fence(rq, &barrier->fence) : 0;
+}
+
+int i915_request_await_active(struct i915_request *rq, struct i915_active *ref)
+{
+       struct active_node *it, *n;
+       int ret;
+
+       ret = i915_request_await_active_request(rq, &ref->last);
+       if (ret)
+               return ret;
+
+       rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
+               ret = i915_request_await_active_request(rq, &it->base);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
+void i915_active_fini(struct i915_active *ref)
+{
+       GEM_BUG_ON(i915_active_request_isset(&ref->last));
+       GEM_BUG_ON(!RB_EMPTY_ROOT(&ref->tree));
+       GEM_BUG_ON(ref->count);
+}
+#endif
+
+int i915_active_request_set(struct i915_active_request *active,
+                           struct i915_request *rq)
+{
+       int err;
+
+       /* Must maintain ordering wrt previous active requests */
+       err = i915_request_await_active_request(rq, active);
+       if (err)
+               return err;
+
+       __i915_active_request_set(active, rq);
+       return 0;
+}
+
+void i915_active_retire_noop(struct i915_active_request *active,
+                            struct i915_request *request)
+{
+       /* Space left intentionally blank */
+}
+
+#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
+#include "selftests/i915_active.c"
+#endif
+
+int __init i915_global_active_init(void)
+{
+       global.slab_cache = KMEM_CACHE(active_node, SLAB_HWCACHE_ALIGN);
+       if (!global.slab_cache)
+               return -ENOMEM;
+
+       return 0;
+}
+
+void __exit i915_global_active_exit(void)
+{
+       kmem_cache_destroy(global.slab_cache);
+}
diff --git a/drivers/gpu/drm/i915/i915_active.h b/drivers/gpu/drm/i915/i915_active.h
new file mode 100644 (file)
index 0000000..12b5c1d
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2019 Intel Corporation
+ */
+
+#ifndef _I915_ACTIVE_H_
+#define _I915_ACTIVE_H_
+
+#include <linux/lockdep.h>
+
+#include "i915_active_types.h"
+#include "i915_request.h"
+
+/*
+ * We treat requests as fences. This is not be to confused with our
+ * "fence registers" but pipeline synchronisation objects ala GL_ARB_sync.
+ * We use the fences to synchronize access from the CPU with activity on the
+ * GPU, for example, we should not rewrite an object's PTE whilst the GPU
+ * is reading them. We also track fences at a higher level to provide
+ * implicit synchronisation around GEM objects, e.g. set-domain will wait
+ * for outstanding GPU rendering before marking the object ready for CPU
+ * access, or a pageflip will wait until the GPU is complete before showing
+ * the frame on the scanout.
+ *
+ * In order to use a fence, the object must track the fence it needs to
+ * serialise with. For example, GEM objects want to track both read and
+ * write access so that we can perform concurrent read operations between
+ * the CPU and GPU engines, as well as waiting for all rendering to
+ * complete, or waiting for the last GPU user of a "fence register". The
+ * object then embeds a #i915_active_request to track the most recent (in
+ * retirement order) request relevant for the desired mode of access.
+ * The #i915_active_request is updated with i915_active_request_set() to
+ * track the most recent fence request, typically this is done as part of
+ * i915_vma_move_to_active().
+ *
+ * When the #i915_active_request completes (is retired), it will
+ * signal its completion to the owner through a callback as well as mark
+ * itself as idle (i915_active_request.request == NULL). The owner
+ * can then perform any action, such as delayed freeing of an active
+ * resource including itself.
+ */
+
+void i915_active_retire_noop(struct i915_active_request *active,
+                            struct i915_request *request);
+
+/**
+ * i915_active_request_init - prepares the activity tracker for use
+ * @active - the active tracker
+ * @rq - initial request to track, can be NULL
+ * @func - a callback when then the tracker is retired (becomes idle),
+ *         can be NULL
+ *
+ * i915_active_request_init() prepares the embedded @active struct for use as
+ * an activity tracker, that is for tracking the last known active request
+ * associated with it. When the last request becomes idle, when it is retired
+ * after completion, the optional callback @func is invoked.
+ */
+static inline void
+i915_active_request_init(struct i915_active_request *active,
+                        struct i915_request *rq,
+                        i915_active_retire_fn retire)
+{
+       RCU_INIT_POINTER(active->request, rq);
+       INIT_LIST_HEAD(&active->link);
+       active->retire = retire ?: i915_active_retire_noop;
+}
+
+#define INIT_ACTIVE_REQUEST(name) i915_active_request_init((name), NULL, NULL)
+
+/**
+ * i915_active_request_set - updates the tracker to watch the current request
+ * @active - the active tracker
+ * @request - the request to watch
+ *
+ * __i915_active_request_set() watches the given @request for completion. Whilst
+ * that @request is busy, the @active reports busy. When that @request is
+ * retired, the @active tracker is updated to report idle.
+ */
+static inline void
+__i915_active_request_set(struct i915_active_request *active,
+                         struct i915_request *request)
+{
+       list_move(&active->link, &request->active_list);
+       rcu_assign_pointer(active->request, request);
+}
+
+int __must_check
+i915_active_request_set(struct i915_active_request *active,
+                       struct i915_request *rq);
+
+/**
+ * i915_active_request_set_retire_fn - updates the retirement callback
+ * @active - the active tracker
+ * @fn - the routine called when the request is retired
+ * @mutex - struct_mutex used to guard retirements
+ *
+ * i915_active_request_set_retire_fn() updates the function pointer that
+ * is called when the final request associated with the @active tracker
+ * is retired.
+ */
+static inline void
+i915_active_request_set_retire_fn(struct i915_active_request *active,
+                                 i915_active_retire_fn fn,
+                                 struct mutex *mutex)
+{
+       lockdep_assert_held(mutex);
+       active->retire = fn ?: i915_active_retire_noop;
+}
+
+static inline struct i915_request *
+__i915_active_request_peek(const struct i915_active_request *active)
+{
+       /*
+        * Inside the error capture (running with the driver in an unknown
+        * state), we want to bend the rules slightly (a lot).
+        *
+        * Work is in progress to make it safer, in the meantime this keeps
+        * the known issue from spamming the logs.
+        */
+       return rcu_dereference_protected(active->request, 1);
+}
+
+/**
+ * i915_active_request_raw - return the active request
+ * @active - the active tracker
+ *
+ * i915_active_request_raw() returns the current request being tracked, or NULL.
+ * It does not obtain a reference on the request for the caller, so the caller
+ * must hold struct_mutex.
+ */
+static inline struct i915_request *
+i915_active_request_raw(const struct i915_active_request *active,
+                       struct mutex *mutex)
+{
+       return rcu_dereference_protected(active->request,
+                                        lockdep_is_held(mutex));
+}
+
+/**
+ * i915_active_request_peek - report the active request being monitored
+ * @active - the active tracker
+ *
+ * i915_active_request_peek() returns the current request being tracked if
+ * still active, or NULL. It does not obtain a reference on the request
+ * for the caller, so the caller must hold struct_mutex.
+ */
+static inline struct i915_request *
+i915_active_request_peek(const struct i915_active_request *active,
+                        struct mutex *mutex)
+{
+       struct i915_request *request;
+
+       request = i915_active_request_raw(active, mutex);
+       if (!request || i915_request_completed(request))
+               return NULL;
+
+       return request;
+}
+
+/**
+ * i915_active_request_get - return a reference to the active request
+ * @active - the active tracker
+ *
+ * i915_active_request_get() returns a reference to the active request, or NULL
+ * if the active tracker is idle. The caller must hold struct_mutex.
+ */
+static inline struct i915_request *
+i915_active_request_get(const struct i915_active_request *active,
+                       struct mutex *mutex)
+{
+       return i915_request_get(i915_active_request_peek(active, mutex));
+}
+
+/**
+ * __i915_active_request_get_rcu - return a reference to the active request
+ * @active - the active tracker
+ *
+ * __i915_active_request_get() returns a reference to the active request,
+ * or NULL if the active tracker is idle. The caller must hold the RCU read
+ * lock, but the returned pointer is safe to use outside of RCU.
+ */
+static inline struct i915_request *
+__i915_active_request_get_rcu(const struct i915_active_request *active)
+{
+       /*
+        * Performing a lockless retrieval of the active request is super
+        * tricky. SLAB_TYPESAFE_BY_RCU merely guarantees that the backing
+        * slab of request objects will not be freed whilst we hold the
+        * RCU read lock. It does not guarantee that the request itself
+        * will not be freed and then *reused*. Viz,
+        *
+        * Thread A                     Thread B
+        *
+        * rq = active.request
+        *                              retire(rq) -> free(rq);
+        *                              (rq is now first on the slab freelist)
+        *                              active.request = NULL
+        *
+        *                              rq = new submission on a new object
+        * ref(rq)
+        *
+        * To prevent the request from being reused whilst the caller
+        * uses it, we take a reference like normal. Whilst acquiring
+        * the reference we check that it is not in a destroyed state
+        * (refcnt == 0). That prevents the request being reallocated
+        * whilst the caller holds on to it. To check that the request
+        * was not reallocated as we acquired the reference we have to
+        * check that our request remains the active request across
+        * the lookup, in the same manner as a seqlock. The visibility
+        * of the pointer versus the reference counting is controlled
+        * by using RCU barriers (rcu_dereference and rcu_assign_pointer).
+        *
+        * In the middle of all that, we inspect whether the request is
+        * complete. Retiring is lazy so the request may be completed long
+        * before the active tracker is updated. Querying whether the
+        * request is complete is far cheaper (as it involves no locked
+        * instructions setting cachelines to exclusive) than acquiring
+        * the reference, so we do it first. The RCU read lock ensures the
+        * pointer dereference is valid, but does not ensure that the
+        * seqno nor HWS is the right one! However, if the request was
+        * reallocated, that means the active tracker's request was complete.
+        * If the new request is also complete, then both are and we can
+        * just report the active tracker is idle. If the new request is
+        * incomplete, then we acquire a reference on it and check that
+        * it remained the active request.
+        *
+        * It is then imperative that we do not zero the request on
+        * reallocation, so that we can chase the dangling pointers!
+        * See i915_request_alloc().
+        */
+       do {
+               struct i915_request *request;
+
+               request = rcu_dereference(active->request);
+               if (!request || i915_request_completed(request))
+                       return NULL;
+
+               /*
+                * An especially silly compiler could decide to recompute the
+                * result of i915_request_completed, more specifically
+                * re-emit the load for request->fence.seqno. A race would catch
+                * a later seqno value, which could flip the result from true to
+                * false. Which means part of the instructions below might not
+                * be executed, while later on instructions are executed. Due to
+                * barriers within the refcounting the inconsistency can't reach
+                * past the call to i915_request_get_rcu, but not executing
+                * that while still executing i915_request_put() creates
+                * havoc enough.  Prevent this with a compiler barrier.
+                */
+               barrier();
+
+               request = i915_request_get_rcu(request);
+
+               /*
+                * What stops the following rcu_access_pointer() from occurring
+                * before the above i915_request_get_rcu()? If we were
+                * to read the value before pausing to get the reference to
+                * the request, we may not notice a change in the active
+                * tracker.
+                *
+                * The rcu_access_pointer() is a mere compiler barrier, which
+                * means both the CPU and compiler are free to perform the
+                * memory read without constraint. The compiler only has to
+                * ensure that any operations after the rcu_access_pointer()
+                * occur afterwards in program order. This means the read may
+                * be performed earlier by an out-of-order CPU, or adventurous
+                * compiler.
+                *
+                * The atomic operation at the heart of
+                * i915_request_get_rcu(), see dma_fence_get_rcu(), is
+                * atomic_inc_not_zero() which is only a full memory barrier
+                * when successful. That is, if i915_request_get_rcu()
+                * returns the request (and so with the reference counted
+                * incremented) then the following read for rcu_access_pointer()
+                * must occur after the atomic operation and so confirm
+                * that this request is the one currently being tracked.
+                *
+                * The corresponding write barrier is part of
+                * rcu_assign_pointer().
+                */
+               if (!request || request == rcu_access_pointer(active->request))
+                       return rcu_pointer_handoff(request);
+
+               i915_request_put(request);
+       } while (1);
+}
+
+/**
+ * i915_active_request_get_unlocked - return a reference to the active request
+ * @active - the active tracker
+ *
+ * i915_active_request_get_unlocked() returns a reference to the active request,
+ * or NULL if the active tracker is idle. The reference is obtained under RCU,
+ * so no locking is required by the caller.
+ *
+ * The reference should be freed with i915_request_put().
+ */
+static inline struct i915_request *
+i915_active_request_get_unlocked(const struct i915_active_request *active)
+{
+       struct i915_request *request;
+
+       rcu_read_lock();
+       request = __i915_active_request_get_rcu(active);
+       rcu_read_unlock();
+
+       return request;
+}
+
+/**
+ * i915_active_request_isset - report whether the active tracker is assigned
+ * @active - the active tracker
+ *
+ * i915_active_request_isset() returns true if the active tracker is currently
+ * assigned to a request. Due to the lazy retiring, that request may be idle
+ * and this may report stale information.
+ */
+static inline bool
+i915_active_request_isset(const struct i915_active_request *active)
+{
+       return rcu_access_pointer(active->request);
+}
+
+/**
+ * i915_active_request_retire - waits until the request is retired
+ * @active - the active request on which to wait
+ *
+ * i915_active_request_retire() waits until the request is completed,
+ * and then ensures that at least the retirement handler for this
+ * @active tracker is called before returning. If the @active
+ * tracker is idle, the function returns immediately.
+ */
+static inline int __must_check
+i915_active_request_retire(struct i915_active_request *active,
+                          struct mutex *mutex)
+{
+       struct i915_request *request;
+       long ret;
+
+       request = i915_active_request_raw(active, mutex);
+       if (!request)
+               return 0;
+
+       ret = i915_request_wait(request,
+                               I915_WAIT_INTERRUPTIBLE | I915_WAIT_LOCKED,
+                               MAX_SCHEDULE_TIMEOUT);
+       if (ret < 0)
+               return ret;
+
+       list_del_init(&active->link);
+       RCU_INIT_POINTER(active->request, NULL);
+
+       active->retire(active, request);
+
+       return 0;
+}
+
+/*
+ * GPU activity tracking
+ *
+ * Each set of commands submitted to the GPU compromises a single request that
+ * signals a fence upon completion. struct i915_request combines the
+ * command submission, scheduling and fence signaling roles. If we want to see
+ * if a particular task is complete, we need to grab the fence (struct
+ * i915_request) for that task and check or wait for it to be signaled. More
+ * often though we want to track the status of a bunch of tasks, for example
+ * to wait for the GPU to finish accessing some memory across a variety of
+ * different command pipelines from different clients. We could choose to
+ * track every single request associated with the task, but knowing that
+ * each request belongs to an ordered timeline (later requests within a
+ * timeline must wait for earlier requests), we need only track the
+ * latest request in each timeline to determine the overall status of the
+ * task.
+ *
+ * struct i915_active provides this tracking across timelines. It builds a
+ * composite shared-fence, and is updated as new work is submitted to the task,
+ * forming a snapshot of the current status. It should be embedded into the
+ * different resources that need to track their associated GPU activity to
+ * provide a callback when that GPU activity has ceased, or otherwise to
+ * provide a serialisation point either for request submission or for CPU
+ * synchronisation.
+ */
+
+void i915_active_init(struct drm_i915_private *i915,
+                     struct i915_active *ref,
+                     void (*retire)(struct i915_active *ref));
+
+int i915_active_ref(struct i915_active *ref,
+                   u64 timeline,
+                   struct i915_request *rq);
+
+int i915_active_wait(struct i915_active *ref);
+
+int i915_request_await_active(struct i915_request *rq,
+                             struct i915_active *ref);
+int i915_request_await_active_request(struct i915_request *rq,
+                                     struct i915_active_request *active);
+
+bool i915_active_acquire(struct i915_active *ref);
+
+static inline void i915_active_cancel(struct i915_active *ref)
+{
+       GEM_BUG_ON(ref->count != 1);
+       ref->count = 0;
+}
+
+void i915_active_release(struct i915_active *ref);
+
+static inline bool
+i915_active_is_idle(const struct i915_active *ref)
+{
+       return !ref->count;
+}
+
+#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
+void i915_active_fini(struct i915_active *ref);
+#else
+static inline void i915_active_fini(struct i915_active *ref) { }
+#endif
+
+int i915_global_active_init(void);
+void i915_global_active_exit(void);
+
+#endif /* _I915_ACTIVE_H_ */
diff --git a/drivers/gpu/drm/i915/i915_active_types.h b/drivers/gpu/drm/i915/i915_active_types.h
new file mode 100644 (file)
index 0000000..b679253
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2019 Intel Corporation
+ */
+
+#ifndef _I915_ACTIVE_TYPES_H_
+#define _I915_ACTIVE_TYPES_H_
+
+#include <linux/rbtree.h>
+#include <linux/rcupdate.h>
+
+struct drm_i915_private;
+struct i915_active_request;
+struct i915_request;
+
+typedef void (*i915_active_retire_fn)(struct i915_active_request *,
+                                     struct i915_request *);
+
+struct i915_active_request {
+       struct i915_request __rcu *request;
+       struct list_head link;
+       i915_active_retire_fn retire;
+};
+
+struct i915_active {
+       struct drm_i915_private *i915;
+
+       struct rb_root tree;
+       struct i915_active_request last;
+       unsigned int count;
+
+       void (*retire)(struct i915_active *ref);
+};
+
+#endif /* _I915_ACTIVE_TYPES_H_ */
index fa2c226fc779db78f264e3cd81c97ca6bd499b31..0bd890c04fe4f7c911bd9bde1a79af11af08ff5c 100644 (file)
@@ -207,7 +207,7 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
                if (vma->fence)
                        seq_printf(m, " , fence: %d%s",
                                   vma->fence->id,
-                                  i915_gem_active_isset(&vma->last_fence) ? "*" : "");
+                                  i915_active_request_isset(&vma->last_fence) ? "*" : "");
                seq_puts(m, ")");
        }
        if (obj->stolen)
@@ -3728,7 +3728,7 @@ static int spr_wm_latency_open(struct inode *inode, struct file *file)
 {
        struct drm_i915_private *dev_priv = inode->i_private;
 
-       if (HAS_GMCH_DISPLAY(dev_priv))
+       if (HAS_GMCH(dev_priv))
                return -ENODEV;
 
        return single_open(file, spr_wm_latency_show, dev_priv);
@@ -3738,7 +3738,7 @@ static int cur_wm_latency_open(struct inode *inode, struct file *file)
 {
        struct drm_i915_private *dev_priv = inode->i_private;
 
-       if (HAS_GMCH_DISPLAY(dev_priv))
+       if (HAS_GMCH(dev_priv))
                return -ENODEV;
 
        return single_open(file, cur_wm_latency_show, dev_priv);
index a7aaa1ac4c999fbbaa829ebf2b23b524b1d03f40..7de90701f6f175e8e7f5fc777f9305dc7b628494 100644 (file)
@@ -2543,6 +2543,10 @@ static void vlv_restore_gunit_s0ix_state(struct drm_i915_private *dev_priv)
 static int vlv_wait_for_pw_status(struct drm_i915_private *dev_priv,
                                  u32 mask, u32 val)
 {
+       i915_reg_t reg = VLV_GTLC_PW_STATUS;
+       u32 reg_value;
+       int ret;
+
        /* The HW does not like us polling for PW_STATUS frequently, so
         * use the sleeping loop rather than risk the busy spin within
         * intel_wait_for_register().
@@ -2550,8 +2554,12 @@ static int vlv_wait_for_pw_status(struct drm_i915_private *dev_priv,
         * Transitioning between RC6 states should be at most 2ms (see
         * valleyview_enable_rps) so use a 3ms timeout.
         */
-       return wait_for((I915_READ_NOTRACE(VLV_GTLC_PW_STATUS) & mask) == val,
-                       3);
+       ret = wait_for(((reg_value = I915_READ_NOTRACE(reg)) & mask) == val, 3);
+
+       /* just trace the final value */
+       trace_i915_reg_rw(false, reg, reg_value, sizeof(reg_value), true);
+
+       return ret;
 }
 
 int vlv_force_gfx_clock(struct drm_i915_private *dev_priv, bool force_on)
index 534e52e3a8da19189ae7674a3ce6c42207479bfa..9adc7bb9e69ccfec96e468f95435b83e084ffcce 100644 (file)
@@ -91,8 +91,8 @@
 
 #define DRIVER_NAME            "i915"
 #define DRIVER_DESC            "Intel Graphics"
-#define DRIVER_DATE            "20190202"
-#define DRIVER_TIMESTAMP       1549095268
+#define DRIVER_DATE            "20190207"
+#define DRIVER_TIMESTAMP       1549572331
 
 /* Use I915_STATE_WARN(x) and I915_STATE_WARN_ON() (rather than WARN() and
  * WARN_ON()) for hw state sanity checks to check for unexpected conditions
@@ -323,8 +323,20 @@ struct drm_i915_display_funcs {
        /* display clock increase/decrease */
        /* pll clock increase/decrease */
 
-       void (*load_csc_matrix)(struct intel_crtc_state *crtc_state);
-       void (*load_luts)(struct intel_crtc_state *crtc_state);
+       /*
+        * Program double buffered color management registers during
+        * vblank evasion. The registers should then latch during the
+        * next vblank start, alongside any other double buffered registers
+        * involved with the same commit.
+        */
+       void (*color_commit)(const struct intel_crtc_state *crtc_state);
+       /*
+        * Load LUTs (and other single buffered color management
+        * registers). Will (hopefully) be called during the vblank
+        * following the latching of any double buffered registers
+        * involved with the same commit.
+        */
+       void (*load_luts)(const struct intel_crtc_state *crtc_state);
 };
 
 #define CSR_VERSION(major, minor)      ((major) << 16 | (minor))
@@ -2490,7 +2502,7 @@ static inline unsigned int i915_sg_segment_size(void)
 
 #define HAS_FW_BLC(dev_priv)   (INTEL_GEN(dev_priv) > 2)
 #define HAS_FBC(dev_priv)      (INTEL_INFO(dev_priv)->display.has_fbc)
-#define HAS_CUR_FBC(dev_priv)  (!HAS_GMCH_DISPLAY(dev_priv) && INTEL_GEN(dev_priv) >= 7)
+#define HAS_CUR_FBC(dev_priv)  (!HAS_GMCH(dev_priv) && INTEL_GEN(dev_priv) >= 7)
 
 #define HAS_IPS(dev_priv)      (IS_HSW_ULT(dev_priv) || IS_BROADWELL(dev_priv))
 
@@ -2570,7 +2582,7 @@ static inline unsigned int i915_sg_segment_size(void)
 #define HAS_PCH_NOP(dev_priv) (INTEL_PCH_TYPE(dev_priv) == PCH_NOP)
 #define HAS_PCH_SPLIT(dev_priv) (INTEL_PCH_TYPE(dev_priv) != PCH_NONE)
 
-#define HAS_GMCH_DISPLAY(dev_priv) (INTEL_INFO(dev_priv)->display.has_gmch_display)
+#define HAS_GMCH(dev_priv) (INTEL_INFO(dev_priv)->display.has_gmch)
 
 #define HAS_LSPCON(dev_priv) (INTEL_GEN(dev_priv) >= 9)
 
@@ -3305,6 +3317,20 @@ mkwrite_device_info(struct drm_i915_private *dev_priv)
        return (struct intel_device_info *)INTEL_INFO(dev_priv);
 }
 
+static inline struct intel_sseu
+intel_device_default_sseu(struct drm_i915_private *i915)
+{
+       const struct sseu_dev_info *sseu = &RUNTIME_INFO(i915)->sseu;
+       struct intel_sseu value = {
+               .slice_mask = sseu->slice_mask,
+               .subslice_mask = sseu->subslice_mask[0],
+               .min_eus_per_subslice = sseu->max_eus_per_subslice,
+               .max_eus_per_subslice = sseu->max_eus_per_subslice,
+       };
+
+       return value;
+}
+
 /* modesetting */
 extern void intel_modeset_init_hw(struct drm_device *dev);
 extern int intel_modeset_init(struct drm_device *dev);
index e802af64d628d6ee96b01f257d9316e1e3846c9f..6728ea5c71d4c2916a37daa20ec767252a96d0ff 100644 (file)
@@ -1681,6 +1681,16 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
        return 0;
 }
 
+static inline bool
+__vma_matches(struct vm_area_struct *vma, struct file *filp,
+             unsigned long addr, unsigned long size)
+{
+       if (vma->vm_file != filp)
+               return false;
+
+       return vma->vm_start == addr && (vma->vm_end - vma->vm_start) == size;
+}
+
 /**
  * i915_gem_mmap_ioctl - Maps the contents of an object, returning the address
  *                      it is mapped to.
@@ -1730,6 +1740,9 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
        addr = vm_mmap(obj->base.filp, 0, args->size,
                       PROT_READ | PROT_WRITE, MAP_SHARED,
                       args->offset);
+       if (IS_ERR_VALUE(addr))
+               goto err;
+
        if (args->flags & I915_MMAP_WC) {
                struct mm_struct *mm = current->mm;
                struct vm_area_struct *vma;
@@ -1739,23 +1752,28 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
                        return -EINTR;
                }
                vma = find_vma(mm, addr);
-               if (vma)
+               if (vma && __vma_matches(vma, obj->base.filp, addr, args->size))
                        vma->vm_page_prot =
                                pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
                else
                        addr = -ENOMEM;
                up_write(&mm->mmap_sem);
+               if (IS_ERR_VALUE(addr))
+                       goto err;
 
                /* This may race, but that's ok, it only gets set */
                WRITE_ONCE(obj->frontbuffer_ggtt_origin, ORIGIN_CPU);
        }
        i915_gem_object_put(obj);
-       if (IS_ERR((void *)addr))
-               return addr;
 
        args->addr_ptr = (u64)addr;
 
        return 0;
+
+err:
+       i915_gem_object_put(obj);
+
+       return addr;
 }
 
 static unsigned int tile_row_pages(const struct drm_i915_gem_object *obj)
@@ -3020,7 +3038,7 @@ static void assert_kernel_context_is_current(struct drm_i915_private *i915)
 
        GEM_BUG_ON(i915->gt.active_requests);
        for_each_engine(engine, i915, id) {
-               GEM_BUG_ON(__i915_gem_active_peek(&engine->timeline.last_request));
+               GEM_BUG_ON(__i915_active_request_peek(&engine->timeline.last_request));
                GEM_BUG_ON(engine->last_retired_context !=
                           to_intel_context(i915->kernel_context, engine));
        }
@@ -3266,7 +3284,7 @@ wait_for_timelines(struct drm_i915_private *i915,
        list_for_each_entry(tl, &gt->active_list, link) {
                struct i915_request *rq;
 
-               rq = i915_gem_active_get_unlocked(&tl->last_request);
+               rq = i915_active_request_get_unlocked(&tl->last_request);
                if (!rq)
                        continue;
 
@@ -4167,7 +4185,8 @@ out:
 }
 
 static void
-frontbuffer_retire(struct i915_gem_active *active, struct i915_request *request)
+frontbuffer_retire(struct i915_active_request *active,
+                  struct i915_request *request)
 {
        struct drm_i915_gem_object *obj =
                container_of(active, typeof(*obj), frontbuffer_write);
@@ -4194,7 +4213,8 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj,
        obj->resv = &obj->__builtin_resv;
 
        obj->frontbuffer_ggtt_origin = ORIGIN_GTT;
-       init_request_active(&obj->frontbuffer_write, frontbuffer_retire);
+       i915_active_request_init(&obj->frontbuffer_write,
+                                NULL, frontbuffer_retire);
 
        obj->mm.madv = I915_MADV_WILLNEED;
        INIT_RADIX_TREE(&obj->mm.get_page.radix, GFP_KERNEL | __GFP_NOWARN);
index 6faf1f6faab52fc7e58c65d8c3762f219cda0d7a..280813a4bf82a6fdd223c1b6f8dcbcdbe2980e12 100644 (file)
@@ -89,6 +89,7 @@
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
 #include "i915_trace.h"
+#include "intel_lrc_reg.h"
 #include "intel_workarounds.h"
 
 #define ALL_L3_SLICES(dev) (1 << NUM_L3_SLICES(dev)) - 1
@@ -321,6 +322,15 @@ static u32 default_desc_template(const struct drm_i915_private *i915,
        return desc;
 }
 
+static void intel_context_retire(struct i915_active_request *active,
+                                struct i915_request *rq)
+{
+       struct intel_context *ce =
+               container_of(active, typeof(*ce), active_tracker);
+
+       intel_context_unpin(ce);
+}
+
 void
 intel_context_init(struct intel_context *ce,
                   struct i915_gem_context *ctx,
@@ -330,6 +340,12 @@ intel_context_init(struct intel_context *ce,
 
        INIT_LIST_HEAD(&ce->signal_link);
        INIT_LIST_HEAD(&ce->signals);
+
+       /* Use the whole device by default */
+       ce->sseu = intel_device_default_sseu(ctx->i915);
+
+       i915_active_request_init(&ce->active_tracker,
+                                NULL, intel_context_retire);
 }
 
 static struct i915_gem_context *
@@ -653,8 +669,8 @@ last_request_on_engine(struct i915_timeline *timeline,
 
        GEM_BUG_ON(timeline == &engine->timeline);
 
-       rq = i915_gem_active_raw(&timeline->last_request,
-                                &engine->i915->drm.struct_mutex);
+       rq = i915_active_request_raw(&timeline->last_request,
+                                    &engine->i915->drm.struct_mutex);
        if (rq && rq->engine == engine) {
                GEM_TRACE("last request for %s on engine %s: %llx:%llu\n",
                          timeline->name, engine->name,
@@ -847,6 +863,56 @@ out:
        return 0;
 }
 
+static int get_sseu(struct i915_gem_context *ctx,
+                   struct drm_i915_gem_context_param *args)
+{
+       struct drm_i915_gem_context_param_sseu user_sseu;
+       struct intel_engine_cs *engine;
+       struct intel_context *ce;
+       int ret;
+
+       if (args->size == 0)
+               goto out;
+       else if (args->size < sizeof(user_sseu))
+               return -EINVAL;
+
+       if (copy_from_user(&user_sseu, u64_to_user_ptr(args->value),
+                          sizeof(user_sseu)))
+               return -EFAULT;
+
+       if (user_sseu.flags || user_sseu.rsvd)
+               return -EINVAL;
+
+       engine = intel_engine_lookup_user(ctx->i915,
+                                         user_sseu.engine_class,
+                                         user_sseu.engine_instance);
+       if (!engine)
+               return -EINVAL;
+
+       /* Only use for mutex here is to serialize get_param and set_param. */
+       ret = mutex_lock_interruptible(&ctx->i915->drm.struct_mutex);
+       if (ret)
+               return ret;
+
+       ce = to_intel_context(ctx, engine);
+
+       user_sseu.slice_mask = ce->sseu.slice_mask;
+       user_sseu.subslice_mask = ce->sseu.subslice_mask;
+       user_sseu.min_eus_per_subslice = ce->sseu.min_eus_per_subslice;
+       user_sseu.max_eus_per_subslice = ce->sseu.max_eus_per_subslice;
+
+       mutex_unlock(&ctx->i915->drm.struct_mutex);
+
+       if (copy_to_user(u64_to_user_ptr(args->value), &user_sseu,
+                        sizeof(user_sseu)))
+               return -EFAULT;
+
+out:
+       args->size = sizeof(user_sseu);
+
+       return 0;
+}
+
 int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data,
                                    struct drm_file *file)
 {
@@ -859,15 +925,17 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data,
        if (!ctx)
                return -ENOENT;
 
-       args->size = 0;
        switch (args->param) {
        case I915_CONTEXT_PARAM_BAN_PERIOD:
                ret = -EINVAL;
                break;
        case I915_CONTEXT_PARAM_NO_ZEROMAP:
+               args->size = 0;
                args->value = test_bit(UCONTEXT_NO_ZEROMAP, &ctx->user_flags);
                break;
        case I915_CONTEXT_PARAM_GTT_SIZE:
+               args->size = 0;
+
                if (ctx->ppgtt)
                        args->value = ctx->ppgtt->vm.total;
                else if (to_i915(dev)->mm.aliasing_ppgtt)
@@ -876,14 +944,20 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data,
                        args->value = to_i915(dev)->ggtt.vm.total;
                break;
        case I915_CONTEXT_PARAM_NO_ERROR_CAPTURE:
+               args->size = 0;
                args->value = i915_gem_context_no_error_capture(ctx);
                break;
        case I915_CONTEXT_PARAM_BANNABLE:
+               args->size = 0;
                args->value = i915_gem_context_is_bannable(ctx);
                break;
        case I915_CONTEXT_PARAM_PRIORITY:
+               args->size = 0;
                args->value = ctx->sched.priority >> I915_USER_PRIORITY_SHIFT;
                break;
+       case I915_CONTEXT_PARAM_SSEU:
+               ret = get_sseu(ctx, args);
+               break;
        default:
                ret = -EINVAL;
                break;
@@ -893,6 +967,281 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data,
        return ret;
 }
 
+static int gen8_emit_rpcs_config(struct i915_request *rq,
+                                struct intel_context *ce,
+                                struct intel_sseu sseu)
+{
+       u64 offset;
+       u32 *cs;
+
+       cs = intel_ring_begin(rq, 4);
+       if (IS_ERR(cs))
+               return PTR_ERR(cs);
+
+       offset = i915_ggtt_offset(ce->state) +
+                LRC_STATE_PN * PAGE_SIZE +
+                (CTX_R_PWR_CLK_STATE + 1) * 4;
+
+       *cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
+       *cs++ = lower_32_bits(offset);
+       *cs++ = upper_32_bits(offset);
+       *cs++ = gen8_make_rpcs(rq->i915, &sseu);
+
+       intel_ring_advance(rq, cs);
+
+       return 0;
+}
+
+static int
+gen8_modify_rpcs_gpu(struct intel_context *ce,
+                    struct intel_engine_cs *engine,
+                    struct intel_sseu sseu)
+{
+       struct drm_i915_private *i915 = engine->i915;
+       struct i915_request *rq, *prev;
+       intel_wakeref_t wakeref;
+       int ret;
+
+       GEM_BUG_ON(!ce->pin_count);
+
+       lockdep_assert_held(&i915->drm.struct_mutex);
+
+       /* Submitting requests etc needs the hw awake. */
+       wakeref = intel_runtime_pm_get(i915);
+
+       rq = i915_request_alloc(engine, i915->kernel_context);
+       if (IS_ERR(rq)) {
+               ret = PTR_ERR(rq);
+               goto out_put;
+       }
+
+       /* Queue this switch after all other activity by this context. */
+       prev = i915_active_request_raw(&ce->ring->timeline->last_request,
+                                      &i915->drm.struct_mutex);
+       if (prev && !i915_request_completed(prev)) {
+               ret = i915_request_await_dma_fence(rq, &prev->fence);
+               if (ret < 0)
+                       goto out_add;
+       }
+
+       /* Order all following requests to be after. */
+       ret = i915_timeline_set_barrier(ce->ring->timeline, rq);
+       if (ret)
+               goto out_add;
+
+       ret = gen8_emit_rpcs_config(rq, ce, sseu);
+       if (ret)
+               goto out_add;
+
+       /*
+        * Guarantee context image and the timeline remains pinned until the
+        * modifying request is retired by setting the ce activity tracker.
+        *
+        * But we only need to take one pin on the account of it. Or in other
+        * words transfer the pinned ce object to tracked active request.
+        */
+       if (!i915_active_request_isset(&ce->active_tracker))
+               __intel_context_pin(ce);
+       __i915_active_request_set(&ce->active_tracker, rq);
+
+out_add:
+       i915_request_add(rq);
+out_put:
+       intel_runtime_pm_put(i915, wakeref);
+
+       return ret;
+}
+
+static int
+__i915_gem_context_reconfigure_sseu(struct i915_gem_context *ctx,
+                                   struct intel_engine_cs *engine,
+                                   struct intel_sseu sseu)
+{
+       struct intel_context *ce = to_intel_context(ctx, engine);
+       int ret = 0;
+
+       GEM_BUG_ON(INTEL_GEN(ctx->i915) < 8);
+       GEM_BUG_ON(engine->id != RCS);
+
+       /* Nothing to do if unmodified. */
+       if (!memcmp(&ce->sseu, &sseu, sizeof(sseu)))
+               return 0;
+
+       /*
+        * If context is not idle we have to submit an ordered request to modify
+        * its context image via the kernel context. Pristine and idle contexts
+        * will be configured on pinning.
+        */
+       if (ce->pin_count)
+               ret = gen8_modify_rpcs_gpu(ce, engine, sseu);
+
+       if (!ret)
+               ce->sseu = sseu;
+
+       return ret;
+}
+
+static int
+i915_gem_context_reconfigure_sseu(struct i915_gem_context *ctx,
+                                 struct intel_engine_cs *engine,
+                                 struct intel_sseu sseu)
+{
+       int ret;
+
+       ret = mutex_lock_interruptible(&ctx->i915->drm.struct_mutex);
+       if (ret)
+               return ret;
+
+       ret = __i915_gem_context_reconfigure_sseu(ctx, engine, sseu);
+
+       mutex_unlock(&ctx->i915->drm.struct_mutex);
+
+       return ret;
+}
+
+static int
+user_to_context_sseu(struct drm_i915_private *i915,
+                    const struct drm_i915_gem_context_param_sseu *user,
+                    struct intel_sseu *context)
+{
+       const struct sseu_dev_info *device = &RUNTIME_INFO(i915)->sseu;
+
+       /* No zeros in any field. */
+       if (!user->slice_mask || !user->subslice_mask ||
+           !user->min_eus_per_subslice || !user->max_eus_per_subslice)
+               return -EINVAL;
+
+       /* Max > min. */
+       if (user->max_eus_per_subslice < user->min_eus_per_subslice)
+               return -EINVAL;
+
+       /*
+        * Some future proofing on the types since the uAPI is wider than the
+        * current internal implementation.
+        */
+       if (overflows_type(user->slice_mask, context->slice_mask) ||
+           overflows_type(user->subslice_mask, context->subslice_mask) ||
+           overflows_type(user->min_eus_per_subslice,
+                          context->min_eus_per_subslice) ||
+           overflows_type(user->max_eus_per_subslice,
+                          context->max_eus_per_subslice))
+               return -EINVAL;
+
+       /* Check validity against hardware. */
+       if (user->slice_mask & ~device->slice_mask)
+               return -EINVAL;
+
+       if (user->subslice_mask & ~device->subslice_mask[0])
+               return -EINVAL;
+
+       if (user->max_eus_per_subslice > device->max_eus_per_subslice)
+               return -EINVAL;
+
+       context->slice_mask = user->slice_mask;
+       context->subslice_mask = user->subslice_mask;
+       context->min_eus_per_subslice = user->min_eus_per_subslice;
+       context->max_eus_per_subslice = user->max_eus_per_subslice;
+
+       /* Part specific restrictions. */
+       if (IS_GEN(i915, 11)) {
+               unsigned int hw_s = hweight8(device->slice_mask);
+               unsigned int hw_ss_per_s = hweight8(device->subslice_mask[0]);
+               unsigned int req_s = hweight8(context->slice_mask);
+               unsigned int req_ss = hweight8(context->subslice_mask);
+
+               /*
+                * Only full subslice enablement is possible if more than one
+                * slice is turned on.
+                */
+               if (req_s > 1 && req_ss != hw_ss_per_s)
+                       return -EINVAL;
+
+               /*
+                * If more than four (SScount bitfield limit) subslices are
+                * requested then the number has to be even.
+                */
+               if (req_ss > 4 && (req_ss & 1))
+                       return -EINVAL;
+
+               /*
+                * If only one slice is enabled and subslice count is below the
+                * device full enablement, it must be at most half of the all
+                * available subslices.
+                */
+               if (req_s == 1 && req_ss < hw_ss_per_s &&
+                   req_ss > (hw_ss_per_s / 2))
+                       return -EINVAL;
+
+               /* ABI restriction - VME use case only. */
+
+               /* All slices or one slice only. */
+               if (req_s != 1 && req_s != hw_s)
+                       return -EINVAL;
+
+               /*
+                * Half subslices or full enablement only when one slice is
+                * enabled.
+                */
+               if (req_s == 1 &&
+                   (req_ss != hw_ss_per_s && req_ss != (hw_ss_per_s / 2)))
+                       return -EINVAL;
+
+               /* No EU configuration changes. */
+               if ((user->min_eus_per_subslice !=
+                    device->max_eus_per_subslice) ||
+                   (user->max_eus_per_subslice !=
+                    device->max_eus_per_subslice))
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int set_sseu(struct i915_gem_context *ctx,
+                   struct drm_i915_gem_context_param *args)
+{
+       struct drm_i915_private *i915 = ctx->i915;
+       struct drm_i915_gem_context_param_sseu user_sseu;
+       struct intel_engine_cs *engine;
+       struct intel_sseu sseu;
+       int ret;
+
+       if (args->size < sizeof(user_sseu))
+               return -EINVAL;
+
+       if (!IS_GEN(i915, 11))
+               return -ENODEV;
+
+       if (copy_from_user(&user_sseu, u64_to_user_ptr(args->value),
+                          sizeof(user_sseu)))
+               return -EFAULT;
+
+       if (user_sseu.flags || user_sseu.rsvd)
+               return -EINVAL;
+
+       engine = intel_engine_lookup_user(i915,
+                                         user_sseu.engine_class,
+                                         user_sseu.engine_instance);
+       if (!engine)
+               return -EINVAL;
+
+       /* Only render engine supports RPCS configuration. */
+       if (engine->class != RENDER_CLASS)
+               return -ENODEV;
+
+       ret = user_to_context_sseu(i915, &user_sseu, &sseu);
+       if (ret)
+               return ret;
+
+       ret = i915_gem_context_reconfigure_sseu(ctx, engine, sseu);
+       if (ret)
+               return ret;
+
+       args->size = sizeof(user_sseu);
+
+       return 0;
+}
+
 int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data,
                                    struct drm_file *file)
 {
@@ -955,7 +1304,9 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data,
                                        I915_USER_PRIORITY(priority);
                }
                break;
-
+       case I915_CONTEXT_PARAM_SSEU:
+               ret = set_sseu(ctx, args);
+               break;
        default:
                ret = -EINVAL;
                break;
index 6ba40ff6b91fd5d444f767697bea44a7bce9efb5..ca150a764c24d48547106108692e1ac5b2e9ed82 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "i915_gem.h"
 #include "i915_scheduler.h"
+#include "intel_device_info.h"
 
 struct pid;
 
@@ -53,6 +54,16 @@ struct intel_context_ops {
        void (*destroy)(struct intel_context *ce);
 };
 
+/*
+ * Powergating configuration for a particular (context,engine).
+ */
+struct intel_sseu {
+       u8 slice_mask;
+       u8 subslice_mask;
+       u8 min_eus_per_subslice;
+       u8 max_eus_per_subslice;
+};
+
 /**
  * struct i915_gem_context - client state
  *
@@ -172,7 +183,16 @@ struct i915_gem_context {
                u64 lrc_desc;
                int pin_count;
 
+               /**
+                * active_tracker: Active tracker for the external rq activity
+                * on this intel_context object.
+                */
+               struct i915_active_request active_tracker;
+
                const struct intel_context_ops *ops;
+
+               /** sseu: Control eu/slice partitioning */
+               struct intel_sseu sseu;
        } __engine[I915_NUM_ENGINES];
 
        /** ring_size: size for allocating the per-engine ring buffer */
index 8eedf7cac4938f1444b2aa32e346182a038ec9a6..02adcaf6ebea69086aa07be57fed55b347636c94 100644 (file)
@@ -753,6 +753,68 @@ static int eb_select_context(struct i915_execbuffer *eb)
        return 0;
 }
 
+static struct i915_request *__eb_wait_for_ring(struct intel_ring *ring)
+{
+       struct i915_request *rq;
+
+       /*
+        * Completely unscientific finger-in-the-air estimates for suitable
+        * maximum user request size (to avoid blocking) and then backoff.
+        */
+       if (intel_ring_update_space(ring) >= PAGE_SIZE)
+               return NULL;
+
+       /*
+        * Find a request that after waiting upon, there will be at least half
+        * the ring available. The hysteresis allows us to compete for the
+        * shared ring and should mean that we sleep less often prior to
+        * claiming our resources, but not so long that the ring completely
+        * drains before we can submit our next request.
+        */
+       list_for_each_entry(rq, &ring->request_list, ring_link) {
+               if (__intel_ring_space(rq->postfix,
+                                      ring->emit, ring->size) > ring->size / 2)
+                       break;
+       }
+       if (&rq->ring_link == &ring->request_list)
+               return NULL; /* weird, we will check again later for real */
+
+       return i915_request_get(rq);
+}
+
+static int eb_wait_for_ring(const struct i915_execbuffer *eb)
+{
+       const struct intel_context *ce;
+       struct i915_request *rq;
+       int ret = 0;
+
+       /*
+        * Apply a light amount of backpressure to prevent excessive hogs
+        * from blocking waiting for space whilst holding struct_mutex and
+        * keeping all of their resources pinned.
+        */
+
+       ce = to_intel_context(eb->ctx, eb->engine);
+       if (!ce->ring) /* first use, assume empty! */
+               return 0;
+
+       rq = __eb_wait_for_ring(ce->ring);
+       if (rq) {
+               mutex_unlock(&eb->i915->drm.struct_mutex);
+
+               if (i915_request_wait(rq,
+                                     I915_WAIT_INTERRUPTIBLE,
+                                     MAX_SCHEDULE_TIMEOUT) < 0)
+                       ret = -EINTR;
+
+               i915_request_put(rq);
+
+               mutex_lock(&eb->i915->drm.struct_mutex);
+       }
+
+       return ret;
+}
+
 static int eb_lookup_vmas(struct i915_execbuffer *eb)
 {
        struct radix_tree_root *handles_vma = &eb->ctx->handles_vma;
@@ -2291,6 +2353,10 @@ i915_gem_do_execbuffer(struct drm_device *dev,
        if (err)
                goto err_rpm;
 
+       err = eb_wait_for_ring(&eb); /* may temporarily drop struct_mutex */
+       if (unlikely(err))
+               goto err_unlock;
+
        err = eb_relocate(&eb);
        if (err) {
                /*
@@ -2435,6 +2501,7 @@ err_batch_unpin:
 err_vma:
        if (eb.exec)
                eb_release_vmas(&eb);
+err_unlock:
        mutex_unlock(&dev->struct_mutex);
 err_rpm:
        intel_runtime_pm_put(eb.i915, wakeref);
index 46e259661294e6d715765a9a390b630d3ad57f80..e037e94792f3530e202e66c8d0b5c915bf49e454 100644 (file)
@@ -223,7 +223,7 @@ static int fence_update(struct drm_i915_fence_reg *fence,
                         i915_gem_object_get_tiling(vma->obj)))
                        return -EINVAL;
 
-               ret = i915_gem_active_retire(&vma->last_fence,
+               ret = i915_active_request_retire(&vma->last_fence,
                                             &vma->obj->base.dev->struct_mutex);
                if (ret)
                        return ret;
@@ -232,7 +232,7 @@ static int fence_update(struct drm_i915_fence_reg *fence,
        if (fence->vma) {
                struct i915_vma *old = fence->vma;
 
-               ret = i915_gem_active_retire(&old->last_fence,
+               ret = i915_active_request_retire(&old->last_fence,
                                             &old->obj->base.dev->struct_mutex);
                if (ret)
                        return ret;
index 49b00996a15e448515e2250d8b45450bd891e867..d646d37eec2f8a751a3a7f1642bae26841d2b24f 100644 (file)
@@ -1917,14 +1917,13 @@ static struct i915_vma *pd_vma_create(struct gen6_hw_ppgtt *ppgtt, int size)
        if (!vma)
                return ERR_PTR(-ENOMEM);
 
-       init_request_active(&vma->last_fence, NULL);
+       i915_active_init(i915, &vma->active, NULL);
+       INIT_ACTIVE_REQUEST(&vma->last_fence);
 
        vma->vm = &ggtt->vm;
        vma->ops = &pd_vma_ops;
        vma->private = ppgtt;
 
-       vma->active = RB_ROOT;
-
        vma->size = size;
        vma->fence_size = size;
        vma->flags = I915_VMA_GGTT;
index 73fec917d097585c230818fd42f161ad56c5cbab..fab040331cdb250d44b5791833da357fa5c82206 100644 (file)
@@ -175,7 +175,7 @@ struct drm_i915_gem_object {
 
        atomic_t frontbuffer_bits;
        unsigned int frontbuffer_ggtt_origin; /* write once */
-       struct i915_gem_active frontbuffer_write;
+       struct i915_active_request frontbuffer_write;
 
        /** Current tiling stride for the object, if it's tiled. */
        unsigned int tiling_and_stride;
index 6e2e5ed2bd0ac92cc5659b70c0b307196fbc421b..9a65341fec097e500ace05a410b62f6f19390d21 100644 (file)
@@ -1062,23 +1062,23 @@ i915_error_object_create(struct drm_i915_private *i915,
 }
 
 /* The error capture is special as tries to run underneath the normal
- * locking rules - so we use the raw version of the i915_gem_active lookup.
+ * locking rules - so we use the raw version of the i915_active_request lookup.
  */
 static inline u32
-__active_get_seqno(struct i915_gem_active *active)
+__active_get_seqno(struct i915_active_request *active)
 {
        struct i915_request *request;
 
-       request = __i915_gem_active_peek(active);
+       request = __i915_active_request_peek(active);
        return request ? request->global_seqno : 0;
 }
 
 static inline int
-__active_get_engine_id(struct i915_gem_active *active)
+__active_get_engine_id(struct i915_active_request *active)
 {
        struct i915_request *request;
 
-       request = __i915_gem_active_peek(active);
+       request = __i915_active_request_peek(active);
        return request ? request->engine->id : -1;
 }
 
index 5d05572c9ff4281121568bb2bcafeaf87c05f109..66f82f3f050f34aa1a6756800b2436ceab50c9ab 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <drm/drm_drv.h>
 
+#include "i915_active.h"
 #include "i915_drv.h"
 #include "i915_selftest.h"
 
@@ -89,7 +90,7 @@
        .num_pipes = 1, \
        .display.has_overlay = 1, \
        .display.overlay_needs_physical = 1, \
-       .display.has_gmch_display = 1, \
+       .display.has_gmch = 1, \
        .gpu_reset_clobbers_display = true, \
        .hws_needs_physical = 1, \
        .unfenced_needs_alignment = 1, \
@@ -130,7 +131,7 @@ static const struct intel_device_info intel_i865g_info = {
 #define GEN3_FEATURES \
        GEN(3), \
        .num_pipes = 2, \
-       .display.has_gmch_display = 1, \
+       .display.has_gmch = 1, \
        .gpu_reset_clobbers_display = true, \
        .ring_mask = RENDER_RING, \
        .has_snoop = true, \
@@ -207,7 +208,7 @@ static const struct intel_device_info intel_pineview_info = {
        GEN(4), \
        .num_pipes = 2, \
        .display.has_hotplug = 1, \
-       .display.has_gmch_display = 1, \
+       .display.has_gmch = 1, \
        .gpu_reset_clobbers_display = true, \
        .ring_mask = RENDER_RING, \
        .has_snoop = true, \
@@ -383,7 +384,7 @@ static const struct intel_device_info intel_valleyview_info = {
        .num_pipes = 2,
        .has_runtime_pm = 1,
        .has_rc6 = 1,
-       .display.has_gmch_display = 1,
+       .display.has_gmch = 1,
        .display.has_hotplug = 1,
        .ppgtt = INTEL_PPGTT_FULL,
        .has_snoop = true,
@@ -475,7 +476,7 @@ static const struct intel_device_info intel_cherryview_info = {
        .has_runtime_pm = 1,
        .has_rc6 = 1,
        .has_logical_ring_contexts = 1,
-       .display.has_gmch_display = 1,
+       .display.has_gmch = 1,
        .ppgtt = INTEL_PPGTT_FULL,
        .has_reset_engine = 1,
        .has_snoop = true,
@@ -800,6 +801,8 @@ static int __init i915_init(void)
        bool use_kms = true;
        int err;
 
+       i915_global_active_init();
+
        err = i915_mock_selftests();
        if (err)
                return err > 0 ? 0 : err;
@@ -831,6 +834,7 @@ static void __exit i915_exit(void)
                return;
 
        pci_unregister_driver(&i915_pci_driver);
+       i915_global_active_exit();
 }
 
 module_init(i915_init);
index 727118301f919dfbe594692229e8b53f7f958cd8..9ebf99f3d8d3ee524443f3cea90a1ab56e48fe3c 100644 (file)
@@ -1677,6 +1677,11 @@ static void gen8_update_reg_state_unlocked(struct i915_gem_context *ctx,
 
                CTX_REG(reg_state, state_offset, flex_regs[i], value);
        }
+
+       CTX_REG(reg_state, CTX_R_PWR_CLK_STATE, GEN8_R_PWR_CLK_STATE,
+               gen8_make_rpcs(dev_priv,
+                              &to_intel_context(ctx,
+                                                dev_priv->engine[RCS])->sseu));
 }
 
 /*
@@ -2098,21 +2103,21 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream,
        if (ret)
                goto err_lock;
 
+       stream->ops = &i915_oa_stream_ops;
+       dev_priv->perf.oa.exclusive_stream = stream;
+
        ret = dev_priv->perf.oa.ops.enable_metric_set(stream);
        if (ret) {
                DRM_DEBUG("Unable to enable metric set\n");
                goto err_enable;
        }
 
-       stream->ops = &i915_oa_stream_ops;
-
-       dev_priv->perf.oa.exclusive_stream = stream;
-
        mutex_unlock(&dev_priv->drm.struct_mutex);
 
        return 0;
 
 err_enable:
+       dev_priv->perf.oa.exclusive_stream = NULL;
        dev_priv->perf.oa.ops.disable_metric_set(dev_priv);
        mutex_unlock(&dev_priv->drm.struct_mutex);
 
index b1cb2d3cae1606b31ba12e6380e9bb96090d28bb..13d70b90dd0f9bebb3130b3116fcf5585f667ec5 100644 (file)
@@ -599,7 +599,8 @@ static void i915_pmu_enable(struct perf_event *event)
         * Update the bitmask of enabled events and increment
         * the event reference counter.
         */
-       GEM_BUG_ON(bit >= I915_PMU_MASK_BITS);
+       BUILD_BUG_ON(ARRAY_SIZE(i915->pmu.enable_count) != I915_PMU_MASK_BITS);
+       GEM_BUG_ON(bit >= ARRAY_SIZE(i915->pmu.enable_count));
        GEM_BUG_ON(i915->pmu.enable_count[bit] == ~0);
        i915->pmu.enable |= BIT_ULL(bit);
        i915->pmu.enable_count[bit]++;
@@ -620,11 +621,16 @@ static void i915_pmu_enable(struct perf_event *event)
                engine = intel_engine_lookup_user(i915,
                                                  engine_event_class(event),
                                                  engine_event_instance(event));
-               GEM_BUG_ON(!engine);
-               engine->pmu.enable |= BIT(sample);
 
-               GEM_BUG_ON(sample >= I915_PMU_SAMPLE_BITS);
+               BUILD_BUG_ON(ARRAY_SIZE(engine->pmu.enable_count) !=
+                            I915_ENGINE_SAMPLE_COUNT);
+               BUILD_BUG_ON(ARRAY_SIZE(engine->pmu.sample) !=
+                            I915_ENGINE_SAMPLE_COUNT);
+               GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.enable_count));
+               GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.sample));
                GEM_BUG_ON(engine->pmu.enable_count[sample] == ~0);
+
+               engine->pmu.enable |= BIT(sample);
                engine->pmu.enable_count[sample]++;
        }
 
@@ -654,9 +660,11 @@ static void i915_pmu_disable(struct perf_event *event)
                engine = intel_engine_lookup_user(i915,
                                                  engine_event_class(event),
                                                  engine_event_instance(event));
-               GEM_BUG_ON(!engine);
-               GEM_BUG_ON(sample >= I915_PMU_SAMPLE_BITS);
+
+               GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.enable_count));
+               GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.sample));
                GEM_BUG_ON(engine->pmu.enable_count[sample] == 0);
+
                /*
                 * Decrement the reference count and clear the enabled
                 * bitmask when the last listener on an event goes away.
@@ -665,7 +673,7 @@ static void i915_pmu_disable(struct perf_event *event)
                        engine->pmu.enable &= ~BIT(sample);
        }
 
-       GEM_BUG_ON(bit >= I915_PMU_MASK_BITS);
+       GEM_BUG_ON(bit >= ARRAY_SIZE(i915->pmu.enable_count));
        GEM_BUG_ON(i915->pmu.enable_count[bit] == 0);
        /*
         * Decrement the reference count and clear the enabled
index 7f164ca3db129472d3262439f5290d505ea6e14a..b3728c5f13e739f7c3a1e5c0d5e1a55deca1c342 100644 (file)
@@ -31,6 +31,8 @@ enum {
        ((1 << I915_PMU_SAMPLE_BITS) + \
         (I915_PMU_LAST + 1 - __I915_PMU_OTHER(0)))
 
+#define I915_ENGINE_SAMPLE_COUNT (I915_SAMPLE_SEMA + 1)
+
 struct i915_pmu_sample {
        u64 cur;
 };
index ede54fdc1676f89b29c2f9ff03bc88d8f350d2df..638a586469f97be9fb83bbbcb152c518e7d46e1e 100644 (file)
@@ -6000,7 +6000,7 @@ enum {
 #define   PLANE_WM_EN          (1 << 31)
 #define   PLANE_WM_LINES_SHIFT 14
 #define   PLANE_WM_LINES_MASK  0x1f
-#define   PLANE_WM_BLOCKS_MASK 0x3ff
+#define   PLANE_WM_BLOCKS_MASK 0x7ff /* skl+: 10 bits, icl+ 11 bits */
 
 #define _CUR_WM_0(pipe) _PIPE(pipe, _CUR_WM_A_0, _CUR_WM_B_0)
 #define CUR_WM(pipe, level) _MMIO(_CUR_WM_0(pipe) + ((4) * (level)))
@@ -6784,8 +6784,7 @@ enum {
 
 #define _PLANE_BUF_CFG_1_B                     0x7127c
 #define _PLANE_BUF_CFG_2_B                     0x7137c
-#define  SKL_DDB_ENTRY_MASK                    0x3FF
-#define  ICL_DDB_ENTRY_MASK                    0x7FF
+#define  DDB_ENTRY_MASK                                0x7FF /* skl+: 10 bits, icl+ 11 bits */
 #define  DDB_ENTRY_END_SHIFT                   16
 #define _PLANE_BUF_CFG_1(pipe) \
        _PIPE(pipe, _PLANE_BUF_CFG_1_A, _PLANE_BUF_CFG_1_B)
@@ -7618,6 +7617,7 @@ enum {
 #define _PIPEB_CHICKEN                 0x71038
 #define _PIPEC_CHICKEN                 0x72038
 #define  PER_PIXEL_ALPHA_BYPASS_EN     (1 << 7)
+#define  PM_FILL_MAINTAIN_DBUF_FULLNESS        (1 << 0)
 #define PIPE_CHICKEN(pipe)             _MMIO_PIPE(pipe, _PIPEA_CHICKEN,\
                                                   _PIPEB_CHICKEN)
 
index 9ed5baf157a392c45271eebefd3d615e12ef1410..c2a5c48c7541d6d1bb230933748b210ff036bd78 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/sched/signal.h>
 
 #include "i915_drv.h"
+#include "i915_active.h"
 #include "i915_reset.h"
 
 static const char *i915_fence_get_driver_name(struct dma_fence *fence)
@@ -125,12 +126,6 @@ static void unreserve_gt(struct drm_i915_private *i915)
                i915_gem_park(i915);
 }
 
-void i915_gem_retire_noop(struct i915_gem_active *active,
-                         struct i915_request *request)
-{
-       /* Space left intentionally blank */
-}
-
 static void advance_ring(struct i915_request *request)
 {
        struct intel_ring *ring = request->ring;
@@ -244,7 +239,7 @@ static void __retire_engine_upto(struct intel_engine_cs *engine,
 
 static void i915_request_retire(struct i915_request *request)
 {
-       struct i915_gem_active *active, *next;
+       struct i915_active_request *active, *next;
 
        GEM_TRACE("%s fence %llx:%lld, global=%d, current %d:%d\n",
                  request->engine->name,
@@ -278,10 +273,10 @@ static void i915_request_retire(struct i915_request *request)
                 * we may spend an inordinate amount of time simply handling
                 * the retirement of requests and processing their callbacks.
                 * Of which, this loop itself is particularly hot due to the
-                * cache misses when jumping around the list of i915_gem_active.
-                * So we try to keep this loop as streamlined as possible and
-                * also prefetch the next i915_gem_active to try and hide
-                * the likely cache miss.
+                * cache misses when jumping around the list of
+                * i915_active_request.  So we try to keep this loop as
+                * streamlined as possible and also prefetch the next
+                * i915_active_request to try and hide the likely cache miss.
                 */
                prefetchw(next);
 
@@ -526,6 +521,11 @@ out:
        return kmem_cache_alloc(ce->gem_context->i915->requests, GFP_KERNEL);
 }
 
+static int add_timeline_barrier(struct i915_request *rq)
+{
+       return i915_request_await_active_request(rq, &rq->timeline->barrier);
+}
+
 /**
  * i915_request_alloc - allocate a request structure
  *
@@ -582,7 +582,7 @@ i915_request_alloc(struct intel_engine_cs *engine, struct i915_gem_context *ctx)
         * We use RCU to look up requests in flight. The lookups may
         * race with the request being allocated from the slab freelist.
         * That is the request we are writing to here, may be in the process
-        * of being read by __i915_gem_active_get_rcu(). As such,
+        * of being read by __i915_active_request_get_rcu(). As such,
         * we have to be very careful when overwriting the contents. During
         * the RCU lookup, we change chase the request->engine pointer,
         * read the request->global_seqno and increment the reference count.
@@ -668,6 +668,10 @@ i915_request_alloc(struct intel_engine_cs *engine, struct i915_gem_context *ctx)
         */
        rq->head = rq->ring->emit;
 
+       ret = add_timeline_barrier(rq);
+       if (ret)
+               goto err_unwind;
+
        ret = engine->request_alloc(rq);
        if (ret)
                goto err_unwind;
@@ -920,8 +924,8 @@ void i915_request_add(struct i915_request *request)
         * see a more recent value in the hws than we are tracking.
         */
 
-       prev = i915_gem_active_raw(&timeline->last_request,
-                                  &request->i915->drm.struct_mutex);
+       prev = i915_active_request_raw(&timeline->last_request,
+                                      &request->i915->drm.struct_mutex);
        if (prev && !i915_request_completed(prev)) {
                i915_sw_fence_await_sw_fence(&request->submit, &prev->submit,
                                             &request->submitq);
@@ -937,7 +941,7 @@ void i915_request_add(struct i915_request *request)
        spin_unlock_irq(&timeline->lock);
 
        GEM_BUG_ON(timeline->seqno != request->fence.seqno);
-       i915_gem_active_set(&timeline->last_request, request);
+       __i915_active_request_set(&timeline->last_request, request);
 
        list_add_tail(&request->ring_link, &ring->request_list);
        if (list_is_first(&request->ring_link, &ring->request_list)) {
@@ -968,7 +972,7 @@ void i915_request_add(struct i915_request *request)
                 * Allow interactive/synchronous clients to jump ahead of
                 * the bulk clients. (FQ_CODEL)
                 */
-               if (!prev || i915_request_completed(prev))
+               if (list_empty(&request->sched.signalers_list))
                        attr.priority |= I915_PRIORITY_NEWCLIENT;
 
                engine->schedule(request, &attr);
index 3cffb96203b9b44f09ba4063538d6621ba4630f7..40f3e8dcbdd51a2f02935092b52cd6c7f9348060 100644 (file)
@@ -403,387 +403,4 @@ static inline void i915_request_mark_complete(struct i915_request *rq)
 
 void i915_retire_requests(struct drm_i915_private *i915);
 
-/*
- * We treat requests as fences. This is not be to confused with our
- * "fence registers" but pipeline synchronisation objects ala GL_ARB_sync.
- * We use the fences to synchronize access from the CPU with activity on the
- * GPU, for example, we should not rewrite an object's PTE whilst the GPU
- * is reading them. We also track fences at a higher level to provide
- * implicit synchronisation around GEM objects, e.g. set-domain will wait
- * for outstanding GPU rendering before marking the object ready for CPU
- * access, or a pageflip will wait until the GPU is complete before showing
- * the frame on the scanout.
- *
- * In order to use a fence, the object must track the fence it needs to
- * serialise with. For example, GEM objects want to track both read and
- * write access so that we can perform concurrent read operations between
- * the CPU and GPU engines, as well as waiting for all rendering to
- * complete, or waiting for the last GPU user of a "fence register". The
- * object then embeds a #i915_gem_active to track the most recent (in
- * retirement order) request relevant for the desired mode of access.
- * The #i915_gem_active is updated with i915_gem_active_set() to track the
- * most recent fence request, typically this is done as part of
- * i915_vma_move_to_active().
- *
- * When the #i915_gem_active completes (is retired), it will
- * signal its completion to the owner through a callback as well as mark
- * itself as idle (i915_gem_active.request == NULL). The owner
- * can then perform any action, such as delayed freeing of an active
- * resource including itself.
- */
-struct i915_gem_active;
-
-typedef void (*i915_gem_retire_fn)(struct i915_gem_active *,
-                                  struct i915_request *);
-
-struct i915_gem_active {
-       struct i915_request __rcu *request;
-       struct list_head link;
-       i915_gem_retire_fn retire;
-};
-
-void i915_gem_retire_noop(struct i915_gem_active *,
-                         struct i915_request *request);
-
-/**
- * init_request_active - prepares the activity tracker for use
- * @active - the active tracker
- * @func - a callback when then the tracker is retired (becomes idle),
- *         can be NULL
- *
- * init_request_active() prepares the embedded @active struct for use as
- * an activity tracker, that is for tracking the last known active request
- * associated with it. When the last request becomes idle, when it is retired
- * after completion, the optional callback @func is invoked.
- */
-static inline void
-init_request_active(struct i915_gem_active *active,
-                   i915_gem_retire_fn retire)
-{
-       RCU_INIT_POINTER(active->request, NULL);
-       INIT_LIST_HEAD(&active->link);
-       active->retire = retire ?: i915_gem_retire_noop;
-}
-
-/**
- * i915_gem_active_set - updates the tracker to watch the current request
- * @active - the active tracker
- * @request - the request to watch
- *
- * i915_gem_active_set() watches the given @request for completion. Whilst
- * that @request is busy, the @active reports busy. When that @request is
- * retired, the @active tracker is updated to report idle.
- */
-static inline void
-i915_gem_active_set(struct i915_gem_active *active,
-                   struct i915_request *request)
-{
-       list_move(&active->link, &request->active_list);
-       rcu_assign_pointer(active->request, request);
-}
-
-/**
- * i915_gem_active_set_retire_fn - updates the retirement callback
- * @active - the active tracker
- * @fn - the routine called when the request is retired
- * @mutex - struct_mutex used to guard retirements
- *
- * i915_gem_active_set_retire_fn() updates the function pointer that
- * is called when the final request associated with the @active tracker
- * is retired.
- */
-static inline void
-i915_gem_active_set_retire_fn(struct i915_gem_active *active,
-                             i915_gem_retire_fn fn,
-                             struct mutex *mutex)
-{
-       lockdep_assert_held(mutex);
-       active->retire = fn ?: i915_gem_retire_noop;
-}
-
-static inline struct i915_request *
-__i915_gem_active_peek(const struct i915_gem_active *active)
-{
-       /*
-        * Inside the error capture (running with the driver in an unknown
-        * state), we want to bend the rules slightly (a lot).
-        *
-        * Work is in progress to make it safer, in the meantime this keeps
-        * the known issue from spamming the logs.
-        */
-       return rcu_dereference_protected(active->request, 1);
-}
-
-/**
- * i915_gem_active_raw - return the active request
- * @active - the active tracker
- *
- * i915_gem_active_raw() returns the current request being tracked, or NULL.
- * It does not obtain a reference on the request for the caller, so the caller
- * must hold struct_mutex.
- */
-static inline struct i915_request *
-i915_gem_active_raw(const struct i915_gem_active *active, struct mutex *mutex)
-{
-       return rcu_dereference_protected(active->request,
-                                        lockdep_is_held(mutex));
-}
-
-/**
- * i915_gem_active_peek - report the active request being monitored
- * @active - the active tracker
- *
- * i915_gem_active_peek() returns the current request being tracked if
- * still active, or NULL. It does not obtain a reference on the request
- * for the caller, so the caller must hold struct_mutex.
- */
-static inline struct i915_request *
-i915_gem_active_peek(const struct i915_gem_active *active, struct mutex *mutex)
-{
-       struct i915_request *request;
-
-       request = i915_gem_active_raw(active, mutex);
-       if (!request || i915_request_completed(request))
-               return NULL;
-
-       return request;
-}
-
-/**
- * i915_gem_active_get - return a reference to the active request
- * @active - the active tracker
- *
- * i915_gem_active_get() returns a reference to the active request, or NULL
- * if the active tracker is idle. The caller must hold struct_mutex.
- */
-static inline struct i915_request *
-i915_gem_active_get(const struct i915_gem_active *active, struct mutex *mutex)
-{
-       return i915_request_get(i915_gem_active_peek(active, mutex));
-}
-
-/**
- * __i915_gem_active_get_rcu - return a reference to the active request
- * @active - the active tracker
- *
- * __i915_gem_active_get() returns a reference to the active request, or NULL
- * if the active tracker is idle. The caller must hold the RCU read lock, but
- * the returned pointer is safe to use outside of RCU.
- */
-static inline struct i915_request *
-__i915_gem_active_get_rcu(const struct i915_gem_active *active)
-{
-       /*
-        * Performing a lockless retrieval of the active request is super
-        * tricky. SLAB_TYPESAFE_BY_RCU merely guarantees that the backing
-        * slab of request objects will not be freed whilst we hold the
-        * RCU read lock. It does not guarantee that the request itself
-        * will not be freed and then *reused*. Viz,
-        *
-        * Thread A                     Thread B
-        *
-        * rq = active.request
-        *                              retire(rq) -> free(rq);
-        *                              (rq is now first on the slab freelist)
-        *                              active.request = NULL
-        *
-        *                              rq = new submission on a new object
-        * ref(rq)
-        *
-        * To prevent the request from being reused whilst the caller
-        * uses it, we take a reference like normal. Whilst acquiring
-        * the reference we check that it is not in a destroyed state
-        * (refcnt == 0). That prevents the request being reallocated
-        * whilst the caller holds on to it. To check that the request
-        * was not reallocated as we acquired the reference we have to
-        * check that our request remains the active request across
-        * the lookup, in the same manner as a seqlock. The visibility
-        * of the pointer versus the reference counting is controlled
-        * by using RCU barriers (rcu_dereference and rcu_assign_pointer).
-        *
-        * In the middle of all that, we inspect whether the request is
-        * complete. Retiring is lazy so the request may be completed long
-        * before the active tracker is updated. Querying whether the
-        * request is complete is far cheaper (as it involves no locked
-        * instructions setting cachelines to exclusive) than acquiring
-        * the reference, so we do it first. The RCU read lock ensures the
-        * pointer dereference is valid, but does not ensure that the
-        * seqno nor HWS is the right one! However, if the request was
-        * reallocated, that means the active tracker's request was complete.
-        * If the new request is also complete, then both are and we can
-        * just report the active tracker is idle. If the new request is
-        * incomplete, then we acquire a reference on it and check that
-        * it remained the active request.
-        *
-        * It is then imperative that we do not zero the request on
-        * reallocation, so that we can chase the dangling pointers!
-        * See i915_request_alloc().
-        */
-       do {
-               struct i915_request *request;
-
-               request = rcu_dereference(active->request);
-               if (!request || i915_request_completed(request))
-                       return NULL;
-
-               /*
-                * An especially silly compiler could decide to recompute the
-                * result of i915_request_completed, more specifically
-                * re-emit the load for request->fence.seqno. A race would catch
-                * a later seqno value, which could flip the result from true to
-                * false. Which means part of the instructions below might not
-                * be executed, while later on instructions are executed. Due to
-                * barriers within the refcounting the inconsistency can't reach
-                * past the call to i915_request_get_rcu, but not executing
-                * that while still executing i915_request_put() creates
-                * havoc enough.  Prevent this with a compiler barrier.
-                */
-               barrier();
-
-               request = i915_request_get_rcu(request);
-
-               /*
-                * What stops the following rcu_access_pointer() from occurring
-                * before the above i915_request_get_rcu()? If we were
-                * to read the value before pausing to get the reference to
-                * the request, we may not notice a change in the active
-                * tracker.
-                *
-                * The rcu_access_pointer() is a mere compiler barrier, which
-                * means both the CPU and compiler are free to perform the
-                * memory read without constraint. The compiler only has to
-                * ensure that any operations after the rcu_access_pointer()
-                * occur afterwards in program order. This means the read may
-                * be performed earlier by an out-of-order CPU, or adventurous
-                * compiler.
-                *
-                * The atomic operation at the heart of
-                * i915_request_get_rcu(), see dma_fence_get_rcu(), is
-                * atomic_inc_not_zero() which is only a full memory barrier
-                * when successful. That is, if i915_request_get_rcu()
-                * returns the request (and so with the reference counted
-                * incremented) then the following read for rcu_access_pointer()
-                * must occur after the atomic operation and so confirm
-                * that this request is the one currently being tracked.
-                *
-                * The corresponding write barrier is part of
-                * rcu_assign_pointer().
-                */
-               if (!request || request == rcu_access_pointer(active->request))
-                       return rcu_pointer_handoff(request);
-
-               i915_request_put(request);
-       } while (1);
-}
-
-/**
- * i915_gem_active_get_unlocked - return a reference to the active request
- * @active - the active tracker
- *
- * i915_gem_active_get_unlocked() returns a reference to the active request,
- * or NULL if the active tracker is idle. The reference is obtained under RCU,
- * so no locking is required by the caller.
- *
- * The reference should be freed with i915_request_put().
- */
-static inline struct i915_request *
-i915_gem_active_get_unlocked(const struct i915_gem_active *active)
-{
-       struct i915_request *request;
-
-       rcu_read_lock();
-       request = __i915_gem_active_get_rcu(active);
-       rcu_read_unlock();
-
-       return request;
-}
-
-/**
- * i915_gem_active_isset - report whether the active tracker is assigned
- * @active - the active tracker
- *
- * i915_gem_active_isset() returns true if the active tracker is currently
- * assigned to a request. Due to the lazy retiring, that request may be idle
- * and this may report stale information.
- */
-static inline bool
-i915_gem_active_isset(const struct i915_gem_active *active)
-{
-       return rcu_access_pointer(active->request);
-}
-
-/**
- * i915_gem_active_wait - waits until the request is completed
- * @active - the active request on which to wait
- * @flags - how to wait
- * @timeout - how long to wait at most
- * @rps - userspace client to charge for a waitboost
- *
- * i915_gem_active_wait() waits until the request is completed before
- * returning, without requiring any locks to be held. Note that it does not
- * retire any requests before returning.
- *
- * This function relies on RCU in order to acquire the reference to the active
- * request without holding any locks. See __i915_gem_active_get_rcu() for the
- * glory details on how that is managed. Once the reference is acquired, we
- * can then wait upon the request, and afterwards release our reference,
- * free of any locking.
- *
- * This function wraps i915_request_wait(), see it for the full details on
- * the arguments.
- *
- * Returns 0 if successful, or a negative error code.
- */
-static inline int
-i915_gem_active_wait(const struct i915_gem_active *active, unsigned int flags)
-{
-       struct i915_request *request;
-       long ret = 0;
-
-       request = i915_gem_active_get_unlocked(active);
-       if (request) {
-               ret = i915_request_wait(request, flags, MAX_SCHEDULE_TIMEOUT);
-               i915_request_put(request);
-       }
-
-       return ret < 0 ? ret : 0;
-}
-
-/**
- * i915_gem_active_retire - waits until the request is retired
- * @active - the active request on which to wait
- *
- * i915_gem_active_retire() waits until the request is completed,
- * and then ensures that at least the retirement handler for this
- * @active tracker is called before returning. If the @active
- * tracker is idle, the function returns immediately.
- */
-static inline int __must_check
-i915_gem_active_retire(struct i915_gem_active *active,
-                      struct mutex *mutex)
-{
-       struct i915_request *request;
-       long ret;
-
-       request = i915_gem_active_raw(active, mutex);
-       if (!request)
-               return 0;
-
-       ret = i915_request_wait(request,
-                               I915_WAIT_INTERRUPTIBLE | I915_WAIT_LOCKED,
-                               MAX_SCHEDULE_TIMEOUT);
-       if (ret < 0)
-               return ret;
-
-       list_del_init(&active->link);
-       RCU_INIT_POINTER(active->request, NULL);
-
-       active->retire(active, request);
-
-       return 0;
-}
-
-#define for_each_active(mask, idx) \
-       for (; mask ? idx = ffs(mask) - 1, 1 : 0; mask &= ~BIT(idx))
-
 #endif /* I915_REQUEST_H */
index 4462007a681c60898c8269efd692c088485db3ae..0e0ddf2e681521915b9255cf1a03dfaf79e3578e 100644 (file)
@@ -862,7 +862,7 @@ bool i915_gem_unset_wedged(struct drm_i915_private *i915)
                struct i915_request *rq;
                long timeout;
 
-               rq = i915_gem_active_get_unlocked(&tl->last_request);
+               rq = i915_active_request_get_unlocked(&tl->last_request);
                if (!rq)
                        continue;
 
index f18afa2bac8d87efa40fe2187d45aff240462a8b..d2f2a9c2fabd67d206ff5191d0af8a149d1088eb 100644 (file)
@@ -86,7 +86,7 @@ int i915_save_state(struct drm_i915_private *dev_priv)
        } else if (IS_GEN(dev_priv, 2)) {
                for (i = 0; i < 7; i++)
                        dev_priv->regfile.saveSWF1[i] = I915_READ(SWF1(i));
-       } else if (HAS_GMCH_DISPLAY(dev_priv)) {
+       } else if (HAS_GMCH(dev_priv)) {
                for (i = 0; i < 16; i++) {
                        dev_priv->regfile.saveSWF0[i] = I915_READ(SWF0(i));
                        dev_priv->regfile.saveSWF1[i] = I915_READ(SWF1(i));
@@ -131,7 +131,7 @@ int i915_restore_state(struct drm_i915_private *dev_priv)
        } else if (IS_GEN(dev_priv, 2)) {
                for (i = 0; i < 7; i++)
                        I915_WRITE(SWF1(i), dev_priv->regfile.saveSWF1[i]);
-       } else if (HAS_GMCH_DISPLAY(dev_priv)) {
+       } else if (HAS_GMCH(dev_priv)) {
                for (i = 0; i < 16; i++) {
                        I915_WRITE(SWF0(i), dev_priv->regfile.saveSWF0[i]);
                        I915_WRITE(SWF1(i), dev_priv->regfile.saveSWF1[i]);
index 5ea3af393ffe8a33b03c9199371aadea1b50305c..b2202d2e58a26341ab57cdc0fc3c86247d653ac4 100644 (file)
@@ -163,7 +163,8 @@ int i915_timeline_init(struct drm_i915_private *i915,
 
        spin_lock_init(&timeline->lock);
 
-       init_request_active(&timeline->last_request, NULL);
+       INIT_ACTIVE_REQUEST(&timeline->barrier);
+       INIT_ACTIVE_REQUEST(&timeline->last_request);
        INIT_LIST_HEAD(&timeline->requests);
 
        i915_syncmap_init(&timeline->sync);
@@ -235,6 +236,7 @@ void i915_timeline_fini(struct i915_timeline *timeline)
 {
        GEM_BUG_ON(timeline->pin_count);
        GEM_BUG_ON(!list_empty(&timeline->requests));
+       GEM_BUG_ON(i915_active_request_isset(&timeline->barrier));
 
        i915_syncmap_free(&timeline->sync);
        hwsp_free(timeline);
index 8caeb66d1cd554e94a10da9d2ba3cc0c6bd529ad..7bec7d2e45bfa242d30d8a76f38b99ae44226903 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/list.h>
 #include <linux/kref.h>
 
+#include "i915_active.h"
 #include "i915_request.h"
 #include "i915_syncmap.h"
 #include "i915_utils.h"
@@ -58,10 +59,10 @@ struct i915_timeline {
 
        /* Contains an RCU guarded pointer to the last request. No reference is
         * held to the request, users must carefully acquire a reference to
-        * the request using i915_gem_active_get_request_rcu(), or hold the
+        * the request using i915_active_request_get_request_rcu(), or hold the
         * struct_mutex.
         */
-       struct i915_gem_active last_request;
+       struct i915_active_request last_request;
 
        /**
         * We track the most recent seqno that we wait on in every context so
@@ -74,6 +75,16 @@ struct i915_timeline {
         */
        struct i915_syncmap *sync;
 
+       /**
+        * Barrier provides the ability to serialize ordering between different
+        * timelines.
+        *
+        * Users can call i915_timeline_set_barrier which will make all
+        * subsequent submissions to this timeline be executed only after the
+        * barrier has been completed.
+        */
+       struct i915_active_request barrier;
+
        struct list_head link;
        const char *name;
        struct drm_i915_private *i915;
@@ -155,4 +166,19 @@ void i915_timelines_init(struct drm_i915_private *i915);
 void i915_timelines_park(struct drm_i915_private *i915);
 void i915_timelines_fini(struct drm_i915_private *i915);
 
+/**
+ * i915_timeline_set_barrier - orders submission between different timelines
+ * @timeline: timeline to set the barrier on
+ * @rq: request after which new submissions can proceed
+ *
+ * Sets the passed in request as the serialization point for all subsequent
+ * submissions on @timeline. Subsequent requests will not be submitted to GPU
+ * until the barrier has been completed.
+ */
+static inline int
+i915_timeline_set_barrier(struct i915_timeline *tl, struct i915_request *rq)
+{
+       return i915_active_request_set(&tl->barrier, rq);
+}
+
 #endif
index d83b8ad5f85976e7af6eb7029af402cff3a2f11d..b713bed20c3880c088a45c49e8a787c786f6e971 100644 (file)
@@ -63,22 +63,23 @@ static void vma_print_allocator(struct i915_vma *vma, const char *reason)
 
 #endif
 
-struct i915_vma_active {
-       struct i915_gem_active base;
-       struct i915_vma *vma;
-       struct rb_node node;
-       u64 timeline;
-};
+static void obj_bump_mru(struct drm_i915_gem_object *obj)
+{
+       struct drm_i915_private *i915 = to_i915(obj->base.dev);
 
-static void
-__i915_vma_retire(struct i915_vma *vma, struct i915_request *rq)
+       spin_lock(&i915->mm.obj_lock);
+       if (obj->bind_count)
+               list_move_tail(&obj->mm.link, &i915->mm.bound_list);
+       spin_unlock(&i915->mm.obj_lock);
+
+       obj->mm.dirty = true; /* be paranoid  */
+}
+
+static void __i915_vma_retire(struct i915_active *ref)
 {
+       struct i915_vma *vma = container_of(ref, typeof(*vma), active);
        struct drm_i915_gem_object *obj = vma->obj;
 
-       GEM_BUG_ON(!i915_vma_is_active(vma));
-       if (--vma->active_count)
-               return;
-
        GEM_BUG_ON(!i915_gem_object_is_active(obj));
        if (--obj->active_count)
                return;
@@ -90,16 +91,12 @@ __i915_vma_retire(struct i915_vma *vma, struct i915_request *rq)
                reservation_object_unlock(obj->resv);
        }
 
-       /* Bump our place on the bound list to keep it roughly in LRU order
+       /*
+        * Bump our place on the bound list to keep it roughly in LRU order
         * so that we don't steal from recently used but inactive objects
         * (unless we are forced to ofc!)
         */
-       spin_lock(&rq->i915->mm.obj_lock);
-       if (obj->bind_count)
-               list_move_tail(&obj->mm.link, &rq->i915->mm.bound_list);
-       spin_unlock(&rq->i915->mm.obj_lock);
-
-       obj->mm.dirty = true; /* be paranoid  */
+       obj_bump_mru(obj);
 
        if (i915_gem_object_has_active_reference(obj)) {
                i915_gem_object_clear_active_reference(obj);
@@ -107,21 +104,6 @@ __i915_vma_retire(struct i915_vma *vma, struct i915_request *rq)
        }
 }
 
-static void
-i915_vma_retire(struct i915_gem_active *base, struct i915_request *rq)
-{
-       struct i915_vma_active *active =
-               container_of(base, typeof(*active), base);
-
-       __i915_vma_retire(active->vma, rq);
-}
-
-static void
-i915_vma_last_retire(struct i915_gem_active *base, struct i915_request *rq)
-{
-       __i915_vma_retire(container_of(base, struct i915_vma, last_active), rq);
-}
-
 static struct i915_vma *
 vma_create(struct drm_i915_gem_object *obj,
           struct i915_address_space *vm,
@@ -137,10 +119,9 @@ vma_create(struct drm_i915_gem_object *obj,
        if (vma == NULL)
                return ERR_PTR(-ENOMEM);
 
-       vma->active = RB_ROOT;
+       i915_active_init(vm->i915, &vma->active, __i915_vma_retire);
+       INIT_ACTIVE_REQUEST(&vma->last_fence);
 
-       init_request_active(&vma->last_active, i915_vma_last_retire);
-       init_request_active(&vma->last_fence, NULL);
        vma->vm = vm;
        vma->ops = &vm->vma_ops;
        vma->obj = obj;
@@ -823,12 +804,11 @@ void i915_vma_reopen(struct i915_vma *vma)
 static void __i915_vma_destroy(struct i915_vma *vma)
 {
        struct drm_i915_private *i915 = vma->vm->i915;
-       struct i915_vma_active *iter, *n;
 
        GEM_BUG_ON(vma->node.allocated);
        GEM_BUG_ON(vma->fence);
 
-       GEM_BUG_ON(i915_gem_active_isset(&vma->last_fence));
+       GEM_BUG_ON(i915_active_request_isset(&vma->last_fence));
 
        mutex_lock(&vma->vm->mutex);
        list_del(&vma->vm_link);
@@ -843,10 +823,7 @@ static void __i915_vma_destroy(struct i915_vma *vma)
                spin_unlock(&obj->vma.lock);
        }
 
-       rbtree_postorder_for_each_entry_safe(iter, n, &vma->active, node) {
-               GEM_BUG_ON(i915_gem_active_isset(&iter->base));
-               kfree(iter);
-       }
+       i915_active_fini(&vma->active);
 
        kmem_cache_free(i915->vmas, vma);
 }
@@ -931,104 +908,15 @@ static void export_fence(struct i915_vma *vma,
        reservation_object_unlock(resv);
 }
 
-static struct i915_gem_active *active_instance(struct i915_vma *vma, u64 idx)
-{
-       struct i915_vma_active *active;
-       struct rb_node **p, *parent;
-       struct i915_request *old;
-
-       /*
-        * We track the most recently used timeline to skip a rbtree search
-        * for the common case, under typical loads we never need the rbtree
-        * at all. We can reuse the last_active slot if it is empty, that is
-        * after the previous activity has been retired, or if the active
-        * matches the current timeline.
-        *
-        * Note that we allow the timeline to be active simultaneously in
-        * the rbtree and the last_active cache. We do this to avoid having
-        * to search and replace the rbtree element for a new timeline, with
-        * the cost being that we must be aware that the vma may be retired
-        * twice for the same timeline (as the older rbtree element will be
-        * retired before the new request added to last_active).
-        */
-       old = i915_gem_active_raw(&vma->last_active,
-                                 &vma->vm->i915->drm.struct_mutex);
-       if (!old || old->fence.context == idx)
-               goto out;
-
-       /* Move the currently active fence into the rbtree */
-       idx = old->fence.context;
-
-       parent = NULL;
-       p = &vma->active.rb_node;
-       while (*p) {
-               parent = *p;
-
-               active = rb_entry(parent, struct i915_vma_active, node);
-               if (active->timeline == idx)
-                       goto replace;
-
-               if (active->timeline < idx)
-                       p = &parent->rb_right;
-               else
-                       p = &parent->rb_left;
-       }
-
-       active = kmalloc(sizeof(*active), GFP_KERNEL);
-
-       /* kmalloc may retire the vma->last_active request (thanks shrinker)! */
-       if (unlikely(!i915_gem_active_raw(&vma->last_active,
-                                         &vma->vm->i915->drm.struct_mutex))) {
-               kfree(active);
-               goto out;
-       }
-
-       if (unlikely(!active))
-               return ERR_PTR(-ENOMEM);
-
-       init_request_active(&active->base, i915_vma_retire);
-       active->vma = vma;
-       active->timeline = idx;
-
-       rb_link_node(&active->node, parent, p);
-       rb_insert_color(&active->node, &vma->active);
-
-replace:
-       /*
-        * Overwrite the previous active slot in the rbtree with last_active,
-        * leaving last_active zeroed. If the previous slot is still active,
-        * we must be careful as we now only expect to receive one retire
-        * callback not two, and so much undo the active counting for the
-        * overwritten slot.
-        */
-       if (i915_gem_active_isset(&active->base)) {
-               /* Retire ourselves from the old rq->active_list */
-               __list_del_entry(&active->base.link);
-               vma->active_count--;
-               GEM_BUG_ON(!vma->active_count);
-       }
-       GEM_BUG_ON(list_empty(&vma->last_active.link));
-       list_replace_init(&vma->last_active.link, &active->base.link);
-       active->base.request = fetch_and_zero(&vma->last_active.request);
-
-out:
-       return &vma->last_active;
-}
-
 int i915_vma_move_to_active(struct i915_vma *vma,
                            struct i915_request *rq,
                            unsigned int flags)
 {
        struct drm_i915_gem_object *obj = vma->obj;
-       struct i915_gem_active *active;
 
        lockdep_assert_held(&rq->i915->drm.struct_mutex);
        GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 
-       active = active_instance(vma, rq->fence.context);
-       if (IS_ERR(active))
-               return PTR_ERR(active);
-
        /*
         * Add a reference if we're newly entering the active list.
         * The order in which we add operations to the retirement queue is
@@ -1037,9 +925,15 @@ int i915_vma_move_to_active(struct i915_vma *vma,
         * add the active reference first and queue for it to be dropped
         * *last*.
         */
-       if (!i915_gem_active_isset(active) && !vma->active_count++)
+       if (!vma->active.count)
                obj->active_count++;
-       i915_gem_active_set(active, rq);
+
+       if (unlikely(i915_active_ref(&vma->active, rq->fence.context, rq))) {
+               if (!vma->active.count)
+                       obj->active_count--;
+               return -ENOMEM;
+       }
+
        GEM_BUG_ON(!i915_vma_is_active(vma));
        GEM_BUG_ON(!obj->active_count);
 
@@ -1048,14 +942,14 @@ int i915_vma_move_to_active(struct i915_vma *vma,
                obj->write_domain = I915_GEM_DOMAIN_RENDER;
 
                if (intel_fb_obj_invalidate(obj, ORIGIN_CS))
-                       i915_gem_active_set(&obj->frontbuffer_write, rq);
+                       __i915_active_request_set(&obj->frontbuffer_write, rq);
 
                obj->read_domains = 0;
        }
        obj->read_domains |= I915_GEM_GPU_DOMAINS;
 
        if (flags & EXEC_OBJECT_NEEDS_FENCE)
-               i915_gem_active_set(&vma->last_fence, rq);
+               __i915_active_request_set(&vma->last_fence, rq);
 
        export_fence(vma, rq, flags);
        return 0;
@@ -1073,8 +967,6 @@ int i915_vma_unbind(struct i915_vma *vma)
         */
        might_sleep();
        if (i915_vma_is_active(vma)) {
-               struct i915_vma_active *active, *n;
-
                /*
                 * When a closed VMA is retired, it is unbound - eek.
                 * In order to prevent it from being recursively closed,
@@ -1090,21 +982,12 @@ int i915_vma_unbind(struct i915_vma *vma)
                 */
                __i915_vma_pin(vma);
 
-               ret = i915_gem_active_retire(&vma->last_active,
-                                            &vma->vm->i915->drm.struct_mutex);
+               ret = i915_active_wait(&vma->active);
                if (ret)
                        goto unpin;
 
-               rbtree_postorder_for_each_entry_safe(active, n,
-                                                    &vma->active, node) {
-                       ret = i915_gem_active_retire(&active->base,
-                                                    &vma->vm->i915->drm.struct_mutex);
-                       if (ret)
-                               goto unpin;
-               }
-
-               ret = i915_gem_active_retire(&vma->last_fence,
-                                            &vma->vm->i915->drm.struct_mutex);
+               ret = i915_active_request_retire(&vma->last_fence,
+                                             &vma->vm->i915->drm.struct_mutex);
 unpin:
                __i915_vma_unpin(vma);
                if (ret)
index 5793abe509a2a0397299bf3f186eb8f4955d6963..7c742027f8661479d9079c71909bd9875d7ede3c 100644 (file)
@@ -34,6 +34,7 @@
 #include "i915_gem_fence_reg.h"
 #include "i915_gem_object.h"
 
+#include "i915_active.h"
 #include "i915_request.h"
 
 enum i915_cache_level;
@@ -108,10 +109,8 @@ struct i915_vma {
 #define I915_VMA_USERFAULT     BIT(I915_VMA_USERFAULT_BIT)
 #define I915_VMA_GGTT_WRITE    BIT(15)
 
-       unsigned int active_count;
-       struct rb_root active;
-       struct i915_gem_active last_active;
-       struct i915_gem_active last_fence;
+       struct i915_active active;
+       struct i915_active_request last_fence;
 
        /**
         * Support different GGTT views into the same object.
@@ -154,9 +153,9 @@ i915_vma_instance(struct drm_i915_gem_object *obj,
 void i915_vma_unpin_and_release(struct i915_vma **p_vma, unsigned int flags);
 #define I915_VMA_RELEASE_MAP BIT(0)
 
-static inline bool i915_vma_is_active(struct i915_vma *vma)
+static inline bool i915_vma_is_active(const struct i915_vma *vma)
 {
-       return vma->active_count;
+       return !i915_active_is_idle(&vma->active);
 }
 
 int __must_check i915_vma_move_to_active(struct i915_vma *vma,
index 4b0044cdcf1a54932bdcf39bd4c59369ef77ef6a..71a1f12c6b2a5016a5846bacfb6d89b4960e4371 100644 (file)
 #define ILK_CSC_COEFF_1_0              \
        ((7 << 12) | ILK_CSC_COEFF_FP(CTM_COEFF_1_0, 8))
 
-static bool lut_is_legacy(struct drm_property_blob *lut)
+static bool lut_is_legacy(const struct drm_property_blob *lut)
 {
        return drm_color_lut_size(lut) == LEGACY_LUT_LENGTH;
 }
 
-static bool crtc_state_is_legacy_gamma(struct intel_crtc_state *crtc_state)
+static bool crtc_state_is_legacy_gamma(const struct intel_crtc_state *crtc_state)
 {
        return !crtc_state->base.degamma_lut &&
                !crtc_state->base.ctm &&
@@ -115,8 +115,8 @@ static u64 *ctm_mult_by_limited(u64 *result, const u64 *input)
 
 static void ilk_load_ycbcr_conversion_matrix(struct intel_crtc *crtc)
 {
-       int pipe = crtc->pipe;
        struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       enum pipe pipe = crtc->pipe;
 
        I915_WRITE(PIPE_CSC_PREOFF_HI(pipe), 0);
        I915_WRITE(PIPE_CSC_PREOFF_ME(pipe), 0);
@@ -137,13 +137,14 @@ static void ilk_load_ycbcr_conversion_matrix(struct intel_crtc *crtc)
        I915_WRITE(PIPE_CSC_MODE(pipe), 0);
 }
 
-static void ilk_load_csc_matrix(struct intel_crtc_state *crtc_state)
+static void ilk_load_csc_matrix(const struct intel_crtc_state *crtc_state)
 {
        struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
        struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
-       int i, pipe = crtc->pipe;
-       u16 coeffs[9] = { 0, };
        bool limited_color_range = false;
+       enum pipe pipe = crtc->pipe;
+       u16 coeffs[9] = {};
+       int i;
 
        /*
         * FIXME if there's a gamma LUT after the CSC, we should
@@ -256,16 +257,16 @@ static void ilk_load_csc_matrix(struct intel_crtc_state *crtc_state)
 /*
  * Set up the pipe CSC unit on CherryView.
  */
-static void cherryview_load_csc_matrix(struct intel_crtc_state *crtc_state)
+static void cherryview_load_csc_matrix(const struct intel_crtc_state *crtc_state)
 {
-       struct drm_device *dev = crtc_state->base.crtc->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       int pipe = to_intel_crtc(crtc_state->base.crtc)->pipe;
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       enum pipe pipe = crtc->pipe;
        u32 mode;
 
        if (crtc_state->base.ctm) {
-               struct drm_color_ctm *ctm = crtc_state->base.ctm->data;
-               u16 coeffs[9] = { 0, };
+               const struct drm_color_ctm *ctm = crtc_state->base.ctm->data;
+               u16 coeffs[9] = {};
                int i;
 
                for (i = 0; i < ARRAY_SIZE(coeffs); i++) {
@@ -303,25 +304,16 @@ static void cherryview_load_csc_matrix(struct intel_crtc_state *crtc_state)
        I915_WRITE(CGM_PIPE_MODE(pipe), mode);
 }
 
-void intel_color_set_csc(struct intel_crtc_state *crtc_state)
-{
-       struct drm_device *dev = crtc_state->base.crtc->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-
-       if (dev_priv->display.load_csc_matrix)
-               dev_priv->display.load_csc_matrix(crtc_state);
-}
-
 /* Loads the legacy palette/gamma unit for the CRTC. */
-static void i9xx_load_luts_internal(struct intel_crtc_state *crtc_state,
-                                   struct drm_property_blob *blob)
+static void i9xx_load_luts_internal(const struct intel_crtc_state *crtc_state,
+                                   const struct drm_property_blob *blob)
 {
        struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
        struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
        enum pipe pipe = crtc->pipe;
        int i;
 
-       if (HAS_GMCH_DISPLAY(dev_priv)) {
+       if (HAS_GMCH(dev_priv)) {
                if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI))
                        assert_dsi_pll_enabled(dev_priv);
                else
@@ -329,14 +321,15 @@ static void i9xx_load_luts_internal(struct intel_crtc_state *crtc_state,
        }
 
        if (blob) {
-               struct drm_color_lut *lut = blob->data;
+               const struct drm_color_lut *lut = blob->data;
+
                for (i = 0; i < 256; i++) {
                        u32 word =
                                (drm_color_lut_extract(lut[i].red, 8) << 16) |
                                (drm_color_lut_extract(lut[i].green, 8) << 8) |
                                drm_color_lut_extract(lut[i].blue, 8);
 
-                       if (HAS_GMCH_DISPLAY(dev_priv))
+                       if (HAS_GMCH(dev_priv))
                                I915_WRITE(PALETTE(pipe, i), word);
                        else
                                I915_WRITE(LGC_PALETTE(pipe, i), word);
@@ -345,7 +338,7 @@ static void i9xx_load_luts_internal(struct intel_crtc_state *crtc_state,
                for (i = 0; i < 256; i++) {
                        u32 word = (i << 16) | (i << 8) | i;
 
-                       if (HAS_GMCH_DISPLAY(dev_priv))
+                       if (HAS_GMCH(dev_priv))
                                I915_WRITE(PALETTE(pipe, i), word);
                        else
                                I915_WRITE(LGC_PALETTE(pipe, i), word);
@@ -353,48 +346,34 @@ static void i9xx_load_luts_internal(struct intel_crtc_state *crtc_state,
        }
 }
 
-static void i9xx_load_luts(struct intel_crtc_state *crtc_state)
+static void i9xx_load_luts(const struct intel_crtc_state *crtc_state)
 {
        i9xx_load_luts_internal(crtc_state, crtc_state->base.gamma_lut);
 }
 
-/* Loads the legacy palette/gamma unit for the CRTC on Haswell. */
-static void haswell_load_luts(struct intel_crtc_state *crtc_state)
+static void hsw_color_commit(const struct intel_crtc_state *crtc_state)
 {
        struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
        struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
-       bool reenable_ips = false;
 
-       /*
-        * Workaround : Do not read or write the pipe palette/gamma data while
-        * GAMMA_MODE is configured for split gamma and IPS_CTL has IPS enabled.
-        */
-       if (IS_HASWELL(dev_priv) && crtc_state->ips_enabled &&
-           (crtc_state->gamma_mode == GAMMA_MODE_MODE_SPLIT)) {
-               hsw_disable_ips(crtc_state);
-               reenable_ips = true;
-       }
-
-       crtc_state->gamma_mode = GAMMA_MODE_MODE_8BIT;
-       I915_WRITE(GAMMA_MODE(crtc->pipe), GAMMA_MODE_MODE_8BIT);
+       I915_WRITE(GAMMA_MODE(crtc->pipe), crtc_state->gamma_mode);
 
-       i9xx_load_luts(crtc_state);
-
-       if (reenable_ips)
-               hsw_enable_ips(crtc_state);
+       ilk_load_csc_matrix(crtc_state);
 }
 
-static void bdw_load_degamma_lut(struct intel_crtc_state *crtc_state)
+static void bdw_load_degamma_lut(const struct intel_crtc_state *crtc_state)
 {
-       struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
-       enum pipe pipe = to_intel_crtc(crtc_state->base.crtc)->pipe;
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       const struct drm_property_blob *degamma_lut = crtc_state->base.degamma_lut;
        u32 i, lut_size = INTEL_INFO(dev_priv)->color.degamma_lut_size;
+       enum pipe pipe = crtc->pipe;
 
        I915_WRITE(PREC_PAL_INDEX(pipe),
                   PAL_PREC_SPLIT_MODE | PAL_PREC_AUTO_INCREMENT);
 
-       if (crtc_state->base.degamma_lut) {
-               struct drm_color_lut *lut = crtc_state->base.degamma_lut->data;
+       if (degamma_lut) {
+               const struct drm_color_lut *lut = degamma_lut->data;
 
                for (i = 0; i < lut_size; i++) {
                        u32 word =
@@ -414,11 +393,13 @@ static void bdw_load_degamma_lut(struct intel_crtc_state *crtc_state)
        }
 }
 
-static void bdw_load_gamma_lut(struct intel_crtc_state *crtc_state, u32 offset)
+static void bdw_load_gamma_lut(const struct intel_crtc_state *crtc_state, u32 offset)
 {
-       struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
-       enum pipe pipe = to_intel_crtc(crtc_state->base.crtc)->pipe;
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       const struct drm_property_blob *gamma_lut = crtc_state->base.gamma_lut;
        u32 i, lut_size = INTEL_INFO(dev_priv)->color.gamma_lut_size;
+       enum pipe pipe = crtc->pipe;
 
        WARN_ON(offset & ~PAL_PREC_INDEX_VALUE_MASK);
 
@@ -427,8 +408,8 @@ static void bdw_load_gamma_lut(struct intel_crtc_state *crtc_state, u32 offset)
                   PAL_PREC_AUTO_INCREMENT |
                   offset);
 
-       if (crtc_state->base.gamma_lut) {
-               struct drm_color_lut *lut = crtc_state->base.gamma_lut->data;
+       if (gamma_lut) {
+               const struct drm_color_lut *lut = gamma_lut->data;
 
                for (i = 0; i < lut_size; i++) {
                        u32 word =
@@ -462,35 +443,32 @@ static void bdw_load_gamma_lut(struct intel_crtc_state *crtc_state, u32 offset)
 }
 
 /* Loads the palette/gamma unit for the CRTC on Broadwell+. */
-static void broadwell_load_luts(struct intel_crtc_state *crtc_state)
+static void broadwell_load_luts(const struct intel_crtc_state *crtc_state)
 {
-       struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
-       enum pipe pipe = to_intel_crtc(crtc_state->base.crtc)->pipe;
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       enum pipe pipe = crtc->pipe;
 
        if (crtc_state_is_legacy_gamma(crtc_state)) {
-               haswell_load_luts(crtc_state);
-               return;
-       }
-
-       bdw_load_degamma_lut(crtc_state);
-       bdw_load_gamma_lut(crtc_state,
-                          INTEL_INFO(dev_priv)->color.degamma_lut_size);
-
-       crtc_state->gamma_mode = GAMMA_MODE_MODE_SPLIT;
-       I915_WRITE(GAMMA_MODE(pipe), GAMMA_MODE_MODE_SPLIT);
-       POSTING_READ(GAMMA_MODE(pipe));
+               i9xx_load_luts(crtc_state);
+       } else {
+               bdw_load_degamma_lut(crtc_state);
+               bdw_load_gamma_lut(crtc_state,
+                                  INTEL_INFO(dev_priv)->color.degamma_lut_size);
 
-       /*
-        * Reset the index, otherwise it prevents the legacy palette to be
-        * written properly.
-        */
-       I915_WRITE(PREC_PAL_INDEX(pipe), 0);
+               /*
+                * Reset the index, otherwise it prevents the legacy palette to be
+                * written properly.
+                */
+               I915_WRITE(PREC_PAL_INDEX(pipe), 0);
+       }
 }
 
-static void glk_load_degamma_lut(struct intel_crtc_state *crtc_state)
+static void glk_load_degamma_lut(const struct intel_crtc_state *crtc_state)
 {
-       struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
-       enum pipe pipe = to_intel_crtc(crtc_state->base.crtc)->pipe;
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       enum pipe pipe = crtc->pipe;
        const u32 lut_size = 33;
        u32 i;
 
@@ -517,48 +495,49 @@ static void glk_load_degamma_lut(struct intel_crtc_state *crtc_state)
                I915_WRITE(PRE_CSC_GAMC_DATA(pipe), (1 << 16));
 }
 
-static void glk_load_luts(struct intel_crtc_state *crtc_state)
+static void glk_load_luts(const struct intel_crtc_state *crtc_state)
 {
-       struct drm_device *dev = crtc_state->base.crtc->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       enum pipe pipe = to_intel_crtc(crtc_state->base.crtc)->pipe;
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       enum pipe pipe = crtc->pipe;
 
        glk_load_degamma_lut(crtc_state);
 
        if (crtc_state_is_legacy_gamma(crtc_state)) {
-               haswell_load_luts(crtc_state);
-               return;
-       }
-
-       bdw_load_gamma_lut(crtc_state, 0);
+               i9xx_load_luts(crtc_state);
+       } else {
+               bdw_load_gamma_lut(crtc_state, 0);
 
-       crtc_state->gamma_mode = GAMMA_MODE_MODE_10BIT;
-       I915_WRITE(GAMMA_MODE(pipe), GAMMA_MODE_MODE_10BIT);
-       POSTING_READ(GAMMA_MODE(pipe));
+               /*
+                * Reset the index, otherwise it prevents the legacy palette to be
+                * written properly.
+                */
+               I915_WRITE(PREC_PAL_INDEX(pipe), 0);
+       }
 }
 
-/* Loads the palette/gamma unit for the CRTC on CherryView. */
-static void cherryview_load_luts(struct intel_crtc_state *crtc_state)
+static void cherryview_load_luts(const struct intel_crtc_state *crtc_state)
 {
-       struct drm_crtc *crtc = crtc_state->base.crtc;
-       struct drm_i915_private *dev_priv = to_i915(crtc->dev);
-       enum pipe pipe = to_intel_crtc(crtc)->pipe;
-       struct drm_color_lut *lut;
-       u32 i, lut_size;
-       u32 word0, word1;
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       const struct drm_property_blob *gamma_lut = crtc_state->base.gamma_lut;
+       const struct drm_property_blob *degamma_lut = crtc_state->base.degamma_lut;
+       enum pipe pipe = crtc->pipe;
+
+       cherryview_load_csc_matrix(crtc_state);
 
        if (crtc_state_is_legacy_gamma(crtc_state)) {
-               /* Turn off degamma/gamma on CGM block. */
-               I915_WRITE(CGM_PIPE_MODE(pipe),
-                          (crtc_state->base.ctm ? CGM_PIPE_MODE_CSC : 0));
-               i9xx_load_luts_internal(crtc_state, crtc_state->base.gamma_lut);
+               i9xx_load_luts_internal(crtc_state, gamma_lut);
                return;
        }
 
-       if (crtc_state->base.degamma_lut) {
-               lut = crtc_state->base.degamma_lut->data;
-               lut_size = INTEL_INFO(dev_priv)->color.degamma_lut_size;
+       if (degamma_lut) {
+               const struct drm_color_lut *lut = degamma_lut->data;
+               int i, lut_size = INTEL_INFO(dev_priv)->color.degamma_lut_size;
+
                for (i = 0; i < lut_size; i++) {
+                       u32 word0, word1;
+
                        /* Write LUT in U0.14 format. */
                        word0 =
                        (drm_color_lut_extract(lut[i].green, 14) << 16) |
@@ -570,10 +549,13 @@ static void cherryview_load_luts(struct intel_crtc_state *crtc_state)
                }
        }
 
-       if (crtc_state->base.gamma_lut) {
-               lut = crtc_state->base.gamma_lut->data;
-               lut_size = INTEL_INFO(dev_priv)->color.gamma_lut_size;
+       if (gamma_lut) {
+               const struct drm_color_lut *lut = gamma_lut->data;
+               int i, lut_size = INTEL_INFO(dev_priv)->color.gamma_lut_size;
+
                for (i = 0; i < lut_size; i++) {
+                       u32 word0, word1;
+
                        /* Write LUT in U0.10 format. */
                        word0 =
                        (drm_color_lut_extract(lut[i].green, 10) << 16) |
@@ -585,11 +567,6 @@ static void cherryview_load_luts(struct intel_crtc_state *crtc_state)
                }
        }
 
-       I915_WRITE(CGM_PIPE_MODE(pipe),
-                  (crtc_state->base.ctm ? CGM_PIPE_MODE_CSC : 0) |
-                  (crtc_state->base.degamma_lut ? CGM_PIPE_MODE_DEGAMMA : 0) |
-                  (crtc_state->base.gamma_lut ? CGM_PIPE_MODE_GAMMA : 0));
-
        /*
         * Also program a linear LUT in the legacy block (behind the
         * CGM block).
@@ -597,14 +574,21 @@ static void cherryview_load_luts(struct intel_crtc_state *crtc_state)
        i9xx_load_luts_internal(crtc_state, NULL);
 }
 
-void intel_color_load_luts(struct intel_crtc_state *crtc_state)
+void intel_color_load_luts(const struct intel_crtc_state *crtc_state)
 {
-       struct drm_device *dev = crtc_state->base.crtc->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
 
        dev_priv->display.load_luts(crtc_state);
 }
 
+void intel_color_commit(const struct intel_crtc_state *crtc_state)
+{
+       struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
+
+       if (dev_priv->display.color_commit)
+               dev_priv->display.color_commit(crtc_state);
+}
+
 static int check_lut_size(const struct drm_property_blob *lut, int expected)
 {
        int len;
@@ -625,6 +609,8 @@ static int check_lut_size(const struct drm_property_blob *lut, int expected)
 int intel_color_check(struct intel_crtc_state *crtc_state)
 {
        struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
+       const struct drm_property_blob *gamma_lut = crtc_state->base.gamma_lut;
+       const struct drm_property_blob *degamma_lut = crtc_state->base.degamma_lut;
        int gamma_length, degamma_length;
        u32 gamma_tests, degamma_tests;
 
@@ -634,17 +620,25 @@ int intel_color_check(struct intel_crtc_state *crtc_state)
        gamma_tests = INTEL_INFO(dev_priv)->color.gamma_lut_tests;
 
        /* Always allow legacy gamma LUT with no further checking. */
-       if (crtc_state_is_legacy_gamma(crtc_state))
+       if (crtc_state_is_legacy_gamma(crtc_state)) {
+               crtc_state->gamma_mode = GAMMA_MODE_MODE_8BIT;
                return 0;
+       }
 
-       if (check_lut_size(crtc_state->base.degamma_lut, degamma_length) ||
-           check_lut_size(crtc_state->base.gamma_lut, gamma_length))
+       if (check_lut_size(degamma_lut, degamma_length) ||
+           check_lut_size(gamma_lut, gamma_length))
                return -EINVAL;
 
-       if (drm_color_lut_check(crtc_state->base.degamma_lut, degamma_tests) ||
-           drm_color_lut_check(crtc_state->base.gamma_lut, gamma_tests))
+       if (drm_color_lut_check(degamma_lut, degamma_tests) ||
+           drm_color_lut_check(gamma_lut, gamma_tests))
                return -EINVAL;
 
+       if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
+               crtc_state->gamma_mode = GAMMA_MODE_MODE_10BIT;
+       else if (INTEL_GEN(dev_priv) >= 9 || IS_BROADWELL(dev_priv))
+               crtc_state->gamma_mode = GAMMA_MODE_MODE_SPLIT;
+       else
+               crtc_state->gamma_mode = GAMMA_MODE_MODE_8BIT;
 
        return 0;
 }
@@ -656,18 +650,17 @@ void intel_color_init(struct intel_crtc *crtc)
        drm_mode_crtc_set_gamma_size(&crtc->base, 256);
 
        if (IS_CHERRYVIEW(dev_priv)) {
-               dev_priv->display.load_csc_matrix = cherryview_load_csc_matrix;
                dev_priv->display.load_luts = cherryview_load_luts;
        } else if (IS_HASWELL(dev_priv)) {
-               dev_priv->display.load_csc_matrix = ilk_load_csc_matrix;
-               dev_priv->display.load_luts = haswell_load_luts;
+               dev_priv->display.load_luts = i9xx_load_luts;
+               dev_priv->display.color_commit = hsw_color_commit;
        } else if (IS_BROADWELL(dev_priv) || IS_GEN9_BC(dev_priv) ||
                   IS_BROXTON(dev_priv)) {
-               dev_priv->display.load_csc_matrix = ilk_load_csc_matrix;
                dev_priv->display.load_luts = broadwell_load_luts;
+               dev_priv->display.color_commit = hsw_color_commit;
        } else if (IS_GEMINILAKE(dev_priv) || IS_CANNONLAKE(dev_priv)) {
-               dev_priv->display.load_csc_matrix = ilk_load_csc_matrix;
                dev_priv->display.load_luts = glk_load_luts;
+               dev_priv->display.color_commit = hsw_color_commit;
        } else {
                dev_priv->display.load_luts = i9xx_load_luts;
        }
index 7bf09cef591a453428422675c9d64c46fa7369ba..e8b8661df746c3317926275310632c0bf05a98b9 100644 (file)
@@ -115,7 +115,7 @@ enum intel_ppgtt {
        func(has_ddi); \
        func(has_dp_mst); \
        func(has_fbc); \
-       func(has_gmch_display); \
+       func(has_gmch); \
        func(has_hotplug); \
        func(has_ipc); \
        func(has_overlay); \
index df7a7a310f2f1bbd5b29616c04163f96f60f71f4..619f1a20cc2db9d320c7b2aa819453fb81ab20df 100644 (file)
@@ -1805,7 +1805,7 @@ static void intel_enable_pipe(const struct intel_crtc_state *new_crtc_state)
         * a plane.  On ILK+ the pipe PLLs are integrated, so we don't
         * need the check.
         */
-       if (HAS_GMCH_DISPLAY(dev_priv)) {
+       if (HAS_GMCH(dev_priv)) {
                if (intel_crtc_has_type(new_crtc_state, INTEL_OUTPUT_DSI))
                        assert_dsi_pll_enabled(dev_priv);
                else
@@ -2094,7 +2094,7 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
         * complicated than this. For example, Cherryview appears quite
         * happy to scanout from anywhere within its global aperture.
         */
-       if (HAS_GMCH_DISPLAY(dev_priv))
+       if (HAS_GMCH(dev_priv))
                pinctl |= PIN_MAPPABLE;
 
        vma = i915_gem_object_pin_to_display_plane(obj,
@@ -3195,7 +3195,7 @@ i9xx_plane_max_stride(struct intel_plane *plane,
 {
        struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
 
-       if (!HAS_GMCH_DISPLAY(dev_priv)) {
+       if (!HAS_GMCH(dev_priv)) {
                return 32*1024;
        } else if (INTEL_GEN(dev_priv) >= 4) {
                if (modifier == I915_FORMAT_MOD_X_TILED)
@@ -3215,28 +3215,38 @@ i9xx_plane_max_stride(struct intel_plane *plane,
        }
 }
 
+static u32 i9xx_plane_ctl_crtc(const struct intel_crtc_state *crtc_state)
+{
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       u32 dspcntr = 0;
+
+       dspcntr |= DISPPLANE_GAMMA_ENABLE;
+
+       if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
+               dspcntr |= DISPPLANE_PIPE_CSC_ENABLE;
+
+       if (INTEL_GEN(dev_priv) < 5)
+               dspcntr |= DISPPLANE_SEL_PIPE(crtc->pipe);
+
+       return dspcntr;
+}
+
 static u32 i9xx_plane_ctl(const struct intel_crtc_state *crtc_state,
                          const struct intel_plane_state *plane_state)
 {
        struct drm_i915_private *dev_priv =
                to_i915(plane_state->base.plane->dev);
-       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
        const struct drm_framebuffer *fb = plane_state->base.fb;
        unsigned int rotation = plane_state->base.rotation;
        u32 dspcntr;
 
-       dspcntr = DISPLAY_PLANE_ENABLE | DISPPLANE_GAMMA_ENABLE;
+       dspcntr = DISPLAY_PLANE_ENABLE;
 
        if (IS_G4X(dev_priv) || IS_GEN(dev_priv, 5) ||
            IS_GEN(dev_priv, 6) || IS_IVYBRIDGE(dev_priv))
                dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
 
-       if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
-               dspcntr |= DISPPLANE_PIPE_CSC_ENABLE;
-
-       if (INTEL_GEN(dev_priv) < 5)
-               dspcntr |= DISPPLANE_SEL_PIPE(crtc->pipe);
-
        switch (fb->format->format) {
        case DRM_FORMAT_C8:
                dspcntr |= DISPPLANE_8BPP;
@@ -3364,11 +3374,13 @@ static void i9xx_update_plane(struct intel_plane *plane,
        struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
        enum i9xx_plane_id i9xx_plane = plane->i9xx_plane;
        u32 linear_offset;
-       u32 dspcntr = plane_state->ctl;
        int x = plane_state->color_plane[0].x;
        int y = plane_state->color_plane[0].y;
        unsigned long irqflags;
        u32 dspaddr_offset;
+       u32 dspcntr;
+
+       dspcntr = plane_state->ctl | i9xx_plane_ctl_crtc(crtc_state);
 
        linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0);
 
@@ -3428,10 +3440,23 @@ static void i9xx_disable_plane(struct intel_plane *plane,
        struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
        enum i9xx_plane_id i9xx_plane = plane->i9xx_plane;
        unsigned long irqflags;
+       u32 dspcntr;
+
+       /*
+        * DSPCNTR pipe gamma enable on g4x+ and pipe csc
+        * enable on ilk+ affect the pipe bottom color as
+        * well, so we must configure them even if the plane
+        * is disabled.
+        *
+        * On pre-g4x there is no way to gamma correct the
+        * pipe bottom color but we'll keep on doing this
+        * anyway.
+        */
+       dspcntr = i9xx_plane_ctl_crtc(crtc_state);
 
        spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
 
-       I915_WRITE_FW(DSPCNTR(i9xx_plane), 0);
+       I915_WRITE_FW(DSPCNTR(i9xx_plane), dspcntr);
        if (INTEL_GEN(dev_priv) >= 4)
                I915_WRITE_FW(DSPSURF(i9xx_plane), 0);
        else
@@ -3668,6 +3693,20 @@ static u32 cnl_plane_ctl_flip(unsigned int reflect)
        return 0;
 }
 
+u32 skl_plane_ctl_crtc(const struct intel_crtc_state *crtc_state)
+{
+       struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
+       u32 plane_ctl = 0;
+
+       if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
+               return plane_ctl;
+
+       plane_ctl |= PLANE_CTL_PIPE_GAMMA_ENABLE;
+       plane_ctl |= PLANE_CTL_PIPE_CSC_ENABLE;
+
+       return plane_ctl;
+}
+
 u32 skl_plane_ctl(const struct intel_crtc_state *crtc_state,
                  const struct intel_plane_state *plane_state)
 {
@@ -3682,10 +3721,7 @@ u32 skl_plane_ctl(const struct intel_crtc_state *crtc_state,
 
        if (INTEL_GEN(dev_priv) < 10 && !IS_GEMINILAKE(dev_priv)) {
                plane_ctl |= skl_plane_ctl_alpha(plane_state);
-               plane_ctl |=
-                       PLANE_CTL_PIPE_GAMMA_ENABLE |
-                       PLANE_CTL_PIPE_CSC_ENABLE |
-                       PLANE_CTL_PLANE_GAMMA_DISABLE;
+               plane_ctl |= PLANE_CTL_PLANE_GAMMA_DISABLE;
 
                if (plane_state->base.color_encoding == DRM_COLOR_YCBCR_BT709)
                        plane_ctl |= PLANE_CTL_YUV_TO_RGB_CSC_FORMAT_BT709;
@@ -3710,19 +3746,27 @@ u32 skl_plane_ctl(const struct intel_crtc_state *crtc_state,
        return plane_ctl;
 }
 
+u32 glk_plane_color_ctl_crtc(const struct intel_crtc_state *crtc_state)
+{
+       struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
+       u32 plane_color_ctl = 0;
+
+       if (INTEL_GEN(dev_priv) >= 11)
+               return plane_color_ctl;
+
+       plane_color_ctl |= PLANE_COLOR_PIPE_GAMMA_ENABLE;
+       plane_color_ctl |= PLANE_COLOR_PIPE_CSC_ENABLE;
+
+       return plane_color_ctl;
+}
+
 u32 glk_plane_color_ctl(const struct intel_crtc_state *crtc_state,
                        const struct intel_plane_state *plane_state)
 {
-       struct drm_i915_private *dev_priv =
-               to_i915(plane_state->base.plane->dev);
        const struct drm_framebuffer *fb = plane_state->base.fb;
        struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
        u32 plane_color_ctl = 0;
 
-       if (INTEL_GEN(dev_priv) < 11) {
-               plane_color_ctl |= PLANE_COLOR_PIPE_GAMMA_ENABLE;
-               plane_color_ctl |= PLANE_COLOR_PIPE_CSC_ENABLE;
-       }
        plane_color_ctl |= PLANE_COLOR_PLANE_GAMMA_DISABLE;
        plane_color_ctl |= glk_plane_color_ctl_alpha(plane_state);
 
@@ -3771,7 +3815,7 @@ __intel_display_resume(struct drm_device *dev,
        }
 
        /* ignore any reset values/BIOS leftovers in the WM registers */
-       if (!HAS_GMCH_DISPLAY(to_i915(dev)))
+       if (!HAS_GMCH(to_i915(dev)))
                to_intel_atomic_state(state)->skip_intermediate_wm = true;
 
        ret = drm_atomic_helper_commit_duplicated_state(state, ctx);
@@ -3896,6 +3940,30 @@ unlock:
        clear_bit(I915_RESET_MODESET, &dev_priv->gpu_error.flags);
 }
 
+static void icl_set_pipe_chicken(struct intel_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       enum pipe pipe = crtc->pipe;
+       u32 tmp;
+
+       tmp = I915_READ(PIPE_CHICKEN(pipe));
+
+       /*
+        * Display WA #1153: icl
+        * enable hardware to bypass the alpha math
+        * and rounding for per-pixel values 00 and 0xff
+        */
+       tmp |= PER_PIXEL_ALPHA_BYPASS_EN;
+
+       /*
+        * W/A for underruns with linear/X-tiled with
+        * WM1+ disabled.
+        */
+       tmp |= PM_FILL_MAINTAIN_DBUF_FULLNESS;
+
+       I915_WRITE(PIPE_CHICKEN(pipe), tmp);
+}
+
 static void intel_update_pipe_config(const struct intel_crtc_state *old_crtc_state,
                                     const struct intel_crtc_state *new_crtc_state)
 {
@@ -3940,6 +4008,9 @@ static void intel_update_pipe_config(const struct intel_crtc_state *old_crtc_sta
                I915_WRITE(SKL_BOTTOM_COLOR(crtc->pipe),
                           SKL_BOTTOM_COLOR_GAMMA_ENABLE |
                           SKL_BOTTOM_COLOR_CSC_ENABLE);
+
+       if (INTEL_GEN(dev_priv) >= 11)
+               icl_set_pipe_chicken(crtc);
 }
 
 static void intel_fdi_normal_train(struct intel_crtc *crtc)
@@ -5294,7 +5365,7 @@ intel_pre_disable_primary_noatomic(struct drm_crtc *crtc)
         * event which is after the vblank start event, so we need to have a
         * wait-for-vblank between disabling the plane and the pipe.
         */
-       if (HAS_GMCH_DISPLAY(dev_priv) &&
+       if (HAS_GMCH(dev_priv) &&
            intel_set_memory_cxsr(dev_priv, false))
                intel_wait_for_vblank(dev_priv, pipe);
 }
@@ -5302,24 +5373,54 @@ intel_pre_disable_primary_noatomic(struct drm_crtc *crtc)
 static bool hsw_pre_update_disable_ips(const struct intel_crtc_state *old_crtc_state,
                                       const struct intel_crtc_state *new_crtc_state)
 {
+       struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+
        if (!old_crtc_state->ips_enabled)
                return false;
 
        if (needs_modeset(&new_crtc_state->base))
                return true;
 
+       /*
+        * Workaround : Do not read or write the pipe palette/gamma data while
+        * GAMMA_MODE is configured for split gamma and IPS_CTL has IPS enabled.
+        *
+        * Disable IPS before we program the LUT.
+        */
+       if (IS_HASWELL(dev_priv) &&
+           (new_crtc_state->base.color_mgmt_changed ||
+            new_crtc_state->update_pipe) &&
+           new_crtc_state->gamma_mode == GAMMA_MODE_MODE_SPLIT)
+               return true;
+
        return !new_crtc_state->ips_enabled;
 }
 
 static bool hsw_post_update_enable_ips(const struct intel_crtc_state *old_crtc_state,
                                       const struct intel_crtc_state *new_crtc_state)
 {
+       struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+
        if (!new_crtc_state->ips_enabled)
                return false;
 
        if (needs_modeset(&new_crtc_state->base))
                return true;
 
+       /*
+        * Workaround : Do not read or write the pipe palette/gamma data while
+        * GAMMA_MODE is configured for split gamma and IPS_CTL has IPS enabled.
+        *
+        * Re-enable IPS after the LUT has been programmed.
+        */
+       if (IS_HASWELL(dev_priv) &&
+           (new_crtc_state->base.color_mgmt_changed ||
+            new_crtc_state->update_pipe) &&
+           new_crtc_state->gamma_mode == GAMMA_MODE_MODE_SPLIT)
+               return true;
+
        /*
         * We can't read out IPS on broadwell, assume the worst and
         * forcibly enable IPS on the first fastset.
@@ -5431,7 +5532,7 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state,
         * event which is after the vblank start event, so we need to have a
         * wait-for-vblank between disabling the plane and the pipe.
         */
-       if (HAS_GMCH_DISPLAY(dev_priv) && old_crtc_state->base.active &&
+       if (HAS_GMCH(dev_priv) && old_crtc_state->base.active &&
            pipe_config->disable_cxsr && intel_set_memory_cxsr(dev_priv, false))
                intel_wait_for_vblank(dev_priv, crtc->pipe);
 
@@ -5708,6 +5809,7 @@ static void ironlake_crtc_enable(struct intel_crtc_state *pipe_config,
         * clocks enabled
         */
        intel_color_load_luts(pipe_config);
+       intel_color_commit(pipe_config);
 
        if (dev_priv->display.initial_watermarks != NULL)
                dev_priv->display.initial_watermarks(old_intel_state, pipe_config);
@@ -5782,7 +5884,6 @@ static void haswell_crtc_enable(struct intel_crtc_state *pipe_config,
        struct intel_atomic_state *old_intel_state =
                to_intel_atomic_state(old_state);
        bool psl_clkgate_wa;
-       u32 pipe_chicken;
 
        if (WARN_ON(intel_crtc->active))
                return;
@@ -5818,8 +5919,6 @@ static void haswell_crtc_enable(struct intel_crtc_state *pipe_config,
 
        haswell_set_pipemisc(pipe_config);
 
-       intel_color_set_csc(pipe_config);
-
        intel_crtc->active = true;
 
        /* Display WA #1180: WaDisableScalarClockGating: glk, cnl */
@@ -5838,17 +5937,10 @@ static void haswell_crtc_enable(struct intel_crtc_state *pipe_config,
         * clocks enabled
         */
        intel_color_load_luts(pipe_config);
+       intel_color_commit(pipe_config);
 
-       /*
-        * Display WA #1153: enable hardware to bypass the alpha math
-        * and rounding for per-pixel values 00 and 0xff
-        */
-       if (INTEL_GEN(dev_priv) >= 11) {
-               pipe_chicken = I915_READ(PIPE_CHICKEN(pipe));
-               if (!(pipe_chicken & PER_PIXEL_ALPHA_BYPASS_EN))
-                       I915_WRITE_FW(PIPE_CHICKEN(pipe),
-                                     pipe_chicken | PER_PIXEL_ALPHA_BYPASS_EN);
-       }
+       if (INTEL_GEN(dev_priv) >= 11)
+               icl_set_pipe_chicken(intel_crtc);
 
        intel_ddi_set_pipe_settings(pipe_config);
        if (!transcoder_is_dsi(cpu_transcoder))
@@ -6183,8 +6275,6 @@ static void valleyview_crtc_enable(struct intel_crtc_state *pipe_config,
 
        i9xx_set_pipeconf(pipe_config);
 
-       intel_color_set_csc(pipe_config);
-
        intel_crtc->active = true;
 
        intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
@@ -6204,6 +6294,7 @@ static void valleyview_crtc_enable(struct intel_crtc_state *pipe_config,
        i9xx_pfit_enable(pipe_config);
 
        intel_color_load_luts(pipe_config);
+       intel_color_commit(pipe_config);
 
        dev_priv->display.initial_watermarks(old_intel_state,
                                             pipe_config);
@@ -6260,6 +6351,7 @@ static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config,
        i9xx_pfit_enable(pipe_config);
 
        intel_color_load_luts(pipe_config);
+       intel_color_commit(pipe_config);
 
        if (dev_priv->display.initial_watermarks != NULL)
                dev_priv->display.initial_watermarks(old_intel_state,
@@ -6705,7 +6797,7 @@ static void intel_crtc_compute_pixel_rate(struct intel_crtc_state *crtc_state)
 {
        struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
 
-       if (HAS_GMCH_DISPLAY(dev_priv))
+       if (HAS_GMCH(dev_priv))
                /* FIXME calculate proper pipe pixel rate for GMCH pfit */
                crtc_state->pixel_rate =
                        crtc_state->base.adjusted_mode.crtc_clock;
@@ -9814,7 +9906,7 @@ static u32 intel_cursor_base(const struct intel_plane_state *plane_state)
        base += plane_state->color_plane[0].offset;
 
        /* ILK+ do this automagically */
-       if (HAS_GMCH_DISPLAY(dev_priv) &&
+       if (HAS_GMCH(dev_priv) &&
            plane_state->base.rotation & DRM_MODE_ROTATE_180)
                base += (plane_state->base.crtc_h *
                         plane_state->base.crtc_w - 1) * fb->format->cpp[0];
@@ -9927,11 +10019,15 @@ i845_cursor_max_stride(struct intel_plane *plane,
        return 2048;
 }
 
+static u32 i845_cursor_ctl_crtc(const struct intel_crtc_state *crtc_state)
+{
+       return CURSOR_GAMMA_ENABLE;
+}
+
 static u32 i845_cursor_ctl(const struct intel_crtc_state *crtc_state,
                           const struct intel_plane_state *plane_state)
 {
        return CURSOR_ENABLE |
-               CURSOR_GAMMA_ENABLE |
                CURSOR_FORMAT_ARGB |
                CURSOR_STRIDE(plane_state->color_plane[0].stride);
 }
@@ -10001,7 +10097,9 @@ static void i845_update_cursor(struct intel_plane *plane,
                unsigned int width = plane_state->base.crtc_w;
                unsigned int height = plane_state->base.crtc_h;
 
-               cntl = plane_state->ctl;
+               cntl = plane_state->ctl |
+                       i845_cursor_ctl_crtc(crtc_state);
+
                size = (height << 12) | width;
 
                base = intel_cursor_base(plane_state);
@@ -10068,27 +10166,36 @@ i9xx_cursor_max_stride(struct intel_plane *plane,
        return plane->base.dev->mode_config.cursor_width * 4;
 }
 
-static u32 i9xx_cursor_ctl(const struct intel_crtc_state *crtc_state,
-                          const struct intel_plane_state *plane_state)
+static u32 i9xx_cursor_ctl_crtc(const struct intel_crtc_state *crtc_state)
 {
-       struct drm_i915_private *dev_priv =
-               to_i915(plane_state->base.plane->dev);
        struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
        u32 cntl = 0;
 
-       if (IS_GEN(dev_priv, 6) || IS_IVYBRIDGE(dev_priv))
-               cntl |= MCURSOR_TRICKLE_FEED_DISABLE;
+       if (INTEL_GEN(dev_priv) >= 11)
+               return cntl;
 
-       if (INTEL_GEN(dev_priv) <= 10) {
-               cntl |= MCURSOR_GAMMA_ENABLE;
+       cntl |= MCURSOR_GAMMA_ENABLE;
 
-               if (HAS_DDI(dev_priv))
-                       cntl |= MCURSOR_PIPE_CSC_ENABLE;
-       }
+       if (HAS_DDI(dev_priv))
+               cntl |= MCURSOR_PIPE_CSC_ENABLE;
 
        if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
                cntl |= MCURSOR_PIPE_SELECT(crtc->pipe);
 
+       return cntl;
+}
+
+static u32 i9xx_cursor_ctl(const struct intel_crtc_state *crtc_state,
+                          const struct intel_plane_state *plane_state)
+{
+       struct drm_i915_private *dev_priv =
+               to_i915(plane_state->base.plane->dev);
+       u32 cntl = 0;
+
+       if (IS_GEN(dev_priv, 6) || IS_IVYBRIDGE(dev_priv))
+               cntl |= MCURSOR_TRICKLE_FEED_DISABLE;
+
        switch (plane_state->base.crtc_w) {
        case 64:
                cntl |= MCURSOR_MODE_64_ARGB_AX;
@@ -10213,7 +10320,8 @@ static void i9xx_update_cursor(struct intel_plane *plane,
        unsigned long irqflags;
 
        if (plane_state && plane_state->base.visible) {
-               cntl = plane_state->ctl;
+               cntl = plane_state->ctl |
+                       i9xx_cursor_ctl_crtc(crtc_state);
 
                if (plane_state->base.crtc_h != plane_state->base.crtc_w)
                        fbc_ctl = CUR_FBC_CTL_EN | (plane_state->base.crtc_h - 1);
@@ -11045,7 +11153,8 @@ static int intel_crtc_atomic_check(struct drm_crtc *crtc,
        int ret;
        bool mode_changed = needs_modeset(crtc_state);
 
-       if (mode_changed && !crtc_state->active)
+       if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv) &&
+           mode_changed && !crtc_state->active)
                pipe_config->update_wm_post = true;
 
        if (mode_changed && crtc_state->enable &&
@@ -11057,7 +11166,7 @@ static int intel_crtc_atomic_check(struct drm_crtc *crtc,
                        return ret;
        }
 
-       if (crtc_state->color_mgmt_changed) {
+       if (mode_changed || crtc_state->color_mgmt_changed) {
                ret = intel_color_check(pipe_config);
                if (ret)
                        return ret;
@@ -11356,7 +11465,7 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc,
                              pipe_config->scaler_state.scaler_users,
                              pipe_config->scaler_state.scaler_id);
 
-       if (HAS_GMCH_DISPLAY(dev_priv))
+       if (HAS_GMCH(dev_priv))
                DRM_DEBUG_KMS("gmch pfit: control: 0x%08x, ratios: 0x%08x, lvds border: 0x%08x\n",
                              pipe_config->gmch_pfit.control,
                              pipe_config->gmch_pfit.pgm_ratios,
@@ -11468,44 +11577,38 @@ static bool check_digital_port_conflicts(struct drm_atomic_state *state)
        return ret;
 }
 
-static void
+static int
 clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
 {
        struct drm_i915_private *dev_priv =
                to_i915(crtc_state->base.crtc->dev);
-       struct intel_crtc_scaler_state scaler_state;
-       struct intel_dpll_hw_state dpll_hw_state;
-       struct intel_shared_dpll *shared_dpll;
-       struct intel_crtc_wm_state wm_state;
-       bool force_thru, ips_force_disable;
+       struct intel_crtc_state *saved_state;
+
+       saved_state = kzalloc(sizeof(*saved_state), GFP_KERNEL);
+       if (!saved_state)
+               return -ENOMEM;
 
        /* FIXME: before the switch to atomic started, a new pipe_config was
         * kzalloc'd. Code that depends on any field being zero should be
         * fixed, so that the crtc_state can be safely duplicated. For now,
         * only fields that are know to not cause problems are preserved. */
 
-       scaler_state = crtc_state->scaler_state;
-       shared_dpll = crtc_state->shared_dpll;
-       dpll_hw_state = crtc_state->dpll_hw_state;
-       force_thru = crtc_state->pch_pfit.force_thru;
-       ips_force_disable = crtc_state->ips_force_disable;
+       saved_state->scaler_state = crtc_state->scaler_state;
+       saved_state->shared_dpll = crtc_state->shared_dpll;
+       saved_state->dpll_hw_state = crtc_state->dpll_hw_state;
+       saved_state->pch_pfit.force_thru = crtc_state->pch_pfit.force_thru;
+       saved_state->ips_force_disable = crtc_state->ips_force_disable;
        if (IS_G4X(dev_priv) ||
            IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
-               wm_state = crtc_state->wm;
+               saved_state->wm = crtc_state->wm;
 
        /* Keep base drm_crtc_state intact, only clear our extended struct */
        BUILD_BUG_ON(offsetof(struct intel_crtc_state, base));
-       memset(&crtc_state->base + 1, 0,
+       memcpy(&crtc_state->base + 1, &saved_state->base + 1,
               sizeof(*crtc_state) - sizeof(crtc_state->base));
 
-       crtc_state->scaler_state = scaler_state;
-       crtc_state->shared_dpll = shared_dpll;
-       crtc_state->dpll_hw_state = dpll_hw_state;
-       crtc_state->pch_pfit.force_thru = force_thru;
-       crtc_state->ips_force_disable = ips_force_disable;
-       if (IS_G4X(dev_priv) ||
-           IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
-               crtc_state->wm = wm_state;
+       kfree(saved_state);
+       return 0;
 }
 
 static int
@@ -11520,7 +11623,9 @@ intel_modeset_pipe_config(struct drm_crtc *crtc,
        int i;
        bool retry = true;
 
-       clear_intel_crtc_state(pipe_config);
+       ret = clear_intel_crtc_state(pipe_config);
+       if (ret)
+               return ret;
 
        pipe_config->cpu_transcoder =
                (enum transcoder) to_intel_crtc(crtc)->pipe;
@@ -13096,7 +13201,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
 
                        /* FIXME unify this for all platforms */
                        if (!new_crtc_state->active &&
-                           !HAS_GMCH_DISPLAY(dev_priv) &&
+                           !HAS_GMCH(dev_priv) &&
                            dev_priv->display.initial_watermarks)
                                dev_priv->display.initial_watermarks(intel_state,
                                                                     new_intel_crtc_state);
@@ -13150,6 +13255,16 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
         */
        drm_atomic_helper_wait_for_flip_done(dev, state);
 
+       for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
+               new_intel_crtc_state = to_intel_crtc_state(new_crtc_state);
+
+               if (new_crtc_state->active &&
+                   !needs_modeset(new_crtc_state) &&
+                   (new_intel_crtc_state->base.color_mgmt_changed ||
+                    new_intel_crtc_state->update_pipe))
+                       intel_color_load_luts(new_intel_crtc_state);
+       }
+
        /*
         * Now that the vblank has passed, we can go ahead and program the
         * optimal watermarks on platforms that need two-step watermark
@@ -13665,19 +13780,16 @@ static void intel_begin_crtc_commit(struct drm_crtc *crtc,
                intel_atomic_get_new_crtc_state(old_intel_state, intel_crtc);
        bool modeset = needs_modeset(&intel_cstate->base);
 
-       if (!modeset &&
-           (intel_cstate->base.color_mgmt_changed ||
-            intel_cstate->update_pipe)) {
-               intel_color_set_csc(intel_cstate);
-               intel_color_load_luts(intel_cstate);
-       }
-
        /* Perform vblank evasion around commit operation */
        intel_pipe_update_start(intel_cstate);
 
        if (modeset)
                goto out;
 
+       if (intel_cstate->base.color_mgmt_changed ||
+           intel_cstate->update_pipe)
+               intel_color_commit(intel_cstate);
+
        if (intel_cstate->update_pipe)
                intel_update_pipe_config(old_intel_cstate, intel_cstate);
        else if (INTEL_GEN(dev_priv) >= 9)
@@ -15074,7 +15186,7 @@ retry:
         * intermediate watermarks (since we don't trust the current
         * watermarks).
         */
-       if (!HAS_GMCH_DISPLAY(dev_priv))
+       if (!HAS_GMCH(dev_priv))
                intel_state->skip_intermediate_wm = true;
 
        ret = intel_atomic_check(dev, state);
@@ -15315,7 +15427,7 @@ int intel_modeset_init(struct drm_device *dev)
         * Note that we need to do this after reconstructing the BIOS fb's
         * since the watermark calculation done here will use pstate->fb.
         */
-       if (!HAS_GMCH_DISPLAY(dev_priv))
+       if (!HAS_GMCH(dev_priv))
                sanitize_watermarks(dev);
 
        /*
@@ -15524,7 +15636,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc,
        if (crtc_state->base.active && !intel_crtc_has_encoders(crtc))
                intel_crtc_disable_noatomic(&crtc->base, ctx);
 
-       if (crtc_state->base.active || HAS_GMCH_DISPLAY(dev_priv)) {
+       if (crtc_state->base.active || HAS_GMCH(dev_priv)) {
                /*
                 * We start out with underrun reporting disabled to avoid races.
                 * For correct bookkeeping mark this on active crtcs.
@@ -16271,7 +16383,7 @@ intel_display_capture_error_state(struct drm_i915_private *dev_priv)
 
                error->pipe[i].source = I915_READ(PIPESRC(i));
 
-               if (HAS_GMCH_DISPLAY(dev_priv))
+               if (HAS_GMCH(dev_priv))
                        error->pipe[i].stat = I915_READ(PIPESTAT(i));
        }
 
index 681e88405ada30c204b6a0095d2927127d150326..cf709835fb9a9eece3c0761c21c53c34a25b7e22 100644 (file)
@@ -1061,6 +1061,10 @@ intel_dp_aux_wait_done(struct intel_dp *intel_dp)
 #define C (((status = I915_READ_NOTRACE(ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0)
        done = wait_event_timeout(dev_priv->gmbus_wait_queue, C,
                                  msecs_to_jiffies_timeout(10));
+
+       /* just trace the final value */
+       trace_i915_reg_rw(false, ch_ctl, status, sizeof(status), true);
+
        if (!done)
                DRM_ERROR("dp aux hw did not signal timeout!\n");
 #undef C
@@ -1227,6 +1231,8 @@ intel_dp_aux_xfer(struct intel_dp *intel_dp,
                        break;
                msleep(1);
        }
+       /* just trace the final value */
+       trace_i915_reg_rw(false, ch_ctl, status, sizeof(status), true);
 
        if (try == 3) {
                static u32 last_status = -1;
@@ -2141,7 +2147,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
                                return ret;
                }
 
-               if (HAS_GMCH_DISPLAY(dev_priv))
+               if (HAS_GMCH(dev_priv))
                        intel_gmch_panel_fitting(intel_crtc, pipe_config,
                                                 conn_state->scaling_mode);
                else
@@ -2152,7 +2158,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
        if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
                return -EINVAL;
 
-       if (HAS_GMCH_DISPLAY(dev_priv) &&
+       if (HAS_GMCH(dev_priv) &&
            adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
                return -EINVAL;
 
@@ -4608,12 +4614,10 @@ go_again:
 
                        return ret;
                } else {
-                       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
                        DRM_DEBUG_KMS("failed to get ESI - device may have failed\n");
                        intel_dp->is_mst = false;
-                       drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
-                       /* send a hotplug event */
-                       drm_kms_helper_hotplug_event(intel_dig_port->base.base.dev);
+                       drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
+                                                       intel_dp->is_mst);
                }
        }
        return -EINVAL;
@@ -5300,7 +5304,7 @@ bool intel_digital_port_connected(struct intel_encoder *encoder)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
 
-       if (HAS_GMCH_DISPLAY(dev_priv)) {
+       if (HAS_GMCH(dev_priv)) {
                if (IS_GM45(dev_priv))
                        return gm45_digital_port_connected(encoder);
                else
@@ -6038,7 +6042,7 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect
                intel_attach_force_audio_property(connector);
 
        intel_attach_broadcast_rgb_property(connector);
-       if (HAS_GMCH_DISPLAY(dev_priv))
+       if (HAS_GMCH(dev_priv))
                drm_connector_attach_max_bpc_property(connector, 6, 10);
        else if (INTEL_GEN(dev_priv) >= 5)
                drm_connector_attach_max_bpc_property(connector, 6, 12);
@@ -6047,7 +6051,7 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect
                u32 allowed_scalers;
 
                allowed_scalers = BIT(DRM_MODE_SCALE_ASPECT) | BIT(DRM_MODE_SCALE_FULLSCREEN);
-               if (!HAS_GMCH_DISPLAY(dev_priv))
+               if (!HAS_GMCH(dev_priv))
                        allowed_scalers |= BIT(DRM_MODE_SCALE_CENTER);
 
                drm_connector_attach_scaling_mode_property(connector, allowed_scalers);
@@ -6919,7 +6923,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
        drm_connector_init(dev, connector, &intel_dp_connector_funcs, type);
        drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs);
 
-       if (!HAS_GMCH_DISPLAY(dev_priv))
+       if (!HAS_GMCH(dev_priv))
                connector->interlace_allowed = true;
        connector->doublescan_allowed = 0;
 
@@ -7096,7 +7100,10 @@ void intel_dp_mst_resume(struct drm_i915_private *dev_priv)
                        continue;
 
                ret = drm_dp_mst_topology_mgr_resume(&intel_dp->mst_mgr);
-               if (ret)
-                       intel_dp_check_mst_status(intel_dp);
+               if (ret) {
+                       intel_dp->is_mst = false;
+                       drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
+                                                       false);
+               }
        }
 }
index 90ba5436370e0af3201b2190f4cfd178839349c5..15db41394b9ed75d9de8545c8a0faff9efbc9a9b 100644 (file)
@@ -213,6 +213,16 @@ struct intel_fbdev {
        unsigned long vma_flags;
        async_cookie_t cookie;
        int preferred_bpp;
+
+       /* Whether or not fbdev hpd processing is temporarily suspended */
+       bool hpd_suspended : 1;
+       /* Set when a hotplug was received while HPD processing was
+        * suspended
+        */
+       bool hpd_waiting : 1;
+
+       /* Protects hpd_suspended */
+       struct mutex hpd_lock;
 };
 
 struct intel_encoder {
@@ -1756,9 +1766,10 @@ static inline u32 intel_plane_ggtt_offset(const struct intel_plane_state *state)
 
 u32 glk_plane_color_ctl(const struct intel_crtc_state *crtc_state,
                        const struct intel_plane_state *plane_state);
+u32 glk_plane_color_ctl_crtc(const struct intel_crtc_state *crtc_state);
 u32 skl_plane_ctl(const struct intel_crtc_state *crtc_state,
                  const struct intel_plane_state *plane_state);
-u32 glk_color_ctl(const struct intel_plane_state *plane_state);
+u32 skl_plane_ctl_crtc(const struct intel_crtc_state *crtc_state);
 u32 skl_plane_stride(const struct intel_plane_state *plane_state,
                     int plane);
 int skl_check_plane_surface(struct intel_plane_state *plane_state);
@@ -2379,8 +2390,8 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
 /* intel_color.c */
 void intel_color_init(struct intel_crtc *crtc);
 int intel_color_check(struct intel_crtc_state *crtc_state);
-void intel_color_set_csc(struct intel_crtc_state *crtc_state);
-void intel_color_load_luts(struct intel_crtc_state *crtc_state);
+void intel_color_commit(const struct intel_crtc_state *crtc_state);
+void intel_color_load_luts(const struct intel_crtc_state *crtc_state);
 
 /* intel_lspcon.c */
 bool lspcon_init(struct intel_digital_port *intel_dig_port);
index 71c01eb13af14999c5829aeac4f932e5c47523bd..49fa43ff02ba09dc63db471b4219e6b4db385cbf 100644 (file)
@@ -1086,7 +1086,7 @@ bool intel_engine_has_kernel_context(const struct intel_engine_cs *engine)
         * the last request that remains in the timeline. When idle, it is
         * the last executed context as tracked by retirement.
         */
-       rq = __i915_gem_active_peek(&engine->timeline.last_request);
+       rq = __i915_active_request_peek(&engine->timeline.last_request);
        if (rq)
                return rq->hw_context == kernel_context;
        else
index 8cf3efe88f025ff88f1e3b8b48ed3f541fb68f83..376ffe842e2678d1f31ee68acd38908cff8a85d9 100644 (file)
@@ -681,6 +681,7 @@ int intel_fbdev_init(struct drm_device *dev)
        if (ifbdev == NULL)
                return -ENOMEM;
 
+       mutex_init(&ifbdev->hpd_lock);
        drm_fb_helper_prepare(dev, &ifbdev->helper, &intel_fb_helper_funcs);
 
        if (!intel_fbdev_init_bios(dev, ifbdev))
@@ -754,6 +755,26 @@ void intel_fbdev_fini(struct drm_i915_private *dev_priv)
        intel_fbdev_destroy(ifbdev);
 }
 
+/* Suspends/resumes fbdev processing of incoming HPD events. When resuming HPD
+ * processing, fbdev will perform a full connector reprobe if a hotplug event
+ * was received while HPD was suspended.
+ */
+static void intel_fbdev_hpd_set_suspend(struct intel_fbdev *ifbdev, int state)
+{
+       bool send_hpd = false;
+
+       mutex_lock(&ifbdev->hpd_lock);
+       ifbdev->hpd_suspended = state == FBINFO_STATE_SUSPENDED;
+       send_hpd = !ifbdev->hpd_suspended && ifbdev->hpd_waiting;
+       ifbdev->hpd_waiting = false;
+       mutex_unlock(&ifbdev->hpd_lock);
+
+       if (send_hpd) {
+               DRM_DEBUG_KMS("Handling delayed fbcon HPD event\n");
+               drm_fb_helper_hotplug_event(&ifbdev->helper);
+       }
+}
+
 void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -775,6 +796,7 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous
                 */
                if (state != FBINFO_STATE_RUNNING)
                        flush_work(&dev_priv->fbdev_suspend_work);
+
                console_lock();
        } else {
                /*
@@ -802,17 +824,26 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous
 
        drm_fb_helper_set_suspend(&ifbdev->helper, state);
        console_unlock();
+
+       intel_fbdev_hpd_set_suspend(ifbdev, state);
 }
 
 void intel_fbdev_output_poll_changed(struct drm_device *dev)
 {
        struct intel_fbdev *ifbdev = to_i915(dev)->fbdev;
+       bool send_hpd;
 
        if (!ifbdev)
                return;
 
        intel_fbdev_sync(ifbdev);
-       if (ifbdev->vma || ifbdev->helper.deferred_setup)
+
+       mutex_lock(&ifbdev->hpd_lock);
+       send_hpd = !ifbdev->hpd_suspended;
+       ifbdev->hpd_waiting = true;
+       mutex_unlock(&ifbdev->hpd_lock);
+
+       if (send_hpd && (ifbdev->vma || ifbdev->helper.deferred_setup))
                drm_fb_helper_hotplug_event(&ifbdev->helper);
 }
 
index 3b9285130ef54a8a15c35e3f009c27418e05c40b..f33de4be4b89a7b07cc6a7b750e43bef9c439c07 100644 (file)
@@ -258,7 +258,7 @@ static bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
        old = !crtc->cpu_fifo_underrun_disabled;
        crtc->cpu_fifo_underrun_disabled = !enable;
 
-       if (HAS_GMCH_DISPLAY(dev_priv))
+       if (HAS_GMCH(dev_priv))
                i9xx_set_fifo_underrun_reporting(dev, pipe, enable, old);
        else if (IS_GEN_RANGE(dev_priv, 5, 6))
                ironlake_set_fifo_underrun_reporting(dev, pipe, enable);
@@ -369,7 +369,7 @@ void intel_cpu_fifo_underrun_irq_handler(struct drm_i915_private *dev_priv,
                return;
 
        /* GMCH can't disable fifo underruns, filter them. */
-       if (HAS_GMCH_DISPLAY(dev_priv) &&
+       if (HAS_GMCH(dev_priv) &&
            crtc->cpu_fifo_underrun_disabled)
                return;
 
@@ -421,7 +421,7 @@ void intel_check_cpu_fifo_underruns(struct drm_i915_private *dev_priv)
                if (crtc->cpu_fifo_underrun_disabled)
                        continue;
 
-               if (HAS_GMCH_DISPLAY(dev_priv))
+               if (HAS_GMCH(dev_priv))
                        i9xx_check_fifo_underruns(crtc);
                else if (IS_GEN(dev_priv, 7))
                        ivybridge_check_fifo_underruns(crtc);
index b53582c0c6c100d900539b4413dbede5a94b83a8..806fdfd7c78a7acf9777aa88cb72d1beb9c09fd4 100644 (file)
@@ -140,6 +140,9 @@ static struct dentry *create_buf_file_callback(const char *filename,
 
        buf_file = debugfs_create_file(filename, mode,
                                       parent, buf, &relay_file_operations);
+       if (IS_ERR(buf_file))
+               return NULL;
+
        return buf_file;
 }
 
index 97a98e1bea5607f6eadef4a58e385d218335313b..f125a62eba8cfe687f613d1aca2c2d03955f4b87 100644 (file)
@@ -1588,7 +1588,7 @@ intel_hdmi_mode_valid(struct drm_connector *connector,
 
        if (hdmi->has_hdmi_sink && !force_dvi) {
                /* if we can't do 8bpc we may still be able to do 12bpc */
-               if (status != MODE_OK && !HAS_GMCH_DISPLAY(dev_priv))
+               if (status != MODE_OK && !HAS_GMCH(dev_priv))
                        status = hdmi_port_clock_valid(hdmi, clock * 3 / 2,
                                                       true, force_dvi);
 
@@ -1613,7 +1613,7 @@ static bool hdmi_deep_color_possible(const struct intel_crtc_state *crtc_state,
                &crtc_state->base.adjusted_mode;
        int i;
 
-       if (HAS_GMCH_DISPLAY(dev_priv))
+       if (HAS_GMCH(dev_priv))
                return false;
 
        if (bpc == 10 && INTEL_GEN(dev_priv) < 11)
@@ -2150,7 +2150,7 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c
        drm_connector_attach_content_type_property(connector);
        connector->state->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
 
-       if (!HAS_GMCH_DISPLAY(dev_priv))
+       if (!HAS_GMCH(dev_priv))
                drm_connector_attach_max_bpc_property(connector, 8, 12);
 }
 
index e027d2b4abe541cb317dbdb1a554f34e0f919b8b..b8937c788f03a58c13b15bda81bfe64d807f03b6 100644 (file)
@@ -470,7 +470,7 @@ void intel_hpd_irq_handler(struct drm_i915_private *dev_priv,
                         * hotplug bits itself. So only WARN about unexpected
                         * interrupts on saner platforms.
                         */
-                       WARN_ONCE(!HAS_GMCH_DISPLAY(dev_priv),
+                       WARN_ONCE(!HAS_GMCH(dev_priv),
                                  "Received HPD interrupt on pin %d although disabled\n", pin);
                        continue;
                }
index 4f6dc8c94634e543fcc3cddef733ed7160c19304..5a733e711355bc66eb8f9a77649d1c51df303350 100644 (file)
@@ -823,7 +823,7 @@ int intel_setup_gmbus(struct drm_i915_private *dev_priv)
 
        if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
                dev_priv->gpio_mmio_base = VLV_DISPLAY_BASE;
-       else if (!HAS_GMCH_DISPLAY(dev_priv))
+       else if (!HAS_GMCH(dev_priv))
                /*
                 * Broxton uses the same PCH offsets for South Display Engine,
                 * even though it doesn't have a PCH.
index a9eb0211ce7781d1768e0d362bc0f1296be4b491..5e98fd79bd9df4a04950bdae683f8b67e2c4e7dd 100644 (file)
@@ -1266,8 +1266,6 @@ static int __context_pin(struct i915_gem_context *ctx, struct i915_vma *vma)
        return i915_vma_pin(vma, 0, 0, flags);
 }
 
-static u32 make_rpcs(struct drm_i915_private *dev_priv);
-
 static void
 __execlists_update_reg_state(struct intel_engine_cs *engine,
                             struct intel_context *ce)
@@ -1281,7 +1279,8 @@ __execlists_update_reg_state(struct intel_engine_cs *engine,
 
        /* RPCS */
        if (engine->class == RENDER_CLASS)
-               regs[CTX_R_PWR_CLK_STATE + 1] = make_rpcs(engine->i915);
+               regs[CTX_R_PWR_CLK_STATE + 1] = gen8_make_rpcs(engine->i915,
+                                                              &ce->sseu);
 }
 
 static struct intel_context *
@@ -2431,21 +2430,49 @@ int logical_xcs_ring_init(struct intel_engine_cs *engine)
        return logical_ring_init(engine);
 }
 
-static u32
-make_rpcs(struct drm_i915_private *dev_priv)
+u32 gen8_make_rpcs(struct drm_i915_private *i915, struct intel_sseu *req_sseu)
 {
-       bool subslice_pg = RUNTIME_INFO(dev_priv)->sseu.has_subslice_pg;
-       u8 slices = hweight8(RUNTIME_INFO(dev_priv)->sseu.slice_mask);
-       u8 subslices = hweight8(RUNTIME_INFO(dev_priv)->sseu.subslice_mask[0]);
+       const struct sseu_dev_info *sseu = &RUNTIME_INFO(i915)->sseu;
+       bool subslice_pg = sseu->has_subslice_pg;
+       struct intel_sseu ctx_sseu;
+       u8 slices, subslices;
        u32 rpcs = 0;
 
        /*
         * No explicit RPCS request is needed to ensure full
         * slice/subslice/EU enablement prior to Gen9.
        */
-       if (INTEL_GEN(dev_priv) < 9)
+       if (INTEL_GEN(i915) < 9)
                return 0;
 
+       /*
+        * If i915/perf is active, we want a stable powergating configuration
+        * on the system.
+        *
+        * We could choose full enablement, but on ICL we know there are use
+        * cases which disable slices for functional, apart for performance
+        * reasons. So in this case we select a known stable subset.
+        */
+       if (!i915->perf.oa.exclusive_stream) {
+               ctx_sseu = *req_sseu;
+       } else {
+               ctx_sseu = intel_device_default_sseu(i915);
+
+               if (IS_GEN(i915, 11)) {
+                       /*
+                        * We only need subslice count so it doesn't matter
+                        * which ones we select - just turn off low bits in the
+                        * amount of half of all available subslices per slice.
+                        */
+                       ctx_sseu.subslice_mask =
+                               ~(~0 << (hweight8(ctx_sseu.subslice_mask) / 2));
+                       ctx_sseu.slice_mask = 0x1;
+               }
+       }
+
+       slices = hweight8(ctx_sseu.slice_mask);
+       subslices = hweight8(ctx_sseu.subslice_mask);
+
        /*
         * Since the SScount bitfield in GEN8_R_PWR_CLK_STATE is only three bits
         * wide and Icelake has up to eight subslices, specfial programming is
@@ -2471,7 +2498,9 @@ make_rpcs(struct drm_i915_private *dev_priv)
         * subslices are enabled, or a count between one and four on the first
         * slice.
         */
-       if (IS_GEN(dev_priv, 11) && slices == 1 && subslices >= 4) {
+       if (IS_GEN(i915, 11) &&
+           slices == 1 &&
+           subslices > min_t(u8, 4, hweight8(sseu->subslice_mask[0]) / 2)) {
                GEM_BUG_ON(subslices & 1);
 
                subslice_pg = false;
@@ -2484,10 +2513,10 @@ make_rpcs(struct drm_i915_private *dev_priv)
         * must make an explicit request through RPCS for full
         * enablement.
        */
-       if (RUNTIME_INFO(dev_priv)->sseu.has_slice_pg) {
+       if (sseu->has_slice_pg) {
                u32 mask, val = slices;
 
-               if (INTEL_GEN(dev_priv) >= 11) {
+               if (INTEL_GEN(i915) >= 11) {
                        mask = GEN11_RPCS_S_CNT_MASK;
                        val <<= GEN11_RPCS_S_CNT_SHIFT;
                } else {
@@ -2512,18 +2541,16 @@ make_rpcs(struct drm_i915_private *dev_priv)
                rpcs |= GEN8_RPCS_ENABLE | GEN8_RPCS_SS_CNT_ENABLE | val;
        }
 
-       if (RUNTIME_INFO(dev_priv)->sseu.has_eu_pg) {
+       if (sseu->has_eu_pg) {
                u32 val;
 
-               val = RUNTIME_INFO(dev_priv)->sseu.eu_per_subslice <<
-                     GEN8_RPCS_EU_MIN_SHIFT;
+               val = ctx_sseu.min_eus_per_subslice << GEN8_RPCS_EU_MIN_SHIFT;
                GEM_BUG_ON(val & ~GEN8_RPCS_EU_MIN_MASK);
                val &= GEN8_RPCS_EU_MIN_MASK;
 
                rpcs |= val;
 
-               val = RUNTIME_INFO(dev_priv)->sseu.eu_per_subslice <<
-                     GEN8_RPCS_EU_MAX_SHIFT;
+               val = ctx_sseu.max_eus_per_subslice << GEN8_RPCS_EU_MAX_SHIFT;
                GEM_BUG_ON(val & ~GEN8_RPCS_EU_MAX_MASK);
                val &= GEN8_RPCS_EU_MAX_MASK;
 
index 3d86c27c6b32c41e0905c9026b631c330a944162..f1aec8a6986fd8ebb610a96dbd48c7bd082d7637 100644 (file)
@@ -112,4 +112,6 @@ void intel_execlists_show_requests(struct intel_engine_cs *engine,
                                                        const char *prefix),
                                   unsigned int max);
 
+u32 gen8_make_rpcs(struct drm_i915_private *i915, struct intel_sseu *ctx_sseu);
+
 #endif /* _INTEL_LRC_H_ */
index a9238fd07e309db5a89f439c2a35e05304e2495d..c0df1dbb0069e805face7c130cd9866791e2b77f 100644 (file)
@@ -186,7 +186,7 @@ struct intel_overlay {
        struct overlay_registers __iomem *regs;
        u32 flip_addr;
        /* flip handling */
-       struct i915_gem_active last_flip;
+       struct i915_active_request last_flip;
 };
 
 static void i830_overlay_clock_gating(struct drm_i915_private *dev_priv,
@@ -214,23 +214,23 @@ static void i830_overlay_clock_gating(struct drm_i915_private *dev_priv,
 
 static void intel_overlay_submit_request(struct intel_overlay *overlay,
                                         struct i915_request *rq,
-                                        i915_gem_retire_fn retire)
+                                        i915_active_retire_fn retire)
 {
-       GEM_BUG_ON(i915_gem_active_peek(&overlay->last_flip,
-                                       &overlay->i915->drm.struct_mutex));
-       i915_gem_active_set_retire_fn(&overlay->last_flip, retire,
-                                     &overlay->i915->drm.struct_mutex);
-       i915_gem_active_set(&overlay->last_flip, rq);
+       GEM_BUG_ON(i915_active_request_peek(&overlay->last_flip,
+                                           &overlay->i915->drm.struct_mutex));
+       i915_active_request_set_retire_fn(&overlay->last_flip, retire,
+                                         &overlay->i915->drm.struct_mutex);
+       __i915_active_request_set(&overlay->last_flip, rq);
        i915_request_add(rq);
 }
 
 static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
                                         struct i915_request *rq,
-                                        i915_gem_retire_fn retire)
+                                        i915_active_retire_fn retire)
 {
        intel_overlay_submit_request(overlay, rq, retire);
-       return i915_gem_active_retire(&overlay->last_flip,
-                                     &overlay->i915->drm.struct_mutex);
+       return i915_active_request_retire(&overlay->last_flip,
+                                         &overlay->i915->drm.struct_mutex);
 }
 
 static struct i915_request *alloc_request(struct intel_overlay *overlay)
@@ -351,8 +351,9 @@ static void intel_overlay_release_old_vma(struct intel_overlay *overlay)
        i915_vma_put(vma);
 }
 
-static void intel_overlay_release_old_vid_tail(struct i915_gem_active *active,
-                                              struct i915_request *rq)
+static void
+intel_overlay_release_old_vid_tail(struct i915_active_request *active,
+                                  struct i915_request *rq)
 {
        struct intel_overlay *overlay =
                container_of(active, typeof(*overlay), last_flip);
@@ -360,7 +361,7 @@ static void intel_overlay_release_old_vid_tail(struct i915_gem_active *active,
        intel_overlay_release_old_vma(overlay);
 }
 
-static void intel_overlay_off_tail(struct i915_gem_active *active,
+static void intel_overlay_off_tail(struct i915_active_request *active,
                                   struct i915_request *rq)
 {
        struct intel_overlay *overlay =
@@ -423,8 +424,8 @@ static int intel_overlay_off(struct intel_overlay *overlay)
  * We have to be careful not to repeat work forever an make forward progess. */
 static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay)
 {
-       return i915_gem_active_retire(&overlay->last_flip,
-                                     &overlay->i915->drm.struct_mutex);
+       return i915_active_request_retire(&overlay->last_flip,
+                                         &overlay->i915->drm.struct_mutex);
 }
 
 /* Wait for pending overlay flip and release old frame.
@@ -1357,7 +1358,7 @@ void intel_overlay_setup(struct drm_i915_private *dev_priv)
        overlay->contrast = 75;
        overlay->saturation = 146;
 
-       init_request_active(&overlay->last_flip, NULL);
+       INIT_ACTIVE_REQUEST(&overlay->last_flip);
 
        mutex_lock(&dev_priv->drm.struct_mutex);
 
index ed9786241307746c4041e971eb4355cf7b9087e1..0f15685529a01a4e10e337d957062d544b645f0d 100644 (file)
@@ -3927,14 +3927,9 @@ static unsigned int skl_cursor_allocation(int num_active)
 static void skl_ddb_entry_init_from_hw(struct drm_i915_private *dev_priv,
                                       struct skl_ddb_entry *entry, u32 reg)
 {
-       u16 mask;
 
-       if (INTEL_GEN(dev_priv) >= 11)
-               mask = ICL_DDB_ENTRY_MASK;
-       else
-               mask = SKL_DDB_ENTRY_MASK;
-       entry->start = reg & mask;
-       entry->end = (reg >> DDB_ENTRY_END_SHIFT) & mask;
+       entry->start = reg & DDB_ENTRY_MASK;
+       entry->end = (reg >> DDB_ENTRY_END_SHIFT) & DDB_ENTRY_MASK;
 
        if (entry->end)
                entry->end += 1;
@@ -4319,7 +4314,7 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate,
        int num_active;
        u64 plane_data_rate[I915_MAX_PLANES] = {};
        u64 uv_plane_data_rate[I915_MAX_PLANES] = {};
-       u16 blocks = 0;
+       u32 blocks;
        int level;
 
        /* Clear the partitioning for disabled planes. */
@@ -4694,8 +4689,11 @@ static void skl_compute_plane_wm(const struct intel_crtc_state *cstate,
        uint_fixed_16_16_t selected_result;
        u32 res_blocks, res_lines, min_ddb_alloc = 0;
 
-       if (latency == 0)
+       if (latency == 0) {
+               /* reject it */
+               result->min_ddb_alloc = U16_MAX;
                return;
+       }
 
        /* Display WA #1141: kbl,cfl */
        if ((IS_KABYLAKE(dev_priv) || IS_COFFEELAKE(dev_priv) ||
@@ -4783,8 +4781,11 @@ static void skl_compute_plane_wm(const struct intel_crtc_state *cstate,
        if (!skl_wm_has_lines(dev_priv, level))
                res_lines = 0;
 
-       if (res_lines > 31)
+       if (res_lines > 31) {
+               /* reject it */
+               result->min_ddb_alloc = U16_MAX;
                return;
+       }
 
        /*
         * If res_lines is valid, assume we can use this watermark level
index b889b27f8aebafccfe660e759b650d23afd41e69..7f841dba87b3026893b2cb330168cb74246d5804 100644 (file)
@@ -49,19 +49,6 @@ static inline u32 intel_hws_seqno_address(struct intel_engine_cs *engine)
                I915_GEM_HWS_INDEX_ADDR);
 }
 
-static unsigned int __intel_ring_space(unsigned int head,
-                                      unsigned int tail,
-                                      unsigned int size)
-{
-       /*
-        * "If the Ring Buffer Head Pointer and the Tail Pointer are on the
-        * same cacheline, the Head Pointer must not be greater than the Tail
-        * Pointer."
-        */
-       GEM_BUG_ON(!is_power_of_2(size));
-       return (head - tail - CACHELINE_BYTES) & (size - 1);
-}
-
 unsigned int intel_ring_update_space(struct intel_ring *ring)
 {
        unsigned int space;
index 34d0a148e664592c902290a27ff26910005a89a1..710ffb2217753b85026c92996f092890fa434ebf 100644 (file)
@@ -403,16 +403,17 @@ struct intel_engine_cs {
                /**
                 * @enable_count: Reference count for the enabled samplers.
                 *
-                * Index number corresponds to the bit number from @enable.
+                * Index number corresponds to @enum drm_i915_pmu_engine_sample.
                 */
-               unsigned int enable_count[I915_PMU_SAMPLE_BITS];
+               unsigned int enable_count[I915_ENGINE_SAMPLE_COUNT];
                /**
                 * @sample: Counter values for sampling events.
                 *
                 * Our internal timer stores the current counters in this field.
+                *
+                * Index number corresponds to @enum drm_i915_pmu_engine_sample.
                 */
-#define I915_ENGINE_SAMPLE_MAX (I915_SAMPLE_SEMA + 1)
-               struct i915_pmu_sample sample[I915_ENGINE_SAMPLE_MAX];
+               struct i915_pmu_sample sample[I915_ENGINE_SAMPLE_COUNT];
        } pmu;
 
        /*
@@ -592,7 +593,20 @@ intel_engine_has_preemption(const struct intel_engine_cs *engine)
 
 static inline bool __execlists_need_preempt(int prio, int last)
 {
-       return prio > max(0, last);
+       /*
+        * Allow preemption of low -> normal -> high, but we do
+        * not allow low priority tasks to preempt other low priority
+        * tasks under the impression that latency for low priority
+        * tasks does not matter (as much as background throughput),
+        * so kiss.
+        *
+        * More naturally we would write
+        *      prio >= max(0, last);
+        * except that we wish to prevent triggering preemption at the same
+        * priority level: the task that is running should remain running
+        * to preserve FIFO ordering of dependencies.
+        */
+       return prio > max(I915_PRIORITY_NORMAL - 1, last);
 }
 
 static inline void
@@ -818,6 +832,18 @@ intel_ring_set_tail(struct intel_ring *ring, unsigned int tail)
        return tail;
 }
 
+static inline unsigned int
+__intel_ring_space(unsigned int head, unsigned int tail, unsigned int size)
+{
+       /*
+        * "If the Ring Buffer Head Pointer and the Tail Pointer are on the
+        * same cacheline, the Head Pointer must not be greater than the Tail
+        * Pointer."
+        */
+       GEM_BUG_ON(!is_power_of_2(size));
+       return (head - tail - CACHELINE_BYTES) & (size - 1);
+}
+
 void intel_engine_write_global_seqno(struct intel_engine_cs *engine, u32 seqno);
 
 int intel_engine_setup_common(struct intel_engine_cs *engine);
index cd42e81f8a90112603602851e296a65b1236ce0a..b56a1a9ad01d2e724715503b545d4396f56f7194 100644 (file)
@@ -484,9 +484,16 @@ skl_program_plane(struct intel_plane *plane,
        struct intel_plane *linked = plane_state->linked_plane;
        const struct drm_framebuffer *fb = plane_state->base.fb;
        u8 alpha = plane_state->base.alpha >> 8;
+       u32 plane_color_ctl = 0;
        unsigned long irqflags;
        u32 keymsk, keymax;
 
+       plane_ctl |= skl_plane_ctl_crtc(crtc_state);
+
+       if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
+               plane_color_ctl = plane_state->color_ctl |
+                       glk_plane_color_ctl_crtc(crtc_state);
+
        /* Sizes are 0 based */
        src_w--;
        src_h--;
@@ -533,8 +540,7 @@ skl_program_plane(struct intel_plane *plane,
        }
 
        if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
-               I915_WRITE_FW(PLANE_COLOR_CTL(pipe, plane_id),
-                             plane_state->color_ctl);
+               I915_WRITE_FW(PLANE_COLOR_CTL(pipe, plane_id), plane_color_ctl);
 
        if (fb->format->is_yuv && icl_is_hdr_plane(plane))
                icl_program_input_csc(plane, crtc_state, plane_state);
@@ -733,6 +739,11 @@ vlv_update_clrc(const struct intel_plane_state *plane_state)
                      SP_SH_SIN(sh_sin) | SP_SH_COS(sh_cos));
 }
 
+static u32 vlv_sprite_ctl_crtc(const struct intel_crtc_state *crtc_state)
+{
+       return SP_GAMMA_ENABLE;
+}
+
 static u32 vlv_sprite_ctl(const struct intel_crtc_state *crtc_state,
                          const struct intel_plane_state *plane_state)
 {
@@ -741,7 +752,7 @@ static u32 vlv_sprite_ctl(const struct intel_crtc_state *crtc_state,
        const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
        u32 sprctl;
 
-       sprctl = SP_ENABLE | SP_GAMMA_ENABLE;
+       sprctl = SP_ENABLE;
 
        switch (fb->format->format) {
        case DRM_FORMAT_YUYV:
@@ -808,7 +819,6 @@ vlv_update_plane(struct intel_plane *plane,
        struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
        enum pipe pipe = plane->pipe;
        enum plane_id plane_id = plane->id;
-       u32 sprctl = plane_state->ctl;
        u32 sprsurf_offset = plane_state->color_plane[0].offset;
        u32 linear_offset;
        const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
@@ -819,6 +829,9 @@ vlv_update_plane(struct intel_plane *plane,
        u32 x = plane_state->color_plane[0].x;
        u32 y = plane_state->color_plane[0].y;
        unsigned long irqflags;
+       u32 sprctl;
+
+       sprctl = plane_state->ctl | vlv_sprite_ctl_crtc(crtc_state);
 
        /* Sizes are 0 based */
        crtc_w--;
@@ -901,6 +914,19 @@ vlv_plane_get_hw_state(struct intel_plane *plane,
        return ret;
 }
 
+static u32 ivb_sprite_ctl_crtc(const struct intel_crtc_state *crtc_state)
+{
+       struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
+       u32 sprctl = 0;
+
+       sprctl |= SPRITE_GAMMA_ENABLE;
+
+       if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
+               sprctl |= SPRITE_PIPE_CSC_ENABLE;
+
+       return sprctl;
+}
+
 static u32 ivb_sprite_ctl(const struct intel_crtc_state *crtc_state,
                          const struct intel_plane_state *plane_state)
 {
@@ -911,14 +937,11 @@ static u32 ivb_sprite_ctl(const struct intel_crtc_state *crtc_state,
        const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
        u32 sprctl;
 
-       sprctl = SPRITE_ENABLE | SPRITE_GAMMA_ENABLE;
+       sprctl = SPRITE_ENABLE;
 
        if (IS_IVYBRIDGE(dev_priv))
                sprctl |= SPRITE_TRICKLE_FEED_DISABLE;
 
-       if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
-               sprctl |= SPRITE_PIPE_CSC_ENABLE;
-
        switch (fb->format->format) {
        case DRM_FORMAT_XBGR8888:
                sprctl |= SPRITE_FORMAT_RGBX888 | SPRITE_RGB_ORDER_RGBX;
@@ -970,7 +993,6 @@ ivb_update_plane(struct intel_plane *plane,
 {
        struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
        enum pipe pipe = plane->pipe;
-       u32 sprctl = plane_state->ctl, sprscale = 0;
        u32 sprsurf_offset = plane_state->color_plane[0].offset;
        u32 linear_offset;
        const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
@@ -982,8 +1004,11 @@ ivb_update_plane(struct intel_plane *plane,
        u32 y = plane_state->color_plane[0].y;
        u32 src_w = drm_rect_width(&plane_state->base.src) >> 16;
        u32 src_h = drm_rect_height(&plane_state->base.src) >> 16;
+       u32 sprctl, sprscale = 0;
        unsigned long irqflags;
 
+       sprctl = plane_state->ctl | ivb_sprite_ctl_crtc(crtc_state);
+
        /* Sizes are 0 based */
        src_w--;
        src_h--;
@@ -1080,6 +1105,11 @@ g4x_sprite_max_stride(struct intel_plane *plane,
        return 16384;
 }
 
+static u32 g4x_sprite_ctl_crtc(const struct intel_crtc_state *crtc_state)
+{
+       return DVS_GAMMA_ENABLE;
+}
+
 static u32 g4x_sprite_ctl(const struct intel_crtc_state *crtc_state,
                          const struct intel_plane_state *plane_state)
 {
@@ -1090,7 +1120,7 @@ static u32 g4x_sprite_ctl(const struct intel_crtc_state *crtc_state,
        const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
        u32 dvscntr;
 
-       dvscntr = DVS_ENABLE | DVS_GAMMA_ENABLE;
+       dvscntr = DVS_ENABLE;
 
        if (IS_GEN(dev_priv, 6))
                dvscntr |= DVS_TRICKLE_FEED_DISABLE;
@@ -1146,7 +1176,6 @@ g4x_update_plane(struct intel_plane *plane,
 {
        struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
        enum pipe pipe = plane->pipe;
-       u32 dvscntr = plane_state->ctl, dvsscale = 0;
        u32 dvssurf_offset = plane_state->color_plane[0].offset;
        u32 linear_offset;
        const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
@@ -1158,8 +1187,11 @@ g4x_update_plane(struct intel_plane *plane,
        u32 y = plane_state->color_plane[0].y;
        u32 src_w = drm_rect_width(&plane_state->base.src) >> 16;
        u32 src_h = drm_rect_height(&plane_state->base.src) >> 16;
+       u32 dvscntr, dvsscale = 0;
        unsigned long irqflags;
 
+       dvscntr = plane_state->ctl | g4x_sprite_ctl_crtc(crtc_state);
+
        /* Sizes are 0 based */
        src_w--;
        src_h--;
index e88f0252d77e74c19b1f8c8cf257dd3ac80cac00..75646a1e0051c336b386c60057abaa9807c9c57e 100644 (file)
@@ -1819,6 +1819,9 @@ int __intel_wait_for_register(struct drm_i915_private *dev_priv,
                                 (reg_value & mask) == value,
                                 slow_timeout_ms * 1000, 10, 1000);
 
+       /* just trace the final value */
+       trace_i915_reg_rw(false, reg, reg_value, sizeof(reg_value), true);
+
        if (out_value)
                *out_value = reg_value;
 
diff --git a/drivers/gpu/drm/i915/selftests/i915_active.c b/drivers/gpu/drm/i915/selftests/i915_active.c
new file mode 100644 (file)
index 0000000..337b1f9
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2018 Intel Corporation
+ */
+
+#include "../i915_selftest.h"
+
+#include "igt_flush_test.h"
+#include "lib_sw_fence.h"
+
+struct live_active {
+       struct i915_active base;
+       bool retired;
+};
+
+static void __live_active_retire(struct i915_active *base)
+{
+       struct live_active *active = container_of(base, typeof(*active), base);
+
+       active->retired = true;
+}
+
+static int __live_active_setup(struct drm_i915_private *i915,
+                              struct live_active *active)
+{
+       struct intel_engine_cs *engine;
+       struct i915_sw_fence *submit;
+       enum intel_engine_id id;
+       unsigned int count = 0;
+       int err = 0;
+
+       submit = heap_fence_create(GFP_KERNEL);
+       if (!submit)
+               return -ENOMEM;
+
+       i915_active_init(i915, &active->base, __live_active_retire);
+       active->retired = false;
+
+       if (!i915_active_acquire(&active->base)) {
+               pr_err("First i915_active_acquire should report being idle\n");
+               err = -EINVAL;
+               goto out;
+       }
+
+       for_each_engine(engine, i915, id) {
+               struct i915_request *rq;
+
+               rq = i915_request_alloc(engine, i915->kernel_context);
+               if (IS_ERR(rq)) {
+                       err = PTR_ERR(rq);
+                       break;
+               }
+
+               err = i915_sw_fence_await_sw_fence_gfp(&rq->submit,
+                                                      submit,
+                                                      GFP_KERNEL);
+               if (err >= 0)
+                       err = i915_active_ref(&active->base,
+                                             rq->fence.context, rq);
+               i915_request_add(rq);
+               if (err) {
+                       pr_err("Failed to track active ref!\n");
+                       break;
+               }
+
+               count++;
+       }
+
+       i915_active_release(&active->base);
+       if (active->retired && count) {
+               pr_err("i915_active retired before submission!\n");
+               err = -EINVAL;
+       }
+       if (active->base.count != count) {
+               pr_err("i915_active not tracking all requests, found %d, expected %d\n",
+                      active->base.count, count);
+               err = -EINVAL;
+       }
+
+out:
+       i915_sw_fence_commit(submit);
+       heap_fence_put(submit);
+
+       return err;
+}
+
+static int live_active_wait(void *arg)
+{
+       struct drm_i915_private *i915 = arg;
+       struct live_active active;
+       intel_wakeref_t wakeref;
+       int err;
+
+       /* Check that we get a callback when requests retire upon waiting */
+
+       mutex_lock(&i915->drm.struct_mutex);
+       wakeref = intel_runtime_pm_get(i915);
+
+       err = __live_active_setup(i915, &active);
+
+       i915_active_wait(&active.base);
+       if (!active.retired) {
+               pr_err("i915_active not retired after waiting!\n");
+               err = -EINVAL;
+       }
+
+       i915_active_fini(&active.base);
+       if (igt_flush_test(i915, I915_WAIT_LOCKED))
+               err = -EIO;
+
+       intel_runtime_pm_put(i915, wakeref);
+       mutex_unlock(&i915->drm.struct_mutex);
+       return err;
+}
+
+static int live_active_retire(void *arg)
+{
+       struct drm_i915_private *i915 = arg;
+       struct live_active active;
+       intel_wakeref_t wakeref;
+       int err;
+
+       /* Check that we get a callback when requests are indirectly retired */
+
+       mutex_lock(&i915->drm.struct_mutex);
+       wakeref = intel_runtime_pm_get(i915);
+
+       err = __live_active_setup(i915, &active);
+
+       /* waits for & retires all requests */
+       if (igt_flush_test(i915, I915_WAIT_LOCKED))
+               err = -EIO;
+
+       if (!active.retired) {
+               pr_err("i915_active not retired after flushing!\n");
+               err = -EINVAL;
+       }
+
+       i915_active_fini(&active.base);
+       intel_runtime_pm_put(i915, wakeref);
+       mutex_unlock(&i915->drm.struct_mutex);
+       return err;
+}
+
+int i915_active_live_selftests(struct drm_i915_private *i915)
+{
+       static const struct i915_subtest tests[] = {
+               SUBTEST(live_active_wait),
+               SUBTEST(live_active_retire),
+       };
+
+       if (i915_terminally_wedged(&i915->gpu_error))
+               return 0;
+
+       return i915_subtests(tests, i915);
+}
index e2c1f0bc2abe4685c8c5b2877597ee7e8b754366..d00d0bb07784229a323145336803802479c6ebb9 100644 (file)
 
 #include <linux/prime_numbers.h>
 
+#include "../i915_reset.h"
 #include "../i915_selftest.h"
 #include "i915_random.h"
 #include "igt_flush_test.h"
 #include "igt_live_test.h"
+#include "igt_reset.h"
+#include "igt_spinner.h"
 
 #include "mock_drm.h"
 #include "mock_gem_device.h"
@@ -576,6 +579,469 @@ out_unlock:
        return err;
 }
 
+static struct i915_vma *rpcs_query_batch(struct i915_vma *vma)
+{
+       struct drm_i915_gem_object *obj;
+       u32 *cmd;
+       int err;
+
+       if (INTEL_GEN(vma->vm->i915) < 8)
+               return ERR_PTR(-EINVAL);
+
+       obj = i915_gem_object_create_internal(vma->vm->i915, PAGE_SIZE);
+       if (IS_ERR(obj))
+               return ERR_CAST(obj);
+
+       cmd = i915_gem_object_pin_map(obj, I915_MAP_WB);
+       if (IS_ERR(cmd)) {
+               err = PTR_ERR(cmd);
+               goto err;
+       }
+
+       *cmd++ = MI_STORE_REGISTER_MEM_GEN8;
+       *cmd++ = i915_mmio_reg_offset(GEN8_R_PWR_CLK_STATE);
+       *cmd++ = lower_32_bits(vma->node.start);
+       *cmd++ = upper_32_bits(vma->node.start);
+       *cmd = MI_BATCH_BUFFER_END;
+
+       i915_gem_object_unpin_map(obj);
+
+       err = i915_gem_object_set_to_gtt_domain(obj, false);
+       if (err)
+               goto err;
+
+       vma = i915_vma_instance(obj, vma->vm, NULL);
+       if (IS_ERR(vma)) {
+               err = PTR_ERR(vma);
+               goto err;
+       }
+
+       err = i915_vma_pin(vma, 0, 0, PIN_USER);
+       if (err)
+               goto err;
+
+       return vma;
+
+err:
+       i915_gem_object_put(obj);
+       return ERR_PTR(err);
+}
+
+static int
+emit_rpcs_query(struct drm_i915_gem_object *obj,
+               struct i915_gem_context *ctx,
+               struct intel_engine_cs *engine,
+               struct i915_request **rq_out)
+{
+       struct i915_request *rq;
+       struct i915_vma *batch;
+       struct i915_vma *vma;
+       int err;
+
+       GEM_BUG_ON(!intel_engine_can_store_dword(engine));
+
+       vma = i915_vma_instance(obj, &ctx->ppgtt->vm, NULL);
+       if (IS_ERR(vma))
+               return PTR_ERR(vma);
+
+       err = i915_gem_object_set_to_gtt_domain(obj, false);
+       if (err)
+               return err;
+
+       err = i915_vma_pin(vma, 0, 0, PIN_USER);
+       if (err)
+               return err;
+
+       batch = rpcs_query_batch(vma);
+       if (IS_ERR(batch)) {
+               err = PTR_ERR(batch);
+               goto err_vma;
+       }
+
+       rq = i915_request_alloc(engine, ctx);
+       if (IS_ERR(rq)) {
+               err = PTR_ERR(rq);
+               goto err_batch;
+       }
+
+       err = engine->emit_bb_start(rq, batch->node.start, batch->node.size, 0);
+       if (err)
+               goto err_request;
+
+       err = i915_vma_move_to_active(batch, rq, 0);
+       if (err)
+               goto skip_request;
+
+       err = i915_vma_move_to_active(vma, rq, EXEC_OBJECT_WRITE);
+       if (err)
+               goto skip_request;
+
+       i915_gem_object_set_active_reference(batch->obj);
+       i915_vma_unpin(batch);
+       i915_vma_close(batch);
+
+       i915_vma_unpin(vma);
+
+       *rq_out = i915_request_get(rq);
+
+       i915_request_add(rq);
+
+       return 0;
+
+skip_request:
+       i915_request_skip(rq, err);
+err_request:
+       i915_request_add(rq);
+err_batch:
+       i915_vma_unpin(batch);
+err_vma:
+       i915_vma_unpin(vma);
+
+       return err;
+}
+
+#define TEST_IDLE      BIT(0)
+#define TEST_BUSY      BIT(1)
+#define TEST_RESET     BIT(2)
+
+static int
+__sseu_prepare(struct drm_i915_private *i915,
+              const char *name,
+              unsigned int flags,
+              struct i915_gem_context *ctx,
+              struct intel_engine_cs *engine,
+              struct igt_spinner **spin_out)
+{
+       int ret = 0;
+
+       if (flags & (TEST_BUSY | TEST_RESET)) {
+               struct igt_spinner *spin;
+               struct i915_request *rq;
+
+               spin = kzalloc(sizeof(*spin), GFP_KERNEL);
+               if (!spin) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               ret = igt_spinner_init(spin, i915);
+               if (ret)
+                       return ret;
+
+               rq = igt_spinner_create_request(spin, ctx, engine, MI_NOOP);
+               if (IS_ERR(rq)) {
+                       ret = PTR_ERR(rq);
+                       igt_spinner_fini(spin);
+                       kfree(spin);
+                       goto out;
+               }
+
+               i915_request_add(rq);
+
+               if (!igt_wait_for_spinner(spin, rq)) {
+                       pr_err("%s: Spinner failed to start!\n", name);
+                       igt_spinner_end(spin);
+                       igt_spinner_fini(spin);
+                       kfree(spin);
+                       ret = -ETIMEDOUT;
+                       goto out;
+               }
+
+               *spin_out = spin;
+       }
+
+out:
+       return ret;
+}
+
+static int
+__read_slice_count(struct drm_i915_private *i915,
+                  struct i915_gem_context *ctx,
+                  struct intel_engine_cs *engine,
+                  struct drm_i915_gem_object *obj,
+                  struct igt_spinner *spin,
+                  u32 *rpcs)
+{
+       struct i915_request *rq = NULL;
+       u32 s_mask, s_shift;
+       unsigned int cnt;
+       u32 *buf, val;
+       long ret;
+
+       ret = emit_rpcs_query(obj, ctx, engine, &rq);
+       if (ret)
+               return ret;
+
+       if (spin)
+               igt_spinner_end(spin);
+
+       ret = i915_request_wait(rq, I915_WAIT_LOCKED, MAX_SCHEDULE_TIMEOUT);
+       i915_request_put(rq);
+       if (ret < 0)
+               return ret;
+
+       buf = i915_gem_object_pin_map(obj, I915_MAP_WB);
+       if (IS_ERR(buf)) {
+               ret = PTR_ERR(buf);
+               return ret;
+       }
+
+       if (INTEL_GEN(i915) >= 11) {
+               s_mask = GEN11_RPCS_S_CNT_MASK;
+               s_shift = GEN11_RPCS_S_CNT_SHIFT;
+       } else {
+               s_mask = GEN8_RPCS_S_CNT_MASK;
+               s_shift = GEN8_RPCS_S_CNT_SHIFT;
+       }
+
+       val = *buf;
+       cnt = (val & s_mask) >> s_shift;
+       *rpcs = val;
+
+       i915_gem_object_unpin_map(obj);
+
+       return cnt;
+}
+
+static int
+__check_rpcs(const char *name, u32 rpcs, int slices, unsigned int expected,
+            const char *prefix, const char *suffix)
+{
+       if (slices == expected)
+               return 0;
+
+       if (slices < 0) {
+               pr_err("%s: %s read slice count failed with %d%s\n",
+                      name, prefix, slices, suffix);
+               return slices;
+       }
+
+       pr_err("%s: %s slice count %d is not %u%s\n",
+              name, prefix, slices, expected, suffix);
+
+       pr_info("RPCS=0x%x; %u%sx%u%s\n",
+               rpcs, slices,
+               (rpcs & GEN8_RPCS_S_CNT_ENABLE) ? "*" : "",
+               (rpcs & GEN8_RPCS_SS_CNT_MASK) >> GEN8_RPCS_SS_CNT_SHIFT,
+               (rpcs & GEN8_RPCS_SS_CNT_ENABLE) ? "*" : "");
+
+       return -EINVAL;
+}
+
+static int
+__sseu_finish(struct drm_i915_private *i915,
+             const char *name,
+             unsigned int flags,
+             struct i915_gem_context *ctx,
+             struct i915_gem_context *kctx,
+             struct intel_engine_cs *engine,
+             struct drm_i915_gem_object *obj,
+             unsigned int expected,
+             struct igt_spinner *spin)
+{
+       unsigned int slices =
+               hweight32(intel_device_default_sseu(i915).slice_mask);
+       u32 rpcs = 0;
+       int ret = 0;
+
+       if (flags & TEST_RESET) {
+               ret = i915_reset_engine(engine, "sseu");
+               if (ret)
+                       goto out;
+       }
+
+       ret = __read_slice_count(i915, ctx, engine, obj,
+                                flags & TEST_RESET ? NULL : spin, &rpcs);
+       ret = __check_rpcs(name, rpcs, ret, expected, "Context", "!");
+       if (ret)
+               goto out;
+
+       ret = __read_slice_count(i915, kctx, engine, obj, NULL, &rpcs);
+       ret = __check_rpcs(name, rpcs, ret, slices, "Kernel context", "!");
+
+out:
+       if (spin)
+               igt_spinner_end(spin);
+
+       if ((flags & TEST_IDLE) && ret == 0) {
+               ret = i915_gem_wait_for_idle(i915,
+                                            I915_WAIT_LOCKED,
+                                            MAX_SCHEDULE_TIMEOUT);
+               if (ret)
+                       return ret;
+
+               ret = __read_slice_count(i915, ctx, engine, obj, NULL, &rpcs);
+               ret = __check_rpcs(name, rpcs, ret, expected,
+                                  "Context", " after idle!");
+       }
+
+       return ret;
+}
+
+static int
+__sseu_test(struct drm_i915_private *i915,
+           const char *name,
+           unsigned int flags,
+           struct i915_gem_context *ctx,
+           struct intel_engine_cs *engine,
+           struct drm_i915_gem_object *obj,
+           struct intel_sseu sseu)
+{
+       struct igt_spinner *spin = NULL;
+       struct i915_gem_context *kctx;
+       int ret;
+
+       kctx = kernel_context(i915);
+       if (IS_ERR(kctx))
+               return PTR_ERR(kctx);
+
+       ret = __sseu_prepare(i915, name, flags, ctx, engine, &spin);
+       if (ret)
+               goto out;
+
+       ret = __i915_gem_context_reconfigure_sseu(ctx, engine, sseu);
+       if (ret)
+               goto out;
+
+       ret = __sseu_finish(i915, name, flags, ctx, kctx, engine, obj,
+                           hweight32(sseu.slice_mask), spin);
+
+out:
+       if (spin) {
+               igt_spinner_end(spin);
+               igt_spinner_fini(spin);
+               kfree(spin);
+       }
+
+       kernel_context_close(kctx);
+
+       return ret;
+}
+
+static int
+__igt_ctx_sseu(struct drm_i915_private *i915,
+              const char *name,
+              unsigned int flags)
+{
+       struct intel_sseu default_sseu = intel_device_default_sseu(i915);
+       struct intel_engine_cs *engine = i915->engine[RCS];
+       struct drm_i915_gem_object *obj;
+       struct i915_gem_context *ctx;
+       struct intel_sseu pg_sseu;
+       intel_wakeref_t wakeref;
+       struct drm_file *file;
+       int ret;
+
+       if (INTEL_GEN(i915) < 9)
+               return 0;
+
+       if (!RUNTIME_INFO(i915)->sseu.has_slice_pg)
+               return 0;
+
+       if (hweight32(default_sseu.slice_mask) < 2)
+               return 0;
+
+       /*
+        * Gen11 VME friendly power-gated configuration with half enabled
+        * sub-slices.
+        */
+       pg_sseu = default_sseu;
+       pg_sseu.slice_mask = 1;
+       pg_sseu.subslice_mask =
+               ~(~0 << (hweight32(default_sseu.subslice_mask) / 2));
+
+       pr_info("SSEU subtest '%s', flags=%x, def_slices=%u, pg_slices=%u\n",
+               name, flags, hweight32(default_sseu.slice_mask),
+               hweight32(pg_sseu.slice_mask));
+
+       file = mock_file(i915);
+       if (IS_ERR(file))
+               return PTR_ERR(file);
+
+       if (flags & TEST_RESET)
+               igt_global_reset_lock(i915);
+
+       mutex_lock(&i915->drm.struct_mutex);
+
+       ctx = i915_gem_create_context(i915, file->driver_priv);
+       if (IS_ERR(ctx)) {
+               ret = PTR_ERR(ctx);
+               goto out_unlock;
+       }
+
+       obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
+       if (IS_ERR(obj)) {
+               ret = PTR_ERR(obj);
+               goto out_unlock;
+       }
+
+       wakeref = intel_runtime_pm_get(i915);
+
+       /* First set the default mask. */
+       ret = __sseu_test(i915, name, flags, ctx, engine, obj, default_sseu);
+       if (ret)
+               goto out_fail;
+
+       /* Then set a power-gated configuration. */
+       ret = __sseu_test(i915, name, flags, ctx, engine, obj, pg_sseu);
+       if (ret)
+               goto out_fail;
+
+       /* Back to defaults. */
+       ret = __sseu_test(i915, name, flags, ctx, engine, obj, default_sseu);
+       if (ret)
+               goto out_fail;
+
+       /* One last power-gated configuration for the road. */
+       ret = __sseu_test(i915, name, flags, ctx, engine, obj, pg_sseu);
+       if (ret)
+               goto out_fail;
+
+out_fail:
+       if (igt_flush_test(i915, I915_WAIT_LOCKED))
+               ret = -EIO;
+
+       i915_gem_object_put(obj);
+
+       intel_runtime_pm_put(i915, wakeref);
+
+out_unlock:
+       mutex_unlock(&i915->drm.struct_mutex);
+
+       if (flags & TEST_RESET)
+               igt_global_reset_unlock(i915);
+
+       mock_file_free(i915, file);
+
+       if (ret)
+               pr_err("%s: Failed with %d!\n", name, ret);
+
+       return ret;
+}
+
+static int igt_ctx_sseu(void *arg)
+{
+       struct {
+               const char *name;
+               unsigned int flags;
+       } *phase, phases[] = {
+               { .name = "basic", .flags = 0 },
+               { .name = "idle", .flags = TEST_IDLE },
+               { .name = "busy", .flags = TEST_BUSY },
+               { .name = "busy-reset", .flags = TEST_BUSY | TEST_RESET },
+               { .name = "busy-idle", .flags = TEST_BUSY | TEST_IDLE },
+               { .name = "reset-idle", .flags = TEST_RESET | TEST_IDLE },
+       };
+       unsigned int i;
+       int ret = 0;
+
+       for (i = 0, phase = phases; ret == 0 && i < ARRAY_SIZE(phases);
+            i++, phase++)
+               ret = __igt_ctx_sseu(arg, phase->name, phase->flags);
+
+       return ret;
+}
+
 static int igt_ctx_readonly(void *arg)
 {
        struct drm_i915_private *i915 = arg;
@@ -1162,6 +1628,7 @@ int i915_gem_context_live_selftests(struct drm_i915_private *dev_priv)
                SUBTEST(live_nop_switch),
                SUBTEST(igt_ctx_exec),
                SUBTEST(igt_ctx_readonly),
+               SUBTEST(igt_ctx_sseu),
                SUBTEST(igt_vm_isolation),
        };
 
index 76b4f87fc85305ed3e02ed1d19fe736af7948183..6d766925ad045f5ec46167bff68d08df7fe9a9ec 100644 (file)
@@ -12,8 +12,9 @@
 selftest(sanitycheck, i915_live_sanitycheck) /* keep first (igt selfcheck) */
 selftest(uncore, intel_uncore_live_selftests)
 selftest(workarounds, intel_workarounds_live_selftests)
-selftest(requests, i915_request_live_selftests)
 selftest(timelines, i915_timeline_live_selftests)
+selftest(requests, i915_request_live_selftests)
+selftest(active, i915_active_live_selftests)
 selftest(objects, i915_gem_object_live_selftests)
 selftest(dmabuf, i915_gem_dmabuf_live_selftests)
 selftest(coherency, i915_gem_coherency_live_selftests)
index fb35f53c9ce342266ee4ca58b592a9eb9cf8b0d0..58144e024751fced7c19649b2569f72ab350c310 100644 (file)
@@ -4,6 +4,8 @@
  * Copyright © 2018 Intel Corporation
  */
 
+#include <linux/prime_numbers.h>
+
 #include "../i915_reset.h"
 
 #include "../i915_selftest.h"
@@ -405,6 +407,106 @@ err_wedged:
        goto err_client_b;
 }
 
+static int live_chain_preempt(void *arg)
+{
+       struct drm_i915_private *i915 = arg;
+       struct intel_engine_cs *engine;
+       struct preempt_client hi, lo;
+       enum intel_engine_id id;
+       intel_wakeref_t wakeref;
+       int err = -ENOMEM;
+
+       /*
+        * Build a chain AB...BA between two contexts (A, B) and request
+        * preemption of the last request. It should then complete before
+        * the previously submitted spinner in B.
+        */
+
+       if (!HAS_LOGICAL_RING_PREEMPTION(i915))
+               return 0;
+
+       mutex_lock(&i915->drm.struct_mutex);
+       wakeref = intel_runtime_pm_get(i915);
+
+       if (preempt_client_init(i915, &hi))
+               goto err_unlock;
+
+       if (preempt_client_init(i915, &lo))
+               goto err_client_hi;
+
+       for_each_engine(engine, i915, id) {
+               struct i915_sched_attr attr = {
+                       .priority = I915_USER_PRIORITY(I915_PRIORITY_MAX),
+               };
+               int count, i;
+
+               for_each_prime_number_from(count, 1, 32) { /* must fit ring! */
+                       struct i915_request *rq;
+
+                       rq = igt_spinner_create_request(&hi.spin,
+                                                       hi.ctx, engine,
+                                                       MI_ARB_CHECK);
+                       if (IS_ERR(rq))
+                               goto err_wedged;
+                       i915_request_add(rq);
+                       if (!igt_wait_for_spinner(&hi.spin, rq))
+                               goto err_wedged;
+
+                       rq = igt_spinner_create_request(&lo.spin,
+                                                       lo.ctx, engine,
+                                                       MI_ARB_CHECK);
+                       if (IS_ERR(rq))
+                               goto err_wedged;
+                       i915_request_add(rq);
+
+                       for (i = 0; i < count; i++) {
+                               rq = i915_request_alloc(engine, lo.ctx);
+                               if (IS_ERR(rq))
+                                       goto err_wedged;
+                               i915_request_add(rq);
+                       }
+
+                       rq = i915_request_alloc(engine, hi.ctx);
+                       if (IS_ERR(rq))
+                               goto err_wedged;
+                       i915_request_add(rq);
+                       engine->schedule(rq, &attr);
+
+                       igt_spinner_end(&hi.spin);
+                       if (i915_request_wait(rq, I915_WAIT_LOCKED, HZ / 5) < 0) {
+                               struct drm_printer p =
+                                       drm_info_printer(i915->drm.dev);
+
+                               pr_err("Failed to preempt over chain of %d\n",
+                                      count);
+                               intel_engine_dump(engine, &p,
+                                                 "%s\n", engine->name);
+                               goto err_wedged;
+                       }
+                       igt_spinner_end(&lo.spin);
+               }
+       }
+
+       err = 0;
+err_client_lo:
+       preempt_client_fini(&lo);
+err_client_hi:
+       preempt_client_fini(&hi);
+err_unlock:
+       if (igt_flush_test(i915, I915_WAIT_LOCKED))
+               err = -EIO;
+       intel_runtime_pm_put(i915, wakeref);
+       mutex_unlock(&i915->drm.struct_mutex);
+       return err;
+
+err_wedged:
+       igt_spinner_end(&hi.spin);
+       igt_spinner_end(&lo.spin);
+       i915_gem_set_wedged(i915);
+       err = -EIO;
+       goto err_client_lo;
+}
+
 static int live_preempt_hang(void *arg)
 {
        struct drm_i915_private *i915 = arg;
@@ -785,6 +887,7 @@ int intel_execlists_live_selftests(struct drm_i915_private *i915)
                SUBTEST(live_preempt),
                SUBTEST(live_late_preempt),
                SUBTEST(live_suppress_self_preempt),
+               SUBTEST(live_chain_preempt),
                SUBTEST(live_preempt_hang),
                SUBTEST(live_preempt_smoke),
        };
index cf39ccd9fc05e5b198b09077bd58aea646ffa52b..d2de9ece211820e320c067832f346ad3c2cab180 100644 (file)
@@ -15,7 +15,8 @@ void mock_timeline_init(struct i915_timeline *timeline, u64 context)
 
        spin_lock_init(&timeline->lock);
 
-       init_request_active(&timeline->last_request, NULL);
+       INIT_ACTIVE_REQUEST(&timeline->barrier);
+       INIT_ACTIVE_REQUEST(&timeline->last_request);
        INIT_LIST_HEAD(&timeline->requests);
 
        i915_syncmap_init(&timeline->sync);
index 696b750acd1dd05e66f6c8fad58219955cedeb46..6403728fe7784f54977b0c318d790ea886553a04 100644 (file)
@@ -275,7 +275,7 @@ static int intel_dsi_compute_config(struct intel_encoder *encoder,
        if (fixed_mode) {
                intel_fixed_panel_mode(fixed_mode, adjusted_mode);
 
-               if (HAS_GMCH_DISPLAY(dev_priv))
+               if (HAS_GMCH(dev_priv))
                        intel_gmch_panel_fitting(crtc, pipe_config,
                                                 conn_state->scaling_mode);
                else
@@ -1633,7 +1633,7 @@ static void intel_dsi_add_properties(struct intel_connector *connector)
                u32 allowed_scalers;
 
                allowed_scalers = BIT(DRM_MODE_SCALE_ASPECT) | BIT(DRM_MODE_SCALE_FULLSCREEN);
-               if (!HAS_GMCH_DISPLAY(dev_priv))
+               if (!HAS_GMCH(dev_priv))
                        allowed_scalers |= BIT(DRM_MODE_SCALE_CENTER);
 
                drm_connector_attach_scaling_mode_property(&connector->base,
index 298b2e197744bbc28782d1a853e1ee3577f02bee..397810fa2d33c95f69770bdf3563ea44213b40c6 100644 (file)
@@ -1486,9 +1486,73 @@ struct drm_i915_gem_context_param {
 #define   I915_CONTEXT_MAX_USER_PRIORITY       1023 /* inclusive */
 #define   I915_CONTEXT_DEFAULT_PRIORITY                0
 #define   I915_CONTEXT_MIN_USER_PRIORITY       -1023 /* inclusive */
+       /*
+        * When using the following param, value should be a pointer to
+        * drm_i915_gem_context_param_sseu.
+        */
+#define I915_CONTEXT_PARAM_SSEU                0x7
        __u64 value;
 };
 
+/**
+ * Context SSEU programming
+ *
+ * It may be necessary for either functional or performance reason to configure
+ * a context to run with a reduced number of SSEU (where SSEU stands for Slice/
+ * Sub-slice/EU).
+ *
+ * This is done by configuring SSEU configuration using the below
+ * @struct drm_i915_gem_context_param_sseu for every supported engine which
+ * userspace intends to use.
+ *
+ * Not all GPUs or engines support this functionality in which case an error
+ * code -ENODEV will be returned.
+ *
+ * Also, flexibility of possible SSEU configuration permutations varies between
+ * GPU generations and software imposed limitations. Requesting such a
+ * combination will return an error code of -EINVAL.
+ *
+ * NOTE: When perf/OA is active the context's SSEU configuration is ignored in
+ * favour of a single global setting.
+ */
+struct drm_i915_gem_context_param_sseu {
+       /*
+        * Engine class & instance to be configured or queried.
+        */
+       __u16 engine_class;
+       __u16 engine_instance;
+
+       /*
+        * Unused for now. Must be cleared to zero.
+        */
+       __u32 flags;
+
+       /*
+        * Mask of slices to enable for the context. Valid values are a subset
+        * of the bitmask value returned for I915_PARAM_SLICE_MASK.
+        */
+       __u64 slice_mask;
+
+       /*
+        * Mask of subslices to enable for the context. Valid values are a
+        * subset of the bitmask value return by I915_PARAM_SUBSLICE_MASK.
+        */
+       __u64 subslice_mask;
+
+       /*
+        * Minimum/Maximum number of EUs to enable per subslice for the
+        * context. min_eus_per_subslice must be inferior or equal to
+        * max_eus_per_subslice.
+        */
+       __u16 min_eus_per_subslice;
+       __u16 max_eus_per_subslice;
+
+       /*
+        * Unused for now. Must be cleared to zero.
+        */
+       __u32 rsvd;
+};
+
 enum drm_i915_oa_format {
        I915_OA_FORMAT_A13 = 1,     /* HSW only */
        I915_OA_FORMAT_A29,         /* HSW only */