diff options
-rw-r--r-- | tests/testdisplay.c | 226 |
1 files changed, 221 insertions, 5 deletions
diff --git a/tests/testdisplay.c b/tests/testdisplay.c index cd83d9c..1eb9310 100644 --- a/tests/testdisplay.c +++ b/tests/testdisplay.c @@ -52,6 +52,7 @@ #include <errno.h> #include <math.h> #include <stdint.h> +#include <strings.h> #include <unistd.h> #include <sys/poll.h> #include <sys/time.h> @@ -68,7 +69,7 @@ drmModeRes *resources; int drm_fd, modes; int dump_info = 0, test_all_modes =0, test_preferred_mode = 0, force_mode = 0, - test_plane, enable_tiling; + test_plane, test_3d_modes, enable_tiling; int sleep_between_modes = 5; uint32_t depth = 24, stride, bpp; int qr_code = 0; @@ -154,8 +155,51 @@ struct connector { drmModeConnector *connector; int crtc; int pipe; + + /* stereo 3d */ + int s3d_format; + char s3d_image[32]; }; +static bool connector_expose_3d(uint32_t connector_id, bool enable) +{ + drmModeConnector *connector; + drmModePropertyRes *property; + bool status = false; + int i; + + connector = drmModeGetConnector(drm_fd, connector_id); + if (connector->count_props == 0) + return false; + + for (i = 0; i < connector->count_props; i++) { + property = drmModeGetProperty(drm_fd, connector->props[i]); + if (!property) + continue; + + if (strcmp(property->name, "expose 3D modes") == 0) { + if (drmModeConnectorSetProperty(drm_fd, + connector_id, + property->prop_id, + enable)) + fprintf(stderr, "failed to set the \"expose 3D " + "modes\" property on connector %d: %s\n", + connector_id, strerror(errno)); + else + status = true; + drmModeFreeProperty(property); + goto out; + } + + drmModeFreeProperty(property); + property = NULL; + } + +out: + drmModeFreeConnector(connector); + return status; +} + static void dump_connectors_fd(int drmfd) { int i, j; @@ -173,11 +217,13 @@ static void dump_connectors_fd(int drmfd) for (i = 0; i < mode_resources->count_connectors; i++) { drmModeConnector *connector; + connector_expose_3d(mode_resources->connectors[i], TRUE); + connector = drmModeGetConnector(drmfd, mode_resources->connectors[i]); if (!connector) { fprintf(stderr, "could not get connector %i: %s\n", mode_resources->connectors[i], strerror(errno)); - continue; + goto next; } printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\n", @@ -189,7 +235,7 @@ static void dump_connectors_fd(int drmfd) connector->count_modes); if (!connector->count_modes) - continue; + goto next; printf(" modes:\n"); printf(" name refresh (Hz) hdisp hss hse htot vdisp " @@ -200,6 +246,9 @@ static void dump_connectors_fd(int drmfd) } drmModeFreeConnector(connector); + +next: + connector_expose_3d(mode_resources->connectors[i], FALSE); } printf("\n"); @@ -563,6 +612,154 @@ set_mode(struct connector *c) drmModeFreeConnector(c->connector); } +static void +paint_3d_image(cairo_t *cr, int l_width, int l_height, void *priv) +{ + struct connector *c = priv; + cairo_surface_t *image; + + image = cairo_image_surface_create_from_png(c->s3d_image); + + cairo_set_source_surface(cr, image, 0, 0); + cairo_paint(cr); + + cairo_surface_destroy(image); +} + +static void adjust_3d_timings(drmModeModeInfo *mode, unsigned int format) +{ + uint16_t vdisplay, vactive_space; + + /* just set the 3D format we are setting (this is not used by the + * kernel, it's just for kmstest_dump_mode()) */ + mode->flags &= ~DRM_MODE_FLAG_3D_MASK; + mode->flags |= format; + + switch (format) { + case DRM_MODE_FLAG_3D_TOP_BOTTOM: + case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF: + return; + case DRM_MODE_FLAG_3D_FRAME_PACKING: + vactive_space = mode->vtotal - mode->vdisplay; + vdisplay = mode->vdisplay; + + mode->vdisplay += vdisplay + vactive_space; + mode->vsync_start += vdisplay + vactive_space; + mode->vsync_end += vdisplay + vactive_space; + mode->vtotal += vdisplay + vactive_space; + mode->clock = (mode->vtotal * mode->htotal * mode->vrefresh) / + 1000; + return; + default: + assert(0); + } +} + +static const char *s3d_format_str(unsigned int format) +{ + switch(format) { + case DRM_MODE_FLAG_3D_TOP_BOTTOM: + return "TB"; + case DRM_MODE_FLAG_3D_FRAME_PACKING: + return "FP"; + } + + return "Unknown format"; +} + +static void do_set_3d_format(struct connector *c, unsigned int format) +{ + uint32_t fb_id; + struct kmstest_fb fb_info; + + snprintf(c->s3d_image, sizeof(c->s3d_image), "%d%s.png", + c->mode.vdisplay, s3d_format_str(format)); + + adjust_3d_timings(&c->mode, format); + width = c->mode.hdisplay; + height = c->mode.vdisplay; + + fb_id = kmstest_create_fb(drm_fd, width, height, bpp, depth, + enable_tiling, &fb_info, + paint_3d_image, c); + + fb_ptr = gem_mmap(drm_fd, fb_info.gem_handle, + fb_info.size, PROT_READ | PROT_WRITE); + assert(fb_ptr); + + gem_close(drm_fd, fb_info.gem_handle); + + kmstest_dump_mode(&c->mode); + + if (drmModeSetCrtc(drm_fd, c->crtc, fb_id, 0, 0, + &c->id, 1, &c->mode)) { + fprintf(stderr, "failed to set mode (%dx%d@%dHz): %s\n", + width, height, c->mode.vrefresh, + strerror(errno)); + } +} + +static void +set_3d_modes(struct connector *c) +{ + int i; + + if (depth <= 8) + bpp = 8; + else if (depth > 8 && depth <= 16) + bpp = 16; + else if (depth > 16 && depth <= 32) + bpp = 32; + + connector_find_preferred_mode(c); + if (!c->mode_valid) + return; + + for (i = 0; i < c->connector->count_modes; i++) { + unsigned int s3d_formats, format; + + c->mode = c->connector->modes[i]; + + if (!c->mode_valid) + continue; + + if (c->mode.flags & DRM_MODE_FLAG_INTERLACE) + continue; + + s3d_formats = c->mode.flags & DRM_MODE_FLAG_3D_MASK; + if (!s3d_formats) + continue; + + do { + format = 1 << (ffs(s3d_formats) - 1); + + /* Modify the mode flags to specify which 3D format is + * being set. + * + * XXX: One would need to also clear the upper bits of + * flags in case extra modes/flags are added + */ + c->mode.flags &= ~DRM_MODE_FLAG_3D_MASK; + c->mode.flags |= format; + + do_set_3d_format(c, format); + + if (qr_code){ + set_single(); + pause(); + } else if (sleep_between_modes) { + sleep(sleep_between_modes); + } + + s3d_formats &= ~(format); + } while (s3d_formats); + + } + + drmModeFreeEncoder(c->encoder); + drmModeFreeConnector(c->connector); +} + /* * Re-probe outputs and light up as many as possible. * @@ -601,11 +798,26 @@ int update_display(void) set_mode(&connectors[c]); } } + + if (test_all_modes || test_3d_modes) { + /* Find connectors that can expose 3D modes */ + for (c = 0; c < resources->count_connectors; c++) { + connectors[c].id = resources->connectors[c]; + + if (!connector_expose_3d(connectors[c].id, TRUE)) + continue; + + set_3d_modes(&connectors[c]); + + connector_expose_3d(connectors[c].id, FALSE); + } + } + drmModeFreeResources(resources); return 1; } -static char optstr[] = "hiaf:s:d:p:mrto:"; +static char optstr[] = "3hiaf:s:d:p:mrto:"; static void __attribute__((noreturn)) usage(char *name) { @@ -616,6 +828,7 @@ static void __attribute__((noreturn)) usage(char *name) fprintf(stderr, "\t-d\t<depth>\tbit depth of scanout buffer\n"); fprintf(stderr, "\t-p\t<planew,h>,<crtcx,y>,<crtcw,h> test overlay plane\n"); fprintf(stderr, "\t-m\ttest the preferred mode\n"); + fprintf(stderr, "\t-3\ttest all 3D modes\n"); fprintf(stderr, "\t-t\tuse a tiled framebuffer\n"); fprintf(stderr, "\t-r\tprint a QR code on the screen whose content is \"pass\" for the automatic test\n"); fprintf(stderr, "\t-o\t<number of the mode>\tonly test specified mode\n"); @@ -675,6 +888,9 @@ int main(int argc, char **argv) opterr = 0; while ((c = getopt(argc, argv, optstr)) != -1) { switch (c) { + case '3': + test_3d_modes = 1; + break; case 'i': dump_info = 1; break; @@ -726,7 +942,7 @@ int main(int argc, char **argv) } } if (!test_all_modes && !force_mode && !dump_info && - !test_preferred_mode && !only_one_mode) + !test_preferred_mode && !only_one_mode && !test_3d_modes) test_all_modes = 1; drm_fd = drm_open_any(); |