/*
* Copyright (c) 2011 Intel Corporation. All Rights Reserved.
* Copyright (c) Imagination Technologies Limited, UK
*
* 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, sub license, 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 NON-INFRINGEMENT.
* IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS 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.
*/
/*
* Authors:
* Li Zeng <li.zeng@intel.com>
*/
#include "tng_vld_dec.h"
#include "psb_drv_debug.h"
#include "hwdefs/dxva_fw_ctrl.h"
#include "hwdefs/reg_io2.h"
#include "hwdefs/msvdx_offsets.h"
#include "hwdefs/msvdx_cmds_io2.h"
#include "va/va_dec_jpeg.h"
#include "va/va_dec_vp8.h"
#include <malloc.h>
#define GET_SURFACE_INFO_colocated_index(psb_surface) ((int) (psb_surface->extra_info[3]))
#define SET_SURFACE_INFO_colocated_index(psb_surface, val) psb_surface->extra_info[3] = (uint32_t) val;
/* Set MSVDX Front end register */
void vld_dec_FE_state(object_context_p obj_context, psb_buffer_p buf)
{
psb_cmdbuf_p cmdbuf = obj_context->cmdbuf;
context_DEC_p ctx = (context_DEC_p) obj_context->format_data;
CTRL_ALLOC_HEADER *cmd_header = (CTRL_ALLOC_HEADER *)psb_cmdbuf_alloc_space(cmdbuf, sizeof(CTRL_ALLOC_HEADER));
cmd_header->ui32Cmd_AdditionalParams = CMD_CTRL_ALLOC_HEADER;
cmd_header->ui32ExternStateBuffAddr = 0;
if (buf)
RELOC(cmd_header->ui32ExternStateBuffAddr, 0, buf);
cmd_header->ui32MacroblockParamAddr = 0; /* Only EC needs to set this */
ctx->cmd_params = &cmd_header->ui32Cmd_AdditionalParams;
ctx->p_slice_params = &cmd_header->ui32SliceParams;
cmd_header->ui32SliceParams = 0;
ctx->slice_first_pic_last = &cmd_header->uiSliceFirstMbYX_uiPicLastMbYX;
*ctx->slice_first_pic_last = 0;
ctx->p_range_mapping_base0 = &cmd_header->ui32AltOutputAddr[0];
ctx->p_range_mapping_base1 = &cmd_header->ui32AltOutputAddr[1];
ctx->alt_output_flags = &cmd_header->ui32AltOutputFlags;
cmd_header->ui32AltOutputFlags = 0;
cmd_header->ui32AltOutputAddr[0] = 0;
cmd_header->ui32AltOutputAddr[1] = 0;
}
/* Programme the Alt output if there is a rotation*/
void vld_dec_setup_alternative_frame(object_context_p obj_context)
{
uint32_t cmd = 0;
psb_cmdbuf_p cmdbuf = obj_context->cmdbuf;
context_DEC_p ctx = (context_DEC_p) obj_context->format_data;
psb_surface_p src_surface = obj_context->current_render_target->psb_surface;
psb_surface_p out_loop_surface = obj_context->current_render_target->out_loop_surface;
int ved_scaling = (CONTEXT_SCALING(obj_context) && !ctx->yuv_ctx);
uint32_t startX = 0, startY = 0, luma_addr_offset = 0, chroma_addr_offset = 0;
/* In VPP ctx, current_render_target is rotated surface */
if (ctx->yuv_ctx && (VAEntrypointVideoProc == obj_context->entry_point)) {
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Setup second-pass rotation\n");
out_loop_surface = src_surface;
src_surface = ctx->yuv_ctx->src_surface;
}
if (CONTEXT_ALTERNATIVE_OUTPUT(obj_context) || obj_context->entry_point == VAEntrypointVideoProc) {
if (ved_scaling) {
out_loop_surface = obj_context->current_render_target->scaling_surface;
#ifndef BAYTRAIL
tng_ved_write_scale_reg(obj_context);
REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS,ALTERNATIVE_OUTPUT_PICTURE_ROTATION, SCALE_INPUT_SIZE_SEL, 1);
REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS,ALTERNATIVE_OUTPUT_PICTURE_ROTATION, SCALE_ENABLE, 1);
#endif
} else {
startX = ((uint32_t)obj_context->current_render_target->offset_x_s + 0x3f) & ~0x3f;
startY = ((uint32_t)obj_context->current_render_target->offset_y_s + 0x1) & ~0x1;
luma_addr_offset = (((uint32_t)(startX + out_loop_surface->stride * startY)) + 0x3f ) & ~0x3f;
chroma_addr_offset = (((uint32_t)(startX + out_loop_surface->stride * startY / 2)) + 0x3f ) & ~0x3f;
}
if (out_loop_surface == NULL) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "out-loop surface is NULL, abort msvdx alternative output\n");
return;
}
if (GET_SURFACE_INFO_rotate(out_loop_surface) != obj_context->msvdx_rotate && !ved_scaling)
drv_debug_msg(VIDEO_DEBUG_WARNING, "Display rotate mode does not match surface rotate mode!\n");
/* CRendecBlock RendecBlk( mCtrlAlloc , RENDEC_REGISTER_OFFSET(MSVDX_CMDS, VC1_LUMA_RANGE_MAPPING_BASE_ADDRESS) ); */
psb_cmdbuf_rendec_start(cmdbuf, RENDEC_REGISTER_OFFSET(MSVDX_CMDS, VC1_LUMA_RANGE_MAPPING_BASE_ADDRESS));
psb_cmdbuf_rendec_write_address(cmdbuf, &out_loop_surface->buf, out_loop_surface->buf.buffer_ofs + luma_addr_offset);
psb_cmdbuf_rendec_write_address(cmdbuf, &out_loop_surface->buf, out_loop_surface->buf.buffer_ofs + chroma_addr_offset + out_loop_surface->chroma_offset);
psb_cmdbuf_rendec_end(cmdbuf);
REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION , ALT_PICTURE_ENABLE, 1);
REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION , ROTATION_ROW_STRIDE, out_loop_surface->stride_mode);
REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION , RECON_WRITE_DISABLE, 0); /* FIXME Always generate Rec */
REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION , ROTATION_MODE, GET_SURFACE_INFO_rotate(out_loop_surface));
RELOC(*ctx->p_range_mapping_base0, out_loop_surface->buf.buffer_ofs + luma_addr_offset, &out_loop_surface->buf);
RELOC(*ctx->p_range_mapping_base1, out_loop_surface->buf.buffer_ofs + chroma_addr_offset + out_loop_surface->chroma_offset, &out_loop_surface->buf);
}
if (obj_context->profile == VAProfileVP8Version0_3 ||
obj_context->profile == VAProfileJPEGBaseline || ctx->yuv_ctx) {
psb_cmdbuf_rendec_start(cmdbuf, (REG_MSVDX_CMD_OFFSET + MSVDX_CMDS_AUX_LINE_BUFFER_BASE_ADDRESS_OFFSET));
psb_cmdbuf_rendec_write_address(cmdbuf, &ctx->aux_line_buffer_vld, ctx->aux_line_buffer_vld.buffer_ofs);
psb_cmdbuf_rendec_end(cmdbuf);
REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION, USE_AUX_LINE_BUF, 1);
if (ctx->yuv_ctx)
REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION , RECON_WRITE_DISABLE, 1);
}
/* Set the rotation registers */
psb_cmdbuf_rendec_start(cmdbuf, RENDEC_REGISTER_OFFSET(MSVDX_CMDS, ALTERNATIVE_OUTPUT_PICTURE_ROTATION));
psb_cmdbuf_rendec_write(cmdbuf, cmd);
*ctx->alt_output_flags = cmd;
cmd = 0;
REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS, EXTENDED_ROW_STRIDE, EXT_ROW_STRIDE, src_surface->stride / 64);
psb_cmdbuf_rendec_write(cmdbuf, cmd);
psb_cmdbuf_rendec_end(cmdbuf);
}
int vld_dec_slice_parameter_size(object_context_p obj_context)
{
int size;
switch (obj_context->profile) {
case VAProfileMPEG2Simple:
case VAProfileMPEG2Main:
size = sizeof(VASliceParameterBufferMPEG2);
break;
case VAProfileMPEG4Simple:
case VAProfileMPEG4AdvancedSimple:
case VAProfileMPEG4Main:
case VAProfileH263Baseline:
size = sizeof(VASliceParameterBufferMPEG4);
break;
case VAProfileH264Baseline:
case VAProfileH264Main:
case VAProfileH264High:
case VAProfileH264ConstrainedBaseline:
size = sizeof(VASliceParameterBufferH264);
break;
case VAProfileVC1Simple:
case VAProfileVC1Main:
case VAProfileVC1Advanced:
size = sizeof(VASliceParameterBufferVC1);
break;
case VAProfileVP8Version0_3:
size = sizeof(VASliceParameterBufferVP8);
case VAProfileJPEGBaseline:
size = sizeof(VASliceParameterBufferJPEGBaseline);
default:
size = 0;
break;
}
return size;
}
VAStatus vld_dec_process_slice_data(context_DEC_p ctx, object_buffer_p obj_buffer)
{
VAStatus vaStatus = VA_STATUS_SUCCESS;
void *slice_param;
int buffer_idx = 0;
unsigned int element_idx = 0, element_size;
ASSERT((obj_buffer->type == VASliceDataBufferType) || (obj_buffer->type == VAProtectedSliceDataBufferType));
ASSERT(ctx->pic_params);
ASSERT(ctx->slice_param_list_idx);
#if 0
if (!ctx->pic_params) {
/* Picture params missing */
return VA_STATUS_ERROR_UNKNOWN;
}
#endif
if ((NULL == obj_buffer->psb_buffer) ||
(0 == obj_buffer->size)) {
/* We need to have data in the bitstream buffer */
return VA_STATUS_ERROR_UNKNOWN;
}
element_size = vld_dec_slice_parameter_size(ctx->obj_context);
while (buffer_idx < ctx->slice_param_list_idx) {
object_buffer_p slice_buf = ctx->slice_param_list[buffer_idx];
if (element_idx >= slice_buf->num_elements) {
/* Move to next buffer */
element_idx = 0;
buffer_idx++;
continue;
}
slice_param = slice_buf->buffer_data;
slice_param = (void *)((unsigned long)slice_param + element_idx * element_size);
element_idx++;
vaStatus = vld_dec_process_slice(ctx, slice_param, obj_buffer);
if (vaStatus != VA_STATUS_SUCCESS) {
DEBUG_FAILURE;
break;
}
}
ctx->slice_param_list_idx = 0;
return vaStatus;
}
/*
* Adds a VASliceParameterBuffer to the list of slice params
*/
VAStatus vld_dec_add_slice_param(context_DEC_p ctx, object_buffer_p obj_buffer)
{
ASSERT(obj_buffer->type == VASliceParameterBufferType);
if (ctx->slice_param_list_idx >= ctx->slice_param_list_size) {
unsigned char *new_list;
ctx->slice_param_list_size += 8;
new_list = realloc(ctx->slice_param_list,
sizeof(object_buffer_p) * ctx->slice_param_list_size);
if (NULL == new_list) {
return VA_STATUS_ERROR_ALLOCATION_FAILED;
}
ctx->slice_param_list = (object_buffer_p*) new_list;
}
ctx->slice_param_list[ctx->slice_param_list_idx] = obj_buffer;
ctx->slice_param_list_idx++;
return VA_STATUS_SUCCESS;
}
void vld_dec_write_kick(object_context_p obj_context)
{
psb_cmdbuf_p cmdbuf = obj_context->cmdbuf;
*cmdbuf->cmd_idx++ = CMD_COMPLETION;
}
VAStatus vld_dec_process_slice(context_DEC_p ctx,
void *vld_slice_param,
object_buffer_p obj_buffer)
{
VAStatus vaStatus = VA_STATUS_SUCCESS;
VASliceParameterBufferBase *slice_param = (VASliceParameterBufferBase *) vld_slice_param;
ASSERT((obj_buffer->type == VASliceDataBufferType) || (obj_buffer->type == VAProtectedSliceDataBufferType));
if ((slice_param->slice_data_flag == VA_SLICE_DATA_FLAG_BEGIN) ||
(slice_param->slice_data_flag == VA_SLICE_DATA_FLAG_ALL)) {
#ifndef SLICE_HEADER_PARSING
if (0 == slice_param->slice_data_size) {
vaStatus = VA_STATUS_ERROR_UNKNOWN;
DEBUG_FAILURE;
return vaStatus;
}
#endif
ASSERT(!ctx->split_buffer_pending);
if (psb_context_get_next_cmdbuf(ctx->obj_context)) {
vaStatus = VA_STATUS_ERROR_UNKNOWN;
DEBUG_FAILURE;
return vaStatus;
}
vld_dec_FE_state(ctx->obj_context, ctx->preload_buffer);
ctx->begin_slice(ctx, slice_param);
ctx->slice_data_buffer = obj_buffer->psb_buffer;
#ifdef SLICE_HEADER_PARSING
if (ctx->parse_enabled == 1)
psb_cmdbuf_dma_write_key(ctx->obj_context->cmdbuf,
ctx->SR_flags,
ctx->parse_key);
else
#endif
psb_cmdbuf_dma_write_bitstream(ctx->obj_context->cmdbuf,
obj_buffer->psb_buffer,
obj_buffer->psb_buffer->buffer_ofs + slice_param->slice_data_offset,
slice_param->slice_data_size,
ctx->bits_offset,
ctx->SR_flags);
if (slice_param->slice_data_flag == VA_SLICE_DATA_FLAG_BEGIN) {
ctx->split_buffer_pending = TRUE;
}
} else {
ASSERT(ctx->split_buffer_pending);
ASSERT(0 == slice_param->slice_data_offset);
if (slice_param->slice_data_size) {
psb_cmdbuf_dma_write_bitstream_chained(ctx->obj_context->cmdbuf,
obj_buffer->psb_buffer,
slice_param->slice_data_size);
}
}
if ((slice_param->slice_data_flag == VA_SLICE_DATA_FLAG_ALL) ||
(slice_param->slice_data_flag == VA_SLICE_DATA_FLAG_END)) {
if (slice_param->slice_data_flag == VA_SLICE_DATA_FLAG_END) {
ASSERT(ctx->split_buffer_pending);
}
ctx->process_slice(ctx, slice_param);
vld_dec_write_kick(ctx->obj_context);
ctx->split_buffer_pending = FALSE;
ctx->obj_context->video_op = psb_video_vld;
ctx->obj_context->flags = 0;
ctx->end_slice(ctx);
if (psb_context_submit_cmdbuf(ctx->obj_context)) {
vaStatus = VA_STATUS_ERROR_UNKNOWN;
}
}
return vaStatus;
}
VAStatus vld_dec_allocate_colocated_buffer(context_DEC_p ctx, object_surface_p obj_surface, uint32_t size)
{
psb_buffer_p buf;
VAStatus vaStatus;
psb_surface_p surface = obj_surface->psb_surface;
int index = GET_SURFACE_INFO_colocated_index(surface);
if (!index) {
index = ctx->colocated_buffers_idx;
if (index >= ctx->colocated_buffers_size) {
return VA_STATUS_ERROR_UNKNOWN;
}
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Allocating colocated buffer for surface %08x size = %08x\n", surface, size);
buf = &(ctx->colocated_buffers[index]);
vaStatus = psb_buffer_create(ctx->obj_context->driver_data, size, psb_bt_vpu_only, buf);
if (VA_STATUS_SUCCESS != vaStatus) {
return vaStatus;
}
ctx->colocated_buffers_idx++;
SET_SURFACE_INFO_colocated_index(surface, index + 1); /* 0 means unset, index is offset by 1 */
} else {
buf = &(ctx->colocated_buffers[index - 1]);
if (buf->size < size) {
psb_buffer_destroy(buf);
vaStatus = psb_buffer_create(ctx->obj_context->driver_data, size, psb_bt_vpu_only, buf);
if (VA_STATUS_SUCCESS != vaStatus) {
return vaStatus;
}
SET_SURFACE_INFO_colocated_index(surface, index); /* replace the original buffer */
}
}
return VA_STATUS_SUCCESS;
}
psb_buffer_p vld_dec_lookup_colocated_buffer(context_DEC_p ctx, psb_surface_p surface)
{
int index = GET_SURFACE_INFO_colocated_index(surface);
if (!index) {
return NULL;
}
return &(ctx->colocated_buffers[index-1]); /* 0 means unset, index is offset by 1 */
}
VAStatus vld_dec_CreateContext(context_DEC_p ctx, object_context_p obj_context)
{
VAStatus vaStatus = VA_STATUS_SUCCESS;
ctx->obj_context = obj_context;
ctx->split_buffer_pending = FALSE;
ctx->slice_param_list_size = 8;
ctx->slice_param_list = (object_buffer_p*) calloc(1, sizeof(object_buffer_p) * ctx->slice_param_list_size);
ctx->slice_param_list_idx = 0;
if (NULL == ctx->slice_param_list) {
vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
DEBUG_FAILURE;
return vaStatus;
}
ctx->colocated_buffers_size = obj_context->num_render_targets;
ctx->colocated_buffers_idx = 0;
ctx->colocated_buffers = (psb_buffer_p) calloc(1, sizeof(struct psb_buffer_s) * ctx->colocated_buffers_size);
if (NULL == ctx->colocated_buffers) {
vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
DEBUG_FAILURE;
free(ctx->slice_param_list);
}
if (vaStatus == VA_STATUS_SUCCESS) {
vaStatus = psb_buffer_create(obj_context->driver_data,
AUX_LINE_BUFFER_VLD_SIZE,
psb_bt_cpu_vpu,
&ctx->aux_line_buffer_vld);
DEBUG_FAILURE;
}
return vaStatus;
}
void vld_dec_DestroyContext(context_DEC_p ctx)
{
int i;
ctx->preload_buffer = NULL;
psb_buffer_destroy(&ctx->aux_line_buffer_vld);
if (ctx->slice_param_list) {
free(ctx->slice_param_list);
ctx->slice_param_list = NULL;
}
if (ctx->colocated_buffers) {
for (i = 0; i < ctx->colocated_buffers_idx; ++i)
psb_buffer_destroy(&(ctx->colocated_buffers[i]));
free(ctx->colocated_buffers);
ctx->colocated_buffers = NULL;
}
}
VAStatus vld_dec_RenderPicture(
object_context_p obj_context,
object_buffer_p *buffers,
int num_buffers)
{
int i;
context_DEC_p ctx = (context_DEC_p) obj_context->format_data;
VAStatus vaStatus = VA_STATUS_SUCCESS;
for (i = 0; i < num_buffers; i++) {
object_buffer_p obj_buffer = buffers[i];
psb__dump_va_buffers_verbose(obj_buffer);
switch (obj_buffer->type) {
case VASliceParameterBufferType:
vaStatus = vld_dec_add_slice_param(ctx, obj_buffer);
DEBUG_FAILURE;
break;
case VASliceDataBufferType:
case VAProtectedSliceDataBufferType:
vaStatus = vld_dec_process_slice_data(ctx, obj_buffer);
DEBUG_FAILURE;
break;
default:
vaStatus = ctx->process_buffer(ctx, obj_buffer);
DEBUG_FAILURE;
}
if (vaStatus != VA_STATUS_SUCCESS) {
break;
}
}
return vaStatus;
}
void vld_dec_yuv_rotate(object_context_p obj_context)
{
VAStatus vaStatus = VA_STATUS_SUCCESS;
struct format_vtable_s *vtable = &tng_yuv_processor_vtable;
struct surface_param_s surface_param;
struct object_buffer_s buffer;
object_buffer_p buffer_p = &buffer;
surface_param.src_surface = obj_context->current_render_target->scaling_surface;
surface_param.display_width = obj_context->current_render_target->buffer_width_s;
surface_param.display_height = obj_context->current_render_target->buffer_height_s;
surface_param.coded_width = obj_context->current_render_target->width_s;
surface_param.coded_height = obj_context->current_render_target->height_s;
buffer.num_elements = 1;
buffer.type = YUVProcessorSurfaceType;
buffer.size = sizeof(struct surface_param_s);
buffer.buffer_data = (unsigned char *)&surface_param;
vtable->createContext(obj_context, NULL);
vtable->beginPicture(obj_context);
vtable->renderPicture(obj_context, &buffer_p, 1);
vtable->endPicture(obj_context);
vtable->destroyContext(obj_context);
}