#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ttytools.h" #include "drmtools.h" #include "render.h" #include "image.h" /* ------------------------------------------------------------------ */ /* dumb fb */ static struct drm_mode_create_dumb creq; static const struct fbformat *fmt = NULL; static uint8_t *fbmem; /* cairo + pixman */ static cairo_surface_t *cs; static pixman_image_t *pxcs; static pixman_image_t *pxfb; /* user options */ static cairo_surface_t *image; /* ------------------------------------------------------------------ */ static void drm_draw(void) { char name[64]; char info1[80], info2[80], info3[80]; cairo_t *cr; snprintf(info1, sizeof(info1), "drm driver: %s, v%d.%d.%d (%s)", version->name, version->version_major, version->version_minor, version->version_patchlevel, version->desc); drm_conn_name(conn, name, sizeof(name)); snprintf(info2, sizeof(info2), "%dx%d, output %.10s, %.10s mode", mode->hdisplay, mode->vdisplay, name, pxcs && pxfb ? "pixman" : "cairo"); if (fmt->fourcc) { snprintf(info3, sizeof(info3), "dumb drm buffer, bpp %d, fourcc %c%c%c%c (ADDFB2)", fmt->bpp, (fmt->fourcc >> 0) & 0xff, (fmt->fourcc >> 8) & 0xff, (fmt->fourcc >> 16) & 0xff, (fmt->fourcc >> 24) & 0xff); } else { snprintf(info3, sizeof(info3), "dumb drm buffer, bpp %d, depth %d (legacy ADDFB)", fmt->bpp, fmt->depth); } cr = cairo_create(cs); if (image) { render_image(cr, mode->hdisplay, mode->vdisplay, image); } else { render_test(cr, mode->hdisplay, mode->vdisplay, info1, info2, info3); } cairo_destroy(cr); if (pxcs && pxfb) { pixman_image_composite(PIXMAN_OP_SRC, pxcs, NULL, pxfb, 0, 0, 0, 0, 0, 0, mode->hdisplay, mode->vdisplay); } } /* ------------------------------------------------------------------ */ static void drm_init_dumb_fb(bool use_pixman) { struct drm_mode_map_dumb mreq; uint32_t zero = 0; int rc; /* create framebuffer */ memset(&creq, 0, sizeof(creq)); creq.width = mode->hdisplay; creq.height = mode->vdisplay; creq.bpp = fmt->bpp; rc = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); if (rc < 0) { fprintf(stderr, "DRM_IOCTL_MODE_CREATE_DUMB: %s\n", strerror(errno)); exit(1); } if (fmt->fourcc) { rc = drmModeAddFB2(fd, creq.width, creq.height, fmt->fourcc, &creq.handle, &creq.pitch, &zero, &fb_id, 0); if (rc < 0) { fprintf(stderr, "drmModeAddFB2() failed (fourcc %c%c%c%c)\n", (fmt->fourcc >> 0) & 0xff, (fmt->fourcc >> 8) & 0xff, (fmt->fourcc >> 16) & 0xff, (fmt->fourcc >> 24) & 0xff); exit(1); } } else { rc = drmModeAddFB(fd, creq.width, creq.height, fmt->depth, fmt->bpp, creq.pitch, creq.handle, &fb_id); if (rc < 0) { fprintf(stderr, "drmModeAddFB() failed (bpp %d, depth %d)\n", fmt->bpp, fmt->depth); exit(1); } } /* map framebuffer */ memset(&mreq, 0, sizeof(mreq)); mreq.handle = creq.handle; rc = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); if (rc < 0) { fprintf(stderr, "DRM_IOCTL_MODE_MAP_DUMB: %s\n", strerror(errno)); exit(1); } fbmem = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mreq.offset); if (fbmem == MAP_FAILED) { fprintf(stderr, "framebuffer mmap: %s\n", strerror(errno)); exit(1); } if (use_pixman) { pxfb = pixman_image_create_bits(fmt->pixman, creq.width, creq.height, (void*)fbmem, creq.pitch); pxcs = pixman_image_create_bits(PIXMAN_x2r10g10b10, creq.width, creq.height, NULL, 0); cs = cairo_image_surface_create_for_data((void*)pixman_image_get_data(pxcs), CAIRO_FORMAT_RGB30, creq.width, creq.height, pixman_image_get_stride(pxcs)); } else { cs = cairo_image_surface_create_for_data(fbmem, fmt->cairo, creq.width, creq.height, creq.pitch); } } static void drm_draw_dumb_fb(void) { drm_draw(); drmModeDirtyFB(fd, fb_id, 0, 0); } /* ------------------------------------------------------------------ */ static void usage(FILE *fp) { fprintf(fp, "\n" "usage: drmtest [ options ]\n" "\n" "options:\n" " -h print this\n" " -p pixman mode\n" " -c pick card\n" " -o pick output\n" " -s set sleep time\n" " -i load and display image \n" " -f pick framebuffer format\n" " -m pick video mode format\n" "\n"); } int main(int argc, char **argv) { int card = 0; int secs = 60; char *output = NULL; char *format = NULL; char *modename = NULL; char buf[32]; bool pixman = false; int c,i; for (;;) { c = getopt(argc, argv, "hpc:s:o:i:f:m:"); if (c == -1) break; switch (c) { case 'p': pixman = true; break; case 'c': card = atoi(optarg); break; case 's': secs = atoi(optarg); break; case 'i': image = load_image(optarg); break; case 'o': output = optarg; break; case 'f': format = optarg; break; case 'm': modename = optarg; break; case 'h': usage(stdout); exit(0); default: usage(stderr); exit(1); } } if (format) { for (i = 0; i < fmtcnt; i++) { if (strcmp(format, fmts[i].name) == 0) { fmt = &fmts[i]; } } if (!fmt) { fprintf(stderr, "unknown format %s, valid choices are:\n", format); for (i = 0; i < fmtcnt; i++) { if (pixman) { if (fmts[i].pixman == 0) continue; } else { if (fmts[i].cairo == CAIRO_FORMAT_INVALID) continue; } drm_print_format(stderr, &fmts[i], 4, false); } exit(1); } } drm_init_dev(card, output, modename, false); if (!fmt) { /* find first supported in list */ for (i = 0; i < fmtcnt; i++) { if (pixman) { if (fmts[i].pixman == 0) continue; } else { if (fmts[i].cairo == CAIRO_FORMAT_INVALID) continue; } if (!drm_probe_format(fd, &fmts[i])) continue; fmt = &fmts[i]; break; } if (!fmt) { drm_fini_dev(); fprintf(stderr, "No drm format (with cairo support) found.\n"); exit(1); } } if (fmt->cairo == CAIRO_FORMAT_INVALID) { if (fmt->pixman) { fprintf(stderr, "format not supported by cairo, enabling pixman mode\n"); pixman = true; } else { fprintf(stderr, "format not supported by cairo or pixman\n"); exit(1); } } drm_init_dumb_fb(pixman); drm_draw_dumb_fb(); drm_show_fb(); tty_raw(); kbd_wait(secs); read(0, buf, sizeof(buf)); tty_restore(); drm_fini_dev(); return 0; }