| /* |
| * Mesa 3-D graphics library |
| * Version: 7.8 |
| * |
| * Copyright (C) 2010 Chia-I Wu <olv@0xlab.org> |
| * |
| * 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 |
| * BRIAN PAUL 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. |
| */ |
| |
| #include <string.h> |
| |
| #include "pipe/p_screen.h" |
| #include "pipe/p_context.h" |
| #include "util/u_debug.h" |
| #include "util/u_memory.h" |
| #include "egllog.h" |
| |
| #include "native_kms.h" |
| |
| static boolean |
| kms_surface_validate(struct native_surface *nsurf, |
| const enum native_attachment *natts, |
| unsigned num_natts, |
| struct pipe_texture **textures, |
| int *width, int *height) |
| { |
| struct kms_surface *ksurf = kms_surface(nsurf); |
| struct kms_display *kdpy = ksurf->kdpy; |
| struct pipe_screen *screen = kdpy->base.screen; |
| struct pipe_texture templ, *ptex; |
| int i; |
| |
| if (num_natts) { |
| if (textures) |
| memset(textures, 0, sizeof(*textures) * num_natts); |
| |
| memset(&templ, 0, sizeof(templ)); |
| templ.target = PIPE_TEXTURE_2D; |
| templ.last_level = 0; |
| templ.width0 = ksurf->width; |
| templ.height0 = ksurf->height; |
| templ.depth0 = 1; |
| templ.format = ksurf->color_format; |
| templ.tex_usage = PIPE_TEXTURE_USAGE_RENDER_TARGET; |
| if (ksurf->type == KMS_SURFACE_TYPE_SCANOUT) |
| templ.tex_usage |= PIPE_TEXTURE_USAGE_PRIMARY; |
| } |
| |
| /* create textures */ |
| for (i = 0; i < num_natts; i++) { |
| enum native_attachment natt = natts[i]; |
| |
| ptex = ksurf->textures[natt]; |
| if (!ptex) { |
| ptex = screen->texture_create(screen, &templ); |
| ksurf->textures[natt] = ptex; |
| } |
| |
| if (textures) |
| pipe_texture_reference(&textures[i], ptex); |
| } |
| |
| if (width) |
| *width = ksurf->width; |
| if (height) |
| *height = ksurf->height; |
| |
| return TRUE; |
| } |
| |
| /** |
| * Add textures as DRM framebuffers. |
| */ |
| static boolean |
| kms_surface_init_framebuffers(struct native_surface *nsurf, boolean need_back) |
| { |
| struct kms_surface *ksurf = kms_surface(nsurf); |
| struct kms_display *kdpy = ksurf->kdpy; |
| int num_framebuffers = (need_back) ? 2 : 1; |
| int i, err; |
| |
| for (i = 0; i < num_framebuffers; i++) { |
| struct kms_framebuffer *fb; |
| enum native_attachment natt; |
| unsigned int handle, stride; |
| uint block_bits; |
| |
| if (i == 0) { |
| fb = &ksurf->front_fb; |
| natt = NATIVE_ATTACHMENT_FRONT_LEFT; |
| } |
| else { |
| fb = &ksurf->back_fb; |
| natt = NATIVE_ATTACHMENT_BACK_LEFT; |
| } |
| |
| if (!fb->texture) { |
| /* make sure the texture has been allocated */ |
| kms_surface_validate(&ksurf->base, &natt, 1, NULL, NULL, NULL); |
| if (!ksurf->textures[natt]) |
| return FALSE; |
| |
| pipe_texture_reference(&fb->texture, ksurf->textures[natt]); |
| } |
| |
| /* already initialized */ |
| if (fb->buffer_id) |
| continue; |
| |
| /* TODO detect the real value */ |
| fb->is_passive = TRUE; |
| |
| if (!kdpy->api->local_handle_from_texture(kdpy->api, |
| kdpy->base.screen, fb->texture, &stride, &handle)) |
| return FALSE; |
| |
| block_bits = util_format_get_blocksizebits(ksurf->color_format); |
| err = drmModeAddFB(kdpy->fd, ksurf->width, ksurf->height, |
| block_bits, block_bits, stride, handle, &fb->buffer_id); |
| if (err) { |
| fb->buffer_id = 0; |
| return FALSE; |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| static boolean |
| kms_surface_flush_frontbuffer(struct native_surface *nsurf) |
| { |
| #ifdef DRM_MODE_FEATURE_DIRTYFB |
| struct kms_surface *ksurf = kms_surface(nsurf); |
| struct kms_display *kdpy = ksurf->kdpy; |
| |
| /* pbuffer is private */ |
| if (ksurf->type == KMS_SURFACE_TYPE_PBUFFER) |
| return TRUE; |
| |
| if (ksurf->front_fb.is_passive) |
| drmModeDirtyFB(kdpy->fd, ksurf->front_fb.buffer_id, NULL, 0); |
| #endif |
| |
| return TRUE; |
| } |
| |
| static boolean |
| kms_surface_swap_buffers(struct native_surface *nsurf) |
| { |
| struct kms_surface *ksurf = kms_surface(nsurf); |
| struct kms_crtc *kcrtc = &ksurf->current_crtc; |
| struct kms_display *kdpy = ksurf->kdpy; |
| struct kms_framebuffer tmp_fb; |
| struct pipe_texture *tmp_texture; |
| int err; |
| |
| /* pbuffer is private */ |
| if (ksurf->type == KMS_SURFACE_TYPE_PBUFFER) |
| return TRUE; |
| |
| if (!ksurf->back_fb.buffer_id) { |
| if (!kms_surface_init_framebuffers(&ksurf->base, TRUE)) |
| return FALSE; |
| } |
| |
| if (ksurf->is_shown && kcrtc->crtc) { |
| err = drmModeSetCrtc(kdpy->fd, kcrtc->crtc->crtc_id, |
| ksurf->back_fb.buffer_id, kcrtc->crtc->x, kcrtc->crtc->y, |
| kcrtc->connectors, kcrtc->num_connectors, &kcrtc->crtc->mode); |
| if (err) |
| return FALSE; |
| } |
| |
| /* swap the buffers */ |
| tmp_fb = ksurf->front_fb; |
| ksurf->front_fb = ksurf->back_fb; |
| ksurf->back_fb = tmp_fb; |
| |
| tmp_texture = ksurf->textures[NATIVE_ATTACHMENT_FRONT_LEFT]; |
| ksurf->textures[NATIVE_ATTACHMENT_FRONT_LEFT] = |
| ksurf->textures[NATIVE_ATTACHMENT_BACK_LEFT]; |
| ksurf->textures[NATIVE_ATTACHMENT_BACK_LEFT] = tmp_texture; |
| |
| return TRUE; |
| } |
| |
| static void |
| kms_surface_wait(struct native_surface *nsurf) |
| { |
| /* no-op */ |
| } |
| |
| static void |
| kms_surface_destroy(struct native_surface *nsurf) |
| { |
| struct kms_surface *ksurf = kms_surface(nsurf); |
| int i; |
| |
| if (ksurf->current_crtc.crtc) |
| drmModeFreeCrtc(ksurf->current_crtc.crtc); |
| |
| if (ksurf->front_fb.buffer_id) |
| drmModeRmFB(ksurf->kdpy->fd, ksurf->front_fb.buffer_id); |
| pipe_texture_reference(&ksurf->front_fb.texture, NULL); |
| |
| if (ksurf->back_fb.buffer_id) |
| drmModeRmFB(ksurf->kdpy->fd, ksurf->back_fb.buffer_id); |
| pipe_texture_reference(&ksurf->back_fb.texture, NULL); |
| |
| for (i = 0; i < NUM_NATIVE_ATTACHMENTS; i++) { |
| struct pipe_texture *ptex = ksurf->textures[i]; |
| pipe_texture_reference(&ptex, NULL); |
| } |
| |
| free(ksurf); |
| } |
| |
| static struct kms_surface * |
| kms_display_create_surface(struct native_display *ndpy, |
| enum kms_surface_type type, |
| const struct native_config *nconf, |
| uint width, uint height) |
| { |
| struct kms_display *kdpy = kms_display(ndpy); |
| struct kms_config *kconf = kms_config(nconf); |
| struct kms_surface *ksurf; |
| |
| ksurf = CALLOC_STRUCT(kms_surface); |
| if (!ksurf) |
| return NULL; |
| |
| ksurf->kdpy = kdpy; |
| ksurf->type = type; |
| ksurf->color_format = kconf->base.color_format; |
| ksurf->width = width; |
| ksurf->height = height; |
| |
| ksurf->base.destroy = kms_surface_destroy; |
| ksurf->base.swap_buffers = kms_surface_swap_buffers; |
| ksurf->base.flush_frontbuffer = kms_surface_flush_frontbuffer; |
| ksurf->base.validate = kms_surface_validate; |
| ksurf->base.wait = kms_surface_wait; |
| |
| return ksurf; |
| } |
| |
| /** |
| * Choose a CRTC that supports all given connectors. |
| */ |
| static uint32_t |
| kms_display_choose_crtc(struct native_display *ndpy, |
| uint32_t *connectors, int num_connectors) |
| { |
| struct kms_display *kdpy = kms_display(ndpy); |
| int idx; |
| |
| for (idx = 0; idx < kdpy->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(kdpy->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(kdpy->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 >= kdpy->resources->count_crtcs) { |
| _eglLog(_EGL_WARNING, |
| "failed to find a CRTC that supports the given %d connectors", |
| num_connectors); |
| return 0; |
| } |
| |
| return kdpy->resources->crtcs[idx]; |
| } |
| |
| /** |
| * Remember the original CRTC status and set the CRTC |
| */ |
| static boolean |
| kms_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 kms_display *kdpy = kms_display(ndpy); |
| struct kms_crtc *kcrtc = &kdpy->saved_crtcs[crtc_idx]; |
| uint32_t crtc_id; |
| int err; |
| |
| if (kcrtc->crtc) { |
| crtc_id = kcrtc->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 = kms_display_choose_crtc(&kdpy->base, |
| connectors, num_connectors); |
| |
| /* save the original CRTC status */ |
| kcrtc->crtc = drmModeGetCrtc(kdpy->fd, crtc_id); |
| if (!kcrtc->crtc) |
| return FALSE; |
| |
| for (i = 0; i < kdpy->num_connectors; i++) { |
| struct kms_connector *kconn = &kdpy->connectors[i]; |
| drmModeConnectorPtr connector = kconn->connector; |
| drmModeEncoderPtr encoder; |
| |
| encoder = drmModeGetEncoder(kdpy->fd, connector->encoder_id); |
| if (encoder) { |
| if (encoder->crtc_id == crtc_id) { |
| kcrtc->connectors[count++] = connector->connector_id; |
| if (count >= Elements(kcrtc->connectors)) |
| break; |
| } |
| drmModeFreeEncoder(encoder); |
| } |
| } |
| |
| kcrtc->num_connectors = count; |
| } |
| |
| err = drmModeSetCrtc(kdpy->fd, crtc_id, buffer_id, x, y, |
| connectors, num_connectors, mode); |
| if (err) { |
| drmModeFreeCrtc(kcrtc->crtc); |
| kcrtc->crtc = NULL; |
| kcrtc->num_connectors = 0; |
| |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| static boolean |
| kms_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 kms_display *kdpy = kms_display(ndpy); |
| struct kms_surface *ksurf = kms_surface(nsurf); |
| const struct kms_mode *kmode = kms_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 (ksurf) { |
| if (!kms_surface_init_framebuffers(&ksurf->base, FALSE)) |
| return FALSE; |
| |
| buffer_id = ksurf->front_fb.buffer_id; |
| /* the mode argument of drmModeSetCrtc is not constified */ |
| mode_tmp = kmode->mode; |
| mode = &mode_tmp; |
| } |
| else { |
| /* disable the CRTC */ |
| buffer_id = 0; |
| mode = NULL; |
| num_nconns = 0; |
| } |
| |
| for (i = 0; i < num_nconns; i++) { |
| struct kms_connector *kconn = kms_connector(nconns[i]); |
| connector_ids[i] = kconn->connector->connector_id; |
| } |
| |
| if (!kms_display_set_crtc(&kdpy->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 (kdpy->shown_surfaces[crtc_idx]) |
| kdpy->shown_surfaces[crtc_idx]->is_shown = FALSE; |
| kdpy->shown_surfaces[crtc_idx] = ksurf; |
| |
| /* remember the settings for buffer swapping */ |
| if (ksurf) { |
| uint32_t crtc_id = kdpy->saved_crtcs[crtc_idx].crtc->crtc_id; |
| struct kms_crtc *kcrtc = &ksurf->current_crtc; |
| |
| if (kcrtc->crtc) |
| drmModeFreeCrtc(kcrtc->crtc); |
| kcrtc->crtc = drmModeGetCrtc(kdpy->fd, crtc_id); |
| |
| assert(num_nconns < Elements(kcrtc->connectors)); |
| memcpy(kcrtc->connectors, connector_ids, |
| sizeof(*connector_ids) * num_nconns); |
| kcrtc->num_connectors = num_nconns; |
| |
| ksurf->is_shown = TRUE; |
| } |
| |
| return TRUE; |
| } |
| |
| static const struct native_mode ** |
| kms_display_get_modes(struct native_display *ndpy, |
| const struct native_connector *nconn, |
| int *num_modes) |
| { |
| struct kms_display *kdpy = kms_display(ndpy); |
| struct kms_connector *kconn = kms_connector(nconn); |
| const struct native_mode **nmodes_return; |
| int count, i; |
| |
| /* delete old data */ |
| if (kconn->connector) { |
| drmModeFreeConnector(kconn->connector); |
| free(kconn->kms_modes); |
| |
| kconn->connector = NULL; |
| kconn->kms_modes = NULL; |
| kconn->num_modes = 0; |
| } |
| |
| /* detect again */ |
| kconn->connector = drmModeGetConnector(kdpy->fd, kconn->connector_id); |
| if (!kconn->connector) |
| return NULL; |
| |
| count = kconn->connector->count_modes; |
| kconn->kms_modes = calloc(count, sizeof(*kconn->kms_modes)); |
| if (!kconn->kms_modes) { |
| drmModeFreeConnector(kconn->connector); |
| kconn->connector = NULL; |
| |
| return NULL; |
| } |
| |
| for (i = 0; i < count; i++) { |
| struct kms_mode *kmode = &kconn->kms_modes[i]; |
| drmModeModeInfoPtr mode = &kconn->connector->modes[i]; |
| |
| kmode->mode = *mode; |
| |
| kmode->base.desc = kmode->mode.name; |
| kmode->base.width = kmode->mode.hdisplay; |
| kmode->base.height = kmode->mode.vdisplay; |
| kmode->base.refresh_rate = kmode->mode.vrefresh / 1000; |
| } |
| |
| nmodes_return = malloc(count * sizeof(*nmodes_return)); |
| if (nmodes_return) { |
| for (i = 0; i < count; i++) |
| nmodes_return[i] = &kconn->kms_modes[i].base; |
| if (num_modes) |
| *num_modes = count; |
| } |
| |
| return nmodes_return; |
| } |
| |
| static const struct native_connector ** |
| kms_display_get_connectors(struct native_display *ndpy, int *num_connectors, |
| int *num_crtc) |
| { |
| struct kms_display *kdpy = kms_display(ndpy); |
| const struct native_connector **connectors; |
| int i; |
| |
| if (!kdpy->connectors) { |
| kdpy->connectors = |
| calloc(kdpy->resources->count_connectors, sizeof(*kdpy->connectors)); |
| if (!kdpy->connectors) |
| return NULL; |
| |
| for (i = 0; i < kdpy->resources->count_connectors; i++) { |
| struct kms_connector *kconn = &kdpy->connectors[i]; |
| |
| kconn->connector_id = kdpy->resources->connectors[i]; |
| /* kconn->connector is allocated when the modes are asked */ |
| } |
| |
| kdpy->num_connectors = kdpy->resources->count_connectors; |
| } |
| |
| connectors = malloc(kdpy->num_connectors * sizeof(*connectors)); |
| if (connectors) { |
| for (i = 0; i < kdpy->num_connectors; i++) |
| connectors[i] = &kdpy->connectors[i].base; |
| if (num_connectors) |
| *num_connectors = kdpy->num_connectors; |
| } |
| |
| if (num_crtc) |
| *num_crtc = kdpy->resources->count_crtcs; |
| |
| return connectors; |
| } |
| |
| static struct native_surface * |
| kms_display_create_scanout_surface(struct native_display *ndpy, |
| const struct native_config *nconf, |
| uint width, uint height) |
| { |
| struct kms_surface *ksurf; |
| |
| ksurf = kms_display_create_surface(ndpy, |
| KMS_SURFACE_TYPE_SCANOUT, nconf, width, height); |
| return &ksurf->base; |
| } |
| |
| static struct native_surface * |
| kms_display_create_pbuffer_surface(struct native_display *ndpy, |
| const struct native_config *nconf, |
| uint width, uint height) |
| { |
| struct kms_surface *ksurf; |
| |
| ksurf = kms_display_create_surface(ndpy, |
| KMS_SURFACE_TYPE_PBUFFER, nconf, width, height); |
| return &ksurf->base; |
| } |
| |
| static struct pipe_context * |
| kms_display_create_context(struct native_display *ndpy, void *context_private) |
| { |
| struct kms_display *kdpy = kms_display(ndpy); |
| struct pipe_context *pctx; |
| |
| pctx = kdpy->api->create_context(kdpy->api, kdpy->base.screen); |
| if (pctx) |
| pctx->priv = context_private; |
| return pctx; |
| } |
| |
| static boolean |
| kms_display_is_format_supported(struct native_display *ndpy, |
| enum pipe_format fmt, boolean is_color) |
| { |
| return ndpy->screen->is_format_supported(ndpy->screen, |
| fmt, PIPE_TEXTURE_2D, |
| (is_color) ? PIPE_TEXTURE_USAGE_RENDER_TARGET : |
| PIPE_TEXTURE_USAGE_DEPTH_STENCIL, 0); |
| } |
| |
| static const struct native_config ** |
| kms_display_get_configs(struct native_display *ndpy, int *num_configs) |
| { |
| struct kms_display *kdpy = kms_display(ndpy); |
| const struct native_config **configs; |
| |
| /* first time */ |
| if (!kdpy->config) { |
| struct native_config *nconf; |
| enum pipe_format format; |
| |
| kdpy->config = calloc(1, sizeof(*kdpy->config)); |
| if (!kdpy->config) |
| return NULL; |
| |
| nconf = &kdpy->config->base; |
| |
| /* always double-buffered */ |
| nconf->mode.doubleBufferMode = TRUE; |
| |
| format = PIPE_FORMAT_A8R8G8B8_UNORM; |
| if (!kms_display_is_format_supported(&kdpy->base, format, TRUE)) { |
| format = PIPE_FORMAT_B8G8R8A8_UNORM; |
| if (!kms_display_is_format_supported(&kdpy->base, format, TRUE)) |
| format = PIPE_FORMAT_NONE; |
| } |
| if (format == PIPE_FORMAT_NONE) |
| return NULL; |
| |
| nconf->color_format = format; |
| nconf->mode.redBits = 8; |
| nconf->mode.greenBits = 8; |
| nconf->mode.blueBits = 8; |
| nconf->mode.alphaBits = 8; |
| nconf->mode.rgbBits = 32; |
| |
| format = PIPE_FORMAT_S8Z24_UNORM; |
| if (!kms_display_is_format_supported(&kdpy->base, format, FALSE)) { |
| format = PIPE_FORMAT_Z24S8_UNORM; |
| if (!kms_display_is_format_supported(&kdpy->base, format, FALSE)) |
| format = PIPE_FORMAT_NONE; |
| } |
| if (format != PIPE_FORMAT_NONE) { |
| nconf->depth_format = format; |
| nconf->stencil_format = format; |
| |
| nconf->mode.depthBits = 24; |
| nconf->mode.stencilBits = 8; |
| nconf->mode.haveDepthBuffer = TRUE; |
| nconf->mode.haveStencilBuffer = TRUE; |
| } |
| |
| nconf->scanout_bit = TRUE; |
| nconf->mode.drawableType = GLX_PBUFFER_BIT; |
| nconf->mode.swapMethod = GLX_SWAP_EXCHANGE_OML; |
| |
| nconf->mode.visualID = 0; |
| nconf->mode.visualType = EGL_NONE; |
| |
| nconf->mode.renderType = GLX_RGBA_BIT; |
| nconf->mode.rgbMode = TRUE; |
| nconf->mode.xRenderable = FALSE; |
| } |
| |
| configs = malloc(sizeof(*configs)); |
| if (configs) { |
| configs[0] = &kdpy->config->base; |
| if (num_configs) |
| *num_configs = 1; |
| } |
| |
| return configs; |
| } |
| |
| static void |
| kms_display_destroy(struct native_display *ndpy) |
| { |
| struct kms_display *kdpy = kms_display(ndpy); |
| int i; |
| |
| if (kdpy->config) |
| free(kdpy->config); |
| |
| if (kdpy->connectors) { |
| for (i = 0; i < kdpy->num_connectors; i++) { |
| struct kms_connector *kconn = &kdpy->connectors[i]; |
| if (kconn->connector) { |
| drmModeFreeConnector(kconn->connector); |
| free(kconn->kms_modes); |
| } |
| } |
| free(kdpy->connectors); |
| } |
| |
| if (kdpy->shown_surfaces) |
| free(kdpy->shown_surfaces); |
| |
| if (kdpy->saved_crtcs) { |
| for (i = 0; i < kdpy->resources->count_crtcs; i++) { |
| struct kms_crtc *kcrtc = &kdpy->saved_crtcs[i]; |
| |
| if (kcrtc->crtc) { |
| /* restore crtc */ |
| drmModeSetCrtc(kdpy->fd, kcrtc->crtc->crtc_id, |
| kcrtc->crtc->buffer_id, kcrtc->crtc->x, kcrtc->crtc->y, |
| kcrtc->connectors, kcrtc->num_connectors, |
| &kcrtc->crtc->mode); |
| |
| drmModeFreeCrtc(kcrtc->crtc); |
| } |
| } |
| free(kdpy->saved_crtcs); |
| } |
| |
| if (kdpy->resources) |
| drmModeFreeResources(kdpy->resources); |
| |
| if (kdpy->base.screen) |
| kdpy->base.screen->destroy(kdpy->base.screen); |
| |
| if (kdpy->fd >= 0) |
| drmClose(kdpy->fd); |
| |
| if (kdpy->api) |
| kdpy->api->destroy(kdpy->api); |
| free(kdpy); |
| } |
| |
| /** |
| * Initialize KMS and pipe screen. |
| */ |
| static boolean |
| kms_display_init_screen(struct native_display *ndpy) |
| { |
| struct kms_display *kdpy = kms_display(ndpy); |
| struct drm_create_screen_arg arg; |
| int fd; |
| |
| fd = drmOpen(kdpy->api->name, NULL); |
| if (fd < 0) { |
| _eglLog(_EGL_WARNING, "failed to open DRM device"); |
| return FALSE; |
| } |
| |
| #if 0 |
| if (drmSetMaster(fd)) { |
| _eglLog(_EGL_WARNING, "failed to become DRM master"); |
| return FALSE; |
| } |
| #endif |
| |
| memset(&arg, 0, sizeof(arg)); |
| arg.mode = DRM_CREATE_NORMAL; |
| kdpy->base.screen = kdpy->api->create_screen(kdpy->api, fd, &arg); |
| if (!kdpy->base.screen) { |
| _eglLog(_EGL_WARNING, "failed to create DRM screen"); |
| drmClose(fd); |
| return FALSE; |
| } |
| |
| kdpy->fd = fd; |
| |
| return TRUE; |
| } |
| |
| static struct native_display_modeset kms_display_modeset = { |
| .get_connectors = kms_display_get_connectors, |
| .get_modes = kms_display_get_modes, |
| .create_scanout_surface = kms_display_create_scanout_surface, |
| .program = kms_display_program |
| }; |
| |
| static struct native_display * |
| kms_create_display(EGLNativeDisplayType dpy, struct drm_api *api, |
| native_flush_frontbuffer flush_frontbuffer) |
| { |
| struct kms_display *kdpy; |
| |
| kdpy = CALLOC_STRUCT(kms_display); |
| if (!kdpy) |
| return NULL; |
| |
| kdpy->api = api; |
| if (!kdpy->api) { |
| _eglLog(_EGL_WARNING, "failed to create DRM API"); |
| free(kdpy); |
| return NULL; |
| } |
| |
| kdpy->fd = -1; |
| if (!kms_display_init_screen(&kdpy->base)) { |
| kms_display_destroy(&kdpy->base); |
| return NULL; |
| } |
| |
| /* resources are fixed, unlike crtc, connector, or encoder */ |
| kdpy->resources = drmModeGetResources(kdpy->fd); |
| if (!kdpy->resources) { |
| kms_display_destroy(&kdpy->base); |
| return NULL; |
| } |
| |
| kdpy->saved_crtcs = |
| calloc(kdpy->resources->count_crtcs, sizeof(*kdpy->saved_crtcs)); |
| if (!kdpy->saved_crtcs) { |
| kms_display_destroy(&kdpy->base); |
| return NULL; |
| } |
| |
| kdpy->shown_surfaces = |
| calloc(kdpy->resources->count_crtcs, sizeof(*kdpy->shown_surfaces)); |
| if (!kdpy->shown_surfaces) { |
| kms_display_destroy(&kdpy->base); |
| return NULL; |
| } |
| |
| kdpy->base.screen->flush_frontbuffer = |
| (void (*)(struct pipe_screen *, struct pipe_surface *, void *)) |
| flush_frontbuffer; |
| |
| kdpy->base.destroy = kms_display_destroy; |
| kdpy->base.get_configs = kms_display_get_configs; |
| kdpy->base.create_context = kms_display_create_context; |
| kdpy->base.create_pbuffer_surface = kms_display_create_pbuffer_surface; |
| |
| kdpy->base.modeset = &kms_display_modeset; |
| |
| return &kdpy->base; |
| } |
| |
| static void |
| dummy_flush_frontbuffer(void *dummy, struct pipe_surface *surf, |
| void *context_private) |
| { |
| _eglLog(_EGL_WARNING, "flush_frontbuffer is not supplied"); |
| } |
| |
| /* the api is destroyed with the native display */ |
| static struct drm_api *drm_api; |
| |
| const char * |
| native_get_name(void) |
| { |
| static char kms_name[32]; |
| |
| if (!drm_api) |
| drm_api = drm_api_create(); |
| |
| if (drm_api) |
| snprintf(kms_name, sizeof(kms_name), "KMS/%s", drm_api->name); |
| else |
| snprintf(kms_name, sizeof(kms_name), "KMS"); |
| |
| return kms_name; |
| } |
| |
| struct native_display * |
| native_create_display(EGLNativeDisplayType dpy, |
| native_flush_frontbuffer flush_frontbuffer) |
| { |
| struct native_display *ndpy = NULL; |
| |
| if (!drm_api) |
| drm_api = drm_api_create(); |
| |
| if (!flush_frontbuffer) |
| flush_frontbuffer = dummy_flush_frontbuffer; |
| |
| if (drm_api) |
| ndpy = kms_create_display(dpy, drm_api, flush_frontbuffer); |
| |
| return ndpy; |
| } |