static void vdec_h264_get_fb(struct vdec_h264_inst *inst, struct h264_ring_fb_list *list, bool disp_list, struct vdec_fb **out_fb) { struct vdec_fb *fb; if (check_list_validity(inst, disp_list)) return; if (list->count == 0) { mtk_vcodec_debug(inst, "[FB] there is no %s fb", disp_list ? "disp" : "free"); *out_fb = NULL; return; } fb = (struct vdec_fb *) (uintptr_t)list->fb_list[list->read_idx].vdec_fb_va; fb->status |= (disp_list ? FB_ST_DISPLAY : FB_ST_FREE); *out_fb = fb; mtk_vcodec_debug(inst, "[FB] get %s fb st=%d poc=%d %llx", disp_list ? "disp" : "free", fb->status, list->fb_list[list->read_idx].poc, list->fb_list[list->read_idx].vdec_fb_va); list->read_idx = (list->read_idx == H264_MAX_FB_NUM - 1) ? 0 : list->read_idx + 1; list->count--; }
static void get_pic_info(struct vdec_h264_inst *inst, struct vdec_pic_info *pic) { *pic = inst->vsi->pic; mtk_vcodec_debug(inst, "pic(%d, %d), buf(%d, %d)", pic->pic_w, pic->pic_h, pic->buf_w, pic->buf_h); mtk_vcodec_debug(inst, "Y(%d, %d), C(%d, %d)", pic->y_bs_sz, pic->y_len_sz, pic->c_bs_sz, pic->c_len_sz); }
int vpu_enc_set_param(struct venc_vpu_inst *vpu, enum venc_set_param_type id, struct venc_enc_param *enc_param) { struct venc_ap_ipi_msg_set_param out; mtk_vcodec_debug(vpu, "id %d ->", id); memset(&out, 0, sizeof(out)); out.msg_id = AP_IPIMSG_ENC_SET_PARAM; out.vpu_inst_addr = vpu->inst_addr; out.param_id = id; switch (id) { case VENC_SET_PARAM_ENC: out.data_item = 0; break; case VENC_SET_PARAM_FORCE_INTRA: out.data_item = 0; break; case VENC_SET_PARAM_ADJUST_BITRATE: out.data_item = 1; out.data[0] = enc_param->bitrate; break; case VENC_SET_PARAM_ADJUST_FRAMERATE: out.data_item = 1; out.data[0] = enc_param->frm_rate; break; case VENC_SET_PARAM_GOP_SIZE: out.data_item = 1; out.data[0] = enc_param->gop_size; break; case VENC_SET_PARAM_INTRA_PERIOD: out.data_item = 1; out.data[0] = enc_param->intra_period; break; case VENC_SET_PARAM_SKIP_FRAME: out.data_item = 0; break; default: mtk_vcodec_err(vpu, "id %d not supported", id); return -EINVAL; } if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_SET_PARAM %d fail", id); return -EINVAL; } mtk_vcodec_debug(vpu, "id %d <-", id); return 0; }
static int h264_enc_set_param(unsigned long handle, enum venc_set_param_type type, struct venc_enc_param *enc_prm) { int ret = 0; struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; mtk_vcodec_debug(inst, "->type=%d", type); switch (type) { case VENC_SET_PARAM_ENC: inst->vsi->config.input_fourcc = enc_prm->input_yuv_fmt; inst->vsi->config.bitrate = enc_prm->bitrate; inst->vsi->config.pic_w = enc_prm->width; inst->vsi->config.pic_h = enc_prm->height; inst->vsi->config.buf_w = enc_prm->buf_width; inst->vsi->config.buf_h = enc_prm->buf_height; inst->vsi->config.gop_size = enc_prm->gop_size; inst->vsi->config.framerate = enc_prm->frm_rate; inst->vsi->config.intra_period = enc_prm->intra_period; inst->vsi->config.profile = h264_get_profile(inst, enc_prm->h264_profile); inst->vsi->config.level = h264_get_level(inst, enc_prm->h264_level); inst->vsi->config.wfd = 0; ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); if (ret) break; if (inst->work_buf_allocated) { h264_enc_free_work_buf(inst); inst->work_buf_allocated = false; } ret = h264_enc_alloc_work_buf(inst); if (ret) break; inst->work_buf_allocated = true; break; case VENC_SET_PARAM_PREPEND_HEADER: inst->prepend_hdr = 1; mtk_vcodec_debug(inst, "set prepend header mode"); break; default: ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); break; } mtk_vcodec_debug_leave(inst); return ret; }
static int h264_encode_pps(struct venc_h264_inst *inst, struct mtk_vcodec_mem *bs_buf, unsigned int *bs_size) { int ret = 0; unsigned int irq_status; mtk_vcodec_debug_enter(inst); ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_PPS, NULL, bs_buf, bs_size); if (ret) return ret; irq_status = h264_enc_wait_venc_done(inst); if (irq_status != MTK_VENC_IRQ_STATUS_PPS) { mtk_vcodec_err(inst, "expect irq status %d", MTK_VENC_IRQ_STATUS_PPS); return -EINVAL; } *bs_size = h264_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT); mtk_vcodec_debug(inst, "bs size %d <-", *bs_size); return ret; }
static void vpu_enc_ipi_handler(void *data, unsigned int len, void *priv) { struct venc_vpu_ipi_msg_common *msg = data; struct venc_vpu_inst *vpu = (struct venc_vpu_inst *)(unsigned long)msg->venc_inst; mtk_vcodec_debug(vpu, "msg_id %x inst %p status %d", msg->msg_id, vpu, msg->status); switch (msg->msg_id) { case VPU_IPIMSG_ENC_INIT_DONE: handle_enc_init_msg(vpu, data); break; case VPU_IPIMSG_ENC_SET_PARAM_DONE: break; case VPU_IPIMSG_ENC_ENCODE_DONE: handle_enc_encode_msg(vpu, data); break; case VPU_IPIMSG_ENC_DEINIT_DONE: break; default: mtk_vcodec_err(vpu, "unknown msg id %x", msg->msg_id); break; } vpu->signaled = 1; vpu->failure = (msg->status != VENC_IPI_MSG_STATUS_OK); mtk_vcodec_debug_leave(vpu); }
static void get_crop_info(struct vdec_h264_inst *inst, struct v4l2_rect *cr) { cr->left = inst->vsi->crop.left; cr->top = inst->vsi->crop.top; cr->width = inst->vsi->crop.width; cr->height = inst->vsi->crop.height; mtk_vcodec_debug(inst, "l=%d, t=%d, w=%d, h=%d", cr->left, cr->top, cr->width, cr->height); }
static unsigned int h264_enc_wait_venc_done(struct venc_h264_inst *inst) { unsigned int irq_status = 0; struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)inst->ctx; if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED, WAIT_INTR_TIMEOUT_MS)) { irq_status = ctx->irq_status; mtk_vcodec_debug(inst, "irq_status %x <-", irq_status); } return irq_status; }
int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode, struct venc_frm_buf *frm_buf, struct mtk_vcodec_mem *bs_buf, unsigned int *bs_size) { struct venc_ap_ipi_msg_enc out; mtk_vcodec_debug(vpu, "bs_mode %d ->", bs_mode); memset(&out, 0, sizeof(out)); out.msg_id = AP_IPIMSG_ENC_ENCODE; out.vpu_inst_addr = vpu->inst_addr; out.bs_mode = bs_mode; if (frm_buf) { if ((frm_buf->fb_addr[0].dma_addr % 16 == 0) && (frm_buf->fb_addr[1].dma_addr % 16 == 0) && (frm_buf->fb_addr[2].dma_addr % 16 == 0)) { out.input_addr[0] = frm_buf->fb_addr[0].dma_addr; out.input_addr[1] = frm_buf->fb_addr[1].dma_addr; out.input_addr[2] = frm_buf->fb_addr[2].dma_addr; } else { mtk_vcodec_err(vpu, "dma_addr not align to 16"); return -EINVAL; } } if (bs_buf) { out.bs_addr = bs_buf->dma_addr; out.bs_size = bs_buf->size; } if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_ENCODE %d fail", bs_mode); return -EINVAL; } mtk_vcodec_debug(vpu, "bs_mode %d state %d size %d key_frm %d <-", bs_mode, vpu->state, vpu->bs_size, vpu->is_key_frm); return 0; }
static int h264_encode_frame(struct venc_h264_inst *inst, struct venc_frm_buf *frm_buf, struct mtk_vcodec_mem *bs_buf, unsigned int *bs_size) { int ret = 0; unsigned int irq_status; mtk_vcodec_debug_enter(inst); ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_FRAME, frm_buf, bs_buf, bs_size); if (ret) return ret; /* * skip frame case: The skip frame buffer is composed by vpu side only, * it does not trigger the hw, so skip the wait interrupt operation. */ if (inst->vpu_inst.state == VEN_IPI_MSG_ENC_STATE_SKIP) { *bs_size = inst->vpu_inst.bs_size; memcpy(bs_buf->va, inst->work_bufs[VENC_H264_VPU_WORK_BUF_SKIP_FRAME].va, *bs_size); ++inst->frm_cnt; return ret; } irq_status = h264_enc_wait_venc_done(inst); if (irq_status != MTK_VENC_IRQ_STATUS_FRM) { mtk_vcodec_err(inst, "irq_status=%d failed", irq_status); return -EIO; } *bs_size = h264_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT); ++inst->frm_cnt; mtk_vcodec_debug(inst, "frm %d bs_size %d key_frm %d <-", inst->frm_cnt, *bs_size, inst->vpu_inst.is_key_frm); return ret; }
static unsigned int h264_get_profile(struct venc_h264_inst *inst, unsigned int profile) { switch (profile) { case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: return 66; case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: return 77; case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: return 100; case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: mtk_vcodec_err(inst, "unsupported CONSTRAINED_BASELINE"); return 0; case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: mtk_vcodec_err(inst, "unsupported EXTENDED"); return 0; default: mtk_vcodec_debug(inst, "unsupported profile %d", profile); return 100; } }
static int vdec_h264_init(struct mtk_vcodec_ctx *ctx, unsigned long *h_vdec) { struct vdec_h264_inst *inst = NULL; int err; inst = kzalloc(sizeof(*inst), GFP_KERNEL); if (!inst) return -ENOMEM; inst->ctx = ctx; inst->vpu.id = IPI_VDEC_H264; inst->vpu.dev = ctx->dev->vpu_plat_dev; inst->vpu.ctx = ctx; inst->vpu.handler = vpu_dec_ipi_handler; err = vpu_dec_init(&inst->vpu); if (err) { mtk_vcodec_err(inst, "vdec_h264 init err=%d", err); goto error_free_inst; } inst->vsi = (struct vdec_h264_vsi *)inst->vpu.vsi; err = allocate_predication_buf(inst); if (err) goto error_deinit; mtk_vcodec_debug(inst, "H264 Instance >> %p", inst); *h_vdec = (unsigned long)inst; return 0; error_deinit: vpu_dec_deinit(&inst->vpu); error_free_inst: kfree(inst); return err; }
static unsigned int h264_get_level(struct venc_h264_inst *inst, unsigned int level) { switch (level) { case V4L2_MPEG_VIDEO_H264_LEVEL_1B: mtk_vcodec_err(inst, "unsupported 1B"); return 0; case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: return 10; case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: return 11; case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: return 12; case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: return 13; case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: return 20; case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: return 21; case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: return 22; case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: return 30; case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: return 31; case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: return 32; case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: return 40; case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: return 41; case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: return 42; default: mtk_vcodec_debug(inst, "unsupported level %d", level); return 31; } }
static void put_fb_to_free(struct vdec_h264_inst *inst, struct vdec_fb *fb) { struct h264_ring_fb_list *list; if (fb) { if (check_list_validity(inst, false)) return; list = &inst->vsi->list_free; if (list->count == H264_MAX_FB_NUM) { mtk_vcodec_err(inst, "[FB] put fb free_list full"); return; } mtk_vcodec_debug(inst, "[FB] put fb into free_list @(%p, %llx)", fb->base_y.va, (u64)fb->base_y.dma_addr); list->fb_list[list->write_idx].vdec_fb_va = (u64)(uintptr_t)fb; list->write_idx = (list->write_idx == H264_MAX_FB_NUM - 1) ? 0 : list->write_idx + 1; list->count++; } }
static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst) { int i; int ret = 0; struct venc_h264_vpu_buf *wb = inst->vsi->work_bufs; mtk_vcodec_debug_enter(inst); for (i = 0; i < VENC_H264_VPU_WORK_BUF_MAX; i++) { /* * This 'wb' structure is set by VPU side and shared to AP for * buffer allocation and IO virtual addr mapping. For most of * the buffers, AP will allocate the buffer according to 'size' * field and store the IO virtual addr in 'iova' field. There * are two exceptions: * (1) RC_CODE buffer, it's pre-allocated in the VPU side, and * save the VPU addr in the 'vpua' field. The AP will translate * the VPU addr to the corresponding IO virtual addr and store * in 'iova' field for reg setting in VPU side. * (2) SKIP_FRAME buffer, it's pre-allocated in the VPU side, * and save the VPU addr in the 'vpua' field. The AP will * translate the VPU addr to the corresponding AP side virtual * address and do some memcpy access to move to bitstream buffer * assigned by v4l2 layer. */ inst->work_bufs[i].size = wb[i].size; if (i == VENC_H264_VPU_WORK_BUF_SKIP_FRAME) { inst->work_bufs[i].va = vpu_mapping_dm_addr( inst->vpu_inst.dev, wb[i].vpua); inst->work_bufs[i].dma_addr = 0; } else { ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->work_bufs[i]); if (ret) { mtk_vcodec_err(inst, "cannot allocate buf %d", i); goto err_alloc; } /* * This RC_CODE is pre-allocated by VPU and saved in VPU * addr. So we need use memcpy to copy RC_CODE from VPU * addr into IO virtual addr in 'iova' field for reg * setting in VPU side. */ if (i == VENC_H264_VPU_WORK_BUF_RC_CODE) { void *tmp_va; tmp_va = vpu_mapping_dm_addr(inst->vpu_inst.dev, wb[i].vpua); memcpy(inst->work_bufs[i].va, tmp_va, wb[i].size); } } wb[i].iova = inst->work_bufs[i].dma_addr; mtk_vcodec_debug(inst, "work_buf[%d] va=0x%p iova=%pad size=%zu", i, inst->work_bufs[i].va, &inst->work_bufs[i].dma_addr, inst->work_bufs[i].size); } /* the pps_buf is used by AP side only */ inst->pps_buf.size = 128; ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->pps_buf); if (ret) { mtk_vcodec_err(inst, "cannot allocate pps_buf"); goto err_alloc; } mtk_vcodec_debug_leave(inst); return ret; err_alloc: h264_enc_free_work_buf(inst); return ret; }
static int vdec_h264_decode(unsigned long h_vdec, struct mtk_vcodec_mem *bs, struct vdec_fb *fb, bool *res_chg) { struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; struct vdec_vpu_inst *vpu = &inst->vpu; int nal_start_idx = 0; int err = 0; unsigned int nal_start; unsigned int nal_type; unsigned char *buf; unsigned int buf_sz; unsigned int data[2]; uint64_t vdec_fb_va = (u64)(uintptr_t)fb; uint64_t y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; uint64_t c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0; mtk_vcodec_debug(inst, "+ [%d] FB y_dma=%llx c_dma=%llx va=%p", ++inst->num_nalu, y_fb_dma, c_fb_dma, fb); /* bs NULL means flush decoder */ if (bs == NULL) return vpu_dec_reset(vpu); buf = (unsigned char *)bs->va; buf_sz = bs->size; nal_start_idx = find_start_code(buf, buf_sz); if (nal_start_idx < 0) goto err_free_fb_out; nal_start = buf[nal_start_idx]; nal_type = NAL_TYPE(buf[nal_start_idx]); mtk_vcodec_debug(inst, "\n + NALU[%d] type %d +\n", inst->num_nalu, nal_type); if (nal_type == NAL_H264_PPS) { buf_sz -= nal_start_idx; if (buf_sz > HDR_PARSING_BUF_SZ) { err = -EILSEQ; goto err_free_fb_out; } memcpy(inst->vsi->hdr_buf, buf + nal_start_idx, buf_sz); } inst->vsi->dec.bs_dma = (uint64_t)bs->dma_addr; inst->vsi->dec.y_fb_dma = y_fb_dma; inst->vsi->dec.c_fb_dma = c_fb_dma; inst->vsi->dec.vdec_fb_va = vdec_fb_va; data[0] = buf_sz; data[1] = nal_start; err = vpu_dec_start(vpu, data, 2); if (err) goto err_free_fb_out; *res_chg = inst->vsi->dec.resolution_changed; if (*res_chg) { struct vdec_pic_info pic; mtk_vcodec_debug(inst, "- resolution changed -"); get_pic_info(inst, &pic); if (inst->vsi->dec.realloc_mv_buf) { err = alloc_mv_buf(inst, &pic); if (err) goto err_free_fb_out; } } if (nal_type == NAL_NON_IDR_SLICE || nal_type == NAL_IDR_SLICE) { /* wait decoder done interrupt */ err = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, WAIT_INTR_TIMEOUT_MS); if (err) goto err_free_fb_out; vpu_dec_end(vpu); } mtk_vcodec_debug(inst, "\n - NALU[%d] type=%d -\n", inst->num_nalu, nal_type); return 0; err_free_fb_out: put_fb_to_free(inst, fb); mtk_vcodec_err(inst, "\n - NALU[%d] err=%d -\n", inst->num_nalu, err); return err; }
static void get_dpb_size(struct vdec_h264_inst *inst, unsigned int *dpb_sz) { *dpb_sz = inst->vsi->dec.dpb_sz; mtk_vcodec_debug(inst, "sz=%d", *dpb_sz); }
static int h264_enc_encode(unsigned long handle, enum venc_start_opt opt, struct venc_frm_buf *frm_buf, struct mtk_vcodec_mem *bs_buf, struct venc_done_result *result) { int ret = 0; struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; struct mtk_vcodec_ctx *ctx = inst->ctx; mtk_vcodec_debug(inst, "opt %d ->", opt); enable_irq(ctx->dev->enc_irq); switch (opt) { case VENC_START_OPT_ENCODE_SEQUENCE_HEADER: { unsigned int bs_size_hdr; ret = h264_encode_header(inst, bs_buf, &bs_size_hdr); if (ret) goto encode_err; result->bs_size = bs_size_hdr; result->is_key_frm = false; break; } case VENC_START_OPT_ENCODE_FRAME: { int hdr_sz; int hdr_sz_ext; int filler_sz = 0; const int bs_alignment = 128; struct mtk_vcodec_mem tmp_bs_buf; unsigned int bs_size_hdr; unsigned int bs_size_frm; if (!inst->prepend_hdr) { ret = h264_encode_frame(inst, frm_buf, bs_buf, &result->bs_size); if (ret) goto encode_err; result->is_key_frm = inst->vpu_inst.is_key_frm; break; } mtk_vcodec_debug(inst, "h264_encode_frame prepend SPS/PPS"); ret = h264_encode_header(inst, bs_buf, &bs_size_hdr); if (ret) goto encode_err; hdr_sz = bs_size_hdr; hdr_sz_ext = (hdr_sz & (bs_alignment - 1)); if (hdr_sz_ext) { filler_sz = bs_alignment - hdr_sz_ext; if (hdr_sz_ext + H264_FILLER_MARKER_SIZE > bs_alignment) filler_sz += bs_alignment; h264_encode_filler(inst, bs_buf->va + hdr_sz, filler_sz); } tmp_bs_buf.va = bs_buf->va + hdr_sz + filler_sz; tmp_bs_buf.dma_addr = bs_buf->dma_addr + hdr_sz + filler_sz; tmp_bs_buf.size = bs_buf->size - (hdr_sz + filler_sz); ret = h264_encode_frame(inst, frm_buf, &tmp_bs_buf, &bs_size_frm); if (ret) goto encode_err; result->bs_size = hdr_sz + filler_sz + bs_size_frm; mtk_vcodec_debug(inst, "hdr %d filler %d frame %d bs %d", hdr_sz, filler_sz, bs_size_frm, result->bs_size); inst->prepend_hdr = 0; result->is_key_frm = inst->vpu_inst.is_key_frm; break; } default: mtk_vcodec_err(inst, "venc_start_opt %d not supported", opt); ret = -EINVAL; break; } encode_err: disable_irq(ctx->dev->enc_irq); mtk_vcodec_debug(inst, "opt %d <-", opt); return ret; }