From: Nagaraju Siddineni <[email protected]> - Enable HW‑lock migration across cores and centralize lock acquisition for better load‑balancing and dynamic resolution. - Refactor decoder run‑path helpers to streamline init, per‑frame, and final‑frame processing with dynamic DPB management. - Add IRQ‑level lock handler that deals with pre‑empted contexts, waiting queues, QoS updates and work scheduling, reducing race conditions. - Expand QoS tracking and black‑bar detection with detailed debug output. - Consolidate error handling into a dedicated ISR‑invoked path, improving recovery reliability and logging. - Update headers/comments and add missing declarations for new helpers.
Signed-off-by: Nagaraju Siddineni <[email protected]> Signed-off-by: Himanshu Dewangan <[email protected]> --- .../samsung/exynos-mfc/mfc_core_hwlock.c | 364 +++++ .../samsung/exynos-mfc/mfc_core_hwlock.h | 8 + .../samsung/exynos-mfc/mfc_core_isr.c | 1353 ++++++++++++++++- .../samsung/exynos-mfc/mfc_core_isr.h | 3 + .../samsung/exynos-mfc/mfc_core_run.c | 145 ++ .../samsung/exynos-mfc/mfc_core_run.h | 4 + .../platform/samsung/exynos-mfc/mfc_rm.c | 1 + 7 files changed, 1877 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c b/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c index 4de836543e82..0b594429fd59 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c @@ -19,8 +19,10 @@ #include "mfc_core_cmd.h" #include "mfc_core_hw_reg_api.h" +#include "base/mfc_queue.h" #include "base/mfc_utils.h" #include "base/mfc_sched.h" +#include "base/mfc_qos.h" static inline void __mfc_print_hwlock(struct mfc_core *core) { @@ -291,6 +293,221 @@ void mfc_core_release_hwlock_ctx(struct mfc_core_ctx *core_ctx) spin_unlock_irqrestore(&core->hwlock.lock, flags); } +void mfc_core_move_hwlock_ctx(struct mfc_core *to_core, struct mfc_core *from_core, + struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx = core_ctx->ctx; + struct mfc_dev *dev = ctx->dev; + struct mfc_listable_wq *listable_wq; + bool is_move = false; + + if (from_core->hwlock.bits & (1UL << core_ctx->num)) + is_move = true; + + list_for_each_entry(listable_wq, &from_core->hwlock.waiting_list, list) { + if (!listable_wq->core_ctx) + continue; + + if (listable_wq->core_ctx->num == core_ctx->num) + is_move = true; + } + + if (is_move) { + mfc_debug(2, "There is a waiting module to moving core%d\n", + from_core->id); + MFC_TRACE_RM("[c:%d] hwlock wait in moving core%d\n", + ctx->num, from_core->id); + /* remove waiting list from from_core->hwlock */ + core_ctx->core = from_core; + __mfc_remove_listable_wq_ctx(core_ctx); + + /* add waiting list to to_core->hwlock */ + core_ctx->core = to_core; + list_add_tail(&core_ctx->hwlock_wq.list, &to_core->hwlock.waiting_list); + to_core->hwlock.wl_count++; + to_core->hwlock.owned_by_irq = 1; + mfc_info("hwlock waiting module moved MFC%d -> MFC%d\n", + from_core->id, to_core->id); + MFC_TRACE_RM("[c:%d] hwlock module move %d -> %d\n", + ctx->num, from_core->id, to_core->id); + } +} + +static inline void __mfc_yield_hwlock(struct mfc_core *core, + struct mfc_core_ctx *core_ctx) +{ + mfc_core_release_hwlock_ctx(core_ctx); + + /* Trigger again if other instance's work is waiting */ + if (core->sched->is_work(core)) + core->sched->queue_work(core); +} + +/* + * Should be called with hwlock.lock + */ +static inline void __mfc_transfer_hwlock_ctx_protected(struct mfc_core *core, + int ctx_index) +{ + core->hwlock.dev = 0; + core->hwlock.bits = 0; + set_bit(ctx_index, &core->hwlock.bits); +} + +/* + * Should be called with hwlock.lock + * + * Return value description + * >=0: succeeded to get hwlock_bit for the context, index of new context + * -1, -EINVAL: failed to get hwlock_bit for a context + */ +static int __mfc_try_to_get_new_ctx_protected(struct mfc_core *core) +{ + struct mfc_dev *dev = core->dev; + int ret = 0; + int index; + struct mfc_ctx *new_ctx; + + if (core->shutdown) { + mfc_core_info("Couldn't lock HW. Shutdown was called\n"); + return -EINVAL; + } + + if (core->sleep) { + mfc_core_info("Couldn't lock HW. Sleep was called\n"); + return -EINVAL; + } + + /* Check whether hardware is not running */ + if (core->hwlock.bits != 0 || core->hwlock.dev != 0) { + /* This is perfectly ok, the scheduled ctx should wait */ + mfc_core_debug(2, "Couldn't lock HW\n"); + return -1; + } + + /* Choose the context to run */ + index = core->sched->pick_next_work(core); + if (index < 0) { + /* This is perfectly ok, the scheduled ctx should wait + * No contexts to run + */ + mfc_core_debug(2, "No ctx is scheduled to be run\n"); + ret = -1; + return ret; + } + + new_ctx = dev->ctx[index]; + if (!new_ctx) { + mfc_core_err("no mfc context to run\n"); + ret = -1; + return ret; + } + + set_bit(new_ctx->num, &core->hwlock.bits); + ret = index; + + return ret; +} + +/* + * Should be called without hwlock holding + * + * Try to run an operation on hardware + */ +void mfc_core_try_run(struct mfc_core *core) +{ + int new_ctx_index; + int ret; + unsigned long flags; + + if (core->state == MFCCORE_ERROR) { + mfc_core_info("[MSR] Couldn't run HW. It's Error state\n"); + return; + } + + spin_lock_irqsave(&core->hwlock.lock, flags); + __mfc_print_hwlock(core); + + new_ctx_index = __mfc_try_to_get_new_ctx_protected(core); + if (new_ctx_index < 0) { + mfc_core_debug(2, "Failed to get new context to run\n"); + __mfc_print_hwlock(core); + spin_unlock_irqrestore(&core->hwlock.lock, flags); + return; + } + + core->hwlock.owned_by_irq = 1; + + __mfc_print_hwlock(core); + spin_unlock_irqrestore(&core->hwlock.lock, flags); + + ret = mfc_core_just_run(core, new_ctx_index); + if (ret) + __mfc_yield_hwlock(core, core->core_ctx[new_ctx_index]); +} + +static int __mfc_just_run_dec(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *core_ctx = core->core_ctx[ctx->num]; + int ret = 0; + + switch (core_ctx->state) { + case MFCINST_FINISHING: + ret = mfc_core_run_dec_last_frames(core, ctx); + break; + case MFCINST_RUNNING: + ret = mfc_core_run_dec_frame(core, ctx); + break; + case MFCINST_INIT: + mfc_core_cmd_open_inst(core, ctx); + break; + case MFCINST_RETURN_INST: + ret = mfc_core_cmd_close_inst(core, ctx); + break; + case MFCINST_GOT_INST: + ret = mfc_core_run_dec_init(core, ctx); + break; + case MFCINST_HEAD_PARSED: + if (core_ctx->codec_buffer_allocated == 0) { + ctx->clear_work_bit = 1; + mfc_err("codec buffer is not allocated\n"); + ret = -EAGAIN; + break; + } + if (ctx->wait_state != WAIT_NONE) { + mfc_err("wait_state(%d) is not ready\n", ctx->wait_state); + ret = -EAGAIN; + break; + } + ret = mfc_core_cmd_dec_init_buffers(core, ctx); + break; + case MFCINST_RES_CHANGE_INIT: + ret = mfc_core_run_dec_last_frames(core, ctx); + break; + case MFCINST_RES_CHANGE_FLUSH: + ret = mfc_core_run_dec_last_frames(core, ctx); + break; + case MFCINST_RES_CHANGE_END: + mfc_debug(2, "[DRC] Finished remaining frames after resolution change\n"); + ctx->capture_state = QUEUE_FREE; + mfc_debug(2, "[DRC] Will re-init the codec\n"); + ret = mfc_core_run_dec_init(core, ctx); + break; + case MFCINST_DPB_FLUSHING: + mfc_core_cmd_dpb_flush(core, ctx); + break; + case MFCINST_MOVE_INST: + mfc_core_cmd_move_inst(core, ctx); + break; + default: + mfc_info("can't try command(decoder just_run), state : %d\n", + core_ctx->state); + ret = -EAGAIN; + } + + return ret; +} + /* Run an operation on hardware */ int mfc_core_just_run(struct mfc_core *core, int new_ctx_index) { @@ -321,6 +538,12 @@ int mfc_core_just_run(struct mfc_core *core, int new_ctx_index) core->next_ctx_idx = -1; } + /* Got context to run in ctx */ + mfc_debug(2, "src: %d(ready: %d), dst: %d, state: %d, dpb_count = %d\n", + mfc_get_queue_count(&ctx->buf_queue_lock, &core_ctx->src_buf_queue), + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_ready_queue), + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue), + core_ctx->state, ctx->dpb_count); mfc_debug(2, "core_ctx->state = %d\n", core_ctx->state); /* Last frame has already been sent to MFC * Now obtaining frames from MFC buffer @@ -332,5 +555,146 @@ int mfc_core_just_run(struct mfc_core *core, int new_ctx_index) else core->continue_clock_on = false; + if (ctx->type == MFCINST_DECODER) { + ret = __mfc_just_run_dec(core, ctx); + } else { + mfc_err("invalid context type: %d\n", ctx->type); + ret = -EAGAIN; + } + + if (ret) { + /* + * Clear any reserved F/W cache flush for next ctx, + * as this will be newly decided in Prediction code. + */ + core->cache_flush_flag = 0; + core->last_cmd_has_cache_flush = 0; + + /* + * Check again the ctx condition and clear work bits + * if ctx is not available. + */ + if (core->sched->dequeue_work(core, core_ctx) == 0) + ctx->clear_work_bit = 0; + if (ctx->clear_work_bit) { + core->sched->clear_work(core, core_ctx); + ctx->clear_work_bit = 0; + } + + mfc_core_pm_clock_off(core, 1); + } + return ret; } + +void mfc_core_hwlock_handler_irq(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason, + unsigned int err) +{ + struct mfc_core_ctx *core_ctx = core->core_ctx[ctx->num]; + int new_ctx_index; + unsigned long flags; + int ret, need_butler = 0; + + if (core->state == MFCCORE_ERROR) { + mfc_info("[MSR] Couldn't lock HW. It's Error state\n"); + return; + } + + spin_lock_irqsave(&core->hwlock.lock, flags); + __mfc_print_hwlock(core); + + /* For handling DRC, when state is RES_CHANGE_INIT or RES_CHANGE_FLUSH, + * we need to make need_butler = 1. Then, rm_request_work will be called. + */ + if ((IS_MULTI_MODE(ctx) && + (core_ctx->state == MFCINST_RUNNING || + core_ctx->state == MFCINST_RES_CHANGE_INIT)) || + (ctx->handle_drc_multi_mode && + (core_ctx->state == MFCINST_RES_CHANGE_FLUSH || + core_ctx->state == MFCINST_RES_CHANGE_FLUSH_FINISHED))) + need_butler = 1; + + if (core->hwlock.owned_by_irq) { + if (core->preempt_core_ctx > MFC_NO_INSTANCE_SET) { + mfc_debug(2, "There is a preempt_core_ctx\n"); + core->continue_clock_on = true; + mfc_wake_up_core_ctx(core_ctx, reason, err); + new_ctx_index = core->preempt_core_ctx; + mfc_debug(2, "preempt_core_ctx is : %d\n", new_ctx_index); + __mfc_transfer_hwlock_ctx_protected(core, new_ctx_index); + spin_unlock_irqrestore(&core->hwlock.lock, flags); + + ret = mfc_core_just_run(core, new_ctx_index); + if (ret) { + core->continue_clock_on = false; + __mfc_yield_hwlock(core, core->core_ctx[new_ctx_index]); + } + } else if (!list_empty(&core->hwlock.waiting_list)) { + mfc_debug(2, "There is a waiting module for hwlock\n"); + core->continue_clock_on = false; + + spin_unlock_irqrestore(&core->hwlock.lock, flags); + + mfc_core_pm_clock_off(core, 1); + mfc_wake_up_core_ctx(core_ctx, reason, err); + mfc_core_release_hwlock_ctx(core_ctx); + core->sched->queue_work(core); + } else { + mfc_debug(2, "No preempt_ctx and no waiting module\n"); + new_ctx_index = core->sched->pick_next_work(core); + if (new_ctx_index < 0) { + mfc_debug(2, "No ctx to run\n"); + /* No contexts to run */ + core->continue_clock_on = false; + + spin_unlock_irqrestore(&core->hwlock.lock, flags); + + mfc_core_pm_clock_off(core, 1); + mfc_wake_up_core_ctx(core_ctx, reason, err); + mfc_core_release_hwlock_ctx(core_ctx); + core->sched->queue_work(core); + } else { + mfc_debug(2, "There is a ctx to run\n"); + core->continue_clock_on = true; + mfc_wake_up_core_ctx(core_ctx, reason, err); + + /* If cache flush command is needed or there is OTF handle, + * handler should stop + */ + if (!need_butler) { + mfc_debug(2, "Work to do successively (next ctx: %d)\n", + new_ctx_index); + __mfc_transfer_hwlock_ctx_protected(core, new_ctx_index); + + spin_unlock_irqrestore(&core->hwlock.lock, flags); + + ret = mfc_core_just_run(core, new_ctx_index); + if (ret) { + core->continue_clock_on = false; + __mfc_yield_hwlock(core, + core->core_ctx[new_ctx_index]); + } + } else { + spin_unlock_irqrestore(&core->hwlock.lock, flags); + mfc_core_release_hwlock_ctx(core_ctx); + } + } + } + } else { + mfc_debug(2, "hwlock is NOT owned by irq\n"); + core->continue_clock_on = false; + + spin_unlock_irqrestore(&core->hwlock.lock, flags); + + mfc_core_pm_clock_off(core, 1); + mfc_wake_up_core_ctx(core_ctx, reason, err); + core->sched->queue_work(core); + } + + if (need_butler) + queue_work(core->dev->butler_wq, &core->dev->butler_work); + + __mfc_print_hwlock(core); +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.h b/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.h index 35f34f306d7d..156809eafede 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.h @@ -68,5 +68,13 @@ int mfc_core_get_hwlock_ctx(struct mfc_core_ctx *core_ctx); void mfc_core_release_hwlock_dev(struct mfc_core *core); void mfc_core_release_hwlock_ctx(struct mfc_core_ctx *core_ctx); + +void mfc_core_move_hwlock_ctx(struct mfc_core *to_core, struct mfc_core *from_core, + struct mfc_core_ctx *core_ctx); + +void mfc_core_try_run(struct mfc_core *core); int mfc_core_just_run(struct mfc_core *core, int new_ctx_index); +void mfc_core_hwlock_handler_irq(struct mfc_core *core, struct mfc_ctx *ctx, + unsigned int reason, unsigned int err); + #endif /* __MFC_CORE_HWLOCK_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c b/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c index 4c6f531ffc02..94cc3c4dfdc5 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c @@ -18,7 +18,38 @@ #include "mfc_core_hw_reg_api.h" #include "mfc_core_reg_api.h" +#include "base/mfc_rate_calculate.h" +#include "base/mfc_qos.h" + +#include "base/mfc_queue.h" #include "base/mfc_utils.h" +#include "base/mfc_buf.h" +#include "base/mfc_mem.h" + +static inline enum vb2_buffer_state __mfc_get_buf_state(unsigned int err) +{ + switch (err) { + case MFC_REG_ERR_NO_KEY_FRAME: + case MFC_REG_ERR_NO_VALID_SEQ_HDR: + case MFC_REG_ERR_NO_VALID_PIC_HDR: + case MFC_REG_ERR_NO_VALID_REF_FOR_SKIP: + case MFC_REG_ERR_UNSUPPORTED_FEATURE: + case MFC_REG_ERR_UNSUPPORTED_RESOLUTION: + case MFC_REG_ERR_HEADER_NOT_FOUND: + case MFC_REG_ERR_INVALID_NAL_TYPE: + case MFC_REG_ERR_SEQUENCE_HEADER_ERROR: + case MFC_REG_ERR_PICTURE_HEADER_ERROR: + case MFC_REG_ERR_SLICE_HEADER_ERROR: + case MFC_REG_ERR_MISSING_FIRST_FIELD: + case MFC_REG_ERR_SLICE_COUNT_IS_OVER_ASO: + case MFC_REG_ERR_TILE_HEADER_ERROR: + case MFC_REG_ERR_MAX_VIEW_NUM_OVER: + case MFC_REG_ERR_MFC_TIMEOUT: + return VB2_BUF_STATE_ERROR; + default: + return VB2_BUF_STATE_DONE; + } +} static inline int __mfc_core_is_err_condition(unsigned int err) { @@ -35,10 +66,1216 @@ static inline int __mfc_core_is_err_condition(unsigned int err) } } +static inline void mfc_handle_force_change_status(struct mfc_core_ctx *core_ctx) +{ + if (core_ctx->state != MFCINST_ABORT && + core_ctx->state != MFCINST_HEAD_PARSED && + core_ctx->state != MFCINST_RES_CHANGE_FLUSH && + core_ctx->state != MFCINST_RES_CHANGE_FLUSH_FINISHED) + mfc_change_state(core_ctx, MFCINST_RUNNING); +} + +static void __mfc_handle_black_bar_info(struct mfc_core *core, + struct mfc_ctx *ctx) +{ + struct v4l2_rect new_black_bar; + int black_bar_info; + struct mfc_dec *dec = ctx->dec_priv; + + black_bar_info = mfc_core_get_black_bar_detection(); + mfc_ctx_debug(3, "[BLACKBAR] type: %#x\n", black_bar_info); + + if (black_bar_info == MFC_REG_DISP_STATUS_BLACK_BAR) { + new_black_bar.left = mfc_core_get_black_bar_pos_x(); + new_black_bar.top = mfc_core_get_black_bar_pos_y(); + new_black_bar.width = mfc_core_get_black_bar_image_w(); + new_black_bar.height = mfc_core_get_black_bar_image_h(); + } else if (black_bar_info == MFC_REG_DISP_STATUS_BLACK_SCREEN) { + new_black_bar.left = -1; + new_black_bar.top = -1; + new_black_bar.width = ctx->img_width; + new_black_bar.height = ctx->img_height; + } else if (black_bar_info == MFC_REG_DISP_STATUS_NOT_DETECTED) { + new_black_bar.left = 0; + new_black_bar.top = 0; + new_black_bar.width = ctx->img_width; + new_black_bar.height = ctx->img_height; + } else { + mfc_ctx_err("[BLACKBAR] Not supported type: %#x\n", black_bar_info); + dec->black_bar_updated = 0; + return; + } + + if (new_black_bar.left == dec->black_bar.left && + new_black_bar.top == dec->black_bar.top && + new_black_bar.width == dec->black_bar.width && + new_black_bar.height == dec->black_bar.height) { + mfc_ctx_debug(4, "[BLACKBAR] information was not changed\n"); + dec->black_bar_updated = 0; + return; + } + + dec->black_bar = new_black_bar; + dec->black_bar_updated = 1; +} + +static unsigned int __mfc_handle_frame_field(struct mfc_core *core, + struct mfc_ctx *ctx) +{ + struct mfc_dec *dec = ctx->dec_priv; + unsigned int interlace_type = 0, is_interlace = 0; + unsigned int field; + + if (IS_H264_DEC(ctx)) { + dec->is_mbaff = mfc_core_is_mbaff_picture(); + is_interlace = mfc_core_is_interlace_picture(); + } + + if (is_interlace) { + interlace_type = mfc_core_get_interlace_type(); + if (interlace_type) + field = V4L2_FIELD_INTERLACED_TB; + else + field = V4L2_FIELD_INTERLACED_BT; + } else if (dec->is_mbaff) { + field = V4L2_FIELD_INTERLACED_TB; + } else { + field = V4L2_FIELD_NONE; + } + + if (is_interlace || dec->is_mbaff) + mfc_ctx_debug(2, "[INTERLACE] is_interlace: %d (type : %d), is_mbaff: %d, field: %#x\n", + is_interlace, interlace_type, dec->is_mbaff, field); + + return field; +} + +static struct mfc_buf *__mfc_handle_last_frame(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_dec *dec = ctx->dec_priv; + struct mfc_buf *dst_mb; + int index, i; + + mfc_ctx_debug(2, "DQ empty DPB with stored tag\n"); + + dst_mb = mfc_get_del_buf(ctx, &ctx->dst_buf_queue, MFC_BUF_NO_TOUCH_USED); + if (!dst_mb) { + mfc_ctx_err("there is no dst buffer for EOS tag\n"); + return NULL; + } + + mfc_ctx_debug(2, "Cleaning up buffer: [%d][%d]\n", + dst_mb->vb.vb2_buf.index, dst_mb->dpb_index); + + index = dst_mb->vb.vb2_buf.index; + + for (i = 0; i < ctx->dst_fmt->mem_planes; i++) + vb2_set_plane_payload(&dst_mb->vb.vb2_buf, i, 0); + + dst_mb->vb.sequence = (++ctx->sequence); + dst_mb->vb.field = __mfc_handle_frame_field(core, ctx); + mfc_clear_mb_flag(dst_mb); + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->dst_ctrls[index]) < 0) + mfc_ctx_err("failed in core_get_buf_ctrls\n"); + + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, ctx->stored_tag); + + mutex_lock(&dec->dpb_mutex); + + index = dst_mb->dpb_index; + dec->dpb[index].queued = 0; + clear_bit(index, &dec->queued_dpb); + dec->display_index = dst_mb->dpb_index; + + mutex_unlock(&dec->dpb_mutex); + mfc_ctx_debug(2, "[DPB] Cleand up index [%d][%d], used_flag = %#lx, queued = %#lx\n", + dst_mb->vb.vb2_buf.index, dst_mb->dpb_index, + dec->dynamic_used, dec->queued_dpb); + + return dst_mb; +} + +static void __mfc_handle_frame_unused_output(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_dec *dec = ctx->dec_priv; + struct mfc_buf *mfc_buf = NULL; + unsigned int index; + + while (1) { + mfc_buf = mfc_get_del_buf(ctx, &ctx->err_buf_queue, MFC_BUF_NO_TOUCH_USED); + if (!mfc_buf) + break; + + index = mfc_buf->vb.vb2_buf.index; + + mfc_clear_mb_flag(mfc_buf); + mfc_buf->vb.flags &= ~(V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME | + V4L2_BUF_FLAG_ERROR); + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->dst_ctrls[index]) < 0) + mfc_ctx_err("failed in core_get_buf_ctrls\n"); + + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS, + MFC_REG_DEC_STATUS_DECODING_ONLY); + + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, UNUSED_TAG); + + dec->ref_buf[dec->refcnt].fd[0] = mfc_buf->vb.vb2_buf.planes[0].m.fd; + dec->refcnt++; + + vb2_buffer_done(&mfc_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + mfc_ctx_debug(2, "[DPB] dst index [%d][%d] fd: %d is buffer done (not used)\n", + mfc_buf->vb.vb2_buf.index, mfc_buf->dpb_index, + mfc_buf->vb.vb2_buf.planes[0].m.fd); + } +} + +static void __mfc_handle_ref_info(struct mfc_ctx *ctx, struct mfc_buf *mfc_buf, unsigned int err) +{ + struct mfc_dec *dec = ctx->dec_priv; + struct dec_dpb_ref_info *ref_info = NULL; + int i; + + if (!mfc_buf) { + for (i = 0; i < dec->refcnt; i++) + mfc_ctx_debug(2, "[REFINFO] Released FD = %d will update with display buffer\n", + dec->ref_buf[i].fd[0]); + return; + } + + ref_info = &dec->ref_info[mfc_buf->vb.vb2_buf.index]; + for (i = 0; i < dec->refcnt; i++) + ref_info->dpb[i].fd[0] = dec->ref_buf[i].fd[0]; + if (dec->refcnt != MFC_MAX_BUFFERS) + ref_info->dpb[i].fd[0] = MFC_INFO_INIT_FD; + dec->refcnt = 0; + + mfc_ctx_debug(2, "[DPB] dst index [%d][%d] fd: %d is buffer done\n", + mfc_buf->vb.vb2_buf.index, mfc_buf->dpb_index, + mfc_buf->vb.vb2_buf.planes[0].m.fd); + vb2_buffer_done(&mfc_buf->vb.vb2_buf, mfc_get_warn(err) ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); +} + +static void __mfc_handle_frame_all_extracted(struct mfc_core *core, + struct mfc_ctx *ctx) +{ + struct mfc_dec *dec = ctx->dec_priv; + struct mfc_core_ctx *core_ctx = core->core_ctx[ctx->num]; + struct mfc_buf *dst_mb; + int index, i, is_first = 1; + + mfc_debug(2, "Decided to finish\n"); + ctx->sequence++; + + if (core_ctx->state == MFCINST_RES_CHANGE_FLUSH) + is_first = 0; + + while (1) { + dst_mb = mfc_get_del_buf(ctx, &ctx->dst_buf_queue, MFC_BUF_NO_TOUCH_USED); + if (!dst_mb) + break; + + mfc_debug(2, "Cleaning up buffer: [%d][%d]\n", + dst_mb->vb.vb2_buf.index, dst_mb->dpb_index); + + index = dst_mb->vb.vb2_buf.index; + + for (i = 0; i < ctx->dst_fmt->mem_planes; i++) + vb2_set_plane_payload(&dst_mb->vb.vb2_buf, i, 0); + + dst_mb->vb.sequence = (ctx->sequence++); + dst_mb->vb.field = __mfc_handle_frame_field(core, ctx); + mfc_clear_mb_flag(dst_mb); + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->dst_ctrls[index]) < 0) + mfc_err("failed in core_get_buf_ctrls\n"); + + if (is_first) { + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, ctx->stored_tag); + is_first = 0; + } else { + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, DEFAULT_TAG); + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_VIDEO_H264_SEI_FP_AVAIL, 0); + } + + mutex_lock(&dec->dpb_mutex); + + index = dst_mb->dpb_index; + dec->dpb[index].queued = 0; + clear_bit(index, &dec->queued_dpb); + + mutex_unlock(&dec->dpb_mutex); + + vb2_buffer_done(&dst_mb->vb.vb2_buf, VB2_BUF_STATE_DONE); + mfc_debug(2, "[DPB] Cleand up index = %d, used_flag = %#lx, queued = %#lx\n", + index, dec->dynamic_used, dec->queued_dpb); + } + + /* dequeue unused DPB */ + __mfc_handle_frame_unused_output(core, ctx); + + mfc_handle_force_change_status(core_ctx); + mfc_debug(2, "After cleanup\n"); + + mfc_cleanup_iovmm_except_used(ctx); + mfc_print_dpb_table(ctx); +} + +static void __mfc_handle_frame_copy_timestamp(struct mfc_core_ctx *core_ctx, + dma_addr_t dec_y_addr) +{ + struct mfc_ctx *ctx = core_ctx->ctx; + struct mfc_buf *dst_mb, *src_mb; + + /* Get the source buffer */ + src_mb = mfc_get_buf(ctx, &core_ctx->src_buf_queue, MFC_BUF_NO_TOUCH_USED); + if (!src_mb) { + mfc_err("[TS] no src buffers\n"); + return; + } + + dst_mb = mfc_find_buf(ctx, &ctx->dst_buf_queue, dec_y_addr); + if (dst_mb) + dst_mb->vb.vb2_buf.timestamp = src_mb->vb.vb2_buf.timestamp; +} + +static struct mfc_buf *__mfc_handle_frame_output_del(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int err) +{ + struct mfc_dev *dev = ctx->dev; + struct mfc_dec *dec = ctx->dec_priv; + struct mfc_raw_info *raw = &ctx->raw_buf; + struct mfc_buf *dst_mb = NULL; + dma_addr_t dspl_y_addr; + unsigned int frame_type; + unsigned int dst_frame_status; + unsigned int is_video_signal_type = 0, is_colour_description = 0; + unsigned int is_content_light = 0, is_display_colour = 0; + unsigned int i, idr_flag, is_last_display; + int index; + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->color_aspect_dec)) { + is_video_signal_type = mfc_core_get_video_signal_type(); + is_colour_description = mfc_core_get_colour_description(); + } + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->static_info_dec)) { + is_content_light = mfc_core_get_sei_avail_content_light(); + is_display_colour = mfc_core_get_sei_avail_mastering_display(); + } + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->black_bar) && + (dec->detect_black_bar || + core->dev->debugfs.feature_option & MFC_OPTION_BLACK_BAR_ENABLE)) + __mfc_handle_black_bar_info(core, ctx); + else + dec->black_bar_updated = 0; + + if (dec->immediate_display == 1) { + dspl_y_addr = mfc_core_get_dec_y_addr(); + frame_type = mfc_core_get_dec_frame_type(); + idr_flag = mfc_core_get_dec_idr_flag(); + } else { + dspl_y_addr = mfc_core_get_disp_y_addr(); + frame_type = mfc_core_get_disp_frame_type(); + idr_flag = mfc_core_get_disp_idr_flag(); + } + + is_last_display = mfc_core_get_last_display(); + + /* If multi_view_enable(MV-HEVC), buffer must not be delete at the irq of main_view. + * Because, buffer will be reused for sub_view. + */ + if (!ctx->multi_view_enable || ctx->select_view_irq == MFC_VIEW_ID_SUB) { + dst_mb = mfc_find_del_buf(ctx, &ctx->dst_buf_queue, dspl_y_addr); + } else { + dst_mb = mfc_find_buf(ctx, &ctx->dst_buf_queue, dspl_y_addr); + mfc_ctx_debug(2, "not delete dst_mb to reuse for VIEW_1\n"); + } + + if (dst_mb) { + index = dst_mb->vb.vb2_buf.index; + /* Check if this is the buffer we're looking for */ + mfc_ctx_debug(2, "[BUFINFO][DPB] ctx[%d] get dst index: [%d][%d], addr[0]: 0x%08llx\n", + ctx->num, index, dst_mb->dpb_index, + dst_mb->addr[0][0]); + + if (dec->crc_enable && dec->crc && + (ctx->dev->debugfs.sfr_dump & MFC_DUMP_DEC_CRC)) { + if (dec->crc_idx < SZ_1K) { + dec->crc[dec->crc_idx++] = mfc_core_get_crc_luma(); + dec->crc[dec->crc_idx++] = mfc_core_get_crc_chroma(); + } else { + mfc_ctx_debug(2, "[CRC] couldn't store CRC dump (idx: %d)\n", + dec->crc_idx); + } + } + + dst_mb->vb.sequence = ctx->sequence; + dst_mb->vb.field = __mfc_handle_frame_field(core, ctx); + + /* Set flag bits in order to inform SEI information */ + if (ctx->select_view_irq == MFC_VIEW_ID_MAIN) { + mfc_clear_mb_flag(dst_mb); + + dst_mb->vb.flags &= ~(V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME | + V4L2_BUF_FLAG_ERROR); + + switch (frame_type) { + case MFC_REG_DISPLAY_FRAME_I: + dst_mb->vb.flags |= V4L2_BUF_FLAG_KEYFRAME; + if (!(CODEC_HAS_IDR(ctx) && !idr_flag)) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_SYNC_FRAME); + mfc_ctx_debug(2, "[FRAME] syncframe IDR\n"); + } + break; + case MFC_REG_DISPLAY_FRAME_P: + dst_mb->vb.flags |= V4L2_BUF_FLAG_PFRAME; + break; + case MFC_REG_DISPLAY_FRAME_B: + dst_mb->vb.flags |= V4L2_BUF_FLAG_BFRAME; + break; + default: + break; + } + } + + if ((ctx->multi_view_enable || ctx->ready_to_be_multi_view_enable) && + mfc_core_get_mvc_right_view_id() == MFC_VIEW_ID_MAIN) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_RIGHT_IS_MAIN_VIEW); + mfc_ctx_debug(2, "[MV-HEVC] Right veiw is the main view.\n"); + } + + if (is_content_light) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_HDR_CONTENT_LIGHT); + mfc_ctx_debug(2, "[HDR] content light level parsed\n"); + } + + if (is_display_colour) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_HDR_DISPLAY_COLOUR); + mfc_ctx_debug(2, "[HDR] mastering display colour parsed\n"); + } + + if (is_video_signal_type) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_HDR_VIDEO_SIGNAL_TYPE); + mfc_ctx_debug(2, "[HDR] video signal type parsed\n"); + if (is_colour_description) { + mfc_set_mb_flag(dst_mb, + MFC_FLAG_HDR_MAXTIX_COEFF); + mfc_ctx_debug(2, "[HDR] matrix coefficients parsed\n"); + mfc_set_mb_flag(dst_mb, + MFC_FLAG_HDR_COLOUR_DESC); + mfc_ctx_debug(2, "[HDR] colour description parsed\n"); + } + } + + if (dec->black_bar_updated) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_BLACKBAR_DETECT); + mfc_ctx_debug(3, "[BLACKBAR] black bar detected\n"); + } + + if (ctx->update_framerate) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_FRAMERATE_CH); + ctx->update_framerate = false; + mfc_ctx_debug(2, "[QoS] framerate changed\n"); + } + + if (is_last_display) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_LAST_FRAME); + mfc_ctx_debug(2, "[FRAME] Last display frame\n"); + } + + if (ctx->dst_fmt->mem_planes == 1) { + vb2_set_plane_payload(&dst_mb->vb.vb2_buf, 0, + raw->total_plane_size); + mfc_ctx_debug(5, "single plane payload: %d\n", + raw->total_plane_size); + } else { + for (i = 0; i < ctx->dst_fmt->mem_planes; i++) { + vb2_set_plane_payload(&dst_mb->vb.vb2_buf, + i, + raw->plane_size[i]); + } + } + + if (mfc_get_warn(err)) { + mfc_ctx_info("Warning for displayed frame: %d\n", mfc_get_warn(err)); + dst_mb->vb.flags |= V4L2_BUF_FLAG_ERROR; + } + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->dst_ctrls[index]) < 0) + mfc_ctx_err("failed in core_get_buf_ctrls\n"); + + if (dec->immediate_display == 1) { + dst_frame_status = mfc_core_get_dec_status(); + + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS, + dst_frame_status); + + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, ctx->stored_tag); + + dec->immediate_display = 0; + } + + mfc_rate_update_last_framerate(ctx, dst_mb->vb.vb2_buf.timestamp); + + mutex_lock(&dec->dpb_mutex); + + dec->dpb[dst_mb->dpb_index].queued = 0; + clear_bit(dst_mb->dpb_index, &dec->queued_dpb); + dec->display_index = dst_mb->dpb_index; + + mutex_unlock(&dec->dpb_mutex); + } else { + mfc_print_dpb_queue_with_lock(core->core_ctx[ctx->num], dec); + } + + /* + * When main-view of MV-HEVC, should return NULL. + * Because, if address of dst_mb returned by this function is not NULL, + * the address will be used by other functions. + * It could cause kernel panic. + */ + if (ctx->multi_view_enable && ctx->select_view_irq == MFC_VIEW_ID_MAIN) + dst_mb = NULL; + + return dst_mb; +} + +static void __mfc_handle_released_buf(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_dec *dec = ctx->dec_priv; + unsigned long prev_flag, new_released_flag, released_flag = 0; + unsigned long flag; + unsigned int released_flag_sub, released_flag_main; + int i, main_i, sub_i; + + mutex_lock(&dec->dpb_mutex); + + prev_flag = dec->dynamic_used; + dec->dynamic_used = mfc_core_get_dec_used_flag(); + released_flag = prev_flag & (~dec->dynamic_used); + mfc_ctx_debug(2, "[DPB] Used flag: old = %#lx, new = %#lx, released = %#lx, queued = %#lx\n", + prev_flag, dec->dynamic_used, released_flag, dec->queued_dpb); + + /* In the case of multi_view_enable(MV-HEVC), both main_view and sub_view must be released + * together. This is because the driver assigns dpb_index with only the lower half bit + * and maps both main_view and sub_view together. + * + * 1) The driver splits the original released_flag into released_flag_sub, + * released_flag_main. + * 2) Using the 'and' operation, it calculates the bits that can be released together + * and creates new_released_flag with the result + * 3) If there are bits that can be released alone (either main or sub), + * it uses the 'xor' operation to set dynamic_used back to 1. + */ + if (ctx->multi_view_enable) { + released_flag_sub = released_flag >> (MFC_MAX_DPBS / 2); + released_flag_main = (unsigned int)released_flag; + released_flag_sub &= released_flag_main; + new_released_flag = ((unsigned long)released_flag_sub << + (MFC_MAX_DPBS / 2)) | released_flag_sub; + dec->dynamic_used |= (new_released_flag ^ released_flag); + released_flag = new_released_flag; + } + + flag = dec->dynamic_used | released_flag; + for (i = __ffs(flag); i < MFC_MAX_DPBS;) { + if (dec->dynamic_used & (1UL << i)) { + dec->dpb[i].ref = 1; + if (dec->dpb[i].mapcnt == 0) + mfc_ctx_err("[DPB] %d index is no dpb table\n", i); + } + if (released_flag & (1UL << i)) { + dec->dpb[i].ref = 0; + + if (ctx->multi_view_enable && i >= MFC_MAX_DPBS / 2) + main_i = i - MFC_MAX_DPBS / 2; + else + main_i = i; + + if (dec->dpb[main_i].queued && (dec->dpb[i].new_fd != -1)) { + dec->ref_buf[dec->refcnt].fd[0] = dec->dpb[i].fd[0]; + dec->refcnt++; + mfc_ctx_debug(3, "[REFINFO] Queued DPB[%d] released fd: %d\n", + i, dec->dpb[i].fd[0]); + dec->dpb[i].fd[0] = dec->dpb[i].new_fd; + dec->dpb[i].new_fd = -1; + mfc_ctx_debug(3, "[REFINFO] Queued DPB[%d] reuse fd: %d\n", + i, dec->dpb[i].fd[0]); + } else if (!dec->dpb[main_i].queued) { + dec->ref_buf[dec->refcnt].fd[0] = dec->dpb[i].fd[0]; + dec->refcnt++; + mfc_ctx_debug(3, "[REFINFO] Dqueued DPB[%d] released fd: %d\n", + i, dec->dpb[i].fd[0]); + + if (dec->dpb[i].new_fd != -1) { + dec->ref_buf[dec->refcnt].fd[0] = dec->dpb[i].new_fd; + dec->refcnt++; + mfc_ctx_debug(3, "[REFINFO] Dqueued DPB[%d] released fd: %d\n", + i, dec->dpb[i].new_fd); + dec->dpb[i].new_fd = -1; + } + + /* + * Except queued buffer, + * the released DPB is deleted from dpb_table + */ + dec->dpb_table_used &= ~(1UL << i); + mfc_put_iovmm(ctx, dec->dpb, ctx->dst_fmt->mem_planes, i); + } else { + mfc_ctx_debug(3, "[REFINFO] Queued DPB[%d] reuse fd: %d\n", + i, dec->dpb[i].fd[0]); + } + } + flag &= ~(1UL << i); + if (flag == 0) + break; + i = __ffs(flag); + } + + /* The displayed and not referenced buffer must be freed from dpb_table */ + if (dec->display_index >= 0) { + i = dec->display_index; + + if (!ctx->multi_view_enable) { + if (!(dec->dynamic_used & (1UL << i)) && + dec->dpb[i].mapcnt && + !dec->dpb[i].queued) { + dec->ref_buf[dec->refcnt].fd[0] = dec->dpb[i].fd[0]; + dec->refcnt++; + mfc_ctx_debug(3, "[REFINFO] display DPB[%d] released fd: %d\n", + i, dec->dpb[i].fd[0]); + dec->dpb_table_used &= ~(1UL << i); + mfc_put_iovmm(ctx, dec->dpb, ctx->dst_fmt->mem_planes, i); + } + } else { + /* display_index is dpb_index of main view (view0) */ + main_i = i; + sub_i = i + MFC_MAX_DPBS / 2; + + /* Both main_view and sub_view must always be released together. */ + if (!(dec->dynamic_used & (1UL << main_i)) && dec->dpb[main_i].mapcnt && + !dec->dpb[main_i].queued && !(dec->dynamic_used & (1UL << sub_i)) && + dec->dpb[sub_i].mapcnt && !dec->dpb[sub_i].queued) { + dec->ref_buf[dec->refcnt].fd[0] = dec->dpb[main_i].fd[0]; + dec->refcnt++; + mfc_ctx_debug(3, "[REFINFO] display DPB[%d] released fd: %d\n", + main_i, dec->dpb[main_i].fd[0]); + dec->dpb_table_used &= ~(1UL << main_i); + mfc_put_iovmm(ctx, dec->dpb, ctx->dst_fmt->mem_planes, main_i); + + dec->ref_buf[dec->refcnt].fd[0] = dec->dpb[sub_i].fd[0]; + dec->refcnt++; + mfc_ctx_debug(3, "[REFINFO] display DPB[%d] released fd: %d\n", + sub_i, dec->dpb[sub_i].fd[0]); + dec->dpb_table_used &= ~(1UL << sub_i); + mfc_put_iovmm(ctx, dec->dpb, ctx->dst_fmt->mem_planes, sub_i); + } else if (dec->dpb[main_i].mapcnt) { + if (ctx->select_view_irq == MFC_VIEW_ID_MAIN) + dec->dynamic_used |= (1UL << main_i); + else + dec->dynamic_used |= (1UL << sub_i); + } + } + + dec->display_index = -1; + } + + mfc_print_dpb_table(ctx); + + mutex_unlock(&dec->dpb_mutex); +} + +static struct mfc_buf *__mfc_handle_frame_output(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int err) +{ + struct mfc_dec *dec = ctx->dec_priv; + unsigned int frame_type; + + frame_type = mfc_core_get_disp_frame_type(); + + if (!ctx->multi_view_enable || ctx->select_view_irq == MFC_VIEW_ID_SUB) + ctx->sequence++; + + if (dec->immediate_display == 1) + frame_type = mfc_core_get_dec_frame_type(); + + mfc_ctx_debug(2, "[FRAME] frame type: %d\n", frame_type); + + /* If frame is same as previous then skip and do not dequeue */ + if (frame_type == MFC_REG_DISPLAY_FRAME_NOT_CODED) + return NULL; + + /* Dequeued display buffer for user */ + return __mfc_handle_frame_output_del(core, ctx, err); +} + +static void __mfc_handle_error_state(struct mfc_ctx *ctx, struct mfc_core_ctx *core_ctx) +{ + mfc_err("[MSR] It's Error state: cleanup queue\n"); + MFC_TRACE_CORE_CTX("*** ERROR state\n"); + + mfc_change_state(core_ctx, MFCINST_ERROR); + + /* Mark all dst buffers as having an error */ + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->dst_buf_queue); + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->err_buf_queue); + /* Mark all src buffers as having an error */ + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->src_buf_ready_queue); + mfc_cleanup_queue(&ctx->buf_queue_lock, &core_ctx->src_buf_queue); +} + +void mfc_core_handle_error(struct mfc_core *core) +{ + struct mfc_dev *dev = core->dev; + struct mfc_core_ctx *core_ctx; + int i; + + mfc_core_err("[MSR] >>>>>>>> MFC CORE is Error state <<<<<<<<\n"); + mfc_core_change_state(core, MFCCORE_ERROR); + + mutex_lock(&dev->mfc_mutex); + for (i = 0; i < MFC_NUM_CONTEXTS; i++) { + if (!core->core_ctx[i]) + continue; + /* TODO: need to check two core mode */ + core_ctx = core->core_ctx[i]; + __mfc_handle_error_state(core_ctx->ctx, core_ctx); + } + mutex_unlock(&dev->mfc_mutex); +} + +/* Error handling for interrupt */ +static inline void __mfc_handle_error(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason, + unsigned int error_code) +{ + struct mfc_core_ctx *core_ctx = core->core_ctx[ctx->num]; + struct mfc_buf *src_mb; + unsigned int err, warn; + + err = mfc_get_err(error_code); + warn = mfc_get_err(error_code); + + if ((err >= MFC_REG_ERR_FRAME_CONCEAL && err <= MFC_REG_ERR_WARNINGS_END) || + (warn >= MFC_REG_ERR_FRAME_CONCEAL && warn <= MFC_REG_ERR_WARNINGS_END)) + mfc_info("Interrupt Warn: display: %d, decoded: %d\n", warn, err); + else + mfc_err("Interrupt Error: display: %d, decoded: %d\n", warn, err); + + /* Error recovery is dependent on the state of context */ + switch (core_ctx->state) { + case MFCINST_RES_CHANGE_END: + case MFCINST_GOT_INST: + /* This error had to happen while parsing the header */ + src_mb = mfc_get_del_buf(ctx, &core_ctx->src_buf_queue, + MFC_BUF_NO_TOUCH_USED); + if (src_mb) { + unsigned char *stream_vir = NULL; + unsigned int strm_size = 0; + + stream_vir = src_mb->vir_addr[0]; + strm_size = src_mb->vb.vb2_buf.planes[0].bytesused; + if (strm_size > 640) + strm_size = 640; + + if (stream_vir && strm_size) + print_hex_dump(KERN_ERR, "No header: ", + DUMP_PREFIX_OFFSET, 32, 4, + stream_vir, strm_size, false); + + mfc_clear_mb_flag(src_mb); + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_DONE); + } + break; + case MFCINST_INIT: + /* This error had to happen while acquireing instance */ + case MFCINST_RETURN_INST: + /* This error had to happen while releasing instance */ + case MFCINST_DPB_FLUSHING: + /* This error had to happen while flushing DPB */ + break; + case MFCINST_HEAD_PARSED: + /* This error had to happen while setting dst buffers */ + case MFCINST_RES_CHANGE_INIT: + case MFCINST_RES_CHANGE_FLUSH: + /* This error has to happen while resolution change */ + case MFCINST_ABORT_INST: + /* This error has to happen while buffer full handling */ + case MFCINST_FINISHING: + /* It is higly probable that an error occurred + * while decoding a frame + */ + __mfc_handle_error_state(ctx, core_ctx); + break; + default: + mfc_err("Encountered an error interrupt which had not been handled\n"); + mfc_err("core_ctx->state = %d, core_ctx->inst_no = %d\n", + core_ctx->state, core_ctx->inst_no); + break; + } + + mfc_wake_up_core(core, reason, error_code); +} + +static void __mfc_handle_frame_error(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason, + unsigned int err) +{ + struct mfc_core_ctx *core_ctx = core->core_ctx[ctx->num]; + struct mfc_dec *dec; + struct mfc_buf *src_mb = NULL; + unsigned int index; + enum vb2_buffer_state vb2_state; + + dec = ctx->dec_priv; + if (!dec) { + mfc_err("no mfc decoder to run\n"); + return; + } + + mfc_err("Interrupt Error: %d\n", err); + + if (mfc_get_err(err) == MFC_REG_ERR_MISSING_NON_BASE_VIEW_DETECTED) + mfc_err("[MV-HEVC] %s, Reuse current main-view, skipping previous one.\n", + "MFC_REG_ERR_MISSING_NON_BASE_VIEW_DETECTED"); + else + /* Get the source buffer */ + src_mb = mfc_get_del_buf(ctx, &core_ctx->src_buf_queue, + MFC_BUF_NO_TOUCH_USED); + + if (!src_mb) { + mfc_err("no src buffers\n"); + } else { + index = src_mb->vb.vb2_buf.index; + if (call_bop(ctx, core_recover_buf_ctrls, core, ctx, &ctx->src_ctrls[index]) < 0) + mfc_err("failed in core_recover_buf_ctrls\n"); + + mfc_ctx_debug(2, "MFC needs next buffer\n"); + dec->consumed = 0; + mfc_clear_mb_flag(src_mb); + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->src_ctrls[index]) < 0) + mfc_err("failed in core_get_buf_ctrls\n"); + + vb2_state = __mfc_get_buf_state(mfc_get_err(err)); + mfc_debug(2, "[STREAM] consumed only by error (state: %d)\n", vb2_state); + vb2_buffer_done(&src_mb->vb.vb2_buf, vb2_state); + } + + if (mfc_get_err(err) == MFC_REG_ERR_UNDEFINED_EXCEPTION) + mfc_core_handle_error(core); + + mfc_debug(2, "Assesing whether this context should be run again\n"); +} + +static void __mfc_handle_frame_input(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int err) +{ + struct mfc_core_ctx *core_ctx = core->core_ctx[ctx->num]; + struct mfc_dec *dec = ctx->dec_priv; + struct mfc_buf *src_mb; + unsigned int index; + int deleted = 0; + unsigned int consumed; + + if (mfc_get_err(err) == MFC_REG_ERR_NON_PAIRED_FIELD) { + /* + * For non-paired field, the same buffer need to be + * resubmitted and the consumed stream will be 0 + */ + mfc_debug(2, "Not paired field. Running again the same buffer\n"); + return; + } + + /* Get the source buffer */ + consumed = mfc_core_get_consumed_stream(); + src_mb = mfc_get_del_if_consumed(ctx, + &core_ctx->src_buf_queue, + consumed, + STUFF_BYTE, + err, + &deleted); + if (!src_mb) { + mfc_err("no src buffers\n"); + return; + } + + index = src_mb->vb.vb2_buf.index; + mfc_debug(2, "[BUFINFO] ctx[%d] get src index: %d(%d), addr: 0x%08llx\n", + ctx->num, index, src_mb->src_index, src_mb->addr[0][0]); + + if (!deleted) { + /* Run MFC again on the same buffer */ + mfc_debug(2, "[MULTIFRAME] Running again the same buffer\n"); + + dec->consumed += consumed; + dec->has_multiframe = 1; + + MFC_TRACE_CORE_CTX("** consumed:%d, remained:%d\n", + dec->consumed, mfc_dec_get_strm_size(ctx, src_mb)); + /* Do not move src buffer to done_list */ + return; + } + + if (call_bop(ctx, core_recover_buf_ctrls, core, ctx, &ctx->src_ctrls[index]) < 0) + mfc_err("failed in core_recover_buf_ctrls\n"); + + mfc_clear_mb_flag(src_mb); + + if (mfc_core_get_disp_status() == MFC_REG_DEC_STATUS_DECODING_ONLY && + mfc_core_get_dec_y_addr() == 0) { + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + mfc_debug(2, "[STREAM] decoding only but there is no address\n"); + } + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->src_ctrls[index]) < 0) + mfc_err("failed in core_get_buf_ctrls\n"); + + dec->consumed = 0; + + if (ctx->multi_view_enable && ctx->select_view == 0) + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + + vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_DONE); + mfc_debug(2, "[STREAM] src index: %d(%d), addr: 0x%08llx is buffer done\n", + index, src_mb->src_index, src_mb->addr[0][0]); +} + +/* Handle frame decoding interrupt */ +static void __mfc_handle_frame(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason, + unsigned int err) +{ + struct mfc_dev *dev = ctx->dev; + struct mfc_core *subcore; + struct mfc_dec *dec = ctx->dec_priv; + struct mfc_core_ctx *core_ctx = core->core_ctx[ctx->num]; + unsigned int dst_frame_status, sei_avail_frame_pack; + unsigned int res_change, need_dpb_change, need_scratch_change; + struct mfc_buf *mfc_buf = NULL; + bool qos_update = false; + int index; + + dst_frame_status = mfc_core_get_disp_status(); + res_change = mfc_core_get_res_change(); + need_dpb_change = mfc_core_get_dpb_change(); + need_scratch_change = mfc_core_get_scratch_change(); + sei_avail_frame_pack = mfc_core_get_sei_avail_frame_pack(); + + if (dec->immediate_display == 1) + dst_frame_status = mfc_core_get_dec_status(); + + mfc_debug(2, "[FRAME] frame status: %d\n", dst_frame_status); + mfc_debug(2, "[FRAME] display status: %d, type: %d, yaddr: %#llx\n", + mfc_core_get_disp_status(), + mfc_core_get_disp_frame_type(), + mfc_core_get_disp_y_addr()); + mfc_debug(2, "[FRAME] decoded status: %d, type: %d, yaddr: %#llx\n", + mfc_core_get_dec_status(), + mfc_core_get_dec_frame_type(), + mfc_core_get_dec_y_addr()); + + mfc_debug(4, "[HDR] SEI available status: 0x%08x\n", + mfc_core_get_sei_avail()); + mfc_debug(4, "[HDR] SEI content light: 0x%08x\n", + mfc_core_get_sei_content_light()); + mfc_debug(4, "[HDR] SEI luminance: 0x%08x, 0x%08x white point: 0x%08x\n", + mfc_core_get_sei_mastering0(), + mfc_core_get_sei_mastering1(), + mfc_core_get_sei_mastering2()); + mfc_debug(4, "[HDR] SEI display primaries: 0x%08x, 0x%08x, 0x%08x\n", + mfc_core_get_sei_mastering3(), + mfc_core_get_sei_mastering4(), + mfc_core_get_sei_mastering5()); + + mfc_qos_get_disp_ratio(ctx, + mfc_core_get_decoded_frame_cnt(), + mfc_core_get_display_frame_cnt()); + qos_update = mfc_qos_mb_calculate(core, + core_ctx, mfc_core_get_processing_cycle(), + mfc_core_get_dec_frame_type()); + + if (core_ctx->state == MFCINST_RES_CHANGE_INIT) + mfc_change_state(core_ctx, MFCINST_RES_CHANGE_FLUSH); + + if (res_change) { + mfc_info("[DRC] Resolution change set to %d\n", res_change); + mutex_lock(&ctx->drc_wait_mutex); + mfc_change_state(core_ctx, MFCINST_RES_CHANGE_INIT); + if (!IS_SINGLE_MODE(ctx)) { + ctx->handle_drc_multi_mode = 1; + if (!ctx->wait_state) { + /* The core that detects DRC must be switched to single */ + ctx->op_core_type = (core->id == MFC_OP_CORE_FIXED_0) ? + MFC_OP_CORE_FIXED_0 : MFC_OP_CORE_FIXED_1; + ctx->wait_state = WAIT_G_FMT | WAIT_STOP; + mfc_debug(2, "[2CORE][DRC] MFC-%d op_core_type: %d\n", + core->id, ctx->op_core_type); + } + mfc_debug(2, "[2CORE][DRC] wait_state: %d\n", ctx->wait_state); + } else { + ctx->handle_drc_multi_mode = 0; + ctx->wait_state = WAIT_G_FMT | WAIT_STOP; + } + mfc_debug(2, "[DRC] Decoding waiting! : %d\n", ctx->wait_state); + mutex_unlock(&ctx->drc_wait_mutex); + mfc_buf = mfc_get_buf(ctx, &core_ctx->src_buf_queue, MFC_BUF_NO_TOUCH_USED); + if (mfc_buf) { + index = mfc_buf->vb.vb2_buf.index; + call_bop(ctx, core_restore_buf_ctrls, ctx, &ctx->src_ctrls[index]); + } + return; + } + + if (need_dpb_change || need_scratch_change) { + mfc_info("[DRC] Interframe resolution changed\n"); + mutex_lock(&ctx->drc_wait_mutex); + ctx->wait_state = WAIT_G_FMT | WAIT_STOP; + mfc_core_get_img_size(core, ctx, MFC_GET_RESOL_DPB_SIZE); + dec->inter_res_change = 1; + mutex_unlock(&ctx->drc_wait_mutex); + __mfc_handle_frame_all_extracted(core, ctx); + return; + } + + if (mfc_is_queue_count_same(&ctx->buf_queue_lock, &core_ctx->src_buf_queue, 0) && + mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->dst_buf_queue, 0)) { + mfc_err("Queue count is zero for src and dst\n"); + goto leave_handle_frame; + } + + if (IS_H264_DEC(ctx) && sei_avail_frame_pack && + dst_frame_status == MFC_REG_DEC_STATUS_DECODING_ONLY) { + mfc_debug(2, "Frame packing SEI exists for a frame\n"); + mfc_debug(2, "Reallocate DPBs and issue init_buffer\n"); + mutex_lock(&ctx->drc_wait_mutex); + ctx->is_dpb_realloc = 1; + mfc_change_state(core_ctx, MFCINST_HEAD_PARSED); + ctx->capture_state = QUEUE_FREE; + ctx->wait_state = WAIT_STOP; + mutex_unlock(&ctx->drc_wait_mutex); + __mfc_handle_frame_all_extracted(core, ctx); + goto leave_handle_frame; + } + + /* All frames remaining in the buffer have been extracted */ + if (dst_frame_status == MFC_REG_DEC_STATUS_DECODING_EMPTY) { + if (core_ctx->state == MFCINST_RES_CHANGE_FLUSH) { + mfc_debug(2, "[DRC] Last frame received after resolution change\n"); + __mfc_handle_frame_all_extracted(core, ctx); + + /* In the case of 2core DRC, state must be changed at subcore deinit. */ + if (ctx->handle_drc_multi_mode) + mfc_change_state(core_ctx, MFCINST_RES_CHANGE_FLUSH_FINISHED); + else + mfc_change_state(core_ctx, MFCINST_RES_CHANGE_END); + + if (IS_MULTI_CORE_DEVICE(dev)) + mfc_rm_load_balancing(ctx, MFC_RM_LOAD_DELETE); + + mfc_rate_reset_ts_list(&ctx->src_ts); + mfc_rate_reset_last_framerate(ctx); + mfc_rate_reset_bufq_framerate(ctx); + mfc_rate_set_framerate(ctx, DEC_DEFAULT_FPS); + mfc_qos_on(core, ctx); + + goto leave_handle_frame; + } else { + mfc_buf = __mfc_handle_last_frame(core, ctx); + } + } + + /* Detection for QoS weight */ + if (!dec->num_of_tile_over_4 && + mfc_core_get_num_of_tile() >= 4) { + dec->num_of_tile_over_4 = 1; + qos_update = true; + } + + if (qos_update) { + mfc_qos_on(core, ctx); + if (IS_TWO_MODE1(ctx)) { + subcore = mfc_get_sub_core(dev, ctx); + if (subcore) { + subcore->core_ctx[ctx->num]->dynamic_weight_level = + core_ctx->dynamic_weight_level; + mfc_qos_on(subcore, ctx); + } + } + } + + /* copy decoded timestamp */ + if (mfc_dec_status_decoding(dst_frame_status)) + __mfc_handle_frame_copy_timestamp(core_ctx, mfc_core_get_dec_y_addr()); + + /* Mark source buffer as complete */ + if (dst_frame_status != MFC_REG_DEC_STATUS_DISPLAY_ONLY) + __mfc_handle_frame_input(core, ctx, err); + + /* A frame has been decoded and is in the buffer */ + if (mfc_dec_status_display(dst_frame_status)) { + ctx->select_view_irq = mfc_core_get_mvc_view_id_disp_order() % MFC_NUM_MULTI_VIEW; + mfc_buf = __mfc_handle_frame_output(core, ctx, err); + } else { + ctx->select_view_irq = mfc_core_get_mvc_view_id_dec_order() % MFC_NUM_MULTI_VIEW; + } + + /* arrangement of assigned dpb table */ + __mfc_handle_released_buf(core, ctx); + + /* dequeue unused DPB */ + __mfc_handle_frame_unused_output(core, ctx); + + /* There is display buffer for user, update reference information */ + __mfc_handle_ref_info(ctx, mfc_buf, err); + + mfc_rate_update_bufq_framerate(ctx, MFC_TS_DST_DQ); + + if (dst_frame_status == MFC_REG_DEC_STATUS_DECODING_EMPTY) { + mfc_handle_force_change_status(core->core_ctx[ctx->num]); + mfc_debug(2, "It can be continue decoding again\n"); + } + + /* The first frame is only for decoding, + * so the select_view should only be increased at display interrupt. + */ + if (ctx->multi_view_enable && mfc_core_get_dec_y_addr()) + ctx->select_view = (ctx->select_view + 1) % MFC_NUM_MULTI_VIEW; + +leave_handle_frame: + mfc_debug(2, "Assesing whether this context should be run again\n"); +} + +static inline void __mfc_handle_done_frame(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason, + unsigned int err) +{ + __mfc_handle_frame(core, ctx, reason, err); +} + +/* Handle header decoder interrupt */ +static int __mfc_handle_seq_dec(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_dev *dev = ctx->dev; + struct mfc_core_ctx *core_ctx = core->core_ctx[ctx->num]; + struct mfc_dec *dec = ctx->dec_priv; + struct mfc_buf *src_mb; + int i, is_interlace; + unsigned int strm_size, consumed; + + if (ctx->src_fmt->fourcc != V4L2_PIX_FMT_FIMV1) { + ctx->img_width = mfc_core_get_img_width(); + ctx->img_height = mfc_core_get_img_height(); + ctx->crop_width = ctx->img_width; + ctx->crop_height = ctx->img_height; + mfc_info("[STREAM] resolution w: %d, h: %d\n", ctx->img_width, ctx->img_height); + } + + ctx->dpb_count = mfc_core_get_dpb_count(); + mfc_ctx_debug(2, "dpb_count: %d\n", ctx->dpb_count); + + ctx->scratch_buf_size = mfc_core_get_scratch_size(); + + mfc_core_dec_get_crop_info(core, ctx); + dec->mv_count = mfc_core_get_mv_count(); + + if (ctx->img_width == 0 || ctx->img_height == 0) { + mfc_err("[STREAM] wrong resolution w: %d, h: %d\n", + ctx->img_width, ctx->img_height); + } else { + is_interlace = mfc_core_is_interlace_picture(); + dec->is_mbaff = mfc_core_is_mbaff_picture(); + if (is_interlace || dec->is_mbaff) + dec->is_interlaced = 1; + mfc_debug(2, "[INTERLACE] interlace: %d, mbaff: %d\n", + is_interlace, dec->is_mbaff); + } + + for (i = 0; i < ctx->dst_fmt->num_planes; i++) { + ctx->min_dpb_size[i] = mfc_core_get_min_dpb_size(i); + mfc_debug(2, "[FRAME] min_dpb_size[%d]: %d, min_dpb_size_2bits[%d]: %d\n", + i, ctx->min_dpb_size[i], i, ctx->min_dpb_size_2bits[i]); + } + + if (IS_MULTI_CORE_DEVICE(dev) && mfc_core_get_two_core_mode()) { + if (dev->debugfs.feature_option & MFC_OPTION_MULTI_CORE_DISABLE) { + mfc_info("[2CORE] op_mode: %d stream, but multi core disable\n", + mfc_core_get_two_core_mode()); + } else { + if (dev->num_inst > 1) + mfc_debug(2, "[2CORE] multi core bits: %#lx, num inst: %d\n", + dev->multi_core_inst_bits, dev->num_inst); + ctx->stream_op_mode = mfc_core_get_two_core_mode(); + mfc_info("[2CORE] This stream need to multi core stream_op_mode(%d)\n", + ctx->stream_op_mode); + } + } + + src_mb = mfc_get_buf + (ctx, &core_ctx->src_buf_queue, MFC_BUF_NO_TOUCH_USED); + if (src_mb) { + consumed = mfc_core_get_consumed_stream(); + strm_size = mfc_dec_get_strm_size(ctx, src_mb); + mfc_debug(2, "[STREAM] header size, %d, %#x, consumed, %d, %#x\n", + strm_size, strm_size, consumed, consumed); + if ((IS_H264_DEC(ctx)) && + (consumed > 0 && strm_size > consumed)) { + dec->consumed += consumed; + mfc_debug(2, "[STREAM] there is remained bytes(%d) after header parsing\n", + (strm_size - consumed)); + } else { + dec->consumed = 0; + } + } + + dec->frame_display_delay = mfc_core_get_display_delay(); + mfc_debug(2, "[FRAME] display delay for first frame %d\n", + dec->frame_display_delay); + + mfc_change_state(core_ctx, MFCINST_HEAD_PARSED); + + return 0; +} + +static inline void __mfc_handle_nal_abort(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason) +{ + struct mfc_core_ctx *core_ctx = core->core_ctx[ctx->num]; + + mfc_change_state(core_ctx, MFCINST_ABORT); +} + irqreturn_t mfc_core_top_half_irq(int irq, void *priv) { struct mfc_core *core = priv; struct mfc_core_ctx *core_ctx; + struct mfc_ctx *ctx; unsigned int err; unsigned int reason; @@ -48,12 +1285,21 @@ irqreturn_t mfc_core_top_half_irq(int irq, void *priv) return IRQ_WAKE_THREAD; } + ctx = core_ctx->ctx; reason = mfc_core_get_int_reason(); err = mfc_core_get_int_err(); core->last_int = reason; core->last_int_time = ktime_to_timespec64(ktime_get()); + if (reason == MFC_REG_R2H_CMD_SEQ_DONE_RET || + reason == MFC_REG_R2H_CMD_INIT_BUFFERS_RET || + reason == MFC_REG_R2H_CMD_FRAME_DONE_RET) { + ctx->frame_cnt++; + /* counting QoS table portion */ + mfc_qos_set_portion(core, ctx); + } + mfc_debug(2, "[c:%d] Int reason: %d (err: %d, warn: %d)\n", core->curr_core_ctx, reason, mfc_get_err(err), mfc_get_warn(err)); MFC_TRACE_CORE_CTX("<< INT(top): %d\n", reason); @@ -78,6 +1324,83 @@ static int __mfc_irq_dev(struct mfc_core *core, unsigned int reason, unsigned in return 1; } +static int __mfc_irq_ctx(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason, + unsigned int err) +{ + struct mfc_core_ctx *core_ctx = core->core_ctx[ctx->num]; + + if ((core->dev->debugfs.feature_option & MFC_OPTION_MSR_ENABLE) && + ctx->frame_cnt == 20) { + reason = 0; + mfc_debug(2, "[MSR] Forced entry into MSR mode, it will be recovery\n"); + core->dev->debugfs.feature_option &= ~MFC_OPTION_MSR_ENABLE; + } + + switch (reason) { + case MFC_REG_R2H_CMD_ERR_RET: + /* An error has occurred */ + if (core_ctx->state == MFCINST_RUNNING || core_ctx->state == MFCINST_ABORT) { + if (mfc_get_err(err) >= MFC_REG_ERR_FRAME_CONCEAL && + mfc_get_err(err) <= MFC_REG_ERR_WARNINGS_END) + __mfc_handle_frame(core, ctx, reason, err); + else + __mfc_handle_frame_error(core, ctx, reason, err); + } else { + __mfc_handle_error(core, ctx, reason, err); + } + break; + case MFC_REG_R2H_CMD_SLICE_DONE_RET: + case MFC_REG_R2H_CMD_FIELD_DONE_RET: + case MFC_REG_R2H_CMD_FRAME_DONE_RET: + case MFC_REG_R2H_CMD_COMPLETE_SEQ_RET: + __mfc_handle_done_frame(core, ctx, reason, err); + break; + case MFC_REG_R2H_CMD_SEQ_DONE_RET: + if (ctx->type == MFCINST_DECODER) + __mfc_handle_seq_dec(core, ctx); + break; + case MFC_REG_R2H_CMD_OPEN_INSTANCE_RET: + core_ctx->inst_no = mfc_core_get_inst_no(); + mfc_change_state(core_ctx, MFCINST_GOT_INST); + break; + case MFC_REG_R2H_CMD_CLOSE_INSTANCE_RET: + mfc_change_state(core_ctx, MFCINST_FREE); + break; + case MFC_REG_R2H_CMD_NAL_ABORT_RET: + __mfc_handle_nal_abort(core, ctx, reason); + break; + case MFC_REG_R2H_CMD_DPB_FLUSH_RET: + ctx->select_view = MFC_VIEW_ID_MAIN; + mfc_change_state(core_ctx, MFCINST_ABORT); + break; + case MFC_REG_R2H_CMD_INIT_BUFFERS_RET: + if (err != 0) { + mfc_err("INIT_BUFFERS_RET error: %d\n", err); + break; + } + + if (IS_MULTI_MODE(ctx)) + mfc_change_state(core_ctx, MFCINST_BUF_INIT_BUT_MULTI_MODE_NOT_CHECKED_YET); + else + mfc_change_state(core_ctx, MFCINST_RUNNING); + + if (ctx->type == MFCINST_DECODER) { + if (ctx->is_dpb_realloc) + ctx->is_dpb_realloc = 0; + } + break; + case MFC_REG_R2H_CMD_MOVE_INSTANCE_RET: + break; + default: + mfc_err("Unknown int reason: %d(%#x)\n", reason, reason); + mfc_core_handle_error(core); + } + + return 1; +} + /* Interrupt processing */ irqreturn_t mfc_core_irq(int irq, void *priv) { @@ -116,7 +1439,35 @@ irqreturn_t mfc_core_irq(int irq, void *priv) ret = __mfc_irq_dev(core, reason, err); if (!ret) goto irq_end; - mfc_ctx_info("not implemented context irq ctx"); + + core_ctx = core->core_ctx[core->curr_core_ctx]; + if (!core_ctx) { + mfc_core_err("no mfc context to run\n"); + mfc_core_clear_int(); + mfc_core_pm_clock_off(core, 1); + goto irq_end; + } + ctx = core_ctx->ctx; + + ret = mfc_get_core_intlock(core_ctx); + if (ret) { + mfc_core_clear_int_only(); + goto irq_end; + } + + ret = __mfc_irq_ctx(core, ctx, reason, err); + if (!ret) + goto irq_end; + + /* clean-up interrupt */ + mfc_core_clear_int(); + + mfc_release_core_intlock(core_ctx); + + if (!(core_ctx->state == MFCINST_RES_CHANGE_INIT && IS_SINGLE_MODE(ctx))) + core->sched->dequeue_work(core, core_ctx); + + mfc_core_hwlock_handler_irq(core, ctx, reason, err); irq_end: mfc_core_debug_leave(); diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.h b/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.h index 046b20e6d4c2..16902b1537c6 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.h @@ -19,4 +19,7 @@ irqreturn_t mfc_core_top_half_irq(int irq, void *priv); irqreturn_t mfc_core_irq(int irq, void *priv); + +void mfc_core_handle_error(struct mfc_core *core); + #endif /* __MFC_CORE_ISR_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c b/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c index fd7ebe95e715..118108f910e2 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c @@ -17,8 +17,10 @@ #include "mfc_core_cmd.h" #include "mfc_core_hw_reg_api.h" +#include "base/mfc_queue.h" #include "base/mfc_utils.h" #include "base/mfc_mem.h" +#include "base/mfc_qos.h" void mfc_core_run_cache_flush(struct mfc_core *core, enum mfc_do_cache_flush do_cache_flush, @@ -263,3 +265,146 @@ int mfc_core_run_wakeup(struct mfc_core *core) return ret; } + +int mfc_core_run_dec_init(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *core_ctx = core->core_ctx[ctx->num]; + struct mfc_dec *dec = ctx->dec_priv; + struct mfc_buf *src_mb; + unsigned int strm_size; + + /* Initializing decoding - parsing header */ + + /* Get the next source buffer */ + src_mb = mfc_get_buf(ctx, &core_ctx->src_buf_queue, MFC_BUF_SET_USED); + if (!src_mb) { + mfc_err("no src buffers\n"); + return -EAGAIN; + } + + strm_size = mfc_dec_get_strm_size(ctx, src_mb); + mfc_debug(2, "Preparing to init decoding\n"); + mfc_debug(2, "[STREAM] Header size: %d, (offset: %u, consumed: %u)\n", + strm_size, + src_mb->vb.vb2_buf.planes[0].data_offset, + dec->consumed); + + mfc_core_set_dec_stream_buffer(core, ctx, src_mb, + mfc_dec_get_strm_offset(ctx, src_mb), strm_size); + + mfc_debug(2, "[BUFINFO] Header addr: 0x%08llx\n", src_mb->addr[0][0]); + mfc_clean_core_ctx_int_flags(core->core_ctx[ctx->num]); + mfc_core_cmd_dec_seq_header(core, ctx); + + return 0; +} + +static int __mfc_check_last_frame(struct mfc_core_ctx *core_ctx, + struct mfc_buf *mfc_buf) +{ + if (mfc_check_mb_flag(mfc_buf, MFC_FLAG_LAST_FRAME)) { + mfc_debug(2, "Setting core_ctx->state to FINISHING\n"); + mfc_change_state(core_ctx, MFCINST_FINISHING); + return 1; + } + + return 0; +} + +int mfc_core_run_dec_frame(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_dec *dec = ctx->dec_priv; + struct mfc_core_ctx *core_ctx = core->core_ctx[ctx->num]; + struct mfc_buf *src_mb, *dst_mb; + int last_frame = 0; + unsigned int index, src_index; + int ret; + + /* Get the next source buffer */ + if (IS_TWO_MODE2(ctx)) { + src_mb = mfc_get_buf_no_used(ctx, &core_ctx->src_buf_queue, + MFC_BUF_SET_USED); + if (!src_mb) { + mfc_debug(2, "no src buffers\n"); + return -EAGAIN; + } + } else { + src_mb = mfc_get_buf(ctx, &core_ctx->src_buf_queue, + MFC_BUF_SET_USED); + if (!src_mb) { + mfc_debug(2, "no src buffers\n"); + return -EAGAIN; + } + } + + /* Try to use the non-referenced DPB on dst-queue */ + if (dec->is_dynamic_dpb) { + dst_mb = mfc_search_for_dpb(core_ctx); + if (!dst_mb) { + src_mb->used = MFC_BUF_RESET_USED; + mfc_debug(2, "[DPB] couldn't find dst buffers\n"); + return -EAGAIN; + } + } + + index = src_mb->vb.vb2_buf.index; + src_index = src_mb->src_index; + + if (mfc_check_mb_flag(src_mb, MFC_FLAG_EMPTY_DATA)) + src_mb->vb.vb2_buf.planes[0].bytesused = 0; + + mfc_core_set_dec_stream_buffer(core, ctx, src_mb, + mfc_dec_get_strm_offset(ctx, src_mb), + mfc_dec_get_strm_size(ctx, src_mb)); + + if (call_bop(ctx, core_set_buf_ctrls, core, ctx, &ctx->src_ctrls[index]) < 0) + mfc_err("failed in core_set_buf_ctrls\n"); + mfc_core_update_tag(core, ctx, ctx->stored_tag); + + if (dec->is_dynamic_dpb) + mfc_core_set_dynamic_dpb(core, ctx, dst_mb); + + mfc_clean_core_ctx_int_flags(core_ctx); + + last_frame = __mfc_check_last_frame(core_ctx, src_mb); + ret = mfc_core_cmd_dec_one_frame(core, ctx, last_frame, src_index); + + return ret; +} + +int mfc_core_run_dec_last_frames(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *core_ctx = core->core_ctx[ctx->num]; + struct mfc_dec *dec = ctx->dec_priv; + struct mfc_buf *dst_mb; + unsigned int src_index; + + if (mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->dst_buf_queue, 0)) { + mfc_debug(2, "no dst buffer\n"); + return -EAGAIN; + } + + /* Try to use the non-referenced DPB on dst-queue */ + if (dec->is_dynamic_dpb) { + dst_mb = mfc_search_for_dpb(core_ctx); + if (!dst_mb) { + mfc_debug(2, "[DPB] couldn't find dst buffers\n"); + return -EAGAIN; + } + } + + /* Get the next source buffer */ + mfc_get_buf(ctx, &core_ctx->src_buf_queue, MFC_BUF_SET_USED); + + /* Frames are being decoded */ + mfc_core_set_dec_stream_buffer(core, ctx, 0, 0, 0); + src_index = ctx->curr_src_index + 1; + + if (dec->is_dynamic_dpb) + mfc_core_set_dynamic_dpb(core, ctx, dst_mb); + + mfc_clean_core_ctx_int_flags(core_ctx); + mfc_core_cmd_dec_one_frame(core, ctx, 1, src_index); + + return 0; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.h b/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.h index 3d243dc18e15..6b9c9ef91e47 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.h @@ -23,4 +23,8 @@ void mfc_core_run_deinit_hw(struct mfc_core *core); int mfc_core_run_sleep(struct mfc_core *core); int mfc_core_run_wakeup(struct mfc_core *core); + +int mfc_core_run_dec_init(struct mfc_core *core, struct mfc_ctx *ctx); +int mfc_core_run_dec_frame(struct mfc_core *core, struct mfc_ctx *ctx); +int mfc_core_run_dec_last_frames(struct mfc_core *core, struct mfc_ctx *ctx); #endif /* __MFC_CORE_RUN_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c b/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c index 0a805464fbc9..a7db47e58589 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c @@ -332,6 +332,7 @@ static int __mfc_rm_move_core_running(struct mfc_ctx *ctx, int to_core_num, int from_core->core_ctx[core_ctx->num] = 0; mfc_core_pm_clock_off(to_core, 0); + mfc_core_move_hwlock_ctx(to_core, from_core, core_ctx); mfc_core_release_hwlock_dev(subcore); mfc_core_release_hwlock_dev(maincore); -- 2.34.1
