summaryrefslogtreecommitdiff
path: root/src/drm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drm.c')
-rw-r--r--src/drm.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/drm.c b/src/drm.c
index b70c6fd..18c2dae 100644
--- a/src/drm.c
+++ b/src/drm.c
@@ -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;
+}