diff options
author | Thierry Reding <treding@nvidia.com> | 2018-04-23 18:32:19 +0200 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2019-08-23 18:08:32 +0200 |
commit | 368e571a6857a591b585eb0c86b2bc8b428f64ca (patch) | |
tree | ee5de36674ddac377e88efa7c9adee7abcb0da49 | |
parent | 4632b0ecbbfdc53b0101523a108611c419a31d8d (diff) |
WIP: tegra: Add VDE support
-rw-r--r-- | src/gallium/drivers/tegra/Makefile.sources | 5 | ||||
-rw-r--r-- | src/gallium/drivers/tegra/meson.build | 5 | ||||
-rw-r--r-- | src/gallium/drivers/tegra/tegra_context.c | 33 | ||||
-rw-r--r-- | src/gallium/drivers/tegra/tegra_context.h | 1 | ||||
-rw-r--r-- | src/gallium/drivers/tegra/tegra_screen.c | 26 | ||||
-rw-r--r-- | src/gallium/drivers/tegra/tegra_screen.h | 3 | ||||
-rw-r--r-- | src/gallium/drivers/tegra/tegra_vde.c | 1690 | ||||
-rw-r--r-- | src/gallium/drivers/tegra/tegra_vde.h | 33 | ||||
-rw-r--r-- | src/gallium/drivers/tegra/tegra_vde_uabi.h | 82 |
9 files changed, 1872 insertions, 6 deletions
diff --git a/src/gallium/drivers/tegra/Makefile.sources b/src/gallium/drivers/tegra/Makefile.sources index af4ff838c7ca..0262a94c6c9c 100644 --- a/src/gallium/drivers/tegra/Makefile.sources +++ b/src/gallium/drivers/tegra/Makefile.sources @@ -3,4 +3,7 @@ C_SOURCES := \ tegra_context.h \ tegra_resource.h \ tegra_screen.c \ - tegra_screen.h + tegra_screen.h \ + tegra_vde.c \ + tegra_vde.h \ + tegra_vde_uabi.h diff --git a/src/gallium/drivers/tegra/meson.build b/src/gallium/drivers/tegra/meson.build index 939d6294601c..48c754afc53a 100644 --- a/src/gallium/drivers/tegra/meson.build +++ b/src/gallium/drivers/tegra/meson.build @@ -23,6 +23,9 @@ files_tegra = files( 'tegra_context.h', 'tegra_resource.h', 'tegra_screen.c', + 'tegra_vde.h', + 'tegra_vde.c', + 'tegra_vde_uabi.h', ) libtegra = static_library( @@ -33,7 +36,7 @@ libtegra = static_library( inc_include, inc_src, inc_gallium, inc_gallium_aux, inc_gallium_drivers, inc_gallium_winsys, ], - dependencies : dep_libdrm, + dependencies : [dep_libdrm, dep_libdrm_tegra], ) driver_tegra = declare_dependency( diff --git a/src/gallium/drivers/tegra/tegra_context.c b/src/gallium/drivers/tegra/tegra_context.c index f9a1c55a7c98..cbc7579671bc 100644 --- a/src/gallium/drivers/tegra/tegra_context.c +++ b/src/gallium/drivers/tegra/tegra_context.c @@ -40,6 +40,9 @@ tegra_destroy(struct pipe_context *pcontext) if (context->base.stream_uploader) u_upload_destroy(context->base.stream_uploader); + if (context->vde) + context->vde->destroy(context->vde); + context->gpu->destroy(context->gpu); free(context); } @@ -988,11 +991,18 @@ tegra_create_video_codec(struct pipe_context *pcontext, struct tegra_context *context = to_tegra_context(pcontext); struct pipe_video_codec *codec = NULL; - printf("> %s(pcontext=%p, template=%p)\n", __func__, pcontext, template); + debug_printf("> %s(pcontext=%p, template=%p)\n", __func__, pcontext, + template); + + if (context->vde) { + codec = context->vde->create_video_codec(context->vde, template); + goto out; + } codec = context->gpu->create_video_codec(context->gpu, template); - printf("< %s() = %p\n", __func__, codec); +out: + debug_printf("< %s() = %p\n", __func__, codec); return codec; } @@ -1003,11 +1013,18 @@ tegra_create_video_buffer(struct pipe_context *pcontext, struct tegra_context *context = to_tegra_context(pcontext); struct pipe_video_buffer *buffer = NULL; - printf("> %s(pcontext=%p, template=%p)\n", __func__, pcontext, template); + debug_printf("> %s(pcontext=%p, template=%p)\n", __func__, pcontext, + template); + + if (context->vde) { + buffer = context->vde->create_video_buffer(context->vde, template); + goto out; + } buffer = context->gpu->create_video_buffer(context->gpu, template); - printf("< %s() = %p\n", __func__, buffer); +out: + debug_printf("< %s() = %p\n", __func__, buffer); return buffer; } @@ -1220,6 +1237,9 @@ tegra_screen_context_create(struct pipe_screen *pscreen, void *priv, struct tegra_screen *screen = to_tegra_screen(pscreen); struct tegra_context *context; + if (screen->vde && (flags & PIPE_CONTEXT_VIDEO_ONLY)) + return screen->vde->context_create(screen->vde, NULL, 0); + context = calloc(1, sizeof(*context)); if (!context) return NULL; @@ -1230,6 +1250,11 @@ tegra_screen_context_create(struct pipe_screen *pscreen, void *priv, goto free; } + /* + if (screen->vde) + context->vde = screen->vde->context_create(screen->vde, NULL, 0); + */ + context->base.screen = &screen->base; context->base.priv = priv; diff --git a/src/gallium/drivers/tegra/tegra_context.h b/src/gallium/drivers/tegra/tegra_context.h index 4869b0913a6f..650bce320dda 100644 --- a/src/gallium/drivers/tegra/tegra_context.h +++ b/src/gallium/drivers/tegra/tegra_context.h @@ -32,6 +32,7 @@ struct tegra_screen; struct tegra_context { struct pipe_context base; struct pipe_context *gpu; + struct pipe_context *vde; }; static inline struct tegra_context * diff --git a/src/gallium/drivers/tegra/tegra_screen.c b/src/gallium/drivers/tegra/tegra_screen.c index ef495cd5c14b..6637cc267fdb 100644 --- a/src/gallium/drivers/tegra/tegra_screen.c +++ b/src/gallium/drivers/tegra/tegra_screen.c @@ -31,6 +31,7 @@ #include "drm-uapi/drm_fourcc.h" #include "drm-uapi/tegra_drm.h" #include <xf86drm.h> +#include <tegra.h> #include "loader/loader.h" #include "pipe/p_state.h" @@ -47,12 +48,17 @@ #include "tegra_context.h" #include "tegra_resource.h" #include "tegra_screen.h" +#include "tegra_vde.h" static void tegra_screen_destroy(struct pipe_screen *pscreen) { struct tegra_screen *screen = to_tegra_screen(pscreen); + if (screen->vde) + screen->vde->destroy(screen->vde); + screen->gpu->destroy(screen->gpu); + drm_tegra_close(screen->drm); free(pscreen); } @@ -105,6 +111,12 @@ tegra_screen_get_video_param(struct pipe_screen *pscreen, enum pipe_video_entrypoint entrypoint, enum pipe_video_cap param) { + struct tegra_screen *screen = to_tegra_screen(pscreen); + + if (screen->vde) + return screen->vde->get_video_param(screen->vde, profile, entrypoint, + param); + switch (param) { case PIPE_VIDEO_CAP_SUPPORTED: if (u_reduce_video_profile(profile) == PIPE_VIDEO_FORMAT_JPEG) @@ -188,6 +200,10 @@ tegra_screen_is_video_format_supported(struct pipe_screen *pscreen, { struct tegra_screen *screen = to_tegra_screen(pscreen); + if (screen->vde) + return screen->vde->is_video_format_supported(screen->vde, format, + profile, entrypoint); + return screen->gpu->is_video_format_supported(screen->gpu, format, profile, entrypoint); } @@ -566,6 +582,7 @@ struct pipe_screen * tegra_screen_create(int fd) { struct tegra_screen *screen; + int err; screen = calloc(1, sizeof(*screen)); if (!screen) @@ -573,6 +590,12 @@ tegra_screen_create(int fd) screen->fd = fd; + err = drm_tegra_new(&screen->drm, fd); + if (err < 0) { + free(screen); + return NULL; + } + screen->gpu_fd = loader_open_render_node("nouveau"); if (screen->gpu_fd < 0) { if (errno != ENOENT) @@ -590,6 +613,9 @@ tegra_screen_create(int fd) return NULL; } + /* VDE may not be present, so continue even if this fails */ + screen->vde = tegra_vde_screen_create(&screen->base, screen->drm); + screen->base.destroy = tegra_screen_destroy; screen->base.get_name = tegra_screen_get_name; screen->base.get_vendor = tegra_screen_get_vendor; diff --git a/src/gallium/drivers/tegra/tegra_screen.h b/src/gallium/drivers/tegra/tegra_screen.h index 558d22f2f993..d03f934a13b3 100644 --- a/src/gallium/drivers/tegra/tegra_screen.h +++ b/src/gallium/drivers/tegra/tegra_screen.h @@ -28,10 +28,13 @@ struct tegra_screen { struct pipe_screen base; + struct drm_tegra *drm; int fd; struct pipe_screen *gpu; int gpu_fd; + + struct pipe_screen *vde; }; static inline struct tegra_screen * diff --git a/src/gallium/drivers/tegra/tegra_vde.c b/src/gallium/drivers/tegra/tegra_vde.c new file mode 100644 index 000000000000..fb52a57d32e4 --- /dev/null +++ b/src/gallium/drivers/tegra/tegra_vde.c @@ -0,0 +1,1690 @@ +/* + * Copyright © 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 <unistd.h> + +#include <sys/ioctl.h> + +#include "pipe/p_video_codec.h" + +#include "util/u_debug.h" +#include "util/u_format.h" +#include "util/u_sampler.h" +#include "util/u_transfer.h" +#include "util/u_upload_mgr.h" +#include "util/u_video.h" + +#include "state_tracker/drm_driver.h" + +#include "vl/vl_defines.h" +#include "vl/vl_rbsp.h" +#include "vl/vl_vlc.h" + +#include "tegra_vde_uabi.h" +#include "tegra_vde.h" + +#include "tegra_drm.h" +#include "tegra.h" + +struct tegra_vde_buffer { + struct pipe_video_buffer base; + + struct pipe_sampler_view *components[VL_NUM_COMPONENTS]; + struct pipe_resource *planes[VL_NUM_COMPONENTS]; + struct pipe_surface *surfaces[VL_MAX_SURFACES]; + unsigned int num_planes; + + unsigned int frame_num; + enum pipe_h264_slice_type slice_type; + bool is_reference; + + struct pipe_resource *aux; +}; + +static inline struct tegra_vde_buffer * +to_tegra_vde_buffer(struct pipe_video_buffer *buffer) +{ + return (struct tegra_vde_buffer *)buffer; +} + +struct tegra_vde_codec { + struct pipe_video_codec base; + + struct drm_tegra *drm; + struct drm_tegra_bo *bitstream; + size_t bitstream_size; + struct drm_tegra_bo *secure; +}; + +static inline struct tegra_vde_codec * +to_tegra_vde_codec(struct pipe_video_codec *codec) +{ + return (struct tegra_vde_codec *)codec; +} + +struct tegra_vde_context { + struct pipe_context base; + struct pipe_context *gpu; +}; + +static inline struct tegra_vde_context * +to_tegra_vde_context(struct pipe_context *context) +{ + return (struct tegra_vde_context *)context; +} + +struct tegra_vde_sampler_view { + struct pipe_sampler_view base; +}; + +static inline struct tegra_vde_sampler_view * +to_tegra_vde_sampler_view(struct pipe_sampler_view *view) +{ + return (struct tegra_vde_sampler_view *)view; +} + +struct tegra_vde_surface { + struct pipe_surface base; +}; + +static inline struct tegra_vde_surface * +to_tegra_vde_surface(struct pipe_surface *surface) +{ + return (struct tegra_vde_surface *)surface; +} + +struct tegra_vde_resource { + struct pipe_resource base; + struct drm_tegra_bo *bo; + unsigned int stride; + unsigned int pitch; + size_t size; +}; + +static inline struct tegra_vde_resource * +to_tegra_vde_resource(struct pipe_resource *resource) +{ + return (struct tegra_vde_resource *)resource; +} + +struct tegra_vde_screen { + struct pipe_screen base; + struct pipe_screen *gpu; + + struct drm_tegra *drm; + int fd; +}; + +static inline struct tegra_vde_screen * +to_tegra_vde_screen(struct pipe_screen *screen) +{ + return (struct tegra_vde_screen *)screen; +} + +/* + * video buffer implementation + */ + +static void tegra_vde_buffer_destroy(struct pipe_video_buffer *pbuffer) +{ + struct tegra_vde_buffer *buffer = to_tegra_vde_buffer(pbuffer); + unsigned int i; + + debug_printf("> %s(pbuffer=%p)\n", __func__, pbuffer); + + if (buffer->surfaces) { + for (i = 0; i < buffer->num_planes; i++) + pipe_surface_reference(&buffer->surfaces[i], NULL); + + free(buffer->surfaces); + } + + pipe_resource_reference(&buffer->aux, NULL); + + if (buffer->planes) { + for (i = 0; i < buffer->num_planes; i++) + pipe_resource_reference(&buffer->planes[i], NULL); + + free(buffer->planes); + } + + free(buffer); + + debug_printf("< %s()\n", __func__); +} + +static struct pipe_sampler_view ** +tegra_vde_buffer_get_sampler_view_planes(struct pipe_video_buffer *pbuffer) +{ + struct pipe_sampler_view **planes = NULL; + + debug_printf("> %s(pbuffer=%p)\n", __func__, pbuffer); + debug_printf("< %s() = %p\n", __func__, planes); + + return planes; +} + +static struct pipe_sampler_view ** +tegra_vde_buffer_get_sampler_view_components(struct pipe_video_buffer *pbuffer) +{ + struct tegra_vde_buffer *buffer = to_tegra_vde_buffer(pbuffer); + struct pipe_sampler_view **components = NULL; + unsigned int i, j, component = 0; + struct pipe_sampler_view tmpl; + + debug_printf("> %s(pbuffer=%p)\n", __func__, pbuffer); + + for (i = 0; i < buffer->num_planes; i++) { + unsigned int num_components = util_format_get_nr_components(buffer->planes[i]->format); + + for (j = 0; j < num_components; j++, component++) { + if (!buffer->components[component]) { + memset(&tmpl, 0, sizeof(tmpl)); + u_sampler_view_default_template(&tmpl, buffer->planes[i], buffer->planes[i]->format); + tmpl.swizzle_r = tmpl.swizzle_g = tmpl.swizzle_b= PIPE_SWIZZLE_X + j; + tmpl.swizzle_a = PIPE_SWIZZLE_1; + + buffer->components[component] = pbuffer->context->create_sampler_view(pbuffer->context, buffer->planes[i], &tmpl); + if (!buffer->components[component]) + goto unref; + } + } + } + + components = buffer->components; + + debug_printf("< %s() = %p\n", __func__, components); + + return components; + +unref: + for (i = 0; i < 3; i++) + pipe_sampler_view_reference(&buffer->components[i], NULL); + + debug_printf("< %s() = NULL\n", __func__); + return NULL; +} + +static struct pipe_surface ** +tegra_vde_buffer_get_surfaces(struct pipe_video_buffer *pbuffer) +{ + struct tegra_vde_buffer *buffer = to_tegra_vde_buffer(pbuffer); + struct pipe_context *context = pbuffer->context; + struct pipe_surface **surfaces = NULL; + struct pipe_surface tmpl; + unsigned int i; + + debug_printf("> %s(pbuffer=%p)\n", __func__, pbuffer); + + for (i = 0; i < buffer->num_planes; i++) { + if (!buffer->surfaces[i]) { + memset(&tmpl, 0, sizeof(tmpl)); + tmpl.format = buffer->planes[i]->format; + + buffer->surfaces[i] = context->create_surface(context, + buffer->planes[i], + &tmpl); + if (!buffer->surfaces[i]) + goto unref; + } + } + + surfaces = buffer->surfaces; + + debug_printf("< %s() = %p\n", __func__, surfaces); + + return surfaces; + +unref: + for (i = 0; i < buffer->num_planes; i++) + pipe_surface_reference(&buffer->surfaces[i], NULL); + + return NULL; +} + +static struct pipe_video_buffer * +tegra_vde_create_video_buffer(struct pipe_context *pcontext, + const struct pipe_video_buffer *template) +{ + struct tegra_vde_buffer *buffer; + unsigned int width, height; + struct pipe_resource tmpl; + + debug_printf("> %s(pcontext=%p, template=%p)\n", __func__, pcontext, + template); + + buffer = calloc(1, sizeof(*buffer)); + if (!buffer) + goto out; + + width = align(template->width, 16); + height = align(template->height, 16); + + buffer->base.context = pcontext; + buffer->base.buffer_format = template->buffer_format; + buffer->base.chroma_format = template->chroma_format; + buffer->base.width = width; + buffer->base.height = height; + buffer->base.interlaced = template->interlaced; + buffer->base.bind = template->bind; + + buffer->base.destroy = tegra_vde_buffer_destroy; + buffer->base.get_sampler_view_planes = tegra_vde_buffer_get_sampler_view_planes; + buffer->base.get_sampler_view_components = tegra_vde_buffer_get_sampler_view_components; + buffer->base.get_surfaces = tegra_vde_buffer_get_surfaces; + + /* XXX parameterize */ + buffer->num_planes = 2; + + memset(&tmpl, 0, sizeof(tmpl)); + tmpl.target = PIPE_TEXTURE_2D; + tmpl.format = PIPE_FORMAT_R8_UNORM; + tmpl.width0 = width; + tmpl.height0 = height; + tmpl.depth0 = 1; + tmpl.array_size = 1; + tmpl.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET; + tmpl.usage = PIPE_USAGE_DEFAULT; + tmpl.flags = 0; + + buffer->planes[0] = pcontext->screen->resource_create(pcontext->screen, + &tmpl); + if (!buffer->planes[0]) + goto destroy; + + tmpl.width0 /= 2; + tmpl.height0 /= 2; + tmpl.format = PIPE_FORMAT_R8G8_UNORM; + + buffer->planes[1] = pcontext->screen->resource_create(pcontext->screen, + &tmpl); + if (!buffer->planes[1]) + goto destroy; + + tmpl.format = PIPE_FORMAT_R8_UNORM; + + buffer->aux = pcontext->screen->resource_create(pcontext->screen, &tmpl); + if (!buffer->aux) + goto destroy; + +out: + debug_printf("< %s() = %p\n", __func__, buffer); + return buffer ? &buffer->base : NULL; + +destroy: + tegra_vde_buffer_destroy(&buffer->base); + debug_printf("< %s() = NULL\n", __func__); + return NULL; +} + +/* + * codec implementation + */ + +static void tegra_vde_codec_destroy(struct pipe_video_codec *codec) +{ + struct tegra_vde_codec *vde = to_tegra_vde_codec(codec); + + debug_printf("> %s(codec=%p)\n", __func__, codec); + + drm_tegra_bo_unref(vde->secure); + drm_tegra_bo_unref(vde->bitstream); + free(vde); + + debug_printf("< %s()\n", __func__); +} + +static void tegra_vde_codec_begin_frame(struct pipe_video_codec *codec, + struct pipe_video_buffer *buffer, + struct pipe_picture_desc *picture) +{ + debug_printf("> %s(codec=%p, buffer=%p, picture=%p)\n", __func__, codec, + buffer, picture); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_codec_decode_bitstream(struct pipe_video_codec *codec, + struct pipe_video_buffer *target, + struct pipe_picture_desc *picture, + unsigned num_buffers, + const void * const *buffers, + const unsigned int *sizes) +{ + struct pipe_h264_picture_desc *desc = (struct pipe_h264_picture_desc *)picture; + struct tegra_vde_buffer *buffer = to_tegra_vde_buffer(target); + struct tegra_vde_codec *vde = to_tegra_vde_codec(codec); + unsigned int i, j, offset = 0; + struct vl_rbsp rbsp; + struct vl_vlc vlc; + void *ptr; + int err; + + debug_printf("> %s(codec=%p, target=%p, picture=%p, num_buffers=%u, buffers=%p, sizes=%p)\n", + __func__, codec, target, picture, num_buffers, buffers, sizes); + debug_printf(" target: %ux%u\n", target->width, target->height); + debug_printf(" planes: %u\n", buffer->num_planes); + debug_printf(" picture: profile: %d entrypoint %d\n", picture->profile, picture->entry_point); + + if (u_reduce_video_profile(picture->profile) != PIPE_VIDEO_FORMAT_MPEG4_AVC) { + debug_printf("not a valid MPEG4 AVC frame\n"); + goto out; + } + + debug_printf(" h264:\n"); + debug_printf(" pps:\n"); + debug_printf(" sps:\n"); + debug_printf(" level_idc: %u\n", desc->pps->sps->level_idc); + debug_printf(" chroma_format_idc: %u\n", desc->pps->sps->chroma_format_idc); + debug_printf(" ...\n"); + debug_printf(" pic_order_cnt_type: %u\n", desc->pps->sps->pic_order_cnt_type); + debug_printf(" ...\n"); + debug_printf(" max_num_ref_frames: %u\n", desc->pps->sps->max_num_ref_frames); + debug_printf(" ..\n"); + debug_printf(" entropy_coding_mode_flags: %u\n", desc->pps->entropy_coding_mode_flag); + debug_printf(" ...\n"); + debug_printf(" chroma_qp_index_offset: %u\n", desc->pps->chroma_qp_index_offset); + debug_printf(" ...\n"); + debug_printf(" frame_num: %u\n", desc->frame_num); + debug_printf(" field_pic_flag: %u\n", desc->field_pic_flag); + debug_printf(" bottom_field_flag: %u\n", desc->bottom_field_flag); + debug_printf(" ...\n"); + debug_printf(" slice_count: %u\n", desc->slice_count); + debug_printf(" field_order_cnt:\n"); + debug_printf(" 0: %u\n", desc->field_order_cnt[0]); + debug_printf(" 1: %u\n", desc->field_order_cnt[1]); + debug_printf(" is_reference: %d\n", desc->is_reference); + debug_printf(" num_ref_frames: %u\n", desc->num_ref_frames); + + for (i = 0; i < desc->num_ref_frames; i++) + debug_printf(" %u: %p\n", i, desc->ref[i]); + + buffer->frame_num = desc->frame_num; + buffer->is_reference = desc->is_reference; + + /* parse slice type from bitstream */ + vl_vlc_init(&vlc, num_buffers, buffers, sizes); + debug_printf(" bitstream:\n"); + debug_printf(" %06x\n", vl_vlc_peekbits(&vlc, 24)); + + if (vl_vlc_peekbits(&vlc, 32) == 0x00000001) + vl_vlc_eatbits(&vlc, 8); + + if (vl_vlc_peekbits(&vlc, 24) != 0x000001) { + debug_printf("bad NAL start code: %06x\n", vl_vlc_peekbits(&vlc, 24)); + return; + } + + vl_vlc_eatbits(&vlc, 24); + + /* forbidden_zero_bit */ + vl_vlc_eatbits(&vlc, 1); + + /* nal_ref_idc */ + vl_vlc_get_uimsbf(&vlc, 2); + + /* nal_unit_type */ + vl_vlc_get_uimsbf(&vlc, 5); + + vl_rbsp_init(&rbsp, &vlc, 128); + + /* first_mb_in_slice */ + vl_rbsp_ue(&rbsp); + + /* slice_type */ + buffer->slice_type = vl_rbsp_ue(&rbsp) % 5; + + err = drm_tegra_bo_map(vde->bitstream, &ptr); + if (err < 0) { + debug_printf("failed to map bitstream buffer: %d\n", err); + goto out; + } + + debug_printf(" copying from %u buffers:\n", num_buffers); + + for (i = 0; i < num_buffers; i++) { + unsigned int count = (sizes[i] < 8) ? sizes[i]: 8; + const uint8_t *data = buffers[i]; + + debug_printf(" %3u: %p (%u bytes)\n", i, buffers[i], sizes[i]); + debug_printf(" "); + + for (j = 0; j < count; j++) + debug_printf(" %02x", data[j]); + + debug_printf("\n"); + + memcpy(ptr + offset, buffers[i], sizes[i]); + + offset += sizes[i]; + } + + memset(ptr + offset, 0, vde->bitstream_size - offset); + drm_tegra_bo_unmap(vde->bitstream); + +out: + debug_printf("< %s()\n", __func__); +} + +static int tegra_vde_h264_frame_init(struct tegra_vde_h264_frame *frame, + struct tegra_vde_buffer *buffer, + int *fds, unsigned int num_fds) +{ + struct tegra_vde_resource *resource; + unsigned int i; + int err; + + for (i = 0; i < buffer->num_planes; i++) { + resource = to_tegra_vde_resource(buffer->planes[i]); + + err = drm_tegra_bo_export(resource->bo, 0); + if (err < 0) { + debug_printf("failed to export plane %u: %d\n", i, err); + + while (i--) + close(fds[i]); + + return err;; + } + + fds[i] = err; + } + + resource = to_tegra_vde_resource(buffer->aux); + + err = drm_tegra_bo_export(resource->bo, 0); + if (err < 0) { + debug_printf("failed to export AUX buffer: %d\n", err); + + while (i--) + close(fds[i]); + + return err; + } + + fds[2] = err; + + resource = to_tegra_vde_resource(buffer->planes[1]); + + debug_printf("%s(): fds: [0]: %d [1]: %d [2]: %d\n", __func__, fds[0], + fds[1], fds[2]); + + frame->y_fd = fds[0]; + frame->cb_fd = fds[1]; + frame->cr_fd = fds[1]; + frame->aux_fd = fds[2]; + + frame->y_offset = 0; + frame->cb_offset = 0; + frame->cr_offset = resource->size / 2; + frame->aux_offset = 0; + + frame->frame_num = buffer->frame_num; + frame->flags = 0; + + if (buffer->slice_type == PIPE_H264_SLICE_TYPE_B) + frame->flags |= FLAG_B_FRAME; + + if (buffer->is_reference) + frame->flags |= FLAG_REFERENCE; + + return 3; +} + +static void tegra_vde_codec_end_frame(struct pipe_video_codec *codec, + struct pipe_video_buffer *buffer, + struct pipe_picture_desc *picture) +{ + struct tegra_vde_screen *screen = to_tegra_vde_screen(codec->context->screen); + struct tegra_vde_buffer *target = to_tegra_vde_buffer(buffer); + struct tegra_vde_codec *vde = to_tegra_vde_codec(codec); + unsigned int num_refs = 0, num_fds, i, index = 0; + int err, bitstream_fd, secure_fd, *fds = NULL; + struct tegra_vde_h264_decoder_ctx args; + struct pipe_h264_picture_desc *desc; + struct tegra_vde_h264_frame *frames; + + debug_printf("> %s(codec=%p, buffer=%p, picture=%p)\n", __func__, codec, + buffer, picture); + + desc = (struct pipe_h264_picture_desc *)picture; + num_refs = desc->num_ref_frames; + + for (i = 0; i < num_refs; i++) { + if (!desc->ref[i]) + break; + } + + num_refs = i; + + debug_printf(" %u reference frames\n", num_refs); + + num_fds = (1 + num_refs) * 3; + + if (u_reduce_video_profile(picture->profile) != PIPE_VIDEO_FORMAT_MPEG4_AVC) { + debug_printf("not a valid MPEG4 AVC frame\n"); + goto out; + } + + frames = calloc(1 + num_refs, sizeof(*frames)); + if (!frames) { + debug_printf("failed to allocate %u frames\n", 1 + num_refs); + goto out; + } + + fds = calloc(num_fds, sizeof(int)); + if (!fds) { + debug_printf("failed to allocate %u file descriptors\n", num_fds); + goto free; + } + + for (i = 0; i < num_fds; i++) + fds[i] = -1; + + bitstream_fd = drm_tegra_bo_export(vde->bitstream, 0); + if (bitstream_fd < 0) { + debug_printf("failed to export bitstream buffer: %d\n", bitstream_fd); + goto free; + } + + secure_fd = drm_tegra_bo_export(vde->secure, 0); + if (secure_fd < 0) { + debug_printf("failed to export secure buffer: %d\n", secure_fd); + goto free; + } + + err = tegra_vde_h264_frame_init(&frames[0], target, &fds[index], + num_fds - index); + if (err < 0) { + debug_printf("failed to initialize H264 frame: %d\n", err); + goto close; + } + + index += err; + + for (i = 0; i < num_refs; i++) { + struct tegra_vde_buffer *ref = to_tegra_vde_buffer(desc->ref[i]); + + err = tegra_vde_h264_frame_init(&frames[1 + i], ref, &fds[index], + num_fds - index); + if (err < 0) { + debug_printf("failed to initialize H264 reference frame: %d\n", err); + goto close; + } + + index += err; + } + + memset(&args, 0, sizeof(args)); + args.bitstream_data_fd = bitstream_fd; + args.bitstream_data_offset = 0; + args.secure_fd = secure_fd; + args.secure_offset = 0; + args.dpb_frames_nb = 1 + num_refs; + args.dpb_frames_ptr = (uintptr_t)frames; + args.dpb_ref_frames_with_earlier_poc_nb = 0; /* XXX */ + args.baseline_profile = 1; + args.level_idc = 15; /* XXX */ + args.log2_max_pic_order_cnt_lsb = desc->pps->sps->log2_max_pic_order_cnt_lsb_minus4 + 4; + args.log2_max_frame_num = desc->pps->sps->log2_max_frame_num_minus4 + 4; + args.pic_order_cnt_type = desc->pps->sps->pic_order_cnt_type; + args.direct_8x8_inference_flag = desc->pps->sps->direct_8x8_inference_flag; + args.pic_width_in_mbs = buffer->width / 16; + args.pic_height_in_mbs = buffer->height / 16; + args.pic_init_qp = desc->pps->pic_init_qp_minus26 + 26; + args.deblocking_filter_control_present_flag = desc->pps->deblocking_filter_control_present_flag; + args.constrained_intra_pred_flag = desc->pps->constrained_intra_pred_flag; + args.chroma_qp_index_offset = desc->pps->chroma_qp_index_offset & 0x1f; + args.pic_order_present_flag = 0; /* XXX */ + args.num_ref_idx_l0_active_minus1 = desc->num_ref_idx_l0_active_minus1; + args.num_ref_idx_l1_active_minus1 = desc->num_ref_idx_l1_active_minus1; + args.reserved = 0; + + do { + err = ioctl(screen->fd, TEGRA_VDE_IOCTL_DECODE_H264, &args); + if (err < 0) + err = -errno; + } while (err == -EINTR || errno == -EAGAIN); + + if (err < 0) + debug_printf("ioctl(TEGRA_VDE_IOCTL_DECODE_H264) failed: %d\n", err); + +close: + for (i = 0; i < num_fds; i++) { + if (fds[i] >= 0) + close(fds[i]); + } + + close(bitstream_fd); + +free: + if (fds) + free(fds); + + free(frames); + +out: + debug_printf("< %s()\n", __func__); +} + +static void tegra_vde_codec_flush(struct pipe_video_codec *codec) +{ + debug_printf("> %s(codec=%p)\n", __func__, codec); + debug_printf("< %s()\n", __func__); +} + +static void tegra_vde_codec_get_feedback(struct pipe_video_codec *codec, + void *feedback, unsigned *size) +{ + debug_printf("> %s(codec=%p, feedback=%p, size%p)\n", __func__, codec, + feedback, size); + debug_printf("< %s()\n", __func__); +} + +static struct pipe_video_codec * +tegra_vde_create_video_codec(struct pipe_context *pcontext, + const struct pipe_video_codec *template) +{ + struct tegra_vde_screen *screen = to_tegra_vde_screen(pcontext->screen); + struct tegra_vde_codec *codec; + int err; + + debug_printf("> %s(pcontext=%p, template=%p)\n", __func__, pcontext, + template); +#ifdef DEBUG + debug_print_video_profile(" profile", template->profile); + debug_printf(" level: %u\n", template->level); + debug_print_video_entrypoint(" entrypoint", template->entrypoint); + debug_printf(" chroma format: %u\n", template->chroma_format); + debug_printf(" resolution: %ux%u\n", template->width, template->height); + debug_printf(" max references: %u\n", template->max_references); + debug_printf(" chunked decode: %d\n", template->expect_chunked_decode); +#endif + + codec = calloc(1, sizeof(*codec)); + if (!codec) + goto out; + + codec->base = *template; + codec->base.context = pcontext; + codec->base.destroy = tegra_vde_codec_destroy; + codec->base.begin_frame = tegra_vde_codec_begin_frame; + codec->base.decode_bitstream = tegra_vde_codec_decode_bitstream; + codec->base.end_frame = tegra_vde_codec_end_frame; + codec->base.flush = tegra_vde_codec_flush; + codec->base.get_feedback = tegra_vde_codec_get_feedback; + + codec->drm = screen->drm; + + err = drm_tegra_bo_new(&codec->bitstream, screen->drm, + DRM_TEGRA_GEM_CONTIGUOUS, 256 * 1024); + if (err < 0) { + free(codec); + codec = NULL; + goto out; + } + + codec->bitstream_size = 256 * 1024; + + err = drm_tegra_bo_new(&codec->secure, screen->drm, + DRM_TEGRA_GEM_CONTIGUOUS, 4096); + if (err < 0) { + drm_tegra_bo_unref(codec->bitstream); + free(codec); + codec = NULL; + goto out; + } + +out: + debug_printf("< %s() = %p\n", __func__, codec); + return codec ? &codec->base : NULL; +} + +/* + * context implementation + */ + +static void tegra_vde_context_destroy(struct pipe_context *pcontext) +{ + struct tegra_vde_context *context = to_tegra_vde_context(pcontext); + + debug_printf("> %s(pcontext=%p)\n", __func__, pcontext); + + if (context->gpu) + context->gpu->destroy(context->gpu); + + free(context); + + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_draw_vbo(struct pipe_context *pcontext, + const struct pipe_draw_info *info) +{ + debug_printf("> %s(pcontext=%p, info=%p)\n", __func__, pcontext, info); + debug_printf("< %s()\n", __func__); +} + +static void * +tegra_vde_create_blend_state(struct pipe_context *pcontext, + const struct pipe_blend_state *template) +{ + void *state = NULL; + debug_printf("> %s(pcontext=%p, template=%p)\n", __func__, pcontext, template); + debug_printf("< %s() = %p\n", __func__, state); + return state; +} + +static void +tegra_vde_bind_blend_state(struct pipe_context *pcontext, void *state) +{ + debug_printf("> %s(pcontext=%p, state=%p)\n", __func__, pcontext, state); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_delete_blend_state(struct pipe_context *pcontext, void *state) +{ + debug_printf("> %s(pcontext=%p, state=%p)\n", __func__, pcontext, state); + debug_printf("< %s()\n", __func__); +} + +static void * +tegra_vde_create_sampler_state(struct pipe_context *pcontext, + const struct pipe_sampler_state *template) +{ + void *state = NULL; + debug_printf("> %s(pcontext=%p, template=%p)\n", __func__, pcontext, template); + debug_printf("< %s() = %p\n", __func__, state); + return state; +} + +static void +tegra_vde_bind_sampler_states(struct pipe_context *pcontext, + enum pipe_shader_type shader, + unsigned int start_slot, + unsigned int num_samplers, + void **samplers) +{ + debug_printf("> %s(pcontext=%p, shader=%d, start_slot=%u, num_samplers=%u, samplers=%p)\n", + __func__, pcontext, shader, start_slot, num_samplers, samplers); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_delete_sampler_state(struct pipe_context *pcontext, void *state) +{ + debug_printf("> %s(pcontext=%p, state=%p)\n", __func__, pcontext, state); + debug_printf("< %s()\n", __func__); +} + +static void * +tegra_vde_create_rasterizer_state(struct pipe_context *pcontext, + const struct pipe_rasterizer_state *template) +{ + void *state = NULL; + debug_printf("> %s(pcontext=%p, template=%p)\n", __func__, pcontext, template); + debug_printf("< %s() = %p\n", __func__, state); + return state; +} + +static void +tegra_vde_bind_rasterizer_state(struct pipe_context *pcontext, + void *state) +{ + debug_printf("> %s(pcontext=%p, state=%p)\n", __func__, pcontext, state); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_delete_rasterizer_state(struct pipe_context *pcontext, void *state) +{ + debug_printf("> %s(pcontext=%p, state=%p)\n", __func__, pcontext, state); + debug_printf("< %s()\n", __func__); +} + +static void * +tegra_vde_create_depth_stencil_alpha_state(struct pipe_context *pcontext, + const struct pipe_depth_stencil_alpha_state *template) +{ + void *state = NULL; + debug_printf("> %s(pcontext=%p, template=%p)\n", __func__, pcontext, template); + debug_printf("< %s() = %p\n", __func__, state); + return state; +} + +static void +tegra_vde_bind_depth_stencil_alpha_state(struct pipe_context *pcontext, + void *state) +{ + debug_printf("> %s(pcontext=%p, state=%p)\n", __func__, pcontext, state); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_delete_depth_stencil_alpha_state(struct pipe_context *pcontext, + void *state) +{ + debug_printf("> %s(pcontext=%p, state=%p)\n", __func__, pcontext, state); + debug_printf("< %s()\n", __func__); +} + +static void * +tegra_vde_create_fs_state(struct pipe_context *pcontext, + const struct pipe_shader_state *template) +{ + void *state = (void *)-1; + debug_printf("> %s(pcontext=%p, template=%p)\n", __func__, pcontext, template); + debug_printf("< %s() = %p\n", __func__, state); + return state; +} + +static void +tegra_vde_bind_fs_state(struct pipe_context *pcontext, void *state) +{ + debug_printf("> %s(pcontext=%p, state=%p)\n", __func__, pcontext, state); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_delete_fs_state(struct pipe_context *pcontext, void *state) +{ + debug_printf("> %s(pcontext=%p, state=%p)\n", __func__, pcontext, state); + debug_printf("< %s()\n", __func__); +} + +static void * +tegra_vde_create_vs_state(struct pipe_context *pcontext, + const struct pipe_shader_state *template) +{ + void *state = (void *)-1; + debug_printf("> %s(pcontext=%p, template=%p)\n", __func__, pcontext, template); + debug_printf("< %s() = %p\n", __func__, state); + return state; +} + +static void +tegra_vde_bind_vs_state(struct pipe_context *pcontext, void *state) +{ + debug_printf("> %s(pcontext=%p, state=%p)\n", __func__, pcontext, state); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_delete_vs_state(struct pipe_context *pcontext, void *state) +{ + debug_printf("> %s(pcontext=%p, state=%p)\n", __func__, pcontext, state); + debug_printf("< %s()\n", __func__); +} + +static void * +tegra_vde_create_vertex_elements_state(struct pipe_context *pcontext, + unsigned int num_elements, + const struct pipe_vertex_element *template) +{ + void *state = (void *)-1; + debug_printf("> %s(pcontext=%p, num_elements=%u, template=%p)\n", __func__, + pcontext, num_elements, template); + debug_printf("< %s() = %p\n", __func__, state); + return state; +} + +static void +tegra_vde_bind_vertex_elements_state(struct pipe_context *pcontext, void *state) +{ + debug_printf("> %s(pcontext=%p, state=%p)\n", __func__, pcontext, state); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_delete_vertex_elements_state(struct pipe_context *pcontext, void *state) +{ + debug_printf("> %s(pcontext=%p, state=%p)\n", __func__, pcontext, state); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_set_constant_buffer(struct pipe_context *pcontext, + enum pipe_shader_type shader, + unsigned int index, + const struct pipe_constant_buffer *buffer) +{ + debug_printf("> %s(pcontext=%p, shader=%d, index=%u, buffer=%p)\n", + __func__, pcontext, shader, index, buffer); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_set_framebuffer_state(struct pipe_context *pcontext, + const struct pipe_framebuffer_state *state) +{ + debug_printf("> %s(pcontext=%p, state=%p)\n", __func__, pcontext, state); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_set_scissor_states(struct pipe_context *pcontext, + unsigned int start_slot, + unsigned int num_scissors, + const struct pipe_scissor_state *states) +{ + debug_printf("> %s(pcontext=%p, start_slot=%u, num_scissors=%u, states=%p)\n", + __func__, pcontext, start_slot, num_scissors, states); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_set_viewport_states(struct pipe_context *pcontext, + unsigned int start_slot, + unsigned int num_viewports, + const struct pipe_viewport_state *states) +{ + debug_printf("> %s(pcontext=%p, start_slot=%u, num_viewports=%u, states=%p)\n", + __func__, pcontext, start_slot, num_viewports, states); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_set_sampler_views(struct pipe_context *pcontext, + enum pipe_shader_type shader, + unsigned int start_slot, + unsigned int num_views, + struct pipe_sampler_view **views) +{ + debug_printf("> %s(pcontext=%p, shader=%d, start_slot=%u, num_views=%u, views=%p)\n", + __func__, pcontext, shader, start_slot, num_views, views); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_set_vertex_buffers(struct pipe_context *pcontext, + unsigned int start_slot, + unsigned int num_buffers, + const struct pipe_vertex_buffer *buffers) +{ + debug_printf("> %s(pcontext=%p, start_slot=%u, num_buffers=%u, buffers=%p)\n", + __func__, pcontext, start_slot, num_buffers, buffers); + debug_printf("< %s()\n", __func__); +} + +static void tegra_vde_clear_render_target(struct pipe_context *pcontext, + struct pipe_surface *dst, + const union pipe_color_union *color, + unsigned dstx, unsigned dsty, + unsigned width, unsigned height, + bool render_condition_enabled) +{ + struct tegra_vde_resource *resource = to_tegra_vde_resource(dst->texture); + void *ptr; + int err; + + debug_printf("> %s(pcontext=%p, dst=%p, color=%p, dstx=%u, dsty=%u, width=%u, height=%u, render_condition_enabled=%d)\n", + __func__, pcontext, dst, color, dstx, dsty, width, height, render_condition_enabled); + + err = drm_tegra_bo_map(resource->bo, &ptr); + if (err < 0) { + debug_printf(" failed to map buffer object: %d\n", err); + goto out; + } + + memset(ptr, 0xff, resource->size); + drm_tegra_bo_unmap(resource->bo); + +out: + debug_printf("< %s()\n", __func__); +} + +static void tegra_vde_flush(struct pipe_context *pcontext, + struct pipe_fence_handle **pfence, + unsigned int flags) +{ + debug_printf("> %s(pcontext=%p, pfence=%p, flags=%x)\n", __func__, + pcontext, pfence, flags); + debug_printf("< %s()\n", __func__); +} + +static void * +tegra_vde_transfer_map(struct pipe_context *pcontext, + struct pipe_resource *presource, + unsigned int level, + unsigned int usage, + const struct pipe_box *box, + struct pipe_transfer **transferp) +{ + struct tegra_vde_resource *resource = to_tegra_vde_resource(presource); + struct pipe_transfer *transfer; + void *map = NULL; + int err; + + debug_printf("> %s(pcontext=%p, presource=%p, level=%u, usage=%x, box=%p, transferp=%p)\n", + __func__, pcontext, presource, level, usage, box, transferp); + + transfer = calloc(1, sizeof(*transfer)); + if (!transfer) + goto out; + + transfer->resource = presource; + transfer->level = level; + transfer->usage = usage; + transfer->box = *box; + transfer->stride = 0; + transfer->layer_stride = 0; + + err = drm_tegra_bo_map(resource->bo, &map); + if (err < 0) { + debug_printf("failed to map buffer object: %d\n", err); + free(transfer); + goto out; + } + + *transferp = transfer; + +out: + debug_printf("< %s() = %p\n", __func__, map); + return map; +} + +static void +tegra_vde_transfer_flush_region(struct pipe_context *pcontext, + struct pipe_transfer *transfer, + const struct pipe_box *box) +{ + debug_printf("> %s(pcontext=%p, transfer=%p, box=%p)\n", __func__, pcontext, transfer, box); + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_transfer_unmap(struct pipe_context *pcontext, + struct pipe_transfer *transfer) +{ + struct tegra_vde_resource *resource = to_tegra_vde_resource(transfer->resource); + + debug_printf("> %s(pcontext=%p, transfer=%p)\n", __func__, pcontext, transfer); + + drm_tegra_bo_unmap(resource->bo); + + debug_printf("< %s()\n", __func__); +} + +static void +tegra_vde_texture_subdata(struct pipe_context *pcontext, + struct pipe_resource *presource, + unsigned int level, unsigned int usage, + const struct pipe_box *box, + const void *data, + unsigned int stride, + unsigned int layer_stride) +{ + debug_printf("> %s(pcontext=%p, presource=%p, level=%u, usage=%x, box=%p, data=%p, stride=%u, layer_stride=%u)\n", + __func__, pcontext, presource, level, usage, box, data, + stride, layer_stride); + + u_default_texture_subdata(pcontext, presource, level, usage, box, data, + stride, layer_stride); + + debug_printf("< %s()\n", __func__); +} + +static struct pipe_sampler_view * +tegra_vde_create_sampler_view(struct pipe_context *pcontext, + struct pipe_resource *texture, + const struct pipe_sampler_view *template) +{ + struct tegra_vde_sampler_view *view; + + debug_printf("> %s(pcontext=%p, texture=%p, template=%p)\n", __func__, + pcontext, texture, template); + + view = calloc(1, sizeof(*view)); + if (!view) + goto out; + + view->base = *template; + view->base.context = pcontext; + + pipe_reference_init(&view->base.reference, 1); + pipe_resource_reference(&view->base.texture, texture); + +out: + debug_printf("< %s() = %p\n", __func__, view); + return view ? &view->base : NULL; +} + +static void +tegra_vde_sampler_view_destroy(struct pipe_context *pcontext, + struct pipe_sampler_view *pview) +{ + struct tegra_vde_sampler_view *view = to_tegra_vde_sampler_view(pview); + + debug_printf("> %s(pcontext=%p, pview=%p)\n", __func__, pcontext, pview); + + free(view); + + debug_printf("< %s()\n", __func__); +} + +static struct pipe_surface * +tegra_vde_create_surface(struct pipe_context *pcontext, + struct pipe_resource *presource, + const struct pipe_surface *template) +{ + struct tegra_vde_surface *surface; + + debug_printf("> %s(pcontext=%p, presource=%p, template=%p)\n", __func__, + pcontext, presource, template); + + surface = calloc(1, sizeof(*surface)); + if (!surface) + goto out; + + pipe_reference_init(&surface->base.reference, 1); + pipe_resource_reference(&surface->base.texture, presource); + + surface->base.context = pcontext; + surface->base.format = template->format; + surface->base.writable = template->writable; + surface->base.width = presource->width0; + surface->base.height = presource->height0; + +out: + debug_printf("< %s() = %p\n", __func__, surface); + return surface ? &surface->base : NULL; +} + +static void +tegra_vde_surface_destroy(struct pipe_context *pcontext, + struct pipe_surface *psurface) +{ + struct tegra_vde_surface *surface = to_tegra_vde_surface(psurface); + + debug_printf("> %s(pcontext=%p, psurface=%p)\n", __func__, pcontext, + psurface); + + free(surface); + + debug_printf("< %s()\n", __func__); +} + +static struct pipe_context * +tegra_vde_context_create(struct pipe_screen *pscreen, void *priv, + unsigned flags) +{ + struct tegra_vde_screen *screen = to_tegra_vde_screen(pscreen); + struct tegra_vde_context *context; + + debug_printf("> %s(pscreen=%p, priv=%p, flags=%x)\n", __func__, pscreen, + priv, flags); + + context = calloc(1, sizeof(*context)); + if (!context) + goto out; + + context->base.screen = pscreen; + context->base.priv = priv; + + context->gpu = screen->gpu->context_create(screen->gpu, NULL, 0); + if (!context->gpu) { + free(context); + goto out; + } + + context->base.stream_uploader = u_upload_create_default(&context->base); + if (!context->base.stream_uploader) { + context->gpu->destroy(context->gpu); + free(context); + goto out; + } + + context->base.const_uploader = context->base.stream_uploader; + + context->base.destroy = tegra_vde_context_destroy; + context->base.draw_vbo = tegra_vde_draw_vbo; + context->base.create_blend_state = tegra_vde_create_blend_state; + context->base.bind_blend_state = tegra_vde_bind_blend_state; + context->base.delete_blend_state = tegra_vde_delete_blend_state; + context->base.create_sampler_state = tegra_vde_create_sampler_state; + context->base.bind_sampler_states = tegra_vde_bind_sampler_states; + context->base.delete_sampler_state = tegra_vde_delete_sampler_state; + context->base.clear_render_target = tegra_vde_clear_render_target; + context->base.flush = tegra_vde_flush; + context->base.transfer_map = tegra_vde_transfer_map; + context->base.transfer_flush_region = tegra_vde_transfer_flush_region; + context->base.transfer_unmap = tegra_vde_transfer_unmap; + context->base.texture_subdata = tegra_vde_texture_subdata; + context->base.create_sampler_view = tegra_vde_create_sampler_view; + context->base.sampler_view_destroy = tegra_vde_sampler_view_destroy; + context->base.create_rasterizer_state = tegra_vde_create_rasterizer_state; + context->base.bind_rasterizer_state = tegra_vde_bind_rasterizer_state; + context->base.delete_rasterizer_state = tegra_vde_delete_rasterizer_state; + context->base.create_depth_stencil_alpha_state = tegra_vde_create_depth_stencil_alpha_state; + context->base.bind_depth_stencil_alpha_state = tegra_vde_bind_depth_stencil_alpha_state; + context->base.delete_depth_stencil_alpha_state = tegra_vde_delete_depth_stencil_alpha_state; + context->base.create_fs_state = tegra_vde_create_fs_state; + context->base.bind_fs_state = tegra_vde_bind_fs_state; + context->base.delete_fs_state = tegra_vde_delete_fs_state; + context->base.create_vs_state = tegra_vde_create_vs_state; + context->base.bind_vs_state = tegra_vde_bind_vs_state; + context->base.delete_vs_state = tegra_vde_delete_vs_state; + context->base.create_vertex_elements_state = tegra_vde_create_vertex_elements_state; + context->base.bind_vertex_elements_state = tegra_vde_bind_vertex_elements_state; + context->base.delete_vertex_elements_state = tegra_vde_delete_vertex_elements_state; + context->base.set_constant_buffer = tegra_vde_set_constant_buffer; + context->base.set_framebuffer_state = tegra_vde_set_framebuffer_state; + context->base.set_scissor_states = tegra_vde_set_scissor_states; + context->base.set_viewport_states = tegra_vde_set_viewport_states; + context->base.set_sampler_views = tegra_vde_set_sampler_views; + context->base.set_vertex_buffers = tegra_vde_set_vertex_buffers; + context->base.create_surface = tegra_vde_create_surface; + context->base.surface_destroy = tegra_vde_surface_destroy; + context->base.create_video_codec = tegra_vde_create_video_codec; + context->base.create_video_buffer = tegra_vde_create_video_buffer; + +out: + debug_printf("< %s() = %p\n", __func__, context); + return context ? &context->base : NULL; +} + +/* + * screen implementation + */ + +static void tegra_vde_screen_destroy(struct pipe_screen *pscreen) +{ + struct tegra_vde_screen *screen = to_tegra_vde_screen(pscreen); + + debug_printf("> %s(pscreen=%p)\n", __func__, pscreen); + + /* XXX this recurses */ + /* + if (screen->gpu) + screen->gpu->destroy(screen->gpu); + */ + + close(screen->fd); + free(screen); + + debug_printf("< %s()\n", __func__); +} + +static int tegra_vde_get_param(struct pipe_screen *pscreen, + enum pipe_cap param) +{ + int ret; + + debug_printf("> %s(pscreen=%p, param=%d)\n", __func__, pscreen, param); + + switch (param) { + case PIPE_CAP_NPOT_TEXTURES: + ret = 1; + break; + + case PIPE_CAP_MAX_TEXTURE_2D_LEVELS: + ret = 12; + break; + + case PIPE_CAP_CONSTBUF0_FLAGS: + ret = 0; + break; + + default: + debug_printf("unknown parameter %d\n", param); + ret = 0; + break; + } + + debug_printf("< %s()\n", __func__); + return ret; +} + +static int tegra_vde_get_video_param(struct pipe_screen *pscreen, + enum pipe_video_profile profile, + enum pipe_video_entrypoint entrypoint, + enum pipe_video_cap param) +{ + int value = 0; + + debug_printf("> %s(pscreen=%p, profile=%d, entrypoint=%d, param=%d)\n", + __func__, pscreen, profile, entrypoint, param); +#ifdef DEBUG + debug_print_video_profile(" profile", profile); + debug_print_video_entrypoint(" entrypoint", entrypoint); + debug_print_video_cap(" param", param); +#endif + + switch (param) { + case PIPE_VIDEO_CAP_SUPPORTED: + switch (profile) { + case PIPE_VIDEO_PROFILE_MPEG4_AVC_BASELINE: + case PIPE_VIDEO_PROFILE_MPEG4_AVC_CONSTRAINED_BASELINE: + switch (entrypoint) { + case PIPE_VIDEO_ENTRYPOINT_BITSTREAM: + value = 1; + break; + + default: + break; + } + + break; + + default: + break; + } + + break; + + case PIPE_VIDEO_CAP_NPOT_TEXTURES: + value = 1; + break; + + case PIPE_VIDEO_CAP_MAX_WIDTH: + value = 2048 - 16; + break; + + case PIPE_VIDEO_CAP_MAX_HEIGHT: + value = 2048 - 16; + break; + + case PIPE_VIDEO_CAP_PREFERED_FORMAT: + value = PIPE_FORMAT_YV12; + break; + + case PIPE_VIDEO_CAP_PREFERS_INTERLACED: + value = 0; + break; + + case PIPE_VIDEO_CAP_SUPPORTS_PROGRESSIVE: + value = 1; + break; + + case PIPE_VIDEO_CAP_SUPPORTS_INTERLACED: + value = 0; + break; + + case PIPE_VIDEO_CAP_MAX_LEVEL: + value = 0; + break; + + case PIPE_VIDEO_CAP_STACKED_FRAMES: + value = 0; + break; + + default: + debug_printf("unknown parameter: %d\n", param); + value = 0; + break; + } + + debug_printf("< %s() = %d\n", __func__, value); + return value; +} + +static boolean +tegra_vde_is_format_supported(struct pipe_screen *pscreen, + enum pipe_format format, + enum pipe_texture_target target, + unsigned sample_count, + unsigned bindings) +{ + boolean ret = false; + + debug_printf("> %s(pscreen=%p, format=%d, target=%d, sample_count=%u, bindings=%x)\n", + __func__, pscreen, format, target, sample_count, bindings); +#ifdef DEBUG + debug_printf(" format: %s\n", util_format_name(format)); +#endif + + switch (format) { + case PIPE_FORMAT_R8G8B8A8_UNORM: + case PIPE_FORMAT_B8G8R8A8_UNORM: + ret = true; + break; + + default: + break; + } + + debug_printf("< %s() = %d\n", __func__, ret); + return ret; +} + +static boolean +tegra_vde_is_video_format_supported(struct pipe_screen *pscreen, + enum pipe_format format, + enum pipe_video_profile profile, + enum pipe_video_entrypoint entrypoint) +{ + boolean ret = false; + + debug_printf("> %s(pscreen=%p, format=%d, profile=%d, entrypoint=%d)\n", + __func__, pscreen, format, profile, entrypoint); +#ifdef DEBUG + debug_printf(" format: %s\n", util_format_name(format)); + debug_print_video_profile(" profile", profile); + debug_print_video_entrypoint(" entrypoint", entrypoint); +#endif + + switch (entrypoint) { + case PIPE_VIDEO_ENTRYPOINT_BITSTREAM: + switch (format) { + /* + case PIPE_FORMAT_UYVY: + case PIPE_FORMAT_YUYV: + */ + case PIPE_FORMAT_YV12: + /* + case PIPE_FORMAT_NV12: + */ + ret = true; + break; + + default: + break; + } + break; + + default: + break; + } + + debug_printf("< %s() = %d\n", __func__, ret); + return ret; +} + +static boolean +tegra_vde_can_create_resource(struct pipe_screen *pscreen, + const struct pipe_resource *template) +{ + debug_printf("> %s(pscreen=%p, template=%p)\n", __func__, pscreen, template); + debug_printf("< %s()\n", __func__); + + return true; +} + +static struct pipe_resource * +tegra_vde_resource_create(struct pipe_screen *pscreen, + const struct pipe_resource *template) +{ + struct tegra_vde_screen *screen = to_tegra_vde_screen(pscreen); + struct tegra_vde_resource *resource; + unsigned int block_size; + int err; + + debug_printf("> %s(pscreen=%p, template=%p)\n", __func__, pscreen, template); + + resource = calloc(1, sizeof(*resource)); + if (!resource) + goto out; + + resource->base = *template; + pipe_reference_init(&resource->base.reference, 1); + resource->base.screen = pscreen; + + block_size = util_format_get_blocksize(template->format); + resource->stride = align(template->width0, 16); + resource->pitch = resource->stride * block_size; + resource->size = resource->pitch * align(template->height0, 16); + + debug_printf(" allocating buffer object with %zu bytes\n", resource->size); + + err = drm_tegra_bo_new(&resource->bo, screen->drm, DRM_TEGRA_GEM_CONTIGUOUS, + resource->size); + if (err < 0) { + debug_printf("failed to allocate buffer object: %d\n", err); + free(resource); + resource = NULL; + goto out; + } + +out: + debug_printf("< %s() = %p\n", __func__, resource); + return resource ? &resource->base : NULL; +} + +static boolean +tegra_vde_resource_get_handle(struct pipe_screen *pscreen, + struct pipe_context *pcontext, + struct pipe_resource *presource, + struct winsys_handle *whandle, + unsigned int usage) +{ + struct tegra_vde_resource *resource = to_tegra_vde_resource(presource); + boolean ret = FALSE; + int err; + + debug_printf("> %s(pscreen=%p, pcontext=%p, presource=%p, whandle=%p, usage=%x)\n", + __func__, pscreen, pcontext, presource, whandle, usage); + + if (whandle->type == DRM_API_HANDLE_TYPE_SHARED) { + uint32_t name; + + err = drm_tegra_bo_get_name(resource->bo, &name); + if (err < 0) + goto out; + + whandle->handle = name; + ret = TRUE; + } else if (whandle->type == DRM_API_HANDLE_TYPE_KMS) { + uint32_t handle; + + err = drm_tegra_bo_get_handle(resource->bo, &handle); + if (err < 0) + goto out; + + whandle->handle = handle; + ret = TRUE; + } else if (whandle->type == DRM_API_HANDLE_TYPE_FD) { + int fd; + + fd = drm_tegra_bo_export(resource->bo, 0); + if (fd < 0) + goto out; + + whandle->handle = fd; + ret = TRUE; + } + +out: + debug_printf("< %s() = %d\n", __func__, ret); + return ret; +} + +static void tegra_vde_resource_destroy(struct pipe_screen *pscreen, + struct pipe_resource *presource) +{ + struct tegra_vde_resource *resource = to_tegra_vde_resource(presource); + + debug_printf("> %s(pscreen=%p, presource=%p)\n", __func__, pscreen, + presource); + + drm_tegra_bo_unref(resource->bo); + free(resource); + + debug_printf("< %s()\n", __func__); +} + +static void tegra_vde_flush_frontbuffer(struct pipe_screen *pscreen, + struct pipe_resource *presource, + unsigned int level, + unsigned int layer, + void *winsys_drawable_handle, + struct pipe_box *box) +{ + debug_printf("> %s(pscreen=%p, presource=%p, level=%u, layer=%u, winsys_drawable_handle=%p, box=%p)\n", + __func__, pscreen, presource, level, layer, + winsys_drawable_handle, box); + debug_printf("< %s()\n", __func__); +} + +static void tegra_vde_fence_reference(struct pipe_screen *pscreen, + struct pipe_fence_handle **ptr, + struct pipe_fence_handle *fence) +{ + debug_printf("> %s(pscreen=%p, ptr=%p, fence=%p)\n", __func__, pscreen, + ptr, fence); + debug_printf("< %s()\n", __func__); +} + +struct pipe_screen *tegra_vde_screen_create(struct pipe_screen *parent, + struct drm_tegra *drm) +{ + struct tegra_vde_screen *screen; + + debug_printf("> %s()\n", __func__); + + screen = calloc(1, sizeof(*screen)); + if (!screen) + goto out; + + screen->gpu = parent; + screen->drm = drm; + + screen->base.destroy = tegra_vde_screen_destroy; + screen->base.get_param = tegra_vde_get_param; + screen->base.get_video_param = tegra_vde_get_video_param; + screen->base.context_create = tegra_vde_context_create; + screen->base.is_format_supported = tegra_vde_is_format_supported; + screen->base.is_video_format_supported = tegra_vde_is_video_format_supported; + screen->base.can_create_resource = tegra_vde_can_create_resource; + screen->base.resource_create = tegra_vde_resource_create; + screen->base.resource_get_handle = tegra_vde_resource_get_handle; + screen->base.resource_destroy = tegra_vde_resource_destroy; + screen->base.flush_frontbuffer = tegra_vde_flush_frontbuffer; + screen->base.fence_reference = tegra_vde_fence_reference; + + screen->fd = open("/dev/tegra_vde", O_RDWR); + if (screen->fd < 0) + goto free; + + debug_printf("< %s() = %p\n", __func__, screen); + return &screen->base; + +free: + free(screen); +out: + debug_printf("< %s() = NULL\n", __func__); + return NULL; +} diff --git a/src/gallium/drivers/tegra/tegra_vde.h b/src/gallium/drivers/tegra/tegra_vde.h new file mode 100644 index 000000000000..089618a9e2de --- /dev/null +++ b/src/gallium/drivers/tegra/tegra_vde.h @@ -0,0 +1,33 @@ +/* + * Copyright © 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. + */ + +#ifndef TEGRA_VDE_H +#define TEGRA_VDE_H + +struct pipe_screen; +struct drm_tegra; + +struct pipe_screen *tegra_vde_screen_create(struct pipe_screen *parent, + struct drm_tegra *drm); + +#endif /* TEGRA_VDE_H */ diff --git a/src/gallium/drivers/tegra/tegra_vde_uabi.h b/src/gallium/drivers/tegra/tegra_vde_uabi.h new file mode 100644 index 000000000000..ff7759297b78 --- /dev/null +++ b/src/gallium/drivers/tegra/tegra_vde_uabi.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2016-2017 Dmitry Osipenko <digetx@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _UAPI_TEGRA_VDE_H_ +#define _UAPI_TEGRA_VDE_H_ + +#include <linux/types.h> +#include <asm/ioctl.h> + +#define FLAG_B_FRAME (1 << 0) +#define FLAG_REFERENCE (1 << 1) + +struct tegra_vde_h264_frame { + __s32 y_fd; + __s32 cb_fd; + __s32 cr_fd; + __s32 aux_fd; + __u32 y_offset; + __u32 cb_offset; + __u32 cr_offset; + __u32 aux_offset; + __u32 frame_num; + __u32 flags; + __u32 block_height; + + __u32 reserved; +} __attribute__((packed)); + +struct tegra_vde_h264_decoder_ctx { + __s32 bitstream_data_fd; + __u32 bitstream_data_offset; + + __s32 secure_fd; + __u32 secure_offset; + + __u64 dpb_frames_ptr; + __u8 dpb_frames_nb; + __u8 dpb_ref_frames_with_earlier_poc_nb; + + // SPS + __u8 baseline_profile; + __u8 level_idc; + __u8 log2_max_pic_order_cnt_lsb; + __u8 log2_max_frame_num; + __u8 pic_order_cnt_type; + __u8 direct_8x8_inference_flag; + __u8 pic_width_in_mbs; + __u8 pic_height_in_mbs; + + // PPS + __u8 pic_init_qp; + __u8 deblocking_filter_control_present_flag; + __u8 constrained_intra_pred_flag; + __u8 chroma_qp_index_offset; + __u8 pic_order_present_flag; + + // Slice header + __u8 num_ref_idx_l0_active_minus1; + __u8 num_ref_idx_l1_active_minus1; + + __u32 reserved; +} __attribute__((packed)); + +#define VDE_IOCTL_BASE ('v' + 0x20) + +#define VDE_IO(nr) _IO(VDE_IOCTL_BASE, nr) +#define VDE_IOR(nr, type) _IOR(VDE_IOCTL_BASE, nr, type) +#define VDE_IOW(nr, type) _IOW(VDE_IOCTL_BASE, nr, type) +#define VDE_IOWR(nr, type) _IOWR(VDE_IOCTL_BASE, nr, type) + +#define TEGRA_VDE_DECODE_H264 0x00 + +#define TEGRA_VDE_IOCTL_DECODE_H264 \ + VDE_IOW(TEGRA_VDE_DECODE_H264, struct tegra_vde_h264_decoder_ctx) + +#endif // _UAPI_TEGRA_VDE_H_ |