/* * 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 */ #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 #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); }