#define RRINT_FLAG_RPM 0x0400
#define DEBUG_DISABLE_RUNREADY_RMBUF 0x0800
#define PRINT_FLAG_DUMP_BUFSPEC 0x1000
+#define PRINT_FLAG_FCC_STATUS 0x2000
#define PRINT_FLAG_V4L_DETAIL 0x8000
#define DISABLE_ERROR_HANDLE 0x10000
#define DEBUG_DUMP_STAT 0x80000
#define DEC_RESULT_EOS 7
#define DEC_RESULT_FORCE_EXIT 8
#define DEC_RESULT_TIMEOUT 9
+#define DEC_RESULT_DISCARD_DATA 10
/*
if (!hw->enable_fence)
hw->buffer_spec[buffer_index].vf_ref = 0;
fill_frame_info(hw, frame);
+
for (i = 0; i < vf_count; i++) {
if (kfifo_get(&hw->newframe_q, &vf) == 0 ||
vf == NULL) {
hw->buffer_spec[buffer_index].aux_data_buf,
hw->buffer_spec[buffer_index].aux_data_size,
false, vdec->vf_provider_name, NULL);
+
if (without_display_mode == 0) {
vf_notify_receiver(vdec->vf_provider_name,
VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL);
return 0;
}
+#ifdef VDEC_FCC_SUPPORT
+static void fcc_discard_mode_process(struct vdec_s *vdec)
+{
+ struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)(vdec->private);
+ struct h264_dpb_stru *p_H264_Dpb = &hw->dpb;
+
+ if (vdec->fcc_status == AGAIN_STATUS) {
+ vdec->stream_offset = (p_H264_Dpb->dpb_param.l.data[OFFSET_DELIMITER_LO] |
+ p_H264_Dpb->dpb_param.l.data[OFFSET_DELIMITER_HI] << 16);
+ dpb_print(DECODE_ID(hw), PRINT_FLAG_FCC_STATUS,
+ "[%d][FCC]: Notify stream_offset: %d\n",
+ vdec->id, vdec->stream_offset);
+ vdec_wakeup_fcc_poll(vdec);
+ amvdec_stop();
+ vdec->mc_loaded = 0;
+ hw->init_flag = 0;
+ vdec->fcc_status = WAIT_MSG_STATUS;
+ hw->dec_result = DEC_RESULT_AGAIN;
+ vdec_schedule_work(&hw->work);
+ } else if (vdec->fcc_status == DISCARD_STATUS) {
+ dpb_print(DECODE_ID(hw), PRINT_FLAG_FCC_STATUS,
+ "[%d][FCC]: Discard current gop and to find next gop!\n",
+ vdec->id);
+ amvdec_stop();
+ vdec->mc_loaded = 0;
+ hw->init_flag = 0;
+ vdec->fcc_status = AGAIN_STATUS;
+ hw->dec_result = DEC_RESULT_DISCARD_DATA;
+ vdec_schedule_work(&hw->work);
+ }
+}
+
+static int vh264_fcc_process(struct vdec_s *vdec)
+{
+ struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)(vdec->private);
+
+ if (input_stream_based(vdec)) {
+ switch (vdec->fcc_mode) {
+ case FCC_DISCARD_MODE:
+ fcc_discard_mode_process(vdec);
+ return 1;
+ case FCC_DEC_MODE:
+ dpb_print(DECODE_ID(hw), PRINT_FLAG_FCC_STATUS,
+ "[%d][FCC]: Current is Dec mode.\n", vdec->id);
+ break;
+ case FCC_BUTT:
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+#endif
static irqreturn_t vh264_isr_thread_fn(struct vdec_s *vdec, int irq)
{
p_H264_Dpb->frame_crop_top_offset = p_H264_Dpb->dpb_param.dpb.frame_crop_top_offset;
p_H264_Dpb->frame_crop_bottom_offset = p_H264_Dpb->dpb_param.dpb.frame_crop_bottom_offset;
- dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL,
+ dpb_print(DECODE_ID(hw), PRINT_FLAG_DPB_DETAIL,
"%s chroma_format_idc %d crop offset: left %d right %d top %d bottom %d\n",
__func__, p_H264_Dpb->chroma_format_idc,
p_H264_Dpb->frame_crop_left_offset,
p_H264_Dpb->frame_crop_bottom_offset);
#endif
+#ifdef VDEC_FCC_SUPPORT
+ if (vh264_fcc_process(vdec) > 0)
+ return IRQ_HANDLED;
+#endif
+
WRITE_VREG(DPB_STATUS_REG, H264_ACTION_CONFIG_DONE);
reset_process_time(hw);
hw->reg_iqidct_control = READ_VREG(IQIDCT_CONTROL);
}
}
- if (slice_header_process_status == 1)
+ if (slice_header_process_status == 1) {
WRITE_VREG(DPB_STATUS_REG, H264_ACTION_DECODE_NEWPIC);
- else
+ } else {
WRITE_VREG(DPB_STATUS_REG, H264_ACTION_DECODE_SLICE);
+ }
hw->last_mby_mbx = 0;
hw->last_vld_level = 0;
start_process_time(hw);
if (hw->init_flag == 0)
WRITE_VREG(DPB_STATUS_REG, 0);
- else
+ else {
WRITE_VREG(DPB_STATUS_REG, H264_ACTION_DECODE_START);
+ }
WRITE_VREG(FRAME_COUNTER_REG, hw->decode_pic_count);
WRITE_VREG(AV_SCRATCH_8, hw->buf_offset);
return;
} else if (hw->dec_result == DEC_RESULT_DONE ||
hw->dec_result == DEC_RESULT_TIMEOUT) {
+
/* if (!hw->ctx_valid)
hw->ctx_valid = 1; */
if ((hw->dec_result == DEC_RESULT_TIMEOUT) &&
vdec_free_irq(VDEC_IRQ_1, (void *)hw);
hw->stat &= ~STAT_ISR_REG;
}
+ } else if (hw->dec_result == DEC_RESULT_DISCARD_DATA) {
+ mutex_lock(&hw->chunks_mutex);
+ vdec_vframe_dirty(hw_to_vdec(hw), hw->chunk);
+ hw->chunk = NULL;
+ mutex_unlock(&hw->chunks_mutex);
}
WRITE_VREG(ASSIST_MBOX1_MASK, 0);
del_timer_sync(&hw->check_timer);
}
+#ifdef VDEC_FCC_SUPPORT
+static void vmh264_wakeup_fcc_poll(struct vdec_s *vdec)
+{
+ amstream_wakeup_fcc_poll(vdec);
+}
+#endif
+
static unsigned long run_ready(struct vdec_s *vdec, unsigned long mask)
{
bool ret = 0;
(struct vdec_h264_hw_s *)vdec->private;
int tvp = vdec_secure(hw_to_vdec(hw)) ?
CODEC_MM_FLAGS_TVP : 0;
+#ifdef VDEC_FCC_SUPPORT
+ int get_msg = 1;
+#endif
if (hw->timeout_processing &&
(work_pending(&hw->work) || work_busy(&hw->work) ||
}
+#ifdef VDEC_FCC_SUPPORT
+ get_msg = vdec_has_get_fcc_new_msg(vdec);
+ ret &= get_msg;
+#endif
+
if (ret)
not_run_ready[DECODE_ID(hw)] = 0;
else
not_run_ready[DECODE_ID(hw)]++;
+
if (vdec->parallel_dec == 1) {
if (hw->mmu_enable == 0)
return ret ? (CORE_MASK_VDEC_1) : 0;
pdata->user_data_read = NULL;
pdata->reset_userdata_fifo = NULL;
#endif
+
+#ifdef VDEC_FCC_SUPPORT
+ pdata->wakeup_fcc_poll = vmh264_wakeup_fcc_poll;
+#endif
+
if (pdata->use_vfm_path) {
snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE,
VFM_DEC_PROVIDER_NAME);
#define DEC_RESULT_EOS 9
#define DEC_RESULT_FORCE_EXIT 10
#define DEC_RESULT_FREE_CANVAS 11
+#define DEC_RESULT_DISCARD_DATA 12
static void vh265_work(struct work_struct *work);
static void vh265_timeout_work(struct work_struct *work);
pic->vf_ref = 1;
put_vf_to_display_q(hevc, vf);
#endif
+
/*count info*/
vdec_count_info(hevc->gvs, 0, stream_offset);
- if (hevc->cur_pic->slice_type == I_SLICE) {
- hevc->gvs->i_decoded_frames++;
- } else if (hevc->cur_pic->slice_type == P_SLICE) {
- hevc->gvs->p_decoded_frames++;
- } else if (hevc->cur_pic->slice_type == B_SLICE) {
- hevc->gvs->b_decoded_frames++;
+ if (hevc->cur_pic != NULL) {
+ if (hevc->cur_pic->slice_type == I_SLICE) {
+ hevc->gvs->i_decoded_frames++;
+ } else if (hevc->cur_pic->slice_type == P_SLICE) {
+ hevc->gvs->p_decoded_frames++;
+ } else if (hevc->cur_pic->slice_type == B_SLICE) {
+ hevc->gvs->b_decoded_frames++;
+ }
}
hevc_update_gvs(hevc);
memcpy(&tmp4x, hevc->gvs, sizeof(struct vdec_info));
return 0;
}
+#ifdef VDEC_FCC_SUPPORT
+static void fcc_discard_mode_process(struct vdec_s *vdec, struct hevc_state_s *hevc)
+{
+ if (vdec->fcc_status == AGAIN_STATUS &&
+ hevc->param.p.slice_type == I_SLICE) {
+ vdec->stream_offset = READ_VREG(HEVC_SHIFT_BYTE_COUNT);
+ hevc_print(hevc, PRINT_FLAG_VDEC_DETAIL,
+ "[%d][FCC]: Notify stream_offset: %u\n",
+ vdec->id, vdec->stream_offset);
+
+ vdec_wakeup_fcc_poll(vdec);
+ amhevc_stop();
+ vdec->fcc_status = WAIT_MSG_STATUS;
+ hevc->dec_result = DEC_RESULT_AGAIN;
+ vdec_schedule_work(&hevc->work);
+ } else if (vdec->fcc_status == DISCARD_STATUS ||
+ hevc->param.p.slice_type != I_SLICE) {
+ hevc_print(hevc, PRINT_FLAG_VDEC_DETAIL,
+ "[%d][FCC]: Discard current gop and to find next gop!\n",
+ vdec->id);
+
+ amhevc_stop();
+ vdec->fcc_status = AGAIN_STATUS;
+ hevc->dec_result = DEC_RESULT_DISCARD_DATA;
+ vdec_schedule_work(&hevc->work);
+ }
+}
+
+static int vh265_fcc_process(struct vdec_s *vdec, struct hevc_state_s *hevc)
+{
+ if (input_stream_based(vdec)) {
+ switch (vdec->fcc_mode) {
+ case FCC_DISCARD_MODE:
+ fcc_discard_mode_process(vdec, hevc);
+ return 1;
+ case FCC_DEC_MODE:
+ hevc_print(hevc, PRINT_FLAG_VDEC_DETAIL,
+ "[%d][FCC]: Current is Dec mode.\n", vdec->id);
+ break;
+ case FCC_BUTT:
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+#endif
+
static irqreturn_t vh265_isr_thread_fn(int irq, void *data)
{
struct hevc_state_s *hevc = (struct hevc_state_s *) data;
check_head_error(hevc);
#endif
}
+#ifdef VDEC_FCC_SUPPORT
+ if (vh265_fcc_process(vdec, hevc) > 0)
+ return IRQ_HANDLED;
+#endif
if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR_MORE) {
hevc_print(hevc, 0,
"rpm_param: (%d)\n", hevc->slice_idx);
}
}
#endif
+
if (hevc->mmu_enable && ((hevc->double_write_mode & 0x10) == 0)) {
hevc->used_4k_num =
READ_VREG(HEVC_SAO_MMU_STATUS) >> 16;
}
hevc_print(hevc, 0, "%s: force exit end\n",
__func__);
+ } else if (hevc->dec_result == DEC_RESULT_DISCARD_DATA) {
+ mutex_lock(&hevc->chunks_mutex);
+ vdec_vframe_dirty(hw_to_vdec(hevc), hevc->chunk);
+ hevc->chunk = NULL;
+ mutex_unlock(&hevc->chunks_mutex);
}
if (hevc->stat & STAT_VDEC_RUN) {
vh265_work_implement(hevc, vdec, 1);
}
+#ifdef VDEC_FCC_SUPPORT
+static void vh265_wakeup_fcc_poll(struct vdec_s *vdec)
+{
+ amstream_wakeup_fcc_poll(vdec);
+}
+#endif
static int vh265_hw_ctx_restore(struct hevc_state_s *hevc)
{
int tvp = vdec_secure(hw_to_vdec(hevc)) ?
CODEC_MM_FLAGS_TVP : 0;
bool ret = 0;
+#ifdef VDEC_FCC_SUPPORT
+ int get_msg = 1;
+#endif
if (step == 0x12)
return 0;
else if (step == 0x11)
}
}
+#ifdef VDEC_FCC_SUPPORT
+ get_msg = vdec_has_get_fcc_new_msg(vdec);
+ ret &= get_msg;
+#endif
if (ret)
not_run_ready[hevc->index] = 0;
else
pdata->irq_handler = vh265_irq_cb;
pdata->threaded_irq_handler = vh265_threaded_irq_cb;
pdata->dump_state = vh265_dump_state;
-
+#ifdef VDEC_FCC_SUPPORT
+ pdata->wakeup_fcc_poll = vh265_wakeup_fcc_poll;
+#endif
hevc->index = pdev->id;
hevc->m_ins_flag = 1;
#define DEC_RESULT_EOS 5
#define DEC_RESULT_GET_DATA 6
#define DEC_RESULT_GET_DATA_RETRY 7
+#define DEC_RESULT_DISCARD_DATA 8
#define DEC_DECODE_TIMEOUT 0x21
#define DECODE_ID(hw) (hw_to_vdec(hw)->id)
#define PRINT_FLAG_USERDATA_DETAIL 0x2000
#define PRINT_FLAG_TIMEOUT_STATUS 0x4000
#define PRINT_FLAG_V4L_DETAIL 0x8000
+#define PRINT_FLAG_FCC_STATUS 0x10000
#define IGNORE_PARAM_FROM_CONFIG 0x8000000
return ret;
}
+#ifdef VDEC_FCC_SUPPORT
+static void fcc_discard_mode_process(struct vdec_s *vdec)
+{
+ struct vdec_mpeg12_hw_s *hw =
+ (struct vdec_mpeg12_hw_s *)(vdec->private);
+ int wrap_count;
+ u32 rp;
+ u32 first_ptr;
+
+ if (vdec->fcc_status == AGAIN_STATUS) {
+ wrap_count = READ_VREG(VLD_MEM_VIFIFO_WRAP_COUNT);
+ rp = READ_VREG(VLD_MEM_VIFIFO_RP);
+ first_ptr = vdec->vbuf.ext_buf_addr;
+
+ vdec->stream_offset = rp + vdec->input.size * wrap_count - first_ptr;
+ debug_print(DECODE_ID(hw), PRINT_FLAG_FCC_STATUS,
+ "[%d][FCC]: Notify stream_offset: %d\n",
+ vdec->id, vdec->stream_offset);
+
+ debug_print(DECODE_ID(hw), PRINT_FLAG_FCC_STATUS,
+ "[%d][FCC]: rp : 0x%x size: 0x%x wrap_count:%d first_ptr: 0x%x\n",
+ vdec->id, rp, vdec->input.size, wrap_count, first_ptr);
+ vdec_wakeup_fcc_poll(vdec);
+ vdec->fcc_status = WAIT_MSG_STATUS;
+ hw->dec_result = DEC_RESULT_AGAIN;
+ vdec_schedule_work(&hw->work);
+ } else if (vdec->fcc_status == DISCARD_STATUS) {
+ debug_print(DECODE_ID(hw), PRINT_FLAG_FCC_STATUS,
+ "[%d][FCC]: Discard current gop and to find next gop!\n",
+ vdec->id);
+ vdec->fcc_status = AGAIN_STATUS;
+ hw->dec_result = DEC_RESULT_DISCARD_DATA;
+ vdec_schedule_work(&hw->work);
+ }
+}
+
+static int vmpeg2_fcc_process(struct vdec_s *vdec)
+{
+ struct vdec_mpeg12_hw_s *hw =
+ (struct vdec_mpeg12_hw_s *)(vdec->private);
+
+ if (input_stream_based(vdec)) {
+ switch (vdec->fcc_mode) {
+ case FCC_DISCARD_MODE:
+ fcc_discard_mode_process(vdec);
+ return 1;
+ case FCC_DEC_MODE:
+ debug_print(DECODE_ID(hw), PRINT_FLAG_FCC_STATUS,
+ "[%d][FCC]: Current is Dec mode.\n", vdec->id);
+ break;
+ case FCC_BUTT:
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+#endif
static irqreturn_t vmpeg12_isr_thread_fn(struct vdec_s *vdec, int irq)
{
"[vdec_kpi][%s] First I frame coming.\n",
__func__);
}
+
+#ifdef VDEC_FCC_SUPPORT
+ if (vmpeg2_fcc_process(vdec) > 0) {
+ WRITE_VREG(AV_SCRATCH_G, 0);
+ return IRQ_HANDLED;
+ }
+#endif
if (hw->is_used_v4l) {
int frame_width = READ_VREG(MREG_PIC_WIDTH);
int frame_height = READ_VREG(MREG_PIC_HEIGHT);
debug_print(DECODE_ID(hw), 0,
"%s: end of stream, num %d(%d)\n",
__func__, hw->disp_num, hw->dec_num);
+ } else if (hw->dec_result == DEC_RESULT_DISCARD_DATA) {
+ hw->dec_again_cnt = 0;
+ vdec_vframe_dirty(vdec, hw->chunk);
+ hw->chunk = NULL;
}
if (hw->stat & STAT_VDEC_RUN) {
amvdec_stop();
return 0;
}
+#ifdef VDEC_FCC_SUPPORT
+static void vmmpeg2_wakeup_fcc_poll(struct vdec_s *vdec)
+{
+ amstream_wakeup_fcc_poll(vdec);
+}
+#endif
+
static unsigned long run_ready(struct vdec_s *vdec, unsigned long mask)
{
struct vdec_mpeg12_hw_s *hw =
hw->buffer_not_ready++;
return 0;
}
+
+#ifdef VDEC_FCC_SUPPORT
+ if (!vdec_has_get_fcc_new_msg(vdec)) {
+ hw->buffer_not_ready++;
+ return 0;
+ }
+#endif
+
hw->not_run_ready = 0;
hw->buffer_not_ready = 0;
if (vdec->parallel_dec == 1)
amvdec_start();
hw->stat |= STAT_VDEC_RUN;
hw->init_flag = 1;
+
mod_timer(&hw->check_timer, jiffies + CHECK_INTERVAL);
}
pdata->reset_userdata_fifo = vmmpeg2_reset_userdata_fifo;
pdata->wakeup_userdata_poll = vmmpeg2_wakeup_userdata_poll;
+#ifdef VDEC_FCC_SUPPORT
+ pdata->wakeup_fcc_poll = vmmpeg2_wakeup_fcc_poll;
+#endif
+
if (pdata->use_vfm_path) {
snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE,
VFM_DEC_PROVIDER_NAME);
static unsigned int debug_trace_num = 16 * 20;
static int step_mode;
static unsigned int clk_config;
-
+#ifdef VDEC_FCC_SUPPORT
+static int fcc_debug;
+static void vdec_fcc_jump_back(struct vdec_s *vdec);
+#endif
/*
* 0x1 : sched_priority to MAX_RT_PRIO -1.
* 0x2 : always reload firmware.
}
EXPORT_SYMBOL(vdec_stream_skip_data);
-
-
/*
*get next frame from input chain
*/
while (READ_VREG(VLD_MEM_SWAP_CTL) & (1<<7))
;
WRITE_VREG(VLD_MEM_SWAP_CTL, 0);
+#ifdef VDEC_FCC_SUPPORT
+ vdec_fcc_jump_back(vdec);
+#endif
/* restore wrap count */
WRITE_VREG(VLD_MEM_VIFIFO_WRAP_COUNT,
& (1<<7))
;
WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 0);
-
+#ifdef VDEC_FCC_SUPPORT
+ vdec_fcc_jump_back(vdec);
+#endif
/* restore stream offset */
WRITE_VREG(HEVC_SHIFT_BYTE_COUNT,
input->stream_cookie);
if (dev_name == NULL)
return -ENODEV;
- pr_info("vdec_init, dev_name:%s, vdec_type=%s\n",
- dev_name, vdec_type_str(vdec));
+ pr_info("vdec_init, dev_name:%s, vdec_type=%s id = %d\n",
+ dev_name, vdec_type_str(vdec), vdec->id);
/*
*todo: VFM patch control should be configurable,
vdec->sys_info->height);
/* vdec is now ready to be active */
vdec_set_status(vdec, VDEC_STATUS_DISCONNECTED);
+
+#ifdef VDEC_FCC_SUPPORT
+ init_waitqueue_head(&vdec->jump_back_wq);
+ mutex_init(&vdec->jump_back_mutex);
+ vdec->fcc_mode = FCC_BUTT;
+ vdec->fcc_status = STATUS_BUTT;
+#endif
return 0;
error:
if (vdec->vbuf.ops && !vdec->master)
vdec->vbuf.ops->release(&vdec->vbuf);
- pr_debug("vdec_release instance %p, total %d\n", vdec,
- atomic_read(&vdec_core->vdec_nr));
+ pr_debug("vdec_release instance %p, total %d id = %d\n", vdec,
+ atomic_read(&vdec_core->vdec_nr), vdec->id);
mutex_lock(&vdec_mutex);
vdec_core->vdec_resouce_status &= ~BIT(vdec->frame_base_video_path);
}
EXPORT_SYMBOL(vdec_set_profile_level);
+#ifdef VDEC_FCC_SUPPORT
+int vdec_wakeup_fcc_poll(struct vdec_s *vdec)
+{
+ if (vdec) {
+ if (vdec->wakeup_fcc_poll)
+ vdec->wakeup_fcc_poll(vdec);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(vdec_wakeup_fcc_poll);
+
+int vdec_has_get_fcc_new_msg(struct vdec_s *vdec)
+{
+ int ret = 1;
+
+ if (vdec == NULL) {
+ pr_info("Error, invalid vdec instance!\n");
+ return 0;
+ }
+
+ if (input_stream_based(vdec)) {
+ if (vdec->fcc_mode == FCC_DISCARD_MODE &&
+ vdec->fcc_status == STATUS_BUTT) {
+ ret = 1;
+ vdec->fcc_status = AGAIN_STATUS;
+ } else if (vdec->fcc_mode == FCC_DEC_MODE &&
+ vdec->fcc_status != SWITCH_DONE_STATUS) {
+ ret = 1;
+
+ if (wait_event_interruptible_timeout(vdec->jump_back_wq, vdec->jump_back_done | vdec->jump_back_error,
+ HZ / 2) <= 0) {
+ pr_info("[%d][FCC]: Error! Wait jump back wp timeout 500ms!\n",
+ vdec->id);
+ vdec->fcc_status = SWITCH_DONE_STATUS;
+ } else {
+ if (fcc_debug_enable())
+ pr_info("[%d][FCC]: jump_back_done = %d\n",
+ vdec->id, vdec->jump_back_done);
+ if (vdec->jump_back_done) {
+ vdec->fcc_status = JUMP_BACK_STATUS;
+ vdec->jump_back_done = 0;
+ } else {
+ vdec->fcc_status = SWITCH_DONE_STATUS;
+ vdec->jump_back_error = 0;
+ }
+ }
+
+ if (fcc_debug_enable())
+ pr_info("[%d][FCC]: It is DEC mode now! fcc_status: %d\n",
+ vdec->id, vdec->fcc_status);
+ } else if (vdec->fcc_mode == FCC_DISCARD_MODE &&
+ !vdec->fcc_new_msg) {
+ if (vdec->fcc_status == WAIT_MSG_STATUS) {
+ if (fcc_debug_enable())
+ pr_info("[%d][FCC]: Wait msg!\n", vdec->id);
+ ret = 0;
+ } else {
+ if (fcc_debug_enable())
+ pr_info("[%d][FCC]: Continue to find header!\n", vdec->id);
+ ret = 1;
+ }
+ } else if (vdec->fcc_new_msg) {
+ if (fcc_debug_enable())
+ pr_info("[%d][FCC]: Got discard msg!\n", vdec->id);
+ ret = 1;
+ vdec->fcc_new_msg = 0;
+ if (vdec->fcc_mode == FCC_DISCARD_MODE) {
+ vdec->fcc_status = DISCARD_STATUS;
+ }
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(vdec_has_get_fcc_new_msg);
+
+int fcc_debug_enable(void)
+{
+ return fcc_debug;
+}
+EXPORT_SYMBOL(fcc_debug_enable);
+
+static void vdec_fcc_jump_back(struct vdec_s *vdec)
+{
+ u32 cur_rp, set_rp;
+ struct vdec_input_s *input = &vdec->input;
+
+ if (fcc_debug_enable())
+ pr_info("[%d][FCC]: fcc_mode = %d fcc_status = %d\n",
+ vdec->id, vdec->fcc_mode, vdec->fcc_status);
+
+ if (input->target == VDEC_INPUT_TARGET_VLD) {
+ if (vdec->fcc_mode == FCC_DEC_MODE &&
+ vdec->fcc_status == JUMP_BACK_STATUS) {
+ set_rp = vdec->jump_back_rp;
+ cur_rp =READ_VREG(VLD_MEM_VIFIFO_RP);
+ input->stream_cookie = READ_VREG(VLD_MEM_VIFIFO_WRAP_COUNT);
+ if (cur_rp < set_rp) {
+ if (fcc_debug_enable())
+ pr_info("[%d][FCC]: Packet is wrapped! VLD_MEM_VIFIFO_WRAP_COUNT = %d\n",
+ vdec->id, input->stream_cookie);
+ input->stream_cookie = (input->stream_cookie - 1) < 0 ?
+ 0 : input->stream_cookie - 1;
+ }
+
+ WRITE_VREG(VLD_MEM_VIFIFO_CURR_PTR, set_rp);
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 1);
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0);
+
+ WRITE_VREG(VLD_MEM_SWAP_ADDR,
+ input->swap_page_phys);
+ WRITE_VREG(VLD_MEM_SWAP_CTL, 3);
+ while (READ_VREG(VLD_MEM_SWAP_CTL) & (1<<7))
+ ;
+ WRITE_VREG(VLD_MEM_SWAP_CTL, 0);
+ vdec->fcc_status = SWITCH_DONE_STATUS;
+ if (fcc_debug_enable()) {
+ pr_info("[%d][FCC]:Current VLD_MEM_VIFIFO_WRAP_COUNT = %d cur_rp = %x set_rp = %x\n",
+ vdec->id, input->stream_cookie, cur_rp, set_rp);
+ }
+ }
+ } else if (input->target == VDEC_INPUT_TARGET_HEVC) {
+ if (vdec->fcc_mode == FCC_DEC_MODE &&
+ vdec->fcc_status == JUMP_BACK_STATUS) {
+ set_rp = vdec->jump_back_rp;
+ cur_rp = READ_VREG(HEVC_STREAM_RD_PTR);
+ if (cur_rp < set_rp) {
+ input->stream_cookie = input->stream_cookie + set_rp - cur_rp - vdec->input.size;
+ } else {
+ input->stream_cookie = input->stream_cookie - cur_rp + set_rp;
+ }
+
+ WRITE_VREG(HEVC_STREAM_RD_PTR, set_rp);
+ vdec->fcc_status = SWITCH_DONE_STATUS;
+
+ WRITE_VREG(HEVC_STREAM_SWAP_ADDR,
+ input->swap_page_phys);
+ WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 3);
+
+ while (READ_VREG(HEVC_STREAM_SWAP_CTRL)
+ & (1<<7))
+ ;
+ WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 0);
+ }
+ }
+
+ return;
+}
+#endif
+
static int dump_mode;
static ssize_t dump_risc_mem_store(struct class *class,
struct class_attribute *attr,
return pbuf - buf;
}
+#ifdef VDEC_FCC_SUPPORT
+static ssize_t store_fcc_debug(struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned int val;
+ ssize_t ret;
+
+ ret = kstrtoint(buf, 0, &val);
+ if (ret != 0)
+ return -EINVAL;
+ fcc_debug = val;
+ return size;
+}
+
+static ssize_t show_fcc_debug(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", fcc_debug);
+}
+#endif
+
static struct class_attribute vdec_class_attrs[] = {
__ATTR_RO(amrisc_regs),
__ATTR_RO(dump_trace),
__ATTR_RO(level_idc),
__ATTR(vfm_path, S_IRUGO | S_IWUSR | S_IWGRP,
show_vdec_vfm_path, store_vdec_vfm_path),
+#ifdef VDEC_FCC_SUPPORT
+ __ATTR(fcc_debug, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_fcc_debug, store_fcc_debug),
+#endif
__ATTR_NULL
};
module_param(debug_vdetect, int, 0664);
MODULE_PARM_DESC(debug_vdetect, "\n debug_vdetect\n");
+#ifdef VDEC_FCC_SUPPORT
+module_param(fcc_debug, int, 0664);
+MODULE_PARM_DESC(fcc_debug, "\n fcc_debug\n");
+#endif
+
module_param(enable_stream_mode_multi_dec, int, 0664);
EXPORT_SYMBOL(enable_stream_mode_multi_dec);
MODULE_PARM_DESC(enable_stream_mode_multi_dec,
#define VDEC_FIFO_ALIGN 8
+#define VDEC_FCC_SUPPORT
+
enum vdec_type_e {
VDEC_1 = 0,
VDEC_HCODEC,
VDEC_NEED_HINT,
VDEC_HINTED,
};
+
+#ifdef VDEC_FCC_SUPPORT
+typedef enum {
+ DISCARD_STATUS = 1,
+ AGAIN_STATUS,
+ WAIT_MSG_STATUS,
+ SWITCHING_STATUS,
+ JUMP_BACK_STATUS,
+ SWITCH_DONE_STATUS,
+ STATUS_BUTT
+} FCC_STATUS;
+#endif
+
extern s32 vdec_request_threaded_irq(enum vdec_irq_num num,
irq_handler_t handler,
irq_handler_t thread_fn,
struct userdata_param_t *puserdata_para);
void (*reset_userdata_fifo)(struct vdec_s *vdec, int bInit);
void (*wakeup_userdata_poll)(struct vdec_s *vdec);
+#ifdef VDEC_FCC_SUPPORT
+ void (*wakeup_fcc_poll)(struct vdec_s *vdec);
+#endif
/* private */
void *private; /* decoder per instance specific data */
#ifdef VDEC_DEBUG_SUPPORT
bool hdr10p_data_valid;
u32 profile_idc;
u32 level_idc;
+#ifdef VDEC_FCC_SUPPORT
+ enum fcc_mode_e fcc_mode;
+ u32 stream_offset;
+ int fcc_new_msg;
+ FCC_STATUS fcc_status;
+ wait_queue_head_t jump_back_wq;
+ struct mutex jump_back_mutex;
+ u32 jump_back_done;
+ u32 jump_back_error;
+ u32 jump_back_rp;
+#endif
};
/* common decoder vframe provider name to use default vfm path */
struct vdec_s *vdec_get_vdec_by_id(int vdec_id);
+#ifdef VDEC_FCC_SUPPORT
+int vdec_wakeup_fcc_poll(struct vdec_s *vdec);
+int vdec_has_get_fcc_new_msg(struct vdec_s *vdec);
+int fcc_debug_enable(void);
+#endif
+
#ifdef VDEC_DEBUG_SUPPORT
extern void vdec_set_step_mode(void);
#endif
struct device *amports_get_dma_device(void);
struct device *get_codec_cma_device(void);
+void amstream_wakeup_fcc_poll(struct vdec_s *vdec);
+
#endif
static ssize_t amstream_mprm_write
(struct file *file, const char *buf, size_t count, loff_t *ppos);
#endif
+#ifdef VDEC_FCC_SUPPORT
+static unsigned int amstream_offset_poll
+(struct file *file, poll_table *wait_table);
+#endif
static const struct file_operations vbuf_fops = {
.owner = THIS_MODULE,
.open = amstream_open,
.release = amstream_release,
+#ifdef VDEC_FCC_SUPPORT
+ .poll = amstream_offset_poll,
+#endif
.write = amstream_vbuf_write,
.unlocked_ioctl = amstream_ioctl,
#ifdef CONFIG_COMPAT
static DEFINE_MUTEX(userdata_mutex);
+#ifdef VDEC_FCC_SUPPORT
+static void amstream_fcc_get_one_slot(struct vdec_s *vdec);
+static void amstream_fcc_release_one_slot(struct vdec_s *vdec);
+
+#define MAX_FCC_CHANNEL_NUM 5
+
+typedef struct {
+ struct mutex mutex;
+ wait_queue_head_t offset_wait;
+ u32 offset[MAX_FCC_CHANNEL_NUM];
+ u32 offset_ready_flag[MAX_FCC_CHANNEL_NUM];
+ int used[MAX_FCC_CHANNEL_NUM];
+ int id[MAX_FCC_CHANNEL_NUM];
+} st_fcc;
+
+st_fcc fcc;
+static u32 fcc_enable;
+#endif
+
static struct stream_port_s ports[] = {
{
.name = "amstream_vbuf",
/*fallthrough*/
case 0: /*release all */
case 3:
+#ifdef VDEC_FCC_SUPPORT
+ if (fcc_enable & 0x1)
+ amstream_fcc_release_one_slot(vdec);
+#endif
if (vdec->slave)
slave = vdec->slave;
vdec_release(vdec);
goto err;
}
}
+#ifdef VDEC_FCC_SUPPORT
+ if (fcc_enable & 0x1)
+ amstream_fcc_get_one_slot(vdec);
+#endif
return 0;
err:
return retVal;
}
+#ifdef VDEC_FCC_SUPPORT
+static unsigned int amstream_offset_poll(struct file *file,
+ poll_table *wait_table)
+{
+ struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
+ struct vdec_s *vdec = priv->vdec;
+ int fd_match = 0;
+ int i;
+
+ poll_wait(file, &fcc.offset_wait, wait_table);
+ mutex_lock(&fcc.mutex);
+ for (i = 0; i < MAX_FCC_CHANNEL_NUM; i++) {
+ if (fcc.id[i] == vdec->id && fcc.offset_ready_flag[i] == 1) {
+ fd_match = 1;
+ if (fcc_debug_enable())
+ pr_info("i = %d vdec->id = %d offset_ready_flag = %d\n",
+ i, vdec->id, fcc.offset_ready_flag[i]);
+ break;
+ }
+ }
+
+ if (fd_match) {
+ mutex_unlock(&fcc.mutex);
+ return POLLIN | POLLRDNORM;
+ }
+
+ mutex_unlock(&fcc.mutex);
+ return 0;
+}
+
+void amstream_wakeup_fcc_poll(struct vdec_s *vdec)
+{
+ int i;
+
+ if (vdec == NULL) {
+ pr_info("Error, invalid vdec instance!\n");
+ return;
+ }
+
+ mutex_lock(&fcc.mutex);
+
+ for (i = 0; i < MAX_FCC_CHANNEL_NUM; i++) {
+ if (fcc.id[i] == vdec->id) {
+ fcc.offset[i] = vdec->stream_offset;
+ fcc.offset_ready_flag[i] = 1;
+ if (fcc_debug_enable())
+ pr_info("i = %d vdec->id = %d stream_offset = %d\n",
+ i, vdec->id, vdec->stream_offset);
+ break;
+ }
+ }
+
+ mutex_unlock(&fcc.mutex);
+
+ wake_up_interruptible(&fcc.offset_wait);
+}
+EXPORT_SYMBOL(amstream_wakeup_fcc_poll);
+
+static void amstream_fcc_init(void)
+{
+ int i;
+
+ init_waitqueue_head(&fcc.offset_wait);
+ mutex_init(&fcc.mutex);
+
+ for (i = 0; i < MAX_FCC_CHANNEL_NUM; i++) {
+ fcc.offset_ready_flag[i] = 0;
+ fcc.id[i] = -1;
+ fcc.used[i] = 0;
+ }
+
+ return;
+}
+
+static void amstream_fcc_get_one_slot(struct vdec_s *vdec)
+{
+ int i;
+
+ mutex_lock(&fcc.mutex);
+ for (i = 0; i < MAX_FCC_CHANNEL_NUM; i++) {
+ if (!fcc.used[i]) {
+ fcc.offset_ready_flag[i] = 0;
+ fcc.id[i] = vdec->id;
+ fcc.used[i] = 1;
+ break;
+ }
+ }
+ mutex_unlock(&fcc.mutex);
+
+ if (i >= MAX_FCC_CHANNEL_NUM)
+ pr_info("Error, no free fcc slot\n");
+
+ return;
+}
+
+static void amstream_fcc_release_one_slot(struct vdec_s *vdec)
+{
+ int i;
+
+ mutex_lock(&fcc.mutex);
+ for (i = 0; i < MAX_FCC_CHANNEL_NUM; i++) {
+ if (fcc.used[i] && vdec->id == fcc.id[i]) {
+ fcc.offset_ready_flag[i] = 0;
+ fcc.id[i] = -1;
+ fcc.used[i] = 0;
+ break;
+ }
+ }
+ mutex_unlock(&fcc.mutex);
+
+ if (i >= MAX_FCC_CHANNEL_NUM)
+ pr_info("Error, no fcc slot matched to release\n");
+
+ return;
+}
+#endif
+
static int amstream_open(struct inode *inode, struct file *file)
{
s32 i;
{
struct stream_port_s *this = priv->port;
long r = 0;
-
+#ifdef VDEC_FCC_SUPPORT
+ int i;
+#endif
struct am_ioctl_parm parm;
if (copy_from_user
case AMSTREAM_GET_FREED_HANDLE:
parm.data_32 = vdec_input_get_freed_handle(priv->vdec);
break;
+ case AMSTREAM_GET_OFFSET:
+#ifdef VDEC_FCC_SUPPORT
+ mutex_lock(&fcc.mutex);
+ for (i = 0; i < MAX_FCC_CHANNEL_NUM; i++) {
+ if (priv->vdec->id == fcc.id[i] &&
+ fcc.offset_ready_flag[i]) {
+ parm.data_32 = fcc.offset[i];
+ fcc.offset_ready_flag[i] = 0;
+ break;
+ }
+ }
+ mutex_unlock(&fcc.mutex);
+ if (i >= MAX_FCC_CHANNEL_NUM)
+ pr_info("Error, Get offset fail!\n");
+#endif
+ break;
default:
r = -ENOIOCTLCMD;
break;
pr_debug("no drmmode\n");
}
break;
+ case AMSTREAM_SET_WORKMODE:
+ case AMSTREAM_SET_FCC_MODE:
+#ifdef VDEC_FCC_SUPPORT
+ if (parm.cmd == AMSTREAM_SET_FCC_MODE) {
+ priv->vdec->fcc_new_msg = 1;
+ if (priv->vdec->fcc_mode == FCC_DEC_MODE) {
+ priv->vdec->fcc_new_msg = 0;
+ if (fcc_debug_enable())
+ pr_info("[%d][FCC]: Current is dec mode, ignore discard message!\n",
+ priv->vdec->id);
+ break;
+ }
+ }
+
+ if (parm.data_32 == FCC_DISCARD_MODE) {
+ if (fcc_debug_enable())
+ pr_info("[%d][FCC]: Set discard pic mode!\n",
+ priv->vdec->id);
+ if ((this->type & PORT_TYPE_VIDEO) &&
+ (priv->vdec)) {
+ priv->vdec->fcc_mode = FCC_DISCARD_MODE;
+ }
+ } else if (parm.data_32 == FCC_DEC_MODE) {
+ if ((this->type & PORT_TYPE_VIDEO) &&
+ (priv->vdec)) {
+ priv->vdec->fcc_mode = FCC_DEC_MODE;
+ }
+ if (fcc_debug_enable()) {
+ pr_info("[%d][FCC]: Set dec pic mode!\n", priv->vdec->id);
+ }
+ } else {
+ pr_info("Para error! Unknow FCC Mode! vdec id: %d\n", priv->vdec->id);
+ }
+#endif
+ break;
case AMSTREAM_SET_APTS: {
unsigned int pts;
/*prealloc fetch buf to avoid no continue buffer later...*/
stbuf_fetch_init();
REG_PATH_CONFIGS("media.amports", amports_configs);
-
+#ifdef VDEC_FCC_SUPPORT
+ if (fcc_enable & 0x1)
+ amstream_fcc_init();
+#endif
/* poweroff the decode core because dos can not be reset when reboot */
if (get_cpu_major_id() == AM_MESON_CPU_MAJOR_ID_G12A)
vdec_power_reset();
MODULE_PARM_DESC(force_dv_mode,
"\n force_dv_mode \n");
+#ifdef VDEC_FCC_SUPPORT
+module_param(fcc_enable, uint, 0664);
+MODULE_PARM_DESC(fcc_enable,
+ "\n fcc_enable \n");
+#endif
+
module_param(def_4k_vstreambuf_sizeM, uint, 0664);
MODULE_PARM_DESC(def_4k_vstreambuf_sizeM,
"\nDefault video Stream buf size for 4K MByptes\n");
}
EXPORT_SYMBOL(stream_buffer_set_ext_buf);
+#ifdef VDEC_FCC_SUPPORT
+void fcc_wakeup_jump_back(struct vdec_s *vdec,
+ struct stream_buffer_metainfo *meta)
+{
+ u32 first_ptr;
+ u32 round_down_size = 0;
+ if (fcc_debug_enable())
+ pr_info("[%d][FCC]: jump_back_done = %d jump_back_flag = %d stbuf_pktaddr = 0x%x size = 0x%x\n",
+ vdec->id, vdec->jump_back_done, meta->jump_back_flag,
+ meta->stbuf_pktaddr, meta->stbuf_size);
+ if (vdec->fcc_mode == FCC_DEC_MODE && vdec->fcc_status != SWITCH_DONE_STATUS) {
+ mutex_lock(&vdec->jump_back_mutex);
+ if (meta->jump_back_flag && !vdec->jump_back_done) {
+ if (vdec->input.target == VDEC_INPUT_TARGET_HEVC)
+ round_down_size = 0x80;
+ else if (vdec->input.target == VDEC_INPUT_TARGET_VLD)
+ round_down_size = 0x100;
+
+ if (vdec->vbuf.ext_buf_addr > (meta->stbuf_pktaddr - round_down_size))
+ first_ptr = vdec->vbuf.ext_buf_addr;
+ else {
+ first_ptr = round_down(meta->stbuf_pktaddr, round_down_size);
+ }
+ vdec->jump_back_done = 1;
+ vdec->jump_back_rp = first_ptr;
+ wake_up_interruptible(&vdec->jump_back_wq);
+ } else if (!vdec->jump_back_done && !vdec->jump_back_error) {
+ vdec->jump_back_error = 1;
+ wake_up_interruptible(&vdec->jump_back_wq);
+ }
+ mutex_unlock(&vdec->jump_back_mutex);
+ }
+
+ return;
+}
+#endif
+
void stream_buffer_meta_write(struct stream_buf_s *stbuf,
struct stream_buffer_metainfo *meta)
{
u32 wp = stbuf->ops->get_wp(stbuf);
if ((stbuf->stream_offset == 0) &&
- (wp == stbuf->ext_buf_addr) &&
+ /*(wp == stbuf->ext_buf_addr) &&*/ /*For fcc, when switching to dec mode it doesn't meet*/
(meta->stbuf_pktaddr > stbuf->ext_buf_addr)) {
struct vdec_s *vdec = container_of(stbuf, struct vdec_s, vbuf);
u32 first_ptr;
stbuf->ops->set_wp(stbuf, wp);
stbuf->stream_offset += meta->stbuf_pktsize;
+
+#ifdef VDEC_FCC_SUPPORT
+ fcc_wakeup_jump_back(container_of(stbuf, struct vdec_s, vbuf), meta);
+#endif
/*
pr_debug("%s, update wp 0x%x + sz 0x%x --> 0x%x, stream_offset 0x%x\n",
__func__, meta->stbuf_pktaddr, meta->stbuf_pktsize, wp, stbuf->stream_offset);
};
u32 stbuf_flag;
u32 stbuf_private;
- u32 reserved[16];
+ u32 jump_back_flag;
+ u32 reserved[15];
};
struct stream_buffer_status {