Prepare for 64bit relocation addresses
[platform/upstream/intel-gpu-tools.git] / tests / kms_mmio_vs_cs_flip.c
1 /*
2  * Copyright © 2014 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23
24 #include <errno.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <time.h>
29
30 #include "drmtest.h"
31 #include "igt_debugfs.h"
32 #include "igt_kms.h"
33 #include "intel_chipset.h"
34 #include "ioctl_wrappers.h"
35
36 typedef struct {
37         int drm_fd;
38         igt_display_t display;
39         igt_pipe_crc_t *pipe_crc;
40         drm_intel_bufmgr *bufmgr;
41         drm_intel_bo *busy_bo;
42         uint32_t devid;
43         bool flip_done;
44 } data_t;
45
46 static void exec_nop(data_t *data, uint32_t handle, unsigned int ring)
47 {
48         struct intel_batchbuffer *batch;
49         drm_intel_bo *bo;
50
51         batch = intel_batchbuffer_alloc(data->bufmgr, data->devid);
52         igt_assert(batch);
53
54         bo = gem_handle_to_libdrm_bo(data->bufmgr, data->drm_fd, "", handle);
55         igt_assert(bo);
56
57         /* add relocs to make sure the kernel will think we write to dst */
58         BEGIN_BATCH(4);
59         OUT_BATCH(MI_BATCH_BUFFER_END);
60         OUT_BATCH(MI_NOOP);
61         OUT_RELOC(bo, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER, 0);
62         OUT_BATCH(MI_NOOP);
63         ADVANCE_BATCH();
64
65         intel_batchbuffer_flush_on_ring(batch, ring);
66         intel_batchbuffer_free(batch);
67
68         drm_intel_bo_unreference(bo);
69 }
70
71 static void exec_blt(data_t *data)
72 {
73         struct intel_batchbuffer *batch;
74         int w, h, pitch, i;
75
76         batch = intel_batchbuffer_alloc(data->bufmgr, data->devid);
77         igt_assert(batch);
78
79         w = 8192;
80         h = data->busy_bo->size / (8192 * 4);
81         pitch = w * 4;
82
83         for (i = 0; i < 40; i++) {
84                 BLIT_COPY_BATCH_START(data->devid, 0);
85                 OUT_BATCH((3 << 24) | /* 32 bits */
86                           (0xcc << 16) | /* copy ROP */
87                           pitch);
88                 OUT_BATCH(0 << 16 | 0);
89                 OUT_BATCH(h << 16 | w);
90                 OUT_RELOC_FENCED(data->busy_bo, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER, 0);
91                 OUT_BATCH(0 << 16 | 0);
92                 OUT_BATCH(pitch);
93                 OUT_RELOC_FENCED(data->busy_bo, I915_GEM_DOMAIN_RENDER, 0, 0);
94                 ADVANCE_BATCH();
95         }
96
97         intel_batchbuffer_flush(batch);
98         intel_batchbuffer_free(batch);
99 }
100
101 static void page_flip_handler(int fd, unsigned int frame, unsigned int sec,
102                               unsigned int usec, void *_data)
103 {
104         data_t *data = _data;
105
106         data->flip_done = true;
107 }
108
109 static void wait_for_flip(data_t *data, uint32_t flip_handle)
110 {
111         struct timeval timeout = {
112                 .tv_sec = 3,
113                 .tv_usec = 0,
114         };
115         drmEventContext evctx = {
116                 .version = DRM_EVENT_CONTEXT_VERSION,
117                 .page_flip_handler = page_flip_handler,
118         };
119         fd_set fds;
120
121         FD_ZERO(&fds);
122         FD_SET(data->drm_fd, &fds);
123
124         while (!data->flip_done) {
125                 int ret = select(data->drm_fd + 1, &fds, NULL, NULL, &timeout);
126
127                 if (ret < 0 && errno == EINTR)
128                         continue;
129
130                 igt_assert(ret >= 0);
131
132                 do_or_die(drmHandleEvent(data->drm_fd, &evctx));
133         }
134
135         /*
136          * The flip completion may have been signalled prematurely, so
137          * also submit another nop batch and wait for it to make sure
138          * the ring has really been drained.
139          */
140         if (IS_GEN7(data->devid) || IS_GEN8(data->devid))
141                 exec_nop(data, flip_handle, I915_EXEC_BLT);
142         else
143                 exec_nop(data, flip_handle, I915_EXEC_RENDER);
144         gem_sync(data->drm_fd, flip_handle);
145 }
146
147 static void make_gpu_busy(data_t *data, uint32_t flip_handle)
148 {
149         /*
150          * Make sure flip_handle has been used on the blt ring.
151          * This should make the flip use the same ring on gen7+.
152          */
153         if (IS_GEN7(data->devid) || IS_GEN8(data->devid))
154                 exec_nop(data, flip_handle, I915_EXEC_BLT);
155
156         /*
157          * Add a pile commands to the ring.  The flip will be
158          * stuck behing these commands and hence gets delayed
159          * significantly.
160          */
161         exec_blt(data);
162
163         /*
164          * Make sure the render ring will block until the blt ring is clear.
165          * This is in case the flip will execute on the render ring and the
166          * blits were on the blt ring (this will be the case on gen6 at least).
167          *
168          * We can't add an explicit dependency between flip_handle and the
169          * blits since that would cause the driver to block until the blits
170          * have completed before it will perform a subsequent mmio flip,
171          * and so the test would fail to exercise the mmio vs. CS flip race.
172          */
173         if (HAS_BLT_RING(data->devid))
174                 exec_nop(data, data->busy_bo->handle, I915_EXEC_RENDER);
175 }
176
177 /*
178  * 1. set primary plane to full red
179  * 2. grab a reference crc
180  * 3. set primary plane to full blue
181  * 4. queue lots of GPU activity to delay the subsequent page flip
182  * 5. queue a page flip to the same blue fb
183  * 6. toggle a fullscreen sprite (green) on and back off again
184  * 7. set primary plane to red fb
185  * 8. wait for GPU to finish
186  * 9. compare current crc with reference crc
187  *
188  * We expect the primary plane to display full red at the end.
189  * If the sprite operations have interfered with the page flip,
190  * the driver may have mistakenly completed the flip before
191  * it was executed by the CS, and hence the subsequent mmio
192  * flips may have overtaken it. So once we've finished everything
193  * the CS flip may have been the last thing to occur, which means
194  * the primary plane may be full blue instead of the red it's
195  * supposed to be.
196  */
197 static bool
198 test_plane(data_t *data, igt_output_t *output, enum pipe pipe, enum igt_plane plane)
199 {
200         struct igt_fb red_fb, green_fb, blue_fb;
201         drmModeModeInfo *mode;
202         igt_plane_t *primary, *sprite;
203         igt_crc_t ref_crc, crc;
204         int ret;
205
206         igt_output_set_pipe(output, pipe);
207         igt_display_commit(&data->display);
208
209         if (!output->valid) {
210                 igt_output_set_pipe(output, PIPE_ANY);
211                 igt_display_commit(&data->display);
212                 return false;
213         }
214
215         primary = igt_output_get_plane(output, 0);
216         sprite = igt_output_get_plane(output, plane);
217
218         mode = igt_output_get_mode(output);
219         igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
220                             DRM_FORMAT_XRGB8888,
221                             I915_TILING_NONE,
222                             1.0, 0.0, 0.0,
223                             &red_fb);
224         igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
225                             DRM_FORMAT_XRGB8888,
226                             I915_TILING_NONE,
227                             0.0, 1.0, 0.0,
228                             &green_fb);
229         igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
230                             DRM_FORMAT_XRGB8888,
231                             I915_TILING_NONE,
232                             0.0, 0.0, 1.0,
233                             &blue_fb);
234
235         /*
236          * Make sure these buffers are suited for display use
237          * because most of the modeset operations must be fast
238          * later on.
239          */
240         igt_plane_set_fb(primary, &blue_fb);
241         igt_display_commit(&data->display);
242         igt_plane_set_fb(sprite, &green_fb);
243         igt_display_commit(&data->display);
244         igt_plane_set_fb(sprite, NULL);
245         igt_display_commit(&data->display);
246
247         if (data->pipe_crc)
248                 igt_pipe_crc_free(data->pipe_crc);
249         data->pipe_crc = igt_pipe_crc_new(pipe, INTEL_PIPE_CRC_SOURCE_AUTO);
250         if (!data->pipe_crc) {
251                 igt_info("auto crc not supported on this connector with crtc %i\n",
252                          pipe);
253
254                 igt_plane_set_fb(primary, NULL);
255                 igt_plane_set_fb(sprite, NULL);
256                 igt_output_set_pipe(output, PIPE_ANY);
257                 igt_display_commit(&data->display);
258
259                 igt_remove_fb(data->drm_fd, &red_fb);
260                 igt_remove_fb(data->drm_fd, &green_fb);
261                 igt_remove_fb(data->drm_fd, &blue_fb);
262
263                 return false;
264         }
265
266         /* set red fb and grab reference crc */
267         igt_plane_set_fb(primary, &red_fb);
268         igt_display_commit(&data->display);
269         igt_pipe_crc_collect_crc(data->pipe_crc, &ref_crc);
270
271         ret = drmModeSetCrtc(data->drm_fd, output->config.crtc->crtc_id,
272                              blue_fb.fb_id, 0, 0, &output->id, 1,
273                              mode);
274         igt_assert(ret == 0);
275
276         make_gpu_busy(data, blue_fb.gem_handle);
277
278         data->flip_done = false;
279         ret = drmModePageFlip(data->drm_fd, output->config.crtc->crtc_id,
280                               blue_fb.fb_id, DRM_MODE_PAGE_FLIP_EVENT, data);
281         igt_assert(ret == 0);
282
283         /*
284          * Toggle a fullscreen sprite on and back off. This will result
285          * in the primary plane getting disabled and re-enbled, and that
286          * leads to mmio flips. The driver may then mistake the flip done
287          * interrupts from the mmio flips as the flip done interrupts for
288          * the CS flip, and hence subsequent mmio flips won't wait for the
289          * CS flips like they should.
290          */
291         ret = drmModeSetPlane(data->drm_fd,
292                               sprite->drm_plane->plane_id,
293                               output->config.crtc->crtc_id,
294                               green_fb.fb_id, 0,
295                               0, 0, mode->hdisplay, mode->vdisplay,
296                               0, 0, mode->hdisplay << 16, mode->vdisplay << 16);
297         igt_assert(ret == 0);
298         ret = drmModeSetPlane(data->drm_fd,
299                               sprite->drm_plane->plane_id,
300                               output->config.crtc->crtc_id,
301                               0, 0,
302                               0, 0, 0, 0,
303                               0, 0, 0, 0);
304         igt_assert(ret == 0);
305
306         /*
307          * Set primary plane to red fb. This should wait for the CS flip
308          * to complete. But if the kernel mistook the flip done interrupt
309          * from the mmio flip as the flip done from the CS flip, this will
310          * not wait for anything. And hence the the CS flip will actually
311          * occur after this mmio flip.
312          */
313         ret = drmModeSetCrtc(data->drm_fd, output->config.crtc->crtc_id,
314                              red_fb.fb_id, 0, 0, &output->id, 1,
315                              mode);
316         igt_assert(ret == 0);
317
318         /* Make sure the flip has been executed */
319         wait_for_flip(data, blue_fb.gem_handle);
320
321         /* Grab crc and compare with the extected result */
322         igt_pipe_crc_collect_crc(data->pipe_crc, &crc);
323
324         igt_plane_set_fb(primary, NULL);
325         igt_display_commit(&data->display);
326
327         igt_remove_fb(data->drm_fd, &red_fb);
328         igt_remove_fb(data->drm_fd, &green_fb);
329         igt_remove_fb(data->drm_fd, &blue_fb);
330
331         igt_pipe_crc_free(data->pipe_crc);
332         data->pipe_crc = NULL;
333
334         igt_output_set_pipe(output, PIPE_ANY);
335         igt_display_commit(&data->display);
336
337         igt_assert(igt_crc_equal(&ref_crc, &crc));
338
339         return true;
340 }
341
342 /*
343  * 1. set primary plane to full red
344  * 2. grab a reference crc
345  * 3. set primary plane to full green
346  * 4. wait for vblank
347  * 5. pan primary plane a bit (to cause a mmio flip w/o vblank wait)
348  * 6. queue lots of GPU activity to delay the subsequent page flip
349  * 6. queue a page flip to a blue fb
350  * 7. set primary plane to red fb
351  * 8. wait for GPU to finish
352  * 9. compare current crc with reference crc
353  *
354  * We expect the primary plane to display full red at the end.
355  * If the previously schedule primary plane pan operation has interfered
356  * with the following page flip, the driver may have mistakenly completed
357  * the flip before it was executed by the CS, and hence the subsequent mmio
358  * flips may have overtaken it. So once we've finished everything
359  * the CS flip may have been the last thing to occur, which means
360  * the primary plane may be full blue instead of the red it's
361  * supposed to be.
362  */
363 static bool
364 test_crtc(data_t *data, igt_output_t *output, enum pipe pipe)
365 {
366         struct igt_fb red_fb, green_fb, blue_fb;
367         drmModeModeInfo *mode;
368         igt_plane_t *primary;
369         igt_crc_t ref_crc, crc;
370         int ret;
371
372         igt_output_set_pipe(output, pipe);
373         igt_display_commit(&data->display);
374
375         if (!output->valid) {
376                 igt_output_set_pipe(output, PIPE_ANY);
377                 igt_display_commit(&data->display);
378                 return false;
379         }
380
381         primary = igt_output_get_plane(output, 0);
382
383         mode = igt_output_get_mode(output);
384         igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay+1,
385                             DRM_FORMAT_XRGB8888,
386                             I915_TILING_NONE,
387                             1.0, 0.0, 0.0,
388                             &red_fb);
389         igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay+1,
390                             DRM_FORMAT_XRGB8888,
391                             I915_TILING_NONE,
392                             0.0, 0.0, 1.0,
393                             &blue_fb);
394         igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay+1,
395                             DRM_FORMAT_XRGB8888,
396                             I915_TILING_NONE,
397                             0.0, 1.0, 0.0,
398                             &green_fb);
399
400         /*
401          * Make sure these buffers are suited for display use
402          * because most of the modeset operations must be fast
403          * later on.
404          */
405         igt_plane_set_fb(primary, &green_fb);
406         igt_display_commit(&data->display);
407         igt_plane_set_fb(primary, &blue_fb);
408         igt_display_commit(&data->display);
409
410         if (data->pipe_crc)
411                 igt_pipe_crc_free(data->pipe_crc);
412         data->pipe_crc = igt_pipe_crc_new(pipe, INTEL_PIPE_CRC_SOURCE_AUTO);
413         if (!data->pipe_crc) {
414                 igt_info("auto crc not supported on this connector with crtc %i\n",
415                          pipe);
416
417                 igt_plane_set_fb(primary, NULL);
418                 igt_output_set_pipe(output, PIPE_ANY);
419                 igt_display_commit(&data->display);
420
421                 igt_remove_fb(data->drm_fd, &red_fb);
422                 igt_remove_fb(data->drm_fd, &green_fb);
423                 igt_remove_fb(data->drm_fd, &blue_fb);
424
425                 return false;
426         }
427
428         /* set red fb and grab reference crc */
429         igt_plane_set_fb(primary, &red_fb);
430         igt_display_commit(&data->display);
431         igt_pipe_crc_collect_crc(data->pipe_crc, &ref_crc);
432
433         /*
434          * Further down we need to issue an mmio flip w/o the kernel
435          * waiting for vblank. The easiest way is to just pan within
436          * the same FB. So pan away a bit here, and later we undo this
437          * with another pan which will result in the desired mmio flip.
438          */
439         ret = drmModeSetCrtc(data->drm_fd, output->config.crtc->crtc_id,
440                              green_fb.fb_id, 0, 1, &output->id, 1,
441                              mode);
442         igt_assert(ret == 0);
443
444         /*
445          * Make it more likely that the CS flip has been submitted into the
446          * ring by the time the mmio flip from the drmModeSetCrtc() below
447          * completes. The driver will then mistake the flip done interrupt
448          * from the mmio flip as the flip done interrupt from the CS flip.
449          */
450         igt_wait_for_vblank(data->drm_fd, pipe);
451
452         /* now issue the mmio flip w/o vblank waits in the kernel, ie. pan a bit */
453         ret = drmModeSetCrtc(data->drm_fd, output->config.crtc->crtc_id,
454                              green_fb.fb_id, 0, 0, &output->id, 1,
455                              mode);
456         igt_assert(ret == 0);
457
458         make_gpu_busy(data, blue_fb.gem_handle);
459
460         /*
461          * Submit the CS flip. The commands must be emitted into the ring
462          * before the mmio flip from the panning operation completes.
463          */
464         data->flip_done = false;
465         ret = drmModePageFlip(data->drm_fd, output->config.crtc->crtc_id,
466                               blue_fb.fb_id, DRM_MODE_PAGE_FLIP_EVENT, data);
467         igt_assert(ret == 0);
468
469         /*
470          * Set primary plane to red fb. This should wait for the CS flip
471          * to complete. But if the kernel mistook the flip done interrupt
472          * from the mmio flip as the flip done from the CS flip, this will
473          * not wait for anything. And hence the the CS flip will actually
474          * occur after this mmio flip.
475          */
476         ret = drmModeSetCrtc(data->drm_fd, output->config.crtc->crtc_id,
477                              red_fb.fb_id, 0, 0, &output->id, 1,
478                              mode);
479         igt_assert(ret == 0);
480
481         /* Make sure the flip has been executed */
482         wait_for_flip(data, blue_fb.gem_handle);
483
484         /* Grab crc and compare with the extected result */
485         igt_pipe_crc_collect_crc(data->pipe_crc, &crc);
486
487         igt_plane_set_fb(primary, NULL);
488         igt_display_commit(&data->display);
489
490         igt_remove_fb(data->drm_fd, &red_fb);
491         igt_remove_fb(data->drm_fd, &green_fb);
492         igt_remove_fb(data->drm_fd, &blue_fb);
493
494         igt_pipe_crc_free(data->pipe_crc);
495         data->pipe_crc = NULL;
496
497         igt_output_set_pipe(output, PIPE_ANY);
498         igt_display_commit(&data->display);
499
500         igt_assert(igt_crc_equal(&ref_crc, &crc));
501
502         return true;
503 }
504
505 static void
506 run_plane_test_for_pipe(data_t *data, enum pipe pipe)
507 {
508         igt_output_t *output;
509         enum igt_plane plane = 1; /* testing with one sprite is enough */
510         int valid_tests = 0;
511
512         igt_require(data->display.pipes[pipe].n_planes > 2);
513
514         for_each_connected_output(&data->display, output) {
515                 if (test_plane(data, output, pipe, plane))
516                         valid_tests++;
517         }
518
519         igt_require_f(valid_tests, "no valid crtc/connector combinations found\n");
520 }
521
522 static void
523 run_crtc_test_for_pipe(data_t *data, enum pipe pipe)
524 {
525         igt_output_t *output;
526         int valid_tests = 0;
527
528         for_each_connected_output(&data->display, output) {
529                 if (test_crtc(data, output, pipe))
530                         valid_tests++;
531         }
532
533         igt_require_f(valid_tests, "no valid crtc/connector combinations found\n");
534 }
535
536 static data_t data;
537
538 igt_main
539 {
540         int pipe;
541
542         igt_skip_on_simulation();
543
544         igt_fixture {
545                 data.drm_fd = drm_open_any();
546
547                 kmstest_set_vt_graphics_mode();
548
549                 data.devid = intel_get_drm_devid(data.drm_fd);
550
551                 igt_require_pipe_crc();
552                 igt_display_init(&data.display, data.drm_fd);
553
554                 data.bufmgr = drm_intel_bufmgr_gem_init(data.drm_fd, 4096);
555                 igt_assert(data.bufmgr);
556                 drm_intel_bufmgr_gem_enable_reuse(data.bufmgr);
557
558                 data.busy_bo = drm_intel_bo_alloc(data.bufmgr, "bo",
559                                                   64*1024*1024, 4096);
560                 gem_set_tiling(data.drm_fd, data.busy_bo->handle, 0, 4096);
561         }
562
563         igt_subtest_f("setplane_vs_cs_flip") {
564                 for (pipe = 0; pipe < data.display.n_pipes; pipe++)
565                         run_plane_test_for_pipe(&data, pipe);
566         }
567
568         igt_subtest_f("setcrtc_vs_cs_flip") {
569                 for (pipe = 0; pipe < data.display.n_pipes; pipe++)
570                         run_crtc_test_for_pipe(&data, pipe);
571         }
572
573         igt_fixture {
574                 drm_intel_bo_unreference(data.busy_bo);
575                 drm_intel_bufmgr_destroy(data.bufmgr);
576                 igt_display_fini(&data.display);
577         }
578 }