summaryrefslogtreecommitdiff
path: root/src/gallium/state_trackers/egl/drm/modeset.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gallium/state_trackers/egl/drm/modeset.c')
-rw-r--r--src/gallium/state_trackers/egl/drm/modeset.c619
1 files changed, 619 insertions, 0 deletions
diff --git a/src/gallium/state_trackers/egl/drm/modeset.c b/src/gallium/state_trackers/egl/drm/modeset.c
new file mode 100644
index 0000000000..06a6077053
--- /dev/null
+++ b/src/gallium/state_trackers/egl/drm/modeset.c
@@ -0,0 +1,619 @@
+/*
+ * Mesa 3-D graphics library
+ * Version: 7.9
+ *
+ * Copyright (C) 2010 LunarG Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Chia-I Wu <olv@lunarg.com>
+ */
+
+#include "util/u_memory.h"
+#include "util/u_inlines.h"
+#include "egllog.h"
+
+#include "native_drm.h"
+
+static boolean
+drm_surface_validate(struct native_surface *nsurf, uint attachment_mask,
+ unsigned int *seq_num, struct pipe_resource **textures,
+ int *width, int *height)
+{
+ struct drm_surface *drmsurf = drm_surface(nsurf);
+
+ if (!resource_surface_add_resources(drmsurf->rsurf, attachment_mask))
+ return FALSE;
+ if (textures)
+ resource_surface_get_resources(drmsurf->rsurf, textures, attachment_mask);
+
+ if (seq_num)
+ *seq_num = drmsurf->sequence_number;
+ if (width)
+ *width = drmsurf->width;
+ if (height)
+ *height = drmsurf->height;
+
+ return TRUE;
+}
+
+/**
+ * Add textures as DRM framebuffers.
+ */
+static boolean
+drm_surface_init_framebuffers(struct native_surface *nsurf, boolean need_back)
+{
+ struct drm_surface *drmsurf = drm_surface(nsurf);
+ struct drm_display *drmdpy = drmsurf->drmdpy;
+ int num_framebuffers = (need_back) ? 2 : 1;
+ int i, err;
+
+ for (i = 0; i < num_framebuffers; i++) {
+ struct drm_framebuffer *fb;
+ enum native_attachment natt;
+ struct winsys_handle whandle;
+ uint block_bits;
+
+ if (i == 0) {
+ fb = &drmsurf->front_fb;
+ natt = NATIVE_ATTACHMENT_FRONT_LEFT;
+ }
+ else {
+ fb = &drmsurf->back_fb;
+ natt = NATIVE_ATTACHMENT_BACK_LEFT;
+ }
+
+ if (!fb->texture) {
+ /* make sure the texture has been allocated */
+ resource_surface_add_resources(drmsurf->rsurf, 1 << natt);
+ fb->texture =
+ resource_surface_get_single_resource(drmsurf->rsurf, natt);
+ if (!fb->texture)
+ return FALSE;
+ }
+
+ /* already initialized */
+ if (fb->buffer_id)
+ continue;
+
+ /* TODO detect the real value */
+ fb->is_passive = TRUE;
+
+ memset(&whandle, 0, sizeof(whandle));
+ whandle.type = DRM_API_HANDLE_TYPE_KMS;
+
+ if (!drmdpy->base.screen->resource_get_handle(drmdpy->base.screen,
+ fb->texture, &whandle))
+ return FALSE;
+
+ block_bits = util_format_get_blocksizebits(drmsurf->color_format);
+ err = drmModeAddFB(drmdpy->fd, drmsurf->width, drmsurf->height,
+ block_bits, block_bits, whandle.stride, whandle.handle,
+ &fb->buffer_id);
+ if (err) {
+ fb->buffer_id = 0;
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static boolean
+drm_surface_flush_frontbuffer(struct native_surface *nsurf)
+{
+#ifdef DRM_MODE_FEATURE_DIRTYFB
+ struct drm_surface *drmsurf = drm_surface(nsurf);
+ struct drm_display *drmdpy = drmsurf->drmdpy;
+
+ if (drmsurf->front_fb.is_passive)
+ drmModeDirtyFB(drmdpy->fd, drmsurf->front_fb.buffer_id, NULL, 0);
+#endif
+
+ return TRUE;
+}
+
+static boolean
+drm_surface_swap_buffers(struct native_surface *nsurf)
+{
+ struct drm_surface *drmsurf = drm_surface(nsurf);
+ struct drm_crtc *drmcrtc = &drmsurf->current_crtc;
+ struct drm_display *drmdpy = drmsurf->drmdpy;
+ struct drm_framebuffer tmp_fb;
+ int err;
+
+ if (!drmsurf->back_fb.buffer_id) {
+ if (!drm_surface_init_framebuffers(&drmsurf->base, TRUE))
+ return FALSE;
+ }
+
+ if (drmsurf->is_shown && drmcrtc->crtc) {
+ err = drmModeSetCrtc(drmdpy->fd, drmcrtc->crtc->crtc_id,
+ drmsurf->back_fb.buffer_id, drmcrtc->crtc->x, drmcrtc->crtc->y,
+ drmcrtc->connectors, drmcrtc->num_connectors, &drmcrtc->crtc->mode);
+ if (err)
+ return FALSE;
+ }
+
+ /* swap the buffers */
+ tmp_fb = drmsurf->front_fb;
+ drmsurf->front_fb = drmsurf->back_fb;
+ drmsurf->back_fb = tmp_fb;
+
+ resource_surface_swap_buffers(drmsurf->rsurf,
+ NATIVE_ATTACHMENT_FRONT_LEFT, NATIVE_ATTACHMENT_BACK_LEFT, FALSE);
+ /* the front/back textures are swapped */
+ drmsurf->sequence_number++;
+ drmdpy->event_handler->invalid_surface(&drmdpy->base,
+ &drmsurf->base, drmsurf->sequence_number);
+
+ return TRUE;
+}
+
+static void
+drm_surface_wait(struct native_surface *nsurf)
+{
+ /* no-op */
+}
+
+static void
+drm_surface_destroy(struct native_surface *nsurf)
+{
+ struct drm_surface *drmsurf = drm_surface(nsurf);
+
+ if (drmsurf->current_crtc.crtc)
+ drmModeFreeCrtc(drmsurf->current_crtc.crtc);
+
+ if (drmsurf->front_fb.buffer_id)
+ drmModeRmFB(drmsurf->drmdpy->fd, drmsurf->front_fb.buffer_id);
+ pipe_resource_reference(&drmsurf->front_fb.texture, NULL);
+
+ if (drmsurf->back_fb.buffer_id)
+ drmModeRmFB(drmsurf->drmdpy->fd, drmsurf->back_fb.buffer_id);
+ pipe_resource_reference(&drmsurf->back_fb.texture, NULL);
+
+ resource_surface_destroy(drmsurf->rsurf);
+ FREE(drmsurf);
+}
+
+static struct drm_surface *
+drm_display_create_surface(struct native_display *ndpy,
+ const struct native_config *nconf,
+ uint width, uint height)
+{
+ struct drm_display *drmdpy = drm_display(ndpy);
+ struct drm_config *drmconf = drm_config(nconf);
+ struct drm_surface *drmsurf;
+
+ drmsurf = CALLOC_STRUCT(drm_surface);
+ if (!drmsurf)
+ return NULL;
+
+ drmsurf->drmdpy = drmdpy;
+ drmsurf->color_format = drmconf->base.color_format;
+ drmsurf->width = width;
+ drmsurf->height = height;
+
+ drmsurf->rsurf = resource_surface_create(drmdpy->base.screen,
+ drmsurf->color_format,
+ PIPE_BIND_RENDER_TARGET |
+ PIPE_BIND_SAMPLER_VIEW |
+ PIPE_BIND_DISPLAY_TARGET |
+ PIPE_BIND_SCANOUT);
+ if (!drmsurf->rsurf) {
+ FREE(drmsurf);
+ return NULL;
+ }
+
+ resource_surface_set_size(drmsurf->rsurf, drmsurf->width, drmsurf->height);
+
+ drmsurf->base.destroy = drm_surface_destroy;
+ drmsurf->base.swap_buffers = drm_surface_swap_buffers;
+ drmsurf->base.flush_frontbuffer = drm_surface_flush_frontbuffer;
+ drmsurf->base.validate = drm_surface_validate;
+ drmsurf->base.wait = drm_surface_wait;
+
+ return drmsurf;
+}
+
+/**
+ * Choose a CRTC that supports all given connectors.
+ */
+static uint32_t
+drm_display_choose_crtc(struct native_display *ndpy,
+ uint32_t *connectors, int num_connectors)
+{
+ struct drm_display *drmdpy = drm_display(ndpy);
+ int idx;
+
+ for (idx = 0; idx < drmdpy->resources->count_crtcs; idx++) {
+ boolean found_crtc = TRUE;
+ int i, j;
+
+ for (i = 0; i < num_connectors; i++) {
+ drmModeConnectorPtr connector;
+ int encoder_idx = -1;
+
+ connector = drmModeGetConnector(drmdpy->fd, connectors[i]);
+ if (!connector) {
+ found_crtc = FALSE;
+ break;
+ }
+
+ /* find an encoder the CRTC supports */
+ for (j = 0; j < connector->count_encoders; j++) {
+ drmModeEncoderPtr encoder =
+ drmModeGetEncoder(drmdpy->fd, connector->encoders[j]);
+ if (encoder->possible_crtcs & (1 << idx)) {
+ encoder_idx = j;
+ break;
+ }
+ drmModeFreeEncoder(encoder);
+ }
+
+ drmModeFreeConnector(connector);
+ if (encoder_idx < 0) {
+ found_crtc = FALSE;
+ break;
+ }
+ }
+
+ if (found_crtc)
+ break;
+ }
+
+ if (idx >= drmdpy->resources->count_crtcs) {
+ _eglLog(_EGL_WARNING,
+ "failed to find a CRTC that supports the given %d connectors",
+ num_connectors);
+ return 0;
+ }
+
+ return drmdpy->resources->crtcs[idx];
+}
+
+/**
+ * Remember the original CRTC status and set the CRTC
+ */
+static boolean
+drm_display_set_crtc(struct native_display *ndpy, int crtc_idx,
+ uint32_t buffer_id, uint32_t x, uint32_t y,
+ uint32_t *connectors, int num_connectors,
+ drmModeModeInfoPtr mode)
+{
+ struct drm_display *drmdpy = drm_display(ndpy);
+ struct drm_crtc *drmcrtc = &drmdpy->saved_crtcs[crtc_idx];
+ uint32_t crtc_id;
+ int err;
+
+ if (drmcrtc->crtc) {
+ crtc_id = drmcrtc->crtc->crtc_id;
+ }
+ else {
+ int count = 0, i;
+
+ /*
+ * Choose the CRTC once. It could be more dynamic, but let's keep it
+ * simple for now.
+ */
+ crtc_id = drm_display_choose_crtc(&drmdpy->base,
+ connectors, num_connectors);
+
+ /* save the original CRTC status */
+ drmcrtc->crtc = drmModeGetCrtc(drmdpy->fd, crtc_id);
+ if (!drmcrtc->crtc)
+ return FALSE;
+
+ for (i = 0; i < drmdpy->num_connectors; i++) {
+ struct drm_connector *drmconn = &drmdpy->connectors[i];
+ drmModeConnectorPtr connector = drmconn->connector;
+ drmModeEncoderPtr encoder;
+
+ encoder = drmModeGetEncoder(drmdpy->fd, connector->encoder_id);
+ if (encoder) {
+ if (encoder->crtc_id == crtc_id) {
+ drmcrtc->connectors[count++] = connector->connector_id;
+ if (count >= Elements(drmcrtc->connectors))
+ break;
+ }
+ drmModeFreeEncoder(encoder);
+ }
+ }
+
+ drmcrtc->num_connectors = count;
+ }
+
+ err = drmModeSetCrtc(drmdpy->fd, crtc_id, buffer_id, x, y,
+ connectors, num_connectors, mode);
+ if (err) {
+ drmModeFreeCrtc(drmcrtc->crtc);
+ drmcrtc->crtc = NULL;
+ drmcrtc->num_connectors = 0;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static boolean
+drm_display_program(struct native_display *ndpy, int crtc_idx,
+ struct native_surface *nsurf, uint x, uint y,
+ const struct native_connector **nconns, int num_nconns,
+ const struct native_mode *nmode)
+{
+ struct drm_display *drmdpy = drm_display(ndpy);
+ struct drm_surface *drmsurf = drm_surface(nsurf);
+ const struct drm_mode *drmmode = drm_mode(nmode);
+ uint32_t connector_ids[32];
+ uint32_t buffer_id;
+ drmModeModeInfo mode_tmp, *mode;
+ int i;
+
+ if (num_nconns > Elements(connector_ids)) {
+ _eglLog(_EGL_WARNING, "too many connectors (%d)", num_nconns);
+ num_nconns = Elements(connector_ids);
+ }
+
+ if (drmsurf) {
+ if (!drm_surface_init_framebuffers(&drmsurf->base, FALSE))
+ return FALSE;
+
+ buffer_id = drmsurf->front_fb.buffer_id;
+ /* the mode argument of drmModeSetCrtc is not constified */
+ mode_tmp = drmmode->mode;
+ mode = &mode_tmp;
+ }
+ else {
+ /* disable the CRTC */
+ buffer_id = 0;
+ mode = NULL;
+ num_nconns = 0;
+ }
+
+ for (i = 0; i < num_nconns; i++) {
+ struct drm_connector *drmconn = drm_connector(nconns[i]);
+ connector_ids[i] = drmconn->connector->connector_id;
+ }
+
+ if (!drm_display_set_crtc(&drmdpy->base, crtc_idx, buffer_id, x, y,
+ connector_ids, num_nconns, mode)) {
+ _eglLog(_EGL_WARNING, "failed to set CRTC %d", crtc_idx);
+
+ return FALSE;
+ }
+
+ if (drmdpy->shown_surfaces[crtc_idx])
+ drmdpy->shown_surfaces[crtc_idx]->is_shown = FALSE;
+ drmdpy->shown_surfaces[crtc_idx] = drmsurf;
+
+ /* remember the settings for buffer swapping */
+ if (drmsurf) {
+ uint32_t crtc_id = drmdpy->saved_crtcs[crtc_idx].crtc->crtc_id;
+ struct drm_crtc *drmcrtc = &drmsurf->current_crtc;
+
+ if (drmcrtc->crtc)
+ drmModeFreeCrtc(drmcrtc->crtc);
+ drmcrtc->crtc = drmModeGetCrtc(drmdpy->fd, crtc_id);
+
+ assert(num_nconns < Elements(drmcrtc->connectors));
+ memcpy(drmcrtc->connectors, connector_ids,
+ sizeof(*connector_ids) * num_nconns);
+ drmcrtc->num_connectors = num_nconns;
+
+ drmsurf->is_shown = TRUE;
+ }
+
+ return TRUE;
+}
+
+static const struct native_mode **
+drm_display_get_modes(struct native_display *ndpy,
+ const struct native_connector *nconn,
+ int *num_modes)
+{
+ struct drm_display *drmdpy = drm_display(ndpy);
+ struct drm_connector *drmconn = drm_connector(nconn);
+ const struct native_mode **nmodes_return;
+ int count, i;
+
+ /* delete old data */
+ if (drmconn->connector) {
+ drmModeFreeConnector(drmconn->connector);
+ FREE(drmconn->drm_modes);
+
+ drmconn->connector = NULL;
+ drmconn->drm_modes = NULL;
+ drmconn->num_modes = 0;
+ }
+
+ /* detect again */
+ drmconn->connector = drmModeGetConnector(drmdpy->fd, drmconn->connector_id);
+ if (!drmconn->connector)
+ return NULL;
+
+ count = drmconn->connector->count_modes;
+ drmconn->drm_modes = CALLOC(count, sizeof(*drmconn->drm_modes));
+ if (!drmconn->drm_modes) {
+ drmModeFreeConnector(drmconn->connector);
+ drmconn->connector = NULL;
+
+ return NULL;
+ }
+
+ for (i = 0; i < count; i++) {
+ struct drm_mode *drmmode = &drmconn->drm_modes[i];
+ drmModeModeInfoPtr mode = &drmconn->connector->modes[i];
+
+ drmmode->mode = *mode;
+
+ drmmode->base.desc = drmmode->mode.name;
+ drmmode->base.width = drmmode->mode.hdisplay;
+ drmmode->base.height = drmmode->mode.vdisplay;
+ drmmode->base.refresh_rate = drmmode->mode.vrefresh;
+ /* not all kernels have vrefresh = refresh_rate * 1000 */
+ if (drmmode->base.refresh_rate > 1000)
+ drmmode->base.refresh_rate = (drmmode->base.refresh_rate + 500) / 1000;
+ }
+
+ nmodes_return = MALLOC(count * sizeof(*nmodes_return));
+ if (nmodes_return) {
+ for (i = 0; i < count; i++)
+ nmodes_return[i] = &drmconn->drm_modes[i].base;
+ if (num_modes)
+ *num_modes = count;
+ }
+
+ return nmodes_return;
+}
+
+static const struct native_connector **
+drm_display_get_connectors(struct native_display *ndpy, int *num_connectors,
+ int *num_crtc)
+{
+ struct drm_display *drmdpy = drm_display(ndpy);
+ const struct native_connector **connectors;
+ int i;
+
+ if (!drmdpy->connectors) {
+ drmdpy->connectors =
+ CALLOC(drmdpy->resources->count_connectors, sizeof(*drmdpy->connectors));
+ if (!drmdpy->connectors)
+ return NULL;
+
+ for (i = 0; i < drmdpy->resources->count_connectors; i++) {
+ struct drm_connector *drmconn = &drmdpy->connectors[i];
+
+ drmconn->connector_id = drmdpy->resources->connectors[i];
+ /* drmconn->connector is allocated when the modes are asked */
+ }
+
+ drmdpy->num_connectors = drmdpy->resources->count_connectors;
+ }
+
+ connectors = MALLOC(drmdpy->num_connectors * sizeof(*connectors));
+ if (connectors) {
+ for (i = 0; i < drmdpy->num_connectors; i++)
+ connectors[i] = &drmdpy->connectors[i].base;
+ if (num_connectors)
+ *num_connectors = drmdpy->num_connectors;
+ }
+
+ if (num_crtc)
+ *num_crtc = drmdpy->resources->count_crtcs;
+
+ return connectors;
+}
+
+static struct native_surface *
+drm_display_create_scanout_surface(struct native_display *ndpy,
+ const struct native_config *nconf,
+ uint width, uint height)
+{
+ struct drm_surface *drmsurf;
+
+ drmsurf = drm_display_create_surface(ndpy, nconf, width, height);
+ return &drmsurf->base;
+}
+
+static struct native_display_modeset drm_display_modeset = {
+ .get_connectors = drm_display_get_connectors,
+ .get_modes = drm_display_get_modes,
+ .create_scanout_surface = drm_display_create_scanout_surface,
+ .program = drm_display_program
+};
+
+void
+drm_display_fini_modeset(struct native_display *ndpy)
+{
+ struct drm_display *drmdpy = drm_display(ndpy);
+ int i;
+
+ if (drmdpy->connectors) {
+ for (i = 0; i < drmdpy->num_connectors; i++) {
+ struct drm_connector *drmconn = &drmdpy->connectors[i];
+ if (drmconn->connector) {
+ drmModeFreeConnector(drmconn->connector);
+ FREE(drmconn->drm_modes);
+ }
+ }
+ FREE(drmdpy->connectors);
+ }
+
+ if (drmdpy->shown_surfaces) {
+ FREE(drmdpy->shown_surfaces);
+ drmdpy->shown_surfaces = NULL;
+ }
+
+ if (drmdpy->saved_crtcs) {
+ for (i = 0; i < drmdpy->resources->count_crtcs; i++) {
+ struct drm_crtc *drmcrtc = &drmdpy->saved_crtcs[i];
+
+ if (drmcrtc->crtc) {
+ /* restore crtc */
+ drmModeSetCrtc(drmdpy->fd, drmcrtc->crtc->crtc_id,
+ drmcrtc->crtc->buffer_id, drmcrtc->crtc->x, drmcrtc->crtc->y,
+ drmcrtc->connectors, drmcrtc->num_connectors,
+ &drmcrtc->crtc->mode);
+
+ drmModeFreeCrtc(drmcrtc->crtc);
+ }
+ }
+ FREE(drmdpy->saved_crtcs);
+ }
+
+ if (drmdpy->resources) {
+ drmModeFreeResources(drmdpy->resources);
+ drmdpy->resources = NULL;
+ }
+
+ drmdpy->base.modeset = NULL;
+}
+
+boolean
+drm_display_init_modeset(struct native_display *ndpy)
+{
+ struct drm_display *drmdpy = drm_display(ndpy);
+
+ /* resources are fixed, unlike crtc, connector, or encoder */
+ drmdpy->resources = drmModeGetResources(drmdpy->fd);
+ if (!drmdpy->resources) {
+ _eglLog(_EGL_DEBUG, "Failed to get KMS resources. Disable modeset.");
+ return FALSE;
+ }
+
+ drmdpy->saved_crtcs =
+ CALLOC(drmdpy->resources->count_crtcs, sizeof(*drmdpy->saved_crtcs));
+ if (!drmdpy->saved_crtcs) {
+ drm_display_fini_modeset(&drmdpy->base);
+ return FALSE;
+ }
+
+ drmdpy->shown_surfaces =
+ CALLOC(drmdpy->resources->count_crtcs, sizeof(*drmdpy->shown_surfaces));
+ if (!drmdpy->shown_surfaces) {
+ drm_display_fini_modeset(&drmdpy->base);
+ return FALSE;
+ }
+
+ drmdpy->base.modeset = &drm_display_modeset;
+
+ return TRUE;
+}