/* SPDX-License-Identifier: Apache-2.0 OR MIT */ /* * Copyright (c) 2022 Rockchip Electronics Co., Ltd. */ #define MODULE_TAG "mpp_dec_nt" #include #include "mpp_buffer_impl.h" #include "mpp_dec_debug.h" #include "mpp_dec_vproc.h" #include "mpp_dec_no_thread.h" #include "rk_hdr_meta_com.h" MPP_RET mpp_dec_decode(MppDec ctx, MppPacket packet) { MppDecImpl *dec = (MppDecImpl *)ctx; Mpp *mpp = (Mpp *)dec->mpp; DecTask *task = (DecTask *)dec->task_single; DecTaskStatus *status = &task->status; MppBufSlots frame_slots = dec->frame_slots; MppBufSlots packet_slots = dec->packet_slots; HalDecTask *task_dec = &task->info.dec; MppMutexCond *cmd_lock = &dec->cmd_lock; MppPacket input = dec->mpp_pkt_in; size_t stream_size = 0; RK_S32 output = 0; RK_U32 i; mpp_mutex_cond_lock(cmd_lock); /* * 1. task no ready and last packet is done try process new input packet */ if (input == NULL && !status->curr_task_rdy) { input = packet; if (input == NULL) { mpp_mutex_cond_unlock(cmd_lock); return MPP_OK; } } if (input) dec_dbg_detail("detail: %p input pkt %p len %d task ready %d\n", dec, input, mpp_packet_get_length(input), status->curr_task_rdy); else dec_dbg_detail("detail: %p input pkt NULL task ready %d\n", dec, input, status->curr_task_rdy); /* * 2. prepare stream to generate task */ if (!status->curr_task_rdy) { mpp_parser_prepare(dec->parser, input, task_dec); dec_dbg_detail("detail: %p prepare pkt %p get task %d\n", dec, input, task_dec->valid); /* record remaining packet */ if (task_dec->valid) { status->curr_task_rdy = 1; dec->mpp_pkt_in = input; } if (input && !mpp_packet_get_length(input)) dec->mpp_pkt_in = NULL; } /* * 3. when task not ready check eos empty task */ if (!status->curr_task_rdy) { if (task_dec->flags.eos) { mpp_dec_flush(dec); output += mpp_dec_push_display(mpp, task_dec->flags); /* * Use -1 as invalid buffer slot index. * Reason: the last task maybe is a empty task with eos flag * only but this task may go through vproc process also. We need * create a buffer slot index for it. */ mpp_dec_put_frame(mpp, -1, task_dec->flags); output++; } mpp_mutex_cond_unlock(cmd_lock); return (MPP_RET)output; } // NOTE: packet in task should be ready now mpp_assert(task_dec->input_packet); dec_dbg_detail("detail: %p hw pkt %p process start\n", dec, task_dec->input_packet); /* * 4. look for a unused packet slot index * 5. malloc hardware buffer for the packet slot index */ dec_dbg_detail("detail: %p hal_pkt_buf_in %p\n", dec, task->hal_pkt_buf_in); if (!task->hal_pkt_buf_in) { MppBuffer hal_buf_in = mpp_packet_get_buffer(task_dec->input_packet); RK_S32 slot_pkt = -1; mpp_buf_slot_get_unused(packet_slots, &slot_pkt); mpp_assert(slot_pkt >= 0); stream_size = mpp_packet_get_size(task_dec->input_packet); if (NULL == hal_buf_in) { mpp_buf_slot_get_prop(packet_slots, slot_pkt, SLOT_BUFFER, &hal_buf_in); } else { /* use external buffer and set to slot */ task_dec->input_no_copy = 1; mpp_buf_slot_set_prop(packet_slots, slot_pkt, SLOT_BUFFER, hal_buf_in); mpp_buffer_attach_dev(hal_buf_in, dec->dev); } if (NULL == hal_buf_in) { mpp_buffer_get(mpp->mPacketGroup, &hal_buf_in, stream_size); if (hal_buf_in) { mpp_buf_slot_set_prop(packet_slots, slot_pkt, SLOT_BUFFER, hal_buf_in); mpp_buffer_attach_dev(hal_buf_in, dec->dev); mpp_buffer_put(hal_buf_in); } } else { MppBufferImpl *buf = (MppBufferImpl *)hal_buf_in; mpp_assert(buf->info.size >= stream_size); } dec_dbg_detail("detail: %p hw pkt %p get buf %p slot %d\n", dec, task_dec->input_packet, hal_buf_in, slot_pkt); task_dec->input = slot_pkt; task->hal_pkt_buf_in = hal_buf_in; } /* * 6. copy data to hardware buffer */ if (!status->dec_pkt_copy_rdy) { if (!task_dec->input_no_copy) { void *src = mpp_packet_get_data(task_dec->input_packet); size_t length = mpp_packet_get_length(task_dec->input_packet); mpp_assert(task->hal_pkt_buf_in); mpp_assert(task_dec->input_packet); dec_dbg_detail("detail: %p copy to hw length %d\n", dec, length); mpp_buffer_write(task->hal_pkt_buf_in, 0, src, length); mpp_buffer_sync_partial_end(task->hal_pkt_buf_in, 0, length); } mpp_buf_slot_set_flag(packet_slots, task_dec->input, SLOT_CODEC_READY); mpp_buf_slot_set_flag(packet_slots, task_dec->input, SLOT_HAL_INPUT); status->dec_pkt_copy_rdy = 1; } /* * 7. parse the stream in input buffer and generate task */ if (!status->task_parsed_rdy) { mpp_clock_start(dec->clocks[DEC_PRS_PARSE]); mpp_parser_parse(dec->parser, task_dec); mpp_clock_pause(dec->clocks[DEC_PRS_PARSE]); status->task_parsed_rdy = 1; /* add extra output slot operaton for jpeg decoding */ if (task_dec->input_no_copy && task_dec->output >= 0) { mpp_buf_slot_set_flag(frame_slots, task_dec->output, SLOT_QUEUE_USE); mpp_buf_slot_enqueue(frame_slots, task_dec->output, QUEUE_DISPLAY); } } dec_dbg_detail("detail: %p parse output slot %d valid %d\n", dec, task_dec->output, task_dec->valid); /* * 8. check task generate failure */ if (task_dec->output < 0 || !task_dec->valid) { /* * We may meet an eos in parser step and there will be no anymore vaild * task generated. So here we try push eos task to hal, hal will push * all frame(s) to display, a frame of them with a eos flag will be * used to inform that all frame have decoded */ if (task_dec->flags.eos) { mpp_dec_flush(dec); output += mpp_dec_push_display(mpp, task_dec->flags); } if (status->dec_pkt_copy_rdy) { mpp_buf_slot_clr_flag(packet_slots, task_dec->input, SLOT_HAL_INPUT); status->dec_pkt_copy_rdy = 0; } status->curr_task_rdy = 0; status->task_parsed_rdy = 0; dec_task_info_init(&task->info); task->hal_pkt_buf_in = NULL; dec_dbg_detail("detail: %p parse return no task with output %d\n", dec, output); mpp_mutex_cond_unlock(cmd_lock); return (MPP_RET)output; } dec_dbg_detail("detail: %p check output index pass\n", dec); /* * 9. parse local task and slot to check whether new buffer or info change is needed. * * detect info change from frame slot */ if (mpp_buf_slot_is_changed(frame_slots)) { if (!status->info_task_gen_rdy) { RK_U32 eos = task_dec->flags.eos; // NOTE: info change should not go with eos flag task_dec->flags.info_change = 1; task_dec->flags.eos = 0; mpp_dec_flush(dec); output += mpp_dec_push_display(mpp, task_dec->flags); mpp_dec_put_frame(mpp, task_dec->output, task_dec->flags); output++; task_dec->flags.eos = eos; status->info_task_gen_rdy = 1; dec_dbg_detail("detail: %p info change found return frame %d\n", dec, output); mpp_mutex_cond_unlock(cmd_lock); return (MPP_RET)output; } dec->info_updated = 0; } task->wait.info_change = mpp_buf_slot_is_changed(frame_slots); if (task->wait.info_change) { mpp_mutex_cond_unlock(cmd_lock); return MPP_OK; } else { status->info_task_gen_rdy = 0; task_dec->flags.info_change = 0; } /* 10. whether the frame buffer group is internal or external */ if (NULL == mpp->mFrameGroup) { mpp_log("mpp_dec use internal frame buffer group\n"); mpp_buffer_group_get_internal(&mpp->mFrameGroup, MPP_BUFFER_TYPE_ION); } /* 10.1 look for a unused hardware buffer for output */ if (mpp->mFrameGroup) { RK_S32 unused = mpp_buffer_group_unused(mpp->mFrameGroup); // NOTE: When dec post-process is enabled reserve 2 buffer for it. task->wait.dec_pic_unusd = (dec->vproc) ? (unused < 3) : (unused < 1); if (task->wait.dec_pic_unusd) { mpp_mutex_cond_wait(cmd_lock); /* return here and process all the flow again */ mpp_mutex_cond_unlock(cmd_lock); return MPP_OK; } } dec_dbg_detail("detail: %p check frame group count pass\n", dec); /* if dec_task is reset quit decoding and mark current packet is done */ if (task_dec->output < 0 || !task_dec->valid) return MPP_OK; if (!task->hal_frm_buf_out) { MppBuffer hal_buf_out = NULL; output = task_dec->output; mpp_buf_slot_get_prop(frame_slots, output, SLOT_BUFFER, &hal_buf_out); if (NULL == hal_buf_out) { size_t size = mpp_buf_slot_get_size(frame_slots); mpp_buffer_get(mpp->mFrameGroup, &hal_buf_out, size); if (hal_buf_out) mpp_buf_slot_set_prop(frame_slots, output, SLOT_BUFFER, hal_buf_out); } if (!dec->info_updated && dec->dev) { MppFrame slot_frm = NULL; mpp_buf_slot_get_prop(frame_slots, output, SLOT_FRAME_PTR, &slot_frm); update_dec_hal_info(dec, slot_frm); dec->info_updated = 1; } task->hal_frm_buf_out = hal_buf_out; } { MppFrame mframe = NULL; mpp_buf_slot_get_prop(frame_slots, task_dec->output, SLOT_FRAME_PTR, &mframe); if (MPP_FRAME_FMT_IS_HDR(mpp_frame_get_fmt(mframe)) && dec->cfg->base.enable_hdr_meta) { fill_hdr_meta_to_frame(mframe, dec->coding); } } task->wait.dec_pic_match = (NULL == task->hal_frm_buf_out); if (task->wait.dec_pic_match) { mpp_mutex_cond_unlock(cmd_lock); return MPP_NOK; } mpp_hal_reg_gen(dec->hal, &task->info); mpp_hal_hw_start(dec->hal, &task->info); mpp_hal_hw_wait(dec->hal, &task->info); dec->dec_hw_run_count++; /* * when hardware decoding is done: * 1. clear decoding flag (mark buffer is ready) * 2. use get_display to get a new frame with buffer * 3. add frame to output list * repeat 2 and 3 until not frame can be output */ mpp_buf_slot_clr_flag(packet_slots, task_dec->input, SLOT_HAL_INPUT); if (task_dec->output >= 0) mpp_buf_slot_clr_flag(frame_slots, task_dec->output, SLOT_HAL_OUTPUT); for (i = 0; i < MPP_ARRAY_ELEMS(task_dec->refer); i++) { RK_S32 index = task_dec->refer[i]; if (index >= 0) mpp_buf_slot_clr_flag(frame_slots, index, SLOT_HAL_INPUT); } if (task_dec->flags.eos) mpp_dec_flush(dec); output += mpp_dec_push_display(mpp, task_dec->flags); status->dec_pkt_copy_rdy = 0; status->curr_task_rdy = 0; status->task_parsed_rdy = 0; status->prev_task_rdy = 0; dec_task_info_init(&task->info); task->hal_pkt_buf_in = NULL; task->hal_frm_buf_out = NULL; mpp_mutex_cond_unlock(cmd_lock); return (MPP_RET)output; } MPP_RET mpp_dec_reset_no_thread(MppDecImpl *dec) { DecTask *task = (DecTask *)dec->task_single; MppBufSlots frame_slots = dec->frame_slots; MppMutexCond *cmd_lock = &dec->cmd_lock; HalDecTask *task_dec = &task->info.dec; RK_S32 index; RK_U32 i; mpp_mutex_cond_lock(cmd_lock); task->status.curr_task_rdy = 0; task->status.prev_task_rdy = 1; task_dec->valid = 0; mpp_parser_reset(dec->parser); mpp_hal_reset(dec->hal); if (dec->vproc) { dec_dbg_reset("reset: vproc reset start\n"); dec_vproc_reset(dec->vproc); dec_dbg_reset("reset: vproc reset done\n"); } // wait hal thread reset ready if (task->wait.info_change) { mpp_log("reset at info change status\n"); mpp_buf_slot_reset(frame_slots, task_dec->output); } if (task->status.task_parsed_rdy) { mpp_log("task no send to hal que must clr current frame hal status\n"); mpp_buf_slot_clr_flag(frame_slots, task_dec->output, SLOT_HAL_OUTPUT); for (i = 0; i < MPP_ARRAY_ELEMS(task_dec->refer); i++) { index = task_dec->refer[i]; if (index >= 0) mpp_buf_slot_clr_flag(frame_slots, index, SLOT_HAL_INPUT); } task->status.task_parsed_rdy = 0; } while (MPP_OK == mpp_buf_slot_dequeue(frame_slots, &index, QUEUE_DISPLAY)) { /* release extra ref in slot's MppBuffer */ MppBuffer buffer = NULL; mpp_buf_slot_get_prop(frame_slots, index, SLOT_BUFFER, &buffer); if (buffer) mpp_buffer_put(buffer); mpp_buf_slot_clr_flag(frame_slots, index, SLOT_QUEUE_USE); } if (task->status.dec_pkt_copy_rdy) { mpp_buf_slot_clr_flag(dec->packet_slots, task_dec->input, SLOT_HAL_INPUT); mpp_buf_slot_clr_flag(dec->packet_slots, task_dec->input, SLOT_CODEC_READY); task->status.dec_pkt_copy_rdy = 0; task->hal_pkt_buf_in = NULL; task_dec->input = -1; } // external packet release by user dec->mpp_pkt_in = NULL; task->status.task_parsed_rdy = 0; dec_task_init(task); dec_dbg_reset("reset: parser reset all done\n"); dec->dec_in_pkt_count = 0; dec->dec_hw_run_count = 0; dec->dec_out_frame_count = 0; dec->info_updated = 0; mpp_mutex_cond_signal(cmd_lock); mpp_mutex_cond_unlock(cmd_lock); return MPP_OK; } MPP_RET mpp_dec_notify_no_thread(MppDecImpl *dec, RK_U32 flag) { // Only notify buffer group control if (flag == (MPP_DEC_NOTIFY_BUFFER_VALID | MPP_DEC_NOTIFY_BUFFER_MATCH)) { mpp_mutex_cond_signal(&dec->cmd_lock); return MPP_OK; } return MPP_OK; } MPP_RET mpp_dec_control_no_thread(MppDecImpl *dec, MpiCmd cmd, void *param) { // cmd_lock is used to sync all async operations MPP_RET ret = MPP_NOK; mpp_mutex_cond_lock(&dec->cmd_lock); dec->cmd_send++; ret = mpp_dec_proc_cfg(dec, cmd, param); mpp_mutex_cond_unlock(&dec->cmd_lock); return ret; } MppDecModeApi dec_api_no_thread = { NULL, NULL, mpp_dec_reset_no_thread, mpp_dec_notify_no_thread, mpp_dec_control_no_thread, };