| /* |
| * Copyright © 2014-2018 NVIDIA Corporation |
| * |
| * 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 (including the next |
| * paragraph) 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 <errno.h> |
| #include <fcntl.h> |
| #include <inttypes.h> |
| #include <stdio.h> |
| |
| #include <sys/stat.h> |
| |
| #include "drm-uapi/drm_fourcc.h" |
| #include "drm-uapi/tegra_drm.h" |
| #include <xf86drm.h> |
| |
| #include "loader/loader.h" |
| #include "pipe/p_state.h" |
| #include "util/u_debug.h" |
| #include "util/u_inlines.h" |
| |
| #include "frontend/drm_driver.h" |
| |
| #include "nouveau/drm/nouveau_drm_public.h" |
| |
| #include "tegra_context.h" |
| #include "tegra_resource.h" |
| #include "tegra_screen.h" |
| |
| static void tegra_screen_destroy(struct pipe_screen *pscreen) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| screen->gpu->destroy(screen->gpu); |
| free(pscreen); |
| } |
| |
| static const char * |
| tegra_screen_get_name(struct pipe_screen *pscreen) |
| { |
| return "tegra"; |
| } |
| |
| static const char * |
| tegra_screen_get_vendor(struct pipe_screen *pscreen) |
| { |
| return "NVIDIA"; |
| } |
| |
| static const char * |
| tegra_screen_get_device_vendor(struct pipe_screen *pscreen) |
| { |
| return "NVIDIA"; |
| } |
| |
| static int |
| tegra_screen_get_param(struct pipe_screen *pscreen, enum pipe_cap param) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->get_param(screen->gpu, param); |
| } |
| |
| static float |
| tegra_screen_get_paramf(struct pipe_screen *pscreen, enum pipe_capf param) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->get_paramf(screen->gpu, param); |
| } |
| |
| static int |
| tegra_screen_get_shader_param(struct pipe_screen *pscreen, unsigned shader, |
| enum pipe_shader_cap param) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->get_shader_param(screen->gpu, shader, param); |
| } |
| |
| static int |
| tegra_screen_get_video_param(struct pipe_screen *pscreen, |
| enum pipe_video_profile profile, |
| enum pipe_video_entrypoint entrypoint, |
| enum pipe_video_cap param) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->get_video_param(screen->gpu, profile, entrypoint, |
| param); |
| } |
| |
| static int |
| tegra_screen_get_compute_param(struct pipe_screen *pscreen, |
| enum pipe_shader_ir ir_type, |
| enum pipe_compute_cap param, |
| void *retp) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->get_compute_param(screen->gpu, ir_type, param, |
| retp); |
| } |
| |
| static uint64_t |
| tegra_screen_get_timestamp(struct pipe_screen *pscreen) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->get_timestamp(screen->gpu); |
| } |
| |
| static bool |
| tegra_screen_is_format_supported(struct pipe_screen *pscreen, |
| enum pipe_format format, |
| enum pipe_texture_target target, |
| unsigned sample_count, |
| unsigned storage_sample_count, |
| unsigned usage) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->is_format_supported(screen->gpu, format, target, |
| sample_count, storage_sample_count, |
| usage); |
| } |
| |
| static bool |
| tegra_screen_is_video_format_supported(struct pipe_screen *pscreen, |
| enum pipe_format format, |
| enum pipe_video_profile profile, |
| enum pipe_video_entrypoint entrypoint) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->is_video_format_supported(screen->gpu, format, profile, |
| entrypoint); |
| } |
| |
| static bool |
| tegra_screen_can_create_resource(struct pipe_screen *pscreen, |
| const struct pipe_resource *template) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->can_create_resource(screen->gpu, template); |
| } |
| |
| static int tegra_screen_import_resource(struct tegra_screen *screen, |
| struct tegra_resource *resource) |
| { |
| struct winsys_handle handle; |
| bool status; |
| int fd, err; |
| |
| memset(&handle, 0, sizeof(handle)); |
| handle.modifier = DRM_FORMAT_MOD_INVALID; |
| handle.type = WINSYS_HANDLE_TYPE_FD; |
| |
| status = screen->gpu->resource_get_handle(screen->gpu, NULL, resource->gpu, |
| &handle, 0); |
| if (!status) |
| return -EINVAL; |
| |
| assert(handle.modifier != DRM_FORMAT_MOD_INVALID); |
| |
| if (handle.modifier == DRM_FORMAT_MOD_INVALID) { |
| close(handle.handle); |
| return -EINVAL; |
| } |
| |
| resource->modifier = handle.modifier; |
| resource->stride = handle.stride; |
| fd = handle.handle; |
| |
| err = drmPrimeFDToHandle(screen->fd, fd, &resource->handle); |
| if (err < 0) |
| err = -errno; |
| |
| close(fd); |
| |
| return err; |
| } |
| |
| static struct pipe_resource * |
| tegra_screen_resource_create(struct pipe_screen *pscreen, |
| const struct pipe_resource *template) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| uint64_t modifier = DRM_FORMAT_MOD_INVALID; |
| struct tegra_resource *resource; |
| int err; |
| |
| resource = calloc(1, sizeof(*resource)); |
| if (!resource) |
| return NULL; |
| |
| /* |
| * Applications that create scanout resources without modifiers are very |
| * unlikely to support modifiers at all. In that case the resources need |
| * to be created with a pitch-linear layout so that they can be properly |
| * shared with scanout hardware. |
| * |
| * Technically it is possible for applications to create resources without |
| * specifying a modifier but still query the modifier associated with the |
| * resource (e.g. using gbm_bo_get_modifier()) before handing it to the |
| * framebuffer creation API (such as the DRM_IOCTL_MODE_ADDFB2 IOCTL). |
| */ |
| if (template->bind & PIPE_BIND_SCANOUT) |
| modifier = DRM_FORMAT_MOD_LINEAR; |
| |
| resource->gpu = screen->gpu->resource_create_with_modifiers(screen->gpu, |
| template, |
| &modifier, 1); |
| if (!resource->gpu) |
| goto free; |
| |
| /* import scanout buffers for display */ |
| if (template->bind & PIPE_BIND_SCANOUT) { |
| err = tegra_screen_import_resource(screen, resource); |
| if (err < 0) |
| goto destroy; |
| } |
| |
| memcpy(&resource->base, resource->gpu, sizeof(*resource->gpu)); |
| pipe_reference_init(&resource->base.reference, 1); |
| resource->base.screen = &screen->base; |
| |
| return &resource->base; |
| |
| destroy: |
| screen->gpu->resource_destroy(screen->gpu, resource->gpu); |
| free: |
| free(resource); |
| return NULL; |
| } |
| |
| /* XXX */ |
| static struct pipe_resource * |
| tegra_screen_resource_create_front(struct pipe_screen *pscreen, |
| const struct pipe_resource *template, |
| const void *map_front_private) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| struct pipe_resource *resource; |
| |
| resource = screen->gpu->resource_create_front(screen->gpu, template, |
| map_front_private); |
| if (resource) |
| resource->screen = pscreen; |
| |
| return resource; |
| } |
| |
| static struct pipe_resource * |
| tegra_screen_resource_from_handle(struct pipe_screen *pscreen, |
| const struct pipe_resource *template, |
| struct winsys_handle *handle, |
| unsigned usage) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| struct tegra_resource *resource; |
| |
| resource = calloc(1, sizeof(*resource)); |
| if (!resource) |
| return NULL; |
| |
| resource->gpu = screen->gpu->resource_from_handle(screen->gpu, template, |
| handle, usage); |
| if (!resource->gpu) { |
| free(resource); |
| return NULL; |
| } |
| |
| memcpy(&resource->base, resource->gpu, sizeof(*resource->gpu)); |
| pipe_reference_init(&resource->base.reference, 1); |
| resource->base.screen = &screen->base; |
| |
| return &resource->base; |
| } |
| |
| /* XXX */ |
| static struct pipe_resource * |
| tegra_screen_resource_from_user_memory(struct pipe_screen *pscreen, |
| const struct pipe_resource *template, |
| void *buffer) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| struct pipe_resource *resource; |
| |
| resource = screen->gpu->resource_from_user_memory(screen->gpu, template, |
| buffer); |
| if (resource) |
| resource->screen = pscreen; |
| |
| return resource; |
| } |
| |
| static bool |
| tegra_screen_resource_get_handle(struct pipe_screen *pscreen, |
| struct pipe_context *pcontext, |
| struct pipe_resource *presource, |
| struct winsys_handle *handle, |
| unsigned usage) |
| { |
| struct tegra_resource *resource = to_tegra_resource(presource); |
| struct tegra_context *context = to_tegra_context(pcontext); |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| bool ret = true; |
| |
| /* |
| * Assume that KMS handles for scanout resources will only ever be used |
| * to pass buffers into Tegra DRM for display. In all other cases, return |
| * the Nouveau handle, assuming they will be used for sharing in DRI2/3. |
| */ |
| if (handle->type == WINSYS_HANDLE_TYPE_KMS && |
| presource->bind & PIPE_BIND_SCANOUT) { |
| handle->modifier = resource->modifier; |
| handle->handle = resource->handle; |
| handle->stride = resource->stride; |
| } else { |
| ret = screen->gpu->resource_get_handle(screen->gpu, |
| context ? context->gpu : NULL, |
| resource->gpu, handle, usage); |
| } |
| |
| return ret; |
| } |
| |
| static void |
| tegra_screen_resource_destroy(struct pipe_screen *pscreen, |
| struct pipe_resource *presource) |
| { |
| struct tegra_resource *resource = to_tegra_resource(presource); |
| |
| pipe_resource_reference(&resource->gpu, NULL); |
| free(resource); |
| } |
| |
| static void |
| tegra_screen_flush_frontbuffer(struct pipe_screen *pscreen, |
| struct pipe_resource *resource, |
| unsigned int level, |
| unsigned int layer, |
| void *winsys_drawable_handle, |
| struct pipe_box *box) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| screen->gpu->flush_frontbuffer(screen->gpu, resource, level, layer, |
| winsys_drawable_handle, box); |
| } |
| |
| static void |
| tegra_screen_fence_reference(struct pipe_screen *pscreen, |
| struct pipe_fence_handle **ptr, |
| struct pipe_fence_handle *fence) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| screen->gpu->fence_reference(screen->gpu, ptr, fence); |
| } |
| |
| static bool |
| tegra_screen_fence_finish(struct pipe_screen *pscreen, |
| struct pipe_context *pcontext, |
| struct pipe_fence_handle *fence, |
| uint64_t timeout) |
| { |
| struct tegra_context *context = to_tegra_context(pcontext); |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->fence_finish(screen->gpu, |
| context ? context->gpu : NULL, |
| fence, timeout); |
| } |
| |
| static int |
| tegra_screen_fence_get_fd(struct pipe_screen *pscreen, |
| struct pipe_fence_handle *fence) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->fence_get_fd(screen->gpu, fence); |
| } |
| |
| static int |
| tegra_screen_get_driver_query_info(struct pipe_screen *pscreen, |
| unsigned int index, |
| struct pipe_driver_query_info *info) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->get_driver_query_info(screen->gpu, index, info); |
| } |
| |
| static int |
| tegra_screen_get_driver_query_group_info(struct pipe_screen *pscreen, |
| unsigned int index, |
| struct pipe_driver_query_group_info *info) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->get_driver_query_group_info(screen->gpu, index, info); |
| } |
| |
| static void |
| tegra_screen_query_memory_info(struct pipe_screen *pscreen, |
| struct pipe_memory_info *info) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| screen->gpu->query_memory_info(screen->gpu, info); |
| } |
| |
| static const void * |
| tegra_screen_get_compiler_options(struct pipe_screen *pscreen, |
| enum pipe_shader_ir ir, |
| unsigned int shader) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| const void *options = NULL; |
| |
| if (screen->gpu->get_compiler_options) |
| options = screen->gpu->get_compiler_options(screen->gpu, ir, shader); |
| |
| return options; |
| } |
| |
| static struct disk_cache * |
| tegra_screen_get_disk_shader_cache(struct pipe_screen *pscreen) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->get_disk_shader_cache(screen->gpu); |
| } |
| |
| static struct pipe_resource * |
| tegra_screen_resource_create_with_modifiers(struct pipe_screen *pscreen, |
| const struct pipe_resource *template, |
| const uint64_t *modifiers, |
| int count) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| struct pipe_resource tmpl = *template; |
| struct tegra_resource *resource; |
| int err; |
| |
| resource = calloc(1, sizeof(*resource)); |
| if (!resource) |
| return NULL; |
| |
| /* |
| * Assume that resources created with modifiers will always be used for |
| * scanout. This is necessary because some of the APIs that are used to |
| * create resources with modifiers (e.g. gbm_bo_create_with_modifiers()) |
| * can't pass along usage information. Adding that capability might be |
| * worth adding to remove this ambiguity. Not all future use-cases that |
| * involve modifiers may always be targetting scanout hardware. |
| */ |
| tmpl.bind |= PIPE_BIND_SCANOUT; |
| |
| resource->gpu = screen->gpu->resource_create_with_modifiers(screen->gpu, |
| &tmpl, |
| modifiers, |
| count); |
| if (!resource->gpu) |
| goto free; |
| |
| err = tegra_screen_import_resource(screen, resource); |
| if (err < 0) |
| goto destroy; |
| |
| memcpy(&resource->base, resource->gpu, sizeof(*resource->gpu)); |
| pipe_reference_init(&resource->base.reference, 1); |
| resource->base.screen = &screen->base; |
| |
| return &resource->base; |
| |
| destroy: |
| screen->gpu->resource_destroy(screen->gpu, resource->gpu); |
| free: |
| free(resource); |
| return NULL; |
| } |
| |
| static void tegra_screen_query_dmabuf_modifiers(struct pipe_screen *pscreen, |
| enum pipe_format format, |
| int max, uint64_t *modifiers, |
| unsigned int *external_only, |
| int *count) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| screen->gpu->query_dmabuf_modifiers(screen->gpu, format, max, modifiers, |
| external_only, count); |
| } |
| |
| static struct pipe_memory_object * |
| tegra_screen_memobj_create_from_handle(struct pipe_screen *pscreen, |
| struct winsys_handle *handle, |
| bool dedicated) |
| { |
| struct tegra_screen *screen = to_tegra_screen(pscreen); |
| |
| return screen->gpu->memobj_create_from_handle(screen->gpu, handle, |
| dedicated); |
| } |
| |
| struct pipe_screen * |
| tegra_screen_create(int fd) |
| { |
| struct tegra_screen *screen; |
| |
| screen = calloc(1, sizeof(*screen)); |
| if (!screen) |
| return NULL; |
| |
| screen->fd = fd; |
| |
| screen->gpu_fd = loader_open_render_node("nouveau"); |
| if (screen->gpu_fd < 0) { |
| if (errno != ENOENT) |
| fprintf(stderr, "failed to open GPU device: %s\n", strerror(errno)); |
| |
| free(screen); |
| return NULL; |
| } |
| |
| screen->gpu = nouveau_drm_screen_create(screen->gpu_fd); |
| if (!screen->gpu) { |
| fprintf(stderr, "failed to create GPU screen\n"); |
| close(screen->gpu_fd); |
| free(screen); |
| return NULL; |
| } |
| |
| screen->base.destroy = tegra_screen_destroy; |
| screen->base.get_name = tegra_screen_get_name; |
| screen->base.get_vendor = tegra_screen_get_vendor; |
| screen->base.get_device_vendor = tegra_screen_get_device_vendor; |
| screen->base.get_param = tegra_screen_get_param; |
| screen->base.get_paramf = tegra_screen_get_paramf; |
| screen->base.get_shader_param = tegra_screen_get_shader_param; |
| screen->base.get_video_param = tegra_screen_get_video_param; |
| screen->base.get_compute_param = tegra_screen_get_compute_param; |
| screen->base.get_timestamp = tegra_screen_get_timestamp; |
| screen->base.context_create = tegra_screen_context_create; |
| screen->base.is_format_supported = tegra_screen_is_format_supported; |
| screen->base.is_video_format_supported = tegra_screen_is_video_format_supported; |
| |
| /* allow fallback implementation if GPU driver doesn't implement it */ |
| if (screen->gpu->can_create_resource) |
| screen->base.can_create_resource = tegra_screen_can_create_resource; |
| |
| screen->base.resource_create = tegra_screen_resource_create; |
| screen->base.resource_create_front = tegra_screen_resource_create_front; |
| screen->base.resource_from_handle = tegra_screen_resource_from_handle; |
| screen->base.resource_from_user_memory = tegra_screen_resource_from_user_memory; |
| screen->base.resource_get_handle = tegra_screen_resource_get_handle; |
| screen->base.resource_destroy = tegra_screen_resource_destroy; |
| |
| screen->base.flush_frontbuffer = tegra_screen_flush_frontbuffer; |
| screen->base.fence_reference = tegra_screen_fence_reference; |
| screen->base.fence_finish = tegra_screen_fence_finish; |
| screen->base.fence_get_fd = tegra_screen_fence_get_fd; |
| |
| screen->base.get_driver_query_info = tegra_screen_get_driver_query_info; |
| screen->base.get_driver_query_group_info = tegra_screen_get_driver_query_group_info; |
| screen->base.query_memory_info = tegra_screen_query_memory_info; |
| |
| screen->base.get_compiler_options = tegra_screen_get_compiler_options; |
| screen->base.get_disk_shader_cache = tegra_screen_get_disk_shader_cache; |
| |
| screen->base.resource_create_with_modifiers = tegra_screen_resource_create_with_modifiers; |
| screen->base.query_dmabuf_modifiers = tegra_screen_query_dmabuf_modifiers; |
| screen->base.memobj_create_from_handle = tegra_screen_memobj_create_from_handle; |
| |
| return &screen->base; |
| } |