diff options
author | Thierry Reding <[email protected]> | 2014-05-28 00:36:48 +0200 |
---|---|---|
committer | Thierry Reding <[email protected]> | 2018-03-09 11:48:22 +0100 |
commit | 1755f608f5201e0a23f00cc3ea1b01edd07eb6ef (patch) | |
tree | 895ac31c09458ff34fa74690b18399ff6607b180 /src/gallium/drivers/tegra/tegra_screen.c | |
parent | 2052dbdae363f4fd184842733ff9c96bd6e7f08c (diff) |
tegra: Initial support
Tegra K1 and later use a GPU that can be driven by the Nouveau driver.
But the GPU is a pure render node and has no display engine, hence the
scanout needs to happen on the Tegra display hardware. The GPU and the
display engine each have a separate DRM device node exposed by the
kernel.
To make the setup appear as a single device, this driver instantiates
a Nouveau screen with each instance of a Tegra screen and forwards GPU
requests to the Nouveau screen. For purposes of scanout it will import
buffers created on the GPU into the display driver. Handles that
userspace requests are those of the display driver so that they can be
used to create framebuffers.
This has been tested with some GBM test programs, as well as kmscube and
weston. All of those run without modifications, but I'm sure there is a
lot that can be improved.
Some fixes contributed by Hector Martin <[email protected]>.
Changes in v2:
- duplicate file descriptor in winsys to avoid potential issues
- require nouveau when building the tegra driver
- check for nouveau driver name on render node
- remove unneeded dependency on libdrm_tegra
- remove zombie references to libudev
- add missing headers to C_SOURCES variable
- drop unneeded tegra/ prefix for includes
- open device files with O_CLOEXEC
- update copyrights
Changes in v3:
- properly unwrap resources in ->resource_copy_region()
- support vertex buffers passed by user pointer
- allocate custom stream and const uploader
- silence error message on pre-Tegra124
- support X without explicit PRIME
Changes in v4:
- ship Meson build files in distribution tarball
- drop duplicate driver_tegra dependency
Reviewed-by: Emil Velikov <[email protected]>
Acked-by: Emil Velikov <[email protected]>
Tested-by: Andre Heider <[email protected]>
Reviewed-by: Dmitry Osipenko <[email protected]>
Reviewed-by: Dylan Baker <[email protected]>
Signed-off-by: Thierry Reding <[email protected]>
Diffstat (limited to 'src/gallium/drivers/tegra/tegra_screen.c')
-rw-r--r-- | src/gallium/drivers/tegra/tegra_screen.c | 688 |
1 files changed, 688 insertions, 0 deletions
diff --git a/src/gallium/drivers/tegra/tegra_screen.c b/src/gallium/drivers/tegra/tegra_screen.c new file mode 100644 index 00000000000..669f22a1944 --- /dev/null +++ b/src/gallium/drivers/tegra/tegra_screen.c @@ -0,0 +1,688 @@ +/* + * 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_fourcc.h> +#include <tegra_drm.h> +#include <xf86drm.h> + +#include "pipe/p_state.h" +#include "util/u_debug.h" +#include "util/u_inlines.h" + +#include "state_tracker/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 boolean +tegra_screen_is_format_supported(struct pipe_screen *pscreen, + enum pipe_format format, + enum pipe_texture_target target, + unsigned sample_count, + unsigned usage) +{ + struct tegra_screen *screen = to_tegra_screen(pscreen); + + return screen->gpu->is_format_supported(screen->gpu, format, target, + sample_count, usage); +} + +static boolean +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 boolean +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_open_render_node(void) +{ + drmDevicePtr *devices, device; + int err, render = -ENOENT, fd; + unsigned int num, i; + + err = drmGetDevices2(0, NULL, 0); + if (err < 0) + return err; + + num = err; + + devices = calloc(num, sizeof(*devices)); + if (!devices) + return -ENOMEM; + + err = drmGetDevices2(0, devices, num); + if (err < 0) { + render = err; + goto free; + } + + for (i = 0; i < num; i++) { + device = devices[i]; + + if ((device->available_nodes & (1 << DRM_NODE_RENDER)) && + (device->bustype == DRM_BUS_PLATFORM)) { + drmVersionPtr version; + + fd = open(device->nodes[DRM_NODE_RENDER], O_RDWR | O_CLOEXEC); + if (fd < 0) + continue; + + version = drmGetVersion(fd); + if (!version) { + close(fd); + continue; + } + + if (strcmp(version->name, "nouveau") != 0) { + close(fd); + continue; + } + + drmFreeVersion(version); + render = fd; + break; + } + } + + drmFreeDevices(devices, num); + +free: + free(devices); + return render; +} + +static int tegra_screen_import_resource(struct tegra_screen *screen, + struct tegra_resource *resource, + bool has_modifiers) +{ + unsigned usage = PIPE_HANDLE_USAGE_READ; + struct drm_tegra_gem_set_tiling args; + struct winsys_handle handle; + boolean status; + int fd, err; + + memset(&handle, 0, sizeof(handle)); + handle.modifier = DRM_FORMAT_MOD_INVALID; + handle.type = DRM_API_HANDLE_TYPE_FD; + + status = screen->gpu->resource_get_handle(screen->gpu, NULL, resource->gpu, + &handle, usage); + 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); + + if (!has_modifiers) { + memset(&args, 0, sizeof(args)); + args.handle = resource->handle; + + switch (handle.modifier) { + case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED: + args.mode = DRM_TEGRA_GEM_TILING_MODE_TILED; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_ONE_GOB: + args.mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK; + args.value = 0; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_TWO_GOB: + args.mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK; + args.value = 1; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_FOUR_GOB: + args.mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK; + args.value = 2; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_EIGHT_GOB: + args.mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK; + args.value = 3; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB: + args.mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK; + args.value = 4; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB: + args.mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK; + args.value = 5; + break; + + default: + debug_printf("unsupported modifier %" PRIx64 ", assuming linear\n", + handle.modifier); + /* fall-through */ + + case DRM_FORMAT_MOD_LINEAR: + args.mode = DRM_TEGRA_GEM_TILING_MODE_PITCH; + break; + } + + err = drmIoctl(screen->fd, DRM_IOCTL_TEGRA_GEM_SET_TILING, &args); + if (err < 0) { + fprintf(stderr, "failed to set tiling parameters: %s\n", + strerror(errno)); + err = -errno; + goto out; + } + } + + return 0; + +out: + 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); + struct tegra_resource *resource; + int err; + + resource = calloc(1, sizeof(*resource)); + if (!resource) + return NULL; + + resource->gpu = screen->gpu->resource_create(screen->gpu, template); + if (!resource->gpu) + goto free; + + /* import scanout buffers for display */ + if (template->bind & PIPE_BIND_SCANOUT) { + err = tegra_screen_import_resource(screen, resource, false); + 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 boolean +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); + boolean 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 == DRM_API_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 boolean +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 tegra_resource *resource; + int err; + + resource = calloc(1, sizeof(*resource)); + if (!resource) + return NULL; + + resource->gpu = screen->gpu->resource_create_with_modifiers(screen->gpu, + template, + modifiers, + count); + if (!resource->gpu) + goto free; + + err = tegra_screen_import_resource(screen, resource, true); + 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 = tegra_open_render_node(); + 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; +} |