Back to index

libdrm  2.4.37
modetest.c
Go to the documentation of this file.
00001 /*
00002  * DRM based mode setting test program
00003  * Copyright 2008 Tungsten Graphics
00004  *   Jakob Bornecrantz <jakob@tungstengraphics.com>
00005  * Copyright 2008 Intel Corporation
00006  *   Jesse Barnes <jesse.barnes@intel.com>
00007  *
00008  * Permission is hereby granted, free of charge, to any person obtaining a
00009  * copy of this software and associated documentation files (the "Software"),
00010  * to deal in the Software without restriction, including without limitation
00011  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00012  * and/or sell copies of the Software, and to permit persons to whom the
00013  * Software is furnished to do so, subject to the following conditions:
00014  *
00015  * The above copyright notice and this permission notice shall be included in
00016  * all copies or substantial portions of the Software.
00017  *
00018  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00019  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00020  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00021  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00022  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00023  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
00024  * IN THE SOFTWARE.
00025  */
00026 
00027 /*
00028  * This fairly simple test program dumps output in a similar format to the
00029  * "xrandr" tool everyone knows & loves.  It's necessarily slightly different
00030  * since the kernel separates outputs into encoder and connector structures,
00031  * each with their own unique ID.  The program also allows test testing of the
00032  * memory management and mode setting APIs by allowing the user to specify a
00033  * connector and mode to use for mode setting.  If all works as expected, a
00034  * blue background should be painted on the monitor attached to the specified
00035  * connector after the selected mode is set.
00036  *
00037  * TODO: use cairo to write the mode info on the selected output once
00038  *       the mode has been programmed, along with possible test patterns.
00039  */
00040 #include "config.h"
00041 
00042 #include <assert.h>
00043 #include <stdio.h>
00044 #include <stdlib.h>
00045 #include <stdint.h>
00046 #include <inttypes.h>
00047 #include <unistd.h>
00048 #include <string.h>
00049 #include <errno.h>
00050 #include <sys/poll.h>
00051 #include <sys/time.h>
00052 
00053 #include "xf86drm.h"
00054 #include "xf86drmMode.h"
00055 #include "drm_fourcc.h"
00056 #include "libkms.h"
00057 
00058 #ifdef HAVE_CAIRO
00059 #include <math.h>
00060 #include <cairo.h>
00061 #endif
00062 
00063 drmModeRes *resources;
00064 int fd, modes;
00065 
00066 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
00067 
00068 struct type_name {
00069        int type;
00070        char *name;
00071 };
00072 
00073 #define type_name_fn(res) \
00074 char * res##_str(int type) {                     \
00075        unsigned int i;                                  \
00076        for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
00077               if (res##_names[i].type == type)   \
00078                      return res##_names[i].name; \
00079        }                                         \
00080        return "(invalid)";                       \
00081 }
00082 
00083 struct type_name encoder_type_names[] = {
00084        { DRM_MODE_ENCODER_NONE, "none" },
00085        { DRM_MODE_ENCODER_DAC, "DAC" },
00086        { DRM_MODE_ENCODER_TMDS, "TMDS" },
00087        { DRM_MODE_ENCODER_LVDS, "LVDS" },
00088        { DRM_MODE_ENCODER_TVDAC, "TVDAC" },
00089 };
00090 
00091 type_name_fn(encoder_type)
00092 
00093 struct type_name connector_status_names[] = {
00094        { DRM_MODE_CONNECTED, "connected" },
00095        { DRM_MODE_DISCONNECTED, "disconnected" },
00096        { DRM_MODE_UNKNOWNCONNECTION, "unknown" },
00097 };
00098 
00099 type_name_fn(connector_status)
00100 
00101 struct type_name connector_type_names[] = {
00102        { DRM_MODE_CONNECTOR_Unknown, "unknown" },
00103        { DRM_MODE_CONNECTOR_VGA, "VGA" },
00104        { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
00105        { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
00106        { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
00107        { DRM_MODE_CONNECTOR_Composite, "composite" },
00108        { DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
00109        { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
00110        { DRM_MODE_CONNECTOR_Component, "component" },
00111        { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
00112        { DRM_MODE_CONNECTOR_DisplayPort, "displayport" },
00113        { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
00114        { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
00115        { DRM_MODE_CONNECTOR_TV, "TV" },
00116        { DRM_MODE_CONNECTOR_eDP, "embedded displayport" },
00117 };
00118 
00119 type_name_fn(connector_type)
00120 
00121 #define bit_name_fn(res)                                \
00122 char * res##_str(int type) {                                   \
00123        int i;                                           \
00124        const char *sep = "";                                   \
00125        for (i = 0; i < ARRAY_SIZE(res##_names); i++) {         \
00126               if (type & (1 << i)) {                           \
00127                      printf("%s%s", sep, res##_names[i]);      \
00128                      sep = ", ";                        \
00129               }                                         \
00130        }                                                \
00131 }
00132 
00133 static const char *mode_type_names[] = {
00134        "builtin",
00135        "clock_c",
00136        "crtc_c",
00137        "preferred",
00138        "default",
00139        "userdef",
00140        "driver",
00141 };
00142 
00143 bit_name_fn(mode_type)
00144 
00145 static const char *mode_flag_names[] = {
00146        "phsync",
00147        "nhsync",
00148        "pvsync",
00149        "nvsync",
00150        "interlace",
00151        "dblscan",
00152        "csync",
00153        "pcsync",
00154        "ncsync",
00155        "hskew",
00156        "bcast",
00157        "pixmux",
00158        "dblclk",
00159        "clkdiv2"
00160 };
00161 
00162 bit_name_fn(mode_flag)
00163 
00164 void dump_encoders(void)
00165 {
00166        drmModeEncoder *encoder;
00167        int i;
00168 
00169        printf("Encoders:\n");
00170        printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
00171        for (i = 0; i < resources->count_encoders; i++) {
00172               encoder = drmModeGetEncoder(fd, resources->encoders[i]);
00173 
00174               if (!encoder) {
00175                      fprintf(stderr, "could not get encoder %i: %s\n",
00176                             resources->encoders[i], strerror(errno));
00177                      continue;
00178               }
00179               printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
00180                      encoder->encoder_id,
00181                      encoder->crtc_id,
00182                      encoder_type_str(encoder->encoder_type),
00183                      encoder->possible_crtcs,
00184                      encoder->possible_clones);
00185               drmModeFreeEncoder(encoder);
00186        }
00187        printf("\n");
00188 }
00189 
00190 void dump_mode(drmModeModeInfo *mode)
00191 {
00192        printf("  %s %d %d %d %d %d %d %d %d %d",
00193               mode->name,
00194               mode->vrefresh,
00195               mode->hdisplay,
00196               mode->hsync_start,
00197               mode->hsync_end,
00198               mode->htotal,
00199               mode->vdisplay,
00200               mode->vsync_start,
00201               mode->vsync_end,
00202               mode->vtotal);
00203 
00204        printf(" flags: ");
00205        mode_flag_str(mode->flags);
00206        printf("; type: ");
00207        mode_type_str(mode->type);
00208        printf("\n");
00209 }
00210 
00211 static void
00212 dump_blob(uint32_t blob_id)
00213 {
00214        uint32_t i;
00215        unsigned char *blob_data;
00216        drmModePropertyBlobPtr blob;
00217 
00218        blob = drmModeGetPropertyBlob(fd, blob_id);
00219        if (!blob)
00220               return;
00221 
00222        blob_data = blob->data;
00223 
00224        for (i = 0; i < blob->length; i++) {
00225               if (i % 16 == 0)
00226                      printf("\n\t\t\t");
00227               printf("%.2hhx", blob_data[i]);
00228        }
00229        printf("\n");
00230 
00231        drmModeFreePropertyBlob(blob);
00232 }
00233 
00234 static void
00235 dump_prop(uint32_t prop_id, uint64_t value)
00236 {
00237        int i;
00238        drmModePropertyPtr prop;
00239 
00240        prop = drmModeGetProperty(fd, prop_id);
00241 
00242        printf("\t%d", prop_id);
00243        if (!prop) {
00244               printf("\n");
00245               return;
00246        }
00247 
00248        printf(" %s:\n", prop->name);
00249 
00250        printf("\t\tflags:");
00251        if (prop->flags & DRM_MODE_PROP_PENDING)
00252               printf(" pending");
00253        if (prop->flags & DRM_MODE_PROP_RANGE)
00254               printf(" range");
00255        if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
00256               printf(" immutable");
00257        if (prop->flags & DRM_MODE_PROP_ENUM)
00258               printf(" enum");
00259        if (prop->flags & DRM_MODE_PROP_BITMASK)
00260               printf(" bitmask");
00261        if (prop->flags & DRM_MODE_PROP_BLOB)
00262               printf(" blob");
00263        printf("\n");
00264 
00265        if (prop->flags & DRM_MODE_PROP_RANGE) {
00266               printf("\t\tvalues:");
00267               for (i = 0; i < prop->count_values; i++)
00268                      printf(" %"PRIu64, prop->values[i]);
00269               printf("\n");
00270        }
00271 
00272        if (prop->flags & DRM_MODE_PROP_ENUM) {
00273               printf("\t\tenums:");
00274               for (i = 0; i < prop->count_enums; i++)
00275                      printf(" %s=%llu", prop->enums[i].name,
00276                             prop->enums[i].value);
00277               printf("\n");
00278        } else if (prop->flags & DRM_MODE_PROP_BITMASK) {
00279               printf("\t\tvalues:");
00280               for (i = 0; i < prop->count_enums; i++)
00281                      printf(" %s=0x%llx", prop->enums[i].name,
00282                             (1LL << prop->enums[i].value));
00283               printf("\n");
00284        } else {
00285               assert(prop->count_enums == 0);
00286        }
00287 
00288        if (prop->flags & DRM_MODE_PROP_BLOB) {
00289               printf("\t\tblobs:\n");
00290               for (i = 0; i < prop->count_blobs; i++)
00291                      dump_blob(prop->blob_ids[i]);
00292               printf("\n");
00293        } else {
00294               assert(prop->count_blobs == 0);
00295        }
00296 
00297        printf("\t\tvalue:");
00298        if (prop->flags & DRM_MODE_PROP_BLOB)
00299               dump_blob(value);
00300        else
00301               printf(" %"PRIu64"\n", value);
00302 
00303        drmModeFreeProperty(prop);
00304 }
00305 
00306 void dump_connectors(void)
00307 {
00308        drmModeConnector *connector;
00309        int i, j;
00310 
00311        printf("Connectors:\n");
00312        printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n");
00313        for (i = 0; i < resources->count_connectors; i++) {
00314               connector = drmModeGetConnector(fd, resources->connectors[i]);
00315 
00316               if (!connector) {
00317                      fprintf(stderr, "could not get connector %i: %s\n",
00318                             resources->connectors[i], strerror(errno));
00319                      continue;
00320               }
00321 
00322               printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t",
00323                      connector->connector_id,
00324                      connector->encoder_id,
00325                      connector_status_str(connector->connection),
00326                      connector_type_str(connector->connector_type),
00327                      connector->mmWidth, connector->mmHeight,
00328                      connector->count_modes);
00329 
00330               for (j = 0; j < connector->count_encoders; j++)
00331                      printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
00332               printf("\n");
00333 
00334               if (connector->count_modes) {
00335                      printf("  modes:\n");
00336                      printf("\tname refresh (Hz) hdisp hss hse htot vdisp "
00337                             "vss vse vtot)\n");
00338                      for (j = 0; j < connector->count_modes; j++)
00339                             dump_mode(&connector->modes[j]);
00340 
00341                      printf("  props:\n");
00342                      for (j = 0; j < connector->count_props; j++)
00343                             dump_prop(connector->props[j],
00344                                      connector->prop_values[j]);
00345               }
00346 
00347               drmModeFreeConnector(connector);
00348        }
00349        printf("\n");
00350 }
00351 
00352 void dump_crtcs(void)
00353 {
00354        drmModeCrtc *crtc;
00355        drmModeObjectPropertiesPtr props;
00356        int i;
00357        uint32_t j;
00358 
00359        printf("CRTCs:\n");
00360        printf("id\tfb\tpos\tsize\n");
00361        for (i = 0; i < resources->count_crtcs; i++) {
00362               crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
00363 
00364               if (!crtc) {
00365                      fprintf(stderr, "could not get crtc %i: %s\n",
00366                             resources->crtcs[i], strerror(errno));
00367                      continue;
00368               }
00369               printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
00370                      crtc->crtc_id,
00371                      crtc->buffer_id,
00372                      crtc->x, crtc->y,
00373                      crtc->width, crtc->height);
00374               dump_mode(&crtc->mode);
00375 
00376               printf("  props:\n");
00377               props = drmModeObjectGetProperties(fd, crtc->crtc_id,
00378                                              DRM_MODE_OBJECT_CRTC);
00379               if (props) {
00380                      for (j = 0; j < props->count_props; j++)
00381                             dump_prop(props->props[j],
00382                                      props->prop_values[j]);
00383                      drmModeFreeObjectProperties(props);
00384               } else {
00385                      printf("\tcould not get crtc properties: %s\n",
00386                             strerror(errno));
00387               }
00388 
00389               drmModeFreeCrtc(crtc);
00390        }
00391        printf("\n");
00392 }
00393 
00394 void dump_framebuffers(void)
00395 {
00396        drmModeFB *fb;
00397        int i;
00398 
00399        printf("Frame buffers:\n");
00400        printf("id\tsize\tpitch\n");
00401        for (i = 0; i < resources->count_fbs; i++) {
00402               fb = drmModeGetFB(fd, resources->fbs[i]);
00403 
00404               if (!fb) {
00405                      fprintf(stderr, "could not get fb %i: %s\n",
00406                             resources->fbs[i], strerror(errno));
00407                      continue;
00408               }
00409               printf("%u\t(%ux%u)\t%u\n",
00410                      fb->fb_id,
00411                      fb->width, fb->height,
00412                      fb->pitch);
00413 
00414               drmModeFreeFB(fb);
00415        }
00416        printf("\n");
00417 }
00418 
00419 static void dump_planes(void)
00420 {
00421        drmModeObjectPropertiesPtr props;
00422        drmModePlaneRes *plane_resources;
00423        drmModePlane *ovr;
00424        unsigned int i, j;
00425 
00426        plane_resources = drmModeGetPlaneResources(fd);
00427        if (!plane_resources) {
00428               fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
00429                      strerror(errno));
00430               return;
00431        }
00432 
00433        printf("Planes:\n");
00434        printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\n");
00435        for (i = 0; i < plane_resources->count_planes; i++) {
00436               ovr = drmModeGetPlane(fd, plane_resources->planes[i]);
00437               if (!ovr) {
00438                      fprintf(stderr, "drmModeGetPlane failed: %s\n",
00439                             strerror(errno));
00440                      continue;
00441               }
00442 
00443               printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%d\n",
00444                      ovr->plane_id, ovr->crtc_id, ovr->fb_id,
00445                      ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
00446                      ovr->gamma_size);
00447 
00448               if (!ovr->count_formats)
00449                      continue;
00450 
00451               printf("  formats:");
00452               for (j = 0; j < ovr->count_formats; j++)
00453                      printf(" %4.4s", (char *)&ovr->formats[j]);
00454               printf("\n");
00455 
00456               printf("  props:\n");
00457               props = drmModeObjectGetProperties(fd, ovr->plane_id,
00458                                              DRM_MODE_OBJECT_PLANE);
00459               if (props) {
00460                      for (j = 0; j < props->count_props; j++)
00461                             dump_prop(props->props[j],
00462                                      props->prop_values[j]);
00463                      drmModeFreeObjectProperties(props);
00464               } else {
00465                      printf("\tcould not get plane properties: %s\n",
00466                             strerror(errno));
00467               }
00468 
00469               drmModeFreePlane(ovr);
00470        }
00471        printf("\n");
00472 
00473        drmModeFreePlaneResources(plane_resources);
00474        return;
00475 }
00476 
00477 /*
00478  * Mode setting with the kernel interfaces is a bit of a chore.
00479  * First you have to find the connector in question and make sure the
00480  * requested mode is available.
00481  * Then you need to find the encoder attached to that connector so you
00482  * can bind it with a free crtc.
00483  */
00484 struct connector {
00485        uint32_t id;
00486        char mode_str[64];
00487        drmModeModeInfo *mode;
00488        drmModeEncoder *encoder;
00489        int crtc;
00490        int pipe;
00491        unsigned int fb_id[2], current_fb_id;
00492        struct timeval start;
00493 
00494        int swap_count;
00495 };
00496 
00497 struct plane {
00498        uint32_t con_id;  /* the id of connector to bind to */
00499        uint32_t w, h;
00500        unsigned int fb_id;
00501        char format_str[5]; /* need to leave room for terminating \0 */
00502 };
00503 
00504 static void
00505 connector_find_mode(struct connector *c)
00506 {
00507        drmModeConnector *connector;
00508        int i, j;
00509 
00510        /* First, find the connector & mode */
00511        c->mode = NULL;
00512        for (i = 0; i < resources->count_connectors; i++) {
00513               connector = drmModeGetConnector(fd, resources->connectors[i]);
00514 
00515               if (!connector) {
00516                      fprintf(stderr, "could not get connector %i: %s\n",
00517                             resources->connectors[i], strerror(errno));
00518                      drmModeFreeConnector(connector);
00519                      continue;
00520               }
00521 
00522               if (!connector->count_modes) {
00523                      drmModeFreeConnector(connector);
00524                      continue;
00525               }
00526 
00527               if (connector->connector_id != c->id) {
00528                      drmModeFreeConnector(connector);
00529                      continue;
00530               }
00531 
00532               for (j = 0; j < connector->count_modes; j++) {
00533                      c->mode = &connector->modes[j];
00534                      if (!strcmp(c->mode->name, c->mode_str))
00535                             break;
00536               }
00537 
00538               /* Found it, break out */
00539               if (c->mode)
00540                      break;
00541 
00542               drmModeFreeConnector(connector);
00543        }
00544 
00545        if (!c->mode) {
00546               fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str);
00547               return;
00548        }
00549 
00550        /* Now get the encoder */
00551        for (i = 0; i < resources->count_encoders; i++) {
00552               c->encoder = drmModeGetEncoder(fd, resources->encoders[i]);
00553 
00554               if (!c->encoder) {
00555                      fprintf(stderr, "could not get encoder %i: %s\n",
00556                             resources->encoders[i], strerror(errno));
00557                      drmModeFreeEncoder(c->encoder);
00558                      continue;
00559               }
00560 
00561               if (c->encoder->encoder_id  == connector->encoder_id)
00562                      break;
00563 
00564               drmModeFreeEncoder(c->encoder);
00565        }
00566 
00567        if (c->crtc == -1)
00568               c->crtc = c->encoder->crtc_id;
00569 
00570        /* and figure out which crtc index it is: */
00571        for (i = 0; i < resources->count_crtcs; i++) {
00572               if (c->crtc == resources->crtcs[i]) {
00573                      c->pipe = i;
00574                      break;
00575               }
00576        }
00577 
00578 }
00579 
00580 static struct kms_bo *
00581 allocate_buffer(struct kms_driver *kms,
00582               int width, int height, int *stride)
00583 {
00584        struct kms_bo *bo;
00585        unsigned bo_attribs[] = {
00586               KMS_WIDTH,   0,
00587               KMS_HEIGHT,  0,
00588               KMS_BO_TYPE, KMS_BO_TYPE_SCANOUT_X8R8G8B8,
00589               KMS_TERMINATE_PROP_LIST
00590        };
00591        int ret;
00592 
00593        bo_attribs[1] = width;
00594        bo_attribs[3] = height;
00595 
00596        ret = kms_bo_create(kms, bo_attribs, &bo);
00597        if (ret) {
00598               fprintf(stderr, "failed to alloc buffer: %s\n",
00599                      strerror(-ret));
00600               return NULL;
00601        }
00602 
00603        ret = kms_bo_get_prop(bo, KMS_PITCH, stride);
00604        if (ret) {
00605               fprintf(stderr, "failed to retreive buffer stride: %s\n",
00606                      strerror(-ret));
00607               kms_bo_destroy(&bo);
00608               return NULL;
00609        }
00610 
00611        return bo;
00612 }
00613 
00614 static void
00615 make_pwetty(void *data, int width, int height, int stride)
00616 {
00617 #ifdef HAVE_CAIRO
00618        cairo_surface_t *surface;
00619        cairo_t *cr;
00620        int x, y;
00621 
00622        surface = cairo_image_surface_create_for_data(data,
00623                                                 CAIRO_FORMAT_ARGB32,
00624                                                 width, height,
00625                                                 stride);
00626        cr = cairo_create(surface);
00627        cairo_surface_destroy(surface);
00628 
00629        cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
00630        for (x = 0; x < width; x += 250)
00631               for (y = 0; y < height; y += 250) {
00632                      char buf[64];
00633 
00634                      cairo_move_to(cr, x, y - 20);
00635                      cairo_line_to(cr, x, y + 20);
00636                      cairo_move_to(cr, x - 20, y);
00637                      cairo_line_to(cr, x + 20, y);
00638                      cairo_new_sub_path(cr);
00639                      cairo_arc(cr, x, y, 10, 0, M_PI * 2);
00640                      cairo_set_line_width(cr, 4);
00641                      cairo_set_source_rgb(cr, 0, 0, 0);
00642                      cairo_stroke_preserve(cr);
00643                      cairo_set_source_rgb(cr, 1, 1, 1);
00644                      cairo_set_line_width(cr, 2);
00645                      cairo_stroke(cr);
00646 
00647                      snprintf(buf, sizeof buf, "%d, %d", x, y);
00648                      cairo_move_to(cr, x + 20, y + 20);
00649                      cairo_text_path(cr, buf);
00650                      cairo_set_source_rgb(cr, 0, 0, 0);
00651                      cairo_stroke_preserve(cr);
00652                      cairo_set_source_rgb(cr, 1, 1, 1);
00653                      cairo_fill(cr);
00654               }
00655 
00656        cairo_destroy(cr);
00657 #endif
00658 }
00659 
00660 static int
00661 create_test_buffer(struct kms_driver *kms,
00662                  int width, int height, int *stride_out,
00663                  struct kms_bo **bo_out)
00664 {
00665        struct kms_bo *bo;
00666        int ret, i, j, stride;
00667        void *virtual;
00668 
00669        bo = allocate_buffer(kms, width, height, &stride);
00670        if (!bo)
00671               return -1;
00672 
00673        ret = kms_bo_map(bo, &virtual);
00674        if (ret) {
00675               fprintf(stderr, "failed to map buffer: %s\n",
00676                      strerror(-ret));
00677               kms_bo_destroy(&bo);
00678               return -1;
00679        }
00680 
00681        /* paint the buffer with colored tiles */
00682        for (j = 0; j < height; j++) {
00683               uint32_t *fb_ptr = (uint32_t*)((char*)virtual + j * stride);
00684               for (i = 0; i < width; i++) {
00685                      div_t d = div(i, width);
00686                      fb_ptr[i] =
00687                             0x00130502 * (d.quot >> 6) +
00688                             0x000a1120 * (d.rem >> 6);
00689               }
00690        }
00691 
00692        make_pwetty(virtual, width, height, stride);
00693 
00694        kms_bo_unmap(bo);
00695 
00696        *bo_out = bo;
00697        *stride_out = stride;
00698        return 0;
00699 }
00700 
00701 static int
00702 create_grey_buffer(struct kms_driver *kms,
00703                  int width, int height, int *stride_out,
00704                  struct kms_bo **bo_out)
00705 {
00706        struct kms_bo *bo;
00707        int size, ret, stride;
00708        void *virtual;
00709 
00710        bo = allocate_buffer(kms, width, height, &stride);
00711        if (!bo)
00712               return -1;
00713 
00714        ret = kms_bo_map(bo, &virtual);
00715        if (ret) {
00716               fprintf(stderr, "failed to map buffer: %s\n",
00717                      strerror(-ret));
00718               kms_bo_destroy(&bo);
00719               return -1;
00720        }
00721 
00722        size = stride * height;
00723        memset(virtual, 0x77, size);
00724        kms_bo_unmap(bo);
00725 
00726        *bo_out = bo;
00727        *stride_out = stride;
00728 
00729        return 0;
00730 }
00731 
00732 void
00733 page_flip_handler(int fd, unsigned int frame,
00734                 unsigned int sec, unsigned int usec, void *data)
00735 {
00736        struct connector *c;
00737        unsigned int new_fb_id;
00738        struct timeval end;
00739        double t;
00740 
00741        c = data;
00742        if (c->current_fb_id == c->fb_id[0])
00743               new_fb_id = c->fb_id[1];
00744        else
00745               new_fb_id = c->fb_id[0];
00746                      
00747        drmModePageFlip(fd, c->crtc, new_fb_id,
00748                      DRM_MODE_PAGE_FLIP_EVENT, c);
00749        c->current_fb_id = new_fb_id;
00750        c->swap_count++;
00751        if (c->swap_count == 60) {
00752               gettimeofday(&end, NULL);
00753               t = end.tv_sec + end.tv_usec * 1e-6 -
00754                      (c->start.tv_sec + c->start.tv_usec * 1e-6);
00755               fprintf(stderr, "freq: %.02fHz\n", c->swap_count / t);
00756               c->swap_count = 0;
00757               c->start = end;
00758        }
00759 }
00760 
00761 /* swap these for big endian.. */
00762 #define RED   2
00763 #define GREEN 1
00764 #define BLUE  0
00765 
00766 static void
00767 fill420(unsigned char *y, unsigned char *u, unsigned char *v,
00768               int cs /*chroma pixel stride */,
00769               int n, int width, int height, int stride)
00770 {
00771        int i, j;
00772 
00773        /* paint the buffer with colored tiles, in blocks of 2x2 */
00774        for (j = 0; j < height; j+=2) {
00775               unsigned char *y1p = y + j * stride;
00776               unsigned char *y2p = y1p + stride;
00777               unsigned char *up = u + (j/2) * stride * cs / 2;
00778               unsigned char *vp = v + (j/2) * stride * cs / 2;
00779 
00780               for (i = 0; i < width; i+=2) {
00781                      div_t d = div(n+i+j, width);
00782                      uint32_t rgb = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
00783                      unsigned char *rgbp = (unsigned char *)&rgb;
00784                      unsigned char y = (0.299 * rgbp[RED]) + (0.587 * rgbp[GREEN]) + (0.114 * rgbp[BLUE]);
00785 
00786                      *(y2p++) = *(y1p++) = y;
00787                      *(y2p++) = *(y1p++) = y;
00788 
00789                      *up = (rgbp[BLUE] - y) * 0.565 + 128;
00790                      *vp = (rgbp[RED] - y) * 0.713 + 128;
00791                      up += cs;
00792                      vp += cs;
00793               }
00794        }
00795 }
00796 
00797 static void
00798 fill422(unsigned char *virtual, int n, int width, int height, int stride)
00799 {
00800        int i, j;
00801        /* paint the buffer with colored tiles */
00802        for (j = 0; j < height; j++) {
00803               uint8_t *ptr = (uint8_t*)((char*)virtual + j * stride);
00804               for (i = 0; i < width; i++) {
00805                      div_t d = div(n+i+j, width);
00806                      uint32_t rgb = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
00807                      unsigned char *rgbp = (unsigned char *)&rgb;
00808                      unsigned char y = (0.299 * rgbp[RED]) + (0.587 * rgbp[GREEN]) + (0.114 * rgbp[BLUE]);
00809 
00810                      *(ptr++) = y;
00811                      *(ptr++) = (rgbp[BLUE] - y) * 0.565 + 128;
00812                      *(ptr++) = y;
00813                      *(ptr++) = (rgbp[RED] - y) * 0.713 + 128;
00814               }
00815        }
00816 }
00817 
00818 static void
00819 fill1555(unsigned char *virtual, int n, int width, int height, int stride)
00820 {
00821        int i, j;
00822        /* paint the buffer with colored tiles */
00823        for (j = 0; j < height; j++) {
00824               uint16_t *ptr = (uint16_t*)((char*)virtual + j * stride);
00825               for (i = 0; i < width; i++) {
00826                      div_t d = div(n+i+j, width);
00827                      uint32_t rgb = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
00828                      unsigned char *rgbp = (unsigned char *)&rgb;
00829 
00830                      *(ptr++) = 0x8000 |
00831                                    (rgbp[RED] >> 3) << 10 |
00832                                    (rgbp[GREEN] >> 3) << 5 |
00833                                    (rgbp[BLUE] >> 3);
00834               }
00835        }
00836 }
00837 
00838 static int
00839 set_plane(struct kms_driver *kms, struct connector *c, struct plane *p)
00840 {
00841        drmModePlaneRes *plane_resources;
00842        drmModePlane *ovr;
00843        uint32_t handles[4], pitches[4], offsets[4] = {0}; /* we only use [0] */
00844        uint32_t plane_id = 0;
00845        struct kms_bo *plane_bo;
00846        uint32_t plane_flags = 0, format;
00847        int ret, crtc_x, crtc_y, crtc_w, crtc_h;
00848        unsigned int i;
00849 
00850        /* find an unused plane which can be connected to our crtc */
00851        plane_resources = drmModeGetPlaneResources(fd);
00852        if (!plane_resources) {
00853               fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
00854                      strerror(errno));
00855               return -1;
00856        }
00857 
00858        for (i = 0; i < plane_resources->count_planes && !plane_id; i++) {
00859               ovr = drmModeGetPlane(fd, plane_resources->planes[i]);
00860               if (!ovr) {
00861                      fprintf(stderr, "drmModeGetPlane failed: %s\n",
00862                             strerror(errno));
00863                      return -1;
00864               }
00865 
00866               if ((ovr->possible_crtcs & (1 << c->pipe)) && !ovr->crtc_id)
00867                      plane_id = ovr->plane_id;
00868 
00869               drmModeFreePlane(ovr);
00870        }
00871 
00872        fprintf(stderr, "testing %dx%d@%s overlay plane\n",
00873                      p->w, p->h, p->format_str);
00874 
00875        if (!plane_id) {
00876               fprintf(stderr, "failed to find plane!\n");
00877               return -1;
00878        }
00879 
00880        if (!strcmp(p->format_str, "XR24")) {
00881               if (create_test_buffer(kms, p->w, p->h, &pitches[0], &plane_bo))
00882                      return -1;
00883               kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
00884               format = DRM_FORMAT_XRGB8888;
00885        } else {
00886               void *virtual;
00887 
00888               /* TODO: this always allocates a buffer for 32bpp RGB.. but for
00889                * YUV formats, we don't use all of it..  since 4bytes/pixel is
00890                * worst case, so live with it for now and just don't use all
00891                * the buffer:
00892                */
00893               plane_bo = allocate_buffer(kms, p->w, p->h, &pitches[0]);
00894               if (!plane_bo)
00895                      return -1;
00896 
00897               ret = kms_bo_map(plane_bo, &virtual);
00898               if (ret) {
00899                      fprintf(stderr, "failed to map buffer: %s\n",
00900                             strerror(-ret));
00901                      kms_bo_destroy(&plane_bo);
00902                      return -1;
00903               }
00904 
00905               /* just testing a limited # of formats to test single
00906                * and multi-planar path.. would be nice to add more..
00907                */
00908               if (!strcmp(p->format_str, "YUYV")) {
00909                      pitches[0] = p->w * 2;
00910                      offsets[0] = 0;
00911                      kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
00912 
00913                      fill422(virtual, 0, p->w, p->h, pitches[0]);
00914 
00915                      format = DRM_FORMAT_YUYV;
00916               } else if (!strcmp(p->format_str, "NV12")) {
00917                      pitches[0] = p->w;
00918                      offsets[0] = 0;
00919                      kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
00920                      pitches[1] = p->w;
00921                      offsets[1] = p->w * p->h;
00922                      kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[1]);
00923 
00924                      fill420(virtual, virtual+offsets[1], virtual+offsets[1]+1,
00925                                    2, 0, p->w, p->h, pitches[0]);
00926 
00927                      format = DRM_FORMAT_NV12;
00928               } else if (!strcmp(p->format_str, "YV12")) {
00929                      pitches[0] = p->w;
00930                      offsets[0] = 0;
00931                      kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
00932                      pitches[1] = p->w / 2;
00933                      offsets[1] = p->w * p->h;
00934                      kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[1]);
00935                      pitches[2] = p->w / 2;
00936                      offsets[2] = offsets[1] + (p->w * p->h) / 4;
00937                      kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[2]);
00938 
00939                      fill420(virtual, virtual+offsets[1], virtual+offsets[2],
00940                                    1, 0, p->w, p->h, pitches[0]);
00941 
00942                      format = DRM_FORMAT_YVU420;
00943               } else if (!strcmp(p->format_str, "XR15")) {
00944                      pitches[0] = p->w * 2;
00945                      offsets[0] = 0;
00946                      kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
00947 
00948                      fill1555(virtual, 0, p->w, p->h, pitches[0]);
00949 
00950                      format = DRM_FORMAT_XRGB1555;
00951               } else if (!strcmp(p->format_str, "AR15")) {
00952                      pitches[0] = p->w * 2;
00953                      offsets[0] = 0;
00954                      kms_bo_get_prop(plane_bo, KMS_HANDLE, &handles[0]);
00955 
00956                      fill1555(virtual, 0, p->w, p->h, pitches[0]);
00957 
00958                      format = DRM_FORMAT_ARGB1555;
00959               } else {
00960                      fprintf(stderr, "Unknown format: %s\n", p->format_str);
00961                      return -1;
00962               }
00963 
00964               kms_bo_unmap(plane_bo);
00965        }
00966 
00967        /* just use single plane format for now.. */
00968        if (drmModeAddFB2(fd, p->w, p->h, format,
00969                      handles, pitches, offsets, &p->fb_id, plane_flags)) {
00970               fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
00971               return -1;
00972        }
00973 
00974        /* ok, boring.. but for now put in middle of screen: */
00975        crtc_x = c->mode->hdisplay / 3;
00976        crtc_y = c->mode->vdisplay / 3;
00977        crtc_w = crtc_x;
00978        crtc_h = crtc_y;
00979 
00980        /* note src coords (last 4 args) are in Q16 format */
00981        if (drmModeSetPlane(fd, plane_id, c->crtc, p->fb_id,
00982                          plane_flags, crtc_x, crtc_y, crtc_w, crtc_h,
00983                          0, 0, p->w << 16, p->h << 16)) {
00984               fprintf(stderr, "failed to enable plane: %s\n",
00985                      strerror(errno));
00986               return -1;
00987        }
00988 
00989        return 0;
00990 }
00991 
00992 static void
00993 set_mode(struct connector *c, int count, struct plane *p, int plane_count,
00994               int page_flip)
00995 {
00996        struct kms_driver *kms;
00997        struct kms_bo *bo, *other_bo;
00998        unsigned int fb_id, other_fb_id;
00999        int i, j, ret, width, height, x, stride;
01000        unsigned handle;
01001        drmEventContext evctx;
01002 
01003        width = 0;
01004        height = 0;
01005        for (i = 0; i < count; i++) {
01006               connector_find_mode(&c[i]);
01007               if (c[i].mode == NULL)
01008                      continue;
01009               width += c[i].mode->hdisplay;
01010               if (height < c[i].mode->vdisplay)
01011                      height = c[i].mode->vdisplay;
01012        }
01013 
01014        ret = kms_create(fd, &kms);
01015        if (ret) {
01016               fprintf(stderr, "failed to create kms driver: %s\n",
01017                      strerror(-ret));
01018               return;
01019        }
01020 
01021        if (create_test_buffer(kms, width, height, &stride, &bo))
01022               return;
01023 
01024        kms_bo_get_prop(bo, KMS_HANDLE, &handle);
01025        ret = drmModeAddFB(fd, width, height, 24, 32, stride, handle, &fb_id);
01026        if (ret) {
01027               fprintf(stderr, "failed to add fb (%ux%u): %s\n",
01028                      width, height, strerror(errno));
01029               return;
01030        }
01031 
01032        x = 0;
01033        for (i = 0; i < count; i++) {
01034               if (c[i].mode == NULL)
01035                      continue;
01036 
01037               printf("setting mode %s on connector %d, crtc %d\n",
01038                      c[i].mode_str, c[i].id, c[i].crtc);
01039 
01040               ret = drmModeSetCrtc(fd, c[i].crtc, fb_id, x, 0,
01041                                  &c[i].id, 1, c[i].mode);
01042 
01043               /* XXX: Actually check if this is needed */
01044               drmModeDirtyFB(fd, fb_id, NULL, 0);
01045 
01046               x += c[i].mode->hdisplay;
01047 
01048               if (ret) {
01049                      fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
01050                      return;
01051               }
01052 
01053               /* if we have a plane/overlay to show, set that up now: */
01054               for (j = 0; j < plane_count; j++)
01055                      if (p[j].con_id == c[i].id)
01056                             if (set_plane(kms, &c[i], &p[j]))
01057                                    return;
01058        }
01059 
01060        if (!page_flip)
01061               return;
01062        
01063        if (create_grey_buffer(kms, width, height, &stride, &other_bo))
01064               return;
01065 
01066        kms_bo_get_prop(other_bo, KMS_HANDLE, &handle);
01067        ret = drmModeAddFB(fd, width, height, 32, 32, stride, handle,
01068                         &other_fb_id);
01069        if (ret) {
01070               fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
01071               return;
01072        }
01073 
01074        for (i = 0; i < count; i++) {
01075               if (c[i].mode == NULL)
01076                      continue;
01077 
01078               ret = drmModePageFlip(fd, c[i].crtc, other_fb_id,
01079                                   DRM_MODE_PAGE_FLIP_EVENT, &c[i]);
01080               if (ret) {
01081                      fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
01082                      return;
01083               }
01084               gettimeofday(&c[i].start, NULL);
01085               c[i].swap_count = 0;
01086               c[i].fb_id[0] = fb_id;
01087               c[i].fb_id[1] = other_fb_id;
01088               c[i].current_fb_id = other_fb_id;
01089        }
01090 
01091        memset(&evctx, 0, sizeof evctx);
01092        evctx.version = DRM_EVENT_CONTEXT_VERSION;
01093        evctx.vblank_handler = NULL;
01094        evctx.page_flip_handler = page_flip_handler;
01095        
01096        while (1) {
01097 #if 0
01098               struct pollfd pfd[2];
01099 
01100               pfd[0].fd = 0;
01101               pfd[0].events = POLLIN;
01102               pfd[1].fd = fd;
01103               pfd[1].events = POLLIN;
01104 
01105               if (poll(pfd, 2, -1) < 0) {
01106                      fprintf(stderr, "poll error\n");
01107                      break;
01108               }
01109 
01110               if (pfd[0].revents)
01111                      break;
01112 #else
01113               struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
01114               fd_set fds;
01115               int ret;
01116 
01117               FD_ZERO(&fds);
01118               FD_SET(0, &fds);
01119               FD_SET(fd, &fds);
01120               ret = select(fd + 1, &fds, NULL, NULL, &timeout);
01121 
01122               if (ret <= 0) {
01123                      fprintf(stderr, "select timed out or error (ret %d)\n",
01124                             ret);
01125                      continue;
01126               } else if (FD_ISSET(0, &fds)) {
01127                      break;
01128               }
01129 #endif
01130 
01131               drmHandleEvent(fd, &evctx);
01132        }
01133 
01134        kms_bo_destroy(&bo);
01135        kms_bo_destroy(&other_bo);
01136        kms_destroy(&kms);
01137 }
01138 
01139 extern char *optarg;
01140 extern int optind, opterr, optopt;
01141 static char optstr[] = "ecpmfs:P:v";
01142 
01143 void usage(char *name)
01144 {
01145        fprintf(stderr, "usage: %s [-ecpmf]\n", name);
01146        fprintf(stderr, "\t-e\tlist encoders\n");
01147        fprintf(stderr, "\t-c\tlist connectors\n");
01148        fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
01149        fprintf(stderr, "\t-m\tlist modes\n");
01150        fprintf(stderr, "\t-f\tlist framebuffers\n");
01151        fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
01152        fprintf(stderr, "\t-s <connector_id>:<mode>\tset a mode\n");
01153        fprintf(stderr, "\t-s <connector_id>@<crtc_id>:<mode>\tset a mode\n");
01154        fprintf(stderr, "\t-P <connector_id>:<w>x<h>\tset a plane\n");
01155        fprintf(stderr, "\t-P <connector_id>:<w>x<h>@<format>\tset a plane\n");
01156        fprintf(stderr, "\n\tDefault is to dump all info.\n");
01157        exit(0);
01158 }
01159 
01160 #define dump_resource(res) if (res) dump_##res()
01161 
01162 static int page_flipping_supported(void)
01163 {
01164        /*FIXME: generic ioctl needed? */
01165        return 1;
01166 #if 0
01167        int ret, value;
01168        struct drm_i915_getparam gp;
01169 
01170        gp.param = I915_PARAM_HAS_PAGEFLIPPING;
01171        gp.value = &value;
01172 
01173        ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
01174        if (ret) {
01175               fprintf(stderr, "drm_i915_getparam: %m\n");
01176               return 0;
01177        }
01178 
01179        return *gp.value;
01180 #endif
01181 }
01182 
01183 int main(int argc, char **argv)
01184 {
01185        int c;
01186        int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
01187        int test_vsync = 0;
01188        char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos" };
01189        unsigned int i;
01190        int count = 0, plane_count = 0;
01191        struct connector con_args[2];
01192        struct plane plane_args[2] = {0};
01193        
01194        opterr = 0;
01195        while ((c = getopt(argc, argv, optstr)) != -1) {
01196               switch (c) {
01197               case 'e':
01198                      encoders = 1;
01199                      break;
01200               case 'c':
01201                      connectors = 1;
01202                      break;
01203               case 'p':
01204                      crtcs = 1;
01205                      planes = 1;
01206                      break;
01207               case 'm':
01208                      modes = 1;
01209                      break;
01210               case 'f':
01211                      framebuffers = 1;
01212                      break;
01213               case 'v':
01214                      test_vsync = 1;
01215                      break;
01216               case 's':
01217                      con_args[count].crtc = -1;
01218                      if (sscanf(optarg, "%d:%64s",
01219                                &con_args[count].id,
01220                                con_args[count].mode_str) != 2 &&
01221                          sscanf(optarg, "%d@%d:%64s",
01222                                &con_args[count].id,
01223                                &con_args[count].crtc,
01224                                con_args[count].mode_str) != 3)
01225                             usage(argv[0]);
01226                      count++;                                 
01227                      break;
01228               case 'P':
01229                      strcpy(plane_args[plane_count].format_str, "XR24");
01230                      if (sscanf(optarg, "%d:%dx%d@%4s",
01231                                    &plane_args[plane_count].con_id,
01232                                    &plane_args[plane_count].w,
01233                                    &plane_args[plane_count].h,
01234                                    plane_args[plane_count].format_str) != 4 &&
01235                             sscanf(optarg, "%d:%dx%d",
01236                                    &plane_args[plane_count].con_id,
01237                                    &plane_args[plane_count].w,
01238                                    &plane_args[plane_count].h) != 3)
01239                             usage(argv[0]);
01240                      plane_count++;
01241                      break;
01242               default:
01243                      usage(argv[0]);
01244                      break;
01245               }
01246        }
01247 
01248        if (argc == 1)
01249               encoders = connectors = crtcs = planes = modes = framebuffers = 1;
01250 
01251        for (i = 0; i < ARRAY_SIZE(modules); i++) {
01252               printf("trying to load module %s...", modules[i]);
01253               fd = drmOpen(modules[i], NULL);
01254               if (fd < 0) {
01255                      printf("failed.\n");
01256               } else {
01257                      printf("success.\n");
01258                      break;
01259               }
01260        }
01261 
01262        if (test_vsync && !page_flipping_supported()) {
01263               fprintf(stderr, "page flipping not supported by drm.\n");
01264               return -1;
01265        }
01266 
01267        if (i == ARRAY_SIZE(modules)) {
01268               fprintf(stderr, "failed to load any modules, aborting.\n");
01269               return -1;
01270        }
01271 
01272        resources = drmModeGetResources(fd);
01273        if (!resources) {
01274               fprintf(stderr, "drmModeGetResources failed: %s\n",
01275                      strerror(errno));
01276               drmClose(fd);
01277               return 1;
01278        }
01279 
01280        dump_resource(encoders);
01281        dump_resource(connectors);
01282        dump_resource(crtcs);
01283        dump_resource(planes);
01284        dump_resource(framebuffers);
01285 
01286        if (count > 0) {
01287               set_mode(con_args, count, plane_args, plane_count, test_vsync);
01288               getchar();
01289        }
01290 
01291        drmModeFreeResources(resources);
01292 
01293        return 0;
01294 }