55 files changed, 11471 insertions, 2282 deletions
diff --git a/drivers/amvdec_ports/decoder/vdec_mpeg4_if.c b/drivers/amvdec_ports/decoder/vdec_mpeg4_if.c new file mode 100644 index 0000000..7a40a4a --- a/dev/null +++ b/drivers/amvdec_ports/decoder/vdec_mpeg4_if.c @@ -0,0 +1,499 @@ +/* +* Copyright (C) 2017 Amlogic, Inc. All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +* Description: +*/ +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <uapi/linux/swab.h> +#include "../vdec_drv_if.h" +#include "../aml_vcodec_util.h" +#include "../aml_vcodec_dec.h" +#include "../aml_vcodec_adapt.h" +#include "../vdec_drv_base.h" +#include "../aml_vcodec_vfm.h" +#include "aml_mpeg4_parser.h" + +#define NAL_TYPE(value) ((value) & 0x1F) +#define HEADER_BUFFER_SIZE (32 * 1024) + +/** + * struct mpeg4_fb - mpeg4 decode frame buffer information + * @vdec_fb_va : virtual address of struct vdec_fb + * @y_fb_dma : dma address of Y frame buffer (luma) + * @c_fb_dma : dma address of C frame buffer (chroma) + * @poc : picture order count of frame buffer + * @reserved : for 8 bytes alignment + */ +struct mpeg4_fb { + uint64_t vdec_fb_va; + uint64_t y_fb_dma; + uint64_t c_fb_dma; + int32_t poc; + uint32_t reserved; +}; + +/** + * struct vdec_mpeg4_dec_info - decode information + * @dpb_sz : decoding picture buffer size + * @resolution_changed : resoltion change happen + * @reserved : for 8 bytes alignment + * @bs_dma : Input bit-stream buffer dma address + * @y_fb_dma : Y frame buffer dma address + * @c_fb_dma : C frame buffer dma address + * @vdec_fb_va : VDEC frame buffer struct virtual address + */ +struct vdec_mpeg4_dec_info { + uint32_t dpb_sz; + uint32_t resolution_changed; + uint32_t reserved; + uint64_t bs_dma; + uint64_t y_fb_dma; + uint64_t c_fb_dma; + uint64_t vdec_fb_va; +}; + +/** + * struct vdec_mpeg4_vsi - shared memory for decode information exchange + * between VPU and Host. + * The memory is allocated by VPU then mapping to Host + * in vpu_dec_init() and freed in vpu_dec_deinit() + * by VPU. + * AP-W/R : AP is writer/reader on this item + * VPU-W/R: VPU is write/reader on this item + * @hdr_buf : Header parsing buffer (AP-W, VPU-R) + * @list_free : free frame buffer ring list (AP-W/R, VPU-W) + * @list_disp : display frame buffer ring list (AP-R, VPU-W) + * @dec : decode information (AP-R, VPU-W) + * @pic : picture information (AP-R, VPU-W) + * @crop : crop information (AP-R, VPU-W) + */ +struct vdec_mpeg4_vsi { + char *header_buf; + int sps_size; + int pps_size; + int sei_size; + int head_offset; + struct vdec_mpeg4_dec_info dec; + struct vdec_pic_info pic; + struct v4l2_rect crop; + bool is_combine; + int nalu_pos; + //struct mpeg4ParamSets ps; +}; + +/** + * struct vdec_mpeg4_inst - mpeg4 decoder instance + * @num_nalu : how many nalus be decoded + * @ctx : point to aml_vcodec_ctx + * @vsi : VPU shared information + */ +struct vdec_mpeg4_inst { + unsigned int num_nalu; + struct aml_vcodec_ctx *ctx; + struct aml_vdec_adapt vdec; + struct vdec_mpeg4_vsi *vsi; + struct vcodec_vfm_s vfm; +}; + +static void get_pic_info(struct vdec_mpeg4_inst *inst, + struct vdec_pic_info *pic) +{ + *pic = inst->vsi->pic; + + aml_vcodec_debug(inst, "pic(%d, %d), buf(%d, %d)", + pic->visible_width, pic->visible_height, + pic->coded_width, pic->coded_height); + aml_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); +} + +static void get_crop_info(struct vdec_mpeg4_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; + + aml_vcodec_debug(inst, "l=%d, t=%d, w=%d, h=%d", + cr->left, cr->top, cr->width, cr->height); +} + +static void get_dpb_size(struct vdec_mpeg4_inst *inst, unsigned int *dpb_sz) +{ + *dpb_sz = 9;//inst->vsi->dec.dpb_sz; + aml_vcodec_debug(inst, "sz=%d", *dpb_sz); +} + +static int vdec_mpeg4_init(struct aml_vcodec_ctx *ctx, unsigned long *h_vdec) +{ + struct vdec_mpeg4_inst *inst = NULL; + int ret = -1; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->ctx = ctx; + + inst->vdec.video_type = VFORMAT_MPEG4; + inst->vdec.format = VIDEO_DEC_FORMAT_MPEG4_5; + inst->vdec.dev = ctx->dev->vpu_plat_dev; + inst->vdec.filp = ctx->dev->filp; + inst->vdec.ctx = ctx; + + /* set play mode.*/ + if (ctx->is_drm_mode) + inst->vdec.port.flag |= PORT_FLAG_DRM; + + /* to eable mpeg4 hw.*/ + inst->vdec.port.type = PORT_TYPE_VIDEO; + + /* init vfm */ + inst->vfm.ctx = ctx; + inst->vfm.ada_ctx = &inst->vdec; + vcodec_vfm_init(&inst->vfm); + + ret = video_decoder_init(&inst->vdec); + if (ret) { + aml_vcodec_err(inst, "vdec_mpeg4 init err=%d", ret); + goto error_free_inst; + } + + /* probe info from the stream */ + inst->vsi = kzalloc(sizeof(struct vdec_mpeg4_vsi), GFP_KERNEL); + if (!inst->vsi) { + ret = -ENOMEM; + goto error_free_inst; + } + + /* alloc the header buffer to be used cache sps or spp etc.*/ + inst->vsi->header_buf = kzalloc(HEADER_BUFFER_SIZE, GFP_KERNEL); + if (!inst->vsi) { + ret = -ENOMEM; + goto error_free_vsi; + } + + inst->vsi->pic.visible_width = 1920; + inst->vsi->pic.visible_height = 1080; + inst->vsi->pic.coded_width = 1920; + inst->vsi->pic.coded_height = 1088; + inst->vsi->pic.y_bs_sz = 0; + inst->vsi->pic.y_len_sz = (1920 * 1088); + inst->vsi->pic.c_bs_sz = 0; + inst->vsi->pic.c_len_sz = (1920 * 1088 / 2); + + aml_vcodec_debug(inst, "mpeg4 Instance >> %p", inst); + + ctx->ada_ctx = &inst->vdec; + *h_vdec = (unsigned long)inst; + + //dump_init(); + + return 0; + +error_free_vsi: + kfree(inst->vsi); +error_free_inst: + kfree(inst); + *h_vdec = 0; + + return ret; +} + +#if 0 +static int refer_buffer_num(int level_idc, int poc_cnt, + int mb_width, int mb_height) +{ + return 20; +} +#endif + +static void fill_vdec_params(struct vdec_mpeg4_inst *inst, + struct mpeg4_dec_param *dec_ps) +{ + struct vdec_pic_info *pic = &inst->vsi->pic; + struct vdec_mpeg4_dec_info *dec = &inst->vsi->dec; + struct v4l2_rect *rect = &inst->vsi->crop; + + /* fill visible area size that be used for EGL. */ + pic->visible_width = dec_ps->m.width; + pic->visible_height = dec_ps->m.height; + + /* calc visible ares. */ + rect->left = 0; + rect->top = 0; + rect->width = pic->visible_width; + rect->height = pic->visible_height; + + /* config canvas size that be used for decoder. */ + pic->coded_width = ALIGN(dec_ps->m.width, 64); + pic->coded_height = ALIGN(dec_ps->m.height, 64); + + pic->y_len_sz = pic->coded_width * pic->coded_height; + pic->c_len_sz = pic->y_len_sz >> 1; + + /* calc DPB size */ + dec->dpb_sz = 9;//refer_buffer_num(sps->level_idc, poc_cnt, mb_w, mb_h); + + pr_info("[%d] The stream infos, coded:(%d x %d), visible:(%d x %d), DPB: %d\n", + inst->ctx->id, pic->coded_width, pic->coded_height, + pic->visible_width, pic->visible_height, dec->dpb_sz); +} + +static int stream_parse(struct vdec_mpeg4_inst *inst, u8 *buf, u32 size) +{ + int ret = 0; + struct mpeg4_param_sets *ps = NULL; + + ps = kzalloc(sizeof(struct mpeg4_param_sets), GFP_KERNEL); + if (ps == NULL) + return -ENOMEM; + + ret = mpeg4_decode_extradata_ps(buf, size, ps); + if (ret) { + pr_err("parse extra data failed. err: %d\n", ret); + goto out; + } + + if (ps->head_parsed) + fill_vdec_params(inst, &ps->dec_ps); + + ret = ps->head_parsed ? 0 : -1; +out: + kfree(ps); + + return ret; +} + +static int vdec_mpeg4_probe(unsigned long h_vdec, + struct aml_vcodec_mem *bs, void *out) +{ + struct vdec_mpeg4_inst *inst = + (struct vdec_mpeg4_inst *)h_vdec; + struct stream_info *st; + u8 *buf = (u8 *)bs->vaddr; + u32 size = bs->size; + int ret = 0; + + st = (struct stream_info *)buf; + if (inst->ctx->is_drm_mode && (st->magic == DRMe || st->magic == DRMn)) + return 0; + + if (st->magic == NORe || st->magic == NORn) + ret = stream_parse(inst, st->data, st->length); + else + ret = stream_parse(inst, buf, size); + + return ret; +} + +static void vdec_mpeg4_deinit(unsigned long h_vdec) +{ + struct vdec_mpeg4_inst *inst = (struct vdec_mpeg4_inst *)h_vdec; + + if (!inst) + return; + + aml_vcodec_debug_enter(inst); + + video_decoder_release(&inst->vdec); + + vcodec_vfm_release(&inst->vfm); + + //dump_deinit(); + + if (inst->vsi && inst->vsi->header_buf) + kfree(inst->vsi->header_buf); + + if (inst->vsi) + kfree(inst->vsi); + + kfree(inst); +} + +static int vdec_mpeg4_get_fb(struct vdec_mpeg4_inst *inst, struct vdec_v4l2_buffer **out) +{ + return get_fb_from_queue(inst->ctx, out); +} + +static void vdec_mpeg4_get_vf(struct vdec_mpeg4_inst *inst, struct vdec_v4l2_buffer **out) +{ + struct vframe_s *vf = NULL; + struct vdec_v4l2_buffer *fb = NULL; + + vf = peek_video_frame(&inst->vfm); + if (!vf) { + aml_vcodec_debug(inst, "there is no vframe."); + *out = NULL; + return; + } + + vf = get_video_frame(&inst->vfm); + if (!vf) { + aml_vcodec_debug(inst, "the vframe is avalid."); + *out = NULL; + return; + } + + atomic_set(&vf->use_cnt, 1); + + fb = (struct vdec_v4l2_buffer *)vf->v4l_mem_handle; + fb->vf_handle = (unsigned long)vf; + fb->status = FB_ST_DISPLAY; + + *out = fb; + + //pr_info("%s, %d\n", __func__, fb->base_y.bytes_used); + //dump_write(fb->base_y.va, fb->base_y.bytes_used); + //dump_write(fb->base_c.va, fb->base_c.bytes_used); + + /* convert yuv format. */ + //swap_uv(fb->base_c.va, fb->base_c.size); +} + +static int vdec_write_nalu(struct vdec_mpeg4_inst *inst, + u8 *buf, u32 size, u64 ts) +{ + int ret = 0; + struct aml_vdec_adapt *vdec = &inst->vdec; + + ret = vdec_vframe_write(vdec, buf, size, ts); + + return ret; +} + +static int vdec_mpeg4_decode(unsigned long h_vdec, struct aml_vcodec_mem *bs, + u64 timestamp, bool *res_chg) +{ + struct vdec_mpeg4_inst *inst = (struct vdec_mpeg4_inst *)h_vdec; + struct aml_vdec_adapt *vdec = &inst->vdec; + struct stream_info *st; + u8 *buf; + u32 size; + int ret = 0; + + /* bs NULL means flush decoder */ + if (bs == NULL) + return 0; + + buf = (u8 *)bs->vaddr; + size = bs->size; + st = (struct stream_info *)buf; + + if (inst->ctx->is_drm_mode && (st->magic == DRMe || st->magic == DRMn)) + ret = vdec_vbuf_write(vdec, st->m.buf, sizeof(st->m.drm)); + else if (st->magic == NORe) + ret = vdec_vbuf_write(vdec, st->data, st->length); + else if (st->magic == NORn) + ret = vdec_write_nalu(inst, st->data, st->length, timestamp); + else if (inst->ctx->is_stream_mode) + ret = vdec_vbuf_write(vdec, buf, size); + else + ret = vdec_write_nalu(inst, buf, size, timestamp); + + return ret; +} + +static int vdec_mpeg4_get_param(unsigned long h_vdec, + enum vdec_get_param_type type, void *out) +{ + int ret = 0; + struct vdec_mpeg4_inst *inst = (struct vdec_mpeg4_inst *)h_vdec; + + if (!inst) { + pr_err("the mpeg4 inst of dec is invalid.\n"); + return -1; + } + + switch (type) { + case GET_PARAM_DISP_FRAME_BUFFER: + vdec_mpeg4_get_vf(inst, out); + break; + + case GET_PARAM_FREE_FRAME_BUFFER: + ret = vdec_mpeg4_get_fb(inst, out); + break; + + case GET_PARAM_PIC_INFO: + get_pic_info(inst, out); + break; + + case GET_PARAM_DPB_SIZE: + get_dpb_size(inst, out); + break; + + case GET_PARAM_CROP_INFO: + get_crop_info(inst, out); + break; + + default: + aml_vcodec_err(inst, "invalid get parameter type=%d", type); + ret = -EINVAL; + } + + return ret; +} + +static void set_param_pic_info(struct vdec_mpeg4_inst *inst, + struct aml_vdec_pic_infos *info) +{ + pr_info("---%s, %d\n", __func__, __LINE__); +} + +static int vdec_mpeg4_set_param(unsigned long h_vdec, + enum vdec_set_param_type type, void *in) +{ + int ret = 0; + struct vdec_mpeg4_inst *inst = (struct vdec_mpeg4_inst *)h_vdec; + + if (!inst) { + pr_err("the mpeg4 inst of dec is invalid.\n"); + return -1; + } + + switch (type) { + case SET_PARAM_PIC_INFO: + set_param_pic_info(inst, in); + break; + + default: + aml_vcodec_err(inst, "invalid set parameter type=%d", type); + ret = -EINVAL; + } + + return ret; +} + +static struct vdec_common_if vdec_mpeg4_if = { + .init = vdec_mpeg4_init, + .probe = vdec_mpeg4_probe, + .decode = vdec_mpeg4_decode, + .get_param = vdec_mpeg4_get_param, + .set_param = vdec_mpeg4_set_param, + .deinit = vdec_mpeg4_deinit, +}; + +struct vdec_common_if *get_mpeg4_dec_comm_if(void); + +struct vdec_common_if *get_mpeg4_dec_comm_if(void) +{ + return &vdec_mpeg4_if; +} |