[Intel-gfx] [PATCH 1/3] drm/1915: Add ring functions to save/restore context for per-ring reset

Siluvery, Arun arun.siluvery at intel.com
Mon Nov 11 15:58:54 CET 2013


From: "Siluvery, Arun" <arun.siluvery at intel.com>

Instead of full GPU reset, where possible a single ring can be reset
individually. This patch adds functions to save ring's current state and
it will be restored with the same state after reset. The state comprises
of a set of ring specific registers. The actual  hang detection
and recovery changes are in subsequent patches.

Signed-off-by: Siluvery, Arun <arun.siluvery at intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h         |   9 +-
 drivers/gpu/drm/i915/i915_reg.h         |   7 +
 drivers/gpu/drm/i915/intel_ringbuffer.c | 595 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_ringbuffer.h |  49 +++
 drivers/gpu/drm/i915/intel_uncore.c     |  25 ++
 5 files changed, 681 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index b98a7c8..b0a244d 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2423,6 +2423,7 @@ extern void intel_display_print_error_state(struct drm_i915_error_state_buf *e,
  */
 void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv);
 void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv);
+void gen6_gt_force_wake_restore(struct drm_device *dev);
 
 int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u8 mbox, u32 *val);
 int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val);
@@ -2456,13 +2457,13 @@ int vlv_freq_opcode(struct drm_i915_private *dev_priv, int val);
 
 #define I915_READ16(reg)	dev_priv->uncore.funcs.mmio_readw(dev_priv, (reg), true)
 #define I915_WRITE16(reg, val)	dev_priv->uncore.funcs.mmio_writew(dev_priv, (reg), (val), true)
-#define I915_READ16_NOTRACE(reg)	dev_priv->uncore.funcs.mmio_readw(dev_priv, (reg), false)
-#define I915_WRITE16_NOTRACE(reg, val)	dev_priv->uncore.funcs.mmio_writew(dev_priv, (reg), (val), false)
+#define I915_READ16_NOTRACE(reg)       dev_priv->uncore.funcs.mmio_readw(dev_priv, (reg), false)
+#define I915_WRITE16_NOTRACE(reg, val) dev_priv->uncore.funcs.mmio_writew(dev_priv, (reg), (val), false)
 
 #define I915_READ(reg)		dev_priv->uncore.funcs.mmio_readl(dev_priv, (reg), true)
 #define I915_WRITE(reg, val)	dev_priv->uncore.funcs.mmio_writel(dev_priv, (reg), (val), true)
-#define I915_READ_NOTRACE(reg)		dev_priv->uncore.funcs.mmio_readl(dev_priv, (reg), false)
-#define I915_WRITE_NOTRACE(reg, val)	dev_priv->uncore.funcs.mmio_writel(dev_priv, (reg), (val), false)
+#define I915_READ_NOTRACE(reg)         dev_priv->uncore.funcs.mmio_readl(dev_priv, (reg), false)
+#define I915_WRITE_NOTRACE(reg, val)   dev_priv->uncore.funcs.mmio_writel(dev_priv, (reg), (val), false)
 
 #define I915_WRITE64(reg, val)	dev_priv->uncore.funcs.mmio_writeq(dev_priv, (reg), (val), true)
 #define I915_READ64(reg)	dev_priv->uncore.funcs.mmio_readq(dev_priv, (reg), true)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 849e595..02e6ec6 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -33,6 +33,7 @@
 
 #define _MASKED_BIT_ENABLE(a) (((a) << 16) | (a))
 #define _MASKED_BIT_DISABLE(a) ((a) << 16)
+#define _MASKED_BIT_ENABLE_ALL(a) (0xFFFF0000 | (a))
 
 /* PCI config space */
 
@@ -104,6 +105,7 @@
 #define  GEN6_GRDOM_RENDER		(1 << 1)
 #define  GEN6_GRDOM_MEDIA		(1 << 2)
 #define  GEN6_GRDOM_BLT			(1 << 3)
+#define  GEN6_GRDOM_VEBOX		(1 << 4)
 
 #define RING_PP_DIR_BASE(ring)		((ring)->mmio_base+0x228)
 #define RING_PP_DIR_BASE_READ(ring)	((ring)->mmio_base+0x518)
@@ -642,6 +644,8 @@
 #define RING_SYNC_0(base)	((base)+0x40)
 #define RING_SYNC_1(base)	((base)+0x44)
 #define RING_SYNC_2(base)	((base)+0x48)
+#define RING_MI_MODE(base)	((base)+0x9c)
+#define RING_UHPTR(base)	((base)+0x134)
 #define GEN6_RVSYNC	(RING_SYNC_0(RENDER_RING_BASE))
 #define GEN6_RBSYNC	(RING_SYNC_1(RENDER_RING_BASE))
 #define GEN6_RVESYNC	(RING_SYNC_2(RENDER_RING_BASE))
@@ -789,6 +793,8 @@
 
 #define MI_MODE		0x0209c
 # define VS_TIMER_DISPATCH				(1 << 6)
+# define MODE_STOP					(1 << 8)
+# define MODE_IDLE					(1 << 9)
 # define MI_FLUSH_ENABLE				(1 << 12)
 # define ASYNC_FLIP_PERF_DISABLE			(1 << 14)
 
@@ -799,6 +805,7 @@
 #define GFX_MODE	0x02520
 #define GFX_MODE_GEN7	0x0229c
 #define RING_MODE_GEN7(ring)	((ring)->mmio_base+0x29c)
+#define RING_EXCC_GEN7(ring)	((ring)->mmio_base+0x028)
 #define   GFX_RUN_LIST_ENABLE		(1<<15)
 #define   GFX_TLB_INVALIDATE_ALWAYS	(1<<13)
 #define   GFX_SURFACE_FAULT_ENABLE	(1<<12)
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index b620337..cce29d0 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -46,6 +46,7 @@ void __intel_ring_advance(struct intel_ring_buffer *ring)
 	struct drm_i915_private *dev_priv = ring->dev->dev_private;
 
 	ring->tail &= ring->size - 1;
+
 	if (dev_priv->gpu_error.stop_rings & intel_ring_flag(ring))
 		return;
 	ring->write_tail(ring, ring->tail);
@@ -428,6 +429,21 @@ static void ring_setup_phys_status_page(struct intel_ring_buffer *ring)
 	I915_WRITE(HWS_PGA, addr);
 }
 
+void intel_ring_resample(struct intel_ring_buffer *ring)
+{
+	struct drm_device *dev = ring->dev;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+
+	if (!drm_core_check_feature(ring->dev, DRIVER_MODESET))
+		i915_kernel_lost_context(ring->dev);
+	else {
+		ring->head = I915_READ_HEAD(ring);
+		ring->tail = I915_READ_TAIL(ring) & TAIL_ADDR;
+		ring->space = ring_space(ring);
+		ring->last_retired_head = -1;
+	}
+}
+
 static int init_ring_common(struct intel_ring_buffer *ring)
 {
 	struct drm_device *dev = ring->dev;
@@ -508,6 +524,9 @@ static int init_ring_common(struct intel_ring_buffer *ring)
 
 	memset(&ring->hangcheck, 0, sizeof(ring->hangcheck));
 
+	if (ring->invalidate_tlb)
+		ring->invalidate_tlb(ring);
+
 out:
 	gen6_gt_force_wake_put(dev_priv);
 
@@ -1318,6 +1337,40 @@ static int init_phys_status_page(struct intel_ring_buffer *ring)
 	return 0;
 }
 
+/* gen6_ring_invalidate_tlb
+ * GFX soft resets do not invalidate TLBs, it is up to
+ * GFX driver to explicitly invalidate TLBs post reset.
+ */
+static int gen6_ring_invalidate_tlb(struct intel_ring_buffer *ring)
+{
+	struct drm_device *dev = ring->dev;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	u32 reg;
+	int ret;
+
+	if ((INTEL_INFO(dev)->gen < 6) || (!ring->stop) || (!ring->start))
+		return -EINVAL;
+
+	/* stop the ring before sync_flush */
+	ret = ring->stop(ring);
+	if ((ret) && (ret != -EALREADY))
+		DRM_ERROR("%s: unable to stop the ring\n", ring->name);
+
+	/* Invalidate TLB */
+	reg = RING_INSTPM(ring->mmio_base);
+	I915_WRITE(reg, _MASKED_BIT_ENABLE(INSTPM_TLB_INVALIDATE |
+					   INSTPM_SYNC_FLUSH));
+	if (wait_for((I915_READ(reg) & INSTPM_SYNC_FLUSH) == 0, 1000))
+		DRM_ERROR("%s: wait for SyncFlush to complete timed out\n",
+			  ring->name);
+
+	/* only start if stop was sucessfull */
+	if (!ret)
+		ring->start(ring);
+
+	return 0;
+}
+
 static int intel_init_ring_buffer(struct drm_device *dev,
 				  struct intel_ring_buffer *ring)
 {
@@ -1801,6 +1854,516 @@ gen6_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
 	return 0;
 }
 
+static int
+gen6_ring_stop(struct intel_ring_buffer *ring)
+{
+	struct drm_device *dev = ring->dev;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+
+	/* check if ring is already stopped */
+	if (I915_READ_MODE(ring) & MODE_STOP)
+		return -EALREADY;
+
+	/* Request the ring to go idle */
+	I915_WRITE_MODE(ring, _MASKED_BIT_ENABLE(MODE_STOP));
+
+	/* Wait for idle */
+	if (wait_for_atomic((I915_READ_MODE(ring) & MODE_IDLE) != 0, 1000)) {
+		DRM_ERROR("%s :timed out trying to stop ring", ring->name);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int
+gen6_ring_start(struct intel_ring_buffer *ring)
+{
+	struct drm_device *dev = ring->dev;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	uint32_t mode;
+
+	/* Clear the MI_MODE stop bit */
+	I915_WRITE_MODE(ring, _MASKED_BIT_DISABLE(MODE_STOP));
+	mode = I915_READ_MODE(ring);    /* Barrier read */
+
+	return 0;
+}
+
+int intel_ring_disable(struct intel_ring_buffer *ring)
+{
+	if (ring && ring->disable)
+		return ring->disable(ring);
+	else {
+		DRM_ERROR("ring disable not supported\n");
+		return -EINVAL;
+	}
+}
+
+static int
+gen6_ring_disable(struct intel_ring_buffer *ring)
+{
+	struct drm_device *dev = ring->dev;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	uint32_t ring_ctl;
+	uint32_t mi_mode;
+	uint32_t retries = 10000;
+
+	/* Request the ring to go idle */
+	I915_WRITE_MODE(ring, _MASKED_BIT_ENABLE(MODE_STOP));
+
+	/* Wait for idle */
+	do {
+		mi_mode = I915_READ_MODE(ring);
+	} while (retries-- && !(mi_mode & MODE_IDLE));
+
+	if (retries == 0) {
+		DRM_ERROR("timed out trying to disable ring %d\n", ring->id);
+		return -ETIMEDOUT;
+	}
+
+	/* Disable the ring */
+	ring_ctl = I915_READ_CTL(ring);
+	ring_ctl &= (RING_NR_PAGES | RING_REPORT_MASK);
+	I915_WRITE_CTL(ring, ring_ctl);
+	ring_ctl = I915_READ_CTL(ring);  /* Barrier read */
+
+	return ((ring_ctl & RING_VALID) == 0) ? 0 : -EIO;
+}
+
+int intel_ring_enable(struct intel_ring_buffer *ring)
+{
+	if (ring && ring->enable)
+		return ring->enable(ring);
+	else {
+		DRM_ERROR("ring enable not supported\n");
+		return -EINVAL;
+	}
+}
+
+static int
+gen6_ring_enable(struct intel_ring_buffer *ring)
+{
+	struct drm_device *dev = ring->dev;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	uint32_t ring_ctl;
+	uint32_t mode;
+
+	/* Clear the MI_MODE stop bit */
+	I915_WRITE_MODE(ring, _MASKED_BIT_DISABLE(MODE_STOP));
+	mode = I915_READ_MODE(ring);    /* Barrier read */
+
+	/* Enable the ring */
+	ring_ctl = I915_READ_CTL(ring);
+	ring_ctl &= (RING_NR_PAGES | RING_REPORT_MASK);
+	I915_WRITE_CTL(ring, ring_ctl | RING_VALID);
+	ring_ctl = I915_READ_CTL(ring); /* Barrier read */
+
+	return ((ring_ctl & RING_VALID) == 0) ? -EIO : 0;
+}
+
+
+/* raw read/write, no need for forcewake etc. */
+#define __raw_i915_read32(dev_priv__, reg__) readl((dev_priv__)->regs + (reg__))
+#define __raw_i915_write32(dev_priv__, reg__, val__) writel(val__, (dev_priv__)->regs + (reg__))
+
+int intel_ring_reset(struct intel_ring_buffer *ring)
+{
+	if (ring && ring->reset) {
+		int ret = ring->reset(ring);
+		/* invalidate TLB, if we got reset due to TLB giving
+		 * stale addr (after soft reset) for HW status page,
+		 * resulting in seqno not getting updated.
+		 */
+		if (ring->invalidate_tlb)
+			ring->invalidate_tlb(ring);
+		return ret;
+	} else {
+		DRM_ERROR("ring reset not supported\n");
+		return -EINVAL;
+	}
+}
+
+static int
+gen6_ring_reset(struct intel_ring_buffer *ring)
+{
+	struct drm_device *dev = ring->dev;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	int ret = 0;
+	char *reset_event[2];
+	unsigned long irqflags;
+	reset_event[1] = NULL;
+
+	/* Hold uncore.lock across reset to prevent any register access
+	* with forcewake not set correctly
+	*/
+	spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
+
+	switch (ring->id) {
+	case RCS:
+		/* GEN6_GDRST is not in the gt power well, no need to check
+		* for fifo space for the write or forcewake the chip for
+		* the read
+		*/
+		__raw_i915_write32(dev_priv, GEN6_GDRST, GEN6_GRDOM_RENDER);
+
+		/* Spin waiting for the device to ack the reset request */
+		ret = wait_for((__raw_i915_read32(dev_priv, GEN6_GDRST)
+					& GEN6_GRDOM_RENDER) == 0, 500);
+		DRM_DEBUG("RCS Reset\n");
+		break;
+
+
+	case BCS:
+		__raw_i915_write32(dev_priv, GEN6_GDRST, GEN6_GRDOM_BLT);
+
+		/* Spin waiting for the device to ack the reset request */
+		ret = wait_for((__raw_i915_read32(dev_priv, GEN6_GDRST)
+				& GEN6_GRDOM_BLT) == 0, 500);
+		DRM_DEBUG("BCS Reset\n");
+		break;
+
+
+	case VCS:
+		__raw_i915_write32(dev_priv, GEN6_GDRST, GEN6_GRDOM_MEDIA);
+
+		/* Spin waiting for the device to ack the reset request */
+		ret = wait_for((__raw_i915_read32(dev_priv, GEN6_GDRST)
+					& GEN6_GRDOM_MEDIA) == 0, 500);
+		DRM_DEBUG("VCS Reset\n");
+		break;
+
+	case VECS:
+		__raw_i915_write32(dev_priv, GEN6_GDRST, GEN6_GRDOM_VEBOX);
+
+		/* Spin waiting for the device to ack the reset request */
+		ret = wait_for((__raw_i915_read32(dev_priv, GEN6_GDRST)
+					& GEN6_GRDOM_VEBOX) == 0, 500);
+		DRM_DEBUG("VECS Reset\n");
+		break;
+
+	default:
+		DRM_ERROR("Unexpected ring ID\n");
+		break;
+	}
+
+	/* Request power management to restore the power state based
+	* on the current reference count(s)*/
+	gen6_gt_force_wake_restore(dev);
+
+	spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
+
+	/* Do uevent outside of spinlock as uevent can sleep */
+	reset_event[0] = kasprintf(GFP_KERNEL, "RESET RING=%d", ring->id);
+	kobject_uevent_env(&dev->primary->kdev->kobj,
+			KOBJ_CHANGE, reset_event);
+	kfree(reset_event[0]);
+
+	return ret;
+}
+
+int intel_ring_save(struct intel_ring_buffer *ring, u32 flags)
+{
+	if (ring && ring->save)
+		return ring->save(ring, ring->saved_state,
+				  I915_RING_CONTEXT_SIZE, flags);
+	else {
+		DRM_ERROR("ring save not supported\n");
+		return -EINVAL;
+	}
+}
+
+static int
+gen6_ring_save(struct intel_ring_buffer *ring, uint32_t *data, uint32_t max,
+		u32 flags)
+{
+	struct drm_device *dev = ring->dev;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	uint32_t idx = 0;
+	uint32_t gen = INTEL_INFO(dev)->gen;
+	uint32_t head;
+	uint32_t tail;
+	uint32_t head_addr;
+	uint32_t tail_addr;
+	int clamp_to_tail = 0;
+
+	/* Ring save only added for gen >= 7 */
+	WARN_ON(gen < 7);
+
+	/* Save common registers */
+	if (max < COMMON_RING_CTX_SIZE)
+		return -EINVAL;
+
+	head = I915_READ_HEAD(ring);
+	tail = I915_READ_TAIL(ring);
+
+	head_addr = head & HEAD_ADDR;
+	tail_addr = tail & TAIL_ADDR;
+
+	if (flags & FORCE_ADVANCE) {
+		/* The head must always chase the tail.
+		* If the tail is beyond the head then do not allow
+		* the head to overtake it. If the tail is less than
+		* the head then the tail has already wrapped and
+		* there is no problem in advancing the head or even
+		* wrapping the head back to 0 as worst case it will
+		* become equal to tail */
+		if (head_addr <= tail_addr)
+			clamp_to_tail = 1;
+
+		/* Force head to next QWORD boundary */
+		head_addr &= ~0x7;
+		head_addr += 8;
+
+		if (clamp_to_tail && (head_addr > tail_addr)) {
+			head_addr = tail_addr;
+		} else if (head_addr >= ring->size) {
+			/* Wrap head back to start if it exceeds ring size*/
+			head_addr = 0;
+		}
+
+		/* Update the register */
+		head &= ~HEAD_ADDR;
+		head |= (head_addr & HEAD_ADDR);
+
+		DRM_DEBUG("Forced head to 0x%08x\n", head);
+	} else if (head & 0x7) {
+		/* Ensure head pointer is pointing to a QWORD boundary */
+		DRM_DEBUG("Rounding up head 0x%08x\n", head);
+		head += 0x7;
+		head &= ~0x7;
+	}
+
+	/* Saved with enable = 0 */
+	data[idx++] = I915_READ_CTL(ring) & (RING_NR_PAGES | RING_REPORT_MASK);
+
+	data[idx++] = (flags & RESET_HEAD_TAIL) ? 0 : tail;
+
+
+	if (flags & RESET_HEAD_TAIL) {
+		/* Save head as 0 so head is reset on restore */
+		data[idx++] = 0;
+	} else {
+		/* Head will already have advanced to next instruction location
+		* even if the current instruction caused a hang, so we just
+		* save the current value as the value to restart at */
+		data[idx++] = head;
+	}
+
+	data[idx++] = I915_READ_START(ring);
+
+	/* Workaround for reading DCLV registers for gen < 8 */
+	data[idx++] = (gen < 8) ?
+			I915_READ(RING_PP_DIR_DCLV(&dev_priv->ring[VCS]))
+			: I915_READ(RING_PP_DIR_DCLV(ring));
+	data[idx++] = (gen < 8) ?
+			 I915_READ(RING_PP_DIR_BASE(&dev_priv->ring[VCS]))
+			: I915_READ(RING_PP_DIR_BASE(ring));
+
+	switch (ring->id) {
+	case RCS:
+		if (max < (COMMON_RING_CTX_SIZE + RCS_RING_CTX_SIZE))
+			return -EINVAL;
+
+		data[idx++] = I915_READ(RENDER_HWS_PGA_GEN7);
+		data[idx++] = I915_READ(RING_UHPTR(ring->mmio_base));
+		data[idx++] = I915_READ(RING_INSTPM(ring->mmio_base));
+		data[idx++] = I915_READ(RING_IMR(ring->mmio_base));
+		data[idx++] = I915_READ(CACHE_MODE_1);
+		data[idx++] = I915_READ(RING_MI_MODE(ring->mmio_base));
+		data[idx++] = I915_READ(FW_BLC2);
+		data[idx++] = I915_READ(_3D_CHICKEN3);
+		data[idx++] = I915_READ(GAM_ECOCHK);
+		data[idx++] = I915_READ(RING_MODE_GEN7(ring));
+		data[idx++] = I915_READ(GEN6_RBSYNC);
+		data[idx++] = I915_READ(GEN7_FF_THREAD_MODE);
+		data[idx++] = I915_READ(MI_ARB_STATE);
+		break;
+
+	case VCS:
+		if (max < (COMMON_RING_CTX_SIZE + VCS_RING_CTX_SIZE))
+			return -EINVAL;
+
+		data[idx++] = I915_READ(BSD_HWS_PGA_GEN7);
+		data[idx++] = I915_READ(RING_MI_MODE(ring->mmio_base));
+		data[idx++] = I915_READ(RING_IMR(ring->mmio_base));
+		data[idx++] = I915_READ(RING_UHPTR(ring->mmio_base));
+		data[idx++] = I915_READ(RING_INSTPM(ring->mmio_base));
+		data[idx++] = I915_READ(RING_EXCC_GEN7(ring));
+		data[idx++] = I915_READ(GAC_ECO_BITS);
+		data[idx++] = I915_READ(RING_MODE_GEN7(ring));
+		data[idx++] = I915_READ(GEN6_VRSYNC);
+		data[idx++] = I915_READ(RING_MAX_IDLE(ring->mmio_base));
+		break;
+
+	case BCS:
+		if (max < (COMMON_RING_CTX_SIZE + BCS_RING_CTX_SIZE))
+			return -EINVAL;
+
+		data[idx++] = I915_READ(BLT_HWS_PGA_GEN7);
+		data[idx++] = I915_READ(RING_MI_MODE(ring->mmio_base));
+		data[idx++] = I915_READ(RING_IMR(ring->mmio_base));
+		data[idx++] = I915_READ(RING_UHPTR(ring->mmio_base));
+		data[idx++] = I915_READ(RING_INSTPM(ring->mmio_base));
+		data[idx++] = I915_READ(RING_EXCC_GEN7(ring));
+		data[idx++] = I915_READ(GAB_CTL);
+		data[idx++] = I915_READ(RING_MODE_GEN7(ring));
+		data[idx++] = I915_READ(GEN6_BRSYNC);
+		data[idx++] = I915_READ(GEN6_BVSYNC);
+		data[idx++] = I915_READ(RING_MAX_IDLE(ring->mmio_base));
+		break;
+
+	case VECS:
+		if (max < (COMMON_RING_CTX_SIZE + VECS_RING_CTX_SIZE))
+			return -EINVAL;
+
+		data[idx++] = I915_READ(VEBOX_HWS_PGA_GEN7);
+		data[idx++] = I915_READ(RING_MI_MODE(ring->mmio_base));
+		data[idx++] = I915_READ(RING_IMR(ring->mmio_base));
+		data[idx++] = I915_READ(RING_UHPTR(ring->mmio_base));
+		data[idx++] = I915_READ(RING_INSTPM(ring->mmio_base));
+		data[idx++] = I915_READ(RING_EXCC_GEN7(ring));
+		data[idx++] = I915_READ(RING_MODE_GEN7(ring));
+		data[idx++] = I915_READ(GEN6_VEVSYNC);
+		break;
+	}
+
+	return 0;
+}
+
+int intel_ring_restore(struct intel_ring_buffer *ring)
+{
+	if (ring && ring->restore)
+		return ring->restore(ring, ring->saved_state,
+				I915_RING_CONTEXT_SIZE);
+	else {
+		DRM_ERROR("ring restore not supported\n");
+		return -EINVAL;
+	}
+}
+
+
+static int
+gen6_ring_restore(struct intel_ring_buffer *ring, uint32_t *data,
+			uint32_t max)
+{
+	struct drm_device *dev = ring->dev;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	uint32_t idx = 0;
+	uint32_t x;
+
+	/* NOTE: Registers are restored in reverse order from when
+	*        they were saved. */
+	switch (ring->id) {
+	case RCS:
+		if (max < (COMMON_RING_CTX_SIZE + RCS_RING_CTX_SIZE))
+			return -EINVAL;
+
+		idx = COMMON_RING_CTX_SIZE + RCS_RING_CTX_SIZE - 1;
+
+		I915_WRITE(MI_ARB_STATE, _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(GEN7_FF_THREAD_MODE, data[idx--]);
+		I915_WRITE(GEN6_RBSYNC, data[idx--]);
+		I915_WRITE(RING_MODE_GEN7(ring),
+			_MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(GAM_ECOCHK, data[idx--]);
+		I915_WRITE(_3D_CHICKEN3, _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(FW_BLC2, _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(RING_MI_MODE(ring->mmio_base),
+			   _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(CACHE_MODE_1, _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(RING_IMR(ring->mmio_base), data[idx--]);
+		I915_WRITE(RING_INSTPM(ring->mmio_base),
+			   _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(RING_UHPTR(ring->mmio_base), data[idx--]);
+		I915_WRITE(RENDER_HWS_PGA_GEN7, data[idx--]);
+		break;
+
+	case VCS:
+		if (max < (COMMON_RING_CTX_SIZE + VCS_RING_CTX_SIZE))
+			return -EINVAL;
+
+		idx = COMMON_RING_CTX_SIZE + VCS_RING_CTX_SIZE - 1;
+
+		I915_WRITE(RING_MAX_IDLE(ring->mmio_base), data[idx--]);
+		I915_WRITE(GEN6_VRSYNC, data[idx--]);
+		I915_WRITE(RING_MODE_GEN7(ring),
+			   _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(GAC_ECO_BITS, data[idx--]);
+		I915_WRITE(RING_EXCC_GEN7(ring),
+			   _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(RING_INSTPM(ring->mmio_base),
+			   _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(RING_UHPTR(ring->mmio_base), data[idx--]);
+		I915_WRITE(RING_IMR(ring->mmio_base), data[idx--]);
+		I915_WRITE(RING_MI_MODE(ring->mmio_base),
+			   _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(BSD_HWS_PGA_GEN7, data[idx--]);
+		break;
+
+	case BCS:
+		if (max < (COMMON_RING_CTX_SIZE + BCS_RING_CTX_SIZE))
+			return -EINVAL;
+
+		idx = COMMON_RING_CTX_SIZE + BCS_RING_CTX_SIZE - 1;
+
+		I915_WRITE(RING_MAX_IDLE(ring->mmio_base), data[idx--]);
+		I915_WRITE(GEN6_BVSYNC, data[idx--]);
+		I915_WRITE(GEN6_BRSYNC, data[idx--]);
+		I915_WRITE(RING_MODE_GEN7(ring),
+			   _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(GAB_CTL, data[idx--]);
+		I915_WRITE(RING_EXCC_GEN7(ring),
+			   _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(RING_INSTPM(ring->mmio_base),
+			   _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(RING_UHPTR(ring->mmio_base), data[idx--]);
+		I915_WRITE(RING_IMR(ring->mmio_base), data[idx--]);
+		I915_WRITE(RING_MI_MODE(ring->mmio_base),
+			   _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(BLT_HWS_PGA_GEN7, data[idx--]);
+		break;
+
+	case VECS:
+		if (max < (COMMON_RING_CTX_SIZE + VECS_RING_CTX_SIZE))
+			return -EINVAL;
+
+		idx = COMMON_RING_CTX_SIZE + VECS_RING_CTX_SIZE - 1;
+
+		I915_WRITE(GEN6_VEVSYNC, data[idx--]);
+		I915_WRITE(RING_MODE_GEN7(ring),
+			   _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(RING_EXCC_GEN7(ring),
+			   _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(RING_INSTPM(ring->mmio_base),
+			   _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(RING_UHPTR(ring->mmio_base), data[idx--]);
+		I915_WRITE(RING_IMR(ring->mmio_base), data[idx--]);
+		I915_WRITE(RING_MI_MODE(ring->mmio_base),
+			   _MASKED_BIT_ENABLE_ALL(data[idx--]));
+		I915_WRITE(VEBOX_HWS_PGA_GEN7, data[idx--]);
+		break;
+	}
+
+	/* Restore common registers */
+	if (max < COMMON_RING_CTX_SIZE)
+		return -EINVAL;
+
+	idx = COMMON_RING_CTX_SIZE - 1;
+
+	I915_WRITE(RING_PP_DIR_BASE(ring), data[idx--]);
+	I915_WRITE(RING_PP_DIR_DCLV(ring), data[idx--]);
+
+	/* Write ring base address before head/tail as it clears head to 0 */
+	I915_WRITE_START(ring, data[idx--]);
+	x = I915_READ_START(ring);
+	I915_WRITE_HEAD(ring, data[idx--]);
+	I915_WRITE_TAIL(ring, data[idx--]);
+	I915_WRITE_CTL(ring, data[idx--]);
+
+	return 0;
+}
+
 /* Blitter support (SandyBridge+) */
 
 static int gen6_ring_flush(struct intel_ring_buffer *ring,
@@ -1869,6 +2432,14 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
 		ring->get_seqno = gen6_ring_get_seqno;
 		ring->set_seqno = ring_set_seqno;
 		ring->sync_to = gen6_ring_sync;
+		ring->enable = gen6_ring_enable;
+		ring->disable = gen6_ring_disable;
+		ring->start = gen6_ring_start;
+		ring->stop = gen6_ring_stop;
+		ring->reset = gen6_ring_reset;
+		ring->save = gen6_ring_save;
+		ring->restore = gen6_ring_restore;
+		ring->invalidate_tlb = gen6_ring_invalidate_tlb;
 		ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_INVALID;
 		ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_RV;
 		ring->semaphore_register[BCS] = MI_SEMAPHORE_SYNC_RB;
@@ -2045,6 +2616,14 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev)
 				gen6_ring_dispatch_execbuffer;
 		}
 		ring->sync_to = gen6_ring_sync;
+		ring->enable = gen6_ring_enable;
+		ring->disable = gen6_ring_disable;
+		ring->start = gen6_ring_start;
+		ring->stop = gen6_ring_stop;
+		ring->reset = gen6_ring_reset;
+		ring->save = gen6_ring_save;
+		ring->restore = gen6_ring_restore;
+		ring->invalidate_tlb = gen6_ring_invalidate_tlb;
 		ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_VR;
 		ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_INVALID;
 		ring->semaphore_register[BCS] = MI_SEMAPHORE_SYNC_VB;
@@ -2102,6 +2681,14 @@ int intel_init_blt_ring_buffer(struct drm_device *dev)
 		ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer;
 	}
 	ring->sync_to = gen6_ring_sync;
+	ring->enable = gen6_ring_enable;
+	ring->disable = gen6_ring_disable;
+	ring->start = gen6_ring_start;
+	ring->stop = gen6_ring_stop;
+	ring->reset = gen6_ring_reset;
+	ring->save = gen6_ring_save;
+	ring->restore = gen6_ring_restore;
+	ring->invalidate_tlb = gen6_ring_invalidate_tlb;
 	ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_BR;
 	ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_BV;
 	ring->semaphore_register[BCS] = MI_SEMAPHORE_SYNC_INVALID;
@@ -2143,6 +2730,14 @@ int intel_init_vebox_ring_buffer(struct drm_device *dev)
 		ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer;
 	}
 	ring->sync_to = gen6_ring_sync;
+	ring->enable = gen6_ring_enable;
+	ring->disable = gen6_ring_disable;
+	ring->start = gen6_ring_start;
+	ring->stop = gen6_ring_stop;
+	ring->reset = gen6_ring_reset;
+	ring->save = gen6_ring_save;
+	ring->restore = gen6_ring_restore;
+	ring->invalidate_tlb = gen6_ring_invalidate_tlb;
 	ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_VER;
 	ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_VEV;
 	ring->semaphore_register[BCS] = MI_SEMAPHORE_SYNC_VEB;
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index 71a73f4..cd96ad9 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -18,6 +18,25 @@ struct  intel_hw_status_page {
 	struct		drm_i915_gem_object *obj;
 };
 
+/* These values must match the requirements of the ring save/restore functions
+ * which may need to change for different versions of the chip */
+#define COMMON_RING_CTX_SIZE 6
+
+#define RCS_RING_CTX_SIZE 13
+#define VCS_RING_CTX_SIZE 10
+#define BCS_RING_CTX_SIZE 11
+#define VECS_RING_CTX_SIZE 8
+
+#define MAX_CTX(a, b) (((a) > (b)) ? (a) : (b))
+
+/* Largest of individual rings + common*/
+#define I915_RING_CONTEXT_SIZE (COMMON_RING_CTX_SIZE + \
+				MAX_CTX(MAX_CTX(RCS_RING_CTX_SIZE,  \
+						VCS_RING_CTX_SIZE), \
+					MAX_CTX(BCS_RING_CTX_SIZE,  \
+						VECS_RING_CTX_SIZE)))
+
+
 #define I915_READ_TAIL(ring) I915_READ(RING_TAIL((ring)->mmio_base))
 #define I915_WRITE_TAIL(ring, val) I915_WRITE(RING_TAIL((ring)->mmio_base), val)
 
@@ -33,6 +52,13 @@ struct  intel_hw_status_page {
 #define I915_READ_IMR(ring) I915_READ(RING_IMR((ring)->mmio_base))
 #define I915_WRITE_IMR(ring, val) I915_WRITE(RING_IMR((ring)->mmio_base), val)
 
+#define I915_READ_MODE(ring) I915_READ(RING_MI_MODE((ring)->mmio_base))
+#define I915_WRITE_MODE(ring, val) \
+	I915_WRITE(RING_MI_MODE((ring)->mmio_base), val)
+
+#define RESET_HEAD_TAIL   0x1
+#define FORCE_ADVANCE     0x2
+
 enum intel_ring_hangcheck_action {
 	HANGCHECK_IDLE = 0,
 	HANGCHECK_WAIT,
@@ -115,6 +141,18 @@ struct  intel_ring_buffer {
 				   struct intel_ring_buffer *to,
 				   u32 seqno);
 
+	int		(*enable)(struct intel_ring_buffer *ring);
+	int		(*disable)(struct intel_ring_buffer *ring);
+	int		(*start)(struct intel_ring_buffer *ring);
+	int		(*stop)(struct intel_ring_buffer *ring);
+	int		(*reset)(struct intel_ring_buffer *ring);
+	int		(*save)(struct intel_ring_buffer *ring,
+				uint32_t *data, uint32_t max,
+				u32 flags);
+	int		(*restore)(struct intel_ring_buffer *ring,
+				uint32_t *data, uint32_t max);
+	int		(*invalidate_tlb)(struct intel_ring_buffer *ring);
+
 	/* our mbox written by others */
 	u32		semaphore_register[I915_NUM_RINGS];
 	/* mboxes this ring signals to */
@@ -155,6 +193,9 @@ struct  intel_ring_buffer {
 	struct i915_hw_context *default_context;
 	struct i915_hw_context *last_context;
 
+	/* Area large enough to store all the register
+	* data associated with this ring */
+	u32 saved_state[I915_RING_CONTEXT_SIZE];
 	struct intel_ring_hangcheck hangcheck;
 
 	struct {
@@ -229,6 +270,7 @@ intel_write_status_page(struct intel_ring_buffer *ring,
 #define I915_GEM_HWS_INDEX		0x20
 #define I915_GEM_HWS_SCRATCH_INDEX	0x30
 #define I915_GEM_HWS_SCRATCH_ADDR (I915_GEM_HWS_SCRATCH_INDEX << MI_STORE_DWORD_INDEX_SHIFT)
+#define I915_GEM_PGFLIP_INDEX           0x38
 
 void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring);
 
@@ -278,4 +320,11 @@ static inline void i915_trace_irq_get(struct intel_ring_buffer *ring, u32 seqno)
 /* DRI warts */
 int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size);
 
+void intel_ring_resample(struct intel_ring_buffer *ring);
+int intel_ring_disable(struct intel_ring_buffer *ring);
+int intel_ring_enable(struct intel_ring_buffer *ring);
+int intel_ring_reset(struct intel_ring_buffer *ring);
+int intel_ring_save(struct intel_ring_buffer *ring, u32 flags);
+int intel_ring_restore(struct intel_ring_buffer *ring);
+
 #endif /* _INTEL_RINGBUFFER_H_ */
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index f9883ce..5349215 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -312,6 +312,31 @@ void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
 	spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
 }
 
+void gen6_gt_force_wake_restore(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	/* Restore the current expected force wake state with the
+	* hardware. This may be required following a reset.
+	*
+	* WARNING: Caller *MUST* hold uncore.lock whilst calling this.
+	*
+	* uncore.lock isn't taken in this function to allow the caller the
+	* flexibility to do other work immediately before/after
+	* whilst holding the lock */
+
+	intel_uncore_forcewake_reset(dev);
+
+	/* If reset with a user forcewake, try to restore, otherwise turn it off */
+	if (dev_priv->uncore.forcewake_count)
+		dev_priv->uncore.funcs.force_wake_get(dev_priv);
+	else
+		dev_priv->uncore.funcs.force_wake_put(dev_priv);
+
+	/* Restore fifo count */
+	dev_priv->uncore.fifo_count = __raw_i915_read32(dev_priv, GT_FIFO_FREE_ENTRIES);
+}
+
 /* We give fast paths for the really cool registers */
 #define NEEDS_FORCE_WAKE(dev_priv, reg) \
 	 ((reg) < 0x40000 && (reg) != FORCEWAKE)
-- 
1.8.4




More information about the Intel-gfx mailing list