diff options
Diffstat (limited to 'src/gallium/state_trackers/egl/drm/modeset.c')
-rw-r--r-- | src/gallium/state_trackers/egl/drm/modeset.c | 619 |
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 00000000000..06a60770537 --- /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 <[email protected]> + */ + +#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; +} |