diff options
Diffstat (limited to 'src/drm.c')
-rw-r--r-- | src/drm.c | 233 |
1 files changed, 233 insertions, 0 deletions
@@ -2,11 +2,14 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <fcntl.h> +#include <unistd.h> #include <xf86drm.h> #include <xf86drmMode.h> #include "drm.h" +#include "egl.h" static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data) { @@ -51,3 +54,233 @@ struct drm_fb_xxx * drm_fb_get_from_bo(struct gbm_bo *bo) return fb; } + +static uint32_t find_crtc_for_encoder(const drmModeRes *resources, + const drmModeEncoder *encoder) { + int i; + + fprintf(stderr, "%s: not impl\n", __func__); + + return -1; /* no match found */ +} +static uint32_t find_crtc_for_connector(const struct drm *drm, const drmModeRes *resources, + const drmModeConnector *connector) { + int i; + + fprintf(stderr, "%s: not impl\n", __func__); + + return -1; /* no match found */ +} + +// call init_egl() after this. +static int init_drm(struct drm * drm, const char *device) +{ + drmModeRes *resources; + drmModeConnector *connector = NULL; + drmModeEncoder *encoder = NULL; + unsigned i, area; + uint64_t has_dumb; + + printf("using card '%s'\n", device); + + /* open the DRM device */ + drm->fd = open(device, O_RDWR | O_CLOEXEC); + if (drm->fd < 0) { + fprintf(stderr, "cannot open card\n"); + return -1; + } + if (drmGetCap(drm->fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0 || + !has_dumb) { + fprintf(stderr, "drm device '%s' does not support dumb buffers\n", device); + close(drm->fd); + return -EOPNOTSUPP; + } + + resources = drmModeGetResources(drm->fd); + if (!resources) { + fprintf(stderr, "connot retrieve DRM resources (%d): %m\n", errno); + return -errno; + } + + /* find a connected connector: */ + for (i = 0; i < resources->count_connectors; i++) { + connector = drmModeGetConnector(drm->fd, resources->connectors[i]); + if (connector->connection == DRM_MODE_CONNECTED) + break; /* it's connected, let's use this! */ + else + printf("ignoring unused connector %u\n", connector->connector_id); + + drmModeFreeConnector(connector); + connector = NULL; + } + + if (!connector) { + /* we could be fancy and listen for hotplug events and wait for + * a connector.. + */ + fprintf(stderr, "no connected connector!\n"); + return -1; + } + + /* find prefered mode or the highest resolution mode: */ + for (i = 0, area = 0; i < connector->count_modes; i++) { + drmModeModeInfo *current_mode = &connector->modes[i]; + if (current_mode->type & DRM_MODE_TYPE_PREFERRED) + drm->mode = current_mode; + + int current_area = current_mode->hdisplay * current_mode->vdisplay; + if (current_area > area) { + drm->mode = current_mode; + area = current_area; + } + } + + /* is there atleast one valid mode? */ + if (!drm->mode) { + fprintf(stderr, "no valid mode for connector %u\n", connector->connector_id); + drmModeFreeConnector(connector); + return -1; + } + + /* find encoder: */ + for (i = 0; i < resources->count_encoders; i++) { + encoder = drmModeGetEncoder(drm->fd, resources->encoders[i]); + if (encoder->encoder_id == connector->encoder_id) + break; + drmModeFreeEncoder(encoder); + encoder = NULL; + } + + if (encoder) { + drm->crtc_id = encoder->crtc_id; + } else { + uint32_t crtc_id = find_crtc_for_connector(drm,resources,connector); + if (crtc_id == 0) { + fprintf(stderr, "no crtc found!\n"); + return -1; + } + drm->crtc_id = crtc_id; + } + + /* find the crtc_id index and store it */ + for (i = 0; i < resources->count_crtcs; i++) + if (resources->crtcs[i] == drm->crtc_id) { + drm->crtc_index = i; + break; + } + + drmModeFreeResources(resources); + drm->connector_id = connector->connector_id; + + return 0; +} + +void deinit_drm(const struct drm * drm) +{ +#if 0 + /* restore saved CRTC */ + drmModeSetCrtc(fd, + iter->saved_crtc->crtc_id, + iter->saved_crtc->buffer_id, + iter->saved_crtc->x, + iter->saved_crtc->y, + &iter->conn, + 1, + &iter->saved_crtc->mode); + drmModeFreeCrtc(iter->saved_crtc); +#endif + close(drm->fd); +} + +/*********************************************************************/ + +/** global state machine */ +static struct drm g_drm; + +static void page_flip_handler(int fd, unsigned int frame, + unsigned int sec, unsigned int usec, void *data) +{ + int *waiting_for_flip = data; + *waiting_for_flip = 0; +} + +static int drm_run_flip_thingy(const struct gbm *gbm, const struct egl *egl) +{ + struct gbm_bo * bo; + struct drm_fb_xxx * fb; + int ret; + + drmEventContext evctx = { + .version = DRM_EVENT_CONTEXT_VERSION, + .page_flip_handler = page_flip_handler, + }; + + fd_set fds; + FD_ZERO(&fds); + FD_SET(0, &fds); + FD_SET(g_drm.fd, &fds); + + eglSwapBuffers(egl->display, egl->surface); + bo = gbm_surface_lock_front_buffer(gbm->surface); + fb = drm_fb_get_from_bo(bo); + + /* set mode: */ + ret = drmModeSetCrtc(g_drm.fd, g_drm.crtc_id, fb->fb_id, 0, 0, + &g_drm.connector_id, 1, g_drm.mode); + if (ret) { + fprintf(stderr, "failed to set mode: %s\n", strerror(errno)); + return ret; + } + + while (1) { + int waiting_for_flip = 1; + struct gbm_bo *next_bo; + + /* lets do our draw calls. */ + egl->draw(); + + eglSwapBuffers(egl->display, egl->surface); + next_bo = gbm_surface_lock_front_buffer(gbm->surface); + fb = drm_fb_get_from_bo(next_bo); + + /* + * Here you could also update drm plane layers if you want + * hw composition + */ + ret = drmModePageFlip(g_drm.fd, g_drm.crtc_id, fb->fb_id, + DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip); + if (ret) { + fprintf(stderr, "failed to queue page flip: %s\n", strerror(errno)); + return -1; + } + while (waiting_for_flip) { + ret = select(g_drm.fd + 1, &fds, NULL, NULL, NULL); + if (ret < 0) + return ret; + else if (ret == 0) + return -1; + else if (FD_ISSET(0, &fds)) + break; + drmHandleEvent(g_drm.fd, &evctx); + } + + /* release last buffer to render on again: */ + gbm_surface_release_buffer(gbm->surface, bo); + bo = next_bo; + } + + return 0; +} + +const struct drm * init_drm_runner(const char *device) +{ + int ret; + + ret = init_drm(&g_drm, device); + if (ret) + return NULL; + + g_drm.run = drm_run_flip_thingy; + + return &g_drm; +} |