summaryrefslogtreecommitdiff
path: root/src/drm_fb.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drm_fb.c')
-rw-r--r--src/drm_fb.c372
1 files changed, 356 insertions, 16 deletions
diff --git a/src/drm_fb.c b/src/drm_fb.c
index 99169de..855fce1 100644
--- a/src/drm_fb.c
+++ b/src/drm_fb.c
@@ -1,25 +1,28 @@
+#define _GNU_SOURCE /* for asprintf() */
+#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "fb.h"
+#include "settings.h"
#include "util.h"
-
/* drm fb backend, everything drm-specific in rototiller resides here. */
typedef struct drm_fb_t {
int drm_fd;
- uint32_t crtc_id;
- uint32_t *connectors;
- int n_connectors;
- drmModeModeInfoPtr mode;
+ drmModeCrtc *crtc;
+ drmModeConnector *connector;
+ drmModeModeInfo *mode;
} drm_fb_t;
typedef struct drm_fb_page_t drm_fb_page_t;
@@ -31,28 +34,362 @@ struct drm_fb_page_t {
uint32_t drm_fb_id;
};
+typedef struct drm_fb_setup_t {
+ const char *dev;
+ const char *connector;
+ const char *mode;
+} drm_fb_setup_t;
+
+
+static const char * connector_type_name(uint32_t type) {
+ static const char *connector_types[] = {
+ "Unknown",
+ "VGA",
+ "DVII",
+ "DVID",
+ "DVIA",
+ "Composite",
+ "SVIDEO",
+ "LVDS",
+ "Component",
+ "SPinDIN",
+ "DisplayPort",
+ "HDMIA",
+ "HDMIB",
+ "TV",
+ "eDP",
+ "VIRTUAL",
+ "DSI"
+ };
+
+ assert(type < nelems(connector_types));
+
+ return connector_types[type];
+}
+
+
+static setting_desc_t * dev_desc_generator(void *setup_context)
+{
+ return setting_desc_new("DRM Device Path",
+ "dev",
+ "/dev/dri/card[0-9]",
+ "/dev/dri/card0",
+ NULL,
+ NULL);
+}
+
+
+/* returns a NULL-terminated array of drm connectors */
+static const char ** get_connectors(const char *dev)
+{
+ int counts[64] = {}; /* assuming this is big enough */
+ char **connectors;
+ int i, fd;
+ drmModeRes *res;
+
+ assert(dev);
+
+ fd = open(dev, O_RDWR);
+ if (fd == -1)
+ return NULL;
+
+ res = drmModeGetResources(fd);
+ if (!res) {
+ close(fd);
+
+ return NULL;
+ }
+
+ connectors = calloc(res->count_connectors + 1, sizeof(*connectors));
+ if (!connectors) {
+ close(fd);
+
+ return NULL;
+ }
+
+ for (i = 0; i < res->count_connectors; i++) {
+ drmModeConnector *con;
+
+ con = drmModeGetConnector(fd, res->connectors[i]);
+ if (!con) {
+ close(fd);
+
+ return NULL;
+ }
+
+ counts[con->connector_type]++;
+ asprintf(&connectors[i], "%s-%i", connector_type_name(con->connector_type), counts[con->connector_type]); /* TODO: errors */
+
+ drmModeFreeConnector(con);
+ }
+
+ drmModeFreeResources(res);
+ close(fd);
+
+ return (const char **)connectors;
+}
+
+
+static void free_strv(const char **strv)
+{
+ int i;
+
+ for (i = 0; strv[i]; i++)
+ free((void *)strv[i]);
+
+ free((void *)strv);
+}
+
+
+static setting_desc_t * connector_desc_generator(void *setup_context)
+{
+ drm_fb_setup_t *s = setup_context;
+ const char **connectors;
+ setting_desc_t *desc;
+
+ connectors = get_connectors(s->dev);
+ if (!connectors)
+ return NULL;
+
+ desc = setting_desc_new("DRM Connector",
+ "connector",
+ "[a-zA-Z0-9]+",
+ connectors[0],
+ connectors,
+ NULL);
+
+ free_strv(connectors);
+
+ return desc;
+}
+
+
+static drmModeConnector * lookup_connector(int fd, const char *connector)
+{
+ int i, counts[64] = {}; /* assuming this is big enough */
+ drmModeConnector *con = NULL;
+ drmModeRes *res;
+
+ res = drmModeGetResources(fd);
+ if (!res)
+ goto _out;
+
+ for (i = 0; i < res->count_connectors; i++) {
+ char *str;
+
+ con = drmModeGetConnector(fd, res->connectors[i]);
+ if (!con)
+ goto _out_res;
+
+ counts[con->connector_type]++;
+ asprintf(&str, "%s-%i", connector_type_name(con->connector_type), counts[con->connector_type]); /* TODO: errors */
-drm_fb_t * drm_fb_new(int drm_fd, uint32_t crtc_id, uint32_t *connectors, int n_connectors, drmModeModeInfoPtr mode)
+ if (!strcasecmp(str, connector)) {
+ free(str);
+
+ break;
+ }
+
+ free(str);
+ drmModeFreeConnector(con);
+ con = NULL;
+ }
+
+_out_res:
+ drmModeFreeResources(res);
+_out:
+ return con;
+}
+
+
+/* returns a NULL-terminated array of drm modes for the supplied device and connector */
+static const char ** get_modes(const char *dev, const char *connector)
+{
+ char **modes = NULL;
+ int i, fd;
+ drmModeConnector *con;
+
+ assert(dev);
+ assert(connector);
+
+ fd = open(dev, O_RDWR);
+ if (fd == -1)
+ goto _out;
+
+ con = lookup_connector(fd, connector);
+ if (!con)
+ goto _out_fd;
+
+ modes = calloc(con->count_modes + 1, sizeof(*modes));
+ if (!modes)
+ goto _out_con;
+
+ for (i = 0; i < con->count_modes; i++)
+ asprintf(&modes[i], "%s@%"PRIu32, con->modes[i].name, con->modes[i].vrefresh);
+
+_out_con:
+ drmModeFreeConnector(con);
+_out_fd:
+ close(fd);
+_out:
+ return (const char **)modes;
+}
+
+
+static setting_desc_t * mode_desc_generator(void *setup_context)
+{
+ drm_fb_setup_t *s = setup_context;
+ setting_desc_t *desc;
+ const char **modes;
+
+ modes = get_modes(s->dev, s->connector);
+ if (!modes)
+ return NULL;
+
+ desc = setting_desc_new("DRM Video Mode",
+ "mode",
+ "[0-9]+[xX][0-9]+@[0-9]+",
+ modes[0],
+ modes,
+ NULL);
+
+ free_strv(modes);
+
+ return desc;
+}
+
+
+/* setup is called repeatedly as settings is constructed, until 0 is returned. */
+/* a negative value is returned on error */
+/* positive value indicates another setting is needed, described in next_setting */
+int drm_fb_setup(settings_t *settings, setting_desc_t **next_setting)
+{
+ drm_fb_setup_t context = {};
+ setting_desc_generator_t generators[] = {
+ {
+ .key = "dev",
+ .value_ptr = &context.dev,
+ .func = dev_desc_generator
+ }, {
+ .key = "connector",
+ .value_ptr = &context.connector,
+ .func = connector_desc_generator
+ }, {
+ .key = "mode",
+ .value_ptr = &context.mode,
+ .func = mode_desc_generator
+ },
+ };
+
+ if (!drmAvailable())
+ return -ENOSYS;
+
+ return settings_apply_desc_generators(settings, generators, nelems(generators), &context, next_setting);
+}
+
+
+/* lookup a mode string in the given connector returning its respective modeinfo */
+static drmModeModeInfo * lookup_mode(drmModeConnector *connector, const char *mode)
+{
+ int i;
+
+ assert(connector);
+ assert(mode);
+
+ for (i = 0; i < connector->count_modes; i++) {
+ char *str;
+
+ asprintf(&str, "%s@%"PRIu32, connector->modes[i].name, connector->modes[i].vrefresh);
+ if (!strcasecmp(str, mode)) {
+ free(str);
+
+ return &connector->modes[i];
+ }
+
+ free(str);
+ }
+
+ return NULL;
+}
+
+
+/* prepare the drm context for use with the supplied settings */
+void * drm_fb_init(settings_t *settings)
{
drm_fb_t *c;
+ const char *dev;
+ const char *connector;
+ const char *mode;
+ drmModeEncoder *enc;
+
+ assert(settings);
+
+ if (!drmAvailable())
+ goto _err;
+
+ dev = settings_get_value(settings, "dev");
+ if (!dev)
+ goto _err;
+
+ connector = settings_get_value(settings, "connector");
+ if (!connector)
+ goto _err;
+
+ mode = settings_get_value(settings, "mode");
+ if (!mode)
+ goto _err;
c = calloc(1, sizeof(drm_fb_t));
if (!c)
- return NULL;
+ goto _err;
+
+ c->drm_fd = open(dev, O_RDWR);
+ if (c->drm_fd < 0)
+ goto _err_ctxt;
+
+ c->connector = lookup_connector(c->drm_fd, connector);
+ if (!c->connector)
+ goto _err_fd;
+
+ c->mode = lookup_mode(c->connector, mode);
+ if (!c->mode)
+ goto _err_con;
- c->drm_fd = drm_fd;
- c->crtc_id = crtc_id;
- c->connectors = connectors;
- c->n_connectors = n_connectors;
- c->mode = mode;
+ enc = drmModeGetEncoder(c->drm_fd, c->connector->encoder_id);
+ if (!enc)
+ goto _err_con;
+
+ c->crtc = drmModeGetCrtc(c->drm_fd, enc->crtc_id);
+ if (!c->crtc)
+ goto _err_enc;
+
+ drmModeFreeEncoder(enc);
return c;
+
+_err_enc:
+ drmModeFreeEncoder(enc);
+_err_con:
+ drmModeFreeConnector(c->connector);
+_err_fd:
+ close(c->drm_fd);
+_err_ctxt:
+ free(c);
+_err:
+ return NULL;
}
-void drm_fb_free(drm_fb_t *context)
+void drm_fb_shutdown(void *context)
{
- free(context);
+ drm_fb_t *c = context;
+
+ assert(c);
+
+ close(c->drm_fd);
+ drmModeFreeConnector(c->connector);
+ drmModeFreeCrtc(c->crtc);
+ free(c);
}
@@ -61,7 +398,7 @@ static int drm_fb_acquire(void *context, void *page)
drm_fb_t *c = context;
drm_fb_page_t *p = page;
- return drmModeSetCrtc(c->drm_fd, c->crtc_id, p->drm_fb_id, 0, 0, c->connectors, c->n_connectors, c->mode);
+ return drmModeSetCrtc(c->drm_fd, c->crtc->crtc_id, p->drm_fb_id, 0, 0, &c->connector->connector_id, 1, c->mode);
}
@@ -141,7 +478,7 @@ static int drm_fb_page_flip(void *context, void *page)
drm_fb_t *c = context;
drm_fb_page_t *p = page;
- if (drmModePageFlip(c->drm_fd, c->crtc_id, p->drm_fb_id, DRM_MODE_PAGE_FLIP_EVENT, NULL) < 0)
+ if (drmModePageFlip(c->drm_fd, c->crtc->crtc_id, p->drm_fb_id, DRM_MODE_PAGE_FLIP_EVENT, NULL) < 0)
return -1;
return drmHandleEvent(c->drm_fd, &drm_ev_ctx);
@@ -149,6 +486,9 @@ static int drm_fb_page_flip(void *context, void *page)
fb_ops_t drm_fb_ops = {
+ .setup = drm_fb_setup,
+ .init = drm_fb_init,
+ .shutdown = drm_fb_shutdown,
.acquire = drm_fb_acquire,
.release = drm_fb_release,
.page_alloc = drm_fb_page_alloc,
© All Rights Reserved