tests/gem_flink_race: check for expected flink failure code
[platform/upstream/intel-gpu-tools.git] / demos / intel_sprite_on.c
1 /*
2  * Copyright © 2012 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  * Author:
25  *   Armin Reese <armin.c.reese@intel.com>
26  */
27
28 /*
29  * This program is intended for testing sprite functionality.
30  */
31 #include <assert.h>
32 #include <errno.h>
33 #include <math.h>
34 #include <stdint.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <termios.h>
40 #include <sys/time.h>
41 #include <sys/poll.h>
42 #include <sys/time.h>
43 #include <sys/mman.h>
44 #include <sys/ioctl.h>
45
46 #include "i915_drm.h"
47 #include "drmtest.h"
48
49 #if defined(DRM_IOCTL_MODE_ADDFB2) && defined(DRM_I915_SET_SPRITE_COLORKEY)
50 #define TEST_PLANES 1
51 #include "drm_fourcc.h"
52 #endif
53
54 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
55
56 /*
57  * Mode setting with the kernel interfaces is a bit of a chore.
58  * First you have to find the connector in question and make sure the
59  * requested mode is available.
60  * Then you need to find the encoder attached to that connector so you
61  * can bind it with a free crtc.
62  */
63 struct connector {
64         uint32_t            id;
65         int                 mode_valid;
66         drmModeModeInfo     mode;
67         drmModeEncoder      *encoder;
68         drmModeConnector    *connector;
69         int                 crtc;
70         int                 pipe;
71 };
72
73 static void dump_mode(drmModeModeInfo *mode)
74 {
75         printf("  %s %d %d %d %d %d %d %d %d %d 0x%x 0x%x %d\n",
76                 mode->name,
77                 mode->vrefresh,
78                 mode->hdisplay,
79                 mode->hsync_start,
80                 mode->hsync_end,
81                 mode->htotal,
82                 mode->vdisplay,
83                 mode->vsync_start,
84                 mode->vsync_end,
85                 mode->vtotal,
86                 mode->flags,
87                 mode->type,
88                 mode->clock);
89 }
90
91 static void dump_connectors(int gfx_fd, drmModeRes *resources)
92 {
93         int i, j;
94
95         printf("Connectors:\n");
96         printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\n");
97         for (i = 0; i < resources->count_connectors; i++) {
98                 drmModeConnector *connector;
99
100                 connector = drmModeGetConnector(gfx_fd, resources->connectors[i]);
101                 if (!connector) {
102                         printf("could not get connector %i: %s\n",
103                                         resources->connectors[i], strerror(errno));
104                         continue;
105                 }
106
107                 printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\n",
108                         connector->connector_id,
109                         connector->encoder_id,
110                         kmstest_connector_status_str(connector->connection),
111                         kmstest_connector_type_str(connector->connector_type),
112                         connector->mmWidth, connector->mmHeight,
113                         connector->count_modes);
114
115                 if (!connector->count_modes)
116                         continue;
117
118                 printf("  modes:\n");
119                 printf("  name refresh (Hz) hdisp hss hse htot vdisp vss vse vtot flags type clock\n");
120                 for (j = 0; j < connector->count_modes; j++)
121                         dump_mode(&connector->modes[j]);
122
123                 drmModeFreeConnector(connector);
124         }
125         printf("\n");
126 }
127
128 static void dump_crtcs(int gfx_fd, drmModeRes *resources)
129 {
130         int i;
131
132         printf("CRTCs:\n");
133         printf("id\tfb\tpos\tsize\n");
134         for (i = 0; i < resources->count_crtcs; i++) {
135                 drmModeCrtc *crtc;
136
137                 crtc = drmModeGetCrtc(gfx_fd, resources->crtcs[i]);
138                 if (!crtc) {
139                         printf("could not get crtc %i: %s\n",
140                                 resources->crtcs[i],
141                                 strerror(errno));
142                         continue;
143                 }
144                 printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
145                         crtc->crtc_id,
146                         crtc->buffer_id,
147                         crtc->x, crtc->y,
148                         crtc->width, crtc->height);
149                 dump_mode(&crtc->mode);
150
151                 drmModeFreeCrtc(crtc);
152         }
153         printf("\n");
154 }
155
156 static void dump_planes(int gfx_fd, drmModeRes *resources)
157 {
158         drmModePlaneRes             *plane_resources;
159         drmModePlane                *ovr;
160         int i;
161
162         plane_resources = drmModeGetPlaneResources(gfx_fd);
163         if (!plane_resources) {
164                 printf("drmModeGetPlaneResources failed: %s\n",
165                         strerror(errno));
166                 return;
167         }
168
169         printf("Planes:\n");
170         printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\n");
171         for (i = 0; i < plane_resources->count_planes; i++) {
172                 ovr = drmModeGetPlane(gfx_fd, plane_resources->planes[i]);
173                 if (!ovr) {
174                         printf("drmModeGetPlane failed: %s\n",
175                                 strerror(errno));
176                         continue;
177                 }
178
179                 printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%d\n",
180                         ovr->plane_id, ovr->crtc_id, ovr->fb_id,
181                         ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
182                         ovr->gamma_size);
183
184                 drmModeFreePlane(ovr);
185         }
186         printf("\n");
187
188         return;
189 }
190
191 static void connector_find_preferred_mode(int gfx_fd,
192                                           drmModeRes *gfx_resources,
193                                           struct connector *c)
194 {
195         drmModeConnector *connector;
196         drmModeEncoder *encoder = NULL;
197         int i, j;
198
199         /* First, find the connector & mode */
200         c->mode_valid = 0;
201         connector = drmModeGetConnector(gfx_fd, c->id);
202         if (!connector) {
203                 printf("could not get connector %d: %s\n",
204                         c->id,
205                         strerror(errno));
206                 drmModeFreeConnector(connector);
207                 return;
208         }
209
210         if (connector->connection != DRM_MODE_CONNECTED) {
211                 drmModeFreeConnector(connector);
212                 return;
213         }
214
215         if (!connector->count_modes) {
216                 printf("connector %d has no modes\n",
217                         c->id);
218                 drmModeFreeConnector(connector);
219                 return;
220         }
221
222         if (connector->connector_id != c->id) {
223                 printf("connector id doesn't match (%d != %d)\n",
224                         connector->connector_id,
225                         c->id);
226                 drmModeFreeConnector(connector);
227                 return;
228         }
229
230         for (j = 0; j < connector->count_modes; j++) {
231                 c->mode = connector->modes[j];
232                 if (c->mode.type & DRM_MODE_TYPE_PREFERRED) {
233                         c->mode_valid = 1;
234                         break;
235                 }
236         }
237
238         if (!c->mode_valid) {
239                 if (connector->count_modes > 0) {
240                         /* use the first mode as test mode */
241                         c->mode = connector->modes[0];
242                         c->mode_valid = 1;
243                 } else {
244                         printf("failed to find any modes on connector %d\n",
245                                 c->id);
246                         return;
247                 }
248         }
249
250         /* Now get the encoder */
251         for (i = 0; i < connector->count_encoders; i++) {
252                 encoder = drmModeGetEncoder(gfx_fd, connector->encoders[i]);
253
254                 if (!encoder) {
255                         printf("could not get encoder %i: %s\n",
256                                 gfx_resources->encoders[i],
257                                 strerror(errno));
258                         drmModeFreeEncoder(encoder);
259                         continue;
260                 }
261
262                 break;
263         }
264
265         c->encoder = encoder;
266
267         if (i == gfx_resources->count_encoders) {
268                 printf("failed to find encoder\n");
269                 c->mode_valid = 0;
270                 return;
271         }
272
273         /* Find first CRTC not in use */
274         for (i = 0; i < gfx_resources->count_crtcs; i++) {
275                 if (gfx_resources->crtcs[i] && (c->encoder->possible_crtcs & (1<<i)))
276                         break;
277         }
278         c->crtc = gfx_resources->crtcs[i];
279         c->pipe = i;
280
281         gfx_resources->crtcs[i] = 0;
282
283         c->connector = connector;
284 }
285
286 static int connector_find_plane(int gfx_fd, struct connector *c)
287 {
288         drmModePlaneRes *plane_resources;
289         drmModePlane *ovr;
290         uint32_t id = 0;
291         int i;
292
293         plane_resources = drmModeGetPlaneResources(gfx_fd);
294         if (!plane_resources) {
295                 printf("drmModeGetPlaneResources failed: %s\n",
296                         strerror(errno));
297                 return 0;
298         }
299
300         for (i = 0; i < plane_resources->count_planes; i++) {
301                 ovr = drmModeGetPlane(gfx_fd, plane_resources->planes[i]);
302                 if (!ovr) {
303                         printf("drmModeGetPlane failed: %s\n",
304                                 strerror(errno));
305                         continue;
306                 }
307
308                 if (ovr->possible_crtcs & (1 << c->pipe)) {
309                         id = ovr->plane_id;
310                         drmModeFreePlane(ovr);
311                         break;
312                 }
313                 drmModeFreePlane(ovr);
314         }
315
316         return id;
317 }
318
319 static int prepare_primary_surface(int fd, int prim_width, int prim_height,
320                                    uint32_t *prim_handle, uint32_t *prim_stride,
321                                    uint32_t *prim_size, int tiled)
322 {
323         uint32_t                        bytes_per_pixel = sizeof(uint32_t);
324         uint32_t                        *prim_fb_ptr;
325         struct drm_i915_gem_set_tiling  set_tiling;
326
327         if (bytes_per_pixel != sizeof(uint32_t)) {
328                 printf("Bad bytes_per_pixel for primary surface: %d\n",
329                         bytes_per_pixel);
330                 return -EINVAL;
331         }
332
333         if (tiled) {
334                 int                         v;
335
336                 /* Round the tiling up to the next power-of-two and the
337                  * region up to the next pot fence size so that this works
338                  * on all generations.
339                  *
340                  * This can still fail if the framebuffer is too large to
341                  * be tiled. But then that failure is expected.
342                  */
343
344                 v = prim_width * bytes_per_pixel;
345                 for (*prim_stride = 512; *prim_stride < v; *prim_stride *= 2)
346                         ;
347
348                 v = *prim_stride * prim_height;
349                 for (*prim_size = 1024*1024; *prim_size < v; *prim_size *= 2)
350                         ;
351         } else {
352                 /* Scan-out has a 64 byte alignment restriction */
353                 *prim_stride = (prim_width * bytes_per_pixel + 63) & ~63;
354                 *prim_size = *prim_stride * prim_height;
355         }
356
357         *prim_handle = gem_create(fd, *prim_size);
358
359         if (tiled) {
360                 set_tiling.handle = *prim_handle;
361                 set_tiling.tiling_mode = I915_TILING_X;
362                 set_tiling.stride = *prim_stride;
363                 if (ioctl(fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling)) {
364                         printf("Set tiling failed: %s (stride=%d, size=%d)\n",
365                                 strerror(errno), *prim_stride, *prim_size);
366                         return -1;
367                 }
368         }
369
370         prim_fb_ptr = gem_mmap(fd, *prim_handle, *prim_size, PROT_READ | PROT_WRITE);
371
372         if (prim_fb_ptr != NULL) {
373                 // Write primary surface with gray background
374                 memset(prim_fb_ptr, 0x3f, *prim_size);
375                 munmap(prim_fb_ptr, *prim_size);
376         }
377
378         return 0;
379 }
380
381 static void fill_sprite(int sprite_width, int sprite_height, int sprite_stride,
382                         int sprite_index, void *sprite_fb_ptr)
383 {
384         __u32                           *pLinePat0,
385                                         *pLinePat1,
386                                         *pLinePtr;
387         int                             i,
388                                         line;
389         int                             stripe_width;
390
391         stripe_width = ((sprite_width > 64) &&
392                         (sprite_height > 64)) ? (sprite_index + 1) * 8 :
393                 (sprite_index + 1) * 2;
394
395         // Note:  sprite_stride is in bytes.  pLinePat0 and pLinePat1
396         //        are both __u32 pointers
397         pLinePat0 = sprite_fb_ptr;
398         pLinePat1 = pLinePat0 + (stripe_width * (sprite_stride / sizeof(*pLinePat0)));
399
400         for (i = 0; i < sprite_width; i++) {
401                 *(pLinePat0 + i) = ((i / stripe_width) & 0x1) ? 0 : ~0;
402                 *(pLinePat1 + i) = ~(*(pLinePat0 + i));
403         }
404
405         for (line = 1; line < sprite_height; line++) {
406                 if (line == stripe_width) {
407                         continue;
408                 }
409
410                 pLinePtr = ((line / stripe_width) & 0x1) ? pLinePat1 : pLinePat0;
411                 memcpy( pLinePat0 + ((sprite_stride / sizeof(*pLinePat0)) * line),
412                                 pLinePtr,
413                                 sprite_width * sizeof(*pLinePat0));
414         }
415
416         return;
417 }
418
419 static int prepare_sprite_surfaces(int fd, int sprite_width, int sprite_height,
420                                    uint32_t num_surfaces, uint32_t *sprite_handles,
421                                    uint32_t *sprite_stride, uint32_t *sprite_size,
422                                    int tiled)
423 {
424         uint32_t                        bytes_per_pixel = sizeof(uint32_t);
425         uint32_t                        *sprite_fb_ptr;
426         struct drm_i915_gem_set_tiling  set_tiling;
427         int                             i;
428
429         if (bytes_per_pixel != sizeof(uint32_t)) {
430                 printf("Bad bytes_per_pixel for sprite: %d\n", bytes_per_pixel);
431                 return -EINVAL;
432         }
433
434         if (tiled) {
435                 int                         v;
436
437                 /* Round the tiling up to the next power-of-two and the
438                  * region up to the next pot fence size so that this works
439                  * on all generations.
440                  *
441                  * This can still fail if the framebuffer is too large to
442                  * be tiled. But then that failure is expected.
443                  */
444
445                 v = sprite_width * bytes_per_pixel;
446                 for (*sprite_stride = 512; *sprite_stride < v; *sprite_stride *= 2)
447                         ;
448
449                 v = *sprite_stride * sprite_height;
450                 for (*sprite_size = 1024*1024; *sprite_size < v; *sprite_size *= 2)
451                         ;
452         } else {
453                 /* Scan-out has a 64 byte alignment restriction */
454                 *sprite_stride = (sprite_width * bytes_per_pixel + 63) & ~63;
455                 *sprite_size = *sprite_stride * sprite_height;
456         }
457
458         for (i = 0; i < num_surfaces;  i++) {
459                 // Create the sprite surface
460                 sprite_handles[i] = gem_create(fd, *sprite_size);
461
462                 if (tiled) {
463                         set_tiling.handle = sprite_handles[i];
464                         set_tiling.tiling_mode = I915_TILING_X;
465                         set_tiling.stride = *sprite_stride;
466                         if (ioctl(fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling)) {
467                                 printf("Set tiling failed: %s (stride=%d, size=%d)\n",
468                                                 strerror(errno), *sprite_stride, *sprite_size);
469                                 return -1;
470                         }
471                 }
472
473                 // Get pointer to the surface
474                 sprite_fb_ptr = gem_mmap(fd,
475                                 sprite_handles[i], *sprite_size,
476                                 PROT_READ | PROT_WRITE);
477
478                 if (sprite_fb_ptr != NULL) {
479                         // Fill with checkerboard pattern
480                         fill_sprite(sprite_width, sprite_height, *sprite_stride, i, sprite_fb_ptr);
481
482                         munmap(sprite_fb_ptr, *sprite_size);
483                 } else {
484                         i--;
485                         while (i >= 0) {
486                                 gem_close(fd, sprite_handles[i]);
487                                 i--;
488                         }
489                 }
490         }
491
492         return 0;
493 }
494
495 static void ricochet(int tiled, int sprite_w, int sprite_h,
496                      int out_w, int out_h, int dump_info)
497 {
498         int                                 ret;
499         int                                 gfx_fd;
500         int                                 keep_moving;
501         const int                           num_surfaces = 3;
502         uint32_t                            sprite_handles[num_surfaces];
503         uint32_t                            sprite_fb_id[num_surfaces];
504         int                                 sprite_x;
505         int                                 sprite_y;
506         uint32_t                            sprite_stride;
507         uint32_t                            sprite_size;
508         uint32_t                            handles[4],
509                                             pitches[4],
510                                             offsets[4]; /* we only use [0] */
511         uint32_t                            prim_width,
512                                             prim_height,
513                                             prim_handle,
514                                             prim_stride,
515                                             prim_size,
516                                             prim_fb_id;
517         struct drm_intel_sprite_colorkey    set;
518         struct connector                    curr_connector;
519         drmModeRes                          *gfx_resources;
520         struct termios                      orig_term,
521                                             curr_term;
522         int                                 c_index;
523         int                                 sprite_index;
524         unsigned int                        sprite_plane_id;
525         uint32_t                            plane_flags = 0;
526         int                                 delta_x,
527                                             delta_y;
528         struct timeval                      stTimeVal;
529         long long                           currTime,
530              prevFlipTime,
531              prevMoveTime,
532              deltaFlipTime,
533              deltaMoveTime,
534              SleepTime;
535         char                                key;
536
537         // Open up I915 graphics device
538         gfx_fd = drmOpen("i915", NULL);
539         if (gfx_fd < 0) {
540                 printf("Failed to load i915 driver: %s\n", strerror(errno));
541                 return;
542         }
543
544         // Obtain pointer to struct containing graphics resources
545         gfx_resources = drmModeGetResources(gfx_fd);
546         if (!gfx_resources) {
547                 printf("drmModeGetResources failed: %s\n", strerror(errno));
548                 return;
549         }
550
551         if (dump_info != 0) {
552                 dump_connectors(gfx_fd, gfx_resources);
553                 dump_crtcs(gfx_fd, gfx_resources);
554                 dump_planes(gfx_fd, gfx_resources);
555         }
556
557         // Save previous terminal settings
558         if (tcgetattr( 0, &orig_term) != 0) {
559                 printf("tcgetattr failure: %s\n",
560                                 strerror(errno));
561                 return;
562         }
563
564         // Set up input to return characters immediately
565         curr_term = orig_term;
566         curr_term.c_lflag &= ~(ICANON | ECHO | ECHONL);
567         curr_term.c_cc[VMIN] = 0;       // No minimum number of characters
568         curr_term.c_cc[VTIME] = 0 ;     // Return immediately, even if
569         // nothing has been entered.
570         if (tcsetattr( 0, TCSANOW, &curr_term) != 0) {
571                 printf("tcgetattr failure: %s\n", strerror(errno));
572                 return;
573         }
574
575         // Cycle through all connectors and display the flying sprite
576         // where there are displays attached and the hardware will support it.
577         for (c_index = 0; c_index < gfx_resources->count_connectors; c_index++)  {
578                 curr_connector.id = gfx_resources->connectors[c_index];
579
580                 // Find the native (preferred) display mode
581                 connector_find_preferred_mode(gfx_fd, gfx_resources, &curr_connector);
582                 if (curr_connector.mode_valid == 0) {
583                         printf("No valid preferred mode detected\n");
584                         goto out;
585                 }
586
587                 // Determine if sprite hardware is available on pipe
588                 // associated with this connector.
589                 sprite_plane_id = connector_find_plane(gfx_fd, &curr_connector);
590                 if (!sprite_plane_id) {
591                         printf("Failed to find sprite plane on crtc\n");
592                         goto out;
593                 }
594
595                 // Width and height of preferred mode
596                 prim_width = curr_connector.mode.hdisplay;
597                 prim_height = curr_connector.mode.vdisplay;
598
599                 // Allocate and fill memory for primary surface
600                 ret = prepare_primary_surface(
601                                 gfx_fd,
602                                 prim_width,
603                                 prim_height,
604                                 &prim_handle,
605                                 &prim_stride,
606                                 &prim_size,
607                                 tiled);
608                 if (ret != 0) {
609                         printf("Failed to add primary fb (%dx%d): %s\n",
610                                 prim_width, prim_height, strerror(errno));
611                         goto out;
612                 }
613
614                 // Add the primary surface framebuffer
615                 ret = drmModeAddFB(gfx_fd, prim_width, prim_height, 24, 32,
616                                    prim_stride, prim_handle, &prim_fb_id);
617                 gem_close(gfx_fd, prim_handle);
618
619                 if (ret != 0) {
620                         printf("Failed to add primary fb (%dx%d): %s\n",
621                                         prim_width, prim_height, strerror(errno));
622                         goto out;
623                 }
624
625                 // Allocate and fill sprite surfaces
626                 ret = prepare_sprite_surfaces(gfx_fd, sprite_w, sprite_h, num_surfaces,
627                                               &sprite_handles[0],
628                                               &sprite_stride, &sprite_size,
629                                               tiled);
630                 if (ret != 0) {
631                         printf("Preparation of sprite surfaces failed %dx%d\n",
632                                 sprite_w, sprite_h);
633                         goto out;
634                 }
635
636                 // Add the sprite framebuffers
637                 for (sprite_index = 0; sprite_index < num_surfaces; sprite_index++) {
638                         handles[0] = sprite_handles[sprite_index];
639                         handles[1] = handles[0];
640                         handles[2] = handles[0];
641                         handles[3] = handles[0];
642                         pitches[0] = sprite_stride;
643                         pitches[1] = sprite_stride;
644                         pitches[2] = sprite_stride;
645                         pitches[3] = sprite_stride;
646                         memset(offsets, 0, sizeof(offsets));
647
648                         ret = drmModeAddFB2(gfx_fd, sprite_w, sprite_h,
649                                             DRM_FORMAT_XRGB8888,
650                                             handles, pitches, offsets,
651                                             &sprite_fb_id[sprite_index], plane_flags);
652                         gem_close(gfx_fd, sprite_handles[sprite_index]);
653
654                         if (ret) {
655                                 printf("Failed to add sprite fb (%dx%d): %s\n",
656                                        sprite_w, sprite_h, strerror(errno));
657
658                                 sprite_index--;
659                                 while (sprite_index >= 0) {
660                                         drmModeRmFB(gfx_fd, sprite_fb_id[sprite_index]);
661                                         sprite_index--;
662                                 }
663                                 goto out;
664                         }
665                 }
666
667                 if (dump_info != 0) {
668                         printf("Displayed Mode Connector struct:\n"
669                                 "    .id = %d\n"
670                                 "    .mode_valid = %d\n"
671                                 "    .crtc = %d\n"
672                                 "    .pipe = %d\n"
673                                 "    drmModeModeInfo ...\n"
674                                 "        .name = %s\n"
675                                 "        .type = %d\n"
676                                 "        .flags = %08x\n"
677                                 "    drmModeEncoder ...\n"
678                                 "        .encoder_id = %d\n"
679                                 "        .encoder_type = %d (%s)\n"
680                                 "        .crtc_id = %d\n"
681                                 "        .possible_crtcs = %d\n"
682                                 "        .possible_clones = %d\n"
683                                 "    drmModeConnector ...\n"
684                                 "        .connector_id = %d\n"
685                                 "        .encoder_id = %d\n"
686                                 "        .connector_type = %d (%s)\n"
687                                 "        .connector_type_id = %d\n\n",
688                                 curr_connector.id,
689                                 curr_connector.mode_valid,
690                                 curr_connector.crtc,
691                                 curr_connector.pipe,
692                                 curr_connector.mode.name,
693                                 curr_connector.mode.type,
694                                 curr_connector.mode.flags,
695                                 curr_connector.encoder->encoder_id,
696                                 curr_connector.encoder->encoder_type,
697                                 kmstest_encoder_type_str(curr_connector.encoder->encoder_type),
698                                 curr_connector.encoder->crtc_id,
699                                 curr_connector.encoder->possible_crtcs,
700                                 curr_connector.encoder->possible_clones,
701                                 curr_connector.connector->connector_id,
702                                 curr_connector.connector->encoder_id,
703                                 curr_connector.connector->connector_type,
704                                 kmstest_connector_type_str(curr_connector.connector->connector_type),
705                                 curr_connector.connector->connector_type_id);
706
707                         printf("Sprite surface dimensions = %dx%d\n"
708                                 "Sprite output dimensions = %dx%d\n"
709                                 "Press any key to continue >\n",
710                                 sprite_w, sprite_h, out_w, out_h);
711
712                         // Wait for a key-press
713                         while( read(0, &key, 1) == 0);
714                         // Purge unread characters
715                         tcflush(0, TCIFLUSH);
716                 }
717
718                 // Set up the primary display mode
719                 ret = drmModeSetCrtc(gfx_fd, curr_connector.crtc, prim_fb_id,
720                                      0, 0, &curr_connector.id, 1, &curr_connector.mode);
721                 if (ret != 0) {
722                         printf("Failed to set mode (%dx%d@%dHz): %s\n",
723                                 prim_width, prim_height, curr_connector.mode.vrefresh,
724                                 strerror(errno));
725                         continue;
726                 }
727
728                 // Set the sprite colorkey state
729                 set.plane_id = sprite_plane_id;
730                 set.min_value = 0;
731                 set.max_value = 0;
732                 set.flags = I915_SET_COLORKEY_NONE;
733                 ret = drmCommandWrite(gfx_fd, DRM_I915_SET_SPRITE_COLORKEY, &set, sizeof(set));
734                 assert(ret == 0);
735
736                 // Set up sprite output dimensions, initial position, etc.
737                 if (out_w > prim_width / 2)
738                         out_w = prim_width / 2;
739                 if (out_h > prim_height / 2)
740                         out_h = prim_height / 2;
741
742                 delta_x = 3;
743                 delta_y = 4;
744                 sprite_x = (prim_width / 2) - (out_w / 2);
745                 sprite_y = (prim_height / 2) - (out_h / 2);
746
747                 currTime = 0;
748                 prevFlipTime = 0;       // Will force immediate sprite flip
749                 prevMoveTime = 0;       // Will force immediate sprite move
750                 deltaFlipTime = 500000; // Flip sprite surface every 1/2 second
751                 deltaMoveTime = 100000; // Move sprite every 100 ms
752                 sprite_index = num_surfaces - 1;
753                 keep_moving = 1;
754
755                 // Bounce sprite off the walls
756                 while (keep_moving) {
757                         // Obtain system time in usec.
758                         if (gettimeofday( &stTimeVal, NULL ) != 0)
759                                 printf("gettimeofday error: %s\n", strerror(errno));
760                         else
761                                 currTime = ((long long)stTimeVal.tv_sec * 1000000) + stTimeVal.tv_usec;
762
763                         // Check if it's time to flip the sprite surface
764                         if (currTime - prevFlipTime > deltaFlipTime) {
765                                 sprite_index = (sprite_index + 1) % num_surfaces;
766
767                                 prevFlipTime = currTime;
768                         }
769
770                         // Move the sprite on the screen and flip
771                         // the surface if the index has changed
772                         // NB: sprite_w and sprite_h must be 16.16 fixed point, herego << 16
773                         if (drmModeSetPlane(gfx_fd, sprite_plane_id, curr_connector.crtc,
774                                             sprite_fb_id[sprite_index], plane_flags,
775                                             sprite_x, sprite_y,
776                                             out_w, out_h,
777                                             0, 0,
778                                             sprite_w << 16, sprite_h << 16))
779                                 printf("Failed to enable sprite plane: %s\n", strerror(errno));
780
781                         // Check if it's time to move the sprite surface
782                         if (currTime - prevMoveTime > deltaMoveTime)  {
783
784                                 // Compute the next position for sprite
785                                 sprite_x += delta_x;
786                                 sprite_y += delta_y;
787                                 if (sprite_x < 0) {
788                                         sprite_x = 0;
789                                         delta_x = -delta_x;
790                                 }
791                                 else if (sprite_x > prim_width - out_w) {
792                                         sprite_x = prim_width - out_w;
793                                         delta_x = -delta_x;
794                                 }
795
796                                 if (sprite_y < 0) {
797                                         sprite_y = 0;
798                                         delta_y = -delta_y;
799                                 }
800                                 else if (sprite_y > prim_height - out_h) {
801                                         sprite_y = prim_height - out_h;
802                                         delta_y = -delta_y;
803                                 }
804
805                                 prevMoveTime = currTime;
806                         }
807
808                         // Fetch a key from input (non-blocking)
809                         if (read(0, &key, 1) == 1) {
810                                 switch (key) {
811                                 case 'q':       // Kill the program
812                                 case 'Q':
813                                         goto out;
814                                         break;
815                                 case 's':       // Slow down sprite movement;
816                                         deltaMoveTime = (deltaMoveTime * 100) / 90;
817                                         if (deltaMoveTime > 800000) {
818                                                 deltaMoveTime = 800000;
819                                         }
820                                         break;
821                                 case 'S':       // Speed up sprite movement;
822                                         deltaMoveTime = (deltaMoveTime * 100) / 110;
823                                         if (deltaMoveTime < 2000) {
824                                                 deltaMoveTime = 2000;
825                                         }
826                                         break;
827                                 case 'f':       // Slow down sprite flipping;
828                                         deltaFlipTime = (deltaFlipTime * 100) / 90;
829                                         if (deltaFlipTime > 1000000)
830                                                 deltaFlipTime = 1000000;
831                                         break;
832                                 case 'F':       // Speed up sprite flipping;
833                                         deltaFlipTime = (deltaFlipTime * 100) / 110;
834                                         if (deltaFlipTime < 20000)
835                                                 deltaFlipTime = 20000;
836                                         break;
837                                 case 'n':       // Next connector
838                                 case 'N':
839                                         keep_moving = 0;
840                                         break;
841                                 default:
842                                         break;
843                                 }
844
845                                 // Purge unread characters
846                                 tcflush(0, TCIFLUSH);
847                         }
848
849                         // Wait for min of flip or move deltas
850                         SleepTime = (deltaFlipTime < deltaMoveTime) ?
851                                 deltaFlipTime : deltaMoveTime;
852                         usleep(SleepTime);
853                 }
854         }
855
856 out:
857         // Purge unread characters
858         tcflush(0, TCIFLUSH);
859         // Restore previous terminal settings
860         if (tcsetattr( 0, TCSANOW, &orig_term) != 0) {
861                 printf("tcgetattr failure: %s\n", strerror(errno));
862                 return;
863         }
864
865         drmModeFreeResources(gfx_resources);
866 }
867
868 static void usage(char *name)
869 {
870         printf("usage: %s -s <plane width>x<plane height> [-dhto]\n"
871                 "\t-d\t[optional] dump mode information\n"
872                 "\t-h\t[optional] output help message\n"
873                 "\t-t\t[optional] enable tiling\n"
874                 "\t-o\t[optional] <output rect width>x<output rect height>\n\n"
875                 "Keyboard control for sprite movement and flip rate ...\n"
876                 "\t'q' or 'Q' - Quit the program\n"
877                 "\t'n' or 'N' - Switch to next display\n"
878                 "\t's'        - Slow sprite movement\n"
879                 "\t'S'        - Speed up sprite movement\n"
880                 "\t'f'        - Slow sprite surface flipping\n"
881                 "\t'F'        - Speed up sprite surface flipping\n",
882                 name);
883 }
884
885 int main(int argc, char **argv)
886 {
887         int                 c;
888         int                 test_overlay = 0,
889                             enable_tiling = 0,
890                             dump_info = 0;
891         int                 plane_width = 0,
892                             plane_height = 0,
893                             out_width = 0,
894                             out_height = 0;
895         static char         optstr[] = "ds:o:th";
896
897         opterr = 0;
898         while ((c = getopt(argc, argv, optstr)) != -1) {
899                 switch (c) {
900                 case 'd':               // Dump information
901                         dump_info = 1;
902                         break;
903                 case 't':               // Tiling enable
904                         enable_tiling = 1;
905                         break;
906                 case 's':               // Surface dimensions
907                         if (sscanf(optarg, "%dx%d", &plane_width, &plane_height) != 2)
908                                 usage(argv[0]);
909                         test_overlay = 1;
910                         break;
911                 case 'o':               // Output dimensions
912                         if (sscanf(optarg, "%dx%d", &out_width, &out_height) != 2)
913                                 usage(argv[0]);
914                         break;
915                 default:
916                         printf("unknown option %c\n", c);
917                         /* fall through */
918                 case 'h':               // Help!
919                         usage(argv[0]);
920                         goto out;
921                 }
922         }
923
924         if (test_overlay) {
925                 if (out_width < (plane_width / 2))
926                         out_width = plane_width;
927
928                 if (out_height < (plane_height / 2))
929                         out_height = plane_height;
930
931                 ricochet(enable_tiling, plane_width, plane_height, out_width, out_height, dump_info);
932         } else {
933                 printf("Sprite dimensions are required:\n");
934                 usage(argv[0]);
935         }
936
937 out:
938         exit(0);
939 }