author | shihong.zheng <shihong.zheng@amlogic.com> | 2020-03-19 05:11:57 (GMT) |
---|---|---|
committer | Shen Liu <shen.liu@amlogic.com> | 2020-03-24 10:59:02 (GMT) |
commit | 8cc771eb8b9b67f9cc4999e119d3148c8136bdf3 (patch) | |
tree | 87792c4a982055ad322208d74fd4d40b50be515a | |
parent | 64e9372b25ce5e713672bf478b6078fca08781a0 (diff) | |
download | media_modules-8cc771eb8b9b67f9cc4999e119d3148c8136bdf3.zip media_modules-8cc771eb8b9b67f9cc4999e119d3148c8136bdf3.tar.gz media_modules-8cc771eb8b9b67f9cc4999e119d3148c8136bdf3.tar.bz2 |
mpeg12: update mpeg12 multi instance code. [1/1]
PD#SWPL-22463
Problem:
mpeg12 video play failed. timeout repeatly and
crash reboot.
Solution:
1. optmized mpeg2 multi instance code.
2. fix first frame two times output and last frame do not flush output;
3. do not config and alloc bmmu buf repeatly;
4. set error skip 2, no mosaic output;
5. fix resolution switch error;
6. reduce timeout trig time in amvdec. avoid softirq long error.
Verify:
u212
Change-Id: Ided6392f429392f1af0abf66d4026e70d49c0c4d
Signed-off-by: shihong.zheng <shihong.zheng@amlogic.com>
-rw-r--r-- | drivers/frame_provider/decoder/mpeg12/vmpeg12_multi.c | 1114 | ||||
-rw-r--r-- | drivers/frame_provider/decoder/utils/amvdec.c | 18 | ||||
-rw-r--r-- | firmware/video_ucode.bin | 104 |
3 files changed, 663 insertions, 573 deletions
diff --git a/drivers/frame_provider/decoder/mpeg12/vmpeg12_multi.c b/drivers/frame_provider/decoder/mpeg12/vmpeg12_multi.c index b6f4a9f..224d366 100644 --- a/drivers/frame_provider/decoder/mpeg12/vmpeg12_multi.c +++ b/drivers/frame_provider/decoder/mpeg12/vmpeg12_multi.c @@ -71,17 +71,17 @@ #define MREG_WAIT_BUFFER AV_SCRATCH_E #define MREG_FATAL_ERROR AV_SCRATCH_F +#define GET_SLICE_TYPE(type) ("IPB##"[((type&PICINFO_TYPE_MASK)>>16)&0x3]) #define PICINFO_ERROR 0x80000000 #define PICINFO_TYPE_MASK 0x00030000 #define PICINFO_TYPE_I 0x00000000 #define PICINFO_TYPE_P 0x00010000 #define PICINFO_TYPE_B 0x00020000 - -#define GET_SLICE_TYPE(type) ("IPB###"[(type&PICINFO_TYPE_MASK)>>16]) #define PICINFO_PROG 0x8000 #define PICINFO_RPT_FIRST 0x4000 #define PICINFO_TOP_FIRST 0x2000 #define PICINFO_FRAME 0x1000 + #define TOP_FIELD 0x1000 #define BOTTOM_FIELD 0x2000 #define FRAME_PICTURE 0x3000 @@ -105,17 +105,20 @@ #define DEFAULT_MEM_SIZE (32*SZ_1M) static u32 buf_size = 32 * 1024 * 1024; static int pre_decode_buf_level = 0x800; +static int start_decode_buf_level = 0x4000; static u32 dec_control; -static u32 error_frame_skip_level; +static u32 error_frame_skip_level = 2; static u32 stat; static u32 udebug_flag; static unsigned int radr; static unsigned int rval; +static u32 without_display_mode; + #define VMPEG12_DEV_NUM 9 static unsigned int max_decode_instance_num = VMPEG12_DEV_NUM; static unsigned int max_process_time[VMPEG12_DEV_NUM]; -static unsigned int decode_timeout_val = 100; +static unsigned int decode_timeout_val = 200; #define INCPTR(p) ptr_atomic_wrap_inc(&p) #define DEC_CONTROL_FLAG_FORCE_2500_720_576_INTERLACE 0x0002 @@ -132,6 +135,15 @@ static unsigned int decode_timeout_val = 100; #define NV21 #endif +#define AGAIN_HAS_THRESHOLD + +#ifdef AGAIN_HAS_THRESHOLD +u32 again_threshold; +#endif + +/* +#define DUMP_USER_DATA +*/ enum { FRAME_REPEAT_TOP, @@ -156,6 +168,16 @@ static struct vframe_s *vmpeg_vf_get(void *); static void vmpeg_vf_put(struct vframe_s *, void *); static int vmpeg_vf_states(struct vframe_states *states, void *); static int vmpeg_event_cb(int type, void *data, void *private_data); +struct pic_info_t { + u32 buffer_info; + u32 index; + u32 offset; + u32 width; + u32 height; + u32 pts; + u64 pts64; + bool pts_valid; +}; struct vdec_mpeg12_hw_s { spinlock_t lock; @@ -164,6 +186,7 @@ struct vdec_mpeg12_hw_s { DECLARE_KFIFO(display_q, struct vframe_s *, VF_POOL_SIZE); struct vframe_s vfpool[VF_POOL_SIZE]; s32 vfbuf_use[DECODE_BUFFER_NUM_MAX]; + s32 ref_use[DECODE_BUFFER_NUM_MAX]; u32 frame_width; u32 frame_height; u32 frame_dur; @@ -186,17 +209,17 @@ struct vdec_mpeg12_hw_s { u32 reg_vcop_ctrl_reg; u32 reg_mb_info; u32 reg_signal_type; - u32 frame_num; + u32 dec_num; + u32 disp_num; struct timer_list check_timer; u32 decode_timeout_count; - u32 start_process_time; + unsigned long int start_process_time; u32 last_vld_level; u32 eos; - u32 buffer_info[DECODE_BUFFER_NUM_MAX]; - u32 pts[DECODE_BUFFER_NUM_MAX]; - u64 pts64[DECODE_BUFFER_NUM_MAX]; - bool pts_valid[DECODE_BUFFER_NUM_MAX]; + struct pic_info_t pics[DECODE_BUFFER_NUM_MAX]; u32 canvas_spec[DECODE_BUFFER_NUM_MAX]; + u64 lastpts64; + u32 last_chunk_pts; struct canvas_config_s canvas_config[DECODE_BUFFER_NUM_MAX][2]; struct dec_sysinfo vmpeg12_amstream_dec_info; @@ -223,8 +246,16 @@ struct vdec_mpeg12_hw_s { u32 get_num; u32 drop_frame_count; u32 buffer_not_ready; + u32 ratio_control; int frameinfo_enable; struct firmware_s *fw; + u32 canvas_mode; +#ifdef AGAIN_HAS_THRESHOLD + u32 pre_parser_wr_ptr; + u8 next_again_flag; +#endif + u32 userdata_wp_ctx; + int tvp_flag; }; static void vmpeg12_local_init(struct vdec_mpeg12_hw_s *hw); static int vmpeg12_hw_ctx_restore(struct vdec_mpeg12_hw_s *hw); @@ -251,6 +282,9 @@ unsigned int mpeg12_debug_mask = 0xff; #define PRINT_FRAMEBASE_DATA 0x0400 #define PRINT_FLAG_VDEC_STATUS 0x0800 #define PRINT_FLAG_PARA_DATA 0x1000 +#define PRINT_FLAG_USERDATA_DETAIL 0x2000 +#define PRINT_FLAG_TIMEOUT_STATUS 0x4000 + int debug_print(int index, int debug_flag, const char *fmt, ...) @@ -297,7 +331,7 @@ static u32 find_buffer(struct vdec_mpeg12_hw_s *hw) u32 i; for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) { - if (hw->vfbuf_use[i] == 0) + if ((hw->vfbuf_use[i] == 0) && (hw->ref_use[i] == 0)) return i; } @@ -318,31 +352,18 @@ static u32 spec_to_index(struct vdec_mpeg12_hw_s *hw, u32 spec) static void set_frame_info(struct vdec_mpeg12_hw_s *hw, struct vframe_s *vf) { - unsigned ar_bits; - u32 temp; + u32 ar_bits; u32 buffer_index = vf->index; -#ifdef CONFIG_AM_VDEC_MPEG12_LOG - bool first = (hw->frame_width == 0) && (hw->frame_height == 0); -#endif - temp = READ_VREG(MREG_PIC_WIDTH); - if (temp > 1920 || temp == 0) - vf->width = hw->frame_width = 1920; - else - vf->width = hw->frame_width = temp; - - temp = READ_VREG(MREG_PIC_HEIGHT); - if (temp > 1088 || temp == 0) - vf->height = hw->frame_height = 1088; - else - vf->height = hw->frame_height = temp; + vf->width = hw->pics[buffer_index].width; + vf->height = hw->pics[buffer_index].height; if (hw->frame_dur > 0) vf->duration = hw->frame_dur; else { vf->duration = hw->frame_dur = frame_rate_tab[(READ_VREG(MREG_SEQ_INFO) >> 4) & 0xf]; - schedule_work(&hw->notify_work); + vdec_schedule_work(&hw->notify_work); } ar_bits = READ_VREG(MREG_SEQ_INFO) & 0xf; @@ -358,6 +379,8 @@ static void set_frame_info(struct vdec_mpeg12_hw_s *hw, struct vframe_s *vf) else vf->ratio_control = 0; + hw->ratio_control = vf->ratio_control; + vf->canvas0Addr = vf->canvas1Addr = -1; vf->plane_num = 2; @@ -367,7 +390,6 @@ static void set_frame_info(struct vdec_mpeg12_hw_s *hw, struct vframe_s *vf) vf->canvas1_config[0] = hw->canvas_config[buffer_index][0]; vf->canvas1_config[1] = hw->canvas_config[buffer_index][1]; - debug_print(DECODE_ID(hw), PRINT_FLAG_PARA_DATA, "mpeg2dec: w(%d), h(%d), dur(%d), dur-ES(%d)\n", hw->frame_width, hw->frame_height, hw->frame_dur, @@ -396,20 +418,26 @@ static bool error_skip(struct vdec_mpeg12_hw_s *hw, return false; } -static inline void vmpeg12_save_hw_context(struct vdec_mpeg12_hw_s *hw) +static inline void vmpeg12_save_hw_context(struct vdec_mpeg12_hw_s *hw, u32 reg) { - hw->seqinfo = READ_VREG(MREG_SEQ_INFO); - hw->reg_pic_width = READ_VREG(MREG_PIC_WIDTH); - hw->reg_pic_height = READ_VREG(MREG_PIC_HEIGHT); - hw->reg_mpeg1_2_reg = READ_VREG(MPEG1_2_REG); - hw->reg_pic_head_info = READ_VREG(PIC_HEAD_INFO); - hw->reg_f_code_reg = READ_VREG(F_CODE_REG); - hw->reg_slice_ver_pos_pic_type = READ_VREG(SLICE_VER_POS_PIC_TYPE); - hw->reg_vcop_ctrl_reg = READ_VREG(VCOP_CTRL_REG); - hw->reg_mb_info = READ_VREG(MB_INFO); - hw->reg_signal_type = READ_VREG(AV_SCRATCH_H); - debug_print(DECODE_ID(hw), PRINT_FLAG_PARA_DATA, - "signal_type = %x", hw->reg_signal_type); + if (reg == 3) { + hw->ctx_valid = 0; + //pr_info("%s, hw->userdata_wp_ctx %d\n", __func__, hw->userdata_wp_ctx); + } else { + hw->seqinfo = READ_VREG(MREG_SEQ_INFO); + hw->reg_pic_width = READ_VREG(MREG_PIC_WIDTH); + hw->reg_pic_height = READ_VREG(MREG_PIC_HEIGHT); + hw->reg_mpeg1_2_reg = READ_VREG(MPEG1_2_REG); + hw->reg_pic_head_info = READ_VREG(PIC_HEAD_INFO); + hw->reg_f_code_reg = READ_VREG(F_CODE_REG); + hw->reg_slice_ver_pos_pic_type = READ_VREG(SLICE_VER_POS_PIC_TYPE); + hw->reg_vcop_ctrl_reg = READ_VREG(VCOP_CTRL_REG); + hw->reg_mb_info = READ_VREG(MB_INFO); + hw->reg_signal_type = READ_VREG(AV_SCRATCH_H); + debug_print(DECODE_ID(hw), PRINT_FLAG_PARA_DATA, + "signal_type = %x", hw->reg_signal_type); + hw->ctx_valid = 1; + } } #if 0 static void userdata_push_do_work(struct work_struct *work) @@ -440,14 +468,210 @@ static void userdata_push_do_work(struct work_struct *work) WRITE_VREG(MREG_BUFFEROUT, 0); } #endif -static irqreturn_t vmpeg12_isr_thread_fn(struct vdec_s *vdec, int irq) + +static int prepare_display_buf(struct vdec_mpeg12_hw_s *hw, + struct pic_info_t *pic) { - u32 reg, info, seqinfo, offset, pts, pts_valid = 0; + u32 field_num = 0, i; + u32 first_field_type = 0, type = 0; struct vframe_s *vf = NULL; - u32 index; + u32 index = pic->index; + u32 info = pic->buffer_info; + struct vdec_s *vdec = hw_to_vdec(hw); + + if (hw == NULL || pic == NULL) + return -1; + + /*user_data_ready_notify(hw, pic->pts, pic->pts_valid);*/ +#ifdef NV21 + type = VIDTYPE_VIU_NV21; +#endif + + if (hw->frame_prog & PICINFO_PROG) { + field_num = 1; + type |= VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; + } else { +#ifdef INTERLACE_SEQ_ALWAYS + /* once an interlace seq, force interlace, to make di easy. */ + hw->dec_control |= DEC_CONTROL_FLAG_FORCE_SEQ_INTERLACE; +#endif + hw->frame_rpt_state = FRAME_REPEAT_NONE; + + first_field_type = (info & PICINFO_TOP_FIRST) ? + VIDTYPE_INTERLACE_TOP : VIDTYPE_INTERLACE_BOTTOM; + field_num = (info & PICINFO_RPT_FIRST) ? 3 : 2; + } + + for (i = 0; i < field_num; i++) { + if (kfifo_get(&hw->newframe_q, &vf) == 0) { + debug_print(DECODE_ID(hw), PRINT_FLAG_ERROR, + "fatal error, no available buffer slot."); + hw->dec_result = DEC_RESULT_ERROR; + vdec_schedule_work(&hw->work); + return -1; + } + hw->vfbuf_use[index]++; + vf->index = index; + set_frame_info(hw, vf); + if (field_num > 1) { + vf->duration = vf->duration / field_num; + vf->duration_pulldown = (field_num == 3) ? + (vf->duration >> 1):0; + if (i > 0) + type = VIDTYPE_VIU_NV21; + if (i == 1) /* second field*/ + type |= (first_field_type == VIDTYPE_INTERLACE_TOP) ? + VIDTYPE_INTERLACE_BOTTOM : VIDTYPE_INTERLACE_TOP; + else + type |= (first_field_type == VIDTYPE_INTERLACE_TOP) ? + VIDTYPE_INTERLACE_TOP : VIDTYPE_INTERLACE_BOTTOM; + } else { + if ((hw->seqinfo & SEQINFO_EXT_AVAILABLE) && + (hw->seqinfo & SEQINFO_PROG)) { + if (info & PICINFO_RPT_FIRST) { + if (info & PICINFO_TOP_FIRST) + vf->duration *= 3; + else + vf->duration *= 2; + } + vf->duration_pulldown = 0; + } else { + vf->duration_pulldown = + (info & PICINFO_RPT_FIRST) ? + vf->duration >> 1 : 0; + } + } + vf->duration += vf->duration_pulldown; + vf->type = type; + vf->signal_type = hw->reg_signal_type; + vf->orientation = 0; + if (i > 0) { + vf->pts = 0; + vf->pts_us64 = 0; + } else { + vf->pts = (pic->pts_valid) ? pic->pts : 0; + vf->pts_us64 = (pic->pts_valid) ? pic->pts64 : 0; + } + vf->type_original = vf->type; + + if ((error_skip(hw, pic->buffer_info, vf)) || + ((hw->first_i_frame_ready == 0) && + ((PICINFO_TYPE_MASK & pic->buffer_info) != + PICINFO_TYPE_I))) { + hw->drop_frame_count++; + hw->vfbuf_use[index]--; + kfifo_put(&hw->newframe_q, + (const struct vframe_s *)vf); + } else { + debug_print(DECODE_ID(hw), PRINT_FLAG_TIMEINFO, + "%s, vf: %p, num[%d]: %d(%c), dur: %d, type: %x, pts: %d(%lld)\n", + __func__, vf, i, hw->disp_num, GET_SLICE_TYPE(info), + vf->duration, vf->type, vf->pts, vf->pts_us64); + hw->disp_num++; + /* + if (i == 0) + decoder_do_frame_check(hw_to_vdec(hw), vf); + vdec->vdec_fps_detec(vdec->id); + */ + vf->mem_handle = + decoder_bmmu_box_get_mem_handle( + hw->mm_blk_handle, index); + /*pr_info("%s: vf %p, index %d, use %d\n", __func__, + vf, index, hw->vfbuf_use[index]);*/ + kfifo_put(&hw->display_q, + (const struct vframe_s *)vf); + if (without_display_mode == 0) { + vf_notify_receiver(vdec->vf_provider_name, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + } else + vmpeg_vf_put(vmpeg_vf_get(vdec), vdec); + + + } + } + return 0; +} + +static void force_interlace_check(struct vdec_mpeg12_hw_s *hw) +{ + if ((hw->dec_control & + DEC_CONTROL_FLAG_FORCE_2500_720_576_INTERLACE) && + (hw->frame_width == 720) && + (hw->frame_height == 576) && + (hw->frame_dur == 3840)) { + hw->frame_prog = 0; + } else if ((hw->dec_control + & DEC_CONTROL_FLAG_FORCE_3000_704_480_INTERLACE) && + (hw->frame_width == 704) && + (hw->frame_height == 480) && + (hw->frame_dur == 3200)) { + hw->frame_prog = 0; + } else if ((hw->dec_control + & DEC_CONTROL_FLAG_FORCE_2500_704_576_INTERLACE) && + (hw->frame_width == 704) && + (hw->frame_height == 576) && + (hw->frame_dur == 3840)) { + hw->frame_prog = 0; + } else if ((hw->dec_control + & DEC_CONTROL_FLAG_FORCE_2500_544_576_INTERLACE) && + (hw->frame_width == 544) && + (hw->frame_height == 576) && + (hw->frame_dur == 3840)) { + hw->frame_prog = 0; + } else if ((hw->dec_control + & DEC_CONTROL_FLAG_FORCE_2500_480_576_INTERLACE) && + (hw->frame_width == 480) && + (hw->frame_height == 576) && + (hw->frame_dur == 3840)) { + hw->frame_prog = 0; + } else if (hw->dec_control + & DEC_CONTROL_FLAG_FORCE_SEQ_INTERLACE) { + hw->frame_prog = 0; + } + +} + +static int update_reference(struct vdec_mpeg12_hw_s *hw, + int index) +{ + hw->ref_use[index]++; + if (hw->refs[1] == -1) { + hw->refs[1] = index; + /* + * first pic need output to show + * usecnt do not decrease. + */ + } else if (hw->refs[0] == -1) { + hw->refs[0] = hw->refs[1]; + hw->refs[1] = index; + /* second pic do not output */ + index = DECODE_BUFFER_NUM_MAX; + } else { + hw->ref_use[hw->refs[0]]--; + hw->refs[0] = hw->refs[1]; + hw->refs[1] = index; + index = hw->refs[0]; + } + return index; +} + +static bool is_ref_error(struct vdec_mpeg12_hw_s *hw) +{ + if ((hw->pics[hw->refs[0]].buffer_info & PICINFO_ERROR) || + (hw->pics[hw->refs[1]].buffer_info & PICINFO_ERROR)) + return 1; + return 0; +} + + +static irqreturn_t vmpeg12_isr_thread_fn(struct vdec_s *vdec, int irq) +{ + u32 reg, index, info, seqinfo, offset, pts, tmp; u64 pts_us64 = 0; + struct pic_info_t *new_pic, *disp_pic; struct vdec_mpeg12_hw_s *hw = - (struct vdec_mpeg12_hw_s *)(vdec->private); + (struct vdec_mpeg12_hw_s *)(vdec->private); if (READ_VREG(AV_SCRATCH_M) != 0 && (debug_enable & PRINT_FLAG_UCODE_DETAIL)) { @@ -463,429 +687,158 @@ static irqreturn_t vmpeg12_isr_thread_fn(struct vdec_s *vdec, int irq) return IRQ_HANDLED; } - reg = READ_VREG(MREG_BUFFEROUT); + reg = READ_VREG(AV_SCRATCH_J); + if (reg & (1<<16)) { + /*vdec_schedule_work(&hw->userdata_push_work);*/ + WRITE_VREG(AV_SCRATCH_J, 0); + return IRQ_HANDLED; + } - if ((reg >> 16) == 0xfe) { - /*pr_info("%s,%d\n",__func__,__LINE__);*/ - /*vdec_schedule_work(&userdata_push_work);*/ - WRITE_VREG(MREG_BUFFEROUT, 0); - } else if (reg == 2) { + reg = READ_VREG(MREG_BUFFEROUT); + if (reg == 2) { /*timeout when decoding next frame*/ - - debug_print(DECODE_ID(hw), PRINT_FLAG_VLD_DETAIL, - "ammvdec_mpeg12: Insufficient data\n"); debug_print(DECODE_ID(hw), PRINT_FLAG_VLD_DETAIL, - "level=%x, vtl=%x,bcnt=%d\n", - READ_VREG(VLD_MEM_VIFIFO_LEVEL), - READ_VREG(VLD_MEM_VIFIFO_CONTROL), - READ_VREG(VIFF_BIT_CNT)); - if (input_frame_based(vdec)) - vmpeg12_save_hw_context(hw); - else { + "mmpeg12: lack data, lvl=%x ctrl=%x bcnt=%x\n", + READ_VREG(VLD_MEM_VIFIFO_LEVEL), + READ_VREG(VLD_MEM_VIFIFO_CONTROL), + READ_VREG(VIFF_BIT_CNT)); + + if (vdec_frame_based(vdec)) { + /* vmpeg12_save_hw_context(hw); */ + reset_process_time(hw); + hw->dec_result = DEC_RESULT_DONE; + vdec_schedule_work(&hw->work); + } else { hw->dec_result = DEC_RESULT_AGAIN; vdec_schedule_work(&hw->work); + /*userdata_pushed_drop(hw);*/ + reset_process_time(hw); } return IRQ_HANDLED; } else { reset_process_time(hw); + info = READ_VREG(MREG_PIC_INFO); offset = READ_VREG(MREG_FRAME_OFFSET); index = spec_to_index(hw, READ_VREG(REC_CANVAS_ADDR)); seqinfo = READ_VREG(MREG_SEQ_INFO); + if ((info & PICINFO_PROG) == 0 && - (info & FRAME_PICTURE_MASK) != FRAME_PICTURE) + (info & FRAME_PICTURE_MASK) != FRAME_PICTURE) hw->first_i_frame_ready = 1; /* for field struct case*/ - debug_print(DECODE_ID(hw), PRINT_FLAG_DEC_DETAIL, - "struct: %d %x\n", (info & PICINFO_FRAME), info); - if (index >= DECODE_BUFFER_NUM_MAX) { + if (index >= DECODE_BUFFER_NUM_MAX) { debug_print(DECODE_ID(hw), PRINT_FLAG_ERROR, - "invalid buffer index,index=%d\n", - index); + "mmpeg12: invalid buf index: %d\n", index); hw->dec_result = DEC_RESULT_ERROR; vdec_schedule_work(&hw->work); return IRQ_HANDLED; } - + hw->dec_num++; hw->dec_result = DEC_RESULT_DONE; + new_pic = &hw->pics[index]; + tmp = READ_VREG(MREG_PIC_WIDTH); + if ((tmp > 1920) || (tmp == 0)) { + new_pic->width = 1920; + hw->frame_width = 1920; + } else { + new_pic->width = tmp; + hw->frame_width = tmp; + } - /*debug_print(DECODE_ID(hw), PRINT_FLAG_TIMEINFO, - "ammvdec_mpeg12: error = 0x%x, offset = 0x%x\n", - info & PICINFO_ERROR, offset);*/ + tmp = READ_VREG(MREG_PIC_HEIGHT); + if ((tmp > 1088) || (tmp == 0)) { + new_pic->height = 1088; + hw->frame_height = 1088; + } else { + new_pic->height = tmp; + hw->frame_height = tmp; + } + new_pic->buffer_info = info; + new_pic->offset = offset; + new_pic->index = index; if (((info & PICINFO_TYPE_MASK) == PICINFO_TYPE_I) || ((info & PICINFO_TYPE_MASK) == PICINFO_TYPE_P)) { if (hw->chunk) { - hw->pts_valid[index] = hw->chunk->pts_valid; - hw->pts[index] = hw->chunk->pts; - hw->pts64[index] = hw->chunk->pts64; - - debug_print(DECODE_ID(hw), PRINT_FLAG_TIMEINFO, - "!!!cpts=%d,pts64=%lld,size=%d,offset=%d\n", - hw->pts[index], hw->pts64[index], - hw->chunk->size, hw->chunk->offset); - } else { - if (pts_lookup_offset_us64(PTS_TYPE_VIDEO, - offset, &pts, 0, &pts_us64) == 0) { - hw->pts_valid[index] = true; - hw->pts[index] = pts; - hw->pts64[index] = pts_us64; - } else { - hw->pts_valid[index] = false; + new_pic->pts_valid = hw->chunk->pts_valid; + new_pic->pts = hw->chunk->pts; + new_pic->pts64 = hw->chunk->pts64; + if (hw->last_chunk_pts == hw->chunk->pts) { + new_pic->pts_valid = 0; + debug_print(DECODE_ID(hw), PRINT_FLAG_TIMEINFO, + "pts invalid\n"); } + } else { + if (pts_lookup_offset_us64(PTS_TYPE_VIDEO, offset, + &pts, 0, &pts_us64) == 0) { + new_pic->pts_valid = true; + new_pic->pts = pts; + new_pic->pts64 = pts_us64; + } else + new_pic->pts_valid = false; } } else { - hw->pts_valid[index] = false; - } - /*if (frame_prog == 0) */ - { - hw->frame_prog = info & PICINFO_PROG; - if ((seqinfo & SEQINFO_EXT_AVAILABLE) - && (!(seqinfo & SEQINFO_PROG))) - hw->frame_prog = 0; - } - if ((hw->dec_control & - DEC_CONTROL_FLAG_FORCE_2500_720_576_INTERLACE) && - (hw->frame_width == 720) && - (hw->frame_height == 576) && - (hw->frame_dur == 3840)) { - hw->frame_prog = 0; - } else if ((hw->dec_control - & DEC_CONTROL_FLAG_FORCE_3000_704_480_INTERLACE) && - (hw->frame_width == 704) && - (hw->frame_height == 480) && - (hw->frame_dur == 3200)) { - hw->frame_prog = 0; - } else if ((hw->dec_control - & DEC_CONTROL_FLAG_FORCE_2500_704_576_INTERLACE) && - (hw->frame_width == 704) && - (hw->frame_height == 576) && - (hw->frame_dur == 3840)) { - hw->frame_prog = 0; - } else if ((hw->dec_control - & DEC_CONTROL_FLAG_FORCE_2500_544_576_INTERLACE) && - (hw->frame_width == 544) && - (hw->frame_height == 576) && - (hw->frame_dur == 3840)) { - hw->frame_prog = 0; - } else if ((hw->dec_control - & DEC_CONTROL_FLAG_FORCE_2500_480_576_INTERLACE) && - (hw->frame_width == 480) && - (hw->frame_height == 576) && - (hw->frame_dur == 3840)) { - hw->frame_prog = 0; - } else if (hw->dec_control - & DEC_CONTROL_FLAG_FORCE_SEQ_INTERLACE) { - hw->frame_prog = 0; + if (hw->chunk) + hw->last_chunk_pts = hw->chunk->pts; + new_pic->pts_valid = false; } - hw->buffer_info[index] = info; + debug_print(DECODE_ID(hw), PRINT_FLAG_RUN_FLOW, + "mmpeg12: new_pic=%d, ind=%d, info=%x, seq=%x, offset=%d\n", + hw->dec_num, index, info, seqinfo, offset); - debug_print(DECODE_ID(hw), PRINT_FLAG_DEC_DETAIL, - "ammvdec_mpeg12: decoded buffer %d, frame_type %c\n", - index, GET_SLICE_TYPE(info)); + hw->frame_prog = info & PICINFO_PROG; + if ((seqinfo & SEQINFO_EXT_AVAILABLE) && + ((seqinfo & SEQINFO_PROG) == 0)) + hw->frame_prog = 0; + force_interlace_check(hw); + + if (is_ref_error(hw)) { + if ((info & PICINFO_TYPE_MASK) == PICINFO_TYPE_B) + new_pic->buffer_info |= PICINFO_ERROR; + } - /* Buffer management - todo: add sequence-end flush */ if (((info & PICINFO_TYPE_MASK) == PICINFO_TYPE_I) || ((info & PICINFO_TYPE_MASK) == PICINFO_TYPE_P)) { - hw->vfbuf_use[index]++; - if (hw->refs[1] == -1) { - hw->refs[1] = index; - index = DECODE_BUFFER_NUM_MAX; - } else if (hw->refs[0] == -1) { - hw->refs[0] = hw->refs[1]; - hw->refs[1] = index; - index = hw->refs[0]; - } else { - hw->vfbuf_use[hw->refs[0]]--; - hw->refs[0] = hw->refs[1]; - hw->refs[1] = index; - index = hw->refs[0]; - } + index = update_reference(hw, index); } else { - /* if this is a B frame, then drop - (depending on if there are two reference frames) - or display immediately*/ + /* drop b frame before reference pic ready */ if (hw->refs[0] == -1) index = DECODE_BUFFER_NUM_MAX; - } - - vmpeg12_save_hw_context(hw); + vmpeg12_save_hw_context(hw, reg); if (index >= DECODE_BUFFER_NUM_MAX) { - debug_print(DECODE_ID(hw), 0, - "invalid buffer index,index=%d\n", index); - hw->dec_result = DEC_RESULT_ERROR; + if (hw->dec_num != 2) { + debug_print(DECODE_ID(hw), 0, + "mmpeg12: drop pic num %d, type %c, index %d, offset %x\n", + hw->dec_num, GET_SLICE_TYPE(info), index, offset); + hw->dec_result = DEC_RESULT_ERROR; + } vdec_schedule_work(&hw->work); return IRQ_HANDLED; } - info = hw->buffer_info[index]; - pts_valid = hw->pts_valid[index]; - pts = hw->pts[index]; - pts_us64 = hw->pts64[index]; + disp_pic = &hw->pics[index]; + info = hw->pics[index].buffer_info; + if (disp_pic->pts_valid && hw->lastpts64 == disp_pic->pts64) + disp_pic->pts_valid = false; + if (disp_pic->pts_valid) + hw->lastpts64 = disp_pic->pts64; if ((hw->first_i_frame_ready == 0) && ((info & PICINFO_TYPE_MASK) == PICINFO_TYPE_I) && ((info & PICINFO_ERROR) == 0)) hw->first_i_frame_ready = 1; - debug_print(DECODE_ID(hw), PRINT_FLAG_RUN_FLOW, - "ammvdec_mpeg12: display buffer %d, frame_type %c\n", - index, GET_SLICE_TYPE(info)); - if (hw->frame_prog & PICINFO_PROG) { - - seqinfo = READ_VREG(MREG_SEQ_INFO); - - if (kfifo_get(&hw->newframe_q, &vf) == 0) { - debug_print(DECODE_ID(hw), PRINT_FLAG_ERROR, - "fatal error, no available buffer slot."); - hw->dec_result = DEC_RESULT_ERROR; - vdec_schedule_work(&hw->work); - - return IRQ_HANDLED; - } - - vf->index = index; - set_frame_info(hw, vf); - vf->signal_type = hw->reg_signal_type; - -#ifdef NV21 - vf->type = - VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD | - VIDTYPE_VIU_NV21; -#else - vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; -#endif - if ((hw->seqinfo & SEQINFO_EXT_AVAILABLE) - && (hw->seqinfo & SEQINFO_PROG)) { - if (info & PICINFO_RPT_FIRST) { - if (info & PICINFO_TOP_FIRST) { - vf->duration = - vf->duration * 3; - /* repeat three times */ - } else { - vf->duration = - vf->duration * 2; - /* repeat two times */ - } - } - vf->duration_pulldown = 0; - /* no pull down */ - - } else { - vf->duration_pulldown = - (info & PICINFO_RPT_FIRST) ? - vf->duration >> 1 : 0; - } - - vf->duration += vf->duration_pulldown; - - vf->orientation = 0; - vf->pts = (pts_valid) ? pts : 0; - vf->pts_us64 = (pts_valid) ? pts_us64 : 0; - vf->type_original = vf->type; - hw->vfbuf_use[index]++; - - if ((error_skip(hw, info, vf)) || - ((hw->first_i_frame_ready == 0) - && ((PICINFO_TYPE_MASK & info) != - PICINFO_TYPE_I))) { - hw->drop_frame_count++; - hw->vfbuf_use[index]--; - kfifo_put(&hw->newframe_q, - (const struct vframe_s *)vf); - - } else { - debug_print(DECODE_ID(hw), PRINT_FLAG_TIMEINFO, - "cpts=%d,pts64=%lld\n", - vf->pts, vf->pts_us64); - kfifo_put(&hw->display_q, - (const struct vframe_s *)vf); - hw->frame_num++; - vf_notify_receiver(vdec->vf_provider_name, - VFRAME_EVENT_PROVIDER_VFRAME_READY, - NULL); - } - } -/*interlace temp*/ - else { - int first_field_type = (info & PICINFO_TOP_FIRST) ? - VIDTYPE_INTERLACE_TOP : - VIDTYPE_INTERLACE_BOTTOM; - -#ifdef INTERLACE_SEQ_ALWAYS - /* once an interlaced sequence exist, - always force interlaced type */ - /* to make DI easy. */ - hw->dec_control |= DEC_CONTROL_FLAG_FORCE_SEQ_INTERLACE; -#endif - - hw->frame_rpt_state = FRAME_REPEAT_NONE; - - if (kfifo_get(&hw->newframe_q, &vf) == 0) { - debug_print(DECODE_ID(hw), PRINT_FLAG_ERROR, - "fatal error, no available buffer slot."); - vdec_schedule_work(&hw->work); - return IRQ_HANDLED; - } - - hw->vfbuf_use[index] += 2; - vf->signal_type = hw->reg_signal_type; - vf->index = index; - set_frame_info(hw, vf); - vf->type = - (first_field_type == VIDTYPE_INTERLACE_TOP) ? - VIDTYPE_INTERLACE_TOP : - VIDTYPE_INTERLACE_BOTTOM; -#ifdef NV21 - vf->type |= VIDTYPE_VIU_NV21; -#endif - if (info & PICINFO_RPT_FIRST) - vf->duration /= 3; - else - vf->duration >>= 1; - vf->duration_pulldown = (info & PICINFO_RPT_FIRST) ? - vf->duration >> 1 : 0; - vf->duration += vf->duration_pulldown; - vf->orientation = 0; - vf->pts = (pts_valid) ? pts : 0; - vf->pts_us64 = (pts_valid) ? pts_us64 : 0; - - if ((error_skip(hw, info, vf)) || - ((hw->first_i_frame_ready == 0) - && ((PICINFO_TYPE_MASK & info) != - PICINFO_TYPE_I))) { - hw->vfbuf_use[index]--; - hw->drop_frame_count++; - kfifo_put(&hw->newframe_q, - (const struct vframe_s *)vf); - } else { - debug_print(DECODE_ID(hw), PRINT_FLAG_TIMEINFO, - "cpts0=%d,pts64=%lld,dur=%d, index %d , use %d\n", - vf->pts, vf->pts_us64, vf->duration, - vf->index, hw->vfbuf_use[index]); - kfifo_put(&hw->display_q, - (const struct vframe_s *)vf); - hw->frame_num++; - vf_notify_receiver(vdec->vf_provider_name, - VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); - } - - if (kfifo_get(&hw->newframe_q, &vf) == 0) { - debug_print(DECODE_ID(hw), PRINT_FLAG_ERROR, - "ammvdec_mpeg12: fatal error, no available buffer slot."); - - hw->vfbuf_use[index]--; - - vdec_schedule_work(&hw->work); - - return IRQ_HANDLED; - } - vf->signal_type = hw->reg_signal_type; - vf->index = index; - set_frame_info(hw, vf); - vf->type = (first_field_type == - VIDTYPE_INTERLACE_TOP) ? - VIDTYPE_INTERLACE_BOTTOM : - VIDTYPE_INTERLACE_TOP; -#ifdef NV21 - vf->type |= VIDTYPE_VIU_NV21; -#endif - if (info & PICINFO_RPT_FIRST) - vf->duration /= 3; - else - vf->duration >>= 1; - vf->duration_pulldown = (info & PICINFO_RPT_FIRST) ? - vf->duration >> 1 : 0; - vf->duration += vf->duration_pulldown; - vf->orientation = 0; - vf->pts = 0; - vf->pts_us64 = 0; - vf->type_original = vf->type; - if ((error_skip(hw, info, vf)) || - ((hw->first_i_frame_ready == 0) - && ((PICINFO_TYPE_MASK & info) != - PICINFO_TYPE_I))) { - hw->drop_frame_count++; - hw->vfbuf_use[index]--; - - kfifo_put(&hw->newframe_q, - (const struct vframe_s *)vf); - } else { - debug_print(DECODE_ID(hw), PRINT_FLAG_TIMEINFO, - "cpts1=%d,pts64=%lld,dur=%d index %d, used %d\n", - vf->pts, vf->pts_us64, vf->duration, vf->index, - hw->vfbuf_use[index]); - kfifo_put(&hw->display_q, - (const struct vframe_s *)vf); - hw->frame_num++; - vf_notify_receiver(vdec->vf_provider_name, - VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); - } + debug_print(DECODE_ID(hw), PRINT_FLAG_RUN_FLOW, + "mmpeg12: disp_pic=%d(%c), ind=%d, offst=%x, pts=(%d,%lld)(%d)\n", + hw->disp_num, GET_SLICE_TYPE(info), index, disp_pic->offset, + disp_pic->pts, disp_pic->pts64, disp_pic->pts_valid); - if (info & PICINFO_RPT_FIRST) { - if (kfifo_get(&hw->newframe_q, &vf) == 0) { - debug_print(DECODE_ID(hw), - PRINT_FLAG_ERROR, - "error, no available buffer slot."); - return IRQ_HANDLED; - } - hw->vfbuf_use[index]++; - vf->index = index; - set_frame_info(hw, vf); - vf->type = (first_field_type == - VIDTYPE_INTERLACE_TOP) ? - VIDTYPE_INTERLACE_TOP : - VIDTYPE_INTERLACE_BOTTOM; -#ifdef NV21 - vf->type |= VIDTYPE_VIU_NV21; -#endif - vf->duration /= 3; - vf->duration_pulldown = - (info & PICINFO_RPT_FIRST) ? - vf->duration >> 1 : 0; - vf->duration += vf->duration_pulldown; - vf->orientation = 0; - - vf->pts = 0; - vf->pts_us64 = 0; - if ((error_skip(hw, info, vf)) || - ((hw->first_i_frame_ready == 0) - && ((PICINFO_TYPE_MASK & info) - != PICINFO_TYPE_I))) { - hw->vfbuf_use[index]--; - hw->drop_frame_count++; - kfifo_put(&hw->newframe_q, - (const struct vframe_s *)vf); - } else { - hw->frame_num++; - debug_print(DECODE_ID(hw), - PRINT_FLAG_TIMEINFO, - "cpts2=%d,pts64=%lld,dur=%d index %d, used %d\n", - vf->pts, vf->pts_us64, vf->duration, - vf->index, hw->vfbuf_use[index]); - kfifo_put(&hw->display_q, - (const struct vframe_s *)vf); - vf_notify_receiver( - vdec->vf_provider_name, - VFRAME_EVENT_PROVIDER_VFRAME_READY, - NULL); - } - } - } + prepare_display_buf(hw, disp_pic); vdec_schedule_work(&hw->work); - - debug_print(DECODE_ID(hw), PRINT_FRAME_NUM, - "frame_num=%d\n", hw->frame_num); - if (hw->frame_num == 1) - debug_print(DECODE_ID(hw), PRINT_FRAME_NUM, - "frame_num==1\n"); - if (hw->frame_num == 1000) - debug_print(DECODE_ID(hw), PRINT_FRAME_NUM, - "frame_num==1000\n"); } return IRQ_HANDLED; @@ -894,7 +847,7 @@ static irqreturn_t vmpeg12_isr(struct vdec_s *vdec, int irq) { u32 info, offset; struct vdec_mpeg12_hw_s *hw = - (struct vdec_mpeg12_hw_s *)(vdec->private); + (struct vdec_mpeg12_hw_s *)(vdec->private); if (hw->eos) return IRQ_HANDLED; info = READ_VREG(MREG_PIC_INFO); @@ -941,87 +894,107 @@ static void wait_vmmpeg12_search_done(struct vdec_mpeg12_hw_s *hw) } while (1); } +static void flush_output(struct vdec_mpeg12_hw_s *hw) +{ + int index = hw->refs[1]; + + /* video only one frame need not flush. */ + if (hw->dec_num < 2) + return; + + if ((hw->refs[0] >= 0) && + (hw->refs[0] < DECODE_BUFFER_NUM_MAX)) + hw->ref_use[hw->refs[0]] = 0; + + if (index >= 0 && index < DECODE_BUFFER_NUM_MAX) { + hw->ref_use[index] = 0; + prepare_display_buf(hw, &hw->pics[index]); + } +} + static void vmpeg12_work(struct work_struct *work) { struct vdec_mpeg12_hw_s *hw = container_of(work, struct vdec_mpeg12_hw_s, work); struct vdec_s *vdec = hw_to_vdec(hw); + if (hw->dec_result != DEC_RESULT_DONE) debug_print(DECODE_ID(hw), PRINT_FLAG_RUN_FLOW, - "ammvdec_mpeg12: vmpeg_work,result=%d,status=%d\n", - hw->dec_result, hw_to_vdec(hw)->next_status); + "%s, result=%d, status=%d\n", __func__, + hw->dec_result, vdec->next_status); if (hw->dec_result == DEC_RESULT_DONE) { - if (!hw->ctx_valid) - hw->ctx_valid = 1; - - vdec_vframe_dirty(hw_to_vdec(hw), hw->chunk); - } else if (hw->dec_result == DEC_RESULT_AGAIN - && (hw_to_vdec(hw)->next_status != - VDEC_STATUS_DISCONNECTED)) { + vdec_vframe_dirty(vdec, hw->chunk); + hw->chunk = NULL; + } else if (hw->dec_result == DEC_RESULT_AGAIN && + (vdec->next_status != VDEC_STATUS_DISCONNECTED)) { /* stream base: stream buf empty or timeout frame base: vdec_prepare_input fail */ - if (!vdec_has_more_input(hw_to_vdec(hw))) { + if (!vdec_has_more_input(vdec)) { hw->dec_result = DEC_RESULT_EOS; vdec_schedule_work(&hw->work); return; } - } else if (hw->dec_result == DEC_RESULT_GET_DATA - && (hw_to_vdec(hw)->next_status != - VDEC_STATUS_DISCONNECTED)) { - if (!vdec_has_more_input(hw_to_vdec(hw))) { +#ifdef AGAIN_HAS_THRESHOLD + hw->next_again_flag = 1; +#endif + } else if (hw->dec_result == DEC_RESULT_GET_DATA && + vdec->next_status != VDEC_STATUS_DISCONNECTED) { + if (!vdec_has_more_input(vdec)) { hw->dec_result = DEC_RESULT_EOS; vdec_schedule_work(&hw->work); return; } debug_print(DECODE_ID(hw), PRINT_FLAG_VLD_DETAIL, - "%s DEC_RESULT_GET_DATA %x %x %x\n", - __func__, - READ_VREG(VLD_MEM_VIFIFO_LEVEL), - READ_VREG(VLD_MEM_VIFIFO_WP), - READ_VREG(VLD_MEM_VIFIFO_RP)); - vdec_vframe_dirty(hw_to_vdec(hw), hw->chunk); - vdec_clean_input(hw_to_vdec(hw)); + "%s DEC_RESULT_GET_DATA %x %x %x\n", + __func__, + READ_VREG(VLD_MEM_VIFIFO_LEVEL), + READ_VREG(VLD_MEM_VIFIFO_WP), + READ_VREG(VLD_MEM_VIFIFO_RP)); + vdec_vframe_dirty(vdec, hw->chunk); + hw->chunk = NULL; + vdec_clean_input(vdec); return; } else if (hw->dec_result == DEC_RESULT_FORCE_EXIT) { debug_print(DECODE_ID(hw), PRINT_FLAG_ERROR, - "%s: force exit\n", __func__); + "%s: force exit\n", __func__); if (hw->stat & STAT_ISR_REG) { amvdec_stop(); - /*disable mbox interrupt */ - WRITE_VREG(ASSIST_MBOX1_MASK, 0); vdec_free_irq(VDEC_IRQ_1, (void *)hw); hw->stat &= ~STAT_ISR_REG; } } else if (hw->dec_result == DEC_RESULT_EOS) { - pr_info("%s: end of stream\n", __func__); if (hw->stat & STAT_VDEC_RUN) { amvdec_stop(); hw->stat &= ~STAT_VDEC_RUN; } hw->eos = 1; - vdec_vframe_dirty(hw_to_vdec(hw), hw->chunk); - vdec_clean_input(hw_to_vdec(hw)); + vdec_vframe_dirty(vdec, hw->chunk); + hw->chunk = NULL; + vdec_clean_input(vdec); + flush_output(hw); + debug_print(DECODE_ID(hw), 0, + "%s: end of stream, num %d(%d)\n", + __func__, hw->disp_num, hw->dec_num); } if (hw->stat & STAT_VDEC_RUN) { amvdec_stop(); hw->stat &= ~STAT_VDEC_RUN; } + /*disable mbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_MASK, 0); wait_vmmpeg12_search_done(hw); if (vdec->parallel_dec == 1) - vdec_core_finish_run(hw_to_vdec(hw), CORE_MASK_VDEC_1); + vdec_core_finish_run(vdec, CORE_MASK_VDEC_1); else - vdec_core_finish_run(hw_to_vdec(hw), CORE_MASK_VDEC_1 | CORE_MASK_HEVC); + vdec_core_finish_run(vdec, CORE_MASK_VDEC_1 | CORE_MASK_HEVC); del_timer_sync(&hw->check_timer); hw->stat &= ~STAT_TIMER_ARM; - if (hw->vdec_cb) { - hw->vdec_cb(hw_to_vdec(hw), hw->vdec_cb_arg); - debug_print(DECODE_ID(hw), 0x80000, - "%s:\n", __func__); - } + if (hw->vdec_cb) + hw->vdec_cb(vdec, hw->vdec_cb_arg); } static struct vframe_s *vmpeg_vf_peek(void *op_arg) @@ -1051,15 +1024,48 @@ static struct vframe_s *vmpeg_vf_get(void *op_arg) return NULL; } +static bool vf_valid_check(struct vdec_mpeg12_hw_s *hw, + struct vframe_s *vf) { + int i; + + if (vf == NULL) + return false; + + for (i = 0; i < VF_POOL_SIZE; i++) { + if (vf == &hw->vfpool[i]) + return true; + } + /* + pr_info(" invalid vf been put, vf = %p\n", vf); + for (i = 0; i < VF_POOL_SIZE; i++) { + pr_info("www valid vf[%d]= %p \n", i, &hw->vfpool[i]); + } + */ + return false; +} + static void vmpeg_vf_put(struct vframe_s *vf, void *op_arg) { struct vdec_s *vdec = op_arg; - struct vdec_mpeg12_hw_s *hw = - (struct vdec_mpeg12_hw_s *)vdec->private; + struct vdec_mpeg12_hw_s *hw = NULL; + + if (vdec == NULL) + return; + hw = (struct vdec_mpeg12_hw_s *)vdec->private; + + if (vf_valid_check(hw, vf) == false) { + debug_print(DECODE_ID(hw), PRINT_FLAG_ERROR, + "vmpeg_vf_put invalid vf: %p\n", vf); + return ; + } + if (hw->vfbuf_use[vf->index] > 0) + hw->vfbuf_use[vf->index]--; - hw->vfbuf_use[vf->index]--; hw->put_num++; - kfifo_put(&hw->newframe_q, (const struct vframe_s *)vf); + /*pr_info("%s: vf: %p, index %d, use %d\n", __func__, + vf, vf->index, hw->vfbuf_use[vf->index]);*/ + kfifo_put(&hw->newframe_q, + (const struct vframe_s *)vf); } static int vmpeg_event_cb(int type, void *data, void *private_data) @@ -1072,7 +1078,7 @@ static int vmpeg_vf_states(struct vframe_states *states, void *op_arg) unsigned long flags; struct vdec_s *vdec = op_arg; struct vdec_mpeg12_hw_s *hw = - (struct vdec_mpeg12_hw_s *)vdec->private; + (struct vdec_mpeg12_hw_s *)vdec->private; spin_lock_irqsave(&hw->lock, flags); @@ -1110,6 +1116,7 @@ static int vmmpeg12_dec_status(struct vdec_s *vdec, struct vdec_info *vstatus) vstatus->total_data = gvs.total_data; vstatus->samp_cnt = gvs.samp_cnt; vstatus->offset = gvs.offset; + //vstatus->ratio_control = hw->ratio_control; snprintf(vstatus->vdec_name, sizeof(vstatus->vdec_name), "%s", DRIVER_NAME); @@ -1184,28 +1191,29 @@ static void vmpeg12_canvas_init(struct vdec_mpeg12_hw_s *hw) } hw->canvas_config[i][0].phy_addr = - decbuf_start; + decbuf_start; hw->canvas_config[i][0].width = - canvas_width; + canvas_width; hw->canvas_config[i][0].height = - canvas_height; + canvas_height; hw->canvas_config[i][0].block_mode = - CANVAS_BLKMODE_32X32; + hw->canvas_mode; + hw->canvas_config[i][0].endian = + (hw->canvas_mode == CANVAS_BLKMODE_LINEAR)?7:0; canvas_config_config(canvas_y(canvas), &hw->canvas_config[i][0]); hw->canvas_config[i][1].phy_addr = - decbuf_start + decbuf_y_size; - hw->canvas_config[i][1].width = - canvas_width; - hw->canvas_config[i][1].height = - canvas_height / 2; - hw->canvas_config[i][1].block_mode = - CANVAS_BLKMODE_32X32; + decbuf_start + decbuf_y_size; + hw->canvas_config[i][1].width = canvas_width; + hw->canvas_config[i][1].height = canvas_height / 2; + hw->canvas_config[i][1].block_mode = hw->canvas_mode; + hw->canvas_config[i][1].endian = + (hw->canvas_mode == CANVAS_BLKMODE_LINEAR)?7:0; canvas_config_config(canvas_u(canvas), - &hw->canvas_config[i][1]); + &hw->canvas_config[i][1]); } } return; @@ -1226,11 +1234,11 @@ static void vmpeg2_dump_state(struct vdec_s *vdec) ); debug_print(DECODE_ID(hw), 0, "is_framebase(%d), eos %d, state 0x%x, dec_result 0x%x dec_frm %d put_frm %d run %d not_run_ready %d,input_empty %d\n", - input_frame_based(vdec), + vdec_frame_based(vdec), hw->eos, hw->stat, hw->dec_result, - hw->frame_num, + hw->dec_num, hw->put_num, hw->run_count, hw->not_run_ready, @@ -1239,7 +1247,8 @@ static void vmpeg2_dump_state(struct vdec_s *vdec) for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) { debug_print(DECODE_ID(hw), 0, - "index %d, used %d\n", i, hw->vfbuf_use[i]); + "index %d, used %d, ref %d\n", i, + hw->vfbuf_use[i], hw->ref_use[i]); } if (vf_get_receiver(vdec->vf_provider_name)) { @@ -1253,13 +1262,13 @@ static void vmpeg2_dump_state(struct vdec_s *vdec) state); } debug_print(DECODE_ID(hw), 0, - "%s, newq(%d/%d), dispq(%d/%d) vf peek/get/put (%d/%d/%d),drop=%d, buffer_not_ready %d\n", + "%s, newq(%d/%d), dispq(%d/%d) vf pre/get/put (%d/%d/%d),drop=%d, buffer_not_ready %d\n", __func__, kfifo_len(&hw->newframe_q), VF_POOL_SIZE, kfifo_len(&hw->display_q), VF_POOL_SIZE, - hw->peek_num, + hw->disp_num, hw->get_num, hw->put_num, hw->drop_frame_count, @@ -1283,7 +1292,7 @@ static void vmpeg2_dump_state(struct vdec_s *vdec) debug_print(DECODE_ID(hw), 0, "PARSER_VIDEO_WP=0x%x\n", READ_PARSER_REG(PARSER_VIDEO_WP)); - if (input_frame_based(vdec) && + if (vdec_frame_based(vdec) && debug_enable & PRINT_FRAMEBASE_DATA ) { int jj; @@ -1304,15 +1313,14 @@ static void vmpeg2_dump_state(struct vdec_s *vdec) for (jj = 0; jj < hw->chunk->size; jj++) { if ((jj & 0xf) == 0) debug_print(DECODE_ID(hw), - PRINT_FRAMEBASE_DATA, + PRINT_FRAMEBASE_DATA, "%06x:", jj); debug_print(DECODE_ID(hw), PRINT_FRAMEBASE_DATA, "%02x ", data[jj]); if (((jj + 1) & 0xf) == 0) debug_print(DECODE_ID(hw), - PRINT_FRAMEBASE_DATA, - "\n"); + PRINT_FRAMEBASE_DATA, "\n"); } if (!hw->chunk->block->is_mapped) @@ -1333,18 +1341,19 @@ static void reset_process_time(struct vdec_mpeg12_hw_s *hw) } static void start_process_time(struct vdec_mpeg12_hw_s *hw) { - hw->decode_timeout_count = 2; + hw->decode_timeout_count = 10; hw->start_process_time = jiffies; } static void timeout_process(struct vdec_mpeg12_hw_s *hw) { struct vdec_s *vdec = hw_to_vdec(hw); + + reset_process_time(hw); amvdec_stop(); debug_print(DECODE_ID(hw), PRINT_FLAG_ERROR, - "%s decoder timeout, status=%d, level=%d\n", - __func__, vdec->status, READ_VREG(VLD_MEM_VIFIFO_LEVEL)); + "%s decoder timeout, status=%d, level=%d\n", + __func__, vdec->status, READ_VREG(VLD_MEM_VIFIFO_LEVEL)); hw->dec_result = DEC_RESULT_DONE; - reset_process_time(hw); hw->first_i_frame_ready = 0; vdec_schedule_work(&hw->work); } @@ -1365,9 +1374,7 @@ static void check_timer_func(unsigned long arg) radr = 0; } - if (debug_enable == 0 && - (input_frame_based(vdec) || - (READ_VREG(VLD_MEM_VIFIFO_LEVEL) > 0x100)) && + if (((debug_enable & PRINT_FLAG_TIMEOUT_STATUS) == 0) && (timeout_val > 0) && (hw->start_process_time > 0) && ((1000 * (jiffies - hw->start_process_time) / HZ) @@ -1393,20 +1400,32 @@ static void check_timer_func(unsigned long arg) static int vmpeg12_hw_ctx_restore(struct vdec_mpeg12_hw_s *hw) { - u32 index; + u32 index, i; index = find_buffer(hw); if (index >= DECODE_BUFFER_NUM_MAX) return -1; - vmpeg12_canvas_init(hw); + if (!hw->init_flag) + vmpeg12_canvas_init(hw); + else { + WRITE_VREG(MREG_CO_MV_START, hw->buf_start); + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) { + canvas_config_config(canvas_y(hw->canvas_spec[i]), + &hw->canvas_config[i][0]); + canvas_config_config(canvas_u(hw->canvas_spec[i]), + &hw->canvas_config[i][1]); + } + } /* prepare REF0 & REF1 points to the past two IP buffers prepare REC_CANVAS_ADDR and ANC2_CANVAS_ADDR points to the output buffer*/ WRITE_VREG(MREG_REF0, - (hw->refs[1] == -1) ? 0xffffffff : hw->canvas_spec[hw->refs[0]]); + (hw->refs[0] == -1) ? 0xffffffff : + hw->canvas_spec[hw->refs[0]]); WRITE_VREG(MREG_REF1, - (hw->refs[0] == -1) ? 0xffffffff : hw->canvas_spec[hw->refs[1]]); + (hw->refs[1] == -1) ? 0xffffffff : + hw->canvas_spec[hw->refs[1]]); WRITE_VREG(REC_CANVAS_ADDR, hw->canvas_spec[index]); WRITE_VREG(ANC2_CANVAS_ADDR, hw->canvas_spec[index]); @@ -1432,6 +1451,8 @@ static int vmpeg12_hw_ctx_restore(struct vdec_mpeg12_hw_s *hw) WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); /* clear buffer IN/OUT registers */ WRITE_VREG(MREG_BUFFEROUT, 0); + /* enable mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_MASK, 1); /* set reference width and height */ if ((hw->frame_width != 0) && (hw->frame_height != 0)) WRITE_VREG(MREG_CMD, @@ -1439,19 +1460,18 @@ static int vmpeg12_hw_ctx_restore(struct vdec_mpeg12_hw_s *hw) else WRITE_VREG(MREG_CMD, 0); - debug_print(DECODE_ID(hw), PRINT_FLAG_RESTORE, - "0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", - hw->frame_width, hw->frame_height, hw->seqinfo, - hw->reg_f_code_reg, hw->reg_slice_ver_pos_pic_type, - hw->reg_mb_info); + "0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + hw->frame_width, hw->frame_height, hw->seqinfo, + hw->reg_f_code_reg, hw->reg_slice_ver_pos_pic_type, + hw->reg_mb_info); WRITE_VREG(MREG_PIC_WIDTH, hw->reg_pic_width); WRITE_VREG(MREG_PIC_HEIGHT, hw->reg_pic_height); WRITE_VREG(MREG_SEQ_INFO, hw->seqinfo); WRITE_VREG(F_CODE_REG, hw->reg_f_code_reg); WRITE_VREG(SLICE_VER_POS_PIC_TYPE, - hw->reg_slice_ver_pos_pic_type); + hw->reg_slice_ver_pos_pic_type); WRITE_VREG(MB_INFO, hw->reg_mb_info); WRITE_VREG(VCOP_CTRL_REG, hw->reg_vcop_ctrl_reg); WRITE_VREG(AV_SCRATCH_H, hw->reg_signal_type); @@ -1464,7 +1484,10 @@ static int vmpeg12_hw_ctx_restore(struct vdec_mpeg12_hw_s *hw) #ifdef NV21 SET_VREG_MASK(MDEC_PIC_DC_CTRL, 1<<17); #endif - + /* + if (!hw->ctx_valid) + WRITE_VREG(AV_SCRATCH_J, hw->userdata_wp_ctx); + */ if (hw->chunk) { /*frame based input*/ WRITE_VREG(MREG_INPUT, @@ -1490,8 +1513,10 @@ static void vmpeg12_local_init(struct vdec_mpeg12_hw_s *hw) kfifo_put(&hw->newframe_q, (const struct vframe_s *)vf); } - for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) { hw->vfbuf_use[i] = 0; + hw->ref_use[i] = 0; + } if (hw->mm_blk_handle) { @@ -1505,7 +1530,8 @@ static void vmpeg12_local_init(struct vdec_mpeg12_hw_s *hw) MAX_BMMU_BUFFER_NUM, 4 + PAGE_SHIFT, CODEC_MM_FLAGS_CMA_CLEAR | - CODEC_MM_FLAGS_FOR_VDECODER); + CODEC_MM_FLAGS_FOR_VDECODER | + hw->tvp_flag); hw->eos = 0; hw->frame_width = hw->frame_height = 0; hw->frame_dur = hw->frame_prog = 0; @@ -1515,7 +1541,8 @@ static void vmpeg12_local_init(struct vdec_mpeg12_hw_s *hw) hw->dec_control &= DEC_CONTROL_INTERNAL_MASK; hw->refs[0] = -1; hw->refs[1] = -1; - hw->frame_num = 0; + hw->disp_num = 0; + hw->dec_num = 0; hw->put_num = 0; hw->run_count = 0; hw->not_run_ready = 0; @@ -1525,6 +1552,7 @@ static void vmpeg12_local_init(struct vdec_mpeg12_hw_s *hw) hw->drop_frame_count = 0; hw->buffer_not_ready = 0; hw->start_process_time = 0; + hw->init_flag = 0; hw->error_frame_skip_level = error_frame_skip_level; if (dec_control) hw->dec_control = dec_control; @@ -1557,7 +1585,6 @@ static s32 vmpeg12_init(struct vdec_mpeg12_hw_s *hw) INIT_WORK(&hw->notify_work, vmpeg12_notify_work); amvdec_enable(); - init_timer(&hw->check_timer); hw->check_timer.data = (unsigned long)hw; hw->check_timer.function = check_timer_func; @@ -1567,7 +1594,6 @@ static s32 vmpeg12_init(struct vdec_mpeg12_hw_s *hw) hw->stat |= STAT_ISR_REG; hw->buf_start = 0; - hw->init_flag = 0; WRITE_VREG(DECODE_STOP_POS, udebug_flag); return 0; @@ -1598,6 +1624,23 @@ static unsigned long run_ready(struct vdec_s *vdec, unsigned long mask) } } +#ifdef AGAIN_HAS_THRESHOLD + if (hw->next_again_flag&& + (!vdec_frame_based(vdec))) { + u32 parser_wr_ptr = + READ_PARSER_REG(PARSER_VIDEO_WP); + if (parser_wr_ptr >= hw->pre_parser_wr_ptr && + (parser_wr_ptr - hw->pre_parser_wr_ptr) < + again_threshold) { + int r = vdec_sync_input(vdec); + debug_print(DECODE_ID(hw), PRINT_FLAG_RUN_FLOW, + "%s buf level%x\n", + __func__, r); + return 0; + } + } +#endif + index = find_buffer(hw); if (index >= DECODE_BUFFER_NUM_MAX) { hw->buffer_not_ready++; @@ -1650,6 +1693,12 @@ void (*callback)(struct vdec_s *, void *), hw->vdec_cb_arg = arg; hw->vdec_cb = callback; +#ifdef AGAIN_HAS_THRESHOLD + hw->pre_parser_wr_ptr = + READ_PARSER_REG(PARSER_VIDEO_WP); + hw->next_again_flag = 0; +#endif + size = vdec_prepare_input(vdec, &hw->chunk); if (size < 0) { hw->input_empty++; @@ -1657,9 +1706,15 @@ void (*callback)(struct vdec_s *, void *), vdec_schedule_work(&hw->work); return; } - if (input_frame_based(vdec)) { + if (vdec_frame_based(vdec) && debug_enable) { u8 *data = NULL; + + if (hw->chunk) + debug_print(DECODE_ID(hw), PRINT_FLAG_RUN_FLOW, + "run: chunk offset 0x%x, size %d\n", + hw->chunk->offset, hw->chunk->size); + if (!hw->chunk->block->is_mapped) data = codec_mm_vmap(hw->chunk->block->start + hw->chunk->offset, size); @@ -1711,15 +1766,7 @@ void (*callback)(struct vdec_s *, void *), hw->input_empty = 0; - debug_print(DECODE_ID(hw), PRINT_FLAG_RUN_FLOW, - "%s,%d, size=%d\n", __func__, __LINE__, size); vdec_enable_input(vdec); - hw->init_flag = 1; - - if (hw->chunk) - debug_print(DECODE_ID(hw), PRINT_FLAG_RUN_FLOW, - "input chunk offset %d, size %d\n", - hw->chunk->offset, hw->chunk->size); hw->dec_result = DEC_RESULT_NONE; if (vdec->mc_loaded) { @@ -1753,6 +1800,7 @@ void (*callback)(struct vdec_s *, void *), start_process_time(hw); amvdec_start(); hw->stat |= STAT_VDEC_RUN; + hw->init_flag = 1; mod_timer(&hw->check_timer, jiffies + CHECK_INTERVAL); } @@ -1773,8 +1821,7 @@ static int ammvdec_mpeg12_probe(struct platform_device *pdev) return -EFAULT; } - hw = (struct vdec_mpeg12_hw_s *)devm_kzalloc(&pdev->dev, - sizeof(struct vdec_mpeg12_hw_s), GFP_KERNEL); + hw = vzalloc(sizeof(struct vdec_mpeg12_hw_s)); if (hw == NULL) { pr_info("\nammvdec_mpeg12 decoder driver alloc failed\n"); return -ENOMEM; @@ -1805,18 +1852,30 @@ static int ammvdec_mpeg12_probe(struct platform_device *pdev) &vf_provider_ops, pdata); platform_set_drvdata(pdev, pdata); - + hw->canvas_mode = CANVAS_BLKMODE_32X32; hw->platform_dev = pdev; + hw->tvp_flag = vdec_secure(pdata) ? CODEC_MM_FLAGS_TVP : 0; if (pdata->sys_info) hw->vmpeg12_amstream_dec_info = *pdata->sys_info; + debug_print(DECODE_ID(hw), 0, + "%s, sysinfo: %dx%d, tvp_flag = 0x%x\n", + __func__, + hw->vmpeg12_amstream_dec_info.width, + hw->vmpeg12_amstream_dec_info.height, + hw->tvp_flag); + if (vmpeg12_init(hw) < 0) { pr_info("ammvdec_mpeg12 init failed.\n"); - devm_kfree(&pdev->dev, (void *)hw); + if (hw) { + vfree(hw); + hw = NULL; + } pdata->dec_status = NULL; return -ENODEV; } + vdec_set_prepare_level(pdata, start_decode_buf_level); if (pdata->parallel_dec == 1) vdec_core_request(pdata, CORE_MASK_VDEC_1); else { @@ -1852,8 +1911,9 @@ static int ammvdec_mpeg12_remove(struct platform_device *pdev) hw->stat &= ~STAT_TIMER_ARM; } - cancel_work_sync(&hw->work); + cancel_work_sync(&hw->notify_work); + cancel_work_sync(&hw->work); if (hw->mm_blk_handle) { decoder_bmmu_box_free(hw->mm_blk_handle); @@ -1876,7 +1936,10 @@ static int ammvdec_mpeg12_remove(struct platform_device *pdev) vfree(hw->fw); hw->fw = NULL; } - + if (hw) { + vfree(hw); + hw = NULL; + } pr_info("ammvdec_mpeg12 removed.\n"); memset(&gvs, 0x0, sizeof(gvs)); @@ -1909,6 +1972,14 @@ static struct mconfig mmpeg12_configs[] = { MC_PU32("dec_control", &dec_control), MC_PU32("error_frame_skip_level", &error_frame_skip_level), MC_PU32("decode_timeout_val", &decode_timeout_val), + MC_PU32("start_decode_buf_level", &start_decode_buf_level), + MC_PU32("pre_decode_buf_level", &pre_decode_buf_level), + MC_PU32("debug_enable", &debug_enable), + MC_PU32("udebug_flag", &udebug_flag), + MC_PU32("without_display_mode", &without_display_mode), +#ifdef AGAIN_HAS_THRESHOLD + MC_PU32("again_threshold", &again_threshold), +#endif }; static struct mconfig_node mmpeg12_node; @@ -1954,11 +2025,26 @@ MODULE_PARM_DESC(pre_decode_buf_level, module_param(decode_timeout_val, uint, 0664); MODULE_PARM_DESC(decode_timeout_val, "\n ammvdec_mpeg12 decode_timeout_val\n"); + +module_param(start_decode_buf_level, int, 0664); +MODULE_PARM_DESC(start_decode_buf_level, + "\n ammvdec_mpeg12 start_decode_buf_level\n"); + module_param_array(max_process_time, uint, &max_decode_instance_num, 0664); module_param(udebug_flag, uint, 0664); MODULE_PARM_DESC(udebug_flag, "\n ammvdec_mpeg12 udebug_flag\n"); + +#ifdef AGAIN_HAS_THRESHOLD +module_param(again_threshold, uint, 0664); +MODULE_PARM_DESC(again_threshold, "\n again_threshold\n"); +#endif + +module_param(without_display_mode, uint, 0664); +MODULE_PARM_DESC(without_display_mode, "\n ammvdec_mpeg12 without_display_mode\n"); + + module_init(ammvdec_mpeg12_driver_init_module); module_exit(ammvdec_mpeg12_driver_remove_module); diff --git a/drivers/frame_provider/decoder/utils/amvdec.c b/drivers/frame_provider/decoder/utils/amvdec.c index e4850b8..1d4e6f5 100644 --- a/drivers/frame_provider/decoder/utils/amvdec.c +++ b/drivers/frame_provider/decoder/utils/amvdec.c @@ -81,7 +81,7 @@ static void amvdec_pg_enable(bool enable) } else { AMVDEC_CLK_GATE_OFF(AMRISC); - timeout = jiffies + HZ / 10; + timeout = jiffies + HZ / 100; while (READ_VREG(MDEC_PIC_DC_STATUS) != 0) { if (time_after(jiffies, timeout)) { @@ -95,7 +95,7 @@ static void amvdec_pg_enable(bool enable) } AMVDEC_CLK_GATE_OFF(MDEC_CLK_PIC_DC); - timeout = jiffies + HZ / 10; + timeout = jiffies + HZ / 100; while (READ_VREG(DBLK_STATUS) & 1) { if (time_after(jiffies, timeout)) { @@ -108,7 +108,7 @@ static void amvdec_pg_enable(bool enable) } } AMVDEC_CLK_GATE_OFF(MDEC_CLK_DBLK); - timeout = jiffies + HZ / 10; + timeout = jiffies + HZ / 100; while (READ_VREG(MC_STATUS0) & 1) { if (time_after(jiffies, timeout)) { @@ -121,7 +121,7 @@ static void amvdec_pg_enable(bool enable) } } AMVDEC_CLK_GATE_OFF(MC_CLK); - timeout = jiffies + HZ / 10; + timeout = jiffies + HZ / 100; while (READ_VREG(DCAC_DMA_CTRL) & 0x8000) { if (time_after(jiffies, timeout)) break; @@ -810,7 +810,7 @@ EXPORT_SYMBOL(amhevc_start); void amvdec_stop(void) { - ulong timeout = jiffies + HZ; + ulong timeout = jiffies + HZ/10; WRITE_VREG(MPSR, 0); WRITE_VREG(CPSR, 0); @@ -820,7 +820,7 @@ void amvdec_stop(void) break; } - timeout = jiffies + HZ; + timeout = jiffies + HZ/10; while (READ_VREG(LMEM_DMA_CTRL) & 0x8000) { if (time_after(jiffies, timeout)) break; @@ -859,7 +859,7 @@ EXPORT_SYMBOL(amvdec_stop); void amvdec2_stop(void) { if (has_vdec2()) { - ulong timeout = jiffies + HZ; + ulong timeout = jiffies + HZ/10; WRITE_VREG(VDEC2_MPSR, 0); WRITE_VREG(VDEC2_CPSR, 0); @@ -889,7 +889,7 @@ EXPORT_SYMBOL(amhcodec_stop); void amhevc_stop(void) { if (has_hevc_vdec()) { - ulong timeout = jiffies + HZ; + ulong timeout = jiffies + HZ/10; WRITE_VREG(HEVC_MPSR, 0); WRITE_VREG(HEVC_CPSR, 0); @@ -899,7 +899,7 @@ void amhevc_stop(void) break; } - timeout = jiffies + HZ; + timeout = jiffies + HZ/10; while (READ_VREG(HEVC_LMEM_DMA_CTRL) & 0x8000) { if (time_after(jiffies, timeout)) break; diff --git a/firmware/video_ucode.bin b/firmware/video_ucode.bin index 1ca3695..8c84166 100644 --- a/firmware/video_ucode.bin +++ b/firmware/video_ucode.bin @@ -1,5 +1,4 @@ -ѩ<K 7ƌ(?rH4؋!FR/a$f+쓚ї^8= w/ګUyTjI[cB)EKbaZU)Sٔ]h6-"EQF'ғ[1p2w3>Yyz#O-I"}kH2>4DE^UuCIq,]5]+F/+
-l8N]p_&KCAP +Sա B< @@ -15683,26 +15682,26 @@ G @ ! G - + - + - + -:x +:x G
@a @x -x +@x , - + - + ! @@ -15715,46 +15714,46 @@ J G$ ` - + @I - + o -I@Ip v Gv v Gw p v Gv v Gw +I@Ip v Gv v Gw p v Gv v Gw - - -k@WL - - + + +k@WW + + r -H - +H + @I ` G
` -x +x G`HR@ `"@ - - + + b 2x -B -@ +B + - + - + A @a - + @@ -15764,47 +15763,52 @@ G
` @I ^ G \ G Z -I +I - - + + I - + QGbɑ@I2 QHbIb P PCIIb - -QRIc P PCII?Hba t - -\ +@ +QRIc P PCIIG
B + +G LI B HbBx a t + + + +@^ GIǁ a O I@I - + - +@ x ->G - - +> +@x + + xQL FLBL -@Lr@ǁ +@Lr@ǁ ?I H I HR I Ix I I L H2X - - + + - + @@ -15823,9 +15827,9 @@ H2X I A@_` -T@GL -U - +T@GL@~ +U + A@ @@ -15861,7 +15865,7 @@ E=== E DL>L b D@ @ - -@xHR& b@I"@J@Hb + +@xHR& b@I"@J@Hb - + |