/************************************************************************** * * Copyright 2022 Advanced Micro Devices, Inc. * All Rights Reserved. * * 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 THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 #include #include #include #include #include #include "si_vpe.h" #define SI_VPE_PRETAG "" #define SI_VPE_LOG_LEVEL_DEFAULT 0 #define SI_VPE_LOG_LEVEL_INFO 1 #define SI_VPE_LOG_LEVEL_WARNING 2 #define SI_VPE_LOG_LEVEL_DEBUG 3 #define SIVPE_INFO(dblv, fmt, args...) \ if ((dblv) >= SI_VPE_LOG_LEVEL_INFO) { \ printf("SIVPE INFO: %s: " fmt, __func__, ##args); \ } #define SIVPE_WARN(dblv, fmt, args...) \ if ((dblv) >= SI_VPE_LOG_LEVEL_WARNING) { \ printf("SIVPE WARNING: %s: " fmt, __func__, ##args); \ } #define SIVPE_DBG(dblv, fmt, args...) \ if ((dblv) >= SI_VPE_LOG_LEVEL_DEBUG) { \ printf("SIVPE DBG: %s: " fmt, __func__, ##args); \ } #define SIVPE_ERR(fmt, args...) \ fprintf(stderr, "SIVPE ERROR %s:%d %s " fmt, __FILE__, __LINE__, __func__, ##args) /* Use this enum to help us for accessing the anonymous struct src, dst * in blit_info. */ enum { USE_SRC_SURFACE, USE_DST_SURFACE }; static void * si_vpe_zalloc(void* mem_ctx, size_t size) { /* mem_ctx is optional for now */ return CALLOC(1, size); } static void si_vpe_free(void* mem_ctx, void *ptr) { /* mem_ctx is optional for now */ if (ptr != NULL) { FREE(ptr); ptr = NULL; } } static void si_vpe_log(void* log_ctx, const char* fmt, ...) { /* log_ctx is optional for now */ va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } static void si_vpe_populate_debug_options(struct vpe_debug_options* debug) { /* ref: vpe-utils */ debug->flags.cm_in_bypass = 0; debug->identity_3dlut = 0; debug->sce_3dlut = 0; debug->disable_reuse_bit = 0; } static void si_vpe_populate_callback_modules(struct vpe_callback_funcs* funcs) { funcs->log = si_vpe_log; funcs->zalloc = si_vpe_zalloc; funcs->free = si_vpe_free; return; } static char* si_vpe_get_cositing_str(enum vpe_chroma_cositing cositing) { switch (cositing) { case VPE_CHROMA_COSITING_NONE: return "NONE"; case VPE_CHROMA_COSITING_LEFT: return "LEFT"; case VPE_CHROMA_COSITING_TOPLEFT: return "TOPLEFT"; case VPE_CHROMA_COSITING_COUNT: default: return "ERROR"; } } static char* si_vpe_get_primarie_str(enum vpe_color_primaries primarie) { switch (primarie) { case VPE_PRIMARIES_BT601: return "BT601"; case VPE_PRIMARIES_BT709: return "BT709"; case VPE_PRIMARIES_BT2020: return "BT2020"; case VPE_PRIMARIES_JFIF: return "JFIF"; case VPE_PRIMARIES_COUNT: default: return "ERROR"; } } static char* si_vpe_get_tf_str(enum vpe_transfer_function tf) { switch (tf) { case VPE_TF_G22: return "G22"; case VPE_TF_G24: return "G24"; case VPE_TF_G10: return "G10"; case VPE_TF_PQ: return "PQ"; case VPE_TF_PQ_NORMALIZED: return "PQ_NORMALIZED"; case VPE_TF_HLG: return "HLG"; case VPE_TF_COUNT: default: return "ERROR"; } } /* cycle to the next set of buffers */ static void next_buffer(struct vpe_video_processor *vpeproc) { ++vpeproc->cur_buf; vpeproc->cur_buf %= vpeproc->bufs_num; } static enum vpe_status si_vpe_populate_init_data(struct si_context *sctx, struct vpe_init_data* params, uint8_t log_level) { if (!sctx || !params) { return VPE_STATUS_ERROR; } params->ver_major = sctx->screen->info.ip[AMD_IP_VPE].ver_major; params->ver_minor = sctx->screen->info.ip[AMD_IP_VPE].ver_minor; params->ver_rev = sctx->screen->info.ip[AMD_IP_VPE].ver_rev; si_vpe_populate_debug_options(¶ms->debug); si_vpe_populate_callback_modules(¶ms->funcs); SIVPE_DBG(log_level, "Get family: %d\n", sctx->family); SIVPE_DBG(log_level, "Get gfx_level: %d\n", sctx->gfx_level); SIVPE_DBG(log_level, "Set ver_major: %d\n", params->ver_major); SIVPE_DBG(log_level, "Set ver_minor: %d\n", params->ver_minor); SIVPE_DBG(log_level, "Set ver_rev: %d\n", params->ver_rev); return VPE_STATUS_OK; } static enum vpe_status si_vpe_allocate_buffer(struct vpe_build_bufs **bufs) { if (!bufs) { return VPE_STATUS_ERROR; } *bufs = (struct vpe_build_bufs *)malloc(sizeof(struct vpe_build_bufs)); if (!*bufs) { return VPE_STATUS_NO_MEMORY; } (*bufs)->cmd_buf.cpu_va = 0; (*bufs)->emb_buf.cpu_va = 0; (*bufs)->cmd_buf.size = 0; (*bufs)->emb_buf.size = 0; return VPE_STATUS_OK; } static void si_vpe_free_buffer(struct vpe_build_bufs *bufs) { if (!bufs) { return; } free(bufs); } static enum vpe_surface_pixel_format si_vpe_format(enum pipe_format format) { enum vpe_surface_pixel_format ret; switch (format) { /* VPE input format: */ case PIPE_FORMAT_NV12: ret = VPE_SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb; break; case PIPE_FORMAT_NV21: ret = VPE_SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr; break; case PIPE_FORMAT_P010: ret = VPE_SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb; break; /* VPE output format: */ case PIPE_FORMAT_A8R8G8B8_UNORM: ret = VPE_SURFACE_PIXEL_FORMAT_GRPH_BGRA8888; break; case PIPE_FORMAT_A8B8G8R8_UNORM: ret = VPE_SURFACE_PIXEL_FORMAT_GRPH_RGBA8888; break; case PIPE_FORMAT_R8G8B8A8_UNORM: ret = VPE_SURFACE_PIXEL_FORMAT_GRPH_ABGR8888; break; case PIPE_FORMAT_B8G8R8A8_UNORM: ret = VPE_SURFACE_PIXEL_FORMAT_GRPH_ARGB8888; break; case PIPE_FORMAT_X8R8G8B8_UNORM: ret = VPE_SURFACE_PIXEL_FORMAT_GRPH_BGRX8888; break; case PIPE_FORMAT_X8B8G8R8_UNORM: ret = VPE_SURFACE_PIXEL_FORMAT_GRPH_RGBX8888; break; case PIPE_FORMAT_R8G8B8X8_UNORM: ret = VPE_SURFACE_PIXEL_FORMAT_GRPH_XBGR8888; break; case PIPE_FORMAT_B8G8R8X8_UNORM: ret = VPE_SURFACE_PIXEL_FORMAT_GRPH_XRGB8888; break; case PIPE_FORMAT_A2R10G10B10_UNORM: ret = VPE_SURFACE_PIXEL_FORMAT_GRPH_BGRA1010102; break; case PIPE_FORMAT_A2B10G10R10_UNORM: ret = VPE_SURFACE_PIXEL_FORMAT_GRPH_RGBA1010102; break; case PIPE_FORMAT_B10G10R10A2_UNORM: ret = VPE_SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010; break; case PIPE_FORMAT_R10G10B10A2_UNORM: ret = VPE_SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010; break; default: ret = VPE_SURFACE_PIXEL_FORMAT_INVALID; break; } return ret; } static enum vpe_status si_vpe_set_color_space(const struct pipe_vpp_desc *process_properties, struct vpe_color_space *color_space, enum pipe_format format, int which_surface) { enum pipe_video_vpp_color_standard_type colors_standard; enum pipe_video_vpp_color_range color_range; enum pipe_video_vpp_chroma_siting chroma_siting; if (which_surface == USE_SRC_SURFACE) { colors_standard = process_properties->in_colors_standard; color_range = process_properties->in_color_range; chroma_siting = process_properties->in_chroma_siting; } else { colors_standard = process_properties->out_colors_standard; color_range = process_properties->out_color_range; chroma_siting = process_properties->out_chroma_siting; } switch (colors_standard) { case PIPE_VIDEO_VPP_COLOR_STANDARD_TYPE_BT601: color_space->primaries = VPE_PRIMARIES_BT601; color_space->tf = VPE_TF_G24; break; case PIPE_VIDEO_VPP_COLOR_STANDARD_TYPE_BT709: color_space->primaries = VPE_PRIMARIES_BT709; color_space->tf = VPE_TF_G22; break; case PIPE_VIDEO_VPP_COLOR_STANDARD_TYPE_BT2020: color_space->primaries = VPE_PRIMARIES_BT2020; color_space->tf = VPE_TF_PQ; break; default: color_space->primaries = VPE_PRIMARIES_BT709; color_space->tf = VPE_TF_G22; break; } switch (color_range) { case PIPE_VIDEO_VPP_CHROMA_COLOR_RANGE_REDUCED: color_space->range = VPE_COLOR_RANGE_STUDIO; break; case PIPE_VIDEO_VPP_CHROMA_COLOR_RANGE_FULL: color_space->range = VPE_COLOR_RANGE_FULL; break; default: color_space->range = VPE_COLOR_RANGE_FULL; break; } /* Default use VPE_CHROMA_COSITING_NONE (CENTER | CENTER) */ color_space->cositing = VPE_CHROMA_COSITING_NONE; if (chroma_siting & PIPE_VIDEO_VPP_CHROMA_SITING_VERTICAL_CENTER){ if (chroma_siting & PIPE_VIDEO_VPP_CHROMA_SITING_HORIZONTAL_LEFT) color_space->cositing = VPE_CHROMA_COSITING_LEFT; //else if (chroma_siting & PIPE_VIDEO_VPP_CHROMA_SITING_HORIZONTAL_CENTER) // color_space->cositing = VPE_CHROMA_COSITING_NONE; } else if (chroma_siting & PIPE_VIDEO_VPP_CHROMA_SITING_VERTICAL_TOP) { if (chroma_siting & PIPE_VIDEO_VPP_CHROMA_SITING_HORIZONTAL_LEFT) color_space->cositing = VPE_CHROMA_COSITING_TOPLEFT; //else if (chroma_siting & PIPE_VIDEO_VPP_CHROMA_SITING_HORIZONTAL_CENTER) // color_space->cositing = VPE_CHROMA_COSITING_NONE; } else if (chroma_siting & PIPE_VIDEO_VPP_CHROMA_SITING_VERTICAL_BOTTOM) { if (chroma_siting & PIPE_VIDEO_VPP_CHROMA_SITING_HORIZONTAL_LEFT) color_space->cositing = VPE_CHROMA_COSITING_LEFT; //else if (chroma_siting & PIPE_VIDEO_VPP_CHROMA_SITING_HORIZONTAL_CENTER) // color_space->cositing = VPE_CHROMA_COSITING_NONE; } /* VPE 1.0 Input format only supports NV12 and NV21 now */ switch (format) { case PIPE_FORMAT_NV12: case PIPE_FORMAT_NV21: case PIPE_FORMAT_P010: color_space->encoding = VPE_PIXEL_ENCODING_YCbCr; break; case PIPE_FORMAT_A8R8G8B8_UNORM: case PIPE_FORMAT_A8B8G8R8_UNORM: case PIPE_FORMAT_R8G8B8A8_UNORM: case PIPE_FORMAT_B8G8R8A8_UNORM: case PIPE_FORMAT_X8R8G8B8_UNORM: case PIPE_FORMAT_X8B8G8R8_UNORM: case PIPE_FORMAT_R8G8B8X8_UNORM: case PIPE_FORMAT_B8G8R8X8_UNORM: case PIPE_FORMAT_A2R10G10B10_UNORM: case PIPE_FORMAT_R10G10B10A2_UNORM: case PIPE_FORMAT_A2B10G10R10_UNORM: case PIPE_FORMAT_B10G10R10A2_UNORM: default: color_space->encoding = VPE_PIXEL_ENCODING_RGB; break; } return VPE_STATUS_OK; } /* Combine si_vpe_set_plane_address and si_vpe_set_plane_size*/ static enum vpe_status si_vpe_set_plane_info(struct vpe_video_processor *vpeproc, const struct pipe_vpp_desc *process_properties, struct pipe_surface **surfaces, int which_surface, struct vpe_surface_info *surface_info) { struct vpe_plane_address *plane_address = &surface_info->address; struct vpe_plane_size *plane_size = &surface_info->plane_size; struct si_resource *si_res; uint32_t width, height, pitch, pos_x, pos_y, offset; enum pipe_format format; if (which_surface == USE_SRC_SURFACE) { pos_x = process_properties->src_region.x0; pos_y = process_properties->src_region.y0; width = process_properties->src_region.x1 - pos_x; height = process_properties->src_region.y1 - pos_y; format = process_properties->base.input_format; } else { pos_x = process_properties->dst_region.x0; pos_y = process_properties->dst_region.y0; width = process_properties->dst_region.x1 - pos_x; height = process_properties->dst_region.y1 - pos_y; format = process_properties->base.output_format; } /* Formate Color Space */ surface_info->format = si_vpe_format(format); si_vpe_set_color_space(process_properties, &surface_info->cs, format, which_surface); /* Get surface info, such as buffer alignment and offset */ if (vpeproc->base.context->screen && vpeproc->base.context->screen->resource_get_info) { vpeproc->base.context->screen->resource_get_info(vpeproc->base.context->screen, surfaces[0]->texture, &pitch, &offset); } else { SIVPE_ERR("Get plane pitch and offset info failed\n"); return VPE_STATUS_ERROR; } si_res = si_resource(surfaces[0]->texture); plane_address->tmz_surface = false; /* here is the SURFACE size, not image rect */ plane_size->surface_size.x = 0; plane_size->surface_size.y = 0; plane_size->surface_size.width = surfaces[0]->width; plane_size->surface_size.height = surfaces[0]->height; plane_size->surface_pitch = pitch; // Byte alignment switch (format) { case PIPE_FORMAT_NV12: case PIPE_FORMAT_NV21: plane_address->type = VPE_PLN_ADDR_TYPE_VIDEO_PROGRESSIVE; plane_address->video_progressive.luma_addr.quad_part = si_res->gpu_address + offset; plane_address->video_progressive.luma_meta_addr.quad_part = 0; plane_address->video_progressive.luma_dcc_const_color.quad_part = 0; //plane_size->surface_pitch /= 1; // Byte alignment to Pixel alignment /* Get 2nd plane buffer info */ if (surfaces[1] && vpeproc->base.context->screen && vpeproc->base.context->screen->resource_get_info) { vpeproc->base.context->screen->resource_get_info(vpeproc->base.context->screen, surfaces[1]->texture, &pitch, &offset); } else { SIVPE_ERR("Get 2nd plane pitch and offset info failed\n"); return VPE_STATUS_ERROR; } si_res = si_resource(surfaces[1]->texture); plane_address->video_progressive.chroma_addr.quad_part = si_res->gpu_address + offset; plane_address->video_progressive.chroma_meta_addr.quad_part = 0; plane_address->video_progressive.chroma_dcc_const_color.quad_part = 0; plane_size->chroma_size.x = pos_x; plane_size->chroma_size.y = pos_y; plane_size->chroma_size.width = (width + 1) / 2; // 2 pixel-width alignment plane_size->chroma_size.height = (height + 1) / 2; // 2 pixel-height alignment plane_size->chroma_pitch = pitch / 2; // Byte alignment to Pixel alignment (NV12/NV21 2nd plane is 16 bits per pixel) break; case PIPE_FORMAT_P010: plane_address->type = VPE_PLN_ADDR_TYPE_VIDEO_PROGRESSIVE; plane_address->video_progressive.luma_addr.quad_part = si_res->gpu_address + offset; plane_address->video_progressive.luma_meta_addr.quad_part = 0; plane_address->video_progressive.luma_dcc_const_color.quad_part = 0; plane_size->surface_pitch /= 2; // Byte alignment to Pixel alignment (P010 plane is 16 bits per pixel) if (surfaces[1] && vpeproc->base.context->screen && vpeproc->base.context->screen->resource_get_info) vpeproc->base.context->screen->resource_get_info(vpeproc->base.context->screen, surfaces[1]->texture, &pitch, &offset); else { SIVPE_ERR("Get 2nd plane pitch and offset info failed\n"); return VPE_STATUS_ERROR; } si_res = si_resource(surfaces[1]->texture); plane_address->video_progressive.chroma_addr.quad_part = si_res->gpu_address + offset; plane_address->video_progressive.chroma_meta_addr.quad_part = 0; plane_address->video_progressive.chroma_dcc_const_color.quad_part = 0; plane_size->chroma_size.x = pos_x; plane_size->chroma_size.y = pos_y; plane_size->chroma_size.width = (width + 1) / 2; // 2 pixel-width alignment plane_size->chroma_size.height = (height + 1) / 2; // 2 pixel-height alignment plane_size->chroma_pitch = pitch / 4; // Byte alignment to Pixel alignment (NV12/NV21 2nd plane is 32 bits per pixel) break; case PIPE_FORMAT_A8R8G8B8_UNORM: case PIPE_FORMAT_A8B8G8R8_UNORM: case PIPE_FORMAT_R8G8B8A8_UNORM: case PIPE_FORMAT_B8G8R8A8_UNORM: case PIPE_FORMAT_X8R8G8B8_UNORM: case PIPE_FORMAT_X8B8G8R8_UNORM: case PIPE_FORMAT_R8G8B8X8_UNORM: case PIPE_FORMAT_B8G8R8X8_UNORM: plane_address->type = VPE_PLN_ADDR_TYPE_GRAPHICS; plane_address->grph.addr.quad_part = si_res->gpu_address + offset; plane_address->grph.meta_addr.quad_part = 0; plane_address->grph.dcc_const_color.quad_part = 0; plane_size->surface_pitch /= 4; // Byte alignment to Pixel alignment (RGBA plane is 32 bits per pixel) plane_size->chroma_size.x = 0; plane_size->chroma_size.y = 0; plane_size->chroma_size.width = 0; plane_size->chroma_size.height = 0; plane_size->chroma_pitch = 0; break; case PIPE_FORMAT_A2R10G10B10_UNORM: case PIPE_FORMAT_R10G10B10A2_UNORM: case PIPE_FORMAT_A2B10G10R10_UNORM: case PIPE_FORMAT_B10G10R10A2_UNORM: default: SIVPE_ERR("Un-supported format %d\n", format); } return VPE_STATUS_OK; } static enum vpe_status si_vpe_set_surface_info(struct vpe_video_processor *vpeproc, const struct pipe_vpp_desc *process_properties, struct pipe_surface **surfaces, int which_surface, struct vpe_surface_info *surface_info) { assert(surface_info); /* Set up surface pitch, plane address, color space */ si_vpe_set_plane_info(vpeproc, process_properties, surfaces, which_surface, surface_info); /* VAAPI does not provide swizzle info right now. * Swizzle mode is strongly releated to hardware DMA design, * intel-vaapi-driver/i965_drv_video.c also does not handle swizzle information, * maybe this is the reason why it is not currently supported. * * Just default to linear or none temporarily. */ surface_info->swizzle = VPE_SW_LINEAR; struct vpe_plane_dcc_param *dcc_param = &surface_info->dcc; dcc_param->enable = false; dcc_param->meta_pitch = 0; dcc_param->independent_64b_blks = false; dcc_param->dcc_ind_blk = 0; dcc_param->meta_pitch_c = 0; dcc_param->independent_64b_blks_c = false; dcc_param->dcc_ind_blk_c = 0; return VPE_STATUS_OK; } static enum vpe_status si_vpe_set_stream(const struct pipe_vpp_desc *process_properties, struct vpe_stream *stream) { struct vpe_scaling_info *scaling_info = &stream->scaling_info; scaling_info->src_rect.x = process_properties->src_region.x0; scaling_info->src_rect.y = process_properties->src_region.y0; scaling_info->src_rect.width = process_properties->src_region.x1 - process_properties->src_region.x0; scaling_info->src_rect.height = process_properties->src_region.y1 - process_properties->src_region.y0; scaling_info->dst_rect.x = process_properties->dst_region.x0; scaling_info->dst_rect.y = process_properties->dst_region.y0; scaling_info->dst_rect.width = process_properties->dst_region.x1 - process_properties->dst_region.x0; scaling_info->dst_rect.height = process_properties->dst_region.y1 - process_properties->dst_region.y0; /* Programmable 1 to 8 taps of vertical polyphase filter, and * programmable 1, 2, 4, 6, 8 taps of horizontal polyphase filter. */ scaling_info->taps.v_taps = 4; scaling_info->taps.h_taps = 4; scaling_info->taps.v_taps_c = 2; scaling_info->taps.h_taps_c = 2; /* Blending is not supported for now */ struct vpe_blend_info *blend_info = &stream->blend_info; blend_info->blending = false; blend_info->pre_multiplied_alpha = false; blend_info->global_alpha = false; blend_info->global_alpha_value = 0.0; /* Global Alpha for Background ? */ if (process_properties->blend.mode == PIPE_VIDEO_VPP_BLEND_MODE_GLOBAL_ALPHA) { blend_info->global_alpha = true; blend_info->global_alpha_value = process_properties->blend.global_alpha; } /* TODO: do ProcAmp in next stage */ struct vpe_color_adjust *color_adj = &stream->color_adj; color_adj->brightness = 0.0; color_adj->contrast = 1.0; color_adj->hue = 0.0; color_adj->saturation = 1.0; /* TODO: Tone Mapping */ //struct vpe_tonemap_params *tm_params = &stream->tm_params; stream->horizontal_mirror = false; stream->vertical_mirror = false; switch (process_properties->orientation & 0xF) { case PIPE_VIDEO_VPP_ROTATION_90: stream->rotation = VPE_ROTATION_ANGLE_90; break; case PIPE_VIDEO_VPP_ROTATION_180: stream->rotation = VPE_ROTATION_ANGLE_180; break; case PIPE_VIDEO_VPP_ROTATION_270: stream->rotation = VPE_ROTATION_ANGLE_270; break; default: stream->rotation = VPE_ROTATION_ANGLE_0; break; } if (process_properties->orientation & PIPE_VIDEO_VPP_FLIP_HORIZONTAL) { stream->horizontal_mirror = true; } if (process_properties->orientation & PIPE_VIDEO_VPP_FLIP_VERTICAL) stream->vertical_mirror = true; stream->enable_luma_key = false; stream->lower_luma_bound = 0.5; stream->upper_luma_bound = 0.5; stream->flags.hdr_metadata = 0; stream->flags.reserved = 0; /* TODO: hdr_metadata support in next stage */ stream->hdr_metadata.redX = 1; stream->hdr_metadata.redY = 1; stream->hdr_metadata.greenX = 1; stream->hdr_metadata.greenY = 1; stream->hdr_metadata.blueX = 1; stream->hdr_metadata.blueY = 1; stream->hdr_metadata.whiteX = 1; stream->hdr_metadata.whiteY = 1; stream->hdr_metadata.min_mastering = 1; stream->hdr_metadata.max_mastering = 1; stream->hdr_metadata.max_content = 1; stream->hdr_metadata.avg_content = 1; return VPE_STATUS_OK; } static void si_vpe_processor_destroy(struct pipe_video_codec *codec) { struct vpe_video_processor *vpeproc = (struct vpe_video_processor *)codec; unsigned int i; assert(codec); if (vpeproc->process_fence) { SIVPE_INFO(vpeproc->log_level, "Wait fence\n"); vpeproc->ws->fence_wait(vpeproc->ws, vpeproc->process_fence, PIPE_DEFAULT_DECODER_FEEDBACK_TIMEOUT_NS); } vpeproc->ws->cs_destroy(&vpeproc->cs); if (vpeproc->vpe_build_bufs) si_vpe_free_buffer(vpeproc->vpe_build_bufs); if (vpeproc->vpe_handle) vpe_destroy(&vpeproc->vpe_handle); if (vpeproc->vpe_build_param) { if (vpeproc->vpe_build_param->streams) FREE(vpeproc->vpe_build_param->streams); FREE(vpeproc->vpe_build_param); } if (vpeproc->emb_buffers) { for (i = 0; i < vpeproc->bufs_num; i++) { if (vpeproc->emb_buffers[i].res) { vpeproc->ws->buffer_unmap(vpeproc->ws, vpeproc->emb_buffers[i].res->buf); si_vid_destroy_buffer(&vpeproc->emb_buffers[i]); } } FREE(vpeproc->emb_buffers); } if (vpeproc->mapped_cpu_va) FREE(vpeproc->mapped_cpu_va); vpeproc->bufs_num = 0; SIVPE_DBG(vpeproc->log_level, "Success\n"); FREE(vpeproc); } static void si_vpe_processor_begin_frame(struct pipe_video_codec *codec, struct pipe_video_buffer *target, struct pipe_picture_desc *picture) { struct vpe_video_processor *vpeproc = (struct vpe_video_processor *)codec; struct pipe_surface **dst_surfaces; assert(codec); dst_surfaces = target->get_surfaces(target); if (!dst_surfaces || !dst_surfaces[0]) { SIVPE_ERR("Get target surface failed\n"); return; } vpeproc->dst_surfaces = dst_surfaces; } static void si_vpe_cs_add_surface_buffer(struct vpe_video_processor *vpeproc, struct pipe_surface **surfaces, unsigned usage) { struct si_resource *si_res; int i; for (i = 0; i < VL_MAX_SURFACES; ++i) { if (!surfaces[i]) continue; si_res = si_resource(surfaces[i]->texture); vpeproc->ws->cs_add_buffer(&vpeproc->cs, si_res->buf, usage | RADEON_USAGE_SYNCHRONIZED, 0); } } static void si_vpe_processor_process_frame(struct pipe_video_codec *codec, struct pipe_video_buffer *input_texture, const struct pipe_vpp_desc *process_properties) { enum vpe_status result = VPE_STATUS_OK; struct vpe_video_processor *vpeproc = (struct vpe_video_processor *)codec; struct vpe *vpe_handle = vpeproc->vpe_handle; struct vpe_build_param *build_param = vpeproc->vpe_build_param; struct pipe_surface **src_surfaces; struct vpe_bufs_req bufs_required; struct rvid_buffer *emb_buf; uint64_t *vpe_ptr; assert(codec); assert(process_properties); assert(vpeproc->dst_surfaces); src_surfaces = input_texture->get_surfaces(input_texture); if (!src_surfaces || !src_surfaces[0]) { SIVPE_ERR("Get source surface failed\n"); return; } vpeproc->src_surfaces = src_surfaces; /* Following setting is from si_vpe_set_build_param()*/ /* VPE 1.0 only support one input */ build_param->num_streams = 1; if (build_param->num_streams > VPE_STREAM_MAX_NUM) { SIVPE_ERR("Can only suppport %d stream(s) now\n", VPE_STREAM_MAX_NUM); return; } if (!build_param->streams) { SIVPE_ERR("Streams structure is not allocated\n"); return; } si_vpe_set_surface_info(vpeproc, process_properties, vpeproc->src_surfaces, USE_SRC_SURFACE, &build_param->streams[0].surface_info); si_vpe_set_stream(process_properties, &build_param->streams[0]); si_vpe_set_surface_info(vpeproc, process_properties, vpeproc->dst_surfaces, USE_DST_SURFACE, &build_param->dst_surface); if (process_properties->background_color) { build_param->target_rect.x = 0; build_param->target_rect.y = 0; build_param->target_rect.width = vpeproc->dst_surfaces[0]->width; build_param->target_rect.height = vpeproc->dst_surfaces[0]->height; } else { build_param->target_rect.x = process_properties->dst_region.x0; build_param->target_rect.y = process_properties->dst_region.y0; build_param->target_rect.width = process_properties->dst_region.x1 - process_properties->dst_region.x0; build_param->target_rect.height = process_properties->dst_region.y1 - process_properties->dst_region.y0; } /* TODO: background color is not specified in pipe_vpp_desc structure. * Need to add this filed in pipe_vpp_desc. */ build_param->bg_color.is_ycbcr = false; if (!(process_properties->background_color & 0xFFFFFF) && (build_param->dst_surface.cs.range == VPE_COLOR_RANGE_STUDIO)) { build_param->bg_color.rgba.a = (float)((process_properties->background_color & 0xFF000000) >> 24) / 255.0; build_param->bg_color.rgba.r = 0.0628; build_param->bg_color.rgba.g = 0.0628; build_param->bg_color.rgba.b = 0.0628; } else if (process_properties->background_color) { build_param->bg_color.rgba.a = (float)((process_properties->background_color & 0xFF000000) >> 24) / 255.0; build_param->bg_color.rgba.r = (float)((process_properties->background_color & 0x00FF0000) >> 16) / 255.0; build_param->bg_color.rgba.g = (float)((process_properties->background_color & 0x0000FF00) >> 8) / 255.0; build_param->bg_color.rgba.b = (float)(process_properties->background_color & 0x000000FF) / 255.0; } else { build_param->bg_color.rgba.r = 0; build_param->bg_color.rgba.g = 0; build_param->bg_color.rgba.b = 0; build_param->bg_color.rgba.a = 0; } build_param->alpha_mode = VPE_ALPHA_OPAQUE; build_param->flags.hdr_metadata = 0; build_param->flags.reserved = 1; /* TODO: hdr_metadata support in next stage */ build_param->hdr_metadata.redX = 1; build_param->hdr_metadata.redY = 1; build_param->hdr_metadata.greenX = 1; build_param->hdr_metadata.greenY = 1; build_param->hdr_metadata.blueX = 1; build_param->hdr_metadata.blueY = 1; build_param->hdr_metadata.whiteX = 1; build_param->hdr_metadata.whiteY = 1; build_param->hdr_metadata.min_mastering = 1; build_param->hdr_metadata.max_mastering = 1; build_param->hdr_metadata.max_content = 1; build_param->hdr_metadata.avg_content = 1; /* Setup CmdBuf and EmbBuf address adn size information */ vpe_ptr = (uint64_t *)vpeproc->cs.current.buf; vpeproc->vpe_build_bufs->cmd_buf.cpu_va = (uintptr_t)vpe_ptr; vpeproc->vpe_build_bufs->cmd_buf.gpu_va = 0; vpeproc->vpe_build_bufs->cmd_buf.size = vpeproc->cs.current.max_dw; vpeproc->vpe_build_bufs->cmd_buf.tmz = false; emb_buf = &vpeproc->emb_buffers[vpeproc->cur_buf]; vpeproc->vpe_build_bufs->emb_buf.cpu_va = (uintptr_t)vpeproc->mapped_cpu_va[vpeproc->cur_buf]; vpeproc->vpe_build_bufs->emb_buf.gpu_va = vpeproc->ws->buffer_get_virtual_address(emb_buf->res->buf); vpeproc->vpe_build_bufs->emb_buf.size = VPE_EMBBUF_SIZE; vpeproc->vpe_build_bufs->emb_buf.tmz = false; if (vpeproc->log_level >= SI_VPE_LOG_LEVEL_DEBUG) { SIVPE_DBG(vpeproc->log_level, "src surface format(%d) rect (%d, %d, %d, %d)\n", build_param->streams[0].surface_info.format, build_param->streams[0].surface_info.plane_size.surface_size.x, build_param->streams[0].surface_info.plane_size.surface_size.y, build_param->streams[0].surface_info.plane_size.surface_size.width, build_param->streams[0].surface_info.plane_size.surface_size.height); SIVPE_DBG(vpeproc->log_level, "src surface Cositing(%s), primaries(%s), tf(%s), range(%s)\n", si_vpe_get_cositing_str(build_param->streams[0].surface_info.cs.cositing), si_vpe_get_primarie_str(build_param->streams[0].surface_info.cs.primaries), si_vpe_get_tf_str(build_param->streams[0].surface_info.cs.tf), (build_param->streams[0].surface_info.cs.range == VPE_COLOR_RANGE_FULL)?"FULL":"STUDIO"); SIVPE_DBG(vpeproc->log_level, "dst surface format(%d) rect (%d, %d, %d, %d)\n", build_param->dst_surface.format, build_param->dst_surface.plane_size.surface_size.x, build_param->dst_surface.plane_size.surface_size.y, build_param->dst_surface.plane_size.surface_size.width, build_param->dst_surface.plane_size.surface_size.height); SIVPE_DBG(vpeproc->log_level, "dst surface Cositing(%s), primaries(%s), tf(%s), range(%s)\n", si_vpe_get_cositing_str(build_param->dst_surface.cs.cositing), si_vpe_get_primarie_str(build_param->dst_surface.cs.primaries), si_vpe_get_tf_str(build_param->dst_surface.cs.tf), (build_param->dst_surface.cs.range == VPE_COLOR_RANGE_FULL)?"FULL":"STUDIO"); SIVPE_DBG(vpeproc->log_level, "Source surface pitch(%d), chroma pitch(%d), dst-surface pitch(%d), chroma pitch(%d)\n", build_param->streams[0].surface_info.plane_size.surface_pitch, build_param->streams[0].surface_info.plane_size.chroma_pitch, build_param->dst_surface.plane_size.surface_pitch, build_param->dst_surface.plane_size.chroma_pitch); SIVPE_DBG(vpeproc->log_level, "background color RGBA(%0.3f, %0.3f, %0.3f, %0.3f)\n", build_param->bg_color.rgba.r, build_param->bg_color.rgba.g, build_param->bg_color.rgba.b, build_param->bg_color.rgba.a); SIVPE_DBG(vpeproc->log_level, "target_rect(%d, %d, %d, %d)\n", build_param->target_rect.x, build_param->target_rect.y, build_param->target_rect.width, build_param->target_rect.height); SIVPE_DBG(vpeproc->log_level, "rotation(%d) horizontal_mirror(%d) vertical_mirror(%d)\n", build_param->streams[0].rotation, build_param->streams[0].horizontal_mirror, build_param->streams[0].vertical_mirror); SIVPE_DBG(vpeproc->log_level, "scaling_src_rect(%d, %d, %d, %d)\n", build_param->streams[0].scaling_info.src_rect.x, build_param->streams[0].scaling_info.src_rect.y, build_param->streams[0].scaling_info.src_rect.width, build_param->streams[0].scaling_info.src_rect.height); SIVPE_DBG(vpeproc->log_level, "scaling_dst_rect(%d, %d, %d, %d)\n", build_param->streams[0].scaling_info.dst_rect.x, build_param->streams[0].scaling_info.dst_rect.y, build_param->streams[0].scaling_info.dst_rect.width, build_param->streams[0].scaling_info.dst_rect.height); SIVPE_DBG(vpeproc->log_level, "scaling_taps h_taps(%d) v_taps(%d) h_taps_c(%d) v_taps_c(%d)\n", build_param->streams[0].scaling_info.taps.h_taps, build_param->streams[0].scaling_info.taps.v_taps, build_param->streams[0].scaling_info.taps.h_taps_c, build_param->streams[0].scaling_info.taps.v_taps_c); SIVPE_DBG(vpeproc->log_level, "blend global_alpha(%d): %0.3f\n", build_param->streams[0].blend_info.global_alpha, build_param->streams[0].blend_info.global_alpha_value); SIVPE_DBG(vpeproc->log_level, "ToneMapping shaper_tf(%d) lut_out_tf(%d) lut_in_gamut(%d) lut_out_gamut(%d)\n", build_param->streams[0].tm_params.shaper_tf, build_param->streams[0].tm_params.lut_out_tf, build_param->streams[0].tm_params.lut_in_gamut, build_param->streams[0].tm_params.lut_out_gamut); //SIVPE_DBG(vpeproc->log_level, "ToneMapping update_3dlut(%d) enable_3dlut(%d)\n", // build_param->streams[0].tm_params.update_3dlut, // build_param->streams[0].tm_params.enable_3dlut); } if(vpe_handle->level == VPE_IP_LEVEL_1_1) { build_param->num_instances = 2; build_param->collaboration_mode = true; } else { build_param->num_instances = 1; build_param->collaboration_mode = false; } result = vpe_check_support(vpe_handle, build_param, &bufs_required); if (VPE_STATUS_OK != result) { SIVPE_ERR("Check support failed with result: %d\n", result); goto fail; } result = vpe_build_commands(vpe_handle, build_param, vpeproc->vpe_build_bufs); if (VPE_STATUS_OK != result) { SIVPE_ERR("Build commands failed with result: %d\n", result); goto fail; } /* Check buffer size */ if (vpeproc->vpe_build_bufs->cmd_buf.size == 0 || vpeproc->vpe_build_bufs->cmd_buf.size == vpeproc->cs.current.max_dw) { SIVPE_ERR("Cmdbuf size wrong\n"); goto fail; } if (vpeproc->vpe_build_bufs->emb_buf.size == 0 || vpeproc->vpe_build_bufs->emb_buf.size == VPE_EMBBUF_SIZE) { SIVPE_ERR("Embbuf size wrong\n"); goto fail; } SIVPE_INFO(vpeproc->log_level, "Used buf size: %" PRIu64 ", %" PRIu64 "\n", vpeproc->vpe_build_bufs->cmd_buf.size, vpeproc->vpe_build_bufs->emb_buf.size); /* Have to tell Command Submission context the command length wrote by libvpe */ vpeproc->cs.current.cdw += (vpeproc->vpe_build_bufs->cmd_buf.size / 4); /* Add embbuf into bo_handle list */ vpeproc->ws->cs_add_buffer(&vpeproc->cs, emb_buf->res->buf, RADEON_USAGE_READ | RADEON_USAGE_SYNCHRONIZED, RADEON_DOMAIN_GTT); si_vpe_cs_add_surface_buffer(vpeproc, vpeproc->src_surfaces, RADEON_USAGE_READ); si_vpe_cs_add_surface_buffer(vpeproc, vpeproc->dst_surfaces, RADEON_USAGE_WRITE); SIVPE_DBG(vpeproc->log_level, "Success\n"); return; fail: vpeproc->ws->buffer_unmap(vpeproc->ws, emb_buf->res->buf); SIVPE_ERR("Failed\n"); return; } static int si_vpe_processor_end_frame(struct pipe_video_codec *codec, struct pipe_video_buffer *target, struct pipe_picture_desc *picture) { struct vpe_video_processor *vpeproc = (struct vpe_video_processor *)codec; struct pipe_fence_handle *process_fence = NULL; assert(codec); vpeproc->ws->cs_flush(&vpeproc->cs, picture->flush_flags, &process_fence); next_buffer(vpeproc); if (picture->fence && process_fence) { *picture->fence = process_fence; SIVPE_INFO(vpeproc->log_level, "Assign process fence\n"); } else SIVPE_WARN(vpeproc->log_level, "Fence may have problem!\n"); SIVPE_INFO(vpeproc->log_level, "Success\n"); return 0; } static void si_vpe_processor_flush(struct pipe_video_codec *codec) { //struct vpe_video_processor *vpeproc = (struct vpe_video_processor *)codec; //assert(codec); //SIVPE_DBG(vpeproc->log_level, "Success\n"); return; } static int si_vpe_processor_get_processor_fence(struct pipe_video_codec *codec, struct pipe_fence_handle *fence, uint64_t timeout) { struct vpe_video_processor *vpeproc = (struct vpe_video_processor *)codec; assert(codec); SIVPE_INFO(vpeproc->log_level, "Wait processor fence\n"); if (!vpeproc->ws->fence_wait(vpeproc->ws, fence, timeout)) { SIVPE_DBG(vpeproc->log_level, "Wait processor fence fail\n"); return 0; } SIVPE_INFO(vpeproc->log_level, "Wait processor fence success\n"); return 1; } struct pipe_video_codec* si_vpe_create_processor(struct pipe_context *context, const struct pipe_video_codec *templ) { struct si_context *sctx = (struct si_context *)context; struct radeon_winsys *ws = sctx->ws; struct vpe_video_processor *vpeproc; struct vpe_init_data *init_data; const char *str = getenv("AMDGPU_SIVPE_LOG_LEVEL"); unsigned int i; vpeproc = CALLOC_STRUCT(vpe_video_processor); if (!vpeproc) { SIVPE_ERR("Allocate struct failed\n"); return NULL; } /* get SI_VPE debug log level */ if (str == NULL) vpeproc->log_level = SI_VPE_LOG_LEVEL_DEFAULT; else vpeproc->log_level = atoi(str); vpeproc->base = *templ; vpeproc->base.context = context; vpeproc->base.width = templ->width; vpeproc->base.height = templ->height; vpeproc->base.destroy = si_vpe_processor_destroy; vpeproc->base.begin_frame = si_vpe_processor_begin_frame; vpeproc->base.process_frame = si_vpe_processor_process_frame; vpeproc->base.end_frame = si_vpe_processor_end_frame; vpeproc->base.flush = si_vpe_processor_flush; vpeproc->base.get_processor_fence = si_vpe_processor_get_processor_fence; vpeproc->ver_major = sctx->screen->info.ip[AMD_IP_VPE].ver_major; vpeproc->ver_minor = sctx->screen->info.ip[AMD_IP_VPE].ver_minor; vpeproc->screen = context->screen; vpeproc->ws = ws; vpeproc->process_fence = NULL; init_data = &vpeproc->vpe_data; if (VPE_STATUS_OK != si_vpe_populate_init_data(sctx, init_data, vpeproc->log_level)){ SIVPE_ERR("Init VPE populate data failed\n"); goto fail; } vpeproc->vpe_handle = vpe_create(init_data); if (!vpeproc->vpe_handle) { SIVPE_ERR("Create VPE handle failed\n"); goto fail; } if (VPE_STATUS_OK != si_vpe_allocate_buffer(&vpeproc->vpe_build_bufs)) { SIVPE_ERR("Allocate VPE buffers failed\n"); goto fail; } /* Create Command Submission context. * The cmdbuf (Vpe Descriptor) will be stored in cs.current.buf * there is no needs to allocate another buffer handle for cmdbuf. */ if (!ws->cs_create(&vpeproc->cs, sctx->ctx, AMD_IP_VPE, NULL, NULL)) { SIVPE_ERR("Get command submission context failed.\n"); goto fail; } /* Allocate Vpblit Descriptor buffers * Descriptor buffer is used to store plane config and VPEP commands */ vpeproc->bufs_num = (uint8_t)debug_get_num_option("AMDGPU_SIVPE_BUF_NUM", VPE_BUFFERS_NUM); vpeproc->cur_buf = 0; vpeproc->emb_buffers = (struct rvid_buffer *)CALLOC(vpeproc->bufs_num, sizeof(struct rvid_buffer)); if (!vpeproc->emb_buffers) { SIVPE_ERR("Allocate command buffer list failed\n"); goto fail; } else SIVPE_INFO(vpeproc->log_level, "Number of emb_buf is %d\n", vpeproc->bufs_num); vpeproc->mapped_cpu_va = (void **)CALLOC(vpeproc->bufs_num, sizeof(void *)); if (!vpeproc->mapped_cpu_va) { SIVPE_ERR("Can't allocated mapped_cpu_va for emb_buf buffers.\n"); goto fail; } for (i = 0; i < vpeproc->bufs_num; i++) { if (!si_vid_create_buffer(vpeproc->screen, &vpeproc->emb_buffers[i], VPE_EMBBUF_SIZE, PIPE_USAGE_DEFAULT)) { SIVPE_ERR("Can't allocated emb_buf buffers.\n"); goto fail; } si_vid_clear_buffer(context, &vpeproc->emb_buffers[i]); vpeproc->mapped_cpu_va[i] = vpeproc->ws->buffer_map(vpeproc->ws, vpeproc->emb_buffers[i].res->buf, &vpeproc->cs, PIPE_MAP_WRITE); if (!vpeproc->mapped_cpu_va[i]) goto fail; } /* Create VPE parameters structure */ vpeproc->vpe_build_param = CALLOC_STRUCT(vpe_build_param); if (!vpeproc->vpe_build_param) { SIVPE_ERR("Allocate build-paramaters sturcture failed\n"); goto fail; } /* Pre-allocate the streams */ vpeproc->vpe_build_param->streams = (struct vpe_stream *)CALLOC(VPE_STREAM_MAX_NUM, sizeof(struct vpe_stream)); if (!vpeproc->vpe_build_param->streams) { SIVPE_ERR("Allocate streams sturcture failed\n"); goto fail; } return &vpeproc->base; fail: SIVPE_ERR("Failed\n"); if (vpeproc) { si_vpe_processor_destroy(&vpeproc->base); } return NULL; }