| /* |
| * 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 |
| * 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. |
| */ |
| |
| #include "pipe/p_screen.h" |
| #include "pipe/p_context.h" |
| #include "util/u_debug.h" |
| #include "util/u_memory.h" |
| #include "util/u_inlines.h" |
| #include "util/u_pointer.h" |
| #include "util/u_string.h" |
| #include "egllog.h" |
| |
| #include "native_kms.h" |
| #include "state_tracker/drm_driver.h" |
| |
| static boolean |
| kms_surface_validate(struct native_surface *nsurf, uint attachment_mask, |
| unsigned int *seq_num, struct pipe_resource **textures, |
| int *width, int *height) |
| { |
| struct kms_surface *ksurf = kms_surface(nsurf); |
| |
| if (!resource_surface_add_resources(ksurf->rsurf, attachment_mask)) |
| return FALSE; |
| if (textures) |
| resource_surface_get_resources(ksurf->rsurf, textures, attachment_mask); |
| |
| if (seq_num) |
| *seq_num = ksurf->sequence_number; |
| 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; |
| struct winsys_handle whandle; |
| 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 */ |
| resource_surface_add_resources(ksurf->rsurf, 1 << natt); |
| fb->texture = |
| resource_surface_get_single_resource(ksurf->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 (!kdpy->base.screen->resource_get_handle(kdpy->base.screen, |
| fb->texture, &whandle)) |
| return FALSE; |
| |
| block_bits = util_format_get_blocksizebits(ksurf->color_format); |
| err = drmModeAddFB(kdpy->fd, ksurf->width, ksurf->height, |
| block_bits, block_bits, whandle.stride, whandle.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; |
| |
| 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; |
| int err; |
| |
| 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; |
| |
| resource_surface_swap_buffers(ksurf->rsurf, |
| NATIVE_ATTACHMENT_FRONT_LEFT, NATIVE_ATTACHMENT_BACK_LEFT, FALSE); |
| /* the front/back textures are swapped */ |
| ksurf->sequence_number++; |
| kdpy->event_handler->invalid_surface(&kdpy->base, |
| &ksurf->base, ksurf->sequence_number); |
| |
| 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); |
| |
| 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_resource_reference(&ksurf->front_fb.texture, NULL); |
| |
| if (ksurf->back_fb.buffer_id) |
| drmModeRmFB(ksurf->kdpy->fd, ksurf->back_fb.buffer_id); |
| pipe_resource_reference(&ksurf->back_fb.texture, NULL); |
| |
| resource_surface_destroy(ksurf->rsurf); |
| FREE(ksurf); |
| } |
| |
| static struct kms_surface * |
| kms_display_create_surface(struct native_display *ndpy, |
| 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->color_format = kconf->base.color_format; |
| ksurf->width = width; |
| ksurf->height = height; |
| |
| ksurf->rsurf = resource_surface_create(kdpy->base.screen, |
| ksurf->color_format, |
| PIPE_BIND_RENDER_TARGET | |
| PIPE_BIND_SAMPLER_VIEW | |
| PIPE_BIND_DISPLAY_TARGET | |
| PIPE_BIND_SCANOUT); |
| if (!ksurf->rsurf) { |
| FREE(ksurf); |
| return NULL; |
| } |
| |
| resource_surface_set_size(ksurf->rsurf, ksurf->width, ksurf->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; |
| /* not all kernels have vrefresh = refresh_rate * 1000 */ |
| if (kmode->base.refresh_rate > 1000) |
| kmode->base.refresh_rate = (kmode->base.refresh_rate + 500) / 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, nconf, width, height); |
| return &ksurf->base; |
| } |
| |
| 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, 0, |
| (is_color) ? PIPE_BIND_RENDER_TARGET : |
| PIPE_BIND_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; |
| |
| nconf->buffer_mask = |
| (1 << NATIVE_ATTACHMENT_FRONT_LEFT) | |
| (1 << NATIVE_ATTACHMENT_BACK_LEFT); |
| |
| format = PIPE_FORMAT_B8G8R8A8_UNORM; |
| if (!kms_display_is_format_supported(&kdpy->base, format, TRUE)) { |
| format = PIPE_FORMAT_A8R8G8B8_UNORM; |
| if (!kms_display_is_format_supported(&kdpy->base, format, TRUE)) |
| format = PIPE_FORMAT_NONE; |
| } |
| if (format == PIPE_FORMAT_NONE) { |
| FREE(kdpy->config); |
| kdpy->config = NULL; |
| return NULL; |
| } |
| |
| nconf->color_format = format; |
| |
| nconf->scanout_bit = TRUE; |
| } |
| |
| configs = MALLOC(sizeof(*configs)); |
| if (configs) { |
| configs[0] = &kdpy->config->base; |
| if (num_configs) |
| *num_configs = 1; |
| } |
| |
| return configs; |
| } |
| |
| static int |
| kms_display_get_param(struct native_display *ndpy, |
| enum native_param_type param) |
| { |
| int val; |
| |
| switch (param) { |
| default: |
| val = 0; |
| break; |
| } |
| |
| return val; |
| } |
| |
| 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); |
| |
| FREE(kdpy); |
| } |
| |
| /** |
| * Initialize KMS and pipe screen. |
| */ |
| static boolean |
| kms_display_init_screen(struct native_display *ndpy) |
| { |
| struct kms_display *kdpy = kms_display(ndpy); |
| int fd; |
| |
| fd = kdpy->fd; |
| if (fd >= 0) { |
| drmVersionPtr version = drmGetVersion(fd); |
| if (!version || strcmp(version->name, driver_descriptor.driver_name)) { |
| if (version) { |
| _eglLog(_EGL_WARNING, "unknown driver name %s", version->name); |
| drmFreeVersion(version); |
| } |
| else { |
| _eglLog(_EGL_WARNING, "invalid fd %d", fd); |
| } |
| |
| return FALSE; |
| } |
| |
| drmFreeVersion(version); |
| } |
| else { |
| fd = drmOpen(driver_descriptor.driver_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 |
| |
| kdpy->base.screen = driver_descriptor.create_screen(fd); |
| 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(int fd, struct native_event_handler *event_handler) |
| { |
| struct kms_display *kdpy; |
| |
| kdpy = CALLOC_STRUCT(kms_display); |
| if (!kdpy) |
| return NULL; |
| |
| kdpy->event_handler = event_handler; |
| kdpy->fd = fd; |
| 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.destroy = kms_display_destroy; |
| kdpy->base.get_param = kms_display_get_param; |
| kdpy->base.get_configs = kms_display_get_configs; |
| |
| kdpy->base.modeset = &kms_display_modeset; |
| |
| return &kdpy->base; |
| } |
| |
| static struct native_display * |
| native_create_display(void *dpy, struct native_event_handler *event_handler) |
| { |
| struct native_display *ndpy; |
| int fd; |
| |
| /* well, this makes fd 0 being ignored */ |
| fd = (dpy) ? (int) pointer_to_intptr(dpy) : -1; |
| ndpy = kms_create_display(fd, event_handler); |
| |
| return ndpy; |
| } |
| |
| static void |
| kms_init_platform(struct native_platform *nplat) |
| { |
| static char kms_name[32]; |
| |
| if (nplat->name) |
| return; |
| |
| util_snprintf(kms_name, sizeof(kms_name), "KMS/%s", driver_descriptor.name); |
| |
| nplat->name = kms_name; |
| nplat->create_probe = NULL; |
| nplat->get_probe_result = NULL; |
| nplat->create_display = native_create_display; |
| } |
| |
| static struct native_platform kms_platform; |
| |
| const struct native_platform * |
| native_get_kms_platform(void) |
| { |
| kms_init_platform(&kms_platform); |
| return &kms_platform; |
| } |