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