#include #include #include #include #include #include #include #include #include #include #include #include #include #include "gfx.h" #include "drmtools.h" /* ------------------------------------------------------------------ */ /* device */ static int fd; static drmModeConnector *conn = NULL; static drmModeEncoder *enc = NULL; static drmModeModeInfo *mode = NULL; static drmModeCrtc *scrtc = NULL; static uint32_t fb_id; /* dumb fb */ static struct drm_mode_create_dumb creq; static uint8_t *fbmem; /* ------------------------------------------------------------------ */ static const char *conn_type[] = { [ DRM_MODE_CONNECTOR_Unknown ] = "unknown", [ DRM_MODE_CONNECTOR_VGA ] = "vga", [ DRM_MODE_CONNECTOR_DVII ] = "dvi-i", [ DRM_MODE_CONNECTOR_DVID ] = "dvi-d", [ DRM_MODE_CONNECTOR_DVIA ] = "dvi-a", [ DRM_MODE_CONNECTOR_Composite ] = "composite", [ DRM_MODE_CONNECTOR_SVIDEO ] = "svideo", [ DRM_MODE_CONNECTOR_LVDS ] = "lvds", [ DRM_MODE_CONNECTOR_Component ] = "component", [ DRM_MODE_CONNECTOR_9PinDIN ] = "9pin-din", [ DRM_MODE_CONNECTOR_DisplayPort ] = "dp", [ DRM_MODE_CONNECTOR_HDMIA ] = "hdmi-a", [ DRM_MODE_CONNECTOR_HDMIB ] = "hdmi-b", [ DRM_MODE_CONNECTOR_TV ] = "tv", [ DRM_MODE_CONNECTOR_eDP ] = "edp", [ DRM_MODE_CONNECTOR_VIRTUAL ] = "virtual", [ DRM_MODE_CONNECTOR_DSI ] = "dsi", }; static void drm_conn_name(drmModeConnector *conn, char *dest, int dlen) { const char *type; if (conn->connector_type_id < sizeof(conn_type)/sizeof(conn_type[0]) && conn_type[conn->connector_type]) { type = conn_type[conn->connector_type]; } else { type = "unknown"; } snprintf(dest, dlen, "%s-%d", type, conn->connector_type_id); } /* ------------------------------------------------------------------ */ static void drm_cleanup_display(void) { /* restore crtc */ if (scrtc) { drmModeSetCrtc(fd, scrtc->crtc_id, scrtc->buffer_id, scrtc->x, scrtc->y, &conn->connector_id, 1, &scrtc->mode); } } static int drm_init_dev(const char *dev, const char *output) { drmModeRes *res; char name[64]; uint64_t has_dumb; int i, rc; /* open device */ fd = open(dev, O_RDWR); if (fd < 0) { fprintf(stderr, "drm: open %s: %s\n", dev, strerror(errno)); return -1; } rc = drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb); if (rc < 0 || !has_dumb) { fprintf(stderr, "drm: no dumb buffer support\n"); return -1; } /* find connector (using first for now) */ res = drmModeGetResources(fd); if (res == NULL) { fprintf(stderr, "drm: drmModeGetResources() failed\n"); return -1; } for (i = 0; i < res->count_connectors; i++) { conn = drmModeGetConnector(fd, res->connectors[i]); if (conn && (conn->connection == DRM_MODE_CONNECTED) && conn->count_modes) { if (output) { drm_conn_name(conn, name, sizeof(name)); if (strcmp(name, output) == 0) { break; } } else { break; } } drmModeFreeConnector(conn); conn = NULL; } if (!conn) { if (output) { fprintf(stderr, "drm: output %s not found or disconnected\n", output); } else { fprintf(stderr, "drm: no usable output found\n"); } return -1; } mode = &conn->modes[0]; enc = drmModeGetEncoder(fd, conn->encoder_id); if (enc == NULL) { fprintf(stderr, "drm: drmModeGetEncoder() failed\n"); return -1; } /* save crtc */ scrtc = drmModeGetCrtc(fd, enc->crtc_id); return 0; } static int drm_init_fb(void) { struct drm_mode_map_dumb mreq; int rc; /* create framebuffer */ memset(&creq, 0, sizeof(creq)); creq.width = mode->hdisplay; creq.height = mode->vdisplay; creq.bpp = 32; rc = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); if (rc < 0) { fprintf(stderr, "drm: DRM_IOCTL_MODE_CREATE_DUMB: %s\n", strerror(errno)); return -1; } rc = drmModeAddFB(fd, creq.width, creq.height, 24, 32, creq.pitch, creq.handle, &fb_id); if (rc < 0) { fprintf(stderr, "drm: drmModeAddFB() failed\n"); return -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: DRM_IOCTL_MODE_MAP_DUMB: %s\n", strerror(errno)); return -1; } fbmem = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mreq.offset); if (fbmem == MAP_FAILED) { fprintf(stderr, "drm: framebuffer mmap: %s\n", strerror(errno)); return -1; } return 0; } static int drm_show_fb(void) { int rc; rc = drmModeSetCrtc(fd, enc->crtc_id, fb_id, 0, 0, &conn->connector_id, 1, &conn->modes[0]); if (rc < 0) { fprintf(stderr, "drm: drmModeSetCrtc() failed\n"); return -1; } return 0; } /* ------------------------------------------------------------------ */ static void drm_restore_display(void) { drm_show_fb(); } static void drm_flush_display(void) { drmModeDirtyFB(fd, fb_id, 0, 0); } gfxstate *drm_init(const char *device, const char *output) { gfxstate *gfx; char dev[64]; if (device) { snprintf(dev, sizeof(dev), "%s", device); } else { snprintf(dev, sizeof(dev), DRM_DEV_NAME, DRM_DIR_NAME, 0); } fprintf(stderr, "trying drm: %s ...\n", dev); if (drm_init_dev(dev, output) < 0) return NULL; if (drm_init_fb() < 0) return NULL; if (drm_show_fb() < 0) return NULL; /* prepare gfx */ gfx = malloc(sizeof(*gfx)); memset(gfx, 0, sizeof(*gfx)); gfx->hdisplay = mode->hdisplay; gfx->vdisplay = mode->vdisplay; gfx->stride = creq.pitch; gfx->mem = fbmem; gfx->rlen = 8; gfx->glen = 8; gfx->blen = 8; gfx->tlen = 8; gfx->roff = 16; gfx->goff = 8; gfx->boff = 0; gfx->toff = 24; gfx->bits_per_pixel = 32; gfx->restore_display = drm_restore_display; gfx->cleanup_display = drm_cleanup_display; gfx->flush_display = drm_flush_display; return gfx; } void drm_info(const char *device) { drmModeConnector *conn; drmModeEncoder *enc; drmModeCrtc *crtc; drmModeRes *res; char name[64]; char dev[64]; int i; if (device) { snprintf(dev, sizeof(dev), "%s", device); } else { snprintf(dev, sizeof(dev), DRM_DEV_NAME, DRM_DIR_NAME, 0); } fd = open(dev, O_RDWR); if (fd < 0) { fprintf(stderr, "drm: open %s: %s\n", dev, strerror(errno)); return; } fprintf(stdout, "connectors for %s:\n", dev); res = drmModeGetResources(fd); if (res == NULL) { return; } for (i = 0; i < res->count_connectors; i++) { conn = drmModeGetConnector(fd, res->connectors[i]); if (!conn) continue; if (!conn->count_encoders) return; drm_conn_name(conn, name, sizeof(name)); enc = NULL; crtc = NULL; if (conn->encoder_id) { enc = drmModeGetEncoder(fd, conn->encoder_id); if (enc && enc->crtc_id) { crtc = drmModeGetCrtc(fd, enc->crtc_id); } } if (conn->connection == DRM_MODE_CONNECTED && crtc) { fprintf(stdout, " %s, connected, %dx%d\n", name, crtc->width, crtc->height); } else { fprintf(stdout, " %s, disconnected\n", name); } drmModeFreeCrtc(crtc); drmModeFreeEncoder(enc); drmModeFreeConnector(conn); } }