Path: blob/21.2-virgl/src/gallium/frontends/va/image.c
4561 views
/**************************************************************************1*2* Copyright 2010 Thomas Balling Sørensen & Orasanu Lucian.3* Copyright 2014 Advanced Micro Devices, Inc.4* All Rights Reserved.5*6* Permission is hereby granted, free of charge, to any person obtaining a7* copy of this software and associated documentation files (the8* "Software"), to deal in the Software without restriction, including9* without limitation the rights to use, copy, modify, merge, publish,10* distribute, sub license, and/or sell copies of the Software, and to11* permit persons to whom the Software is furnished to do so, subject to12* the following conditions:13*14* The above copyright notice and this permission notice (including the15* next paragraph) shall be included in all copies or substantial portions16* of the Software.17*18* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS19* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF20* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.21* IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR22* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,23* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE24* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.25*26**************************************************************************/2728#include "pipe/p_screen.h"2930#include "util/u_memory.h"31#include "util/u_handle_table.h"32#include "util/u_surface.h"33#include "util/u_video.h"34#include "util/u_process.h"3536#include "vl/vl_winsys.h"37#include "vl/vl_video_buffer.h"3839#include "va_private.h"4041static const VAImageFormat formats[] =42{43{VA_FOURCC('N','V','1','2')},44{VA_FOURCC('P','0','1','0')},45{VA_FOURCC('P','0','1','6')},46{VA_FOURCC('I','4','2','0')},47{VA_FOURCC('Y','V','1','2')},48{VA_FOURCC('Y','U','Y','V')},49{VA_FOURCC('Y','U','Y','2')},50{VA_FOURCC('U','Y','V','Y')},51{.fourcc = VA_FOURCC('B','G','R','A'), .byte_order = VA_LSB_FIRST, 32, 32,520x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000},53{.fourcc = VA_FOURCC('R','G','B','A'), .byte_order = VA_LSB_FIRST, 32, 32,540x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000},55{.fourcc = VA_FOURCC('B','G','R','X'), .byte_order = VA_LSB_FIRST, 32, 24,560x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000},57{.fourcc = VA_FOURCC('R','G','B','X'), .byte_order = VA_LSB_FIRST, 32, 24,580x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000}59};6061static void62vlVaVideoSurfaceSize(vlVaSurface *p_surf, int component,63unsigned *width, unsigned *height)64{65*width = p_surf->templat.width;66*height = p_surf->templat.height;6768vl_video_buffer_adjust_size(width, height, component,69pipe_format_to_chroma_format(p_surf->templat.buffer_format),70p_surf->templat.interlaced);71}7273VAStatus74vlVaQueryImageFormats(VADriverContextP ctx, VAImageFormat *format_list, int *num_formats)75{76struct pipe_screen *pscreen;77enum pipe_format format;78int i;7980STATIC_ASSERT(ARRAY_SIZE(formats) == VL_VA_MAX_IMAGE_FORMATS);8182if (!ctx)83return VA_STATUS_ERROR_INVALID_CONTEXT;8485if (!(format_list && num_formats))86return VA_STATUS_ERROR_INVALID_PARAMETER;8788*num_formats = 0;89pscreen = VL_VA_PSCREEN(ctx);90for (i = 0; i < ARRAY_SIZE(formats); ++i) {91format = VaFourccToPipeFormat(formats[i].fourcc);92if (pscreen->is_video_format_supported(pscreen, format,93PIPE_VIDEO_PROFILE_UNKNOWN,94PIPE_VIDEO_ENTRYPOINT_BITSTREAM))95format_list[(*num_formats)++] = formats[i];96}9798return VA_STATUS_SUCCESS;99}100101VAStatus102vlVaCreateImage(VADriverContextP ctx, VAImageFormat *format, int width, int height, VAImage *image)103{104VAStatus status;105vlVaDriver *drv;106VAImage *img;107int w, h;108109if (!ctx)110return VA_STATUS_ERROR_INVALID_CONTEXT;111112if (!(format && image && width && height))113return VA_STATUS_ERROR_INVALID_PARAMETER;114115drv = VL_VA_DRIVER(ctx);116117img = CALLOC(1, sizeof(VAImage));118if (!img)119return VA_STATUS_ERROR_ALLOCATION_FAILED;120mtx_lock(&drv->mutex);121img->image_id = handle_table_add(drv->htab, img);122mtx_unlock(&drv->mutex);123124img->format = *format;125img->width = width;126img->height = height;127w = align(width, 2);128h = align(height, 2);129130switch (format->fourcc) {131case VA_FOURCC('N','V','1','2'):132img->num_planes = 2;133img->pitches[0] = w;134img->offsets[0] = 0;135img->pitches[1] = w;136img->offsets[1] = w * h;137img->data_size = w * h * 3 / 2;138break;139140case VA_FOURCC('P','0','1','0'):141case VA_FOURCC('P','0','1','6'):142img->num_planes = 2;143img->pitches[0] = w * 2;144img->offsets[0] = 0;145img->pitches[1] = w * 2;146img->offsets[1] = w * h * 2;147img->data_size = w * h * 3;148break;149150case VA_FOURCC('I','4','2','0'):151case VA_FOURCC('Y','V','1','2'):152img->num_planes = 3;153img->pitches[0] = w;154img->offsets[0] = 0;155img->pitches[1] = w / 2;156img->offsets[1] = w * h;157img->pitches[2] = w / 2;158img->offsets[2] = w * h * 5 / 4;159img->data_size = w * h * 3 / 2;160break;161162case VA_FOURCC('U','Y','V','Y'):163case VA_FOURCC('Y','U','Y','V'):164case VA_FOURCC('Y','U','Y','2'):165img->num_planes = 1;166img->pitches[0] = w * 2;167img->offsets[0] = 0;168img->data_size = w * h * 2;169break;170171case VA_FOURCC('B','G','R','A'):172case VA_FOURCC('R','G','B','A'):173case VA_FOURCC('B','G','R','X'):174case VA_FOURCC('R','G','B','X'):175img->num_planes = 1;176img->pitches[0] = w * 4;177img->offsets[0] = 0;178img->data_size = w * h * 4;179break;180181default:182return VA_STATUS_ERROR_INVALID_IMAGE_FORMAT;183}184185status = vlVaCreateBuffer(ctx, 0, VAImageBufferType,186align(img->data_size, 16),1871, NULL, &img->buf);188if (status != VA_STATUS_SUCCESS)189return status;190*image = *img;191192return status;193}194195VAStatus196vlVaDeriveImage(VADriverContextP ctx, VASurfaceID surface, VAImage *image)197{198vlVaDriver *drv;199vlVaSurface *surf;200vlVaBuffer *img_buf;201VAImage *img;202VAStatus status;203struct pipe_screen *screen;204struct pipe_surface **surfaces;205struct pipe_video_buffer *new_buffer = NULL;206int w;207int h;208int i;209unsigned stride = 0;210unsigned offset = 0;211212/* This function is used by some programs to test for hardware decoding, but on213* AMD devices, the buffers default to interlaced, which causes this function to fail.214* Some programs expect this function to fail, while others, assume this means215* hardware acceleration is not available and give up without trying the fall-back216* vaCreateImage + vaPutImage217*/218const char *proc = util_get_process_name();219const char *derive_interlaced_allowlist[] = {220"vlc",221"h264encode",222"hevcencode"223};224225if (!ctx)226return VA_STATUS_ERROR_INVALID_CONTEXT;227228drv = VL_VA_DRIVER(ctx);229230if (!drv)231return VA_STATUS_ERROR_INVALID_CONTEXT;232233screen = VL_VA_PSCREEN(ctx);234235if (!screen)236return VA_STATUS_ERROR_INVALID_CONTEXT;237238surf = handle_table_get(drv->htab, surface);239240if (!surf || !surf->buffer)241return VA_STATUS_ERROR_INVALID_SURFACE;242243if (surf->buffer->interlaced) {244for (i = 0; i < ARRAY_SIZE(derive_interlaced_allowlist); i++)245if ((strcmp(derive_interlaced_allowlist[i], proc) == 0))246break;247248if (i >= ARRAY_SIZE(derive_interlaced_allowlist) ||249!screen->get_video_param(screen, PIPE_VIDEO_PROFILE_UNKNOWN,250PIPE_VIDEO_ENTRYPOINT_BITSTREAM,251PIPE_VIDEO_CAP_SUPPORTS_PROGRESSIVE))252return VA_STATUS_ERROR_OPERATION_FAILED;253}254255surfaces = surf->buffer->get_surfaces(surf->buffer);256if (!surfaces || !surfaces[0]->texture)257return VA_STATUS_ERROR_ALLOCATION_FAILED;258259img = CALLOC(1, sizeof(VAImage));260if (!img)261return VA_STATUS_ERROR_ALLOCATION_FAILED;262263img->format.fourcc = PipeFormatToVaFourcc(surf->buffer->buffer_format);264img->buf = VA_INVALID_ID;265/* Use the visible dimensions. */266img->width = surf->templat.width;267img->height = surf->templat.height;268img->num_palette_entries = 0;269img->entry_bytes = 0;270/* Image data size is computed using internal dimensions. */271w = align(surf->buffer->width, 2);272h = align(surf->buffer->height, 2);273274for (i = 0; i < ARRAY_SIZE(formats); ++i) {275if (img->format.fourcc == formats[i].fourcc) {276img->format = formats[i];277break;278}279}280281mtx_lock(&drv->mutex);282if (screen->resource_get_info) {283screen->resource_get_info(screen, surfaces[0]->texture, &stride,284&offset);285if (!stride)286offset = 0;287}288289img->num_planes = 1;290img->offsets[0] = offset;291292switch (img->format.fourcc) {293case VA_FOURCC('U','Y','V','Y'):294case VA_FOURCC('Y','U','Y','V'):295img->pitches[0] = stride > 0 ? stride : w * 2;296assert(img->pitches[0] >= (w * 2));297img->data_size = img->pitches[0] * h;298break;299300case VA_FOURCC('B','G','R','A'):301case VA_FOURCC('R','G','B','A'):302case VA_FOURCC('B','G','R','X'):303case VA_FOURCC('R','G','B','X'):304img->pitches[0] = stride > 0 ? stride : w * 4;305assert(img->pitches[0] >= (w * 4));306img->data_size = img->pitches[0] * h;307break;308309case VA_FOURCC('N','V','1','2'):310case VA_FOURCC('P','0','1','0'):311case VA_FOURCC('P','0','1','6'):312if (surf->buffer->interlaced) {313struct u_rect src_rect, dst_rect;314struct pipe_video_buffer new_template;315316new_template = surf->templat;317new_template.interlaced = false;318new_buffer = drv->pipe->create_video_buffer(drv->pipe, &new_template);319320/* not all devices support non-interlaced buffers */321if (!new_buffer) {322status = VA_STATUS_ERROR_OPERATION_FAILED;323goto exit_on_error;324}325326/* convert the interlaced to the progressive */327src_rect.x0 = dst_rect.x0 = 0;328src_rect.x1 = dst_rect.x1 = surf->templat.width;329src_rect.y0 = dst_rect.y0 = 0;330src_rect.y1 = dst_rect.y1 = surf->templat.height;331332vl_compositor_yuv_deint_full(&drv->cstate, &drv->compositor,333surf->buffer, new_buffer,334&src_rect, &dst_rect,335VL_COMPOSITOR_WEAVE);336337/* recalculate the values now that we have a new surface */338surfaces = surf->buffer->get_surfaces(new_buffer);339if (screen->resource_get_info) {340screen->resource_get_info(screen, surfaces[0]->texture, &stride,341&offset);342if (!stride)343offset = 0;344}345346w = align(new_buffer->width, 2);347h = align(new_buffer->height, 2);348}349350img->num_planes = 2;351img->pitches[0] = stride > 0 ? stride : w;352img->pitches[1] = stride > 0 ? stride : w;353img->offsets[1] = (stride > 0 ? stride : w) * h;354img->data_size = (stride > 0 ? stride : w) * h * 3 / 2;355break;356357default:358/* VaDeriveImage only supports contiguous planes. But there is now a359more generic api vlVaExportSurfaceHandle. */360status = VA_STATUS_ERROR_OPERATION_FAILED;361goto exit_on_error;362}363364img_buf = CALLOC(1, sizeof(vlVaBuffer));365if (!img_buf) {366status = VA_STATUS_ERROR_ALLOCATION_FAILED;367goto exit_on_error;368}369370img->image_id = handle_table_add(drv->htab, img);371372img_buf->type = VAImageBufferType;373img_buf->size = img->data_size;374img_buf->num_elements = 1;375376pipe_resource_reference(&img_buf->derived_surface.resource, surfaces[0]->texture);377img_buf->derived_image_buffer = new_buffer;378379img->buf = handle_table_add(VL_VA_DRIVER(ctx)->htab, img_buf);380mtx_unlock(&drv->mutex);381382*image = *img;383384return VA_STATUS_SUCCESS;385386exit_on_error:387FREE(img);388mtx_unlock(&drv->mutex);389return status;390}391392VAStatus393vlVaDestroyImage(VADriverContextP ctx, VAImageID image)394{395vlVaDriver *drv;396VAImage *vaimage;397VAStatus status;398399if (!ctx)400return VA_STATUS_ERROR_INVALID_CONTEXT;401402drv = VL_VA_DRIVER(ctx);403mtx_lock(&drv->mutex);404vaimage = handle_table_get(drv->htab, image);405if (!vaimage) {406mtx_unlock(&drv->mutex);407return VA_STATUS_ERROR_INVALID_IMAGE;408}409410handle_table_remove(VL_VA_DRIVER(ctx)->htab, image);411mtx_unlock(&drv->mutex);412status = vlVaDestroyBuffer(ctx, vaimage->buf);413FREE(vaimage);414return status;415}416417VAStatus418vlVaSetImagePalette(VADriverContextP ctx, VAImageID image, unsigned char *palette)419{420if (!ctx)421return VA_STATUS_ERROR_INVALID_CONTEXT;422423return VA_STATUS_ERROR_UNIMPLEMENTED;424}425426VAStatus427vlVaGetImage(VADriverContextP ctx, VASurfaceID surface, int x, int y,428unsigned int width, unsigned int height, VAImageID image)429{430vlVaDriver *drv;431vlVaSurface *surf;432vlVaBuffer *img_buf;433VAImage *vaimage;434struct pipe_sampler_view **views;435enum pipe_format format;436bool convert = false;437void *data[3];438unsigned pitches[3], i, j;439440if (!ctx)441return VA_STATUS_ERROR_INVALID_CONTEXT;442443drv = VL_VA_DRIVER(ctx);444445mtx_lock(&drv->mutex);446surf = handle_table_get(drv->htab, surface);447if (!surf || !surf->buffer) {448mtx_unlock(&drv->mutex);449return VA_STATUS_ERROR_INVALID_SURFACE;450}451452vaimage = handle_table_get(drv->htab, image);453if (!vaimage) {454mtx_unlock(&drv->mutex);455return VA_STATUS_ERROR_INVALID_IMAGE;456}457458if (x < 0 || y < 0) {459mtx_unlock(&drv->mutex);460return VA_STATUS_ERROR_INVALID_PARAMETER;461}462463if (x + width > surf->templat.width ||464y + height > surf->templat.height) {465mtx_unlock(&drv->mutex);466return VA_STATUS_ERROR_INVALID_PARAMETER;467}468469if (width > vaimage->width ||470height > vaimage->height) {471mtx_unlock(&drv->mutex);472return VA_STATUS_ERROR_INVALID_PARAMETER;473}474475img_buf = handle_table_get(drv->htab, vaimage->buf);476if (!img_buf) {477mtx_unlock(&drv->mutex);478return VA_STATUS_ERROR_INVALID_BUFFER;479}480481format = VaFourccToPipeFormat(vaimage->format.fourcc);482if (format == PIPE_FORMAT_NONE) {483mtx_unlock(&drv->mutex);484return VA_STATUS_ERROR_OPERATION_FAILED;485}486487488if (format != surf->buffer->buffer_format) {489/* support NV12 to YV12 and IYUV conversion now only */490if ((format == PIPE_FORMAT_YV12 &&491surf->buffer->buffer_format == PIPE_FORMAT_NV12) ||492(format == PIPE_FORMAT_IYUV &&493surf->buffer->buffer_format == PIPE_FORMAT_NV12))494convert = true;495else if (format == PIPE_FORMAT_NV12 &&496(surf->buffer->buffer_format == PIPE_FORMAT_P010 ||497surf->buffer->buffer_format == PIPE_FORMAT_P016)) {498mtx_unlock(&drv->mutex);499return VA_STATUS_ERROR_OPERATION_FAILED;500}501else {502mtx_unlock(&drv->mutex);503return VA_STATUS_ERROR_OPERATION_FAILED;504}505}506507views = surf->buffer->get_sampler_view_planes(surf->buffer);508if (!views) {509mtx_unlock(&drv->mutex);510return VA_STATUS_ERROR_OPERATION_FAILED;511}512513for (i = 0; i < vaimage->num_planes; i++) {514data[i] = img_buf->data + vaimage->offsets[i];515pitches[i] = vaimage->pitches[i];516}517if (vaimage->format.fourcc == VA_FOURCC('I','4','2','0')) {518void *tmp_d;519unsigned tmp_p;520tmp_d = data[1];521data[1] = data[2];522data[2] = tmp_d;523tmp_p = pitches[1];524pitches[1] = pitches[2];525pitches[2] = tmp_p;526}527528for (i = 0; i < vaimage->num_planes; i++) {529unsigned box_w = align(width, 2);530unsigned box_h = align(height, 2);531unsigned box_x = x & ~1;532unsigned box_y = y & ~1;533if (!views[i]) continue;534vl_video_buffer_adjust_size(&box_w, &box_h, i,535pipe_format_to_chroma_format(surf->templat.buffer_format),536surf->templat.interlaced);537vl_video_buffer_adjust_size(&box_x, &box_y, i,538pipe_format_to_chroma_format(surf->templat.buffer_format),539surf->templat.interlaced);540for (j = 0; j < views[i]->texture->array_size; ++j) {541struct pipe_box box = {box_x, box_y, j, box_w, box_h, 1};542struct pipe_transfer *transfer;543uint8_t *map;544map = drv->pipe->texture_map(drv->pipe, views[i]->texture, 0,545PIPE_MAP_READ, &box, &transfer);546if (!map) {547mtx_unlock(&drv->mutex);548return VA_STATUS_ERROR_OPERATION_FAILED;549}550551if (i == 1 && convert) {552u_copy_nv12_to_yv12(data, pitches, i, j,553transfer->stride, views[i]->texture->array_size,554map, box.width, box.height);555} else {556util_copy_rect(data[i] + pitches[i] * j,557views[i]->texture->format,558pitches[i] * views[i]->texture->array_size, 0, 0,559box.width, box.height, map, transfer->stride, 0, 0);560}561pipe_texture_unmap(drv->pipe, transfer);562}563}564mtx_unlock(&drv->mutex);565566return VA_STATUS_SUCCESS;567}568569VAStatus570vlVaPutImage(VADriverContextP ctx, VASurfaceID surface, VAImageID image,571int src_x, int src_y, unsigned int src_width, unsigned int src_height,572int dest_x, int dest_y, unsigned int dest_width, unsigned int dest_height)573{574vlVaDriver *drv;575vlVaSurface *surf;576vlVaBuffer *img_buf;577VAImage *vaimage;578struct pipe_sampler_view **views;579enum pipe_format format;580void *data[3];581unsigned pitches[3], i, j;582583if (!ctx)584return VA_STATUS_ERROR_INVALID_CONTEXT;585586drv = VL_VA_DRIVER(ctx);587mtx_lock(&drv->mutex);588589surf = handle_table_get(drv->htab, surface);590if (!surf || !surf->buffer) {591mtx_unlock(&drv->mutex);592return VA_STATUS_ERROR_INVALID_SURFACE;593}594595vaimage = handle_table_get(drv->htab, image);596if (!vaimage) {597mtx_unlock(&drv->mutex);598return VA_STATUS_ERROR_INVALID_IMAGE;599}600601img_buf = handle_table_get(drv->htab, vaimage->buf);602if (!img_buf) {603mtx_unlock(&drv->mutex);604return VA_STATUS_ERROR_INVALID_BUFFER;605}606607if (img_buf->derived_surface.resource) {608/* Attempting to transfer derived image to surface */609mtx_unlock(&drv->mutex);610return VA_STATUS_ERROR_UNIMPLEMENTED;611}612613format = VaFourccToPipeFormat(vaimage->format.fourcc);614615if (format == PIPE_FORMAT_NONE) {616mtx_unlock(&drv->mutex);617return VA_STATUS_ERROR_OPERATION_FAILED;618}619620if ((format != surf->buffer->buffer_format) &&621((format != PIPE_FORMAT_YV12) || (surf->buffer->buffer_format != PIPE_FORMAT_NV12)) &&622((format != PIPE_FORMAT_IYUV) || (surf->buffer->buffer_format != PIPE_FORMAT_NV12))) {623struct pipe_video_buffer *tmp_buf;624625surf->templat.buffer_format = format;626if (format == PIPE_FORMAT_YUYV || format == PIPE_FORMAT_UYVY ||627format == PIPE_FORMAT_B8G8R8A8_UNORM || format == PIPE_FORMAT_B8G8R8X8_UNORM ||628format == PIPE_FORMAT_R8G8B8A8_UNORM || format == PIPE_FORMAT_R8G8B8X8_UNORM)629surf->templat.interlaced = false;630tmp_buf = drv->pipe->create_video_buffer(drv->pipe, &surf->templat);631632if (!tmp_buf) {633mtx_unlock(&drv->mutex);634return VA_STATUS_ERROR_ALLOCATION_FAILED;635}636637surf->buffer->destroy(surf->buffer);638surf->buffer = tmp_buf;639}640641views = surf->buffer->get_sampler_view_planes(surf->buffer);642if (!views) {643mtx_unlock(&drv->mutex);644return VA_STATUS_ERROR_OPERATION_FAILED;645}646647for (i = 0; i < vaimage->num_planes; i++) {648data[i] = img_buf->data + vaimage->offsets[i];649pitches[i] = vaimage->pitches[i];650}651if (vaimage->format.fourcc == VA_FOURCC('I','4','2','0')) {652void *tmp_d;653unsigned tmp_p;654tmp_d = data[1];655data[1] = data[2];656data[2] = tmp_d;657tmp_p = pitches[1];658pitches[1] = pitches[2];659pitches[2] = tmp_p;660}661662for (i = 0; i < vaimage->num_planes; ++i) {663unsigned width, height;664struct pipe_resource *tex;665666if (!views[i]) continue;667tex = views[i]->texture;668669vlVaVideoSurfaceSize(surf, i, &width, &height);670for (j = 0; j < tex->array_size; ++j) {671struct pipe_box dst_box = {0, 0, j, width, height, 1};672673if (((format == PIPE_FORMAT_YV12) || (format == PIPE_FORMAT_IYUV))674&& (surf->buffer->buffer_format == PIPE_FORMAT_NV12)675&& i == 1) {676struct pipe_transfer *transfer = NULL;677uint8_t *map = NULL;678679map = drv->pipe->texture_map(drv->pipe,680tex,6810,682PIPE_MAP_WRITE |683PIPE_MAP_DISCARD_RANGE,684&dst_box, &transfer);685if (map == NULL) {686mtx_unlock(&drv->mutex);687return VA_STATUS_ERROR_OPERATION_FAILED;688}689690u_copy_nv12_from_yv12((const void * const*) data, pitches, i, j,691transfer->stride, tex->array_size,692map, dst_box.width, dst_box.height);693pipe_texture_unmap(drv->pipe, transfer);694} else {695drv->pipe->texture_subdata(drv->pipe, tex, 0,696PIPE_MAP_WRITE, &dst_box,697data[i] + pitches[i] * j,698pitches[i] * views[i]->texture->array_size, 0);699}700}701}702drv->pipe->flush(drv->pipe, NULL, 0);703mtx_unlock(&drv->mutex);704705return VA_STATUS_SUCCESS;706}707708709