aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2009-09-05 13:07:06 -0400
committerEric Anholt <eric@anholt.net>2009-09-06 14:29:06 -0400
commit0ef82af7253c1929a3995f271b8b0db462d1a0c3 (patch)
treefb34eee409dffa5bfef725b00d38d6d54b8d31c8 /drivers/gpu/drm
parent5e17ee74b541b56b5d4cfab6502a5116f224e32c (diff)
drm/i915: Pad ringbuffer with NOOPs before wrapping
According to the docs, the ringbuffer is not allowed to wrap in the middle of an instruction. G45 PRM, Vol 1b, p101: While the “free space” wrap may allow commands to be wrapped around the end of the Ring Buffer, the wrap should only occur between commands. Padding (with NOP) may be required to follow this restriction. Do as commanded. [Having seen bug reports where there is evidence of split commands, but apparently the GPU has continued on merrily before a bizarre and untimely death, this may or may not fix a few random hangs.] Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> CC: Eric Anholt <eric@anholt.net> Signed-off-by: Eric Anholt <eric@anholt.net>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c4
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c29
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h43
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c1
4 files changed, 50 insertions, 27 deletions
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 8f28325bc5ef..1e3bdcee863c 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -333,15 +333,13 @@ static int i915_ringbuffer_info(struct seq_file *m, void *data)
333 struct drm_info_node *node = (struct drm_info_node *) m->private; 333 struct drm_info_node *node = (struct drm_info_node *) m->private;
334 struct drm_device *dev = node->minor->dev; 334 struct drm_device *dev = node->minor->dev;
335 drm_i915_private_t *dev_priv = dev->dev_private; 335 drm_i915_private_t *dev_priv = dev->dev_private;
336 unsigned int head, tail, mask; 336 unsigned int head, tail;
337 337
338 head = I915_READ(PRB0_HEAD) & HEAD_ADDR; 338 head = I915_READ(PRB0_HEAD) & HEAD_ADDR;
339 tail = I915_READ(PRB0_TAIL) & TAIL_ADDR; 339 tail = I915_READ(PRB0_TAIL) & TAIL_ADDR;
340 mask = dev_priv->ring.tail_mask;
341 340
342 seq_printf(m, "RingHead : %08x\n", head); 341 seq_printf(m, "RingHead : %08x\n", head);
343 seq_printf(m, "RingTail : %08x\n", tail); 342 seq_printf(m, "RingTail : %08x\n", tail);
344 seq_printf(m, "RingMask : %08x\n", mask);
345 seq_printf(m, "RingSize : %08lx\n", dev_priv->ring.Size); 343 seq_printf(m, "RingSize : %08lx\n", dev_priv->ring.Size);
346 seq_printf(m, "Acthd : %08x\n", I915_READ(IS_I965G(dev) ? ACTHD_I965 : ACTHD)); 344 seq_printf(m, "Acthd : %08x\n", I915_READ(IS_I965G(dev) ? ACTHD_I965 : ACTHD));
347 345
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 50d1f782768c..f135bdcc6d5b 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -80,6 +80,34 @@ int i915_wait_ring(struct drm_device * dev, int n, const char *caller)
80 return -EBUSY; 80 return -EBUSY;
81} 81}
82 82
83/* As a ringbuffer is only allowed to wrap between instructions, fill
84 * the tail with NOOPs.
85 */
86int i915_wrap_ring(struct drm_device *dev)
87{
88 drm_i915_private_t *dev_priv = dev->dev_private;
89 volatile unsigned int *virt;
90 int rem;
91
92 rem = dev_priv->ring.Size - dev_priv->ring.tail;
93 if (dev_priv->ring.space < rem) {
94 int ret = i915_wait_ring(dev, rem, __func__);
95 if (ret)
96 return ret;
97 }
98 dev_priv->ring.space -= rem;
99
100 virt = (unsigned int *)
101 (dev_priv->ring.virtual_start + dev_priv->ring.tail);
102 rem /= 4;
103 while (rem--)
104 *virt++ = MI_NOOP;
105
106 dev_priv->ring.tail = 0;
107
108 return 0;
109}
110
83/** 111/**
84 * Sets up the hardware status page for devices that need a physical address 112 * Sets up the hardware status page for devices that need a physical address
85 * in the register. 113 * in the register.
@@ -200,7 +228,6 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
200 } 228 }
201 229
202 dev_priv->ring.Size = init->ring_size; 230 dev_priv->ring.Size = init->ring_size;
203 dev_priv->ring.tail_mask = dev_priv->ring.Size - 1;
204 231
205 dev_priv->ring.map.offset = init->ring_start; 232 dev_priv->ring.map.offset = init->ring_start;
206 dev_priv->ring.map.size = init->ring_size; 233 dev_priv->ring.map.size = init->ring_size;
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 76914ae65c31..2d5bce643e6a 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -85,7 +85,6 @@ struct drm_i915_gem_phys_object {
85}; 85};
86 86
87typedef struct _drm_i915_ring_buffer { 87typedef struct _drm_i915_ring_buffer {
88 int tail_mask;
89 unsigned long Size; 88 unsigned long Size;
90 u8 *virtual_start; 89 u8 *virtual_start;
91 int head; 90 int head;
@@ -790,33 +789,32 @@ extern void intel_modeset_cleanup(struct drm_device *dev);
790 789
791#define I915_VERBOSE 0 790#define I915_VERBOSE 0
792 791
793#define RING_LOCALS unsigned int outring, ringmask, outcount; \ 792#define RING_LOCALS volatile unsigned int *ring_virt__;
794 volatile char *virt; 793
795 794#define BEGIN_LP_RING(n) do { \
796#define BEGIN_LP_RING(n) do { \ 795 int bytes__ = 4*(n); \
797 if (I915_VERBOSE) \ 796 if (I915_VERBOSE) DRM_DEBUG("BEGIN_LP_RING(%d)\n", (n)); \
798 DRM_DEBUG("BEGIN_LP_RING(%d)\n", (n)); \ 797 /* a wrap must occur between instructions so pad beforehand */ \
799 if (dev_priv->ring.space < (n)*4) \ 798 if (unlikely (dev_priv->ring.tail + bytes__ > dev_priv->ring.Size)) \
800 i915_wait_ring(dev, (n)*4, __func__); \ 799 i915_wrap_ring(dev); \
801 outcount = 0; \ 800 if (unlikely (dev_priv->ring.space < bytes__)) \
802 outring = dev_priv->ring.tail; \ 801 i915_wait_ring(dev, bytes__, __func__); \
803 ringmask = dev_priv->ring.tail_mask; \ 802 ring_virt__ = (unsigned int *) \
804 virt = dev_priv->ring.virtual_start; \ 803 (dev_priv->ring.virtual_start + dev_priv->ring.tail); \
804 dev_priv->ring.tail += bytes__; \
805 dev_priv->ring.tail &= dev_priv->ring.Size - 1; \
806 dev_priv->ring.space -= bytes__; \
805} while (0) 807} while (0)
806 808
807#define OUT_RING(n) do { \ 809#define OUT_RING(n) do { \
808 if (I915_VERBOSE) DRM_DEBUG(" OUT_RING %x\n", (int)(n)); \ 810 if (I915_VERBOSE) DRM_DEBUG(" OUT_RING %x\n", (int)(n)); \
809 *(volatile unsigned int *)(virt + outring) = (n); \ 811 *ring_virt__++ = (n); \
810 outcount++; \
811 outring += 4; \
812 outring &= ringmask; \
813} while (0) 812} while (0)
814 813
815#define ADVANCE_LP_RING() do { \ 814#define ADVANCE_LP_RING() do { \
816 if (I915_VERBOSE) DRM_DEBUG("ADVANCE_LP_RING %x\n", outring); \ 815 if (I915_VERBOSE) \
817 dev_priv->ring.tail = outring; \ 816 DRM_DEBUG("ADVANCE_LP_RING %x\n", dev_priv->ring.tail); \
818 dev_priv->ring.space -= outcount * 4; \ 817 I915_WRITE(PRB0_TAIL, dev_priv->ring.tail); \
819 I915_WRITE(PRB0_TAIL, outring); \
820} while(0) 818} while(0)
821 819
822/** 820/**
@@ -839,6 +837,7 @@ extern void intel_modeset_cleanup(struct drm_device *dev);
839#define I915_GEM_HWS_INDEX 0x20 837#define I915_GEM_HWS_INDEX 0x20
840#define I915_BREADCRUMB_INDEX 0x21 838#define I915_BREADCRUMB_INDEX 0x21
841 839
840extern int i915_wrap_ring(struct drm_device * dev);
842extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); 841extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
843 842
844#define IS_I830(dev) ((dev)->pci_device == 0x3577) 843#define IS_I830(dev) ((dev)->pci_device == 0x3577)
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 73b58193afed..076752112a39 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -4099,7 +4099,6 @@ i915_gem_init_ringbuffer(struct drm_device *dev)
4099 4099
4100 /* Set up the kernel mapping for the ring. */ 4100 /* Set up the kernel mapping for the ring. */
4101 ring->Size = obj->size; 4101 ring->Size = obj->size;
4102 ring->tail_mask = obj->size - 1;
4103 4102
4104 ring->map.offset = dev->agp->base + obj_priv->gtt_offset; 4103 ring->map.offset = dev->agp->base + obj_priv->gtt_offset;
4105 ring->map.size = obj->size; 4104 ring->map.size = obj->size;