]>
Commit | Line | Data |
---|---|---|
a8f45b2e GKH |
1 | From e552eb7038a36d9b18860f525aa02875e313fe16 Mon Sep 17 00:00:00 2001 |
2 | From: Jesse Barnes <jbarnes@virtuousgeek.org> | |
3 | Date: Wed, 21 Apr 2010 11:39:23 -0700 | |
4 | Subject: drm/i915: use PIPE_CONTROL instruction on Ironlake and Sandy Bridge | |
5 | ||
6 | From: Jesse Barnes <jbarnes@virtuousgeek.org> | |
7 | ||
8 | commit e552eb7038a36d9b18860f525aa02875e313fe16 upstream. | |
9 | ||
10 | Since 965, the hardware has supported the PIPE_CONTROL command, which | |
11 | provides fine grained GPU cache flushing control. On recent chipsets, | |
12 | this instruction is required for reliable interrupt and sequence number | |
13 | reporting in the driver. | |
14 | ||
15 | So add support for this instruction, including workarounds, on Ironlake | |
16 | and Sandy Bridge hardware. | |
17 | ||
18 | https://bugs.freedesktop.org/show_bug.cgi?id=27108 | |
19 | ||
20 | Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> | |
21 | Tested-by: Chris Wilson <chris@chris-wilson.co.uk> | |
22 | Signed-off-by: Eric Anholt <eric@anholt.net> | |
23 | Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> | |
24 | ||
25 | --- | |
26 | drivers/gpu/drm/i915/i915_drv.h | 4 + | |
27 | drivers/gpu/drm/i915/i915_gem.c | 145 ++++++++++++++++++++++++++++++++++++---- | |
28 | drivers/gpu/drm/i915/i915_irq.c | 8 +- | |
29 | drivers/gpu/drm/i915/i915_reg.h | 11 +++ | |
30 | 4 files changed, 152 insertions(+), 16 deletions(-) | |
31 | ||
32 | --- a/drivers/gpu/drm/i915/i915_drv.h | |
33 | +++ b/drivers/gpu/drm/i915/i915_drv.h | |
34 | @@ -206,11 +206,14 @@ typedef struct drm_i915_private { | |
35 | ||
36 | drm_dma_handle_t *status_page_dmah; | |
37 | void *hw_status_page; | |
38 | + void *seqno_page; | |
39 | dma_addr_t dma_status_page; | |
40 | uint32_t counter; | |
41 | unsigned int status_gfx_addr; | |
42 | + unsigned int seqno_gfx_addr; | |
43 | drm_local_map_t hws_map; | |
44 | struct drm_gem_object *hws_obj; | |
45 | + struct drm_gem_object *seqno_obj; | |
46 | struct drm_gem_object *pwrctx; | |
47 | ||
48 | struct resource mch_res; | |
49 | @@ -1090,6 +1093,7 @@ extern int i915_wait_ring(struct drm_dev | |
50 | ||
51 | #define HAS_PCH_SPLIT(dev) (IS_IRONLAKE(dev) || \ | |
52 | IS_GEN6(dev)) | |
53 | +#define HAS_PIPE_CONTROL(dev) (IS_IRONLAKE(dev) || IS_GEN6(dev)) | |
54 | ||
55 | #define PRIMARY_RINGBUFFER_SIZE (128*1024) | |
56 | ||
57 | --- a/drivers/gpu/drm/i915/i915_gem.c | |
58 | +++ b/drivers/gpu/drm/i915/i915_gem.c | |
59 | @@ -1559,6 +1559,13 @@ i915_gem_object_move_to_inactive(struct | |
60 | i915_verify_inactive(dev, __FILE__, __LINE__); | |
61 | } | |
62 | ||
63 | +#define PIPE_CONTROL_FLUSH(addr) \ | |
64 | + OUT_RING(GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE | \ | |
65 | + PIPE_CONTROL_DEPTH_STALL); \ | |
66 | + OUT_RING(addr | PIPE_CONTROL_GLOBAL_GTT); \ | |
67 | + OUT_RING(0); \ | |
68 | + OUT_RING(0); \ | |
69 | + | |
70 | /** | |
71 | * Creates a new sequence number, emitting a write of it to the status page | |
72 | * plus an interrupt, which will trigger i915_user_interrupt_handler. | |
73 | @@ -1593,13 +1600,47 @@ i915_add_request(struct drm_device *dev, | |
74 | if (dev_priv->mm.next_gem_seqno == 0) | |
75 | dev_priv->mm.next_gem_seqno++; | |
76 | ||
77 | - BEGIN_LP_RING(4); | |
78 | - OUT_RING(MI_STORE_DWORD_INDEX); | |
79 | - OUT_RING(I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT); | |
80 | - OUT_RING(seqno); | |
81 | + if (HAS_PIPE_CONTROL(dev)) { | |
82 | + u32 scratch_addr = dev_priv->seqno_gfx_addr + 128; | |
83 | ||
84 | - OUT_RING(MI_USER_INTERRUPT); | |
85 | - ADVANCE_LP_RING(); | |
86 | + /* | |
87 | + * Workaround qword write incoherence by flushing the | |
88 | + * PIPE_NOTIFY buffers out to memory before requesting | |
89 | + * an interrupt. | |
90 | + */ | |
91 | + BEGIN_LP_RING(32); | |
92 | + OUT_RING(GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE | | |
93 | + PIPE_CONTROL_WC_FLUSH | PIPE_CONTROL_TC_FLUSH); | |
94 | + OUT_RING(dev_priv->seqno_gfx_addr | PIPE_CONTROL_GLOBAL_GTT); | |
95 | + OUT_RING(seqno); | |
96 | + OUT_RING(0); | |
97 | + PIPE_CONTROL_FLUSH(scratch_addr); | |
98 | + scratch_addr += 128; /* write to separate cachelines */ | |
99 | + PIPE_CONTROL_FLUSH(scratch_addr); | |
100 | + scratch_addr += 128; | |
101 | + PIPE_CONTROL_FLUSH(scratch_addr); | |
102 | + scratch_addr += 128; | |
103 | + PIPE_CONTROL_FLUSH(scratch_addr); | |
104 | + scratch_addr += 128; | |
105 | + PIPE_CONTROL_FLUSH(scratch_addr); | |
106 | + scratch_addr += 128; | |
107 | + PIPE_CONTROL_FLUSH(scratch_addr); | |
108 | + OUT_RING(GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE | | |
109 | + PIPE_CONTROL_WC_FLUSH | PIPE_CONTROL_TC_FLUSH | | |
110 | + PIPE_CONTROL_NOTIFY); | |
111 | + OUT_RING(dev_priv->seqno_gfx_addr | PIPE_CONTROL_GLOBAL_GTT); | |
112 | + OUT_RING(seqno); | |
113 | + OUT_RING(0); | |
114 | + ADVANCE_LP_RING(); | |
115 | + } else { | |
116 | + BEGIN_LP_RING(4); | |
117 | + OUT_RING(MI_STORE_DWORD_INDEX); | |
118 | + OUT_RING(I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT); | |
119 | + OUT_RING(seqno); | |
120 | + | |
121 | + OUT_RING(MI_USER_INTERRUPT); | |
122 | + ADVANCE_LP_RING(); | |
123 | + } | |
124 | ||
125 | DRM_DEBUG_DRIVER("%d\n", seqno); | |
126 | ||
127 | @@ -1744,7 +1785,10 @@ i915_get_gem_seqno(struct drm_device *de | |
128 | { | |
129 | drm_i915_private_t *dev_priv = dev->dev_private; | |
130 | ||
131 | - return READ_HWSP(dev_priv, I915_GEM_HWS_INDEX); | |
132 | + if (IS_I965G(dev)) | |
133 | + return ((volatile u32 *)(dev_priv->seqno_page))[0]; | |
134 | + else | |
135 | + return READ_HWSP(dev_priv, I915_GEM_HWS_INDEX); | |
136 | } | |
137 | ||
138 | /** | |
139 | @@ -4576,6 +4620,49 @@ i915_gem_idle(struct drm_device *dev) | |
140 | return 0; | |
141 | } | |
142 | ||
143 | +/* | |
144 | + * 965+ support PIPE_CONTROL commands, which provide finer grained control | |
145 | + * over cache flushing. | |
146 | + */ | |
147 | +static int | |
148 | +i915_gem_init_pipe_control(struct drm_device *dev) | |
149 | +{ | |
150 | + drm_i915_private_t *dev_priv = dev->dev_private; | |
151 | + struct drm_gem_object *obj; | |
152 | + struct drm_i915_gem_object *obj_priv; | |
153 | + int ret; | |
154 | + | |
155 | + obj = drm_gem_object_alloc(dev, 4096); | |
156 | + if (obj == NULL) { | |
157 | + DRM_ERROR("Failed to allocate seqno page\n"); | |
158 | + ret = -ENOMEM; | |
159 | + goto err; | |
160 | + } | |
161 | + obj_priv = obj->driver_private; | |
162 | + obj_priv->agp_type = AGP_USER_CACHED_MEMORY; | |
163 | + | |
164 | + ret = i915_gem_object_pin(obj, 4096); | |
165 | + if (ret) | |
166 | + goto err_unref; | |
167 | + | |
168 | + dev_priv->seqno_gfx_addr = obj_priv->gtt_offset; | |
169 | + dev_priv->seqno_page = kmap(obj_priv->pages[0]); | |
170 | + if (dev_priv->seqno_page == NULL) | |
171 | + goto err_unpin; | |
172 | + | |
173 | + dev_priv->seqno_obj = obj; | |
174 | + memset(dev_priv->seqno_page, 0, PAGE_SIZE); | |
175 | + | |
176 | + return 0; | |
177 | + | |
178 | +err_unpin: | |
179 | + i915_gem_object_unpin(obj); | |
180 | +err_unref: | |
181 | + drm_gem_object_unreference(obj); | |
182 | +err: | |
183 | + return ret; | |
184 | +} | |
185 | + | |
186 | static int | |
187 | i915_gem_init_hws(struct drm_device *dev) | |
188 | { | |
189 | @@ -4593,7 +4680,8 @@ i915_gem_init_hws(struct drm_device *dev | |
190 | obj = drm_gem_object_alloc(dev, 4096); | |
191 | if (obj == NULL) { | |
192 | DRM_ERROR("Failed to allocate status page\n"); | |
193 | - return -ENOMEM; | |
194 | + ret = -ENOMEM; | |
195 | + goto err; | |
196 | } | |
197 | obj_priv = obj->driver_private; | |
198 | obj_priv->agp_type = AGP_USER_CACHED_MEMORY; | |
199 | @@ -4601,7 +4689,7 @@ i915_gem_init_hws(struct drm_device *dev | |
200 | ret = i915_gem_object_pin(obj, 4096); | |
201 | if (ret != 0) { | |
202 | drm_gem_object_unreference(obj); | |
203 | - return ret; | |
204 | + goto err_unref; | |
205 | } | |
206 | ||
207 | dev_priv->status_gfx_addr = obj_priv->gtt_offset; | |
208 | @@ -4610,10 +4698,16 @@ i915_gem_init_hws(struct drm_device *dev | |
209 | if (dev_priv->hw_status_page == NULL) { | |
210 | DRM_ERROR("Failed to map status page.\n"); | |
211 | memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); | |
212 | - i915_gem_object_unpin(obj); | |
213 | - drm_gem_object_unreference(obj); | |
214 | - return -EINVAL; | |
215 | + ret = -EINVAL; | |
216 | + goto err_unpin; | |
217 | } | |
218 | + | |
219 | + if (HAS_PIPE_CONTROL(dev)) { | |
220 | + ret = i915_gem_init_pipe_control(dev); | |
221 | + if (ret) | |
222 | + goto err_unpin; | |
223 | + } | |
224 | + | |
225 | dev_priv->hws_obj = obj; | |
226 | memset(dev_priv->hw_status_page, 0, PAGE_SIZE); | |
227 | I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr); | |
228 | @@ -4621,6 +4715,30 @@ i915_gem_init_hws(struct drm_device *dev | |
229 | DRM_DEBUG_DRIVER("hws offset: 0x%08x\n", dev_priv->status_gfx_addr); | |
230 | ||
231 | return 0; | |
232 | + | |
233 | +err_unpin: | |
234 | + i915_gem_object_unpin(obj); | |
235 | +err_unref: | |
236 | + drm_gem_object_unreference(obj); | |
237 | +err: | |
238 | + return 0; | |
239 | +} | |
240 | + | |
241 | +static void | |
242 | +i915_gem_cleanup_pipe_control(struct drm_device *dev) | |
243 | +{ | |
244 | + drm_i915_private_t *dev_priv = dev->dev_private; | |
245 | + struct drm_gem_object *obj; | |
246 | + struct drm_i915_gem_object *obj_priv; | |
247 | + | |
248 | + obj = dev_priv->seqno_obj; | |
249 | + obj_priv = obj->driver_private; | |
250 | + kunmap(obj_priv->pages[0]); | |
251 | + i915_gem_object_unpin(obj); | |
252 | + drm_gem_object_unreference(obj); | |
253 | + dev_priv->seqno_obj = NULL; | |
254 | + | |
255 | + dev_priv->seqno_page = NULL; | |
256 | } | |
257 | ||
258 | static void | |
259 | @@ -4644,6 +4762,9 @@ i915_gem_cleanup_hws(struct drm_device * | |
260 | memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); | |
261 | dev_priv->hw_status_page = NULL; | |
262 | ||
263 | + if (HAS_PIPE_CONTROL(dev)) | |
264 | + i915_gem_cleanup_pipe_control(dev); | |
265 | + | |
266 | /* Write high address into HWS_PGA when disabling. */ | |
267 | I915_WRITE(HWS_PGA, 0x1ffff000); | |
268 | } | |
269 | --- a/drivers/gpu/drm/i915/i915_irq.c | |
270 | +++ b/drivers/gpu/drm/i915/i915_irq.c | |
271 | @@ -297,7 +297,7 @@ irqreturn_t ironlake_irq_handler(struct | |
272 | READ_BREADCRUMB(dev_priv); | |
273 | } | |
274 | ||
275 | - if (gt_iir & GT_USER_INTERRUPT) { | |
276 | + if (gt_iir & GT_PIPE_NOTIFY) { | |
277 | u32 seqno = i915_get_gem_seqno(dev); | |
278 | dev_priv->mm.irq_gem_seqno = seqno; | |
279 | trace_i915_gem_request_complete(dev, seqno); | |
280 | @@ -738,7 +738,7 @@ void i915_user_irq_get(struct drm_device | |
281 | spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); | |
282 | if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)) { | |
283 | if (HAS_PCH_SPLIT(dev)) | |
284 | - ironlake_enable_graphics_irq(dev_priv, GT_USER_INTERRUPT); | |
285 | + ironlake_enable_graphics_irq(dev_priv, GT_PIPE_NOTIFY); | |
286 | else | |
287 | i915_enable_irq(dev_priv, I915_USER_INTERRUPT); | |
288 | } | |
289 | @@ -754,7 +754,7 @@ void i915_user_irq_put(struct drm_device | |
290 | BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0); | |
291 | if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) { | |
292 | if (HAS_PCH_SPLIT(dev)) | |
293 | - ironlake_disable_graphics_irq(dev_priv, GT_USER_INTERRUPT); | |
294 | + ironlake_disable_graphics_irq(dev_priv, GT_PIPE_NOTIFY); | |
295 | else | |
296 | i915_disable_irq(dev_priv, I915_USER_INTERRUPT); | |
297 | } | |
298 | @@ -1034,7 +1034,7 @@ static int ironlake_irq_postinstall(stru | |
299 | /* enable kind of interrupts always enabled */ | |
300 | u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT | | |
301 | DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE; | |
302 | - u32 render_mask = GT_USER_INTERRUPT; | |
303 | + u32 render_mask = GT_PIPE_NOTIFY; | |
304 | u32 hotplug_mask = SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG | | |
305 | SDE_PORTC_HOTPLUG | SDE_PORTD_HOTPLUG; | |
306 | ||
307 | --- a/drivers/gpu/drm/i915/i915_reg.h | |
308 | +++ b/drivers/gpu/drm/i915/i915_reg.h | |
309 | @@ -210,6 +210,16 @@ | |
310 | #define ASYNC_FLIP (1<<22) | |
311 | #define DISPLAY_PLANE_A (0<<20) | |
312 | #define DISPLAY_PLANE_B (1<<20) | |
313 | +#define GFX_OP_PIPE_CONTROL ((0x3<<29)|(0x3<<27)|(0x2<<24)|2) | |
314 | +#define PIPE_CONTROL_QW_WRITE (1<<14) | |
315 | +#define PIPE_CONTROL_DEPTH_STALL (1<<13) | |
316 | +#define PIPE_CONTROL_WC_FLUSH (1<<12) | |
317 | +#define PIPE_CONTROL_IS_FLUSH (1<<11) /* MBZ on Ironlake */ | |
318 | +#define PIPE_CONTROL_TC_FLUSH (1<<10) /* GM45+ only */ | |
319 | +#define PIPE_CONTROL_ISP_DIS (1<<9) | |
320 | +#define PIPE_CONTROL_NOTIFY (1<<8) | |
321 | +#define PIPE_CONTROL_GLOBAL_GTT (1<<2) /* in addr dword */ | |
322 | +#define PIPE_CONTROL_STALL_EN (1<<1) /* in addr word, Ironlake+ only */ | |
323 | ||
324 | /* | |
325 | * Fence registers | |
326 | @@ -2111,6 +2121,7 @@ | |
327 | #define DEIER 0x4400c | |
328 | ||
329 | /* GT interrupt */ | |
330 | +#define GT_PIPE_NOTIFY (1 << 4) | |
331 | #define GT_SYNC_STATUS (1 << 2) | |
332 | #define GT_USER_INTERRUPT (1 << 0) | |
333 |